Leaflet Layer control same map as base and overlay - leaflet

I have 3 maps Osm, Lidar and Aerial . I want to select one as the baselayer and one as an overlay. E.g. osm + aerial, osm + Lidar, Aerial + lidar. The LayerControl nearly does what I want but fires baselayerchanged when an overlay is enabled that is also a baselayer optionn. Any ideas on how to sort this would be welcome.

Use two instances of L.Control.Layers, each having a different set of 3 "baselayers".
The nomenclature of "baselayers" and "overlays" can be confusing. As far as a L.Control.Layers is concerned, baselayers are mutually exclusive and overlays are not. You can have something that looks like a graphical overlay but is defined as a baselayer in a control.

Here is the code I ended up with. The important thing is that when changing base layer with an overlay selected, the overlays have to be unloaded to load the new base at the bottom of the z order.
$(document).ready(function () {
var map = L.map('map').setView([-14.0349289249, -171.438639761], 14);
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors'
});
var aerial =L.tileLayer('aerial/{z}/{x}/{y}.png', {
maxZoom: 18,
tms: false,
attribution: 'Aerial from Centre for Samoan Studies (CSS)'
});
var lidar =L.tileLayer('lidar/{z}/{x}/{y}.png', {
maxZoom: 18,
tms: false,
attribution: 'LIDAR from Centre for Samoan Studies (CSS)'
});
// our set of layers
var layers = [osm, aerial, lidar];
// initial base layer
osm.addTo(map)
// to enable base/overlay swapping, add extra properties with unlikely to clash names
osm.isSamoaBase = true;
osm.isSamoaOverlay = false;
osm.samoaName = 'Base';
lidar.isSamoaBase = false;
lidar.isSamoaOverlay = false;
lidar.samoaName = 'LIDAR';
aerial.isSamoaBase = false;
aerial.samoaName = 'Aerial';
aerial.isSamoaOverlay = false;
// set true when the existing base layer is to be replaced
var baseChange = false;
// layer selector for both base and overlays,
// sorted to preserve order as layers move between base and overlay sections
var layerControl = L.control.layers(null, null, {sortLayers:true});
layerControl.addTo(map);
loadLayerControl(layerControl);
map.on('layerremove', function(le){
if (le.layer.isSamoaBase) {
le.layer.isSamoaBase = false;
// a new base will be selected next
baseChange = true;
}
if (le.layer.isSamoaOverlay) {
le.layer.isSamoaOverlay = false;
}
loadLayerControl(layerControl);
});
map.on('layeradd', function(le){
if (baseChange) {
baseChange = false;
// tag as new base
le.layer.isSamoaBase = true;
le.layer.isSamoaOverlay = false;
// restore full base layer opacity
le.layer.setOpacity(1);
}
if (!le.layer.isSamoaBase) {
le.layer.isSamoaOverlay = true;
}
loadLayerControl(layerControl);
//set opacity from slider for any selected overlays
var val = $('#basemapslider').slider("option", "value");
map.eachLayer(function(layer){
if (layer.isSamoaOverlay) {
layer.setOpacity(val);
}
});
});
function loadLayerControl(lc) {
//remove twice in case both layer both a base and overlay option
for (var i = 0; i < layers.length; i++) {
lc.removeLayer(layers[i]);
lc.removeLayer(layers[i]);
}
// must add base first to get base at bottom of z order
for (var i = 0; i < layers.length; i++) {
if (!layers[i].isSamoaOverlay) {
lc.addBaseLayer(layers[i], layers[i].samoaName);
}
}
// finally add overlays
for (var i = 0; i < layers.length; i++) {
if (!layers[i].isSamoaBase) {
lc.addOverlay(layers[i], layers[i].samoaName);
}
}
}
})

Related

Automatically Zoom the map to fit all markers

