How do I get a single set of country boundaries from Mapbox country boundaries - mapbox

https://codepen.io/m12n/pen/XWNRZMg?editors=0010
mapboxgl.accessToken =
"pk.eyJ1IjoiaW50ZWxsaWdlbmNlZnVzaW9uIiwiYSI6ImNrM2l2cnk1NzBibGIzbm5yaHNycXltZmYifQ.tPEnnW5NAPmInCJDYVfJxA";
var map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/dark-v10",
zoom: 8,
center: [-71.4935, 41.5852]
});
map.on("load", function () {
map.addSource("countries-no-simplification", {
type: "vector",
url: "mapbox://mapbox.country-boundaries-v1"
});
map.addLayer({
id: "countries-simplification-data",
type: "fill",
source: "countries-no-simplification",
"source-layer": "country_boundaries",
paint: {
"fill-color": "#ff69b4",
"fill-opacity": 0.3
}
});
});
The above code and pen shows usage of the MapBox county boundaries where I fill the layer with a colour and a low opacity.
Because the source has multiple world views I get both boundaries for disputed countries showing at the same time, and therefore get the effect shown (stronger colouring).
Eventually I actually want to apply a colour based on a specific datapoint, but for now I would be happy with being able to render only a single set of boundaries (a single world view?).
I cant seem to figure out how to get only one such set of boundaries from the source.

You can apply a filter to keep the boundaries of a specific world view (US, CN, IN, etc.) and remove disputed boundaries. Example:
map.addLayer({
id: "countries-simplification-data",
type: "fill",
source: "countries-no-simplification",
"source-layer": "country_boundaries",
filter: [
'all',
['match', ['get', 'worldview'], ['all', 'US'], true, false],
["!=", "true", ["get", "disputed"]],
],
paint: {
"fill-color": "#ff69b4",
"fill-opacity": 0.3
}
});

Related

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.

Update style of individual feature from single geoJSON source on Mapbox map, when clicked

I'm working with Mapbox GL JS to plot geoJSON data on a map using their external geoJSON example as a starting point. The geoJSON file contains lots of features which are plotted as individual markers on the same layer. I would like to highlight the clicked marker by changing its colour from red to blue. I have adapted the example to show a pop-up with the point id when clicked (just as a proof of concept that the markers can fire events when clicked), however, I'm struggling to find a way to change the styling of the individual clicked marker.
The code is currently as follows:
mapboxgl.accessToken = 'pk.eyJ1IjoiZGFuYnJhbWFsbCIsImEiOiJjbDB3ODFveHYxOG5rM2pubWpwZ2R1Y2xuIn0.yatzJHqBTjQ6F3DHASlriw';
const map = new mapboxgl.Map({
container: 'map', // container ID
style: 'mapbox://styles/mapbox/satellite-v9', // style URL
zoom: 7, // starting zoom
center: [138.043, 35.201] // starting center
});
map.on('load', () => {
map.addSource('earthquakes', {
type: 'geojson',
data: 'https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson'
});
map.addLayer({
'id': 'earthquakes-layer',
'type': 'circle',
'source': 'earthquakes',
'paint': {
'circle-radius': 8,
'circle-stroke-width': 2,
'circle-color': 'red',
'circle-stroke-color': 'white'
}
});
});
map.on('click', 'earthquakes-layer', (e) => {
new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML('Id: ' + e.features[0].properties.id)
.addTo(map);
});
Here is a codepen: https://codepen.io/danb1/pen/BaYjOyx
Is it the case that it's actually not possible to use this approach, and instead each feature from the geoJSON file needs to be plotted as a separate layer? I'm struggling to find any examples of this and am not able to modify the geoJSON source — it has to come from one single file (rather than loading multiple geoJSON files separately on separate layers).
This is possible using feature-state. The first thing to do is to ensure the layer data contains ids for each feature (in the example the source data doesn't so we need to add generateId: true to the map.addSource method).
We then need to add mousemove and mouseleave events to the map to store the moused-over feature id (if there is one, i.e. if the mouse is hovering over a feature):
let hoveredEarthquakeId = null;
map.on('mousemove', 'earthquakes-layer', (e) => {
map.getCanvas().style.cursor = 'pointer';
if (e.features.length > 0) {
map.setFeatureState(
{ source: 'earthquakes', id: e.features[0].id },
{ hover: true }
);
hoveredEarthquakeId = e.features[0].id;
}
});
map.on('mouseleave', 'earthquakes-layer', () => {
map.getCanvas().style.cursor = '';
if (hoveredEarthquakeId !== null) {
map.setFeatureState(
{ source: 'earthquakes', id: hoveredEarthquakeId },
{ hover: false }
);
}
hoveredEarthquakeId = null;
});
Finally, in the layer properties, the colour setting of the circle needs to be updated to reflect the hover value stored against the feature:
'circle-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#00f',
'#f00'
],
The final thing can be seen in the modified pen. There is also a MapBox tutorial covering this kind of thing in a slightly more complicated way, which I hadn't come across until now: https://docs.mapbox.com/help/tutorials/create-interactive-hover-effects-with-mapbox-gl-js/.

