Visibility of villages in Mapbox - mapbox

In my Mapbox GL project, I have a layer with towns and villages which looks like this:
{
"id": "towns",
"type": "symbol",
"source": "composite",
"source-layer": "place_label",
"minzoom": 1,
"maxzoom": 14,
"filter": [
"all",
[
"match",
["get", "type"],
["town", "village", "hamlet"],
true,
false
],
["match", ["get", "name"], [
// list of villages
], true, false]
],
"layout": {
"text-variable-anchor": ["top", "bottom", "left", "right"],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-allow-overlap": true,
"text-field": ["get", "name"],
"text-font": ["Roboto Regular", "Arial Unicode MS Regular"],
"text-size": [
"interpolate",
["linear"],
["zoom"],
3,
8,
22,
16
]
},
"paint": {
"text-halo-color": "hsla(0, 0%, 100%, 0.95)",
"text-halo-width": 1,
"text-halo-blur": 0.4
}
},
The text-size zoom interpolation works fine for the towns. However, the village will only show up after a zoomlevel of 9. Is there any way I could make the villages visible at all time?

Most likely, the Mapbox Streets Tileset you're using only includes those villages in the data after zoom 9, so there is nothing you can do in the style to make it show up earlier.
You'd need to create your own dataset which includes the data you want at lower zoom levels (and maybe just check this is the cause first in Mapbox Studio data view).

Related

Symbol placed with line-center doesn't rotate as the map rotates

I'm having a problem rotating an image with property symbol-placement set to line-center.
This is what it looks like
Removing the property half-solve the issue; the icon rotate as I rotate the map, but they position at the start of the line, and it's not what I'm trying to do.
The definition of my layer is:
{
"id": "my_symbol",
"type": "symbol",
"source": "XXXX",
"source-layer": "XXXXXXX",
"minzoom": 19,
"layout": {
"symbol-placement": "line-center",
"icon-size": {
"stops": [
[20, 0.2],
[21, 0.1],
[22, 0.3],
[23, 0.5],
[24, 0.7]
]
},
"icon-image": [
"concat",
"edge_",
["get", "status_list"]
],
"icon-allow-overlap": true
}
}
I'm trying to achieve this behaviour only by the layer's definition.
I tried looking for a solution, here on SO and on some other sites but I haven't found anything that would help me.
you can set your icon rotation with icon-rotation-alignment
setting it to viewport will fix the icon rotation to your viewport while setting it to map won't rotate it
i'm sorry the gif is hard to see!
{
"id": "my_symbol",
"type": "symbol",
"source": "XXXX",
"source-layer": "XXXXXXX",
"minzoom": 19,
"layout": {
"symbol-placement": "line-center",
"icon-size": {
"stops": [
[20, 0.2],
[21, 0.1],
[22, 0.3],
[23, 0.5],
[24, 0.7]
]
},
"icon-image": [
"concat",
"edge_",
["get", "status_list"]
],
"icon-allow-overlap": true,
"symbol-placement": "line-center",
"icon-rotation-alignment": "viewport"
}
}

How filter features by status

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:

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.

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]
]
}
}
});

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
}
}