Following an example, you can see the plunker her http://plnkr.co/edit/lJHyP3dhT3v8aHVdt3D3?p=preview
Regardless of whatever zoom value is provided while initializitng the map, I want to zoom the map automatically so all the markers are inside the view. Here is my code
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '© OpenStreetMap contributors, Points &copy 2012 LINZ'
}),
latlng = L.latLng(-37.82, 175.24);
var map = L.map('map', {center: latlng, zoom: 10, layers: [tiles]});
var markers = L.markerClusterGroup();
for (var i = 0; i < addressPoints.length; i++) {
var a = addressPoints[i];
var title = a[2];
var marker = L.marker(new L.LatLng(a[0], a[1]), { title: title });
marker.bindPopup(title);
markers.addLayer(marker);
}
map.addLayer(markers);
var group = new L.featureGroup(markers);
map.fitBounds(group.getBounds());
You need to
create an array
push all your markers in array
once all the markers are added to array, create a featureGroup
add your markers array to featureGroup and then zoom to its bounds.
Below is the modified code
var markerArray = []; //create new markers array
for (var i = 0; i < addressPoints.length; i++) {
var a = addressPoints[i];
var title = a[2];
var marker = L.marker(new L.LatLng(a[0], a[1]), { title: title });
marker.bindPopup(title);
markers.addLayer(marker);
markerArray.push(marker); //add each markers to array
if(i==addressPoints.length-1){//this is the case when all the markers would be added to array
var group = L.featureGroup(markerArray); //add markers array to featureGroup
map.fitBounds(group.getBounds());
}
}
Here is the working plunk

Mapbox and Leaflet Draw edit only 1 group

I want to add multiple layergroup or featuregroup objects or something else if there's a better way on a map that can contain multiple polygons in each. I want these groups to be distinguishable so they can be used in conjunction with datatables and the leaflet draw tool so that when I select a row within datatables, only the polygons within the related group can be edited or deleted using the draw tool and any new polygons are only added to that group.
I've done this before with only a single polygon and one featuregroup, but how do I handle multiple groups?
featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
},
draw: {
marker: false,
polygon: true,
polyline: false,
rectangle: false,
circle: false
},
locate: {
marker: true
}
}).addTo(map);
$('.leaflet-control-zoom').css('margin-top', '45px');
L.drawLocal.draw.toolbar.buttons.polygon = 'Draw damage area';
L.drawLocal.edit.toolbar.buttons.edit = 'Edit damage area';
L.drawLocal.edit.toolbar.buttons.remove = 'Delete damage area';
if (jsModel.polygonpoints)
{
var polygonPoints = jsModel.polygonpoints.split(';');
if (polygonPoints.length > 0) {
var polyPoints = [];
for (var i = 0; i < polygonPoints.length; i++) {
var point = polygonPoints[i].split(',');
polyPoints.push([parseFloat(point[0]), parseFloat(point[1])]);
}
var points = polyPoints;
var polyOptions = {
color: '#f06eaa'
};
var polygon;
if (typeof featureGroup !== "undefined") {
polygon = L.polygon(points, polyOptions).addTo(self.featureGroup);
self.polygon = polygon;
}
}
}
I'm trying to add the polygons during a render of my datatable columns. Here is what I have so far. I saw a post somewhere that talked about adding multiple draw tools and then only make the current one available. Not sure if this is a good way to do it, but if it is, what can I use in my button's "data-draw-control" to be able to point to the one I want to be current?
"columns": [
{
"render": function (data, type, row) {
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
},
draw: {
marker: false,
polygon: true,
polyline: false,
rectangle: false,
circle: false
}
}).addTo(map);
for (var al = 0; al < row.areaList.length; al++)
{
var polyPoints = [];
for (var ap = 0; ap < row.areaList[al].areaPointList.length; ap++)
{
if (row.areaList[al].areaPointList[ap].x == 0 && row.areaList[al].areaPointList[ap].y == 0)
{
if (polyPoints.length != 0)
{
//add polygon to map
L.polygon(polyPoints).addTo(featureGroup);
polyPoints.length = 0;
}
}
else
{
polyPoints.push([parseFloat(row.areaList[al].areaPointList[ap].x), parseFloat(row.areaList[al].areaPointList[ap].y)]);
}
}
}
return "<button type='button' class='btn-zoom' data-draw-control='" + drawControl + "'><i class='fa fa-search'></i></button";
}
}
First create two featuregroups and a layercontrol so you can toggle between them:
var layerControl = new L.Control.Layers({
'FeatureGroup 1': new L.FeatureGroup(),
'FeatureGroup 2': new L.FeatureGroup()
}).addTo(map);
Now then you toggle between those, the map fires a basemapchanged event which contains the currently selected featuregroup, store that in a variable:
var currentFeatureGroup;
map.on('baselayerchange', function (e) {
currentFeatureGroup = e.layer;
});
When you draw something, the map fires another event where you can process the drawn feature. Create a function that returns the currently selected featuregroup and store it into that:
function getCurrentFeatureGroup () {
return currentFeatureGroup;
}
map.on('draw:created', function (e) {
var featureGroup = getCurrentFeatureGroup();
if (featureGroup instanceof L.FeatureGroup) {
featureGroup.addLayer(e.layer);
} else {
alert('Please select a featuregroup');
}
});
Here's an example of the concept on Plunker: http://plnkr.co/edit/9ZEjuP?p=preview

