I have a feature in a Leaflet map made up of many circle markers (L.circleMarker). The markers are symbolized to show whether a picture was taken at that location.
What I would like to do is bring all the markers that do have a photo at that location to the top of the layer, similar to how a marker can be raised with a mouse-over (but without mouse-over in this case).
Here is an illustration of the original version:
And this is what I would like them to be:
I've considered having different layers for photos vs. no photos, but due to some functions in the map, it's preferable that they are in a single layer.
Any ideas as to how this could be done with JavaScript and Leaflet?
In the style definition for the circle marker it's possible to specify the "map pane" to which the marker is rendered (http://leafletjs.com/reference-1.0.3.html#map-pane). It seems like this is the only way to specify the zIndex of circleMarkers (regular markers have a zindexOffset option).
My code looks like this:
var style = {
radius: 1.75,
fillColor: "darkred",
color: "darkred",
pane: 'overlayPane'
};
var picStyle = {
radius: 0.25,
fillColor: "blue",
color: "blue",
pane: 'shadowPane'
};
var site2 = new L.geoJSON(valuesSite2, {
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
feature.properties.layer = "Site 2";
onEachFeature: onEachFeature;
if (<there is a picture>) {
return L.circleMarker(latlng, picStyle)
} else {
return L.circleMarker(latlng, style);
}
}
}).addTo(map);
The result looks like:
Related
// 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!
I am trying to style individual markers, or clusters of size 1, based on some feature property.
var markers = L.markerClusterGroup();
function onEachFeature(feature, layer) {
if (feature.properties.EncounterType && feature.properties.Year) {
layer.bindPopup(feature.properties.EncounterType + " in " +
feature.properties.Year);
}
}
function style(feature) {
switch (feature.properties.EncounterType) {
case 'Shooting':
return {
color: "ff0000"
};
case 'Sighting':
return {
color: "0000ff"
};
case 'Hunting':
return {
color: "ff0000"
};
}
}
var geoJsonLayer = L.geoJSON(storer, {
onEachFeature: onEachFeature
}, {
style: style
});
markers.addLayer(geoJsonLayer);
map.addLayer(markers);
The onEachFeature function successfully creates the popups. However, the style function does not change the color of the clusters of size 1. I've tried using the iconCreateFunction when initializing the markerclustergroup, however, that did not work either.
Your style option is separated in a 3rd argument of your call to L.geoJSON factory, whereas it should have been placed within the 2nd argument, alongside onEachFeature option.
var geoJsonLayer = L.geoJSON(storer, {
onEachFeature: onEachFeature,
style: style
});
But that is probably not the only reason for your issue.
style option will apply to vector shapes (polylines, polygons, etc.), i.e. to non-point data. It may also apply to Circle Markers, which can be used for Point type geometries, but you have to explicitly create them (typically through the pointToLayer option).
Those non-point data cannot be handled by Leaflet.markercluster.
Therefore if you see "clusters of size 1" (I guess you mean markers), they come from unstyled Point type geometries in your storer GeoJSON data.
This is a normal marker:
It is a PNG image that cannot be styled.
If you want to customize the appearance of your Point geometries, use custom icons, a plugin that provide such custom icons, or Circle Markers which you can modify the colour easily (including through the style option).
For example, if you were to choose that last option, you could do something like:
var geoJsonLayer = L.geoJSON(storer, {
pointToLayer: function (geoJsonPoint, latlng) {
return L.circleMarker(latlng);
},
onEachFeature: onEachFeature,
style: style
});
I'm adding some points to a map using the code below and they look great. I'm also adding some json polygons without issue.
When a certain zoom level is reached I would like the points and polygons to turn off. Using the map.removeLayer(name of polygon) turns off the polygon perfectly and then zoom back out I use map.addLayer(name of polygon) and they come back (using 'zoomend' and if else statement).
The point features do not react to the removeLayer function like the polygons do. I've also tried to harvestPoints.setOpacity(0) which does not work either. What code should I use to turn these geojson markers "on" and "off" like the polygon features?
function onEachPoint(feature, layer) {
layer.bindPopup(feature.properties.MGNT_AREA.toString());
layer.on('click', function (e) { layer.openPopup(); });
layer.bindLabel(feature.properties.MGNT_AREA.toString(), {
noHide: true,
className: "my-label",
offset: [-2, -25]
}).addTo(map);
};
var areaIcon = {
icon: L.icon({
iconUrl: 'labels/MonitoringIcon.png',
iconAnchor: [20, 24]
})
};
var harvestPoints = new L.GeoJSON.AJAX('labels/dfo_areas_point.json', {
onEachFeature: onEachPoint,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, areaIcon);
}
});
Not sure exactly what is the root cause for your issue, as we are missing how exactly you reference yours points (markers) when you try to remove them from the map.
Normally, there should be no difference between polygons and points (markers) to achieve what you described (removing the layers from the map at certain zoom level, and add them back at other zooms).
Note that setOpacity is a method for L.Markers, whereas you apply it to harvestPoints which is your geoJson layer group.
What may happen is that you add individual points (markers) to your map (last instruction in your onEachPoint function), but try to remove the layer group harvestPoints from map. Because it seems to be never added to the map, nothing happens.
If you want to turn on/off ALL the points in your harvestPoints layer group at the same time, then you would simply add / remove that layer group to / from the map, instead of adding individual markers:
var harvestPoints = L.geoJson.ajax('labels/dfo_areas_point.json', {
onEachFeature: onEachPoint,
pointToLayer: function (feature, latlng) {
// make sure `areaIcon` is an OPTIONS objects
return L.marker(latlng, areaIcon);
}
}).addTo(map); // Adding the entire geoJson layer to the map.
map.on("zoomend", function () {
var newMapZoom = map.getZoom();
if (newMapZoom >= 13) {
map.addLayer(harvestPoints);
} else {
// Removing entire geoJson layer that contains the points.
map.removeLayer(harvestPoints);
}
});
Side note: popups open on click by default, you should not need to add an on click listener for that?
Demo: http://jsfiddle.net/ve2huzxw/62/
I'm mixing up the choropleth example
and the layes control example
Using layers control with tiles, i've no problems, it works perfectly, but i need to work with geojson and this is my problem:
the gejson layer is ever in foreground and tiles on background.
but i need that when selecting a "streets" tile in layercontrol it come in foreground and l.geojson in backgrnd.
i need it to show labels. Using trasparency is not good for my purposes.
any solution?
here a part of the code:
var mapdataviz = L.mapbox.map('map', 'andria.h41061in')
.setView([39.0981, 16.5619], 8);
mapdataviz.legendControl.addLegend(document.getElementById('legend-content').innerHTML);
L.control.layers({
'city': L.mapbox.tileLayer('andria.cal_com_ief1'),
'com_geojson': L.geoJson(comData, {
style: getStyle,
onEachFeature: onEachFeature
}).addTo(mapdataviz),
'streets': L.mapbox.tileLayer('andria.h41061in')
},
{
'streets': L.mapbox.tileLayer('andria.h41061in'),
'com_geojson': L.geoJson(comData, {
style: getStyle,
onEachFeature: onEachFeature
}).addTo(mapdataviz),
'bounds': L.mapbox.tileLayer('andria.cal_conf')
}).addTo(mapdataviz);
var popup = new L.Popup({ autoPan: false });
// comData comes from the 'us-states.js' script included above
var comLayer = L.geoJson(comData, {
style: getStyle,
onEachFeature: onEachFeature
})
function getStyle(feature) {
return {
weight: 0.7,
opacity: 0.5,
color: 'white',
fillOpacity: 0.8,
fillColor: getColor(feature.properties.IEF1_2008)
};
}
You mention that transparency is not good for your purposes but this MapBox tutorial demonstrates how to put the geoJson layer behind the street map layer.
https://www.mapbox.com/blog/neighborhood-mapping-with-dynamic-vector-data-layers/
Is that helpful?
For anyone who is familiar with Leaflet, do you know a way to dynamically change a polygon's color? For example, take a circle defined like this:
window.circle = L.circle([51.508, -0.11], 500, {
color: 'red',
fillColor: '#ffffff',
fillOpacity: 0.5
}).addTo(map);
Then later, after a user clicks a button somewhere on an interface (for example), I want to change the color of the circle like this:
window.circle.options.fillColor = "#dddddd";
The code changes the value for window.circle.options.fillColor, but the change is not reflected by a change to the color of the polygon on the map. I've searched around but haven't found anything. Any ideas?
Thanks.
L.Circle extends L.Path (http://leafletjs.com/reference.html#path), that have method setStyle( <Path options> object ), and you can apply new style as window.circle.setStyle({fillColor: '#dddddd'});
If you are looking for something like this:
const circle = L.circle([lat, lng], {
style: style,
onEachFeature: onEachFeature,
});
These options are available for geoJson data ie: L.geojson()..... :D
So, for polygon .
Try,
circle.setStyle({
color: 'red'
});
I have a set of polygons in my map, this code can change the fillcolor of each polygon dynamically :
// 'file' is a geojson layer
L.geoJSON(file, {
onEachFeature: colorlayer,
style: {
color: "#00008c",
opacity: 0.6,
fillColor: '#333333',
fillOpacity: 0
}
}).addTo(map);
function colorlayer(feature, layer) {
layer.on('mouseover', function (e) {
layer.setStyle({
fillOpacity: 0.4
});
});
layer.on('mouseout', function (e) {
layer.setStyle({
fillOpacity: 0
});
});
}