querySourceFeature returns empty array

I am using mapbox to try and query all features of the mapbox tileset and return them as geojson. From what I understand, to query features that are no visible on the screen, you must use querysourcefeatures. My tileset only shows up at zoom 14 so I am having trouble querying all the features in the dataset at once and then applying a filter. Is this possible? It stills like querySourceFeatures returns an empty array.
function addLayers() {
map.addSource('plutonew-c03oxi', {
'type': 'vector',
'url': 'mapbox://samtpr.4ehwzn0r'
});
map.addLayer({
"id": "parcels_fill",
"type": "fill",
"source": "plutonew-c03oxi",
"source-layer": "plutonew-c03oxi",
'layout': {
'visibility': 'visible'
},
paint: {
'fill-color': 'blue',
'fill-outline-color': 'gray',
"fill-opacity": ["case",
["boolean", ["feature-state", "hover"], false],
0.5,
0
]
}
});
var features = map.querySourceFeatures('plutonew-c03oxi', {filter: ["==", ['get','ZIPCODE'], zipcode_val]});
From what I understand, to query features that are no visible on the screen, you must use querysourcefeatures
You may have misunderstood. querySourceFeatures allows you to query features that are present within the vector tiles currently loaded and displayable within the current viewport and zoom level. Unlike queryRenderedFeatures they don't have to actually be rendered through a visible layer, however.
In this case it sounds like you're hoping to gain access to features that are not available at the current zoom level, which is not possible.

Check if a GeoJSON source is present in mapbox viewport

I have a map with several layers of GeoJSON each with their own unique layer name:
var map = new mapboxgl.Map({
container: 'map',
center: [-97.5651505, 37.89549,],
zoom: 4
});
var sources = {
'ord': 'chicago',
'pit': 'pittsburgh',
'atl': 'atlanta'
};
map.on('load', function () {
for (var s in sources) {
map.addSource(s, { type: 'geojson', data: `/geojson/${s}.json` });
map.addLayer({
'id': sources[s],
'type': 'fill',
'source': s,
'layout': {
'visibility': 'visible'
},
'paint': {
'fill-color': '#088',
'fill-opacity': 0.5
}
});
}
});
I would like to check if a user has zoomed in past zoom level 13 evaluate if any of these three layers is in the viewport. If it is I'll take action to add a button to the overlay. However, I'm having issues finding any documentation other than leaflet on how to check if a layer is inside the viewport. I've found some mention of markers that that doesn't seem to apply.
You can achieve this with queryRenderedFeatures which returns an array of features rendered within a given bounding box. However, if you omit the bounding box argument, queryRenderedFeatures will query within the entire viewport. You can also use the options.layers argument to limit your query to specific layers to avoid getting a bunch of features that are in the underlying style (for example, streets and lakes). You can do this query in a zoomend event listener to achieve your desired outcome. Putting it all together would look something like this:
map.on('zoomend', () => {
if (map.getZoom() > 13) {
const visibleFeatures = map.queryRenderedFeatures(null, {layers: ['ord', 'pit', 'atl']});
// if none of the layers are visible, visibleFeatures will be an empty array
if (visibleFeatures.length) {
// figure out which layers are showing and add your button
}
}
});

Add water outline to my "light" mapbox map

I see that some maps styles use a layer called "water outline." You can see this layer in this pic as the blue outline next to the water.
I am using the light-v9 style from mapbox and am wondering how to "import" this layer onto my map so that I can have water outlines, too.
Here is how I set my map:
var map = new mapboxgl.Map({
container: this.mapContainer,
style: 'mapbox://styles/mapbox/light-v9',
interactive: true,
maxZoom: 16,
});
And this is how I make a couple style changes:
map.on('load', function () {
map.setPaintProperty('background','background-color', 'rgb(246, 246, 246)')
map.setPaintProperty('parks','fill-color', 'rgb(217, 232, 222)')
map.setPaintProperty('water','fill-color', 'rgb(224, 230, 230)')
...
})
It looks like that layer comes from the Mapbox Streets v7 vector tiles. If you would like to create a new line layer from that source, you would want to query the map's source for the water layer, then use it to create a line layer. For example:
map.addLayer({
'id': 'water-line-layer',
'source': 'composite',
'source-layer': 'water',
'type': 'line',
'minzoom': 15,
'paint': {
'line-color': '#000000',
'line-width': 10
}
}