How to change the color of results from leaflet-knn on the map - leaflet

I have displayed the the result markers for the leaflet-knn on the map with following code:
const myloc = new L.LatLng(13.7433242, 100.5421583);
var gjLayer = L.geoJson(testCities, {
onEachFeature: function(feature, layer) {
content = "<b>Name:</b> " + feature.properties.name;
layer.bindPopup(content);
}
});
var longitude = myloc.lng,
latitude = myloc.lat;
var res = leafletKnn(gjLayer).nearest(
[longitude, latitude], 5, distance);
for (i = 0; i < res.length; i++) {
map.addLayer(res[i].layer);
}
Now I want to change the color of this marker that is added or I want to change the icon.
Can anybody tell me how can I do?

Leaflet-knn is agnostic when it comes to the representation of the results - it relies on the existing L.Layers: it takes a L.GeoJSON as an input and then iterates through all its members in order to fetch all their coordinates (in the case of polylines and polygons) and then store a reference to the L.Layer for each of its coordinates.
The results of a leaflet-knn search include the original L.Layer from the L.GeoJSON that was passed at instantiation time.
Either symbolize your GeoJSON afterwards, as explained in the Leaflet tutorials, or create new markers/symbols for the results after each query.
Right now your code is relying on the default symbolization of GeoJSON data (instantiate a L.Marker with a L.Icon.Default for points). I suggest the approach of displaying your L.GeoJSON in the map to ensure that it looks like you want it to (even if it's a partial set of the data), then implementing the leaflet-knn search.

Related

Mapbox: Filtering out markers in a Leaflet Omnivore KML layer

I am exporting Google Directions routes as KML and displaying them on a Mapbox map by reading them with Omnivore and adding them to the map,
The Google KML stores each route as two Places (the start and end points) and one LineString (the route). In Mapbox I would like to show only the routes, that is to filter out the markers somehow. I'm displaying markers out of my own database and the Google markers clutter it up.
Here is my code. I change the styling of the LineStrings just to show that I can, but do not know what magic call(s) to make to not display the Points.
Thanks.
runLayer = omnivore.kml('data/xxxx.kml')
.on('ready', function() {
var llBnds = runLayer.getBounds();
map.fitBounds(llBnds);
this.eachLayer(function (layer) {
if (layer.feature.geometry.type == 'LineString') {
layer.setStyle({
color: '#4E3508',
weight: 4
});
}
if (layer.feature.geometry.type == 'Point') {
//
// Do something useful here to not display these items!!
//
}
});
})
.addTo(map);
Welcome to SO!
Many possible solutions:
Most straight forward from the code you provided, just use the removeLayer method on your runLayer Layer Group when you get a 'Point' feature.
Cleaner solution would be to filter out those features before they are even converted into Leaflet layers, through a custom GeoJSON Layer Group passed as 3rd argument of omnivore.kml, with a specified filter option:
var customLayer = L.geoJSON(null, {
filter: function(geoJsonFeature) {
// my custom filter function: do not display Point type features.
return geoJsonFeature.geometry.type !== 'Point';
}
}).addTo(map);
var runLayer = omnivore.kml('data/xxxx.kml', null, customLayer);
You can also use the style and/or onEachFeature options on customLayer to directly apply your desired style on your LineString.

leaftletjs-adding points dynamically and draw line string

I am trying to draw the path of a flight using leafletjs and geojson. I'll be getting the geometry from a stream.
this is what I have done so far:
let index = 0;
let geoJsonLayer;
let intervalFn = setInterval(function () {
let point = trackData.features[index++];
if(point) {
let coords = point.geometry.coordinates;
coords.pop();
coords.reverse();
geoFeature.geometry.coordinates.push(coords);
if(map.hasLayer(geoJsonLayer)) map.removeLayer(geoJsonLayer);
geoJsonLayer = L.geoJson(geoFeature, {
onEachFeature: (feature, layer) => {
const content = feature.properties.title;
layer.bindPopup(content);
}
});
geoJsonLayer.addTo(map);
// console.log(coords);
} else {
clearInterval(intervalFn);
}
}, 100);
setInterval is to simulate the part whereby I get the geometry from a stream.
now when a user clicks on the path I need to show some properties of the path, and I am trying to use the onEachFeature for that, but its not working correctly.
I suspect its because I am removing the layers (I did this to improve the performance)
Is there any other better ways to do what I am trying to achieve ?
You should probably try addLatLng()
Adds a given point to the polyline.
Your geoFeature sounds to be a single Feature, so your geoJsonLayer will contain a single layer (polyline):
let myPolyline;
geoJsonLayer.eachLayer(function (layer) {
myPolyline = layer; // Will be done only once actually.
});
// When you receive a new point…
myPolyline.addLatLng([lat, lng]);
With this you should not have to remove your layers every time.
The popup should therefore stay open, if it is shown.
Demo: https://jsfiddle.net/3v7hd2vx/265/ (click on the button to add new points)

Leaflet map with Socrata open data with lat/lng in a combined geom point column?

I learned how to create a Leaflet map with Socrata open data when latitude and longitude appear in separate columns. See working examples at https://github.com/JackDougherty/leaflet-socrata
My problem: how to make this work when latitude and longitude are combined in one point data field (geom) inside Socrata? This is common for datasets published on http://data.hartford.gov. See my non-working example at https://github.com/JackDougherty/leaflet-socrata
Solved with this code snippet:
// load open data from Socrata endpoint in GeoJSON format
// Food Establishments, City of Hartford Open Data https://data.hartford.gov/resource/daux-ukcc
$.getJSON("https://data.hartford.gov/resource/daux-ukcc.geojson", function (data){
var geoJsonLayer = L.geoJson(data, {
pointToLayer: function( feature, latlng) {
var marker = L.marker(latlng);
marker.bindPopup(feature.properties.blms_dba); // replace last term with property data labels to display from GeoJSON file
return marker;
}
}).addTo(map);
});
See my notes and more examples at https://www.datavizforall.org/leaflet/with-socrata/

How to set the zIndex layer order for geoJson layers?

I would like to have certain layers to be always on top of others, no matter in which order they are added to the map.
I am aware of bringToFront(), but it does not meet my requirements. I would like to set the zIndex dynamically based on properties.
Leaflet has the method setZIndex(), but this apparently does not work for geoJson layers:
https://jsfiddle.net/jw2srhwn/
Any ideas?
Cannot be done for vector geometries.
zIndex is a property of HTMLElements, and vector geometries (lines and polygons) are rendered as SVG elements, or programatically as <canvas> draw calls. Those two methods have no concept of zIndex, so the only thing that works is pushing elements to the top (or bottom) of the SVG group or <canvas> draw sequence.
Also, remind that L.GeoJSON is just a specific type of L.LayerGroup, in your case containing instances of L.Polygon. Furthermore, if you read Leaflet's documentation about the setZIndex() method on L.LayerGroup:
Calls setZIndex on every layer contained in this group, passing the z-index.
So, do L.Polygons have a setZIndex() method? No. So calling that in their containing group does nothing. It will have an effect on any L.GridLayers contained in that group, though.
Coming back to your problem:
I would like to have certain layers to be always on top of others, no matter in which order they are added to the map.
Looks like the thing you're looking for is map panes. Do read the map panes tutorial.
This is one of the reason for the implementation of user defined "panes" in Leaflet 1.0 (compared to versions 0.x).
Create panes: var myPane = map.createPane("myPaneName")
If necessary, set the class / z-index of the pane element: myPane.style.zIndex = 450 (refer to z-index values of built-in panes)
When creating your layers, specify their target pane option: L.rectangle(corners, { pane: "myPaneName" })
When building through the L.geoJSON factory, you can loop through your features with the onEachFeature option to clone your layers with specified target pane.
Demo: https://jsfiddle.net/3v7hd2vx/90/
For peoples who are searching about Z-Index
All path layers (so all except for markers) have no z-index because svg layers have a fix order. The first element is painted first. So the last element is painted on top.
#IvanSanchez described good why zIndex not working.
You can control the order with layer.bringToBack() or layer.bringToFront().
With that code you have more options to control the order of the layers.
L.Path.include({
getZIndex: function() {
var node = this._path;
var index = 0;
while ( (node = node.previousElementSibling) ) {
index++;
}
return index;
},
setZIndex: function(idx) {
var obj1 = this._path;
var parent = obj1.parentNode;
if(parent.childNodes.length < idx){
idx = parent.childNodes.length-1;
}
var obj2 = parent.childNodes[idx];
if(obj2 === undefined || obj2 === null){
return;
}
var next2 = obj2.nextSibling;
if (next2 === obj1) {
parent.insertBefore(obj1, obj2);
} else {
parent.insertBefore(obj2, obj1);
if (next2) {
parent.insertBefore(obj1, next2);
} else {
parent.appendChild(obj1);
}
}
},
oneUp: function(){
this.setZIndex(this.getZIndex()+1)
},
oneDown: function(){
this.setZIndex(this.getZIndex()-1)
}
});
Then you can call
polygon.oneUp()
polygon.oneDown()
polygon.setZIndex(2)
polygon.getZIndex()
And now layergroup.setZIndex(2) are working

How do I set a version 7.0 bing map center to a location

I am using version 7.0 of the Bing Maps API. After creating the map, an array of pins are pushed into the EntityCollection object of the map class. Next, I want to center the map so that all of these pins are viewed on the map. The map's zoom is large enough to accommodate this. In the previous version, map.setMapView() was used, but BING Maps 7.0 has erased this function.
Some code for relevance:
map = new Microsoft.Maps.Map(document.getElementById("myMap"), mapOptions);
map.getCredentials(function(credentials) {
var searchRequest = 'https://dev.virtualearth.net/REST/v1/Locations/' + address + '?output=json&jsonp=getLatLong&key=' + credentials;
var mapscript = document.createElement('script');
mapscript.type = 'text/javascript';
mapscript.src = searchRequest;
document.getElementById('myMap').appendChild(mapscript);
});
function getLatLong(json){
findPlaceResults = new Microsoft.Maps.Location(json.resourceSets[0].resources[0].point.coordinates[0], json.resourceSets[0].resources[0].point.coordinates[1]);
myShape = new Microsoft.Maps.Pushpin(findPlaceResults);
//...
var pins = new Array();
for (var i = 0; i < AllLocations.length; i++) {
var shape = new Microsoft.Maps.Location(AllLocations[i].Latitude, AllLocations[i].Longitude);
var pins = new Microsoft.Maps.Pushpin(shape);
map.entities.push(pins);
}
map.entities.push(myShape);
if (map.entities.getLength() > 0) {
//map.SetMapView(pins);
}
Code TLDR: Stuff happens, try to SetMapView, doesn't work.
Any thoughts would be helpful!
When you creating your pins you need to create helper array will that lead to your goal.
Create array that contains all the locations converted to Microsoft location objects.
// The array
var arrLocations= [];
for (var i = 0; i < AllLocations.length; i++) {
var shape = new Microsoft.Maps.Location(AllLocations[i].Latitude,AllLocations[i].Longitude);
// You add those two lines.
var yourLocation= new Microsoft.Maps.Location(AllLocations[i].Latitude,AllLocations[i].Longitude);
arrLocations.push(yourLocation);
var pins = new Microsoft.Maps.Pushpin(shape);
map.entities.push(pins);
}
Now you use bing maps feature that gives you best zoom and pointing according to given locations.(The LocationRect)
var bestView = Microsoft.Maps.LocationRect.fromLocations(arrLocations);
Then you set the map view according to the best view that we found.
setTimeout((function () {
map.setView({ bounds: bestView });
}).bind(this), 1000);
Well, I found the answer at this website http://www.i-programmer.info/projects/131-mapping-a-gis/1609-getting-started-with-bing-maps-ajax-control-70.html?start=1
"If you are familiar with earlier versions of the Map object you need to know that the new version has far fewer methods. The idea is that instead of having lots of methods the new control has a few methods that accept complex objects as a parameter that specifies lots of settings.
For example, the original map control's SetCenter method will move the map location to the specified latitude and longitude. The new V7 map control has a setView method which accepts a ViewOptions object which in turn has a center property that can be set to a Location object which specifies the center of the map."