How do I simply get the current geolocation using leaflet without events? - javascript

I've read the leaflet documentation fairly in depth and I can't find a simple answer to my question.
First some context:
I've created a new map and centred it at the current location and added a listener for location found.
var map = L.map('map', {zoomControl: false}).fitWorld();
L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/outdoors-v9/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiZG9ucZhc3o3ODMifQ.3-x6nnTgePEK0ERPllV7oQ', {
maxZoom: 20
}).addTo(map);
map.locate({setView: true, watch: true, maxZoom: 17});
map.on('locationfound', onLocationFound);
I now want to create a marker at the current location e.g.
marker = L.marker(map.getLatLong);
marker.addTo(map);
This is to be done outwith the onLocationFound() function as I want to create it only once and then use the locationfound event to have the marker move using marker.setLatLng().
How am I able to use leaflet to get the current LatLong? Does it have a simple alternative to the HTML5 Geolocation API?
I've tried map.getCenter but it returns (0,0)

This is to be done out with the onLocationFound() function as I want to create it only once
In that case, use map.once() instead of map.on(). As you can read in the documentation, once() is the same as on() but only runs on the first event of that kind.
and then use the locationfound event to have the marker move using marker.setLatLng().
In that case, then check if the marker is created, create if it's not, or move it if it is. Something like...
var marker;
map.on('locationfound', function(ev){
if (!marker) {
marker = L.marker(ev.latlng);
} else {
marker.setLatLng(ev.latlng);
}
})
I'd also appreciate if anyone knows of a tidy way of doing it without events
In that case, do have a look at the HTML5 geolocation API. There are no events, but there are asynchronous callbacks anyway. You can use those callbacks directly, you can wrap them in event handlers (as Leaflet does), you can wrap them in Promises, but one way or the other you'll have to deal with not-totally-trivial asynchronicity issues.

Related

Leaflet featureGroup with tracks fitbounds invalid

Is there a better way???
I am adding several tracks to a map (using leaflet-gpx plugin) and want to fit the maps to the bounds of all the added tracks.
By using a featureGroup layer to contain the tracks it is possible to used the fitBounds() method using the feature group.
However the featureGroup does not have a loaded event to trigger the fitBounds() and the map also doesn't seem to have a suitable trigger.
Calling fitBounds() directly on the map after it is rendered fails with 'invalid bounds' error. However delaying the call for about a second allows it to execute correctly.
I presume what is happening is that it takes time for all of the gpx layers in the group to be loaded and until that is complete the group can't return a valid set of bounds.
So using a setTimeout to delay the fitBounds() works, but causes some annoying flashing as the map re-renders itself. Is there a better way?
This works:
// mapname has been created and tracks have already been loaded into their individual layers
var tracksLayer = L.featureGroup([track1,track2,track3]);
tracksLayer.addTo(mapname);
//this works, with more and complex tracks you might need a longer timeout
setTimeout(function(){ mapname.fitBounds(tracksLayer.getBounds()); }, 1000);
// this however does not work
// mapname.on('load', function() { mapname.fitBounds(tracksLayer.getBounds());});
//nor does this despite the fact that by this stage the map has been created and the featureGroup added
// mapname.fitBounds(tracksLayer.getBounds());

How Can I animate Bing map pushpins based on a live GPS feed

I have looked over the bing maps documentation trying to find an answer to my question. I would prefer to do everything using the bing maps api and not have to add a third party library if possible.
Question: How can I animate a pushpin to make a smooth transition from one set of gps coordinate(longitude/latitude) to another in order to simulate smooth movement of a Pushpin on Bing maps?
Question: can deleting an entire object out of the map.entities array waste enough resources to cause performance issues? If so how can I change the pushpin latitude and longitude properties without deleting the entire object?
Sample code of trying to change the pushpins properties without deleting the object out of the array. This code does not work… I am unsure why it is not working?
map.entities.get(theIndexOfThePushPin)._location.latitude = newLat;
map.entities.get(theIndexOfThePushPin)._location.longitude = newLon;
I create a pushpin like so - This works fine
map.entities.push(new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(lat, lon), {
text: text,
visible: true,
textOffset: new Microsoft.Maps.Point(0, 5)
}));
Pseudo code for my recursive angular $http call
function BusMoveGPSRefresh() {
$http.get(resourceURL)
.then(function (data) {
if ('if pushpins have not been created') {
//create pushpins...
}
} else {
//delete pushpins out of the array and then recreate them
//with updated lon/lat. Or just update existing objects lon/lat properties if possible?
}
}
BusMoveGPSRefresh();//after everything is done then go get more info and start again. recursion...
}, function (reason) {// if fail than do
console.log(reason);
console.log("This Is not Working!!! Dang!!");
});
}
Any insight into the problem would be greatly appreciated! Thanks!
I could not find a simple answer for adding animation. I did find a site that gave a step by step tutorial on a more in-depth answer on how to animate pushpins.
Answer: 1 -- tutorial on how to animate bing map pushpins
https://blogs.bing.com/maps/2014/08/07/bring-your-maps-to-life-creating-animations-with-bing-maps-javascript/
Answer: 2 -- this is how to update the pushpin location without deleting the pushpin object out of the entities array.
Instead of deleting the entire pushpin object find the pushpin index and then use the property "setLocation()" and then add a new location.
//check to make sure that the lat and lon have
//changed if it is still the same location do nothing. else update
if (map.entities.get(indexOfPushpin).getLocation().latitude != lat
|| map.entities.get(indexOfPushpin).getLocation().longitude != lon) {
map.entities.get(indexOfPushpin).setLocation(new Microsoft.Maps.Location(lat, lon));
}

