Grouping Waypoints on Bing Maps to Create a Schedule - bing-maps

I need to group waypoints based on their proximity to each other. We have resources that are assigned appointments (patients). For each patient, we see them 2x per week. I need to calculate the schedule for each day and then the route. The route part is easy with a call to the Bing Maps API. But, I'm struggling with how to generate a schedule.
For instance, if Resource1 sees 8 patients per week and each of them 2x - that's 16 appointments. Let's also assume I will see them on Mon & Wed or Tues & Thur. How would I assign which of the 8 patients should be seen on Mon/Wed & which ones are Tues/Thur. It should be based on their proximity to each other. So, give me a calculation which calculates which 4 should be seen on Mon/Wed & which 4 should be seen on Tues/Thur (assuming it's split 4/4 and not 5/3, etc)

There are a lot of ways to do this.
If you are using Power automate, there is a chance you are using Dynamics. Dynamics has a scheduler capability built in for this specific scenario. https://learn.microsoft.com/en-us/dynamics365/field-service/universal-resource-scheduling-for-field-service
You can cluster that data points initially. There are different methods to do this; distance based, travel time based, k-means clustering, DBscan clustering. distance, k-means, and DBscan are fairly common, but don't take into consideration the physical layout which can have a big impact (person might have to go out of way to get to a location on other side of a river). That said, travel time-based clustering has a much higher cost.
I want to say that the Route itinerary optimizations service would help here, but it would need you to already know which customers you are visiting for the day. https://learn.microsoft.com/en-us/bingmaps/rest-services/routes/optimized-itinerary You might be able to trick it and double the time of a shift, and then split those out afterwards. This assumes you don't care which resource visits which patients.
There are many existing services out there that provide this capability, so also worth comparing the dev/maintenance cost with a license for something already working at scale.
Looking at your scenario it looks like it would be fair to say that you would want half the patients to be visited on Mon/Wed, and the other half on Tues/Thurs. Furthermore, you would want a single resource works 4 days, and would be assigned half their patients per combination of days. With the above in mind, if we cluster the data into groups of no more than 4 patients per cluster, a single cluster would be the schedule for a single resource for a single day combo (e.g. Mon/Wed). Once you have these clusters, you can then slip them in half and assign to your resources. For a fairly accurate approach, I would want to use travel time for the clustering and would tackle this like so:
Take first patient, calculate a travel time matrix to all other patients.
Loop through all other patients and find the 3 closest and consider that an assigned cluster. Optionally assign the day combo/resource.
Take the next unclustered/unassigned patient and calculate a travel time matrix to all other patients.
Loop through all other unclustered/unassigned patients and find the 3 closest. Assign them. When half the patients are assigned, use the second day combo and allow all resources to be assigned a second time for the remaining calculated clusters.
Repeat steps 3 and 4 until all patients are assigned.
A cheaper, but less spatially accurate version of the above (less time efficient), would be to calculate straight line distances rather than travel time. Straight line distances are a simple calculation (Haversine formula).
If you want to go a step further, calculate the distance from the cluster or one of the patients in the cluster to each resource, and assign the resource who is closest (assuming the resources are not all located at the same location).
Here is proof of concept:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
table, td, th {
border: 1px solid;
}
table {
width: 100%;
border-collapse: collapse;
}
</style>
</head>
<body>
<input type="button" onclick="schedulePatients()" value="Schedule patients"/>
<br/><br />
<div id="output"></div>
<script>
var resources = {
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": 248, "properties": { "assignedMonWed": false, "assignedTuesThurs": false, "MonWebSchedule": [], "TuesThursSchedule": [] }, "geometry": { "type": "Point", "coordinates": [-122.050234696603, 47.6808627484536] } },
{ "type": "Feature", "id": 467, "properties": { "assignedMonWed": false, "assignedTuesThurs": false, "MonWebSchedule": [], "TuesThursSchedule": [] }, "geometry": { "type": "Point", "coordinates": [-122.003917771292, 47.697428787792] } },
{ "type": "Feature", "id": 415, "properties": { "assignedMonWed": false, "assignedTuesThurs": false, "MonWebSchedule": [], "TuesThursSchedule": [] }, "geometry": { "type": "Point", "coordinates": [-122.023083481977, 47.6673715172954] } },
{ "type": "Feature", "id": 564, "properties": { "assignedMonWed": false, "assignedTuesThurs": false, "MonWebSchedule": [], "TuesThursSchedule": [] }, "geometry": { "type": "Point", "coordinates": [-121.985338674036, 47.7022520412864] } },
{ "type": "Feature", "id": 533, "properties": { "assignedMonWed": false, "assignedTuesThurs": false, "MonWebSchedule": [], "TuesThursSchedule": [] }, "geometry": { "type": "Point", "coordinates": [-122.006075987628, 47.7046529915799] } }
]
};
var patients = {
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": 1, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.997913065484, 47.7195839450159] } },
{ "type": "Feature", "id": 2, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.040368485372, 47.6869829619951] } },
{ "type": "Feature", "id": 3, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.048824870578, 47.7013191572943] } },
{ "type": "Feature", "id": 4, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.071031688833, 47.7449665072182] } },
{ "type": "Feature", "id": 5, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.015643772116, 47.7570578745646] } },
{ "type": "Feature", "id": 6, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.034052201107, 47.7451844538795] } },
{ "type": "Feature", "id": 7, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.032132503429, 47.714347009898] } },
{ "type": "Feature", "id": 8, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.04547396506, 47.6893615399414] } },
{ "type": "Feature", "id": 9, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.068392436109, 47.6832920975517] } },
{ "type": "Feature", "id": 10, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.984992007383, 47.6735499724146] } },
{ "type": "Feature", "id": 11, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.036957923548, 47.7530782996456] } },
{ "type": "Feature", "id": 12, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.066388808463, 47.7044949126107] } },
{ "type": "Feature", "id": 13, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.065430673396, 47.6936482443828] } },
{ "type": "Feature", "id": 14, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.005818300822, 47.6841991471929] } },
{ "type": "Feature", "id": 15, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.013423871487, 47.7267318710591] } },
{ "type": "Feature", "id": 16, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.991867919251, 47.6730825754312] } },
{ "type": "Feature", "id": 17, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.075035596741, 47.6825165944555] } },
{ "type": "Feature", "id": 18, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.02704005476, 47.7359799244539] } },
{ "type": "Feature", "id": 19, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.069711259786, 47.7582015474685] } },
{ "type": "Feature", "id": 20, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.00363876449, 47.696504598779] } },
{ "type": "Feature", "id": 21, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.027230559197, 47.7463213935986] } },
{ "type": "Feature", "id": 22, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.020278138182, 47.7565298732157] } },
{ "type": "Feature", "id": 23, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.041675234009, 47.7605001277431] } },
{ "type": "Feature", "id": 24, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.059226260036, 47.6842719244522] } },
{ "type": "Feature", "id": 25, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.99079261183, 47.6745521677151] } },
{ "type": "Feature", "id": 26, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.998635617356, 47.741838009627] } },
{ "type": "Feature", "id": 27, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.983315341491, 47.7529779792852] } },
{ "type": "Feature", "id": 28, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.996663951226, 47.6982171986915] } },
{ "type": "Feature", "id": 29, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.069967005873, 47.7499047491259] } },
{ "type": "Feature", "id": 30, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.032190749586, 47.757600296338] } },
{ "type": "Feature", "id": 31, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.992400954596, 47.6827035465803] } },
{ "type": "Feature", "id": 32, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.063594482899, 47.7247082989927] } },
{ "type": "Feature", "id": 33, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.075535378044, 47.6772273648534] } },
{ "type": "Feature", "id": 34, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.994743747063, 47.7387867524387] } },
{ "type": "Feature", "id": 35, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.041914349392, 47.6784018054591] } },
{ "type": "Feature", "id": 36, "properties": {}, "geometry": { "type": "Point", "coordinates": [-121.985731516363, 47.7173223782956] } },
{ "type": "Feature", "id": 37, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.066638385437, 47.7276733135571] } },
{ "type": "Feature", "id": 38, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.004119609955, 47.6871862520471] } },
{ "type": "Feature", "id": 39, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.042819720217, 47.699794194686] } },
{ "type": "Feature", "id": 40, "properties": {}, "geometry": { "type": "Point", "coordinates": [-122.029705426136, 47.72521276633] } }
]
};
//Check to see if there is enough resources for the number of patients (Half the patients assigned per day combo, with a resource having no more than 4 patients per day.
if (Math.ceil(patients.features.length) / 2 / 4 > resources.features.length) {
console.log('Not enough resources to see all patients');
}
function schedulePatients() {
var currentDayCombo = 'MonWeb';
var midPoint = Math.ceil(patients.features.length / 2);
var assigned = 0;
for (var i = 0; i < patients.features.length; i++) {
var p = patients.features[i];
var cluster = getThreeClosest(i);
//Add patient to the cluster.
cluster.push(p);
if (assigned >= midPoint) {
currentDayCombo = 'TuesThurs';
}
//Loop through resources and assign clusters.
var resource = getClosestUnassignedResource(cluster, currentDayCombo);
if (resource === null) {
//No available resource left. Handle this some how. Possibly throw an alert or log to console.
cluster.forEach(c => {
console.log(`No resources left, patient ${c.id} unassigned.`)
});
} else {
assigned += cluster.length;
}
}
//Scheduling complete. Recommend using route optimizate for the patients of each resource to get the most efficient order to visit those patients.
//Creating a table of the schedule.
var html = ['<table><tr><td>Resource ID</td><td>Mon/Wed Patient IDs</td><td>Tues/Thurs Patient IDs</td></tr>'];
resources.features.forEach(r => {
html.push(`<tr><td>${r.id}</td><td>${r.properties.MonWebSchedule.join(',')}</td><td>${r.properties.TuesThursSchedule.join(',') }</td></tr>`);
});
html.push('</table>');
document.getElementById('output').innerHTML = html.join('');
}
function getThreeClosest(patientIdx) {
//Create an array of distances and indices for each patient.
var distances = [];
var p = patients.features[patientIdx];
//Look at remaining unassigned patients and calculate distances.
for (var k = patientIdx + 1; k < patients.features.length; k++) {
var p2 = patients.features[k];
if (!p2.properties.assigned) {
distances.push({
distance: haversine(p.geometry.coordinates, p2.geometry.coordinates),
idx: k //Capture the index of the patient to do a quick lookup later.
});
}
}
//Sort the distances in ascending order.
distances.sort((a, b) => { return a.distance - b.distance });
//Possible there are less than 3 patients remaining.
var grab = Math.min(3, distances.length);
var closest = [];
for (var i = 0; i < grab; i++) {
closest.push(patients.features[distances[i].idx]);
}
return closest;
}
function getClosestUnassignedResource(cluster, dayCombo) {
//Calculate the average position of the cluster.
var sumLon = cluster.reduce((acc, curr) => acc + curr.geometry.coordinates[0], 0);
var sumLat = cluster.reduce((acc, curr) => acc + curr.geometry.coordinates[1], 0);
var origin = [sumLon / cluster.length, sumLat / cluster.length];
var currentDayProp = (dayCombo === 'MonWeb') ? 'assignedMonWed' : 'assignedTuesThurs';
//Create an array of distances and indices for each resource.
var closest = null;
var minDist = Infinity;
//Loop through resources and assign clusters.
for (var j = 0; j < resources.features.length; j++) {
var r = resources.features[j];
//Check that resource is not assigned.
if (!r.properties[currentDayProp]) {
var d = haversine(origin, r.geometry.coordinates);
if (d < minDist) {
minDist = d;
closest = r;
}
}
}
//Assign resource.
if (closest) {
closest.properties[currentDayProp] = true;
cluster.forEach(c => {
c.properties.dayCombo = dayCombo;
c.properties.resource = closest.id;
closest.properties[dayCombo + 'Schedule'].push(c.id);
});
}
return closest;
}
function haversine(p1, p2) {
var lat1 = p1[1] / 180.0 * Math.PI;
var lon1 = p1[0] / 180.0 * Math.PI;
var lat2 = p2[1] / 180.0 * Math.PI;
var lon2 = p2[0] / 180.0 * Math.PI;
var R = 6372.8; // km
var dLat = lat2 - lat1;
var dLon = lon2 - lon1;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.asin(Math.sqrt(a));
return R * c;
}
</script>
</body>
</html>

