Remove custom geojson markers (loaded with Leaflet) after zoom - javascript

I'm adding some points to a map using the code below and they look great. I'm also adding some json polygons without issue.
When a certain zoom level is reached I would like the points and polygons to turn off. Using the map.removeLayer(name of polygon) turns off the polygon perfectly and then zoom back out I use map.addLayer(name of polygon) and they come back (using 'zoomend' and if else statement).
The point features do not react to the removeLayer function like the polygons do. I've also tried to harvestPoints.setOpacity(0) which does not work either. What code should I use to turn these geojson markers "on" and "off" like the polygon features?
function onEachPoint(feature, layer) {
layer.bindPopup(feature.properties.MGNT_AREA.toString());
layer.on('click', function (e) { layer.openPopup(); });
layer.bindLabel(feature.properties.MGNT_AREA.toString(), {
noHide: true,
className: "my-label",
offset: [-2, -25]
}).addTo(map);
};
var areaIcon = {
icon: L.icon({
iconUrl: 'labels/MonitoringIcon.png',
iconAnchor: [20, 24]
})
};
var harvestPoints = new L.GeoJSON.AJAX('labels/dfo_areas_point.json', {
onEachFeature: onEachPoint,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, areaIcon);
}
});

Not sure exactly what is the root cause for your issue, as we are missing how exactly you reference yours points (markers) when you try to remove them from the map.
Normally, there should be no difference between polygons and points (markers) to achieve what you described (removing the layers from the map at certain zoom level, and add them back at other zooms).
Note that setOpacity is a method for L.Markers, whereas you apply it to harvestPoints which is your geoJson layer group.
What may happen is that you add individual points (markers) to your map (last instruction in your onEachPoint function), but try to remove the layer group harvestPoints from map. Because it seems to be never added to the map, nothing happens.
If you want to turn on/off ALL the points in your harvestPoints layer group at the same time, then you would simply add / remove that layer group to / from the map, instead of adding individual markers:
var harvestPoints = L.geoJson.ajax('labels/dfo_areas_point.json', {
onEachFeature: onEachPoint,
pointToLayer: function (feature, latlng) {
// make sure `areaIcon` is an OPTIONS objects
return L.marker(latlng, areaIcon);
}
}).addTo(map); // Adding the entire geoJson layer to the map.
map.on("zoomend", function () {
var newMapZoom = map.getZoom();
if (newMapZoom >= 13) {
map.addLayer(harvestPoints);
} else {
// Removing entire geoJson layer that contains the points.
map.removeLayer(harvestPoints);
}
});
Side note: popups open on click by default, you should not need to add an on click listener for that?
Demo: http://jsfiddle.net/ve2huzxw/62/

Related

Leaflet.js: zoom map to visible markers

I have a leaflet.js map with a collection of markers, which can be shown/hidden in a legend div done with plugin Leaflet.StyledLayerControl (I think this is not too relevant but just in case).
I can capture the event when layers are shown or hidden and in that event I would like to zoom to fit all visible markers.
There are a few questions in SO with a very similar title but most of them pretend to fit zoom to a known collection of markers, which is not my case (some other questions with very similar subject refer to Google maps, not leaflet).
So, the question would be:
A) Can I set zoom to fit all visible markers?
B) or, how can I get an array of all visible markers to apply the other solutions seen?
This is how I create markers on map:
const icon_general = L.divIcon({html: '<i class="fas fa-map-marker fa-2x"></i>', iconSize: [20, 20], className: 'node_icon'});
var node_10031 = L.marker([40.7174605,-3.9199218],{ icon: icon_general}).addTo(layer1);
node_10031.bindPopup('<h2>Title</h2>');
var node_10032 = L.marker([40.7184576,-3.9202692],{ icon: icon_general}).addTo(layer1);
node_10032.bindPopup('<h2>Title</h2>');
var node_10032 = L.marker([40.7361371,-3.9453966],{ icon: icon_general}).addTo(layer2);
node_10032.bindPopup('<h2>Title</h2>');
Layers are then hidden or shown and I can capture that event, that is where I want to modifiy zoom or loop through visible markers.
EDIT (final solution based on Seth Lutske response):
This is my final solution, maybe it's less eficient as on each click it loops through all visible markers in map, but after some attempts this was the succesful one (I wasn't able to manage properly the individual events for add/remove layer):
function setZoom2Visible()
{
var visibleLayerGroup = new L.FeatureGroup();
mymap.eachLayer(function(layer){
if (layer instanceof L.Marker)
visibleLayerGroup.addLayer(layer);
});
const bounds = visibleLayerGroup.getBounds();
mymap.fitBounds(bounds);
}
$('.menu-item-checkbox input[type=checkbox]').click(function(){
setZoom2Visible();
});
Create a featureGroup and add the markers to it:
const myGroup = L.featureGroup([node_10031, node_10032, node_10033]);
On whatever event you're using to capture the markers being added to the map, you can get the bounds of the featureGroup and set the map's bounds to that:
function onMarkersAddedEventHandler(){
const bounds = myGroup.getBounds();
map.fitBounds(bounds);
}
Now if you're saying that each marker has its own toggle in the UI, it's a bit more complicated. You'll have to attach an event to each marker's UI checkbox, and on change of that checkbox, add or remove the marker from the group, then reset the bounds:
const node_10031_checkbox = document.querySelector('input[type="checkbox"]#10031');
function onMarkersAddedEventHandler(e){
if (e.target.checked){
myGroup.addLayer(node_10031)
} else {
myGroup.removeLayer(node_10031)
}
const bounds = myGroup.getBounds();
map.fitBounds(bounds);
}

