Add Leaflet Search through a Feature Group - javascript

Similar to Search for markers in a markercluster group Leaflet-MarkerCluster
But i am using a Control group ontop of Marker Cluster so they will be displayed upon a radio button click.
var map = L.map("map"),
parentGroup = L.markerClusterGroup(options), // Could be any other Layer Group type.
// arrayOfMarkers refers to layers to be added under the parent group as sub group feature
mySubGroup = L.featureGroup.subGroup(parentGroup, arrayOfMarkers);
parentGroup.addTo( map );
mySubGroup.addTo( map );
I am attempting to implement Leaflet Search - but as per the documentation says it requires a group layer of markers as the second parameter for it work. Trouble is using L.featureGroup.subGroup requires an array of markers.
Attempted to iterate through mySubGroup at run time to get the layers of markers using Leaflet eachLayer but this will duplicate the amount of markers i have on the map for the search to work.
var markersLayer = new L.LayerGroup().addTo( this.map );
forEach( mySubGroup, layers => {
layers.eachLayer( function (layer ) {
console.log ( layer );
markersLayer.addLayer( layer );
})
});
map.addControl( new L.Control.Search({layer: markersLayer}) );

Solved this issue - though it's quite inefficient. If you can find a more elegant solution to avoid duplication then feel free to contribute it as an answer!
var title = layer.options.title;
// iterate through the cluster points
forEach( mySubGroup, layers => {
layers.eachLayer(function (layer) {
var title = layer.options.title; // match the search title to marker title
marker = new L.Circle(new L.LatLng(layer.getLatLng().lat,layer.getLatLng().lng),
{radius: 0, title: title, fill: 'red', fillOpacity: 0, opacity: 0 }); // Create an invisible L circle marker for each cluseter marker
markersLayer.addLayer(marker);
})
});
You then add the markersLayer to the Leaflet Search

Related

Delete the last 2 additions to a featuregroup in Leaflet

I have a featuregroup that adds circlemarkers and paths -- they are added in sequence and I need a way to create a function that will remove the last (2) in the _layers every time it's clicked.
I tried slicing but that doesn't work because it's not really an array.
Any thoughts on how to proceed? I've been searching stackoverflow for awhile and cannot find anything that matches what I'm trying to do.
Let's simply refer to Leaflet documentation:
You can use the methods the layerGroup/featureGroup provides to remove the last two layers from the group.
The getLayers method returns an array of all the layers added to the group.
The eachLayer method iterates over the layers of the group, optionally specifying context of the iterator function.
The removeLayer method removes the layer with the given internal ID from the group.
Leaflet's featureGroup is an extension of the layerGroup so all of these will work on featureGroup as well.
So, say that you have your layers set up like so:
// Layers:
var layers = L.layerGroup().addTo(map);
var marker = L.marker([51.5, -0.09]).addTo(layers);
var circle = L.circle([51.508, -0.11], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5,
radius: 500,
}).addTo(layers);
var polygon = L.polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047],
]).addTo(layers);
You can use those layerGroup methods to remove the last two elements of the array:
// Pass the layerGroup to the function
function removeLastTwo(layerGroup) {
// Use getLayers to get the array
var layerArr = layerGroup.getLayers();
var minusOne = layerArr.length - 1;
var minusTwo = layerArr.length - 2;
// Use eachLayer to iterate the layerGroup
layerGroup.eachLayer((layer) => {
// Grab the index of the layer
var layerIndex = layerArr.indexOf(layer);
// Remove the last two elements of the layerGroup array
if (layerIndex === minusOne || layerIndex === minusTwo) {
layerGroup.removeLayer(layer);
}
});
}
Here is a live example, with this function attached to the click event listener of a button.

Add concentric circles to a map at specific distances around a marker using Leaflet and GeoJSON

