Cannot combine multiple filters (ISO country codes) - mapbox-gl-js

I followed this tutorial to highlight countries on my Mapbox map (using the Mapbox Countries v1 vector tileset). However, some countries seem to be highlighted twice (i.e. their shading is darker).
Here's my map right now:
As you can see, China, India, etc are a more vibrant color than the other countries in Asia. (This is also a phenomenon in the Medium post, as you can see in the third photo in the "Add a layer" section.)
Here's my code:
map.addLayer({
id: 'country-boundaries',
source: 'country-boundaries',
'source-layer': 'country_boundaries',
type: 'fill',
paint: {
'fill-color': palette.blue.flats,
'fill-opacity': 0.4,
},
filter: [
'in',
'iso_3166_1_alpha_3',
...highlightedContinents.reduce((countries, c) => {
return countries.concat(...ContinentRegionDefinitions[c].countries);
}, [] as ISOCountry[]),
],
});
and the array being returned is just a list of 3-digit ISO 3166-1 codes (['AFG', 'ARE', ...]).
I believe the problem is that I'm showing all worldviews, so countries with disputed boundaries are being shown twice.
I can change the filter from being the country codes to filtering by worldview (see bottom of docs page for sample code):
filter: [
'any',
['==', 'all', ['get', 'worldview']],
['in', 'US', ['get', 'worldview']],
],
and I get this, where almost all (except the circled) are only highlighted once:
Now, one would assume that, to get what I'm after, I could just combine these two filters like so:
[
'all',
[
'any',
['==', 'all', ['get', 'worldview']],
['in', 'US', ['get', 'worldview']],
],
[
'in',
'iso_3166_1_alpha_3',
...highlightedContinents.reduce((countries, c) => {
return countries.concat(...ContinentRegionDefinitions[c].countries);
}, [] as ISOCountry[]),
],
]
but for some reason that doesn't work:
(I've tried removing the ...highlightedCountinents.reduce(...) part and just replacing it with a short, static array, but that didn't help.)
Any thoughts? Is my filter wrong?

If anyone else stumbles across this I managed to get this to work with the following filter:
let masterFilter = [
'all',
['any',
['==', 'all', ['get', 'worldview']],
['in', 'US', ['get', 'worldview']],
],
['==', 'false', ['get', 'disputed']],
['any', ['all', ['in', ['get', 'iso_3166_1_alpha_3'], ["literal", countriesToShow.flat()]]]],
];

Related

Mapbox GL - Prevent Layer Label Fading Based on Zoom

Using Mapbox GL JS 1.12.0 with a GeoJSON source I'm adding circles that have a numeric label based on the "name" value in the GeoJSON.
map.addLayer({
'id': 'marker',
'type': 'circle',
'source': 'geojson',
'minzoom': 0,
'maxzoom': 24,
'paint': {
'circle-radius': 10,
'circle-color': '#FFFFFF',
'circle-stroke-color': '#000000',
'circle-stroke-width': 2,
'circle-opacity': 1,
},
'filter': ['==', '$type', 'Point']
});
map.addLayer({
'id': 'marker-label',
'type': 'symbol',
'source': 'geojson',
'minzoom': 0,
'maxzoom': 24,
'layout': {
'text-field': [ 'format', ['get', 'name'], { 'font-scale': 0.8, 'text-translate': [0,-20] } ],
},
'filter': ['==', '$type', 'Point']
});
When you're zoomed in they look correct:
As you zoom out the circles start to overlap, but still look correct:
But then the "2" fades out and the "1" shows through the circle for #2 (even though the circles have a white fill and an opacity of 1) and makes it unclear which is #1 and which is #2.
Is there a better way to do this? I wish you could just add a centered label to the circle itself.
Can I disable this automatic fading / transparency?
Can I somehow bind the label to that specific circle?
Can I disable this automatic fading / transparency?
You can use "text-allow-overlap": true to disable it. You may not like the result.
Can I somehow bind the label to that specific circle?
No.

Mapbox data driven styling for paint with boolean

