Refreshing PopUp content in GeoJSON layer in leaflet - popup

I am loading a geojson file using leaflet's L.geoJSON() to display a map.
Initially i have attahced a popup with each feature.The popup shows a certain property "Count". The code is:
var geoJSON = new L.geoJson(var_name,{
onEachFeature: function(feature,layer){layer.bindPopup('Count :'+feature.properties.Count);},
style: applyStyle
}).addTo(map);
This code works fine.
After this, the user initiates an event and i make an ajax call and assign new values to the property "Count" of each feature. I also use the setStyle function so that the style is reassigned based on new values of "Count". Here is the code:
$.ajax({
type: 'GET',
url: 'new_data.json',
dataType: 'json',
success: function(data){
var i;
for(i=0;i<48;i++){
var_name.features[i].properties.Count = data[i];
}
geoJSON.setStyle(applyStyle);
}
});
The style changes work well but the popup still contains the old value for the property "Count".
There is so setPopUp function like setStyle. So how do i make the popUp change its value?
In other words, can we call the onEachFeature method again after the geoJSON layer has been loaded?
P.S.: I am not using same popup content for each feature. After i make the ajax call, i update the "Count" property of each feature. I want the pop-up for each feature to show the value of its new value of "Count" property.

What you're doing now is using the setStyle method of your L.GeoJSON layer, which when called, iterates all the features contained in the layer and calls the setStyle method of each feature. (If the feature has a setStyle method). Since there is no setPopup method in L.GeoJSON. (it's a very rare use case if you would want the same popup content on each feature) you'll have to iterate the features in your GeoJSON layer yourself and set the new content on the popup itself:
var geojsonLayer = new L.GeoJSON(geojsonCollection, {
'onEachFeature': function (feature, layer) {
layer.bindPopup('Initial content');
}
}).addTo(map);
geojsonLayer.eachLayer(function (layer) {
layer._popup.setContent('Updated content')
});
Here's an example on Plunker: http://embed.plnkr.co/uYHC8jZtgls351YhsmPS/preview

