Reputation: 366
I am using leafletjs in my Rails app, adding the markers and using layer groups and overlays as "categories". I looked for examples and ideas on how I could bound/auto-zoom in and out based on the visible ("checked") overlays but couldn't find much.
Currently, I'm using a markers array that stores all the map markers and using the markers array to bound the map:
var group = L.featureGroup(markers);
map.fitBounds(group.getBounds());
But I'm not sure how to dynamically update the bounds based on the visible overlay markers on the map. This is what I have so far:
var markers = [];
// a sample of the map markers
var consulting = new L.LayerGroup();
<% @maps.each do |consulting| %>
<% if consulting.category == "Consulting" %>
markers.push( L.marker( [<%= raw consulting.latitude.to_json %>, <%= raw consulting.longitude.to_json %>]));
L.marker( [<%= raw consulting.latitude.to_json %>, <%= raw consulting.longitude.to_json %>], {icon: consultingIcon} )
.bindPopup( 'hello')
.addTo(consulting);
<% end %>
<% end %>
var education = new L.LayerGroup();
<% @maps.each do |education| %>
<% if education.category == "Education" %>
markers.push( L.marker( [<%= raw education.latitude.to_json %>, <%= raw education.longitude.to_json %>]));
L.marker( [<%= raw education.latitude.to_json %>, <%= raw education.longitude.to_json %>], {icon: educationIcon} )
.bindPopup( 'hello')
.addTo(education);
<% end %>
<% end %>
var mbAttr = '' +
'Imagery © <a href="http://mapbox.com">Mapbox</a>',
mbUrl = 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoidGVyZXNhc2hpaCIsImEiOiJjajU4cWFqNWgwMWgwMnFtazIycTJpbG51In0.4FH-NfH6b44wCc4BFodqWQ';
var grayscale = L.tileLayer(mbUrl, {id: 'mapbox.light', attribution: mbAttr}),
streets = L.tileLayer(mbUrl, {id: 'mapbox.streets', attribution: mbAttr});
var map = L.map('mapid', {
center: [43.6532, -79.3832],
zoom: 5,
scrollWheelZoom: false,
layers: [grayscale, consulting, education]
});
var baseLayers = {
"Grayscale": grayscale,
"Streets": streets
};
var overlays = {
"Consulting": consulting,
"Education": education
};
L.control.layers(baseLayers, overlays).addTo(map);
var group = L.featureGroup(markers);
map.fitBounds(group.getBounds());
Upvotes: 2
Views: 4102
Reputation: 53185
If I understand correctly, you have some overlays (consulting
and education
Layer Groups) in a Layers Control, and you would like your map to automatically fit bounds of the visible markers whenever the user toggles one of these overlays.
Then the difficulty is that you need to retrieve the list of visible markers, so that you can compute their bounds and have the map adjust accordingly.
An easy solution would be to use Leaflet.FeatureGroup.SubGroup plugin, an intermediate "parent" Feature Group that will get the markers, and to replace your category Layer Groups by SubGroups from that plugin. This way, when added to the map, those SubGroups will actually add their child markers into the parent group. It then becomes trivial to get the bounds of all visible markers:
var parentGroup = L.featureGroup().addTo(map),
subGroup1 = L.featureGroup.subGroup(parentGroup),
subGroup2 = L.featureGroup.subGroup(parentGroup);
// Add your markers into the appropriate SubGroup…
var overlays = {
'SubGroup 1': subGroup1,
'SubGroup 2': subGroup2
};
L.control.layers(null, overlays).addTo(map);
// Have the map adjust view anytime the user uses the Layers Control overlays.
map.on('overlayadd overlayremove', function () {
var bounds = parentGroup.getBounds();
// Fit bounds only if the Parent Group actually has some markers,
// i.e. it returns valid bounds.
if (bounds.isValid()) {
map.fitBounds(bounds);
}
});
Demo: https://jsfiddle.net/3v7hd2vx/354/
Note that you no longer need your overall markers
array to duplicate your markers into.
Disclaimer: I am the author of that plugin.
Upvotes: 2
Reputation: 28638
You can keep track of layers being added to and removed from the map using the layeradd
and layerremove
events. You'll need to build the bounds everytime one of your featuregroups gets added or removed. Working snippet with comments to elaborate:
var map = new L.Map('leaflet', {
center: [0, 0],
zoom: 0,
layers: [
new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
'attribution': 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors'
})
]
});
map.on('layeradd layerremove', function () {
// Create new empty bounds
var bounds = new L.LatLngBounds();
// Iterate the map's layers
map.eachLayer(function (layer) {
// Check if layer is a featuregroup
if (layer instanceof L.FeatureGroup) {
// Extend bounds with group's bounds
bounds.extend(layer.getBounds());
}
});
// Check if bounds are valid (could be empty)
if (bounds.isValid()) {
// Valid, fit bounds
map.fitBounds(bounds);
} else {
// Invalid, fit world
map.fitWorld();
}
});
var markers = new L.FeatureGroup([
new L.Marker([-30, -30]),
new L.Marker([30, -30]),
new L.Marker([-30, -60]),
new L.Marker([30, -60])
]).addTo(map);
var polylines = new L.FeatureGroup([
new L.Polyline([[-30, 30], [30, 60]]),
new L.Polyline([[30, 30], [-30, 60]])
]).addTo(map);
var control = new L.Control.Layers(null, {
'Markers': markers,
'Polylines': polylines
}).addTo(map);
body {
margin: 0;
}
html, body, #leaflet {
height: 100%;
}
<!DOCTYPE html>
<html>
<head>
<title>Leaflet 1.0.3</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/[email protected]/dist/leaflet.css" />
</head>
<body>
<div id="leaflet"></div>
<script type="application/javascript" src="//unpkg.com/[email protected]/dist/leaflet.js"></script>
</body>
</html>
Hope that helps, good luck.
Upvotes: 8