I'm trying to dynamically set the fill colour for a GeoJSON layer based on its properties.
The properties for the feature would be:
properties: {
color: 'cyan',
colorSelected: 'magenta',
selected: false,
}
For the mapbox paint attribute, I have:
paint: {
"fill-color": [
'case',
['==', ['get', 'selected'], ['get', 'colorSelected']],
['get', 'color'],
]
}
The idea being that if the "selected" property is true, get colourSelected = 'magenta', else the default of 'cyan'
This is not working, how can I do this boolean selection?
This solution ended up resolving my problem:
"fill-color": [
'case',
['==', ['get', 'selected'], true],
['get', 'fillColorSelected'],
['get', 'fillColor'],
],

Mapbox JS Runtime Styling from collection

So I have a Mapbox Tileset I've uploaded with census-block information for the US. Each geometry in the tileset has a property called GeoID, that I am trying to apply a style to based on another collection I have.
The collection is an array objects with this format: [{geoid: string, weight: number}, {geoid: string, weight: number},...]
I want to match the GeoIDs from the array to the layer from the tileset and color it based on the corresponding weight property from that object. Weight is a number between 0 and 1. I have tried searching the documentation on runtime styling and expressions from Mapbox, but I can't find anything about extrapolating data from a collection and conditionally applying it to the right geometries from a tileset.
You can generate an expression from your list of weights and than pass it to the layer your want to style:
const weights = [
{geoid: 1, weight: 10},
{geoid: 2, weight: 5},
{geoid: 3, weight: 30},
];
const cases = weights.map(weight => {
const condition = ['==', ['get', 'geoid'], weight.geoid];
const output = getColor(weight.weight);
return [
condition,
output
];
});
const expresion = ['case',
...cases.reduce((flat, conditionAndOutput) => [...flat, ...conditionAndOutput]),
'<default-color>'
];
/*
Will result in:
[
"case",
[
"==",
[
"get",
"geoid"
],
1
],
"rgb(255, 255, 255)",
[
"==",
[
"get",
"geoid"
],
2
],
"rgb(255, 255, 255)",
[
"==",
[
"get",
"geoid"
],
3
],
"rgb(255, 255, 255)",
"<default-color>"
]
*/

Composite property functions in Mapbox GL JS

Is there a way to use more than one property in a property function in Mapbox GL JS? In CartoCSS, I would do something like the following:
.states {
[name="California"] {
"polygon-fill": "blue"
},
[name_abbrev="WA"] {
"polygon-fill": "green"
}
}
In Mapbox GL JS it seems that I can style the fill-color based on either the property name -or- name_abbrev, but not a combination of two properties. For example:
'fill-color': {
'property': 'name',
'type': 'categorical',
'stops': [
['California', 'blue']
]
},
'fill-color': {
'property': 'name_abbrev',
'type': 'categorical',
'stops': [
['WA', 'green']
]
}
This results in the second fill-color overriding the first, and California would simply be green.
Mapbox GL does not support styling based on multiple properties within one layer. You could rework your data to combine the properties based on which you'd like to style into one layer, or you could create two layers and use filters to separate features, if your reasoning is that not all features have a certain layer/some need to use a fallback property: something like
{
... layer metadata (id, source, source-layer, type, etc) ...
'filter': ['has', 'name'],
'paint': {
'fill-color': {
'property': 'name',
'type': 'categorical',
'stops': [
['California', 'blue']
]
}
}
},
{
... layer metadata (id, source, source-layer, type, etc) ...
'filter': ['!has', 'name'],
'paint': {
'fill-color': {
'property': 'name_abbrev',
'type': 'categorical',
'stops': [
['WA', 'green']
]
}
}
}

Mapbox - How can I show buildings the way foursquare shows them?

Here's what I mean.
Take a look at the following screenshot from foursquare:
However, using street view, here's what that same area looks like in my map at the same zoom level:
As you can see, the buildings don't show up. How can I make these buildings show up?
If you use mapbox gl-js API than I might know the solution.
I think you need to add another layer, I did it like this(add the code below to your client-side javascritpt file):
`map.on('load', function () {
map.addLayer({
'id': '3d-buildings',
'source': 'composite',
'source-layer': 'building',
'filter': ['==', 'extrude', 'true'],
'type': 'fill-extrusion',
'minzoom': 14,
'paint': {
'fill-extrusion-color': '#aaa',
'fill-extrusion-height': {
'type': 'identity',
'property': 'height'
},
'fill-extrusion-base': {
'type': 'identity',
'property': 'min_height'
},
'fill-extrusion-opacity': .6
}
});
});`