Removing geojson layers Mapbox.js/Leaflet.js

I'm having trouble removing multiple geojson layers at a time. When I keep them as feature layers, there is memory of each and every layer added, one right after the other. But when they become marker layers only the last layer clicked is removed.
I've tried adding them to a group and calling clearLayers on the group, but that still only removes the last layer added, not all. Tried passing an id also, but that didn't seem to work either.
$(function() {
var geojson;
var map = L.mapbox.map('map')
.setView([29.9629, -90.0603], 6);
var filters = document.getElementById('filters');
var layerTwo = L.mapbox.featureLayer()
.loadURL('panel/data/tamorethan5.geojson')
.on('ready', layer)
var layerOne = L.mapbox.featureLayer()
.loadURL('panel/data/talessthan5.geojson')
.on('ready', layer)
var layerThree = L.mapbox.featureLayer()
.loadURL('panel/data/palessthan5.geojson')
.on('ready', layer)
var layerFour = L.mapbox.featureLayer()
.loadURL('panel/data/pamorethan5.geojson')
.on('ready', layer)
function layer() {
var assetLayerGroup = new L.LayerGroup();
var layer = this
var name = layer.getGeoJSON().name;
var item = filters.appendChild(document.createElement('div'));
var checkbox = item.appendChild(document.createElement('input'));
var label = item.appendChild(document.createElement('label'));
checkbox.type = 'checkbox';
checkbox.name ='radio';
checkbox.id = 'myCheckbox';
checkbox.value = name;
label.innerHTML = name;
label.class = label;
label.setAttribute('for', name);
checkbox.addEventListener('change', update);
function update(val) {
if (checkbox.checked) {
console.log(layer)
drawLocations(layer._geojson)
} else {
newMapLayer.clearLayers();
}
}
drawLocations = function(layer) {
L.stamp(layer)
newMapLayer = L.geoJson(layer, {
style: style,
onEachFeature: onEachFeature,
pointToLayer: function(feature, latlng) {
var rad = 3;
var col = getColor(feature.properties, 'status');
return L.circleMarker(latlng, {
radius: rad,
fillColor: "#ff7800",
stroke: true,
weight: 2,
opacity: 0.8,
fillOpacity: 0.8,
className: "circle"
});
}
}).addTo(map);
};
} //end layer
Hm, okay - a few tweaks are necessary or recommended here:
drawLocations(layer._geojson)
Anything starting with a underscore in leaflet or Mapbox should be considered off-limits. Use the .getGeoJSON method instead.
drawLocations(layer.getGeoJSON());
It doesn't look like you're actually adding layers to the assetLayerGroup. If you wanted to do that, you would call
var assetLayerGroup = L.layerGroup().addTo(map);
Right after the
var map = L.mapbox.map('map')
.setView([29.9629, -90.0603], 6);
lines, and instead of calling .addTo(map) on your L.geoJson layers, you would call .addTo(assetLayerGroup).
Then calling assetLayerGroup.clearLayers() would remove all of the added layers.

find associated polylines for selected marker/circlemarker/point in leaflet

I have a multiple circlemarkers on map.
I want to find how many polylines passes through that marker & want to remove that polyline & If polyline does not exists then I want to add polyline.
I am using leaflet.
<script type="text/javascript">
function init(){
var map = L.map('map').setView([51.49521, -0.10062], 13);
L.tileLayer('http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © CloudMade'
}).addTo(map);
// get all 6 points
var points = [
[51.49346, -0.11518],
[51.49827, -0.06763],
[51.48331, -0.08154],
[51.52284, -0.09974],
[51.51932, -0.06695],
[51.50949, -0.1363]
];
// polyline
var selection = [];
var polyline = new L.Polyline([], {
color: 'blue',
weight: 5,
smoothFactor: 1
}).addTo(map);
var changeMarkerState = function (marker, select) {
if (marker instanceof L.CircleMarker) {
if (select) {
marker.setRadius(25);
} else {
marker.setRadius(10);
}
}
if (marker instanceof L.Marker) {
if (select) {
marker.options.title = 'selected';
} else {
marker.options.title = 'unselected';
}
marker.setIcon(new L.Icon.Default());
}
};
var onClick = function () {
var index = selection.indexOf(this);
if (index !== -1) {
changeMarkerState(this, false);
selection.splice(index, 1);
polyline.spliceLatLngs(index, 1);
} else {
changeMarkerState(this, true);
selection.push(this);
polyline.addLatLng(this.getLatLng())
}
};
// centerpoint
var centerPoint = new L.LatLng(51.49521, -0.10062);
var marker1 = L.marker([51.49521, -0.10062],
{title: 'unselected'}).on('click', onClick).addTo(map);
// adding allo points to map
for (var i = 0, l = points.length; i < l; i++)
{
// here I can use marker also(if solution is possible with markers)
L.circleMarker(points[i]).on('click', onClick).addTo(map);
var myPoint = new L.LatLng(points[i][0],points[i][1]);
var myPointList = [myPoint, centerPoint];
var firstpolyline = new L.Polyline(myPointList, {
color: 'red',
weight: 5,
smoothFactor: 1
}).addTo(map);
}
}
</script>
In above code what I am doing is that I am drawing multiple red polylines from different circlemarkers to one center point.
On selection of two circle markers I am drawing blue polyline between them.
At same time I want to remove the red polyline which there between circlemarkers & centerpoint.
Also If circlemarker is unselected then that red polyline between that circlemarker & centerpoint should be added.
To compare two latlngs use L.LatLng.equals: https://github.com/Leaflet/Leaflet/blob/master/src/geo/LatLng.js#L24.
To get latlngs from polyline use L.Polyline.getLatLngs: https://github.com/Leaflet/Leaflet/blob/master/src/layer/vector/Polyline.js#L34.
Now you can get your point in polyline and remove it using L.Polyline.spliceLatLngs: https://github.com/Leaflet/Leaflet/blob/master/src/layer/vector/Polyline.js#L48 or add using L.Polyline.addLatLng: https://github.com/Leaflet/Leaflet/blob/master/src/layer/vector/Polyline.js#L43 or set using L.Polyline.setLatLngs: https://github.com/Leaflet/Leaflet/blob/master/src/layer/vector/Polyline.js#L38.

select two markers & draw line between them in leaflet

I am very much new to leaflet.
I have multiple markers/circle Markers plotted on my map in leaflet.
now I have to draw line between two markers//circle Markers when I select them.
Can any one help in doing this.
var map = L.map('map').setView([51.49521, -0.10062], 13);
L.tileLayer('http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © CloudMade'
}).addTo(map);
// get all 6 points
var points = new Array(
[51.49346, -0.11518],
[51.49827, -0.06763],
[51.48331, -0.08154],
[51.52284, -0.09974],
[51.51932, -0.06695],
[51.50949, -0.1363]
);
// centerpoint
var centerPoint = new L.LatLng(51.49521, -0.10062);
var marker1 = L.marker([51.49521, -0.10062]).addTo(map);
// adding allo points to map
for (var i =0 ; i < points.length; i++)
{
// here I can use marker also(if solution is possible with markers)
L.circleMarker([points[i][0],points[i][1]]).addTo(map);
var point = new L.LatLng(points[i][0],points[i][1]);
var pointList = [point, centerPoint];
var firstpolyline = new L.Polyline(pointList, {
color: 'red',
weight: 5,
smoothFactor: 1
}).addTo(map);
}
You must store selection (however it can be polyline points or flag in your markers). When you select two or more markers you must add points to you polyline - it draws line on map, otherwise you must remove point from polyline. See details about polyline: http://leafletjs.com/reference.html#polyline.
See next code for example:
// Init map
var map = L.map('map').setView([53.902257, 27.561640], 13);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
// Init selection and lines logic
var selection = [];
var polyline = L.polyline([], {color: 'red'}).addTo(map);
var onClick = function () {
var index = selection.indexOf(this);
if (index !== -1) {
this.setRadius(10);
selection.splice(index, 1);
polyline.spliceLatLngs(index, 1);
} else {
this.setRadius(25);
selection.push(this);
polyline.addLatLng(this.getLatLng())
}
};
// Init circle markers
L.circleMarker([53.90, 27.56]).on('click', onClick).addTo(map);
L.circleMarker([53.92, 27.60]).on('click', onClick).addTo(map);
L.circleMarker([53.88, 27.60]).on('click', onClick).addTo(map);
// Init selection droping on ESC pressed
L.DomEvent.on(document, 'keydown', function (e) {
if (e.keyCode === 27) {
var oldSelection = selection.slice(0);
for (var i = 0, l = oldSelection.length; i < l; i++) {
oldSelection[i].fire('click');
}
}
});
UPD:
It's analogically, see updated code:
var map = L.map('map').setView([51.49521, -0.10062], 13);
L.tileLayer('http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © CloudMade'
}).addTo(map);
// get all 6 points
var points = [
[51.49346, -0.11518],
[51.49827, -0.06763],
[51.48331, -0.08154],
[51.52284, -0.09974],
[51.51932, -0.06695],
[51.50949, -0.1363]
];
// polyline
var selection = [];
var polyline = new L.Polyline([], {
color: 'red',
weight: 5,
smoothFactor: 1
}).addTo(map);
var changeMarkerState = function (marker, select) {
if (marker instanceof L.CircleMarker) {
if (select) {
marker.setRadius(25);
} else {
marker.setRadius(10);
}
}
if (marker instanceof L.Marker) {
if (select) {
marker.options.title = 'selected';
} else {
marker.options.title = 'unselected';
}
marker.setIcon(new L.Icon.Default());
}
};
var onClick = function () {
var index = selection.indexOf(this);
if (index !== -1) {
changeMarkerState(this, false);
selection.splice(index, 1);
polyline.spliceLatLngs(index, 1);
} else {
changeMarkerState(this, true);
selection.push(this);
polyline.addLatLng(this.getLatLng())
}
};
// centerpoint
var centerPoint = new L.LatLng(51.49521, -0.10062);
var marker1 = L.marker([51.49521, -0.10062],
{title: 'unselected'}).on('click', onClick).addTo(map);
// adding allo points to map
for (var i = 0, l = points.length; i < l; i++)
{
// here I can use marker also(if solution is possible with markers)
L.circleMarker(points[i]).on('click', onClick).addTo(map);
}