How filter features by status - mapbox

I want to show in my map in a cluster layer filtering by if is opened or not. How can do it? Should I create two layers?
One with filter: filter["has", "opened"] and other with filter: filter["!", ["has", "opened"]]?
export const clusterLayerOpened = {
id: "clusters",
type: "circle",
source: "earthquakes",
filter: ["has", "opened"],
paint: {
"circle-color": [ "step", ["get", "opened"], "#51bbd6",100,"#f1f075",750,"#f28cb1", ],
"circle-radius": ["step", ["get", "opened"], 20, 100, 30, 750, 40],
},
};
export const clusterLayerNoOpened = {
id: "clusters",
type: "circle",
source: "earthquakes",
filter: ["!", ["has", "opened"]],
paint: {
"circle-color": [ "step", ["get", "opened"], "#51bbd6",100,"#f1f075",750,"#f28cb1", ],
"circle-radius": ["step", ["get", "opened"], 20, 100, 30, 750, 40],
},
};
This is my geojson:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"id": "ak16994521",
"mag": 2.3,
"time": 1507425650893,
"felt": null,
"tsunami": 0,
"opened": true
},
"geometry": {
"type": "Point",
"coordinates": [-151.5129, 63.1016, 0.0]
}
},
{
"type": "Feature",
"properties": {
"id": "ak16994519",
"mag": 1.7,
"time": 1507425289659,
"felt": null,
"tsunami": 0,
"opened": false
},
"geometry": {
"type": "Point",
"coordinates": [-150.4048, 63.1224, 105.5]
}
}
]
}

It's not necessary to create two separate layers to filter by whether the a point has been opened or not. Here is some code showing how to add a layer which displays all points with the property "opened": true, and hides all points with "opened": false:
map.addLayer({
'id': 'opened',
'type': 'circle',
'source': 'points',
'paint': {
'circle-radius': 10,
'circle-opacity': ["match", ["to-string", ["get", "opened"]], 'true', 1 , 'false', 0, 0]
}
});
To instead show all points with the property "opened": false, you can switch the 'circle-opacity' expression to read:
["match", ["to-string", ["get", "opened"]], 'true', 0 , 'false', 1, 0]
This code makes use of a few Mapbox expressions. I've linked the documentation to each relevant expression here: match, to-string, and get.
Here is a JSFiddle where two layers are added to the map: https://jsfiddle.net/hpkzrm4n/. The points with "opened": true are shown in red and the points with "opened": false are shown in blue. Note that you will need to add your own Mapbox access token where indicated in order to view the results. Here is a screenshot, as a preview:

Related

Flutter: Mapbox Cluster and click event on unclustered point

