Leaflet markers aggregating when dezooming the map - javascript

I am new to Leaflet and I am trying to show a map with markers.
The problem I have is that the markers disappear when I zoom out, and are replaced with a number:
I used CircleMarkers to be able to set the color of each marker, and so that the markers keep their size no matter the zoom level. I add the markers using markercluster because I want to be able to delete them all easily.
<script src="https://unpkg.com/leaflet.markercluster#1.3.0/dist/leaflet.markercluster.js"></script>
function addMarkers(data){
// setup a marker group
markerList = L.markerClusterGroup();
for (var i = 0; i < data.length; i++){
var circleColor = getcolor(data[i].roundScore);
var circle = L.circleMarker([data[i].solutionLat, data[i].solutionLng], {
color: circleColor,
fillColor: circleColor,
fillOpacity: 0.5,
radius: 5
});
circle.bindPopup("Score : " + data[i].roundScore + "\n Address :" + data[i].address);
markerList.addLayer(circle);
}
window.mapMark.addLayer(markerList);
}
function deleteMarkers(){
if(markerList) {
markerList.clearLayers();
}
}
I have no idea how to change this behavior, and whether it is linked to the markers themselves or to the tile I use, which is openstreetmap:
const attribution =
'© OpenStreetMap contributors';
const tileUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
const tiles = L.tileLayer(tileUrl, { attribution });
tiles.addTo(mapMark);
}
Any help or hint would be greatly appreciated.
Thank you very much :)

The disappearance of your Circle Markers and replacement by numbers is the expected effect of Leaflet.markercluster. should you have included the plugin CSS files as well, these numbers would appear in green/yellow/orange bubbles.
To avoid this clustering, but still be able to remove all your Markers at once, simply use a basic Layer Group instead of a MarkerClusterGroup:
https://leafletjs.com/reference-1.7.1.html#layergroup
Used to group several layers and handle them as one.
markerList = L.layerGroup();
// you can also clear layers of a basic Layer Group
markerList.clearLayers();

Related

trying to create event listener using leaflet and javascript, how to change tectonic fault line color when user changes map base layer? not working?

// Create the map object with center, zoom level and default layer.
let map = L.map('mapid', {
center: [40.7, -94.5],
zoom: 3,
layers: [streets]
});
// Create a base layer that holds all three maps.
let baseMaps = {
"Streets": streets,
"Satellite": satelliteStreets,
"Night Navigation": nightNav
};
// 1. Add a 2nd layer group for the tectonic plate data.
let allEarthquakes = new L.LayerGroup();
let allTectonics = new L.LayerGroup();
let majorEQ = new L.LayerGroup();
// 2. Add a reference to the tectonic plates group to the overlays object.
let overlays = {
"Earthquakes": allEarthquakes,
"Tectonic Plates": allTectonics,
"Major Earthquakes": majorEQ
};
// Then we add a control to the map that will allow the user to change which
// layers are visible.
L.control.layers(baseMaps, overlays).addTo(map);
let tectonicData = "https://raw.githubusercontent.com/fraxen/tectonicplates/master/GeoJSON/PB2002_boundaries.json";
let tstyle = map.on('baselayerchange', function(feature) {
console.log('base layer has been changed');
return {
fillColor: tecStyle(L.overlays),
fillOpacity: 0.8,
weight: 0.5
};
});
function tecStyle(feature) {
if (feature === baseMaps["Streets"]) { return "purple";
}
if (feature === baseMaps["Satellite"]) {
return "red";
}
if (feature === baseMaps["Night Navigation"]) {
return "red";
}
};
// 3. Use d3.json to make a call to get our Tectonic Plate geoJSON data.
d3.json(tectonicData).then(function(data) {
// Create GeoJSON layer with the retrieved data.
L.geoJSON(data, {
style: tstyle,
onEachFeature: function(feature, layer){
layer.bindPopup("<h3><b> Plate Boundary Name: " + feature.properties.Name + "</h3></b>");
}
}).addTo(allTectonics);
allTectonics.addTo(map);
});
});
I want the tectonic fault line color to change colors depending on whether the user is on the 'Streets', 'Satellite', or 'Night Navigation' layers. For example, I want the fault line color to be 'purple' when on 'Streets', 'red' when on 'Satellite', and 'blue' when on 'Night Navigation' base layer maps. I was hoping I could get it to work using map.on and the event listener is working, because it comes up in my DevTools console, but the color of the fault line remains the default color of 'blue' no matter what layer I am on. What am I doing wrong here? or is there an easier way to do this?
update "style: tstyle" in step 3 to "style: tecStyle" and you should be good to go!

Leaflet.js: zoom map to visible markers