Leaflet - Event on tiles loading

I am currently developing a map-based application and need a way to get notified when Leaflet is pulling tiles from the TileProvider (which, in my case, is MapBox). I read the Leaflet documentation, especially the part with the TileLayer. Currently, I am using the following code to attach a tileload handler:
map.eachLayer(function (layer) {
layer.on('tileload', function(e) {
console.log(e);
});
});
Is there a better way to get the TileLayer of the current map? One problem with this approach is that I hook the handler to all layers (although only TileLayers will raise events, it is unclean to hook it too all layers). Or can I attach the handler directly to the map instance somehow?
Update
I initialize the map with the following MapBox code snippet:
map = L.mapbox.map( element, '...', mapOptions );
This automatically creates a TileLayer (and several other layers), attaches them to the map object and returns this object for later use.
Why not use tileload event directly on the tile layer, like this:
//create a variable to store the tilelayer
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
//add the tileload event directly to that variable
osm.on('tileload', function (e) {
console.log(e);
});
If you've got a lot of L.mapbox.TileLayer instances and you don't want to add the eventhandler manually to each instance like Alexandru Pufan suggests in his answer you could still use a loop and Object's instanceof method:
map.eachLayer(function (layer) {
if (layer instanceof L.mapbox.TileLayer) {
layer.on('tileload', function(e) {
console.log(e);
});
}
});
After reading your comment on Alexandru's answer i'm guessing you only have one layer, then it would be best to add it manually to the instance, which is possible with L.mapbox.TileLayer like this:
var layer = L.mapbox.tileLayer(YOUR MAP ID);
layer.on('tileload', function(e) {
console.log(e);
});
var map = L.mapbox.map('mapbox', null, {
'center': [0, 0],
'zoom': 0,
'layers': [layer]
});

How to get max zoom for maptype in location

On Google Maps' JS API reference page, it says that the method getMaxZoomAtLatLng:
Returns the maximum zoom level available at a particular LatLng for
the Satellite map type.
For some reason, the terrain maptype bottoms out at level 15 in Calgary (for example) but the Satellite maptype can go all the way to zoom 19. I have two map services running simulataneously, and I want the maps to be in sync, but they can't sync up if they can't reach the same zoom level.
So, basically I want to know when the terrain map (or any maptype) can't cope, and trigger some special case functions.
Is there a method, or an alternative, for getting the maximum zoom for a location for a specific map type (since the above mentioned method only works for Satellite)?
Well, one solution I've decided to go with in the meantime is to attach a zoom-end handler to my ArcGIS map (could easily do this with Leaflet as well) and a maptypeid_changed handler to the Google map.
So, for the ArcGIS map (eMap) I did this:
eMap.on('zoom-end', function (e) {
// map zooms are synced
gMap.setCenter(esri.geometry.webMercatorToGeographic(e.extent.getCenter()));
gMap.setZoom(e.level);
// if the google map couldn't do it, reset eMap
if (gMap.getZoom() !== e.level) {
eMap.setZoom(gMap.getZoom());
// runs the handler again
}
});
And if the google map was set to Satellite, then changed, it also needed to trigger the same check, so I forced it to execute another zoomEnd handler:
google.maps.event.addListener(gMap, 'maptypeid_changed', function () {
// for some reason, ESRI has issues setting zoom to current zoom
// so it has to run an initial check before calling zoomEnd
if (gMap.getZoom() !== eMap.getZoom()) {
eMap.setZoom(gMap.getZoom());
}
});
Hope this helps someone, but really, I'm looking forward to seeing a better solution than this. Seems like an awfully complicated approach to merely firing a function if Google's maptype can't reach a certain zoom level.

Openlayers initial extent

According to the OpenLayers documentation, the constructor, OpenLayers.Map(), allows for an additional property extent which is "The initial extent of the map" (see here).
However, I cannot get it to have any effect. I know I can set an initial extent by calling .zoomToExtent() after constructing the map. But I would like to use this extent property because I set a zoomend event in the eventListeners property but don't want it to trigger with an initial call to .zoomToExtent(). Does anyone have a clue how to use this extent property?
This is the code that isn't working
map = new OpenLayers.Map('map',{
extent: bounds,
layers: [osmLayer,vectorLayer],
projection: "EPSG:900913",
eventListeners: {
zoomend: function() {
//..zoomend event listener code
}
}
});
In the above example:
bounds is a valid OpenLayers.Bounds object
osmLayer and vectorLayer are valid OpenLayers.Layer objects of which osmLayer is the base layer.
What happens with above code is that the map is completely zoomed out (in fact you can't actually see anything) and any attempts to pan results in errors being thrown. To get to map into a correct state the user has to zoom in and then panning works again and you can see the map.
I'm afraid I'm not sure off-handedly how to make the code you listed work, however the following alternative has worked for me:
map = new OpenLayers.Map('map',{projection: new OpenLayers.Projection("EPSG:4326")});
//ADD A BUNCH OF LAYERS AND SUCH HERE
//Set map center to this location at this zoom level
map.setCenter(new OpenLayers.LonLat(-93.9196,45.7326),5);
As a last resort, you could put a line at the beginning of your zoom function:
if(!map_ready) return;
And then set the variable map_ready to true at the appropriate time.
I ran into the same problem.
The "extent" option mentioned in the documentation is there probably by mistake. It does not exist. I checked the source code and could not see any code handling such option. I contacted the author and he already created a pull request for the removal of this option from the documentation.

Categories