I use this code to have clusters:
for (int index = 0; index < punti_di_interesse.length; index++) {
var point = {
"type": "Feature",
"id": index,
"properties": {
"id": index,
"name": punti_di_interesse[index].name,
'descrizione': punti_di_interesse[index].descrizione,
},
"geometry": {
"type": "Point",
"coordinates": [
punti_di_interesse[index].longitude,
punti_di_interesse[index].latitude,
0.0
]
}
};
allSymbolGEOJSON.add(point);
}
var allPoints = {
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {"name": "urn:ogc:def:crs:OGC:1.3:CRS84"}
},
"features": allSymbolGEOJSON,
};
var source = await controller.addSource(
"earthquakes",
GeojsonSourceProperties(
data: allPoints,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50,
),
);
await controller.addLayer(
"earthquakes",
"clusters",
CircleLayerProperties(circleColor: [
Expressions.step,
[Expressions.get, 'point_count'],
'#51bbd6',
100,
'#f1f075',
750,
'#f28cb1'
], circleRadius: [
Expressions.step,
[Expressions.get, 'point_count'],
20,
100,
30,
750,
40
]),
);
await controller.addLayer(
"earthquakes",
"cluster-count",
SymbolLayerProperties(
textField: [Expressions.get, 'point_count_abbreviated'],
textSize: 12,
));
await controller.addSymbolLayer(
"earthquakes",
"unclustered-point",
SymbolLayerProperties(
textField: [Expressions.get, "name"],
textHaloWidth: 1,
textSize: 12.5,
textHaloColor: '#ffffff',
textOffset: [
Expressions.literal,
[0, 2]
],
iconImage: "assets/icona_stato-rosso.png",
iconSize: 2,
iconAllowOverlap: true,
textAllowOverlap: true,
textColor: '#000000',
textHaloBlur: 1,
),
filter: [
'!',
['has', 'point_count']
],
enableInteraction: true,
);
Under my custom iconImage I see a black dot, how can this be removed?
In addition, I need that when the iconImage is clicked, a modal opens with information that is contained in the data structure in "properties"
I have tried using controller.onFeatureTapped.add(onFeatureTap);
but in the onFeatureTap function how do I access properties from featureId, point or latLng?
Am I doing something wrong?
For anyone stumbling across this problem, i figured out the following:
the CircleLayerProperties set a standard #000000 color value for circles. This together with the fact that you recolor everything with a point_count (which singular symbols do not have) leads to the CircleLayer drawing a black circle.
To fix this, use expressions to set the circleOpacity to 0.0 for all the unclustered symbols like so:
circleOpacity: [
"case",
["has", "point_count"],
1.0,
[
"!",
["has", "point_count"]
],
0.0,
1.0
]
Edit:
To your second point, i can shed some light there too:
in general, your are on the right path, but let me put together a more complete example for everyone:
First, define your GeoJson with an id:
var point = {
"type": "Feature",
"id": index,
"properties": {
...
},
"geometry": {
...
}
};
Then, add a listener to your MapController:
mapController.onFeatureTapped.add((id, point, coordinates) {
print(id);
}
The print(id) in the above handler will then print the id you defined in your GeoJson (or the id of a cluster, if applicable).
Hope that helps someone!

mapbox-gl-draw data driven style on LineString

I use Mapbox GL Draw and I want to customize the fill color of my LineString Feature using data driven.
I have a set userProperties: true and I have a property prefixed with user_ .
here is my style configuration :
{
id: "gl-draw-linestring-fill-inactive",
type: "fill",
filter: ["all", ["==", "active", "false"], ["==", "$type", "LineString"],["!=", "mode", "static"],],
paint: {
"fill-color": [
"case",
["==", ["get", "user_type"], "meetingRoom"],
"#00ff00",
["==", ["get", "user_type"], "notUsed"],
"#00ff00",
"#ff0000",
],
"fill-outline-color": "#3bb2d0",
"fill-opacity": 0.4,
},
}
and my feature :
{
"type": "Feature",
"id": "ROOM-floor-1-1",
"properties": {
"parentId": "floor-1",
"user_type": "notUsed"
},
"geometry": {
"coordinates": [
[2.334699793548168, 48.85506145052912],
[2.3334337908935083, 48.85340956808176],
[2.3360301692199243, 48.85314130852265],
[2.3368884761040363, 48.85547088304844],
[2.3368884761040363, 48.85547088304844],
[2.334699793548168, 48.85506145052912]
],
"type": "LineString"
}
}
Feature is always paint with default value (#ff0000). It should be #00ff00 in this example.
In the same application I use the same property (user_type) to set Line color on Polygon and it works fine !
Any Idea ?
I just figured out how to do it, I'm putting the answer here in case other people make the same mistake as me.
I misunderstood the mapbox documentation for using my own properties in data driven styles.
If you want to use a property named myProp from your feature, you have to prefix it with user_ but only in the style rule.
For example:
{
"type": "Feature",
"id": "1",
"properties": {
"myProp": "myValue"
},
"geometry": {
"coordinates": [...],
"type": "LineString"
}
}
And:
{
id: 'my-rule',
type: 'line',
filter: ['all', ['==', 'active', 'false'], ['==', '$type', 'LineString'],['!=', 'mode', 'static']],
paint: {
'line-color': [
'match', ['get', 'user_myProp'], // get the property
'myValue', '#00ff00',
'#ff0000']
},
}
My mistake was to add prefix user_ in the style rule AND the feature properties.
I dont really understand, why you are using "type: fill" in your style configuration for a linestring. You shoud be using the line-specific style properties as shown in this mapbox example https://docs.mapbox.com/mapbox-gl-js/example/data-driven-lines/
Also, since you are refering to a property in your data driven styling, there is no need to use the "case". you can simply use "match"
So it would rather be:
{
id: 'gl-draw-linestring-fill-inactive',
type: 'line',
filter: ['all', ['==', 'active', 'false'], ['==', '$type', 'LineString'],['!=', 'mode', 'static']],
paint: {
'line-color': [
'match', ['get', 'user_type'], // get the property
'meetingRoom', '#00ff00',
'notUsed', '#00ff00',
'#ff0000'],
'line-width': 3,
},
}
By the way: ids on feature level should be integers or strings, that can be cast in as integers:
https://github.com/mapbox/mapbox-gl-js/issues/7632

How to set Mapbox heatmap color by data value?

I would like to display a heatmap of Bluetooth scans such that scans with strong signals appear green and scans with weak signal strength appear red. I have tried playing with all of the Mapbox heatmap properties (weight, intensity, radius, color, opacity) and have not been able to achieve this effect. Is there any advice on how to do this?
GeoJSON data format ("rssi" is the signal strength)
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-79.92068447220412,
43.259061411756505
]
},
"properties": {
"name": "heatmap",
"rssi": "55"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-79.92068446786702,
43.25906141184957
]
},
"properties": {
"name": "heatmap",
"rssi": "59"
}
},
...
]
}
Heatmap layer so far
map.addLayer({
id: 'heatmap_heatmap_layer_id',
type: 'heatmap',
source: 'heatmap_source_id',
maxzoom: 24,
paint: {
'heatmap-weight': {
property: 'rssi',
type: 'exponential',
stops: [
[0, 1],
[120, 10]
]
},
'heatmap-intensity': {
stops: [
[currentZoom, 1],
[24, 2]
]
},
'heatmap-color': [
'interpolate',
['linear'],
['heatmap-density'],
0, 'rgba(240,29,29,0)',
0.2, 'rgba(198,0,12,1)',
0.4, 'rgba(32,43,222,1)',
0.7, 'rgba(1,1,1,1)',
1.0, 'rgba(200,144,153,1)'
],
'heatmap-radius': {
stops: [
[currentZoom, 20],
[middleZoom, 30]
]
},
'heatmap-opacity': {
default: 1,
stops: [
[currentZoom, 1],
[24, 1]
]
},
}
});
This one's a bit tricky, as heatmaps automatically blend and add points in close proximity with one another. This means that a single point of strong signal will appear the same color, as many points of weak signal close together.
You may be better off visualizing the points as discrete circles, colored by RSSI.

