Using feature state for icon image, but expression parser is complaining - mapbox-gl-js

I am trying to use a feature state in a case statement for an icon-image with a zoom step, but the parser is refusing to take it, even though there should only be one at a time, am I missing something here?
layout: {
'icon-image': ["case",
["boolean", ["feature-state", "hover"], false],
['step', ['zoom'], 'marker-13', 7, 'marker-17'],
['step', ['zoom'], 'active-marker-13', 7, 'active-marker-17']
],
'icon-size': 1
},
Error received:
Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.
Using 1.6.0.
TIA.

As the message is indicating, you can't have two separate ['zoom'] expressions.
You can solve this by flipping your expression inside out so the zoom is on the outside.
{
layout: {
'icon-image': ['step', ['zoom'],
['case',
["boolean", ["feature-state", "hover"], false],
'marker-13',
'active-marker-13'
],
7,
['case',
["boolean", ["feature-state", "hover"], false],
'marker-17',
'active-marker-17'
]
]
}
}

Related

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

with Mapbox gl draw, after overriding polygon features' circle-radius, how to enlarge the selected vertex's circle-radius?

in the official demo, after creating a polygon in the "draw_polygon" mode, as you direct_select a vertex on the polygon, that vertex will be enlarged. https://docs.mapbox.com/mapbox-gl-js/example/mapbox-gl-draw/
In this js fiddle: https://jsfiddle.net/frankzhang2046/y49mhtjx/16/
after overriding the styling rules for the vertices from line 258-271, the selected vertex under "direct_select" doesn't get enlarged anymore.
Was wondering what API/selector can I use to target the selected vertex to restore the "enlarged when selected" behavior. Thanks.
{
id: "i-guess-id-doesnt-matter",
type: "circle",
filter: [
"all",
["==", "meta", "vertex"],
["==", "$type", "Point"],
["!=", "mode", "static"],
],
paint: {
"circle-radius": 3,
"circle-color": "green"
},
},
update: discovered the ruleset used to target the selected vertex in the unminified MapboxDraw library. Changing the circle-radius bigging solved the problem
{
'id': 'gl-draw-point-active',
'type': 'circle',
'filter': ['all',
['==', '$type', 'Point'],
['!=', 'meta', 'midpoint'],
['==', 'active', 'true']],
'paint': {
'circle-radius': 5,
'circle-color': colorHexVal
}
},

Extrude land in MapBox

I am trying to emphasize the fact that the land is higher than the water in my map so wanted to add an extrusion to the land layer. I thought taking the https://docs.mapbox.com/mapbox-gl-js/example/3d-buildings/ of the one with buildings and changing the layer source to 'land' would work but it didn't. Is this something specific to building layers or am I doing something wrong? Here is my layer definition in my style JSON:
{
"id": "3d-land",
"source": "composite",
"source-layer": "land", # Changed this from building
"filter": ["==", "extrude", "true"],
"type": "fill-extrusion",
"minzoom": 0,
"paint": {
"fill-extrusion-color": "#000",
"fill-extrusion-height": [
"interpolate", ["linear"], ["zoom"],
15, 0,
18.0, 30.0
],
"fill-extrusion-base": [
"interpolate", ["linear"], ["zoom"],
15, 0,
18.0, ["get", "min_height"]
],
"fill-extrusion-opacity": 0.8
}
}
First reason is as the console says, "land" does not exist on source "composite". "land" layer is background layer which is exist separately in the style. You cannot use fill-extrusion for background layer. You may want to use layers which use "compose" source.
The other reason is from filter. "filter": ["==", "extrude", "true"] means filtering if the value of layer's property called "extrude" is "true". land layer doesn't have a property extrude so it's always false.
So, the result of fix would be looks like:
map.addLayer(
{
id: "3d-landcover",
source: "composite",
"source-layer": "landcover",
"type": "fill-extrusion",
"minzoom": 0,
"paint": {
"fill-extrusion-color": "#000",
"fill-extrusion-height": [
"interpolate", ["linear"], ["zoom"],
15, 0,
18.0, 30.0
],
"fill-extrusion-base": [
"interpolate", ["linear"], ["zoom"],
15, 0,
18.0, ["get", "min_height"]
],
"fill-extrusion-opacity": 0.8
}
}
);
As the first reason, all the layers except water should be added as above if you want to make the land higher than water. it's not very effective.