Fill Leaflet popup on click when acctually needed

I have a map with a lot of markers and a complex popup content, generated by a function called popupcontent(), which takes a lot of time to compute when it is done for all markers on the map with the oneachfeature function.
Is there a way to trigger a function in a popup only when it is actually opened instead of generating all the popups in the beginning? This would speed up loading time a lot.
This is my code so far (I am using the markerclusterer extension):
var geojson1 = L.geoJson(bigJson,{
onEachFeature: function (feature, layer) {
layer.bindPopup(popupcontent(feature,layer));
}
})
.addLayer(tiles);
var markers = L.markerClusterGroup({
spiderfyOnMaxZoom: true,
showCoverageOnHover: true,
zoomToBoundsOnClick: true,
disableClusteringAtZoom: 10,
removeOutsideVisibleBounds:true
});
var geoJsonLayer = L.geoJson(bigJson, {
});
markers.addLayer(geojson1);
map.addLayer(markers);
map.fitBounds(markers.getBounds());
Demo: http://stefang.cepheus.uberspace.de/farmshops/
I think what you're looking for is something like so (if your layer is an interactive layer):
onEachFeature: function (feature, layer) {
layer.once("click", ()=>{
layer.bindPopup(popupcontent(feature,layer)).openPopup();
});
}
Use "once" instead of "on" so it only gets binded once when the layer is clicked.

Styling individual markers in markercluster

I am trying to style individual markers, or clusters of size 1, based on some feature property.
var markers = L.markerClusterGroup();
function onEachFeature(feature, layer) {
if (feature.properties.EncounterType && feature.properties.Year) {
layer.bindPopup(feature.properties.EncounterType + " in " +
feature.properties.Year);
}
}
function style(feature) {
switch (feature.properties.EncounterType) {
case 'Shooting':
return {
color: "ff0000"
};
case 'Sighting':
return {
color: "0000ff"
};
case 'Hunting':
return {
color: "ff0000"
};
}
}
var geoJsonLayer = L.geoJSON(storer, {
onEachFeature: onEachFeature
}, {
style: style
});
markers.addLayer(geoJsonLayer);
map.addLayer(markers);
The onEachFeature function successfully creates the popups. However, the style function does not change the color of the clusters of size 1. I've tried using the iconCreateFunction when initializing the markerclustergroup, however, that did not work either.
Your style option is separated in a 3rd argument of your call to L.geoJSON factory, whereas it should have been placed within the 2nd argument, alongside onEachFeature option.
var geoJsonLayer = L.geoJSON(storer, {
onEachFeature: onEachFeature,
style: style
});
But that is probably not the only reason for your issue.
style option will apply to vector shapes (polylines, polygons, etc.), i.e. to non-point data. It may also apply to Circle Markers, which can be used for Point type geometries, but you have to explicitly create them (typically through the pointToLayer option).
Those non-point data cannot be handled by Leaflet.markercluster.
Therefore if you see "clusters of size 1" (I guess you mean markers), they come from unstyled Point type geometries in your storer GeoJSON data.
This is a normal marker:
It is a PNG image that cannot be styled.
If you want to customize the appearance of your Point geometries, use custom icons, a plugin that provide such custom icons, or Circle Markers which you can modify the colour easily (including through the style option).
For example, if you were to choose that last option, you could do something like:
var geoJsonLayer = L.geoJSON(storer, {
pointToLayer: function (geoJsonPoint, latlng) {
return L.circleMarker(latlng);
},
onEachFeature: onEachFeature,
style: style
});

MapBox pop-up descriptions on markers not showing when clicking

I've integrated a MapBox map with marker clustering. For some reason the descriptions/titles will not show when you click on the marker.
Any idea why this is happening or how to fix it?
The page: https://vpnarea.com/front/member/signuptest
My MapBox code:
<script>
L.mapbox.accessToken = 'pk.xxxxxxxxxxxxxxxxxxxxxxx';
var markers = L.markerClusterGroup();
$.getJSON("https://vpnarea.com/data2.geojson-copy", function(data) {
var geojson = L.geoJson(data, {
onEachFeature: function (feature, layer) {
layer.setIcon(L.mapbox.marker.icon({'marker-color': 'f5c200','marker-size': 'small'}));
}
});
markers.addLayer(geojson);
// CONSTRUCT THE MAP
var map = L.mapbox.map('map1', 'vpnarea.m9b2pf4n') .setView([60, -55], 3);
markers.addTo(map);
});
</script>
You'll need to bind popups in order for them to appear: Mapbox.js's L.mapbox.featureLayer does this by default, but you're using L.geoJson, which does not. So you'll need to check out the Leaflet documentation for .bindPopup and use it.

