Get FeatureId of target in popup on vector layer in Leaflet - leaflet

The below code works ok, I get lots of interesting data in getFeatureId.
How do I get my hands on that in the popup ?
var map = L.map('map').setView([53.505, -7.09], 7);
L.tileLayer('https://{s}.etc.etc/{z}/{x}/{y}.png', {
attribution: 'osm..'
}).addTo(map);
var VectorTileOptions = {
rendererFactory: L.canvas.tile,
attribution: '',
interactive: true,
getFeatureId:function(feat){
return feat.properties.routes
}
};
var TilesPbfLayer = L.vectorGrid.protobuf(tileurl, VectorTileOptions).addTo(map);
var popup = L.popup();
map.on('popupopen', function(e) {
popup.setContent("how do i get the feature Id ? ")
});
TilesPbfLayer.bindPopup(popup)

I can get a click event on the tile layer, and its got a layer with my stuff in
TilesPbfLayer.on('click', function(e) {
if (e.layer)
popup.setContent(e.layer.properties.routes)
})

Related

Leaflet: Layer check box state is reset every time the map moves or zooms

I have the following code which fetches some remote GeoJSON from an API and displays the results on a Leaflet map:
<script>
// Center the map
var map = L.map('map').setView([54.233669, -4.406027], 6);
// Attribution
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=REMOVED', {
attribution: 'Map © OpenStreetMap',
id: 'mapbox.streets'
}).addTo(map);
// Create an empty layergroup for the data
var LayerUTMGroundHazards = L.layerGroup();
var LayerUTMAirspace = L.layerGroup();
// Style the features
function setStyle(feature) {
return {
fillColor: feature.properties.fillColor,
color: feature.properties.strokeColor,
fillOpacity: feature.properties.fillOpacity,
opacity: feature.properties.strokeOpacity
};
}
// Build Guardian UTM
function getGuardianUTMdata() {
// Clear the current Layer content
LayerUTMGroundHazards.clearLayers();
LayerUTMAirspace.clearLayers();
// Define what we want to include
function FuncGroundHazards(feature) {
if (feature.properties.category === "groundHazard") return true
}
function FuncAirspace(feature) {
if (
(feature.properties.category === "airspace" || feature.properties.category === "airport")
&& feature.properties.detailedCategory !== "uk:frz"
) return true
}
// Build the layers
fetch("https://example.com/json?n=" + map.getBounds().getNorth() + "&e=" + map.getBounds().getEast() + "&s=" + map.getBounds().getSouth() + "&w=" + map.getBounds().getWest(), { headers: { 'Authorization': 'REMOVED', 'X-AA-DeviceId': 'mySite' } })
.then(function (responseGuardianUTM) { return responseGuardianUTM.json() })
.then(function (dataGuardianUTM) {
// Create Layer: Ground Hazards
var featuresUTMGroundHazards = L.geoJson(dataGuardianUTM, {
filter: FuncGroundHazards,
style: setStyle,
pointToLayer: function (feature, latlng) { return L.marker(latlng, { icon: L.icon({ iconUrl: feature.properties.iconUrl, iconSize: [25, 25], }), }) },
onEachFeature: function (feature, layer) { layer.bindPopup(feature.properties.name); },
});
// Add the L.GeoJSON instance to the empty layergroup
LayerUTMGroundHazards.addLayer(featuresUTMGroundHazards).addTo(map);
});
// other layers are here (removed from this example)
}
// Update the Guardian UTM layer if the map moves
map.on('dragend', function () { getGuardianUTMdata(); });
map.on('zoomend', function () { getGuardianUTMdata(); });
// Layer controls
var layerControl = new L.Control.Layers(null, {
'Airspace Restrictions': LayerUTMAirspace,
'Ground Hazards': LayerUTMGroundHazards
// other layers are here (removed from this example)
}).addTo(map);
</script>
The problem is that every time the map is moved or zoomed, all of the Layer checkboxes are reset to Checked again, regardless of how many were checked before the map moved. They do not honour / remember their state when the map moves.
Given my code above, how can I store or preserve the checkbox state for each of the multiple Layers that I have so they are not reset every time the map is moved?
EDIT:
Here is a working fiddle. Remove the checkbox from the 'Ground Hazards', then move or zoom the map, you will see how it puts a tick back in the box again
https://jsfiddle.net/hdwz1b6t/1/
You're (re-)adding LayerUTMGroundHazards every time. This line here...
// Add the L.GeoJSON instance to the empty layergroup
LayerUTMGroundHazards.addLayer(featuresUTMGroundHazards).addTo(map);
...is not only adding featureUTMGroundHazards to layerUTMGroundHazards, it's also (re-)adding layerUTMGroundHazards to the map.
And quoting from https://leafletjs.com/examples/layers-control/ :
The layers control is smart enough to detect what layers we’ve already added and have corresponding checkboxes and radioboxes set.
So when you do LayerUTMGroundHazards.addTo(map);, the checkboxes reset.