Possible to show a subset of the points using one datalayer with mapbox-gl-js?

I have datalayer consisting of points/ markers in Mapbox-gl-js. When I would like to split my points into categories I can use data-styling, for example like this:
"source-layer": "sf2010",
"paint": {
"circle-radius": {
"base": 1.75,
"stops": [[12, 2], [22, 180]]
},
"circle-color": {
property: 'ethnicity',
type: 'categorical',
stops: [
['White', "#fbb03b"],
['Black', '#223b53'],
['Hispanic', '#e55e5e'],
['Asian', '#3bb2d0'],
['Other', '#ccc']]
}
It seems, using one datalayer, all points (possibly with different category-stylings) will be included on the map. I can style a category of points to not show by giving it a circle-radius of zero, but then it still reacts to clicks etc. Or is there a way to really present a subset of the points using one datalayer?
As Molly suggested, you want a filter:
"source-layer": "sf2010",
"paint": {
"circle-radius": {
"base": 1.75,
"stops": [[12, 2], [22, 180]]
},
"circle-color": {
property: 'ethnicity',
type: 'categorical',
stops: [
['White', "#fbb03b"],
['Black', '#223b53'],
['Hispanic', '#e55e5e'],
['Asian', '#3bb2d0'],
['Other', '#ccc']]
}
},
"filter": ['!==', 'ethnicity','Martian']

mapbox gl data driven style: What does the "value" parameter do?

I'm modifying my icon size based on the current zoom value by using the zoom data to drive the size.
As I understand it, I'm using the property "zoom" to track the zoom value (This is required as per the spec), In the stops I setup each desired "zoom" value and then output the icon-size value for that state (shown by the 0-1 > 3 range). If I don't include the parameter "value": something the code fails. If I set it all to 1, it works fine, so it seems that this value is ignored.
Can anyone explain its actual use?
// Add layer to map populating data from Geojson
map.addLayer({
"id": "seed",
"type": "symbol",
"source": "objects",
"layout": {
"icon-rotation-alignment": "map",
"icon-keep-upright": true,
"icon-rotate": 0,
"icon-image": "{icon}",
"icon-allow-overlap": true,
"icon-size": {
"property": "zoom",
"type": "exponential",
"stops": [
[{ "zoom": 15, "value": 1}, 0.1],
[{ "zoom": 16, "value": 1}, 0.3],
[{ "zoom": 17, "value": 1}, 0.6],
[{ "zoom": 18, "value": 1}, 0.8],
[{ "zoom": 19, "value": 1}, 2],
[{ "zoom": 20, "value": 1}, 3]
]
}
}
});
});
Apologies for the confusion #stuffyjoelab!
There are three types of functions in Mapbox GL:
Zoom functions allow the appearance of a map feature to change with map’s zoom level
Property functions allow the appearance of a map feature to change with its properties.
Zoom-and-property functions allow the appearance of a map feature to change with both its properties and zoom.
(There's more info about these in our style spec docs)
I'm using the property "zoom" to track the zoom value
We define a "property" as being per-feature metadata, a la GeoJSON feature properties. By this definition, zoom is not a property (unless you have a property on your features called zoom)
If you switch to the zoom function syntax by removing "property": "zoom" and replacing the {zoom, value} objects from your stops with numeric zooms, everything should work as expected.
// Add layer to map populating data from Geojson
map.addLayer({
"id": "seed",
"type": "symbol",
"source": "objects",
"layout": {
"icon-rotation-alignment": "map",
"icon-keep-upright": true,
"icon-rotate": 0,
"icon-image": "{icon}",
"icon-allow-overlap": true,
"icon-size": {
"type": "exponential",
"stops": [
[15, 0.1],
[16, 0.3],
[17, 0.6],
[18, 0.8],
[19, 2],
[20, 3]
]
}
}
});