Mapbox looses popups when switching between satellite and street view - mapbox

On Mapbox street map view we are drawing a geo json line and placing popups at each of the waypoints.
When switching from street map view the satellite view the waypoints disappear.
The attached fiddle contains both the style layers for street map and satellite so that you can switch between the two and see the information change for satellite.
What is the reason for this? How do we get the waypoints to appear on satellite view.
https://jsfiddle.net/Profiler/a0wemkjz/1/
mapboxgl.accessToken = 'pk.eyJ1IjoiY2xvbmc0MCIsImEiOiJjazNpeXV3a3MwY2Y4M2pxbGFybjZ5MTM4In0.HNQRjQR4y5V1koLlpenKUw';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/clong40/ckbdypecg2ev71jqzc0xhdx54', //streetmap view, below is satelite view
// style: 'mapbox://styles/clong40/ckd74lwfm0l7f1is3iys2s6qx',
center: [3.1428,39.8669],
zoom: 7
});
// Add geolocate control to the map.
map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
},
),'bottom-right'
);
/* Creates start point marker and listens to drag event */
var el = document.createElement('img');
el.src = '/assets/images/letter_s.png';
var startMarker = new mapboxgl.Marker(el,{draggable: true}).setLngLat([3.1428,39.8669]).addTo(map);
function onStartDragEnd() { var lngLat = this.getLngLat();$("#start").val(lngLat.lat.toFixed(4)+','+lngLat.lng.toFixed(4));}
startMarker.on('dragend', onStartDragEnd);
/* Creates end point marker and listens to drag event */
var el = document.createElement('img');
el.src = '/assets/images/letter_e.png';
var endMarker = new mapboxgl.Marker(el,{draggable: true}).setLngLat([4.0812,40.0724]).addTo(map);
function onEndDragEnd() { var lngLat = this.getLngLat();$("#end").val(lngLat.lat.toFixed(4)+','+lngLat.lng.toFixed(4));}
endMarker.on('dragend', onEndDragEnd);
/* Let's also listen to start and end text boxes to sync back with markers */
$(document).ready(function(){
$('#start').change(function(){if(!this.value) return; var parts = this.value.split(',');startMarker.setLngLat([parts[1].trim(),parts[0].trim()]);});
$('#end').change(function(){if(!this.value) return; var parts = this.value.split(',');endMarker.setLngLat([parts[1].trim(),parts[0].trim()]);});
});
map.on('load', function() {
map.addSource('route', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': [
[3.1428091990252,39.866845647036],
[3.14265625,39.866640625],
[3.1425,39.8665625],
[3.141875,39.8671875],
[3.14375,39.869375],
[3.2,39.91],
[3.89,40.08],
[4.0812,40.0724],
]
}
}
});
map.addLayer({
'id': 'route',
'type': 'line',
'source': 'route',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#333399',
'line-width': 4
}
});
});
//add markers
map.on('load', function() {
map.addSource('places', {
'type': 'geojson',
'data': {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"icon": "information",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.1428091990252,39.866845647036]
}
}, {
"type": "Feature",
"properties": {
"icon": "information",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.1425,39.8665625]
}
}, {
"type": "Feature",
"properties": {
"icon": "information",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.14375,39.869375]
}
}, {
"type": "Feature",
"properties": {
"icon": "information",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.89,40.08]
}
},
]
}
});
// Add a layer showing the places.
map.addLayer({
'id': 'places',
'type': 'symbol',
'source': 'places',
'layout': {
'icon-image': 'information',
'icon-size': 0.10,
'icon-allow-overlap': true
}
});
// When a click event occurs on a feature in the places layer, open a popup at the
// location of the feature, with description HTML from its properties.
map.on('click', 'places', function(e) {
var coordinates = e.features[0].geometry.coordinates.slice();
var description = e.features[0].properties.description;
// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(description)
.addTo(map);
});
// Change the cursor to a pointer when the mouse is over the places layer.
map.on('mouseenter', 'places', function() {
map.getCanvas().style.cursor = 'pointer';
});
// Change it back to a pointer when it leaves.
map.on('mouseleave', 'places', function() {
map.getCanvas().style.cursor = '';
});
});
map.addControl(
new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl
}), 'top-left'
);
var map = new mapboxgl.Map({attributionControl: false})
.addControl(new mapboxgl.AttributionControl({
compact: true
}));
var canvas = map.getCanvasContainer();
map.addControl(new mapboxgl.NavigationControl(), 'bottom-right');
```

Looking at the console when I switch to the satellite style, I see this:
Image "information" could not be loaded. Please make sure you have
added the image with map.addImage() or a "sprite" property in your
style. You can provide missing images by listening for the
"styleimagemissing" map event.
This is referring to map.addSource('places', ...) where you set "icon": "information"
Using the styles API, you can check for the different sprites your styles have: https://docs.mapbox.com/api/maps/#retrieve-a-sprite-image-or-json
you can see in the sprites for your first style that you have a sprite called "information".
this is missing from your satellite style
in your JSFiddle, I swapped all references of "information" to "information-15" (a sprite that exists in both styles), and changed the icon-size to 1 in your addLayer and was able to switch between the styles and have the icons appear in both
map.addSource('places', {
'type': 'geojson',
'data': {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"icon": "information-15",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.1428091990252, 39.866845647036]
}
}, {
"type": "Feature",
"properties": {
"icon": "information-15",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.1425, 39.8665625]
}
}, {
"type": "Feature",
"properties": {
"icon": "information-15",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.14375, 39.869375]
}
}, {
"type": "Feature",
"properties": {
"icon": "information-15",
"icon-allow-overlap": true,
"iconSize": "15"
},
"geometry": {
"type": "Point",
"coordinates": [3.89, 40.08]
}
}, ]
}
});
// Add a layer showing the places.
map.addLayer({
'id': 'places',
'type': 'symbol',
'source': 'places',
'layout': {
'icon-image': 'information-15',
'icon-size': 1,
'icon-allow-overlap': true
}
});

Related

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

Mapbox GL: Get Layer IDs

I have a map with a few dozen layers, each with a unique ID. I have checkboxes to turn the layers on and off, for which I need a single array of all the layer IDs. I can't figure out how to loop through all of the map layers to capture the layer IDs. I tried using map.getLayer() but this returns the layer as an object, not the layer ID as a string. I want to loop through all of the map layers and push the layer ID strings to a new array. How do I do this?
mapboxgl.accessToken = "myaccesstoken";
var map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mymapboxstyle",
center: [-71.0664, 42.358],
minZoom: 14 //
});
map.on("style.load", function () {
map.addSource("contours", {
type: "vector",
url: "mapbox://mapbox.mapbox-terrain-v2"
});
map.addSource("hDistricts-2017", {
"type": "vector",
"url": "mapbox://mysource"
});
map.addLayer({
"id": "contours",
"type": "line",
"source": "contours",
"source-layer": "contour",
"layout": {
"visibility": "none",
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#877b59",
"line-width": 1
}
});
map.addLayer({
"id": "Back Bay Architectural District",
"source": "hDistricts-2017",
"source-layer": "Boston_Landmarks_Commission_B-7q48wq",
"type": "fill",
"layout": {
"visibility": "none"
},
"filter": ["==", "OBJECTID", 13],
"paint": {
"fill-color": "#192E39",
"fill-outline-color": "#000000",
"fill-opacity": 0.5
}
});
});
var layerIds = [];
function getIds() {
//here I need to iterate through map layers to get id strings.
//how do I do this???
layerIds.push( ); //then push those ids to new array.
console.log(layerIds); //["contours", "Back Bay Architectural District"]
}
If kielni answer isn't convenient because of unknown reason, use map.getStyle().layers to get an array of object layers then map it to get an array of string ids.
var layers = map.getStyle().layers;
var layerIds = layers.map(function (layer) {
return layer.id;
});
You have the layer ids when you add the layer; you can save them then:
function addLayer(map, options, layerIds) {
map.addLayer(options);
layerIds.push(options.id);
}
addLayer(map, {
"id": "Back Bay Architectural District",
"source": "hDistricts-2017",
"source-layer": "Boston_Landmarks_Commission_B-7q48wq",
"type": "fill",
"layout": {
"visibility": "none"
},
"filter": ["==", "OBJECTID", 13],
"paint": {
"fill-color": "#192E39",
"fill-outline-color": "#000000",
"fill-opacity": 0.5
}
},
layerIds);

Color only the edge of a circle mapbox gl js

I want to show the outline of a circle on an interactive map (no fill) however, the paint options in mapbox-gl-js seem limited to fill only.
https://www.mapbox.com/mapbox-gl-style-spec/#layers-circle
var styles = [{
"id": 'points',
"interactive": true,
"type": "circle",
"source": "geojson",
"paint": {
"circle-radius": 5,
"circle-color": "#000
},
"filter": ["in", "$type", "Point"]
}, {
"type": "line",
"source": "geojson",
"layout": {
"line-cap": "round",
"line-join": "round"
},
"paint": {
"line-color": "#000",
"line-width": 2.5
},
"filter": ["in", "$type", "LineString"]
}];
Am i missing something or is this just not possible?
This is now possible, with circle-opacity.
E.g.:
"paint": {
"circle-opacity": 0,
"circle-stroke-width": 1,
"circle-stroke-color": #000
}
Not currently possible. Current top workaround appears to be layering two circles of slightly different sizes.
https://github.com/mapbox/mapbox-gl-js/issues/1713
https://github.com/mapbox/mapbox-gl-style-spec/issues/379
I'm having trouble running custom color 'match' and having opacity controls running simultaneously.
I can get both working, but not at the same time. See code below.
var coorAddresses = [ [ -75.7040473, 45.418067,"Medium" ], [-75.7040473, 45.418067, "Medium"], [-79.32930440000001, 43.7730495, "Unknown"]]
$.getJSON(coodAddresses, function(data) {
for(var itemIndex in data) {
// push new feature to the collection
featureCollection.push({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [data[itemIndex][0], data[itemIndex][1]]
},
"properties": {
"size_by": data[itemIndex][2],
"color_by": data[itemIndex][2]
},
});
}
});
map.on('load', function () {
map.addLayer({
"id": "points",
"type": "circle",
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": featureCollection
}
},
"paint": {
"circle-color": [
'match',
['get', 'size_by'],
'Easy',
'#e4f400',
'Medium',
'#f48a00',
'Unknown',
'#6af400',
/* other */ '#00e4f4'
],
"circle-radius": [
'match',
['get', 'size_by'],
'Easy',
4,
'Medium',
7,
'Unknown',
2,
/* other */ 1000
],
// "circle-opacity": 0, // color does not show if i uncomment these lines
// "circle-stroke-width": 1, // do not get desired 'hollow' circle unless these lines run
}});
Trying to troubleshoot.

Can't get custom markers to work in Mapbox GL JS

I've followed both the example at Mapbox site and this instruction on GitHub but can't get markers to show on my map:
http://codepen.io/znak/pen/waPPRj (using Mapbox style and sprites)
http://codepen.io/znak/pen/PqOEyV (using custom style and sprites)
var center = [51.5, -0.1];
var map = new mapboxgl.Map({
container: 'map',
center: center,
zoom: 8,
style: 'https://www.mapbox.com/mapbox-gl-styles/styles/mapbox-streets-v7.json'
});
// Markers
map.on('style.load', function() {
map.addSource("markers", {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [51.48, -0.08]
},
"properties": {
"title": "Lorem",
"marker-symbol": "default_marker"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [51.52, -0.12]
},
"properties": {
"title": "Ipsum",
"marker-symbol": "secondary_marker"
}
}]
}
});
map.addLayer({
"id": "markers",
"type": "symbol",
"source": "markers",
"layout": {
"icon-image": "{marker-symbol}",
"text-field": "{title}",
"text-font": "Open Sans Semibold, Arial Unicode MS Bold",
"text-offset": [0, 0.6],
"text-anchor": "top"
},
"paint": {
"text-size": 14
}
});
});
All styles, JSON and PNG files with markers seem to load properly.
Any ideas?
The GeoJSON layer type of Mapbox GL JS follows the GeoJSON specification, which requires that coordinates be in longitude, latitude order. Your examples have them reversed. Flipping them shows the markers, which have the correct icons.
"geometry": {
"type": "Point",
"coordinates": [-0.12, 51.52]
}

addingand using marker variable with geoJsonData and markercluster with leaflet.js

I've been searching for hours and hours now, and i'm still stuck. I have a feeling it is something easy/stupid i'm missing.
I am using the example of markercluster from GitHub. I have 2 different markers (simply 2 different colors) that i would like to show which i would defined in the json format.
I have followed guide from leaflet site to define different markers.
I added my variable to the json part, but i can not figure out how to make the map load the different markers. It's either giving me no map or error; or it still uses the default blue marker.
here is my code:
<script type="text/javascript">
var geoJsonData = {
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id":"1", "properties": { "address": "2","marker": "cmIcon"}, "geometry": { "type": "Point", "coordinates": [175.2209316333,-37.8210922667 ] } },
{ "type": "Feature", "id":"2", "properties": { "address": "151","marker": "otherIcon" }, "geometry": { "type": "Point", "coordinates": [175.2238417833,-37.80975435 ] } },
{ "type": "Feature", "id":"3", "properties": { "address": "21","marker": "cmIcon" }, "geometry": { "type": "Point", "coordinates": [175.2169955667,-37.818193 ] } },
{ "type": "Feature", "id":"4", "properties": { "address": "14","marker": "otherIcon" }, "geometry": { "type": "Point", "coordinates": [175.2240856667,-37.8216963 ] } },
{ "type": "Feature", "id":"5", "properties": { "address": "38B","marker": "cmIcon" }, "geometry": { "type": "Point", "coordinates": [175.2196982333,-37.8188702167 ] } },
{ "type": "Feature", "id":"6", "properties": { "address": "38","marker": "otherIcon" }, "geometry": { "type": "Point", "coordinates": [175.2209942 ,-37.8192782833 ] } }
]
};
var cloudmade = L.tileLayer('http://{s}.tile.cloudmade.com/{key}/997/256/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © 2011 OpenStreetMap contributors, Imagery © 2011 CloudMade',
key: 'BC9A493B41014CAABB98F0471D759707'
});
var LeafIcon = L.Icon.extend({
options: {
shadowUrl: 'marker/marker-shadow.png',
iconSize: [32, 32],
shadowSize: [36, 20],
iconAnchor: [22, 94],
shadowAnchor: [4, 62],
popupAnchor: [-3, -76]
}
});
var cmIcon = new LeafIcon({iconUrl: 'marker/marker-cm.png'}),
otherIcon = new LeafIcon({iconUrl: 'marker/marker-others.png'});
var map = L.map('map')
.addLayer(cloudmade);
var markers = new L.MarkerClusterGroup();
var geoJsonLayer = L.geoJson(geoJsonData, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.address);
}
});
markers.addLayer(geoJsonLayer);
map.addLayer(markers);
map.fitBounds(markers.getBounds());
function onLocationFound(e) {
var radius = e.accuracy / 2;
L.marker(e.latlng).addTo(map)
.bindPopup("Vous etes ici").openPopup();
L.circle(e.latlng, radius).addTo(map);
}
function onLocationError(e) {
alert(e.message);
}
map.on('locationfound', onLocationFound);
map.on('locationerror', onLocationError);
map.locate({setView: true, maxZoom: 16});
</script>
I suspect that i need to tell leaflet to get the marker variable, probably in
var geoJsonLayer = L.geoJson(geoJsonData, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.address);
}
});
but i cannot make it work.
i even tried as suggested some where else:
var geoJsonLayer = L.geoJson(geoJsonData, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.address),
layer.setIcon(feature.properties.marker);
}
});
i'm still getting an error: Uncaught TypeError: Object cmIcon has no method 'createIcon'
does anybody have an idea on how to do that?
Any help would be greatly appreciated.
Thank you in advance.
You need to tell the MarkerClusterGroup how to create the icon. The marker cluster docs show an example with a DivIcon like this:
var markers = new L.MarkerClusterGroup({
iconCreateFunction: function(cluster) {
return new L.DivIcon({ html: '<b>' + cluster.getChildCount() + '</b>' });
}
});
You can use your custom icon instead like this:
var markers = new L.MarkerClusterGroup({
iconCreateFunction: function(cluster) {
// decide which icon you want to use
return (cluster.getChildCount() > 10) ? cmIcon : otherIcon;
}
});