Leaflet: layers control for WMS - leaflet

based on this tutorial (http://leafletjs.com/examples/layers-control.html), I've tried to add 2 layers for a WMS based solution. In the tutorial, they managed to add 2 layers (Street and GrayScales) and switch between them.
My code:
function getDefaultWmsValues() {
var layer1, layer2;
layer2= 'Street';
layer1= 'Satellite';
return {
url: "http://dummy.com/wms/service",
mapCenter: [1, 2],
startZoom: 15,
layer: [layer1,layer2],
imageFormat: 'image/jpeg',
autor: "WMS Dummy",
maxZoom: 17,
minZoom: 12,
version: '1.1.0',
interactiveMapBoundaries: [[123, 1234], [1245.164611, 17890.023279]],
usedProjection: L.CRS.EPSG4326
};
}
function getWmsConfig(wmsDefaultValues) {
return L.tileLayer.wms(wmsDefaultValues.url, {
layers: wmsDefaultValues.layer,
format: wmsDefaultValues.imageFormat,
version: wmsDefaultValues.version,
maxZoom: wmsDefaultValues.maxZoom,
minZoom: wmsDefaultValues.minZoom,
crs: wmsDefaultValues.usedProjection,
attribution: wmsDefaultValues.autor
});
}
function createLeafletMap(wmsDefaultValues) {
var map = L.map('map', {center: wmsDefaultValues.mapCenter, zoom: wmsDefaultValues.startZoom});
var wmsConfig = getWmsConfig(wmsDefaultValues);
wmsConfig.addTo(map);
L.control.scale().addTo(map);
map.setMaxBounds(wmsDefaultValues.interactiveMapBoundaries);
L.marker(wmsDefaultValues.mapCenter).addTo(map);
var baseMaps = {
"Layer Name 1": 'Satellite',
"Layer Name 2": 'Street'
};
L.control.layers(baseMaps).addTo(map);
return map;
}
var wmsDefaultValues = getDefaultWmsValues();
var leafletMap = createLeafletMap(wmsDefaultValues);
In the function getDefaultWmsValues(), I have 2 valid layers, layer1 and layer2. If I let either => layer: [layer1] or layer: [layer2], my code will alternatively show the desired layer.
However, when I try to configure both to be able to switch with
layer: [layer1,layer2], only one of the layer will be shown and the widget to switch between layers like in the tutorial (Grayscale / Street) seems to be broken=> it will only show one of the layers...
Any help would be very much appreciated!
PS: I've replaced variables with dummy data, but my code is really built like this snippet...

Few things to notice here,
you're adding both the layers to a single variable, so they can't be treated as a two layers and hence can't be viewed in control box as two layers.
Further, you specify that you want to switch between layers, i.e., you want to see only one layer at a time, so, by default this functionality could only be achieved if we set our layers as base layer as mentioned here
Hence, you first need to change the getDefaultWmsValues() function as below
function getDefaultWmsValues() {
var layer1, layer2;
layer2= 'Street';
layer1= 'Satellite';
return {
url: "http://dummy.com/wms/service",
mapCenter: [1, 2],
startZoom: 15,
layer1: [layer1],
layer2: [layer2],
imageFormat: 'image/jpeg',
autor: "WMS Dummy",
maxZoom: 17,
minZoom: 12,
version: '1.1.0',
interactiveMapBoundaries: [[123, 1234], [1245.164611, 17890.023279]],
usedProjection: L.CRS.EPSG4326
};
}
Similarly, you need to create modify getWmsConfig() function, and pass the layer attribute separately as shown below
function getWmsConfig(wmsDefaultValues, layer) {
return L.tileLayer.wms(wmsDefaultValues.url, {
layers: layer,
format: wmsDefaultValues.imageFormat,
version: wmsDefaultValues.version,
maxZoom: wmsDefaultValues.maxZoom,
minZoom: wmsDefaultValues.minZoom,
crs: wmsDefaultValues.usedProjection,
attribution: wmsDefaultValues.autor
});
}
Now, call the getWmsConfig() two times passing one layer each time
var wmsConfig1 = getWmsConfig(wmsDefaultValues,wmsDefaultValues.layer1);
var wmsConfig2 = getWmsConfig(wmsDefaultValues,wmsDefaultValues.layer2);
wmsConfig1.addTo(map);
wmsConfig2.addTo(map);
Now, add these two wmsConfig variables to control
var baseMaps = {
"Layer Name 1": wmsConfig1,
"Layer Name 2": wmsConfig2
};
Good Luck

Related

Add choropleth layer to Leaflet map

Trying to construct a Leaflet map. The goal is to place average points on the map for each country, using Choropleth. BindpopUp works. But for some reason, it doesn't show the borders of the countries like it was intended to but only the simple markers. Which is what I do not want.
var myMap = L.map("map", {
center: [40.7128, -74.0059],
zoom: 2.5
});
// Adding tile layer
L.tileLayer(
"https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}",
{
attribution:
'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox',
maxZoom: 18,
id: "mapbox.streets",
accessToken: API_KEY
}
).addTo(myMap);
var geojson;
// Grab the data with d3
d3.json("static/js/wine.json").then(function(data) {
// This should place borders for the countries
L.geoJson(data).addTo(myMap);
// Create a new choropleth layer
geojson = L.choropleth(data, {
// Define what property in the features to use
valueProperty: "points",
// Set color scale
scale: ["#ffffb2", "#b10026"],
// Number of breaks in step range
steps: 10,
// q for quartile, e for equidistant, k for k-means
mode: "q",
style: {
// Border color
color: "#fff",
weight: 1,
fillOpacity: 0.8
},
// Binding a pop-up to each layer
onEachFeature: function(feature, layer) {
layer.bindPopup(
feature.properties.country +
", " +
feature.properties.points+
"<br>Median Price per bottle of wine:<br>" +
"$" +
feature.properties.price
);
}
}).addTo(myMap);
});
Based on the sample data from your previous question, you simply need to modify your GeoJSON data to specify "Polygon" type geometries (with array of coordinates), instead of your current "Point" type geometries (which are rendered by default by Leaflet as simple Markers).

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

Bergfex Leaflet Layer

I have used the first code to display a Bergfex layer in OpenLayers and this works up to high zoom layers whereas I cannot get a Leaflet layer to work above zoom 12.
Does anybody know if this is a limitation or are other elements needed? I have tried it both as a base layer or overlay. Both sets of code are below.
OpenLayers:
bergfex = new OpenLayers.Layer.XYZ("Bergfex Topo Austria",
"http://static7.bergfex.at/images/amap/${z}$folder/${z}_${x}_${y}.png", {
sphericalMercator: true,
buffer: 0,
opacity: 0.5,
isBaseLayer: false,
visibility: false,
attribution: "© 2008, 2013 BEV,<a href='http://www.bergfex.at'>bergfex GmbH</a>",
getURL: function(bounds) {
var path = OpenLayers.Layer.XYZ.prototype.getURL.apply(this, arguments);
var parts = path.split("$folder/");
var z = parseInt(parts[0].substr(-2));
path = path.replace("$folder", z >= 14 ?
"/" + parts[1].substr(3, 2 + z - 14) : "");
return path;
}
});
Leaflet:
bf = L.tileLayer('http://static7.bergfex.at/images/amap/{z}/{z}_{x}_{y}.png', {
maxZoom: 18,
attribution: bergfexAttribution,
detectRetina: true
})
The URL template used in your Leaflet code ('http://static7.bergfex.at/images/amap/{z}/{z}_{x}_{y}.png') has tiles available only above Austria, and from zoom levels 8 to 13 (included). There are no tiles (404 error) from zoom 0 to 7 (included), and zoom 14 and above.
To avoid unnecessary network request, you might be interested in using minZoom and bounds Tile Layer options:
bf = L.tileLayer('http://static7.bergfex.at/images/amap/{z}/{z}_{x}_{y}.png', {
maxZoom: 13,
minZoom: 8,
bounds: [
[45, 10], // I just used arbitrary bounds, you should adjust them.
[50, 15]
],
attribution: bergfexAttribution,
detectRetina: true
});
Now to go beyond zoom level 13, your OpenLayers code changes that URL template dynamically (see the getURL options), so it looks like 'http://static7.bergfex.at/images/amap/{z}/{x2}/{z}_{x}_{y}.png' with x2 being the first 2 digits of x at zoom 14 and the first 3 at zoom 15 (maybe and so on).
You will need to do a similar "URL template dynamic adjustment" for Leaflet. Unfortunately, Leaflet does not expose a similar getURL option as OpenLayers. Nevertheless, you could modify the getTileUrl method of your bf Tile Layer instance so that it does that adjustment (you would have to adapt your OpenLayers code):
var bf2 = L.tileLayer('http://static7.bergfex.at/images/amap/{z}/{x2}/{z}_{x}_{y}.png', {
maxZoom: 18, // Looks like tiles are available only up to 15 included, or the URL template changes again?
minZoom: 14,
bounds: [
[45, 10], // I just used arbitrary bounds, you should adjust them.
[50, 15]
],
attribution: bergfexAttribution,
detectRetina: true
});
bf2.getTileUrl = function (tilePoint) {
var x2 = Math.floor(tilePoint.x / 100);
return L.Util.template(this._url, L.extend({
s: this._getSubdomain(tilePoint),
z: tilePoint.z,
x: tilePoint.x,
y: tilePoint.y,
x2: x2
}, this.options));
};
bf2.addTo(map);
Demo: http://jsfiddle.net/ve2huzxw/217/

Leaflet Circle radius changing dependant on y/lng coords

I am using mapbox/leaflet to display a picture of a human body rather than a regular map.
I am using leaflet draw and I need to be able to create a circle and move it around while maintaining its radius. However, when I move it towards the bottom of the map/screen, the size increases exponentialy. I want it to stay the same size.
I assume it's something to do with projection or CRS but I'm not sure what to do to stop it.
My code is :
var mapMinZoom = 0;
var mapMaxZoom = 4;
var map = L.map('map', {
maxZoom: mapMaxZoom,
minZoom: mapMinZoom,
crs: L.CRS.Simple,
noWrap: true,
continuousWorld: true
}).setView([0, 0], mapMaxZoom);
var mapBounds = new L.LatLngBounds(
map.unproject([0, 3840], mapMaxZoom),
map.unproject([4096, 0], mapMaxZoom));
map.fitBounds(mapBounds);
L.tileLayer('/tiles/{z}/{x}/{y}.png', {
minZoom: mapMinZoom, maxZoom: mapMaxZoom,
bounds: mapBounds,
attribution: 'Rendered with MapTiler',
noWrap: true,
continuousWorld: true
}).addTo(map);
var touches;
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
}
}).addTo(map);
map.on('draw:created', function (e) {
featureGroup.addLayer(e.layer);
});
Any ideas? I don't need to use leaflet draw, just the L.circle would do, but it has the same issue.
Gif of issue here :
Turns out there are a load of todos in the leaflet 0.7 code... including this little gem :
// TODO Earth hardcoded, move into projection code!
_getLatRadius: function () {
return (this._mRadius / 40075017) * 360;
},
_getLngRadius: function () {
return this._getLatRadius() / Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
},
Updated to 0.8Dev and it has all been fixed!