Related

How to convert city level geojson to country level geojson

I have a GeoJson file that represent a country however that file doesn't contain the polygon representing the country itself rather a list of cities and their respective geometries.
Is there a way to merge all those city geometries into a single polygon representing the country.
Example: Germany
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [...]
},
"properties": {
"name": "Berlin",
"description": "test"
}
},
{
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [...]
},
"properties": {
"name": "Hamburg",
"description": "test"
}
},
...
]
Is there a method to extract Germany polygon from its cities?
Thanks

How to extract point coordinates of lines from MGLShapeSource in Mapbox Swift

I plot lines correctly on a Mapbox iOS map using a geojson files I have created.
I read the geojson file and associate it to a shape like so:
shapeFromGeoJSON = try? MGLShape(data: jsonData, encoding: String.Encoding.utf8.rawValue)
Then I create a linesSource and a linesLayer and then I show the linesLayer on the map correctly:
linesSource = MGLShapeSource(identifier: "polyline", shape: shapeFromGeoJSON, options: nil)
linesLayer = MGLLineStyleLayer(identifier: "polyline", source: linesSource)
mapView.style?.addSource(linesSource)
mapView.style?.addLayer(linesLayer)
Inside the geojson file, each line is represented by a "geometry" with multiple "coordinates" of type "LineString".
I need to do some special processing that involves knowing all the coordinates of the points making the lines.
Question: how can I get all the multiple point coordinates of each LineString from linesSource?
Here's my geojson file for testing:
{
"features":
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
-9.316333,
38.680709
],
"type": "Point"
},
"id": "1234"
},
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
-9.31687,
38.680772
],
[
-9.317531,
38.679794
],
[
-9.318001,
38.679196
],
[
-9.318436,
38.678612
],
[
-9.318592,
38.678354
]
],
"type": "LineString"
},
"id": "567"
},
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
-9.316057,
38.680838
],
"type": "Point"
},
"id": "89"
},
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
-9.316719,
38.680715
],
"type": "Point"
},
"id": "1011"
},
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
-9.315931,
38.681066
],
"type": "Point"
},
"id": "1213"
}
],
"type": "FeatureCollection"
}
From my experiences with MapBox, not everything is easily user accessible. It might just be easiest to make a simple struct, decode the data into that, and access the coordinates from there.

