Map contains two types of markers
Circle icon is added by:
let circle = new customCircleMarker([item.latitude, item.longitude], {
color: '#2196F3',
fillColor: '#2196F3',
fillOpacity: 0.8,
radius: radM,
addressId: item.address_id
}).bindTooltip(`Address: <b>${item.address}</b><br/>
Patients from this address: <b>${item.total_patients}</b><br/>
Production: <b>$${item.total_production}</b><br/>`)
.addTo(this.mapInstance).on('click', this.circleClick);
Icon marker is added in the following method:
// create marker object, pass custom icon as option, pass content and options to popup, add to map
// create marker object, pass custom icon as option, pass content and options to popup, add to map
L.marker([item.latitude, item.longitude], { icon: chartIcon })
.bindTooltip(`Address: <b>${item.address}</b><br/>
Patients from this address: <b>${item.total_patients}</b><br/>
Production: <b>$${item.total_production}</b><br/>`)
.addTo(this.mapInstance).on('click', this.circleClick);
On clearing map Icon marker is not removed
Map clearing function:
if (this.mapInstance) {
for (let i in this.mapInstance._layers) {
if (this.mapInstance._layers[i]._path !== undefined) {
try {
this.mapInstance.removeLayer(this.mapInstance._layers[i]);
} catch (e) {
console.log('problem with ' + e + this.mapInstance._layers[i]);
}
}
}
}
You're checking for a _path property before removing. It will skip the L.Marker layers because they don't have a _path property, only vector layers (extended from L.Path) do.
If you ever need to delete only certain layer types from your map you are best off by grouping them in a grouping layer like L.LayerGroup or L.FeatureGroup and then using their clearLayers method:
var layerGroup = new L.LayerGroup([
new L.Marker(...),
new L.CircleMarker()
]).addTo(map);
layerGroup.clearLayers();
If that is not an option you could iterate the map's layers and then check the instance of the layer:
function customClearLayers () {
map.eachLayer(function (layer) {
if (layer instanceof L.Marker || layer instanceof L.CircleMarker) {
map.removeLayer(layer);
}
});
}
Related
Im trying to check each geojson feature if it is a Marker. If it is I want to remove the placed layer then init drawing marker again.
If it is not the same position, I will just add it to the feature layer.
The problem is with eachLayer it always returns true because it loops trough all layers and it always return true because marker is added to feature. So it always repeats.
features.eachLayer(layer => {
if(layer.pm._shape === 'Marker') {
if(e.layer._latlng !== layer._latlng) { //This is never true, should be true if the placed marker is not placed over an existing features marker
features.addLayer(e.layer);
} else if(e.layer._latlng === layer._latlng) { //this elseif is always true for some reason and will loop
map.removeLayer(e.layer)
DrawUtil.addMarker(map, isSnapping); //Alias for pm.enableDraw.marker
features.addLayer(e.layer);
}
}
})
Here is fiddle, my bad forgot to add vital code.
https://jsfiddle.net/2ftmy0bu/2/
Change your code to:
// listen to when a new layer is created
map.on('pm:create', function(e) {
//should only place one marker each time
// check if the layer with the same latlng exists
var eq = features.getLayers().find(function(layer){
if(layer instanceof L.Marker) {
return layer.getLatLng().equals(e.layer.getLatLng())
}else{
return false;
}
}) !== undefined;
if(!eq) {
console.log('not equal')
features.addLayer(e.layer);
map.pm.disableDraw('Marker')
//if marker is placed on the map and it is not placed on same place as another marker
} else if(eq) {
console.log('equal')
//if marker is placed on the map over another marker, remove marker, then init draw marker again.
map.removeLayer(e.layer);
map.pm.enableDraw('Marker', {
snappable: true,
snapDistance: 20,
finishOn: 'click' || 'mousedown',
});
// TODO: I think you don't want this
// features.addLayer(e.layer);
}
});
https://jsfiddle.net/falkedesign/c6Lf758j/
I have the following code which fetches some remote GeoJSON from an API and displays the results on a Leaflet map:
<script>
// Center the map
var map = L.map('map').setView([54.233669, -4.406027], 6);
// Attribution
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=REMOVED', {
attribution: 'Map © OpenStreetMap',
id: 'mapbox.streets'
}).addTo(map);
// Create an empty layergroup for the data
var LayerUTMGroundHazards = L.layerGroup();
var LayerUTMAirspace = L.layerGroup();
// Style the features
function setStyle(feature) {
return {
fillColor: feature.properties.fillColor,
color: feature.properties.strokeColor,
fillOpacity: feature.properties.fillOpacity,
opacity: feature.properties.strokeOpacity
};
}
// Build Guardian UTM
function getGuardianUTMdata() {
// Clear the current Layer content
LayerUTMGroundHazards.clearLayers();
LayerUTMAirspace.clearLayers();
// Define what we want to include
function FuncGroundHazards(feature) {
if (feature.properties.category === "groundHazard") return true
}
function FuncAirspace(feature) {
if (
(feature.properties.category === "airspace" || feature.properties.category === "airport")
&& feature.properties.detailedCategory !== "uk:frz"
) return true
}
// Build the layers
fetch("https://example.com/json?n=" + map.getBounds().getNorth() + "&e=" + map.getBounds().getEast() + "&s=" + map.getBounds().getSouth() + "&w=" + map.getBounds().getWest(), { headers: { 'Authorization': 'REMOVED', 'X-AA-DeviceId': 'mySite' } })
.then(function (responseGuardianUTM) { return responseGuardianUTM.json() })
.then(function (dataGuardianUTM) {
// Create Layer: Ground Hazards
var featuresUTMGroundHazards = L.geoJson(dataGuardianUTM, {
filter: FuncGroundHazards,
style: setStyle,
pointToLayer: function (feature, latlng) { return L.marker(latlng, { icon: L.icon({ iconUrl: feature.properties.iconUrl, iconSize: [25, 25], }), }) },
onEachFeature: function (feature, layer) { layer.bindPopup(feature.properties.name); },
});
// Add the L.GeoJSON instance to the empty layergroup
LayerUTMGroundHazards.addLayer(featuresUTMGroundHazards).addTo(map);
});
// other layers are here (removed from this example)
}
// Update the Guardian UTM layer if the map moves
map.on('dragend', function () { getGuardianUTMdata(); });
map.on('zoomend', function () { getGuardianUTMdata(); });
// Layer controls
var layerControl = new L.Control.Layers(null, {
'Airspace Restrictions': LayerUTMAirspace,
'Ground Hazards': LayerUTMGroundHazards
// other layers are here (removed from this example)
}).addTo(map);
</script>
The problem is that every time the map is moved or zoomed, all of the Layer checkboxes are reset to Checked again, regardless of how many were checked before the map moved. They do not honour / remember their state when the map moves.
Given my code above, how can I store or preserve the checkbox state for each of the multiple Layers that I have so they are not reset every time the map is moved?
EDIT:
Here is a working fiddle. Remove the checkbox from the 'Ground Hazards', then move or zoom the map, you will see how it puts a tick back in the box again
https://jsfiddle.net/hdwz1b6t/1/
You're (re-)adding LayerUTMGroundHazards every time. This line here...
// Add the L.GeoJSON instance to the empty layergroup
LayerUTMGroundHazards.addLayer(featuresUTMGroundHazards).addTo(map);
...is not only adding featureUTMGroundHazards to layerUTMGroundHazards, it's also (re-)adding layerUTMGroundHazards to the map.
And quoting from https://leafletjs.com/examples/layers-control/ :
The layers control is smart enough to detect what layers we’ve already added and have corresponding checkboxes and radioboxes set.
So when you do LayerUTMGroundHazards.addTo(map);, the checkboxes reset.
I just starting to learn about Leaflet.js for my upcoming project.
What i am trying to accomplish:
I need to make a list of marker which displayed on the map, and when the list item is being hovered (or mouseover) it will show where the position on the map (for single marker, it should change its color. For Clustered marker, it should display Coverage Line like how it behave when we hover it.. and perhaps change its color too if possible).
The map should not be changed as well as the zoom level, to put it simply, i need to highlight the marker/ Cluster on the map.
What i have accomplished now : I am able to do it on Single Marker.
what i super frustrated about : I failed to find a way to make it happen on Clustered Marker.
I use global var object to store any created marker.
function updateMapMarkerResult(data) {
markers.clearLayers();
for (var i = 0; i < data.length; i++) {
var a = data[i];
var myIcon = L.divIcon({
className: 'prop-div-icon',
html: a.Description
});
var marker = L.marker(new L.LatLng(a.Latitude, a.Longitude), {
icon: myIcon
}, {
title: a.Name
});
marker.bindPopup('<div><div class="row"><h5>Name : ' + a.Name + '</h5></div><div class="row">Lat : ' + a.Latitude + '</div><div class="row">Lng : ' + a.Longitude + '</div>' + '</div>');
marker.on('mouseover', function(e) {
if (this._icon != null) {
this._icon.classList.remove("prop-div-icon");
this._icon.classList.add("prop-div-icon-shadow");
}
});
marker.on('mouseout', function(e) {
if (this._icon != null) {
this._icon.classList.remove("prop-div-icon-shadow");
this._icon.classList.add("prop-div-icon");
}
});
markersRef[a.LocId] = marker; // <-- Store Reference
markers.addLayer(marker);
updateMapListResult(a, i + 1);
}
map.addLayer(markers);
}
But i don't know which object or property to get the Clustered Marker reference.
And i trigger the marker event by my global variable (which only works on single marker).
...
li.addEventListener("mouseover", function(e) {
jQuery(this).addClass("btn-info");
markersRef[this.getAttribute('marker')].fire('mouseover'); // --> Trigger Marker Event "mouseover"
// TODO : Trigger ClusteredMarker Event "mouseover"
});
...
This is my current https://jsfiddle.net/oryza_anggara/2gze75L6/, any lead could be a very big help. Thank you.
Note: the only js lib i'm familiar is JQuery, i have no knowledge for others such as Angular.js
You are probably looking for markers.getVisibleParent(marker) method, to retrieve the containing cluster in case your marker is clustered.
Unfortunately, it is then not enough to fire your event on that cluster. The coverage display functionality is set on the Cluster Group, not on its individual clusters. Therefore you need to fire your event on that group:
function _fireEventOnMarkerOrVisibleParentCluster(marker, eventName) {
var visibleLayer = markers.getVisibleParent(marker);
if (visibleLayer instanceof L.MarkerCluster) {
// In case the marker is hidden in a cluster, have the clusterGroup
// show the regular coverage polygon.
markers.fire(eventName, {
layer: visibleLayer
});
} else {
marker.fire(eventName);
}
}
var marker = markersRef[this.getAttribute('marker')];
_fireEventOnMarkerOrVisibleParentCluster(marker, 'mouseover');
Updated JSFiddle: https://jsfiddle.net/2gze75L6/5/
That being said, I think another interesting UI, instead of showing the regular coverage polygon that you get when "manually" hovering a cluster, would be to spiderfy the cluster and highlight your marker. Not very easy to implement, but the result seems nice to me. Here is a quick try, it would probably need more work to make it bullet proof:
Demo: https://jsfiddle.net/2gze75L6/6/
function _fireEventOnMarkerOrVisibleParentCluster(marker, eventName) {
if (eventName === 'mouseover') {
var visibleLayer = markers.getVisibleParent(marker);
if (visibleLayer instanceof L.MarkerCluster) {
// We want to show a marker that is currently hidden in a cluster.
// Make sure it will get highlighted once revealed.
markers.once('spiderfied', function() {
marker.fire(eventName);
});
// Now spiderfy its containing cluster to reveal it.
// This will automatically unspiderfy other clusters.
visibleLayer.spiderfy();
} else {
// The marker is already visible, unspiderfy other clusters if
// they do not contain the marker.
_unspiderfyPreviousClusterIfNotParentOf(marker);
marker.fire(eventName);
}
} else {
// For mouseout, marker should be unclustered already, unless
// the next mouseover happened before?
marker.fire(eventName);
}
}
function _unspiderfyPreviousClusterIfNotParentOf(marker) {
// Check if there is a currently spiderfied cluster.
// If so and it does not contain the marker, unspiderfy it.
var spiderfiedCluster = markers._spiderfied;
if (
spiderfiedCluster
&& !_clusterContainsMarker(spiderfiedCluster, marker)
) {
spiderfiedCluster.unspiderfy();
}
}
function _clusterContainsMarker(cluster, marker) {
var currentLayer = marker;
while (currentLayer && currentLayer !== cluster) {
currentLayer = currentLayer.__parent;
}
// Say if we found a cluster or nothing.
return !!currentLayer;
}
I am having a bit of trouble setting the style on a L.geoJSON based layer,my code looks like this:
var addProperties = function addProperties(prop,map)
{
//the API does not seem to support adding properties to an existing feature,
//the idea here is simple:
//(1) currentFeature.toGeoJson() needs to be called to obtain a json representation
//(2) set the properties on the geojson
//(3) create a new feature based on the geojson
//(4) remove res and add the new feature as res
var style = function style(feature){
var markerStyle = {
draggable: 'true',
icon: L.AwesomeMarkers.icon({
icon: 'link',
prefix: 'glyphicon',
markerColor: 'red',
spin: true
})
};
if(feature.geometry.type==='Point')
return markerStyle;
};
var onEachFeature = function onEachFeature(feature,layer){
console.log("Inside on each feature,checking to see if feature was passed to it ",feature);
layer.on('click',function(e){
//open display sidebar
console.log("Checking to see if setupTabs exists ",setupTabs);
setupTabs('#display-feature-tabs');
console.log("Checking to see if featureInfo exists ",featureInfo);
var featureInfoAPI =featureInfo('feature-properties');
featureInfoAPI.swap(feature.properties);
setTimeout(function() {
sidebar.show();
}, 100);
});
};
console.log("Inside add properties");
var geoJSON,feature;
if(res != null)
{
geoJSON = res.toGeoJSON();
geoJSON.properties = prop;
console.log(geoJSON);
feature = L.geoJson(geoJSON,{style:style,onEachFeature:onEachFeature});
console.log("The new feature that has been created ",feature);
removeFeature(map);
addFeature(feature);
feature.addTo(map);
}
};
I have also tried the style method as well,I am looking to add styles to the active layer for points(styles will also be added for lines and polylines by type).
To style point features, you should use pointToLayer option instead.
I can't seem to get the my map markers to display when I use an image sprite on the iphone. They appear when I use the standard google map markers on iphone and when viewing the site in the desktop the sprite icons work fine.
Here is the code I use to create the markers, I am using Zepto but JQuery could as easily apply.
$.ajax({
dataType: 'jsonp',
url: myLocations.LocatorUrl,
timeout: 8000,
success: function(data) {
var infoWindow = new google.maps.InfoWindow();
var bounds = new google.maps.LatLngBounds();
$.each(data, function(index, item){
var data = item, pincolor,
latLng = new google.maps.LatLng(data.lat, data.lng);
var d = 'http://blah';
var pinImage = new google.maps.MarkerImage(d+"/assets/img/sprite.locator.png",
new google.maps.Size(24, 36),
new google.maps.Point(0,25),
new google.maps.Point(10, 34));
// Creating a marker and putting it on the map
var marker = new google.maps.Marker({
position: latLng,
map: map,
title: data.type,
icon: pinImage
});
bounds.extend(latLng); // Extend the Latlng bound method
var bubbleHtml = '<div class="bubble"><h2>'+item.type+'</h2><p>'+item.address+'</p></div>'; // Custom HTML for the bubble
(function(marker, data) {
// Attaching a click event to the current marker
google.maps.event.addListener(marker, "click", function(e) {
infoWindow.setContent(bubbleHtml);
infoWindow.open(map, marker);
});
markers.push(marker); // Push markers into an array so they can be removed
})(marker, data);
});
map.fitBounds(bounds); // Center based on values added to bounds
}, error: function(x, t, m) {
console.log('errors')
if(t==="timeout") {
alert("got timeout");
} else {
alert(t);
}
}
});
Got it. Turns out the images I was referencing were on localhost, when I swapped this to the actual IP address of my local machine it worked.