Using the each layer function as suggested by #iH8 , i was able to do this in the following way:
var j = 0; geoJSON.eachLayer(function (layer){
layer._popup.setContent("new count = " + var_name.features[j++].properties.Count);

Related

Import Leaflet Markers from a external GeoJSON

I am starting with leaflet and wanted to display more information when clicking on a marker in a map using a sidebar.
I use the php call where the geoJeson called allande_geoJson is generated with all the information I need from my database:
In the example I'm using, a maker is generated so that when you click on it, the information is displayed in the sidebar:
var marker = L.marker([51.2, 7]).addTo(map).on('click', function () {
sidebar.toggle();
});
I wanted to enter the makers directly from my geoJeson and I am trying various ways with no success, such as:
var marker= L.marker(new L.GeoJSON(allande_geoJson)).addTo(map).on('click', function () {
sidebar.toggle();
});
any ideas?
The Markers are loaded automatically when you put the geojson data in the L.geoJSON() layer.
With onEachFeature you add the click event to each marker.
function onEachFeature(feature, layer) {
layer.on('click', function () {
sidebar.toggle();
});
}
L.geoJSON(allande_geoJson, {
onEachFeature: onEachFeature
}).addTo(map);

How can I pull a URL stored inside a Mapbox dataset and add it to a 'click' function on a popup?

I have a function code that pulls data from the "description" field and displays it inside a popup on mouseenter, but can someone help me figure out how to pull in the URL stored inside "linkurl" and use it to open that URL when the icon is clicked? The popup displays properly over an icon, but I can't figure out how to bring the URL in as a link on click. Here's the code I'm working with:
map.on('load', function() {
// Create a popup, but don't add it to the map yet.
var popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
// POINTS OF INTEREST
function showPopup(e) {
// Updates the cursor to a hand (interactivity)
map.getCanvas().style.cursor = 'pointer';
// Show the popup at the coordinates with some data
popup
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(checkEmpty(e.features[0].properties.description))
.addTo(map);
}
function hidePopup() {
map.getCanvas().style.cursor = '';
popup.remove();
}
function checkEmpty(info) {
return (info) ? info : "No data";
}
// CHANGE: Add layer names that need to be interactive
map.on('mouseenter', 'points-of-interest-2019', showPopup);
map.on('mouseleave', 'points-of-interest-2019', hidePopup);
});
To add a link within the Popup itself, you can use Popup#setHTML in conjunction with the <a> tag define a hyperlink. For example:
// Show the popup at the coordinates with some data
var properties = e.features[0].properties;
popup
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(
'<a href=\'' + properties.linkurl + '\'>'
+ checkEmpty(properties.description)
+ '</a>')
.addTo(map);
Since creating a Popup with the GL JS API automatically creates a DOM element as outlined in the source code here, there is currently not a way to make the entire Popup clickable to navigate to a particular link. You could instead use Popup#setHTML along with some minimal CSS to create a link which wraps the entire content added to the Popup, so that clicking the content of the Popup will open the link.
Alternatively, if you are using Marker instances and would like clicking on the marker itself to open a link, you could utilize the options.element parameter to specify a DOM element wrapped in a link to use as a marker. For example, consider a slight modification to this example:
var el = document.createElement('a');
el.href = 'https://www.mapbox.com/'
el.className = 'marker';
el.style.backgroundImage =
'url(https://placekitten.com/g/' +
marker.properties.iconSize.join('/') +
'/)';
el.style.width = marker.properties.iconSize[0] + 'px';
el.style.height = marker.properties.iconSize[1] + 'px';
// add marker to map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.addTo(map);

Editing/Removing GeoJSON layers from a featureGroup in Mapbox using Leaflet

I'm using Mapbox with Leaflet for drawing, editing and removing polygons etc. Every time I create a new polygon, I convert them to a GeoJSON layer and then add it to the featureGroup that I created, because I want to associate each layer with an ID property that I can use later. This is what I have:
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
},
draw: {
polygon: {
allowIntersection: false
},
polyline: false,
rectangle: false,
circle: false,
marker: false
}
}).addTo(map);
map.on('draw:created', addPolygon);
map.on('draw:edited', editPolygon);
map.on('draw:deleted', deletePolygon);
function deletePolygon(e) {
featureGroup.removeLayer(e.layer);
}
function editPolygon(e) {
featureGroup.eachLayer(function (layer) {
layer.eachLayer(function (layer) {
addPolygon({ layer: layer });
});
});
}
function addPolygon(e) {
var geojsonstring = e.layer.toGeoJSON();
var geojson = L.geoJson(geojsonstring);
featureGroup.addLayer(geojson);
}
When I do this, creating polygons is not a problem. But when I try to edit or delete polygons, it doesn't work properly.
When I try to edit a polygon, it tells me "TypeError: i.Editing is undefined". It doesn't allow me to cancel editing as well.
When I try to delete a polygon, it is not displayed in the map anymore, but it is still not removed from the featureGroup.
What am I doing wrong here?
Edit: The way I'm currently doing this is the same way that ghybs has suggested. But the problem is, once all the edits are done, the polygons are saved to a database (I convert it to a WKT string to save in SQLServer). And when the page is loaded the next time, the polygons are loaded back from the database, and the user can edit or delete them and save it back to the database.
As it is right now, when the user makes the edit and saves them again, it only creates duplicate polygons. and I don't know of any way to connect the edited polygons to the ones from the database.
So I thought if I could convert them to GeoJSON and assign an ID property to each layer (something like ID=0 if it is a new layer, and the corresponding polygonID from the database if it is loaded from the database). So that when they are saved again, I can check this ID and based on that, I can either update the available polygon, or create a new polygon in the database.
Is there a better way of doing this?
Not sure exactly why in addPolygon you go through a GeoJSON object that you convert back into a Leaflet layer group through L.geoJson?
You could have directly added the created layer, as in Leaflet.draw "draw:created" example:
function addPolygon(e) {
var layer = e.layer;
var feature = layer.feature = layer.feature || {}; // Initialize layer.feature
// use the feature.id: http://geojson.org/geojson-spec.html#feature-objects
feature.id = 0; // you can change it with your DB id once created.
featureGroup.addLayer(layer);
// record into DB, assuming it returns a Promise / Deferred.
recordToDb(layer.toGeoJSON()).done(function (result) {
feature.id = result.id; // Update the layer id.
});
}
As for the reason for you error, it is due to the fact that you add a (GeoJSON) Layer Group to your featureGroup, which Leaflet.draw plugin does not know how to handle. You must add only "non group" layers.
See also: https://gis.stackexchange.com/questions/203540/how-to-edit-an-existing-layer-using-leaflet