Changing overlay layers when switching base layer

I have built a leaflet map with two base layers, and each of these base layers will have their own unique points of interest. The points of interest are being stored as geojson that I loop over to create multiple overlays for different categories. So when viewing the default base layer you would see layers for Show All, Cat1, Cat2 etc.
I need a way to be able to attach overlay layers to a base layer, or remove all overlay layers and then load the relevant ones when the base layer changes.
I tried using the following, which worked to switch categories, with the baselayerchange event, but the overlay layers were still displaying when I switched base layers.
layerControl._layers.forEach(function(layer){
if(layer.overlay){
map.removeLayer(layer.layer)
}
});
I've been searching for a couple of days now for an answer to this without any luck, any help is greatly appreciated.
EDIT
Posting additional code for context. This is not the entirety of the code, there are some plugins that I'm not including code for and have excluded definitions for a several variables, but this should provide better insight into how things are working.
//Initialize the map
var map = new L.Map('map', {
maxZoom: mapMaxZoom,
minZoom: mapMinZoom,
crs: crs1848,
attributionControl: false,
layers: [pano1848]
});
//add controls to the map
var layerControl = L.control.layers(null, null, {position: 'bottomleft'}).addTo(map);
//building category layers from geojson
var types = ['African Americans', 'Art Architecture Culture', 'Education Religion Reform', 'Everyday Life', 'Immigrants', 'Science Inventions', 'Transportation Industry Commerce'];
types.forEach(function(type){
var catType = type.replace(/\s/g,"");
var catPoints = L.geoJson(mapData, {
filter: function(feature, layer){
var cat = feature.properties['category'];
return cat.indexOf(catType) >= 0;
},
onEachFeature: function (feature, layer) {
layer.bindTooltip(feature.properties.name);
(function(layer, properties){
//Create Numeric markers
var numericMarker = L.ExtraMarkers.icon({
icon: 'fa-number',
markerColor: 'yellow',
number: feature.properties['id']
});
layer.setIcon(numericMarker);
layer.on('click', function() {
$.ajax({
url:feature.properties['url'],
dataType:'html',
success: function(result){
$('#detailContainer').html(result);
$('#overlay').fadeIn(300);
}
});
});
})(layer, feature.properties);
}
});
layerControl.addOverlay(catPoints, catType);
});
//Base Layer Change Event
map.on('baselayerchange', function(base){
var layerName;
layerControl._layers.forEach(function(layer){
if(layer.overlay){
map.removeLayer(layer.layer)
}
});
if(base._url.indexOf('1848') >= 0){
map.options.crs = crs1848;
map.fitBounds([
crs1848.unproject(L.point(mapExtent1848[2], mapExtent1848[3])),
crs1848.unproject(L.point(mapExtent1848[0], mapExtent1848[1]))
]);
var southWest = map.unproject([0, 8192], map.getMaxZoom());
var northEast = map.unproject([90112, 0], map.getMaxZoom());
map.setMaxBounds(new L.LatLngBounds(southWest, northEast));
map.addLayer(allPoints);
layerName = '1848 Panorama';
}
else if(base._url.indexOf('2018') >= 0){
map.options.crs = crs2018;
map.fitBounds([
crs2018.unproject(L.point(mapExtent2018[2], mapExtent2018[3])),
crs2018.unproject(L.point(mapExtent2018[0], mapExtent2018[1]))
]);
var southWest = map.unproject([0, 8192], map.getMaxZoom());
var northEast = map.unproject([49152, 0], map.getMaxZoom());
map.setMaxBounds(new L.LatLngBounds(southWest, northEast));
layerName = '2018 Panorama'
}
miniMap.changeLayer(minimapLayers[layerName]);
//map.setView(map.getCenter(), map.getZoom());
});
You may create global variable call "overlays", and remove it like an example below.
Here is the similar example to illustrate your problem jsFiddle
var overlays = {
'Name 1': catPoints,
'Name 2': catType
};
L.control.layers(null, overlays).addTo(map);
// Whenever you want to remove all overlays:
for (var name in overlays) {
map.removeLayer(overlays[name]);
}

How can I Open Multiple Popups in Leaflet Marker at a time

