How to use clusterProperties in mapbox-gl - mapbox-gl-js

I built a mapbox-gl js (v0.52) map where points are getting aggregated into clusters; much like in the clusters example from mapbox page.
The cluster color needs to be a function of an aggregation of individual points properties: For simplicity, say each point has a status property, which determine its color, and the color of a cluster should just be the color corresponding to the max of each of its points' status values.
Example geojson data would look like:
const geoData = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {
id: 'fakeid11',
status: 20,
},
geometry: {
type: 'Point',
coordinates: [-151.5129, 63.1016, 0]
}
},
{
type: 'Feature',
properties: {
id: 'fakeid22',
status: 10,
},
geometry: {
type: 'Point',
coordinates: [-150.4048, 63.1224, 105.5]
}
}
]
};
I am trying to use clusterProperties to compute the aggregation as described in the api docs, similar to this example from the source code, to create this layer:
map.addSource('earthquakes', {
type: 'geojson',
data: geoData,
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points on
clusterRadius: 50, // Radius of each cluster when clustering points (defaults to 50)
clusterProperties: {
status: ['max', ['get', 'status']]
}
});
This snippet is exactly like the clusters example from mapbox page, just replacing the data by my static 2-elements copy, and adding clusterProperties.
However this throws a validation error (a bit mangled from the minified mapbox-gl version):
Error: sources.earthquakes: unknown property "clusterProperties"
at Object.Jr [as emitValidationErrors] (modules.js?hash=34588b9e7240c1a9b3fd2f8685e299d9dbbb40d9:504146)
at De (modules.js?hash=34588b9e7240c1a9b3fd2f8685e299d9dbbb40d9:504150)
at i._validate (modules.js?hash=34588b9e7240c1a9b3fd2f8685e299d9dbbb40d9:504150)
What is wrong with this clusterProperties usage? It would seem it is simply refusing to validate this property. Note that the map works ok (without the status aggregated property of course) if I comment the last 3 lines that set it.

I found the answer days later: The version of react-map-gl we were using had dependency on mapbox-gl ~0.52.0, which did not yet support clusterProperties. Support for these aggregated properties comes in mapbox-gl 0.53. (And since the react wrapper uses undocumented features of mapbox-gl, they depend on exact versions at patch level). This was confirmed by the react library developers.
Now react-map-gl 4.0.14 is released and it works ok with clusterProperties, using internally mapbox-gl 0.53.0.

Related

Mapbox GL JS Change the fill-color's property value based on an expression

I have a chloropleth map with a geojson data source backing it. The data set contains data for each country for two different years, with the fill color based on one of the properties in the JSON. Here is my addLayer code currently, which works fine.
map.addLayer({
id: 'emissions',
type: 'fill',
source: {
type: 'geojson',
data: './data.json'
},
paint: {
'fill-color': {
property: 'total_2014',
type: 'exponential',
base: 0.99999,
stops: [
[3, "hsl(114, 66%, 53%)"],
[2806634, "hsl(0, 64%, 51%)"]
]
},
'fill-opacity': 1
}
});
I would like to be able to programatically switch the json property on which the fill color is based, and an expression seems the obvious way to do so, however the following code fails with the error layers.emissions.paint.fill-color.property: string expected, array found.
...
paint: {
'fill-color': {
property: ['get', propName], // propName var is e.g. 'total_2014'
type: 'exponential',
base: 0.99999,
stops: [
[3, "hsl(114, 66%, 53%)"],
[2806634, "hsl(0, 64%, 51%)"]
]
},
'fill-opacity': 1
}
...
Is there a way to achieve what I'm aiming for? I'm very new to using Mapbox GL JS, so apologies if this is a basic question.
Just in case anyone else happens across this requirement, I found a workaround by updating the map's style property directly. This isn't an exact answer since the approach doesn't use expressions, but the performance of mapbox diffing source and applying the changes is very fast and meets my requirements.
function loadDataForYear(year) {
const style = map.getStyle();
style.layers.find(({ id }) => id === "emissions").paint['fill-color']['property'] = 'total_' + year;
map.setStyle(style);
}

Mapbox GL JS interactivity on a Warehouse Floorplan as the base map plausible?

I am trying to figure out if Mapbox GL JS would be applicable or fit for my use case:
My base map example has to be made of several components of a warehouse instead of Geospatial map
I would like to display a clustering layer representing, for example, the number of products stored at a particular shelf/section in the warehouse.
Each box/product in the warehouse would be represented as a pinpoint and while zooming out, collection of products/boxes would be represented as a cluster.
With that said, I'm aware 2) and 3) would be supported in case of a geospatial map but my concern is aimed at an customized non-geospatial map, if applicable.
Geospatial or not, it all boils down to coordinates. Mapbox GL JS expects geographic coordinates, so you just need to carve out a portion of the coordinate range (-180 thru 180 on the x axis, -90 to 90 on the y axis) that will meet your needs, and make sure you have good coordinates for the shelves or wherever you need to visualize clusters.
Here's a codepen that shows a simple mapbox style with only a background and a rectangle. No streets, no rivers, no labels...
const map = (window.map = new mapboxgl.Map({
container: "map", // container ID
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: {
version: 0,
name: "Foo",
sources: {
"building-outline": {
type: "geojson",
data: {
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
[
[0, 0],
[60, 0],
[60, 30],
[0, 30],
[0, 0]
]
]
}
}
}
},
layers: [
{
id: "background",
type: "background",
paint: {
"background-color": "steelblue"
}
},
{
id: "building-fill",
type: "fill",
source: "building-outline",
paint: {
"fill-color": "#ccc"
}
},
{
id: "building-line",
type: "line",
source: "building-outline"
}
]
}, // style URL
center: [30,20], // starting position [lng, lat]
zoom: 2 // starting zoom
}));
https://codepen.io/chriswhong/pen/XWqpPXN
You can also build your own style in Mapbox Studio.

