Mapbox GL Js: adding and removing GeoJSON sources and layers - mapbox-gl-js

Im having a problems adding and removing layers in mapbox gl.
I have this layer:
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}
},
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 8
}
});
then I remove it by doing:
map.removeLayer('route')
Everything works fine.
However when trying to add the same layer again I get the following error:
Error: There is already a source with this ID
Even though I'm adding a Layer. I don't know how to get the source removed because the source does not have an ID.
My final result is to be able to add and remove this layer by clicking on a button.
Can anyone help me here?

I just found out that the source is created with the same id as in the layer so:
map.removeSource('route')
Worked perfectly to completely remove both the layer and source.

You should remove layer first then remove its source.
map.removeLayer(id);
map.removeSource(id);
in your case id = 'route'.

...and check that the source/layer exist before you try to remove them
if (map.getLayer(id)) {
map.removeLayer(id);
}
if (map.getSource(id)) {
map.removeSource(id);
}

Related

filename.geojson:2 Uncaught SyntaxError: Unexpected token ':' (at filename.geojson:2:9)

I keep receiving the following errors with my geojson file:
hippocanon.geojson:2 Uncaught SyntaxError: Unexpected token ':' (at hippocanon.geojson:2:9)
leaflet.js:5 Uncaught Error: Invalid GeoJSON object.
at wi (leaflet.js:5:87066)
at e.addData (leaflet.js:5:85801)
at e.initialize (leaflet.js:5:85560)
at new e (leaflet.js:5:2708)
at frame4.html:46:26`
My current geojson file looks like this:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"start": "2009-09-15",
"end": "2010-08-07",
"author": "Kelly McCoy",
"rank": "CPT",
"service": "US Army",
"title": "Caveman 6 -- Stories from the CP",
"pitch": "Stories I tell...",
"url": "https://lex.page/read/0e66c104-7b9f-4a56-95ca-989bc8470795"
},
"geometry": {
"type": "Point",
"coordinates": [33.251778104724416, 44.27766716031223]
}
},
{
"type": "Feature",
"properties": {
"start": "2006-03-15",
"end": "2006-03-15",
"author": "Kelly McCoy",
"rank": "1LT",
"service": "US Army",
"unit": "C/502d MI BN",
"title": "From Bagram with Love",
"pitch": "How I met your mother",
"url": "https://lex.page/read/0e66c104-7b9f-4a56-95ca-989bc8470795"
},
"geometry": {
"type": "Point",
"coordinates": [34.937613863066176, 69.25211845755727]
}
}
]
}
Any idea on why I get this error?
I have used OpenAI playground and multiple other geojson examples to try and fix this issue. I keep receiving the same issue.

Lines with popup values and route sums

I'm newer to Mapbox and have struggled to find this answer in the documention; there's so much, it's a bit overwhelming at first. I'm doing some calculations locally and uploading the results as a geojson which includes segments of many roads and a number to go with each segment. E.g. road segment A has a numeric value of 1, and road segment B has a numeric value of 3. I'd like to do two things:
Set it so that these road segments have popup boxes displaying the value. I'm currently loading them and displaying their color based on this value, but I can't seem to find a way to add a popup to those segments which would display that value; all the popup documentation I find is focused on points.
Create an option which would evaluate navigation routes by summing all the numeric values of the segments encountered along the route. So if a route went through the above two segments (A and B) it would show a value of 4 at the end.
I'm doing all this in browser so none of the SDKs are needed as far as I can tell, though I think mapbox-GL might be needed. I'm at a loss as to where to start; any help would be most appreciated!
This is possible. You can also replace the onClick event listener with an event listener that listens onHover or else. To make it work, just replace <Your_Access_Token> with Your Access Token.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add a GeoJSON line</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = '<Your_Access_Token>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {
"name": "Hello, world!"
},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}
},
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 8
}
});
map.on('click', 'route', function (e) {
new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML(e.features[0].properties.name)
.addTo(map);
});
// Change the cursor to a pointer when the mouse is over the states layer.
map.on('mouseenter', 'route', function () {
map.getCanvas().style.cursor = 'pointer';
});
// Change it back to a pointer when it leaves.
map.on('mouseleave', 'route', function () {
map.getCanvas().style.cursor = '';
});
});
</script>
</body>
</html>

How to set map.loadImage visibility by zoom level in Mapbox GL JS?

I'm loading an image with the following code:
map.on('load', function () {
map.loadImage('....png', function(error, image) {
if (error) throw error;
map.addImage('b7', image);
map.addLayer({
"id": "b7",
"type": "symbol",
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0, 0]
}
}]
}
},
"layout": {
"icon-image": "b7",
"icon-size": 0.2
}
});
});
How can i set the visibility to none, at a certain zoom level?
It looks like that you cant use map.setLayoutProperty on an loadImage. In the console, it says: Error: The layer 'b7' does not exist in the map's style and cannot be styled.
Whey i try something like:
map.setLayoutProperty( 'b7', 'visibility', 'none' );
Any ideas?
Two suggestions on how to solve this:
First, make sure your image name and layer name are different. It could be that the function is looking for b7 layer but it finds an image named b7 first (or vice versa). Either way this should be changed as it creates conflicting variables.
Second, if that doesn't work try adding your source separately instead of inside the layer.
map.addSource("mySource", {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-74.981906, 41.742503]
},
"properties": {
"title": "title ",
"icon": "myImage",
}
}]
}
});
And then add the layer with the source.
map.addLayer({
"id": "b7",
"type": "symbol",
"source": "mySource",
"layout": {
"icon-image": "myImage",
"icon-size": 0.2
}
});
You can now call the setLayoutProperty on a zoomstart listener. Add a conditional if you want it only at a specific zoom level using map.getZoom(); You need to be setting the visibility for the layer here, not the image.
map.on('zoomstart', 'b7', function(e) {
if (map.getZoom() > 12) {
map.setLayoutProperty('b7', 'visibility', 'none');
}
})
Snippet is below, let me know if you encounter any problems.
map.on('load', function() {
map.loadImage('myImage.png', function(error, image) {
if (error) throw error;
map.addImage('myImage', image);
map.addSource("mySource", {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-73.981906, 40.742503]
},
"properties": {
"title": "title ",
"icon": "myImage",
}
}]
}
});
map.addLayer({
"id": "b7",
"type": "symbol",
"source": "mySource",
"layout": {
"icon-image": "myImage",
"icon-size": 0.2
}
});
});
});
map.on('zoomstart', 'b7', function(e) {
if (map.getZoom() > 12) {
map.setLayoutProperty('b7', 'visibility', 'none');
}
})

How to draw dashed line which wouldn't be changing when scaling

When I draw dashed line with 'line-dasharray' property, it behaves strange - lengths of line and gap changes when scaling. See the example. Question: how to draw dashed line which has constant line and gap lengths which don't change when I zoom in/out?
//this stupid bot says that my post is mostly code. don't know what's wrong in it,
//but have to add some stupid text lines
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add a GeoJSON line</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibHVjYXN3b2oiLCJhIjoiY2l5Nmg4cWU1MDA0ejMzcDJtNHJmZzJkcyJ9.WhcEdTYQH6sSw2pm0RSP9Q';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}
},
"paint": {
"line-color": "#888",
"line-width": 8,
"line-dasharray": [5, 5]
}
});
});
</script>
</body>
</html>
Well, I found something that makes it a bit better, but still it is not perfect as dashed lines continue to change with scaling.
A part of the answer is that gaps between lines are specified in line width units and not in pixels. And when the line width is constant (in pixels) and doesn't depend on scaling, it's size (for example, in meters) is getting larger or smaller when zoom level changes. That causes gaps between lines to change as well. Here you can find a description how to make line with variable width which will depend on zoom level and preserve it's relative size.
Here is what I'm getting applying this approach to my example (it became better, but still line changes when I zoom in/zoom out):
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add a GeoJSON line</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibHVjYXN3b2oiLCJhIjoiY2l5Nmg4cWU1MDA0ejMzcDJtNHJmZzJkcyJ9.WhcEdTYQH6sSw2pm0RSP9Q';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
let baseWidth = 5;
let baseZoom = 15;
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}
},
"paint": {
"line-color": "#888",
"line-width":
{
'type': 'exponential',
'base': 2,
'stops': [
[0, baseWidth * Math.pow(2, (0 - baseZoom))],
[22, baseWidth * Math.pow(2, (22 - baseZoom))]
]
},
"line-dasharray": [5, 5]
}
});
});
</script>
</body>
</html>
You get much better behaviour (in my eyes) by tweaking the actual dasharray values. By taking the first value smaller than the second, for example [0.2, 2], the scaling makes a lot more sense. This is what they use in the mapbox-gl-draw library.
edit: here's how it looks:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add a GeoJSON line</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibHVjYXN3b2oiLCJhIjoiY2l5Nmg4cWU1MDA0ejMzcDJtNHJmZzJkcyJ9.WhcEdTYQH6sSw2pm0RSP9Q';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}
},
layout: {
'line-join': 'round',
'line-cap': 'round',
},
"paint": {
"line-color": "#888",
"line-width": 8,
"line-dasharray": [0.2, 2]
}
});
});
</script>
</body>
</html>

How to draw a double line from GeoJson source in MapBox GL JS?

I'm using data-driven styles in Mapbox to customize style of lines I get from GeoJson source. Is there any way to draw a double line somehow except than turning lines to polygons or drawing a second line nearby?
You may consider using the line-gap-width
property.
Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add a GeoJSON line</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibHVjYXN3b2oiLCJhIjoiY2l5Nmg4cWU1MDA0ejMzcDJtNHJmZzJkcyJ9.WhcEdTYQH6sSw2pm0RSP9Q';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}
},
"paint": {
"line-color": "#888",
"line-width": 8,
"line-gap-width": 10
}
});
});
</script>
</body>
</html>