How to make heatmap with leaflet and geoJson file data

I have a heat map in Qgis and with the qgis2web plugin I generated the preview page. However, it showed nothing on the page. I did some research, did some tests, but I'm not able to generate a leaflet heat map with data from a geoJson file. Can someone help me?
Follows the generated code.
Thanks.
Follows model of the file with the data:
var json_dados_centroids = {
"type": "FeatureCollection",
"name": "dados",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "VINC": "6" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489342251452101, -23.66511439561954 ] ] } },
{ "type": "Feature", "properties": { "VINC": "1" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489439968502133, -23.665105357310239 ] ] } },
{ "type": "Feature", "properties": { "VINC": "0" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489537955929293, -23.665096808547311 ] ] } },
{ "type": "Feature", "properties": { "VINC": "0" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.559119794597237, -23.653810429167063 ] ] } },
{ "type": "Feature", "properties": { "VINC": "0" }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -46.489140609582904, -23.665134091460494 ] ] } }
]
}
var centroids_hm = geoJson2heat(json_dados_centroids, 'VINC');
var layer_centroids = new L.heatLayer(centroids_hm, {
attribution: '',
interactive: true,
radius: 30,
max: 11917,
minOpacity: 1,
gradient: {0: '#fcfdbf', 0.019608: '#fcf4b6', 0.039216: '#fdebac', 0.0588942: '#fde2a3', 0.078431: '#fed89a',
0.098039: '#fecf92', 0.117647: '#fec68a', 0.137255: '#febd82', 0.156863: '#feb47b',
0.176471: '#feaa74', 0.196078: '#fea16e', 0.215686: '#fd9869', 0.235294: '#fc8e64',
0.254902: '#fb8560', 0.27451: '#f97b5d', 0.294118: '#f7725c', 0.313725: '#f4695c',
0.333333: '#f1605d', 0.352941: '#ec5860', 0.372549: '#e75263', 0.392157: '#e04c67',
0.411765: '#d9466b', 0.431373: '#d2426f', 0.45098: '#ca3e72', 0.470588: '#c23b75',
0.490196: '#ba3878', 0.509804: '#b2357b', 0.529412: '#aa337d', 0.54902: '#a1307e',
0.568627: '#992d80', 0.588235: '#912b81', 0.607843: '#892881', 0.627451: '#812581',
0.647059: '#792282', 0.666667: '#721f81', 0.686275: '#6a1c81', 0.705882: '#621980',
0.72549: '#5a167e', 0.745098: '#52137c', 0.764706: '#4a1079', 0.784314: '#420f75',
0.803922: '#390f6e', 0.823529: '#311165', 0.843137: '#29115a', 0.862745: '#21114e',
0.882353: '#1a1042', 0.901961: '#140e36', 0.921569: '#0e0b2b', 0.941176: '#090720',
0.960784: '#050416', 0.980392: '#02020b', 1: '#000004'}
}
);
layer_centroids.setData(centroids_hm);
function geoJson2heat(geojson, weight) {
return geojson.features.map(function(feature) {
return [
feature.geometry.coordinates[1],
feature.geometry.coordinates[0],
feature.properties[weight]
];
});
}
map.addLayer(layer_centroids);
First, your GeoJSON contains geometry in the form of multipoints. However, in 'geoJson2heat' you parse points.
I would advise converting the geometry to point (in the 'json_dados_centroids' variable).
Also layer_centroids.setData(centroids_hm) needs to be removed.
Something like that
var json_dados_centroids = {
"type": "FeatureCollection",
"name": "dados",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [{
"type": "Feature",
"properties": {
"VINC": "6"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489342251452101,
-23.66511439561954
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "1"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489439968502133,
-23.665105357310239
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "0"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489537955929293,
-23.665096808547311
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "0"
},
"geometry": {
"type": "Point",
"coordinates": [-46.559119794597237,
-23.653810429167063
]
}
},
{
"type": "Feature",
"properties": {
"VINC": "0"
},
"geometry": {
"type": "Point",
"coordinates": [-46.489140609582904,
-23.665134091460494
]
}
}
]
}
var centroids_hm = geoJson2heat(json_dados_centroids, 'VINC');
var layer_centroids = new L.heatLayer(centroids_hm, {
attribution: '',
interactive: true,
radius: 30,
max: 11917,
minOpacity: 1,
gradient: {
0: '#fcfdbf',
0.019608: '#fcf4b6',
0.039216: '#fdebac',
0.0588942: '#fde2a3',
0.078431: '#fed89a',
0.098039: '#fecf92',
0.117647: '#fec68a',
0.137255: '#febd82',
0.156863: '#feb47b',
0.176471: '#feaa74',
0.196078: '#fea16e',
0.215686: '#fd9869',
0.235294: '#fc8e64',
0.254902: '#fb8560',
0.27451: '#f97b5d',
0.294118: '#f7725c',
0.313725: '#f4695c',
0.333333: '#f1605d',
0.352941: '#ec5860',
0.372549: '#e75263',
0.392157: '#e04c67',
0.411765: '#d9466b',
0.431373: '#d2426f',
0.45098: '#ca3e72',
0.470588: '#c23b75',
0.490196: '#ba3878',
0.509804: '#b2357b',
0.529412: '#aa337d',
0.54902: '#a1307e',
0.568627: '#992d80',
0.588235: '#912b81',
0.607843: '#892881',
0.627451: '#812581',
0.647059: '#792282',
0.666667: '#721f81',
0.686275: '#6a1c81',
0.705882: '#621980',
0.72549: '#5a167e',
0.745098: '#52137c',
0.764706: '#4a1079',
0.784314: '#420f75',
0.803922: '#390f6e',
0.823529: '#311165',
0.843137: '#29115a',
0.862745: '#21114e',
0.882353: '#1a1042',
0.901961: '#140e36',
0.921569: '#0e0b2b',
0.941176: '#090720',
0.960784: '#050416',
0.980392: '#02020b',
1: '#000004'
}
});
function geoJson2heat(geojson, weight) {
return geojson.features.map(function(feature) {
return [
feature.geometry.coordinates[1],
feature.geometry.coordinates[0],
feature.properties[weight]
];
});
}
map.addLayer(layer_centroids);
P.S. "VINC": "6" - strange value of intensity.