This question (leaflet concentric circles (angular 2)) is similar, but since its not quite the same and no-one has answered it I thought I would start over.
My Leaflet map is populated by markers created in PHP from a MySql database. Here is a link if you want to see it; https://net-control.us/map1Test.php.
I would like to be able to right click on any given marker and have a series of concentric circles drawn around that marker at various distances in miles (example: 2, 5, 10, 15).
I think I need to do this with GeoJSON but any method that does what I need is OK with me. I just have not figured out how to first of all draw the circles at the distances I need and secondly how to allow a marker to be right clicked to display them.
I can't find any examples, and the only similar question has not been answered. Can someone show me how to do this? I use OSM maps.
The code (PHP) to make each marker looks like this;
$markers .= " var $callsign = new L.Marker(new L.LatLng($row[koords]),{ icon: new L.{$row[iconColor]}({number: '$rowno' }), title: \"marker_$rowno\"}).addTo(fg).bindPopup(\"$rowno<br>$row[mrkrfill]\").openPopup(); $($callsign._icon).addClass(\"$row[classColor]\"); markers.push($callsign); \n";
You don't share the code which creates the markers, so this is necessarily theoretical...
When creating the marker, add a contextmenu hook, which calls a function that uses L.circle to add circles to the map.
EDIT: So I peeked at the source of your site, your Javascript snippet for each marker should end up like this:
var WA0TJT = new L.Marker(new L.LatLng(39.202911,-94.602887),{
icon: new L.NumberedDivIcon({number: '1' }),
title:
"marker_1"}).addTo(fg).bindPopup("1<br><b>#0013</b><br>WA0TJT<br>Keith Kaiser<br>Platte Co., MO Dist: A<br>39.202911, -94.602887<br>EM29QE").openPopup().on("contextmenu", drawCircles);
$(WA0TJT._icon).addClass("bluemrkr");
markers.push(WA0TJT);
with a new function like this
function drawCircles(event e) {
L.circle(e.target.getLatLng(), {radius: <radius in metres>}).addTo(map);
// ... any more circles you need ...
}
var i;
var r = 1609.34; // in meters = 1 mile, 4,828.03 meters in 3 miles
var circleOptions = {
color: 'blue',
fillColor: '#69e',
fillOpacity: 0
}
for (i=0 ; i < 3; i++ ) {
r = (r * i) + r;
alert(lat+", "+lng);
var circle = L.circle([lat, lng], r, circleOptions);
circle.addTo(map);
r = 1609.34; // reset r so r calculation above works for each 1 mile step
}

How to change cluster zoom level on click with Leaflet map?

I have a leaflet map that has zoom levels 2-7 and uses the MarkerCluster plugin, by default I have the L.MarkerClusterGroup disable clustering a zoom level 2 (which means no clustering) and I'm trying to allow the user to click a button that then changes the clustering zoom level to 5. Is this possible?
I know I could do it by making two markercluster groups, one that has no clustering and one that has clustering and remove/add it based on click but that just seems incredibly messy. Really, there's several ways to do it but they are so incredibly clunky.
Code:
Default (2 is the lowest level of zoom):
var markers = new L.MarkerClusterGroup (
{
disableClusteringAtZoom: 2,
maxClusterRadius: 100,
animateAddingMarkers: true
});
What I want to do be able to do:
$('#mcluster').click(function() {
//do some code that sets the disableClusterAtZoom to 5
});
I could not find a way to disable clustering or set a new value for disableClustering at zoom, but I found a less clunky way of achieving this.
var markers = new L.LayerGroup(); //non cluster layer is added to map
markers.addTo(map);
var clusters = new L.MarkerClusterGroup (
{
disableClusteringAtZoom: 5,
maxClusterRadius: 100,
animateAddingMarkers: true
}); //cluster layer is set and waiting to be used
var clusterStatus = 'no'; //since non cluster group is on by default, the status for cluster is set to no
$('#mcluster').click(function( event ) {
if(clusterStatus === 'no'){
clusterStatus = 'yes';
var current1 = markers.getLayers(); //get current layers in markers
map.removeLayer(markers); // remove markers from map
clusters.clearLayers(); // clear any layers in clusters just in case
current1.forEach(function(item) { //loop through the current layers and add them to clusters
clusters.addLayer(item);
});
map.addLayer(clusters);
} else {
clusterStatus = 'no'; //we're turning off clustering here
var current2 = clusters.getLayers(); //same code as before just reversed
map.removeLayer(clusters);
markers.clearLayers();
current2.forEach(function(item) {
markers.addLayer(item);
});
map.addLayer(markers);
}
});
I'm sure there is a more elegant solution but with my still growing knowledge this is what I came up with.
I know you needed a solution a few months ago, but just to let you know that I released recently a sub-plugin for Leaflet.markercluster that can perform exactly what you are looking for (with a few extra code): Leaflet.MarkerCluster.Freezable (demo here).
var mcg = L.markerClusterGroup().addTo(map),
disableClusteringAtZoom = 2;
function changeClustering() {
if (map.getZoom() >= disableClusteringAtZoom) {
mcg.disableClustering(); // New method from sub-plugin.
} else {
mcg.enableClustering(); // New method from sub-plugin.
}
}
map.on("zoomend", changeClustering);
$('#mcluster').click(function () {
disableClusteringAtZoom = (disableClusteringAtZoom === 2) ? 5 : 2;
changeClustering();
});
mcg.addLayers(arrayOfMarkers);
// Initially disabled, as if disableClusteringAtZoom option were at 2.
changeClustering();
Demo: http://jsfiddle.net/fqnbwg3q/3/
Note: in the above demo I used a refinement to make sure the markers merge with animation when clustering is re-enabled. Simply use a timeout before using enableClustering():
// Use a timeout to trigger clustering after the zoom has ended,
// and make sure markers animate.
setTimeout(function () {
mcg.enableClustering();
}, 0);

Markerclusterer customization google maps

