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);
}
Related
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/
I have a polygon which is editable so when i start to edit it it shows small icon on it.
Below in picture an arrow icon (for undo changes).
so i want to add another icon by my own so user can delete that polygon by clicking on it. So ho to add icon there?
Update
Here is what i do so far
Link
Now i just want to add icon when polygon complete event fired.
So you've already got the event listener for when the polygon is complete.
What you could do is add a marker in the middle of your polygon, with a custom icon that looks similar or identical to the 'undo' icon Google are using.
google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
drawingManager.setDrawingMode(google.maps.drawing.OverlayType.FALSE);
// find out the paths of this polygon
var path = polygon.getPath();
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < path.length; i++) {
bounds.extend(path.getAt(i));
}
var centre = bounds.getCenter();
// add a marker here:
var marker = new google.maps.Marker({
position: centre,
map: map,
icon: 'https://google-developers.appspot.com/maps/documentation/javascript/examples/full/images/beachflag.png'
});
});
I am developing a website that has a mapping and i am using leaflet. Now im on the part that i will hide /show markers that i made.
below is my code finding the image that i want and use it as a marker
var Icon1 = L.icon({
iconUrl: 'legends/fire.GIF',
iconSize: [170, 120], // size of the icon
iconAnchor: [100, 120], // point of the icon which will correspond to marker's location
popupAnchor: [-7, -80] // point from which the popup should open relative to the iconAnchor
the other one below is my code when putting the mark on the map.
function mark()
{
if (select1.value === "Fire"){
var note = document.getElementById('note');
var datepick = document.getElementById('demo1');
var timepick = document.getElementById('timepick');
map.on('click', function(e){
var marker = new L.Marker(e.latlng,{icon: Icon1});
marker.bindPopup("</a><br><strong>FIRE</strong></br><strong>Date:</strong>"+datepick.value+"</br><strong>Time:</strong>"+timepick.value+"</br><strong>Address:</strong>"+note.value+"<strong><br><strong>Suspect Sketch</strong><br><a href=legends/suspect.jpg rel=lightbox><img src = legends/suspect.jpg height=100 width = 100/>").addTo(map);
marker.on('dragend');
});
This is my code in hiding the marker.
script type="text/javascript">
function closure(marker){
var checkbox = document.getElementById("chbx")
$(chbx).click(function(){
if(map.hasLayer(marker)){
window.alert("I want to hide the marker");
}
window.alert("I want to show the marker");
})
}
</script>
This is just what i wanted.
1.Add A marker on the map
2.Hide/Show the marker in the map
3.Make this happen during run time or when i try it.
I try everything but still nothing happens.
What is the right thing to do to call my hide/show function in checkbox?
Here's a way to do it:
Define a function which takes marker as its argument, and with jQuery create a function to toggle the visibility of the layer:
function closure(marker){
$('#yourcheckbox id').click(function(){
if(map.hasLayer(marker)){
map.removeLayer(marker)
}
else {map.addLayer(marker)}
})
}
Than, inside the click event of the map, add the closure function:
map.on('click', function(e){
marker = new L.Marker(e.latlng).addTo(map);
closure (marker)
})
With .addTo(map) should works.
However, you can use addLayer and removeLayer for add/remove markers:
var marker = new L.Marker(e.latlng,{icon: Icon1});
marker.bindPopup('<div>Hello World</div>');
//add the marker to the map
map.addLayer(marker);
//remove the marker from the map
map.removeLayer(marker);
I assume that you have created the map
In some cases for hiding markers we may use such method, may be.
marker._icon.style.display = 'none';
if (value._popup._isOpen)
{
marker.closePopup();
}
For show markers after hiding.
marker._icon.style.display = '';
I have a database of locations which I want to be able to print on a map. Ideally there should be one map with multiple pins for each location you have toggled on. So click a button for location X and it shows up on the map. Click the button for location Y and it shows up on the same map. Click X again and it hides from the map.
Currently I have it so I click on X and the map gets redrawn centered around point X.
Here is the HTML for each button:
<input type='button' data-lat='38.89864400' data-long='-77.05283400'
data-when='20 Aug at 2:00am' value='Location X' class='click' />
The jQuery I'm using is:
jQuery(document).ready(
function initialize() {
jQuery("input.click").click(function() {
showOnMap(jQuery(this).data('lat'), jQuery(this).data('long'), jQuery(this).data('when'));
});
}
);
function showOnMap(lat, long, message) {
var myLatlng = new google.maps.LatLng(lat, long);
var mapOptions = {
zoom: 13,
center: myLatlng
}
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: message
});
google.maps.event.addDomListener(window, 'load', showOnMap);
}
Is there an easy way to switch from what I have to what I want? I've searched for a while but no one seems to be asking this use case in a browser, just Android (which I'm not doing).
Thanks!
There is an example in the documentation on how to hide/show markers. In short, a marker is:
hidden by setting its map to null
showed by setting its map to map
To do so, you will need to access each marker individually. If you have a definite number of locations, it can be done by naming them with different names (eg var markerLocationX, var markerLocationY, etc). Otherwise, the markers need to be stored in an array.
Supposing you have a definite number of known locations to toggle the markers, your javascript code may look like this:
function toggleMarker(markerName) {
if (markerName.getMap() == null) {
markerName.setMap(map);
} else {
markerName.setMap(null);
}
}
I have pins on my leaflet.js map where the image is determined by the status of the object they are representing. For example, online and offline users - online are green and offline are red. I do this by adding a class to the divIcon and then control the images with css.
I have now added marker clustering to my map. What I want to do is determine the color of the cluster by the majority of status' within the cluster. My first idea was to do something like this:
this.markers = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
// Use this somehow to filter through and look at the pin elements
console.log(cluster.getAllChildMarkers());
return new L.DivIcon({ html: /* ?? */ });
}
});
But unfortunately I am not able to access the HTML elements from the array returned from getAllChildMarkers.
Anyone have any ideas on how I might be able to do this? Or a way to get the pin's HTML element?
Thanks
EDIT:
Here is where I create my map pins (assigned to my backbone model's mapPin attribute):
that.mapPins.org = L.divIcon({
className: 'org-div-icon',
html: "<div class='org-status "+ org.getGroupStatus() +"'></div>",
iconSize: [35, 35],
iconAnchor: [18, 17]
});
And here is how I change the class dynamically:
$(model.get('mapPin')._icon).find('.org-status').attr('class', 'org-status ' + model.getGroupStatus());
I thought that I would be able to access _icon from the return of getAllChildMarkers like I do above, but it doesn't seem to be there.
You can use getAllChildMarkers to get all of the markers in the cluster. Once you have a marker, you can access its class with marker.options.icon.options.className. You can access the html with marker.options.icon.options.html
Here's some code that uses underscore.js functions to count the number of markers with each class, find the one that's most popular, and use that class for the cluster marker.
var markers = L.markerClusterGroup({
iconCreateFunction: function (cluster) {
var childMarkers = cluster.getAllChildMarkers();
// count how many there are of each class
var counts = _.countBy(childMarkers, function(marker) {
// class at icon level
//return marker.options.icon.options.className;
// class inside html
return $(marker.options.icon.options.html).attr('class');
});
// get the class with the highest count
var maxClass = _.invert(counts)[_.max(counts)];
// use this class in the cluster marker
return L.divIcon({ html: cluster.getChildCount(), className: maxClass });
},
});