Leaflet: Layer disappears on some zoom-levels

On this Leaflet application I show all available bus stops in Denmark.
http://drivetime.mapicture.dk/stops
The strange thing is, that if you zoom down to one of the last 4 zoom levels, the layer with these bus stops simply disappears. They reappear when you zoom out again.
The bus stops are made as a single layer retrieved through a WMS request to our Geoserver. The entire javascript code is actually pretty simple, so I have trouble finding out, what causes this behavior.
function CurrentStopsMap(mapNode, zoomSettings) {
var self = this;
self.mapNode = mapNode;
self.zoomSettings = zoomSettings;
self.mapquest = new L.TileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: "©<a href='http://openstreetmap.org/' target='_blank'>OpenStreetMap</a> contributors, Tiles Courtesy of <a href='http://open.mapquest.com' target='_blank'>MapQuest</a>",
subdomains: ['1', '2', '3', '4']
});
self.regions = new L.TileLayer.WMS("http://backend.mapicture.dk:8080/geoserver/mapicture/wms", {
layers: 'mapicture:region',
format: 'image/png',
styles: 'regions',
transparent: false
});
self.stops = new L.TileLayer.WMS("http://backend.mapicture.dk:8080/geoserver/mapicture/wms", {
layers: 'mapicture:stops',
format: 'image/png',
transparent: true
});
self.map = new L.Map(mapNode, {
center: new L.LatLng(zoomSettings.lat, zoomSettings.lon),
zoom: zoomSettings.level,
layers: [self.mapquest, self.stops],
zoomControl: true
});
L.control.layers({
"Kort": self.mapquest,
"Områder": self.regions
}, {
"Stoppesteder": self.stops
}).addTo(self.map);
}
var zoom = {
lat: 56.24,
lon: 10.68,
level: 7
};
var map = new CurrentStopsMap('map', zoom)
This was actually a problem with the Geoserver daemon.