Leaflet createTile coords reference to this._tile - leaflet

In the createTile function, I use coords to store a list of 'AbortControllers'. The key I use for every tile creation :
createTile(coords: Coords, done: DoneCallback) {
const abortController = ...
const key = this._tileCoordsToKey(coords)
this.abortControllers[key] = abortController;
}
In the initialise function I listen for the tileunload function:
TileLayer.WMS.extend({
initialize: function (url, options) {
this.on("tileunload", (event) => {
this._tileCoordsToKey(event.coords);
});
}
});
This coordsKey from the tileunload event does not match the coordsKey I can create in createTile. The internal this._tiles cache uses a different cache key?
Any way to reference the event coords to the creation coords?

Related

Return location from searchManager.geocode callback

I'm trying to make a call to the searchManager.geocode function and return the location object to be stored in an array.
I'm a javascript novice, so I expect this is probably a question more fundamental to javacript than to bing maps, but...
I'm experimenting with the Bing Maps search using this demo as my template:
https://www.bing.com/api/maps/sdk/mapcontrol/isdk/searchbyaddress
Here is the example:
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
/* No need to set credentials if already passed in URL */
center: new Microsoft.Maps.Location(47.624527, -122.355255),
zoom: 8 });
Microsoft.Maps.loadModule('Microsoft.Maps.Search', function () {
var searchManager = new Microsoft.Maps.Search.SearchManager(map);
var requestOptions = {
bounds: map.getBounds(),
where: 'Seattle',
callback: function (answer, userData) {
map.setView({ bounds: answer.results[0].bestView });
map.entities.push(new Microsoft.Maps.Pushpin(answer.results[0].location));
}
};
searchManager.geocode(requestOptions);
});
What I would like to do is not actually do any work in the callback function, but simply return the location (`answer.results[0]') so that I can make a series of calls like this:
locations.push(geocodeAddress("address 1, city, state"));
locations.push(geocodeAddress("address 2, city, state"));
That locations array will be used for map bounds, adding pushpins, etc later.
Since the geocode function is an async one, you can't ask it to immediately return something for later use. However, what you could do is pushing the location to array directly in the callback function:
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
/* No need to set credentials if already passed in URL */
center: new Microsoft.Maps.Location(47.624527, -122.355255),
zoom: 8 });
var locations = [];
Microsoft.Maps.loadModule('Microsoft.Maps.Search', function () {
var searchManager = new Microsoft.Maps.Search.SearchManager(map);
var requestOptions = {
bounds: map.getBounds(),
where: 'Seattle',
callback: function (answer, userData) {
locations.push(answer.results[0].location);
}
};
searchManager.geocode(requestOptions);
});
And your later calls would just be below since pushing to array is already taken care of:
geocodeAddress("address 1, city, state");
geocodeAddress("address 2, city, state");
Of course, you would still need some mechanism to keep track that all geocode calls are finished before using the locations array (perhaps through a counter in each callback). Also you would only need to load the Microsoft.Maps.Search module once despite repetitive usage.

How to update geojson markers periodically

What I am trying to do is to use Leaflet with OSM map,
and load data from PHP in GeoJSON format + update periodically.
I can manage to display a map, load data, but do not know how to update points instead of still adding a new ones.
function update_position() {
$.getJSON('link_to_php', function(data) {
//get data into object
var geojsonFeature = JSON.parse(data);
// how to remove here old markers???
//add new layer
var myLayer = L.geoJSON().addTo(mymap);
//add markers to layet
myLayer.addData(geojsonFeature);
setTimeout(update_position, 1000);
});
}
update_position();
have tried mymap.removeLayer("myLayer"); but this seems to now work inside of function. Please help
L.geoJSON extends from LayerGroup which provide a function named clearLayers(docs), so you call that to clear markers from the layer.
Also, it is recommended that you put the layer variable outside the function:
var geoJSONLayer = L.geoJSON().addTo(mymap);
function update_position() {
$.getJSON('link_to_php', function(data) {
//get data into object
var geojsonFeature = JSON.parse(data);
geoJSONLayer.clearLayers();
//add markers to layet
geoJSONLayer.addData(geojsonFeature);
setTimeout(update_position, 1000);
});
}
update_position();

geoJSON onEachFeature Mouse Event

I have a problem where i try to use the onEachFeature Methode for a geoJSON Layer. I try to assign a click listener to every Feature.
The problem is that i always get that error when i click at a feature:
Uncaught TypeError: Cannot read property 'detectChanges' of undefined
I can think of that this is because the Layer is assigned before the constructor runs but to do that in the ngOnInit function wont worked either. Would be cool if ther is a good way to do that :)
constructor(private changeDetector: ChangeDetectorRef){}
fitBounds: LatLngBounds;
geoLayer = geoJSON(statesData, {onEachFeature : this.onEachFeature});
onEachFeature(feature , layer) {
layer.on('click', <LeafletMouseEvent> (e) => {
this.fitBounds = [
[0.712, -74.227],
[0.774, -74.125]
];
this.changeDetector.detectChanges();
});
}
layer: Layer[] = [];
fitBounds: LatLngBounds;
onEachFeature(feature , layer : geoJSON) {
layer.on('click', <LeafletMouseEvent> (e) => {
console.log("tets"+e.target.getBounds().toBBoxString());
this.fitBounds = [
[0.712, -74.227],
[0.774, -74.125]
];
this.changeDetector.detectChanges();
});
}
constructor(private changeDetector: ChangeDetectorRef){}
ngOnInit() {
let geoLayer = geoJSON(statesData, {onEachFeature : this.onEachFeature});
this.layer.push(geoLayer);
}
You need to make sure that the right this is accessible in your callback. You do this using function.bind() in Javascript. See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
constructor(private changeDetector: ChangeDetectorRef){}
fitBounds: LatLngBounds;
geoLayer = geoJSON(statesData, {
// Need to bind the proper this context
onEachFeature : this.onEachFeature.bind(this)
});
onEachFeature(feature , layer) {
// 'this' will now refer to your component's context
let that = this;
layer.on('click', <LeafletMouseEvent> (e) => {
that.fitBounds = [
[0.712, -74.227],
[0.774, -74.125]
];
// Aliased 'that' to refer to 'this' so it is in scope
that.changeDetector.detectChanges();
});
}
The let that = this trick is to make sure you don't have the same problem on the click event handler. But, you could also make that handler be a function in your class and use bind to set this.

How to get selected layers in control.layers?

Is there a way to select all selected layers in the control.layers with leaflet api?
I can do it with the help of jquery like this :
$('.leaflet-control-layers-selector:checked')
But maybe there is an api?
Thanks
There is no API for that but you could easily create one yourself:
// Add method to layer control class
L.Control.Layers.include({
getActiveOverlays: function () {
// Create array for holding active layers
var active = [];
// Iterate all layers in control
this._layers.forEach(function (obj) {
// Check if it's an overlay and added to the map
if (obj.overlay && this._map.hasLayer(obj.layer)) {
// Push layer to active array
active.push(obj.layer);
}
});
// Return array
return active;
}
});
var control = new L.Control.Layers(...),
active = control.getActiveOverlays();
Based on iH8's answer
L.Control.Layers.include({
getOverlays: function() {
// create hash to hold all layers
var control, layers;
layers = {};
control = this;
// loop thru all layers in control
control._layers.forEach(function(obj) {
var layerName;
// check if layer is an overlay
if (obj.overlay) {
// get name of overlay
layerName = obj.name;
// store whether it's present on the map or not
return layers[layerName] = control._map.hasLayer(obj.layer);
}
});
return layers;
}
});
Now you can use
var control = new L.Control.Layers(...)
control.getOverlays(); // { Truck 1: true, Truck 2: false, Truck 3: false }
I find this a little more useful because
all the layers are included
the key is the name of the layer
if the layer is showing, it has a value of of true, else false

Getting the bounds of loaded tile of leaflet

Using leaflet is there any way I can get the bounds (NorthEast, SouthWest) of a loaded tile? I want to request the server to load the markers only for a particular tile which is loaded, so that when user is panning/dragging the map he can easily see the new markers on new area.
What you really want to do is a subclass of L.GridLayer. This will allow fine control over loaded/unloaded tiles, and is the best way to use the private L.GridLayer._tileCoordsToBounds() method.
With some basic handling of loaded markers, it should look like:
L.GridLayer.MarkerLoader = L.GridLayer.extend({
onAdd: function(map){
// Add a LayerGroup to the map, to hold the markers
this._markerGroup = L.layerGroup().addTo(map);
L.GridLayer.prototype.onAdd.call(this, map);
// Create a tilekey index of markers
this._markerIndex = {};
},
onRemove: function(map){
this._markergroup.removeFrom(map);
L.GridLayer.prototype.onRemove.call(this, map);
};
createTile: function(coords, done) {
var tileBounds = this._tileCoordsToBounds(coords);
var tileKey = this._tileCoordsToKey(coords);
var url = ...; // Compute right url using tileBounds & coords.z
fetch(url).then(function(res){
if (!key in this._markerIndex) { this._markerIndex[key] = []; }
// unpack marker data from result, instantiate markers
// Loop as appropiate
this._markerGroup.addLayer(marker);
this._markerIndex[key] = marker;
done(); // Signal that the tile has been loaded successfully
});
},
_removeTile: function (key) {
for (var i in this._markerIndex[key]) {
this._markerGroup.remove(this._markerIndex[key][i]);
}
L.GridLayer.prototype._removeTile.call(this, key);
}
});
Please note that zooming might be a source of bugs and graphical glitches (markers being removed because a tile unloads, before the markers at the new zoom level are loaded). Beware of that.