Styling markers from GeoJSON based on non-string feature properties in Mapbox

as the title suggests, I would like to style some markers stored in a GeoJSON file. In each feature, I saved a "rotate": some_integer, based on how I want to rotate the marker.
In the stylejson in Mapbox I wrote the following style for the markers:
{
"id": "markers_test",
"type": "symbol",
"source": "markers_test",
"layout": {
"symbol-placement": "point",
"icon-image": "marker_{style_id}",
"icon-rotate": "{rotate}",
"text-field": "{name}",
"text-font": ["Open Sans Semibold"],
"text-anchor": "top-left",
"text-padding": 20,
"text-size": 10,
"text-optional": true
},
"paint": {
"text-color": "#dddddd",
"text-halo-color": "#0000ff",
"text-halo-width": 1,
"text-halo-blur": 1
}
}
Setting the correct name and icon works flawlessly, but as soon as I try to set up rotate, it crashes saying the value should be a number, not a string. Only - how do i reference a property of a feature outside a string?
The {token} syntax only works for text-field and icon-image. For all other properties you must use the property function syntax.
{
"id": "markers_test",
"type": "symbol",
"source": "markers_test",
"layout": {
"symbol-placement": "point",
"icon-image": "marker_{style_id}",
"icon-rotate": {
"property": "rotate",
"stops": [[0, 0], [365, 365]]
},
"text-field": "{name}",
"text-font": ["Open Sans Semibold"],
"text-anchor": "top-left",
"text-padding": 20,
"text-size": 10,
"text-optional": true
},
"paint": {
"text-color": "#dddddd",
"text-halo-color": "#0000ff",
"text-halo-width": 1,
"text-halo-blur": 1
}
}

Color only the edge of a circle mapbox gl js

I want to show the outline of a circle on an interactive map (no fill) however, the paint options in mapbox-gl-js seem limited to fill only.
https://www.mapbox.com/mapbox-gl-style-spec/#layers-circle
var styles = [{
"id": 'points',
"interactive": true,
"type": "circle",
"source": "geojson",
"paint": {
"circle-radius": 5,
"circle-color": "#000
},
"filter": ["in", "$type", "Point"]
}, {
"type": "line",
"source": "geojson",
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#000",
"line-width": 2.5
},
"filter": ["in", "$type", "LineString"]
}];
Am i missing something or is this just not possible?
This is now possible, with circle-opacity.
E.g.:
"paint": {
"circle-opacity": 0,
"circle-stroke-width": 1,
"circle-stroke-color": #000
}
Not currently possible. Current top workaround appears to be layering two circles of slightly different sizes.
https://github.com/mapbox/mapbox-gl-js/issues/1713
https://github.com/mapbox/mapbox-gl-style-spec/issues/379
I'm having trouble running custom color 'match' and having opacity controls running simultaneously.
I can get both working, but not at the same time. See code below.
var coorAddresses = [ [ -75.7040473, 45.418067,"Medium" ], [-75.7040473, 45.418067, "Medium"], [-79.32930440000001, 43.7730495, "Unknown"]]
$.getJSON(coodAddresses, function(data) {
for(var itemIndex in data) {
// push new feature to the collection
featureCollection.push({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [data[itemIndex][0], data[itemIndex][1]]
},
"properties": {
"size_by": data[itemIndex][2],
"color_by": data[itemIndex][2]
},
});
}
});
map.on('load', function () {
map.addLayer({
"id": "points",
"type": "circle",
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": featureCollection
}
},
"paint": {
"circle-color": [
'match',
['get', 'size_by'],
'Easy',
'#e4f400',
'Medium',
'#f48a00',
'Unknown',
'#6af400',
/* other */ '#00e4f4'
],
"circle-radius": [
'match',
['get', 'size_by'],
'Easy',
4,
'Medium',
7,
'Unknown',
2,
/* other */ 1000
],
// "circle-opacity": 0, // color does not show if i uncomment these lines
// "circle-stroke-width": 1, // do not get desired 'hollow' circle unless these lines run
}});
Trying to troubleshoot.