circle-radius bigger with a data-driven property

I want to paint bigger circles when the data property--total is bigger.
deviceGeojson is the data, I think I should write some code within 'circle-raduis' but I dont know how to get it.
var deviceGeojson = [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [144.961027, -37.795947]
},
"properties": {
"PC": 100,
"Mobile": 200,
"Laptop": 300,
"total": 600
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [144.960205, -37.797596]
},
"properties": {
"PC": 100,
"Mobile": 200,
"Laptop": 300,
"total": 600
}
}
];
map.addLayer({
'id': 'test',
'type': 'circle',
'source' : {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": deviceGeojson,
}
},
'paint':{
'circle-color': '#00b7bf',
'circle-radius': {
},
'circle-stroke-width': 1,
'circle-stroke-color': '#333',
}
});
I dont know how to write the code within 'paint',Thank you
You can use expressions to get value of a property:
"circle-radius": ['get', 'total']
Or if you were to device the value of total property by say 20, you'd do something like this:
"circle-radius": ['/', ['get', 'total'], 20],
You can read more about expressions here: https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions

Is there a possibility to give a marker an id or class within the "properties" of the marker and to use this ID/Class in a click function afterwards?

map.on('load', function () {
map.addSource("markers", {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [4.869929, 50.465887]
},
"properties": {
"marker-symbol": "pin-2"
"id or class?": ".."
}
}]
}
});
What you wrote on your code is good, you can add any properties in the properties part.
Please refer to my example : http://codepen.io/poupi/pen/qZeXdK