I have a leaflet.js map with a collection of markers, which can be shown/hidden in a legend div done with plugin Leaflet.StyledLayerControl (I think this is not too relevant but just in case).
I can capture the event when layers are shown or hidden and in that event I would like to zoom to fit all visible markers.
There are a few questions in SO with a very similar title but most of them pretend to fit zoom to a known collection of markers, which is not my case (some other questions with very similar subject refer to Google maps, not leaflet).
So, the question would be:
A) Can I set zoom to fit all visible markers?
B) or, how can I get an array of all visible markers to apply the other solutions seen?
This is how I create markers on map:
const icon_general = L.divIcon({html: '<i class="fas fa-map-marker fa-2x"></i>', iconSize: [20, 20], className: 'node_icon'});
var node_10031 = L.marker([40.7174605,-3.9199218],{ icon: icon_general}).addTo(layer1);
node_10031.bindPopup('<h2>Title</h2>');
var node_10032 = L.marker([40.7184576,-3.9202692],{ icon: icon_general}).addTo(layer1);
node_10032.bindPopup('<h2>Title</h2>');
var node_10032 = L.marker([40.7361371,-3.9453966],{ icon: icon_general}).addTo(layer2);
node_10032.bindPopup('<h2>Title</h2>');
Layers are then hidden or shown and I can capture that event, that is where I want to modifiy zoom or loop through visible markers.
EDIT (final solution based on Seth Lutske response):
This is my final solution, maybe it's less eficient as on each click it loops through all visible markers in map, but after some attempts this was the succesful one (I wasn't able to manage properly the individual events for add/remove layer):
function setZoom2Visible()
{
var visibleLayerGroup = new L.FeatureGroup();
mymap.eachLayer(function(layer){
if (layer instanceof L.Marker)
visibleLayerGroup.addLayer(layer);
});
const bounds = visibleLayerGroup.getBounds();
mymap.fitBounds(bounds);
}
$('.menu-item-checkbox input[type=checkbox]').click(function(){
setZoom2Visible();
});
Create a featureGroup and add the markers to it:
const myGroup = L.featureGroup([node_10031, node_10032, node_10033]);
On whatever event you're using to capture the markers being added to the map, you can get the bounds of the featureGroup and set the map's bounds to that:
function onMarkersAddedEventHandler(){
const bounds = myGroup.getBounds();
map.fitBounds(bounds);
}
Now if you're saying that each marker has its own toggle in the UI, it's a bit more complicated. You'll have to attach an event to each marker's UI checkbox, and on change of that checkbox, add or remove the marker from the group, then reset the bounds:
const node_10031_checkbox = document.querySelector('input[type="checkbox"]#10031');
function onMarkersAddedEventHandler(e){
if (e.target.checked){
myGroup.addLayer(node_10031)
} else {
myGroup.removeLayer(node_10031)
}
const bounds = myGroup.getBounds();
map.fitBounds(bounds);
}

Google Maps v3 move Markers on top when updated

My App show couriers on Google Maps and as soon as the application receive a GPS update for one of the courier I keep the Map updated with the new courier position. It happen every second for each of my couriers.
The issue is that every time a courier is updated it is moved on top of all others causing a "disco" effect on the map (because couriers may have differe marker color).
I wish to keep the Marker at the same position (in terms of zIndex or whatever) when its position is updated.
How can I do that?
I don't think any code is required here... I do nothing special than create markers and update markers, but I'm pretty sure a comment will arrive with: "show code", so here we are:
funciton createCourierMarkers() {
var couriersStorage = [];
for (var i = 0; i < couriers.length; i++) {
var courier = couriers[i];
var status = courier.status || 'free';
var courierPosition = _getCourierPosition(courier);
var icon = MarkersService.getCourierIcon(status);
var courierMarker = new google.maps.Marker({
map: map,
position: courierPosition,
icon: icon,
zIndex: 200,
type: 'courier',
id: courier.id,
status: status
});
couriersStorage.push(courierMarker);
}
}
function updateCourierMarkerPosition(courier, marker) {
var courierPosition = _getCourierPosition(courier);
marker.setPosition(courierPosition);
}

Shift a polyline which hides another one on Google Map

