Get extent on all layers in Openalyers 5 - openlayers-5

I'm trying to do quite a trivial activity: fit a map to the extent of all layers.
But I'm stuck:
Here is my code:
import { createEmpty, getWidth, getHeight, extend } from 'ol/extent.js';
function centraTuttiLayer(map)
{
var extent = createEmpty();
//modifico l'estensione del layer per contenere tutto
map.getLayers().forEach(function(l) {
extend(extent, l.getExtent());
});
map.getView().fit(extent);
}
I alway get getExtent() as empty.
I've also tried l.getSource() and then getExtent(), but with no luck.
What am I doing wrong?
Thanks
Massimo

You can`t get extent of layer. Do it with source
map.getLayers().forEach(function(l) {
let source = l.getSource();
if(!(source instanceof OSM)){
extend(extent, source.getExtent());
}
});
map.getView().fit(extent,{
maxZoom: 15,
padding: [50,50,50,50]
});

Related

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]);
}

Bing Maps directions with clustering

I am able to draw the Map with maneuverPoints.
Below is the screen shot for map
Below is the code for the routing.
var maneuverPoints = this.model.get("maneuverPoints");
if (maneuverPoints) {
var routePoints = [];
_.each(maneuverPoints, function (point) {
routePoints.push(new MsMaps.Location(point.latitude, point.longitude));
});
var routeOptions = {
strokeColor: new MsMaps.Color(1, 65, 255, 35),
strokeThickness: 3
};
var routeShape = new MsMaps.Polyline(routePoints, routeOptions);
map.entities.push(routeShape);
}
Now I am trying to implement clustering and I am able to do it as below:
The pink pushpins are the clusters.
Code for clustering is as below:
Microsoft.Maps.loadModule("Microsoft.Maps.Clustering", function () {
var clusterLayer = new Microsoft.Maps.ClusterLayer(pushpins);
map.layers.insert(clusterLayer);
clusterLayer.setPushpins(pushpins);
map.setView(viewOptions);
var maneuverPoints = this.model.get("maneuverPoints");
if (maneuverPoints) {
var routePoints = [];
_.each(maneuverPoints, function (point) {
routePoints.push(new MsMaps.Location(point.latitude, point.longitude));
});
var routeOptions = {
strokeColor: new MsMaps.Color(1, 65, 255, 35),
strokeThickness: 3
};
var routeShape = new MsMaps.Polyline(routePoints, routeOptions);
map.entities.push(routeShape);
}
I am not able to do clustering and routing at same time. Is it possible to do both at same time?
Your code isn't doing routing by the looks of things, it is simply taking route line points and rendering them as a polyline. I don't see the directions module being used in your code. If you are using that directions module, why don't you simply use the built in rendering? You can customize how it looks. If you continue using a polyline as you are, consider adding it to a layer, it will make it easier to manage later.
That said, the code you provided is simply clustering and drawing a polyline. This works fine when I test this scenario.
Any reason why you are calling setPushpins in the cluster layer? You already passed the pushpins in when creating the layer, no need to pass them in again.

Leaflet-Draw: Get polygon latLng in 'draw:editvertex' event

When a draw:editvertex event fires, how can I get information about the polygon which triggered it?
this.map.on('draw:editvertex', function (e) { debugger;
var layers = e.layers;
// I want to get current polygon latLng here
}.bind(this));
This approach works for me (but doesn't feel like best practice) –
In my draw:editvertex handler I loop through the target._layers and look for the edited property:
map.on('draw:editvertex', function(e) {
for (thisLayer in e.target._layers) {
if (e.target._layers.hasOwnProperty(thisLayer)) {
if (e.target._layers[thisLayer].hasOwnProperty("edited")) {
console.log("we think we found the polygon?");
console.log(e.target._layers[thisLayer]);
// the updated Polygon array points are here:
newPolyLatLngArray = e.target._layers[thisLayer].editing.latlngs[0];
}
}
};
});
...like I said, this doesn't feel Awesome, but it is working for me so far.
There are not only layers in e, but also the target layer poly can be approached easily.
map.on('draw:editvertex', function (e) {
var poly = e.poly;
var latlngs = poly.getLatLngs(); // here the polygon latlngs
});

Leaflet: how to change map center and zoom level clicking on circleMarker?

I'm working on a map with a few circleMarkers. When a user is clickin on one of this circleMarker, I would like to center the map on this circleMarker and to zoom in. It works when I try this on multipolygon layers, but I didn't succeed on circleMarkers. Does anyone can help me?
Here is the code for my circleMarkers:
<script>
var map = L.map('map', {
center: [41.8, 12.5],
zoom: 5,
zoomControl:true, maxZoom:15, minZoom:4,
});
var feature_group = new L.featureGroup([]);
var raster_group = new L.LayerGroup([]);
var basemap = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri — Source: Esri',
});
basemap.addTo(map);
function style1(feature) {
return {
weight: 2,
radius: 10,
opacity: 1,
color: 'black',
weight: 1,
fillOpacity: 1,
fillColor: 'red'
};
}
L.geoJson(villes, {
style: style1,
pointToLayer: function (feature, latlng)
{
return L.circleMarker(latlng).bindLabel( feature.properties.Name, {className: "ville", noHide: true });
}
}
)
.addTo(map)
.showLabel;
</script>.
Here is a link to the complete map.
Thank you.
This can be achieved really simply.
Let's assume marker points to your leaflet marker and map to your leaflet map.
Quick way (recommended)
marker.on("click", function(event) {
map.setView(marker.getLatLng(), 16);
}, marker);
Here, I don't even compute the needed zoom level, because I assume leaflet will have to zoom to its max level anyway.
I could also have added my marker to a L.LayerGroup, even if the main point of a LayerGroup is to group multiple markers.
Anyhow, the more precise way
marker.on("click", function(event) {
map.setView(marker.getLatLng(), map.getBoundsZoom(layerGroup.getBounds());
}, marker);
Note that the quick way will do it nicely too. The second solution might seem more precise but it also slower, and implies a use of LayerGroup which is not the way it has been designed to work (create a new layergroup for each marker).
Don't forget to take yout time reading the docs, it's well-designed and pretty easy to understand.
I find the solution:
function onClick(e) {map.setView(this.getLatLng(),15);}
L.geoJson(villes, {
style: style1,
pointToLayer: function (feature, latlng)
{
return L.circleMarker(latlng).bindLabel( feature.properties.Name, {className: "ville", noHide: true }).on('click', onClick);
}
}
)
.addTo(zoom1)
.showLabel;
Thanks Stranded Kid for your help.

How to display Leaflet Static Labels for objects in a LayerGroup?

I'm trying to add a static label to a few CircleMarkers I've created. These markers are added to a LayerGroup and then added to the map. I've read that I need to call .showLabel() after I've added it to the object to the map. But since I am building the LayerGroup first, then adding it to the map I'm unsure of how to do this.
I thought about using L.LayerGroup.eachLayer but I'm unsure which object I would actually call the .showLayers() on. My code is below, any help is appreciated, thanks!
var jsonLayers = new L.LayerGroup();
jsonLayers.addLayer(L.geoJson(mapFeature.features[i], {
style: function (feature) {
return feature.properties && feature.properties.style;
},
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
var newCircle = L.circleMarker(latlng, {
radius: 5,
fillColor: fColor,
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
newCircle.bindLabel(feature.properties.name, { noHide: true });
return newCircle;
}
}));
map.addLayer(jsonLayers);
It turns out that static labels are not supported on CircleMarkers. To solve this, I added code to Leaflet.label to allow this. I've also issued a pull request in case someone else would like to do the same thing I am doing.