I have a basic markerclusterer example which works very well.
var center = new google.maps.LatLng(37.4419, -122.1419);
var options = {
'zoom': 13,
'center': center,
'mapTypeId': google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), options);
var markers = [];
for (var i = 0; i < 100; i++) {
var latLng = new google.maps.LatLng(data.photos[i].latitude,
data.photos[i].longitude);
var marker = new google.maps.Marker({'position': latLng});
markers.push(marker);
}
var markerCluster = new MarkerClusterer(map, markers);
What I would like to do is cluster the markers by country and then once you click on it they are still clustered until on3 further click. Currently they are clustered until you are down to one result. I have thousands of markers and would like them visible after one country click and then one more click.
I looked for a solution online and found this http://google-maps-utility-library-v3.googlecode.com/svn/tags/markermanager/1.0/examples/google_northamerica_offices.html
which is produced using this
var officeLayer = [
{
"zoom": [0, 3],
"places": [
{ "name": "US Offices", "icon": ["us", "flag-shadow"], "posn": [40, -97] },
{ "name": "Canadian Offices", "icon": ["ca", "flag-shadow"], "posn": [58, -101] }
]
},
...
};
function setupOfficeMarkers() {
allmarkers.length = 0;
for (var i in officeLayer) {
if (officeLayer.hasOwnProperty(i)) {
var layer = officeLayer[i];
var markers = [];
for (var j in layer["places"]) {
if (layer["places"].hasOwnProperty(j)) {
var place = layer["places"][j];
var icon = getIcon(place["icon"]);
var title = place["name"];
var posn = new google.maps.LatLng(place["posn"][0], place["posn"][1]);
var marker = createMarker(posn, title, getIcon(place["icon"]));
markers.push(marker);
allmarkers.push(marker);
}
}
mgr.addMarkers(markers, layer["zoom"][0], layer["zoom"][1]);
}
}
mgr.refresh();
updateStatus(mgr.getMarkerCount(map.getZoom()));
}
I'm not sure how to implement this into what I've currently got and if i need to include any other scripts/ libraries also.
You are looking at two totally different libraries, there. Your question is about the MarkerClusterer library, but your example solution is about the MarkerManager library.
The MarkerClusterer library automatically clumps markers together based on an algorithm that tries to decide when too markers would be so close together that you can't visibly distinguish one from another. You don't really have a lot of control over when and how it decides to merge markers together this way, so this library is idea when it doesn't matter to you how they get merged, as long as merging happens. Since you want to merge markers together by political boundaries (countries) and not by proximity to each other, this is not the library for you.
The MarkerManager library does not automatically merge markers together at all. What it does do is to selectively hide and reveal markers based on the zoom level of the current map viewport. What you would need to do is do your own merging, and then add to the MarkerManager all of the merged markers, as well as the detail markers, and the zoom levels where you want each marker to be visible. Doing your own merging means you will need an alternate way of determining which country each marker point falls within. Hopefully, you already know (or can get) that information, because it's not automatically provided by any of these libraries.
tl;dr - use the MarkerManager library and not the MarkerClusterer library for grouping by countries, and it's up to you to identify the location for each country and which marker goes with which one.

check if map markers are within selected bounds

I have a map with various markers and i need to be able to draw a rectangle on the map and select the markers which are within the rectangle bounds.
So far i have found some great info here: How to get markers inside an area selected by mouse drag?
I have implemented the keymapzoom plugin ok. like so
$('#dispatcher').gmap3({action:'get'}).enableKeyDragZoom({
boxStyle: {
border: "dashed black",
//backgroundColor: "red",
opacity: 0.5
},
paneStyle: {
backgroundColor: "gray",
opacity: 0.2
}
});
var dz = $('#dispatcher').gmap3({action:'get'}).getDragZoomObject();
google.maps.event.addListener(dz, 'dragend', function (bnds) {
alert(bnds);
});
This gives me the following
((lat,long),(lat,long)) format from the alert(bnds);
I need to know how i can now check if any markers are within this?
I already have an object that is storing the markers for another reason. like:
markers[name] = {};
markers[name].lat = lati;
markers[name].lng = longi;
which might be useful?
I don't understand how to use the GLatLngBounds and containsLatLng(latlng:GLatLng) as suggested.
Your question is tagged with the v3 version of the Maps API, so I'll assume you are using that version (which you should as v2 is deprecated). Note that some classes and methods are named different than in your question.
Bounds are represented with the LatLngBounds class. You can perform the contains method on an instance of that class to determine if a point lies within those bounds.
If you have an object with all your markers, you can loop through them and check each marker, for example:
var bounds = new google.maps.LatLngBounds(sw, ne);
for (var a in markers) {
if (bounds.contains(new google.maps.LatLng(markers[a].lat, markers[a].lng)) {
// marker is within bounds
}
}
On a side note, I would store the LatLng object in the markers object when creating them. That way you don't have to create them wherever you need.
Box/Rectangle Draw Selection in Google Maps
This was my solution..
google.maps.event.addListener(dz, 'dragend', function(e) { //important listener
for(var i = 0; i < markers.length; i++){ // looping through my Markers Collection
if(e.contains(markers[i].position))
console.log("Marker"+ i +" - matched");
}
});

Categories

Resources