Nothing showing when plotting vector tile layer from Arcgis server on MapBox GL JS map

I'm trying to plot a supplied vector tile layer onto a map using MapBox GL JS. I've followed the documentation here but nothing apart from the basic map is being output and there are no console errors. In the browser's Network tab I can see lots of .pbf requests being returned with data so it would seem that the endpoint is passing data back, but I don't know how to determine what the problem is in plotting that data onto the map.
The code is as follows:
mapboxgl.accessToken = '[my access token]';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
zoom: 6,
center: [-0.118092, 51.509865]
});
map.once("load", () => {
map.addSource("ncn", {
type: "vector",
tiles: [
"https://tiles.arcgis.com/tiles/1ZHcUS1lwPTg4ms0/arcgis/rest/services/NCN_Vector_Tile_Package/VectorTileServer/tile/{z}/{y}/{x}.pbf"
]
});
map.addLayer({
id: "ncn-lines",
type: "line",
source: "ncn",
"source-layer": "NCN_Vector_Tile_Package",
"layout": {
"visibility": "visible"
},
'paint': {
'line-color': 'rgb(255, 0, 0)'
}
});
});
I am fairly sure that the type should be line (rather than fill) as the data is supposed to contain route lines rather than vector shapes.
I don't have access to the Arcgis server so can't see how anything is configured at that side. Can anyone suggest what might be wrong here and/or how to debug?
It looks like the value for source-layer is not correct - it should be NCN_2020. Here's a demo showing it working: https://jsbin.com/xunuhibuki/1/edit?html,output
How do you get that value? I'm not quite sure the best way, but the way I found: add ?f=html to your vector tile layer like this: https://tiles.arcgis.com/tiles/1ZHcUS1lwPTg4ms0/arcgis/rest/services/NCN_Vector_Tile_Package/VectorTileServer/?f=html then click "Styles" link at the bottom which gives you an example of how to construct your map.addLayer() commands in your mapboxgl code.

How to display stacked locations in a mapbox layer?

I'm very new to mapboxgl and trying to create a map that allows users to filter points over time. I customized this tutorial with my data to get the initial map up and running:
https://docs.mapbox.com/help/tutorials/show-changes-over-time/
everything worked really well until I loaded my data and realized that many points shared the same coordinates.
Some googling revealed this answer:
Multiple markers - Same coordinates
Both the idea of using offsets or spidering feel like reasonable solutions. Unfortunately, it is not clear to me how to apply those to the data as I loaded it. I believe that the relevant portion of the script is:
map.on('load', function() {
map.addLayer({
id: 'year',
type: 'circle',
source: {
type: 'geojson',
data: './grandpascan.geojson' // replace this with the url of your own geojson
},
paint: {
'circle-radius': [
'interpolate',
['linear'],
['number', ['get', 'Pages']],
0, 4,
5, 24
],
'circle-color': [
'interpolate',
['linear'],
['number', ['get', 'Pages']],
0, '#2DC4B2',
1, '#3BB3C3',
2, '#669EC4',
3, '#8B88B6',
4, '#A2719B',
5, '#AA5E79'
],
'circle-opacity': 0.8
},
If that is correct, how can I identify those points for styling? They do not appear to be markers that can easily addressed in CSS, although I may be completely misunderstanding how this all fits together.
thanks!
The code here is creating a layer within the map, of type circle. You could offset locations using the circle-translate property.
If you want to use CSS transforms to offset markers, you should create them as Marker objects which exist in HTML, outside the map.

Charts JS xAxes is not shared by data

I am visualizing multiple dataset on a Chart JS. Whenever I enable/disable a dataset. This dataset would create an instance of its data. On my case, it is xAxes.
and when I enable another dataset:
I want to disable creating another instance of xAxes and every it would share the same fixed axes.
xAxes: [{
type: 'time',
distribution: 'linear',
ticks: {
source: 'data'
},
time: {
parser: 'HH:mm:ss',
unit: 'hour',
unitStepSize: 1,
//min: '00:00:00',
//max: '23:59:59'
}
}],
Update: 1
I used round: 'hour', to include in xAxes.time. It looks fixed. However, I don't know why points always go to corners and spread freely with time as shown:
Update: 2
I removed
/*ticks: {
source: 'data'
},*/
Now it is scaled.
Removing the
ticks: {
source: 'data'
},
made the graph scaled with time.