layerGroup in layerControl is radio. want checkbox - leaflet

if I add "baseMaps" to my layer control, "huts" is actually a checkbox. But I only have one basemap, so I dont need that in my layer controls. But I still wanted to be able tu turn off my "huts" overlay. But as soon as I remove "baseMaps" "huts" becomes a radio button and cannot be turned off. Is there a way?
var huts = L.layerGroup();
var hut00 = L.marker([0, 0]).bindPopup('This is <b>HTML</b>content').addTo(huts);
bounds = new L.LatLngBounds(new L.LatLng(90, -180), new L.LatLng(-90, 180));
var baseMap = L.tileLayer("mapTiles/{z}_{x}_{y}.jpg", {tileSize: 512, minZoom: 1, maxZoom: 4, noWrap: true});
var pcMap = L.map('pcMapDiv', {
layers: [baseMap, huts],
center: bounds.getCenter(),
zoom: 0,
maxBounds: bounds,
maxBoundsViscosity: 1.0
});
pcMap.setView([0, 0], 0);
var baseMaps = {
"Plan": baseMap,
};
var overlays = {'Hütten': huts};
var layerControl = L.control.layers(baseMaps, overlays).addTo(pcMap);
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(pcMap);
}
pcMap.on('click', onMapClick);

The 1st argument of L.control.layers populates the "base maps" (with radio button), while its 2nd argument populates "overlays" (with checkboxes).
If you only want overlays, you can simply pass null to the 1st argument:
L.control.layers(null, overlays)
or initialize an empty Control and explicitly add the overlay:
var layerControl = L.control.layers().addTo(map);
layerControl.addOverlay(huts, "Hütten");

Related

Leaflet current position multiple markers

Hello everyone I have some problems its about the current positionmarker in my leaflet its supposed to update every 3 second and it does but it everytime it puts a new "position" marker on the map and the old one stays how can i fix this?
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
attribution: '© Leaflet 2021',
tileSize: 512,
zoomOffset: -1,
id: 'mapbox/streets-v11',
accessToken: '######'
}).addTo(map);
var greenIcon = L.icon({
iconUrl: 'person.png',
iconSize: [35, 35], // size of the icon // the same for the shadow
popupAnchor: [0, -20] // point from which the popup should open relative to the iconAnchor
});
// placeholders for the L.marker and L.circle representing user's current position and accuracy
var current_position, current_accuracy;
function onLocationFound(e) {
var radius = e.accuracy / 2;
var marker;
L.marker(e.latlng, {icon: greenIcon}).addTo(map)
}
// wrap map.locate in a function
function locate() {
map.locate({setView: true, maxZoom: 15});
}
map.on('locationfound', onLocationFound);
// call locate every 3 seconds... forever
setInterval(locate, 3000);
An efficient way to fix this is to keep a reference to the marker you create, so that you can update its position rather than creating a new marker each time you get a location update. The reference needs to be held in a variable that is outside your callback function, but in scope when the callback is created.
For instance, your callback can check whether the marker already exists, and either create it and attach it to the map object for easy re-use, or just update its coordinates if it is already there:
function onLocationFound(e) {
var radius = e.accuracy / 2;
if (map._here_marker) {
// Update the marker if it already exists.
map._here_marker.setLatLng(e.latlng);
} else {
// Create a new marker and add it to the map
map._here_marker = L.marker(e.latlng, {icon: greenIcon}).addTo(map);
}
}
Having this reference will also let you edit the marker from other functions, e.g. to change the icon or popup, hide it from view, etc.
You can do it, for example, in the following way.
Add an ID (customId) to the marker:
const marker = L.marker([lng, lat], {
id: customId
});
And when you add a new marker remove the existing one with the code below:
map.eachLayer(function(layer) {
if (layer.options && layer.options.pane === "markerPane") {
if (layer.options.id !== customId) {
map.removeLayer(layer);
}
}
});

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

With Leaflet, how do I order controls if I have two in the same position?

I have a custom control on my map that has the following property:
position: "topright"
This puts it in the same position as the default Layers control. However, my custom control is placed above the layers control. How do I specify which should be on top? The documentation on control positions doesn't mention what to do if more than one occupies the same spot.
My map is initialized like this:
var map = L.map('map', {
center: [0.0, 0.0],
zoom: 2
});
map.addControl(new L.Control.Cluster())
The button itself is set up like this, in `onA
This sets up my top right like this:
L.Control.Cluster = L.Control.extend({
options: {
position: "topright",
},
onAdd: function (map) {
var clusterName = "leaflet-control-cluster"
, container = L.DomUtil.create("div", clusterName + " leaflet-bar")
, options = this.options
this._map = map
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
container.style.backgroundColor = 'white';
container.style.backgroundImage = "url(image.png)";
container.style.backgroundRepeat = "no-repeat";
container.style.backgroundPosition = "center";
container.style.backgroundSize = "25px 25px";
container.style.width = '36px';
container.style.height = '36px';
return container
}
})
This sets up my top right like this:
How do I indicate that I want my custom control below the layer control?
It just has to do with the order of creating the controls.
This will add your custom control after the layer control:
L.control.layers(baseLayers, overlays).addTo(map);
map.addControl(new L.Control.Cluster());
http://plnkr.co/edit/3VipBSvsQWsiQa5hfBCD?p=preview

Changing Leaflet ImageLayer With File Input

I'm working on a dynamic image mapper that will users can load their floor plan of apartment then put markers on parts of floor. So I wan't to change url of image layer of Leaflet map dynamically.
I'm loading map with ChangeMap function for the first time. It loads my image correctly.
function ChangeMap(_url)
{
var map = L.map('map', {
minZoom: 1,
maxZoom: 5,
center: [0, 0],
zoom: 3,
crs: L.CRS.Simple
}).setView([50.4333, 30.5167], 3);
// dimensions of the image
var w = 1526,
h = 626,
url = _url;
// calculate the edges of the image, in coordinate space
var southWest = map.unproject([0, h], map.getMaxZoom() - 1);
var northEast = map.unproject([w, 0], map.getMaxZoom() - 1);
var bounds = new L.LatLngBounds(southWest, northEast);
// add the image overlay,
// so that it covers the entire map
var overlay = L.imageOverlay(url, bounds);
overlay.addTo(map);
}
But if I try another time without refresh the page I'm getting an error "map container is alreay initialized". After that error I thought I can add div with id='map' dynamically like this.
var mapContainer = $("#mapContainer");
mapContainer.append("<div id='map' width='100%' height='400px'></div>");
I added that append function at the beginning of my ChangeMap() function. But this time there was no map on page. How can I do this ?
Only initialize the map once...So take var map = L.map('map', {... out of ChangeMap and only run it once before.I'd also recommend only initializing the L.imageOverlay once...and using setUrl to dynamically swap when needed inside ChangeMap.

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!