Mapbox Filter Markers loaded via json

I am looking for solution to add filter (not checkbox) to my site. I have this code loading blank map from Mapbox and added Markers from JSON file. I was trying to add setFilter function, but probably I am using it wrong. I would like to filter items by category property from my JSON file.
<script>
L.mapbox.accessToken = '*************';
var baseLayer = L.mapbox.tileLayer('test****');
var markers = L.markerClusterGroup();
// CALL THE GEOJSON HERE
jQuery.getJSON("locations.geojson", function(data) {
var geojson = L.geoJson(data, {
onEachFeature: function (feature, layer) {
// USE A CUSTOM MARKER
layer.setIcon(L.mapbox.marker.icon({'marker-symbol': 'circle-stroked', 'marker-color': '004E90'}));
// ADD A POPUP
layer.bindPopup("<h1>" + feature.properties.title + "</h1><p>" + feature.properties.description + "</p><p><a href=' + feature.properties.website + '>" + feature.properties.website + "</a></p>");
layer.on('mouseover', function (e) {
this.openPopup();
});
layer.on('mouseout', function (e) {
this.closePopup();
});
}
});
markers.addLayer(geojson);
// CONSTRUCT THE MAP
var map = L.map('map', {
searchControl: {layer: markers},
zoom: 6,
center: [51.505, -0.09],
maxZoom: 13
})
.setView([62.965, 19.929], 5)
.fitBounds(markers.getBounds());
baseLayer.addTo(map);
markers.addTo(map);
L.control.fullscreen().addTo(map);
});
</script>
Could you please help me add filter buttons (something like here: https://www.mapbox.com/mapbox.js/example/v1.0.0/filtering-markers)
PS: I think I tried all examples from Mapbox website, yet seems my skills are very limited here.
Thank you
I was trying to add setFilter function, but probably I am using it wrong. I would like to filter items by category property from my JSON file.
This code example is using L.geoJson to load the markers into your map. Like the Mapbox example, you'll need to use L.mapbox.featureLayer instead, since it includes the setFilter function and L.geoJson does not.
tmcw's answer is correct, L.mapbox.featureLayer is confusing
this tutorial helped me!
https://www.mapbox.com/mapbox.js/example/v1.0.0/custom-marker-tooltip/

Cant open popup programmatically

I have a map on which im loading the markers with geoJSON.
When the map loads i run a function buildVisibleSys which is responsible to build a list of currently visible systems on the map.
That function looks like this:
buildVisibleSys = function() {
var bounds, visibleSys;
visibleSys = [];
bounds = map.getBounds();
return systemLocations.eachLayer(function(marker) {
var link;
link = onScreenEl.appendChild(document.createElement('a'));
link.href = '#';
link.id = "marker" + marker._leaflet_id;
link.innerHTML = marker.options.title;
link.onclick = function() {
marker.openPopup();
map.panTo(marker.getLatLng());
};
});
};
map.on('load', buildVisibleSys);
In this function, for each layer im getting some data and building a html block with the names of each marker. Each of those names, associated to the link var, have a onclick event attached that will center the map on the correspondent marker. This all works except for the marker.openPopup() call i also have on that onclick event.
Any idea of what am I missing here?
I've also made a demo of the code available here:
http://jsfiddle.net/lmartins/z8wBW/
UPDATE:
Even more confusing to me is that with mouseover the same method works without a problem, that is, in the function above the following code do open the popup:
link.onmouseover = function(ev) {
marker.openPopup();
marker._icon.classList.add('is-active');
};
Change your link handler to
link.onclick = function(e) {
marker.openPopup();
map.panTo(marker.getLatLng());
e.stopPropagation();
e.preventDefault();
};
The click of the link to open the popup is bubbling down to the map and closing the popup right after it's opened.