Map` like this:
L.Map = L.Map.extend({
openPopup: function(popup) {
this._popup = popup;
return this.addLayer(popup).fire('popupopen', {
popup: this._popup
});
}
});
But I am using leaflet. Is there anyway to extent like so that i can prevent closing my marker popup?
L.mapbox.accessToken = constant.accessToken;
var map = L.mapbox.map('map', 'mapbox.streets', {zoomControl: true});
Update Dec 2017
Leaflet popup options have been extended to include { autoClose: false } which has the required effect :
var my_marker = L.marker([my_lat, my_lng], {icon: my_icon})
.addTo(map)
.bindPopup('My Popup HTML', {closeOnClick: false, autoClose: false});
Let me quote the Leaflet documentation on L.Popup:
Used to open popups in certain places of the map. Use Map.openPopup to open popups while making sure that only one popup is open at one time (recommended for usability), or use Map.addLayer to open as many as you want.
In order to open several popups, instantiate them using L.popup(latlng, options), then .addTo(map) them.
Define an array:
let map=L.map('mymap');
...
var markers = L.markerClusterGroup();
...
var marker=[];
marker[0]= L.marker([latitud1,longitud1]).addTo(map).bindPopup('Hola 0',{autoClose: false, closeOnClick: false});
marker[1]= L.marker([latitud2,longitud2]).addTo(map).bindPopup('Hola 1',{autoClose: false, closeOnClick: false});
To put on the map:
marker.forEach(function(marker) {
markers.addLayer(marker);
map.addLayer(markers);
});
Show the popup for only one:
var curPos = marker[0].getLatLng();
map.setView(new L.LatLng(curPos.lat,curPos.lng), 13);
marker[0].openPopup();
Show all popups:
marker.forEach(function(marker) {
var popup = marker.getPopup();
marker.bindPopup(popup.getContent()).openPopup();
});
Close all popups:
map.eachLayer(function (layer) {
layer.closePopup();
});

How to get all markers on Leaflet

I have a listener that will detect changes of the location of objects on databases. It will pass all the information of the object that is being changed.
I want to get all markers from the current map and find the marker that is affected. Once found, update the location.
But, I am still looking for the best ways to get all markers from the map and then I can update the location.
var map = L.map('map').setView([37.78541,-122.40787], 13);
var markers = new L.FeatureGroup();
var mapLink =
'OpenStreetMap';
L.tileLayer(
'https://{s}.tiles.mapbox.com/v4/examples.map-i87786ca/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoiZ2Vja29iIiwiYSI6IndzVjRGN0kifQ.lToORsjE0lkt-VQ2SXOb-Q', {
attribution: '© ' + mapLink + ' Contributors',
maxZoom: 18,
}).addTo(map);
var marker = createCircleMarker([44.977368, -93.232659]);
marker._id = "69"; // Id of the marker
map.addLayer(marker);
var socket = io();
socket.on('update location', function(obj) {
// Get all markers and find markers with attribute obj.name to
// update the location to [obj.lat,obj.lon]
});
Use eachLayer method on L.map. Like
map.eachLayer(function (layer) {
if (layer.options.name === 'XXXXX') {
layer.setLatLng([newLat,newLon])
}
});
Documentation at http://leafletjs.com/reference-1.2.0.html#map-eachlayer
To add an option without using map.eachLayer; all layers within the map are internally stored in map._layers.
Use
map._layers.forEach(function (layer) {
...
});
to iterate over ALL Layer elements. Not just the ones currently visible.

Google Map Makers Sprite in OSX not displaying

I can't seem to get the my map markers to display when I use an image sprite on the iphone. They appear when I use the standard google map markers on iphone and when viewing the site in the desktop the sprite icons work fine.
Here is the code I use to create the markers, I am using Zepto but JQuery could as easily apply.
$.ajax({
dataType: 'jsonp',
url: myLocations.LocatorUrl,
timeout: 8000,
success: function(data) {
var infoWindow = new google.maps.InfoWindow();
var bounds = new google.maps.LatLngBounds();
$.each(data, function(index, item){
var data = item, pincolor,
latLng = new google.maps.LatLng(data.lat, data.lng);
var d = 'http://blah';
var pinImage = new google.maps.MarkerImage(d+"/assets/img/sprite.locator.png",
new google.maps.Size(24, 36),
new google.maps.Point(0,25),
new google.maps.Point(10, 34));
// Creating a marker and putting it on the map
var marker = new google.maps.Marker({
position: latLng,
map: map,
title: data.type,
icon: pinImage
});
bounds.extend(latLng); // Extend the Latlng bound method
var bubbleHtml = '<div class="bubble"><h2>'+item.type+'</h2><p>'+item.address+'</p></div>'; // Custom HTML for the bubble
(function(marker, data) {
// Attaching a click event to the current marker
google.maps.event.addListener(marker, "click", function(e) {
infoWindow.setContent(bubbleHtml);
infoWindow.open(map, marker);
});
markers.push(marker); // Push markers into an array so they can be removed
})(marker, data);
});
map.fitBounds(bounds); // Center based on values added to bounds
}, error: function(x, t, m) {
console.log('errors')
if(t==="timeout") {
alert("got timeout");
} else {
alert(t);
}
}
});
Got it. Turns out the images I was referencing were on localhost, when I swapped this to the actual IP address of my local machine it worked.