Update Leaflet GeoJSON layer with data inside bounding box

I'm using leaflet/JavaScript for the first time and I want to display a map, with a GeoJSON layer which change on every move… To only show points on the area.
This is my code source:
// Function to refresh points to display
function actualiseGeoJSON() {
// Default icon for my points
var defaultIcon = L.icon({
iconUrl: '../images/icones/cabane.png',
iconSize: [16, 16],
iconAnchor: [8, 8],
popupAnchor: [0, -8]
});
// We create each point with its style (from GeoJSON file)
function onEachFeature(feature, layer) {
var popupContent = '' + feature.properties.nom + "";
layer.bindPopup(popupContent);
var cabaneIcon = L.icon({
iconUrl: '../images/icones/' + feature.properties.type + '.png',
iconSize: [16, 16],
iconAnchor: [8, 8],
popupAnchor: [0, -8]
});
layer.setIcon(cabaneIcon);
}
// We download the GeoJSON file (by using ajax plugin)
var GeoJSONlayer = L.geoJson.ajax('../exportations/exportations.php?format=geojson&bbox=' + map.getBounds().toBBoxString() + '',{
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {icon: defaultIcon});
}
}).addTo(map);
}
// We create the map
var map = L.map('map');
L.tileLayer('http://maps.refuges.info/hiking/{z}/{x}/{y}.png', {
attribution: '© Contributeurs d\'OpenStreetMap',
maxZoom: 18
}).addTo(map);
// An empty base layer
var GeoJSONlayer = L.geoJson().addTo(map);
// Used to only show your area
function onLocationFound(e) {
var radius = e.accuracy / 2;
L.marker(e.latlng).addTo(map);
actualiseGeoJSON();
}
function onLocationError(e) {
alert(e.message);
actualiseGeoJSON();
}
function onMove() {
// map.removeLayer(GeoJSONlayer);
actualiseGeoJSON();
}
map.locate({setView: true, maxZoom: 14});
// Datas are modified if
map.on('locationerror', onLocationError);
map.on('locationfound', onLocationFound);
map.on('moveend', onMove);
I have tried to remove the layer in my first function but GeoJSONlayer is not defined
I have tried to remove the layer in onMove() but nothing appears
I have tried to remove the layer in moveend event but I have an syntax error…
If somebody can help me…
Sorry for my bad English, French guy ith french function names
I see you are using the leaflet ajax plugin.
The simplest way to get your map to work is to download all available data right at the start, providing a giant bounding box, and add it to the map just once. This will probably work just fine, unless there's insanely many cabins and stuff to download.
But if you wish to refresh the data regularly, based on the bounding box, you can use the refresh method in the leaflet-ajax plugin:
you can also add an array of urls instead of just one, bear in mind
that "addUrl" adds the new url(s) to the list of current ones, but if
you want to replace them use refresh e.g.:
var geojsonLayer = L.geoJson.ajax("data.json");
geojsonLayer.addUrl("data2.json");//we now have 2 layers
geojsonLayer.refresh();//redownload those two layers
geojsonLayer.refresh(["new1.json","new2.json"]);//add two new layer replacing the current ones
So initially:
var defaultIcon = ...
function onEachFeature(feature, layer) ...
// Do this in the same scope as the actualiseGeoJSON function,
// so it can read the variable
var GeoJSONlayer = L.geoJson.ajax(
'../exportations/exportations.php?format=geojson&bbox='
+ map.getBounds().toBBoxString() + '',{
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {icon: defaultIcon});
}
}).addTo(map);
And then for each update:
function actualiseGeoJSON() {
// GeoJSONLayer refers to the variable declared
// when the layer initially got added
GeoJSONlayer.refresh(
['../exportations/exportations.php?format=geojson&bbox='
+ map.getBounds().toBBoxString() + '']);
}
Alternatively, you could set the layer as a property of the map, instead of as a var:
map.geoJsonLayer = L.geoJson.ajax(...)
And later refer to it as follows:
map.geoJsonLayer.refresh(...)
this leaflet plugin is more suitable for your purpose, manage map events and zoom.
Caching remote requests and much more.
http://labs.easyblog.it/maps/leaflet-layerjson/
var ajaxUrl = "search.php?lat1={minlat}&lat2={maxlat}&lon1={minlon}&lon2={maxlon}";
map.addLayer( new L.LayerJSON({url: ajaxUrl }) );
Extend L.GeoJSON with more features and support ajax or jsonp request. See the source code comments for more documentation.

Categories