I am drawing transport lines that are overlapping: two lines are going through the same road.
var routePath = new google.maps.Polyline({
path: polylinePoints,
As one line hides the other, I would like to shift one of these lines to make the two lines visible on the network.
I was wondering if it was possible to move a polyline as "one whole shape"?
because when I use the options: draggable:true, editable:true I get the opportunity to redraw the polyline "point by point", and as you have understood, it is not what I would like to do.
Thanks.
You can try this way (it is only a suggestion not tested ):
manage the polyline by click event
google.maps.event.addListener(drawingManager, 'click', function(line) {
var coord = line.getPath();
var newCoords
var numCoord = coord.length;
for (i = 0; i < numCoord; i++) {
newCoord[i] = new google.maps.LatLng(coord[i].lat + yourShiftY, coord[i].lng + yourShiftX);
}
line.setPath(newCoord);
});

Google Maps: Render marker above markerclusterer

I have a set of markers that get clustered on my map.
Another set of markers are displayed individually, and i happen to need these to be displayed above the clusters.
I have tried setting zIndex in the clusters options object, lower than that of the 2nd set of markers, but to no avail.
Any idea how to go about this?
It can be done, but it's a pretty bumpy way until you get there... As Rick says, the problem is that the MarkerClusterer adds an own OverlayView with its cluster icons on a higher pane as the regular markers. The only way to add a marker above the clusters is to beat the clusterer with his own weapons and add an own OverlayView and add the marker icon markup to an even higher pane (read about panes here). I don't really like it - but it's the only way I found.
To do this you have to create a custom overlay implementing google.maps.OverlayView (reference), a good example can be found here (with explanations, I used a bit of code from it).
Here is a rough CustomOverlay prototype:
// build custom overlay class which implements google.maps.OverlayView
function CustomOverlay(map, latlon, icon, title) {
this.latlon_ = latlon;
this.icon_ = icon;
this.title_ = title;
this.markerLayer = jQuery('<div />').addClass('overlay');
this.setMap(map);
};
CustomOverlay.prototype = new google.maps.OverlayView;
CustomOverlay.prototype.onAdd = function() {
var $pane = jQuery(this.getPanes().floatPane); // Pane 6, one higher than the marker clusterer
$pane.append(this.markerLayer);
};
CustomOverlay.prototype.onRemove = function(){
this.markerLayer.remove();
};
CustomOverlay.prototype.draw = function() {
var projection = this.getProjection();
var fragment = document.createDocumentFragment();
this.markerLayer.empty(); // Empty any previous rendered markers
var location = projection.fromLatLngToDivPixel(this.latlon_);
var $point = jQuery('<div class="map-point" title="'+this.title_+'" style="'
+'width:32px; height:32px; '
+'left:'+location.x+'px; top:'+location.y+'px; '
+'position:absolute; cursor:pointer; '
+'">'
+'<img src="'+this.icon_+'" style="position: absolute; top: -16px; left: -16px" />'
+'</div>');
fragment.appendChild($point.get(0));
this.markerLayer.append(fragment);
};
This overlay gets the map, a LatLng object and the URL of an icon. The good thing is that you can write your own HTML to the layer, the bad thing is that you have to handle all the stuff the Maps API does for you (like marker image anchor handling) by your own. The example only works good with 32x32px images where the anchor is in the middle of the image, so it's still pretty rough.
To use the CustomOverlay, just instantiate it like this:
// your map center / marker LatLng
var myLatlng = new google.maps.LatLng(24.247471, 89.920990);
// instantiate map
var map = new google.maps.Map(
document.getElementById("map-canvas"),
{zoom: 4, center: myLatlng, mapTypeId: google.maps.MapTypeId.ROADMAP}
);
// create the clusterer, but of course with markers
//var markerClusterer = new MarkerClusterer(map, []);
// add custom overlay to map
var customCustomOverlay = new CustomOverlay(map, myLatlng, 'http://www.foo.bar/icon.png');
I hope this works for you.
I had the same issue but didn't want to handle a new overlay.
Without having to create a specific Overlay, you can just switch the overlay's parent containers z-indexes.
This can be achieved by using the following function :
_changeOverlayOrder = function(map) {
var panes = map.getPanes();
var markerOverlayDiv = panes.overlayImage.parentNode;
var clusterOverlayDiv = panes.overlayMouseTarget.parentNode;
// Make the clusters clickable.
if(!markerOverlayDiv.style.pointerEvents) {
markerOverlayDiv.style.cssText += ";pointer-events: none;";
}
// Switch z-indexes
if(markerOverlayDiv.style.zIndex < clusterOverlayDiv.style.zIndex) {
var tmp = markerOverlayDiv.style.zIndex;
markerOverlayDiv.style.zIndex = clusterOverlayDiv.style.zIndex;
clusterOverlayDiv.style.zIndex = tmp;
}
};
Hope it helps.
As far as I know this can't be done. The clusters reside in higher pane than the marker image.
https://developers.google.com/maps/documentation/javascript/reference#MapPanes
You can override it with CSS.
CSS
.gm-style-pbc + div > div > div:first-child{ z-index: 108 !important; }
SCSS
.gm-style-pbc{
+ div{
> div{
>div{
&:first-child{
z-index: 108 !important;
}
}
}
}
}

Categories