I have a feature layer pull from geoJson and then syncing a table. I'm trying to make it when I zoom in on eachFeature, it filters the table to those features. Below is my script that is not working. I'm getting the error at 'if (map.getBounds().contains(layer.getBounds()))' Can I get some help?
var featureLayer = L.geoJson(null, {
filter: function(feature, layer) {
return feature.geometry.coordinates[0] !== 0 && feature.geometry.coordinates[1] !== 0;
},
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {
title: feature.properties["status_title_github"],
riseOnHover: true,
icon: L.icon({
iconUrl: "assets/pictures/markers/cb0d0c.png",
iconSize: [30, 40],
iconAnchor: [15, 32]
})
});
},
onEachFeature: function (feature, layer) {
if (feature.properties) {
layer.on({
click: function (e) {
identifyFeature(L.stamp(layer));
highlightLayer.clearLayers();
highlightLayer.addData(featureLayer.getLayer(L.stamp(layer)).toGeoJSON());
},
mouseover: function (e) {
if (config.hoverProperty) {
$(".info-control").html(feature.properties[config.hoverProperty]);
$(".info-control").show();
}
},
mouseout: function (e) {
$(".info-control").hide();
}
});
if (feature.properties["marker-color"]) {
layer.setIcon(
L.icon({
iconUrl: "assets/pictures/markers/" + feature.properties["marker-color"].replace("#",'').toLowerCase() + ".png",
iconSize: [30, 40],
iconAnchor: [15, 32]
})
);
legendItems[feature.properties.Status] = feature.properties["marker-color"];
}
}
}
});
function syncTable() {
tableFeatures = [];
featureLayer.eachLayer(function (layer) {
layer.feature.properties.leaflet_stamp = L.stamp(layer);
if (map.hasLayer(featureLayer)) {
if (map.getBounds().contains(layer.getBounds())) {
tableFeatures.push(layer.feature.properties);
}
}
});
$("#table").bootstrapTable("load", JSON.parse(JSON.stringify(tableFeatures)));
var featureCount = $("#table").bootstrapTable("getData").length;
if (featureCount == 1) {
$("#feature-count").html($("#table").bootstrapTable("getData").length + " visible feature");
} else {
$("#feature-count").html($("#table").bootstrapTable("getData").length + " visible features");
}
}
Most probably you are trying to getBounds on a marker.
You understand that point features do not cover any area, therefore there should be no reason to try retrieving their "bounds".
Before testing if your map viewport contains the layer bounds, check whether it is a Marker or not, i.e. a point type feature
layer instanceof L.Marker
Or:
getLatLng in layer
Or since your layers come from GeoJSON data and are built through L.geoJSON factory:
layer.feature.geometry.type === "Point"
Then you can check if that layer is visible in your current map view port in a similar way:
map.getBounds().contains(layer.getLatLng())
BTW for other (i.e. non point type) geometries, I think you would probably prefer checking if their bounds intersects the map view port, rather than is completely contained within.
Related
I'm trying to Change Leaflet markers to circleMarker on data coming from geoJSON file.
Until now her is how I display data on map:
const geodesic = new L.Geodesic().addTo(map); /* Affiche les ligne géodésiques*/
geodesic.fromGeoJson(waypoints);
function pointFilter(feature) {
if (feature.geometry.type === "Point") return true
}
var points = new L.geoJson(waypoints, {filter: pointFilter}).addTo(map);
My geoJSON file contains LineStrings and Points.
The geodesic lines and standard icon markers are displayed, but I would change them by circleMarker between each lines.
Hope I'm clear enough.
Thanks
Pierre
This is what the code may look like, change it as you see fit.
Here'a a whole example - autocomplete-with-geojson
const geojsonlayer = L.geoJSON(object, {
style: function (feature) {
return {
color: feature.properties.color || "red",
weight: 7,
opacity: 1,
fillOpacity: 0.7,
};
},
pointToLayer: (feature, latlng) => {
if (feature.properties.type === "Point") {
return new L.circleMarker(latlng, {
radius: 20,
});
}
},
onEachFeature: function (feature, layer) {},
});
I have two leaflet maps which are loaded with geojson on the basis of a search button click. This shows some polygons with a style opacity 0.3 so you can see the street names under the polygons.
It works great except that any additional searches and loading of polygons starts to change the opacity of the polygon, making it more solid so you cant read the names of the streets under the polygon.
I try clearing the geojson layer before adding to the map, but the issue persists.
I have created a rough code pen of the issue here:
https://codepen.io/joomkit/pen/xxXgLPJ?editors=1111
Essentially just click the search button to load the layer no need to fill the listener runs a function and gets static data.
I have tried various methods to remove layer. A second click on the search is meant to clear the layer and load a new one. In the example it's just reloading the original data but the opacity is clearly demonstrated.
Main code is also below.
var geoMap2;
var lamap = new L.Map("map2", {
center: new L.LatLng(51.44094723464765, 0.048892332250943187),
// center: new L.LatLng(39.75621,-104.99404),
zoom: 14,
maxZoom: 18
});
var osm2 = L.tileLayer(
"https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
{
attribution:
'© OpenStreetMap contributors © CARTO',
subdomains: "abcd",
maxZoom: 18
}
);
lamap.addLayer(osm2);
searchButton.addEventListener("click", function (e) {
let searchQuery = inputSearch.value;
// searchOpenFunding(searchQuery);
setLaMap(data);
});
function setLaMap(data) {
removeLayers();
let geojsonFeatureCollection2 = {
type: "FeatureCollection",
features: setFeatureCollection2(data)
};
geoMap2 = L.geoJSON(geojsonFeatureCollection2, {
style: polyStyleLAMap,
onEachFeature: function myonEachFeatureLaMap(feature, layer) {
layer.myTag = "myGeoJSON";
}
}).addTo(lamap);
lamap.setMaxBounds(lamap.fitBounds(geoMap2.getBounds()));
lamap.setZoom(13);
}
var removeLayers = function () {
lamap.eachLayer(function (layer) {
if (layer.myTag && layer.myTag === "myGeoJSON") {
lamap.removeLayer(layer);
console.log("rem layer from ");
}
});
};
function setFeatureCollection2(data) {
for (const [key, item] of Object.entries(data)) {
// setup lealfet geojson collection from data mapit api is incomplete pe ritem so we build it here
geoJsonFeatures2.push({
type: "Feature",
properties: {
craftentryId: item.id,
areaId: item.mapitAreaId,
lsoacode: item.lsoacode,
localauthority: item.localauthority,
openForFunding: item.openForFunding,
fundableRegion: item.fundableRegion,
title: item.title,
popupContent: ""
},
geometry: item.geojson
});
}
return geoJsonFeatures2;
}
function polyStyleLAMap(feature) {
return {
fillColor: getFillColorLaMap(feature.properties.openForFunding),
weight: 1,
opacity: 1,
color: getBorderColor(feature.properties.openForFunding),
dashArray: "0",
fillOpacity: 0.3
};
}
function getFillColorLaMap(d) {
return d === true ? "#FFFFFF" : d === false ? "#FED976" : "#FED976";
}
function getBorderColor(d) {
return d === true ? "#0e9c12" : d === false ? "#adabab" : "#cccccc";
}
Look here https://codepen.io
The code I modified is:
// 029A, 029C
function setLaMap(data) {
removeLayers();
let geojsonFeatureCollection2 = {
type: "FeatureCollection",
features: setFeatureCollection2(data),
};
console.log(geojsonFeatureCollection2);
const geoMap2 = L.geoJSON(geojsonFeatureCollection2, {
style: polyStyleLAMap,
onEachFeature: function (feature, layer) {
layer.myTag = "myGeoJSON";
},
});
map.addLayer(geoMap2);
map.setMaxBounds(map.fitBounds(geoMap2.getBounds()));
// map.setZoom(13);
}
function removeLayers() {
map.eachLayer((layer) => {
if (layer.myTag && layer.myTag === "myGeoJSON") {
console.log(layer);
map.removeLayer(layer);
}
});
}
You need to keep the geojson layer in a global variable and remove it before over writing with the new layer data.
var geoMap2;
function setLaMap(data){
if(geoMap2) { // if geoMap2 is set remove it
lamap.removeLayer(geoMap2)
}
const geojsonFeatureCollection2 = {
"type": "FeatureCollection",
"features": setFeatureCollection2(data)
}
geoMap2 = L.geoJSON(geojsonFeatureCollection2, {
style: polyStyleLAMap,
onEachFeature: myonEachFeatureLaMap
})
.........
Lines and polygons are added without problems, but when trying to add markers on top of a polygon layer ("lyrPoly"), it interprets that I want to click on the polygon (e.g. opens popup) instead of adding marker. How can I avoid this? I am using leaflet.pm to add markers, linestrings and polygons.
This is the code for the marker:
map.on('pm:create', function(e) {
e.marker;
var type = e.layerType,
layer = e.layer;
$(document).ready(function() {
var jsnMarker = layer.toGeoJSON().geometry;
jsnMarker = {
type: "MultiPoint",
coordinates: [jsnMarker.coordinates]
};
})
});
$(function() {
$('.check').change(function() {
var data = $(this).val();
if (data == "versionOne") {
var markerStyle = {
draggable: false,
icon: customIcon,
};
var options = {
markerStyle,
}
map.pm.enableDraw('Marker', options);
} else if (data == "versionTwo") {
var markerStyle = {
draggable: false,
icon: otherIcon,
};
var options = {
markerStyle,
}
map.pm.enableDraw('Marker', options);
}
$('.check').trigger('change');
});
And inside the ajax function I use this:
lyrMarker = L.geoJSON(jsnMarker, {
pointToLayer: function (feature, latlng) {
if(feature.properties.marker_type=="versionOne"){
return L.marker(latlng, {
icon: customIcon
})
}else if
(feature.properties.marker_type=="versionTwo"){
return L.marker(latlng, {
icon: otherIcon
})
} ,
onEachFeature: forEachMarker
});
For the polygon-layer I have a corresponding "onEachFeature"-function that looks like this:
function forEachFeature(feature, layer) {
var att = feature.properties;
var popupContent =
"Popup-content" ;
layer.bindPopup(popupContent, {
offset: [0, -120]
});
layer.on("click", function (e) {
lyrPoly.setStyle(style); //resets layer colors
layer.setStyle(highlight); //highlights selected.
});
};
Adding marker to the background/basemap works fine, but not onto the polygon layer. Why is that, and what would be a good solution? I don't want to add the marker to the same layer as the polygons, but avoid polygons "getting in the way".
I've had ideas about making the polygon layer interactive: false while in adding-marker-mode, but haven't succeeded.
Update your click event and test if drawing mode is enabled:
function forEachFeature(feature, layer) {
var att = feature.properties;
var popupContent =
"Popup-content" ;
layer.bindPopup(popupContent, {
offset: [0, -120]
});
layer.on("click", function (e) {
if(!map.pm.Draw.Marker.enabled()){
lyrPoly.setStyle(style); //resets layer colors
layer.setStyle(highlight); //highlights selected.
}
});
};
Update
To disable the popup open event add following code at the top of your file. Before you create a Layer with a Popup:
L.Layer.include({
_openPopup: function (e) {
var layer = e.layer || e.target;
//This IF is new
if(map.pm.Draw.Marker.enabled()){
return;
}
if (!this._popup) {
return;
}
if (!this._map) {
return;
}
// prevent map click
L.DomEvent.stop(e);
// if this inherits from Path its a vector and we can just
// open the popup at the new location
if (layer instanceof L.Path) {
this.openPopup(e.layer || e.target, e.latlng);
return;
}
// otherwise treat it like a marker and figure out
// if we should toggle it open/closed
if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
this.closePopup();
} else {
this.openPopup(layer, e.latlng);
}
},
});
Example: https://jsfiddle.net/falkedesign/dg4rpcqe/
I've been making a Leaflet map for a while and trying to figure out the way to make it so if I click one of the polygon in GeoJSON layer, it will remove the current layer and replace it with another layer.
Likewise, if I click it again, it will remove the new layer and replace it with previous layer.
I've been trying to tinker with different stuff but nothing works. This is one of my recent attempt.
<script type="text/javascript" src="provinces.js"></script>
<script>
var map = L.map('map').setView([-2.5, 119], 5);
L.tileLayer('http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap © CartoDB',
subdomains: 'abcd',
maxZoom: 19
}).addTo(map);
// get color depending on population density value
function getColor(d) {
return d > 5000 ? '#800026' :
d > 2500 ? '#BD0026' :
d > 1000 ? '#E31A1C' :
d > 500 ? '#FC4E2A' :
'#FFEDA0';
}
function style(feature) {
return {
weight: 2,
opacity: 1,
color: 'white',
dashArray: '',
fillOpacity: 0.7,
fillColor: getColor(feature.properties.kode)
};
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#ccc',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
var geojson;
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function addNewBoundary(e) { // this function doesn't do anything
var newLayerBoundary = new L.geoJson();
newLayerBoundary.addTo(map);
$.ajax({
dataType: "json",
url: "province-details.geojson",
success: function(data) {
$(data.features).each(function(key, data) {
newLayerBoundary.addData(data);
});
}
}).error(function() {});
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: clearLayers // with this it just clears the layers before being clicked
});
}
geojson = L.geoJson(provinces, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
</script>
var layers = [firstLayer,secondLayer]
function switchLayers(){
if(map.haslayer(layers[firstLayer])){
map.addLayer(layers[secondLayer]);
map.removeLayer(layers[firstLayer]);
}else{
if(map.haslayer(layers[secondLayer])){
map.addLayer(layers[firstLayer]);
map.removeLayer(layers[secondLayer]);
}
}
I need to load a WFS service (points) in my Leaflet map.
I know how to load a WFS service in my map but I've to check dinamically the map current scale / extent because I've to limit the number of features I've to request to the server and to render on my map.
My service has a lot of points and I'd like to limit the visualization of my layer only when we are al level=18 of my basemap (OpenStreetMap).
Is there any way to check dinamically the map current extent / scale and so decide if invoke or not my WFS Service?
Any example?
Thank you very much in advance, any suggestion is appreciate!!!!
Cesare
I solved in this manner
.....
.....
.....
function onEachFeature(feature, layer) {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
};
myGeoJSONLayeraddressPCN3 = L.geoJson(myGeoJSON, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, geojsonMarkerOptions);
},
onEachFeature: onEachFeature
});
var baseLayers = {
"OSM": background
};
var overlays = {
"My GeoJSON Layer name": myGeoJSONLayer
};
map = new L.Map('map', {
center: [42, 12],
zoom: 6,
layers: [background]
});
// *** add base layers and overlay layers list to map ... ***
TOC = L.control.layers(baseLayers, overlays);
TOC.addTo(map);
map.on('overlayadd', function(e) {
map.removeLayer(myGeoJSONLayer);
TOC.removeLayer(myGeoJSONLayer);
if ((e.name == "My GeoJSON Layer name") && (map.getZoom() < 18)){
alert('To view the layer zoom at max details ...');
TOC.addOverlay(myGeoJSONLayer, "My GeoJSON Layer name");
isOnMap = 0;
}
if ((e.name == "My GeoJSON Layer name") && (map.getZoom() >= 18)){
var currentExtent = map.getBounds().toBBoxString();
invokeService(currentExtent);
}
});
map.on('overlayremove', function(e) {
if (e.name == "My GeoJSON Layer name"){
isOnMap = 0;
}
});
map.on('dragend', function(e) {
if ((map.getZoom() >= 18) && (isOnMap == 1)){
map.removeLayer(myGeoJSONLayer);
TOC.removeLayer(myGeoJSONLayer);
var currentExtent = map.getBounds().toBBoxString();
invokeService(currentExtent);
}
});
map.on('zoomend', function(e) {
if (map.getZoom() < 18) {
map.removeLayer(myGeoJSONLayer);
TOC.removeLayer(myGeoJSONLayer);
TOC.addOverlay(myGeoJSONLayer, "My GeoJSON Layer name");
}
});
and I've also a function invokeService() where I've ...
function invokeService (extent) {
.....
.....
.....
function onEachFeature(feature, layer) {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
}
myGeoJSONLayeraddressPCN3 = L.geoJson(MyGeoJSON, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, geojsonMarkerOptions);
},
onEachFeature: onEachFeature
});
addressPCN3.addTo(map);
TOC.addOverlay(addressPCN3, "My GeoJSON Layer name");
isOnMap = 1;
}
.....
.....
.....
}
It's working now!