watchPosition() vs getCurrentPosition() with setTimeout - javascript

I need to determine a person's location within 50m. I'm wondering if I should use navigator.location.watchPostion() or call getCurrentPostion() over and over again. watchPostion() is the proper W3C API for doing what I want, but practically, it seems to be overkill.
Here's my code:
var map = null;
var marker = null;
var layer = null;
function locFn(pos) {
var lat = pos.coords.latitude;
var lon = pos.coords.longitude;
$("#hlat").val(lat);
$("#hlong").val(lon);
document.getElementById("lnkMap").href =
"http://maps.google.com/maps?q=My+Loc#" + lat
+ "," + lon + "&z=18&t=h";
var point = new GLatLng(lat, lon);
if (pos.coords.accuracy < 100) {
map.setCenter(point, 18);
if (marker != null) {
marker.setLatLng(point);
}
else {
var ico = new GIcon();
ico.image = "img/Blue-Dot.png";
ico.iconSize = new GSize(22, 22);
ico.iconAnchor = new GPoint(11, 11);
marker = new GMarker(point, { icon: ico });
layer = map.addOverlay(marker, { title: "You are here." });
}
}
else if (pos.coords.accuracy > 2000) {
if (marker != null) { marker.setLatLng(point); }
map.setCenter(point, 15);
}
else if (pos.coords.accuracy > 900) {
if (marker != null) { marker.setLatLng(point); }
map.setCenter(point, 16);
}
else if (pos.coords.accuracy > 100) {
if (marker != null) { marker.setLatLng(point); }
map.setCenter(point, 17);
}
}
function locFail() {
//alert("Failed to retrieve location.");
}
var watchID = null;
function processPoints() {
map = new GMap2(document.getElementById("map_canvas"),
{ mapTypes: [G_HYBRID_MAP] });
try {
watchID = navigator.geolocation.watchPosition(locFn, locFail,
{ enableHighAccuracy: true });
}
catch(err) { /* desktop?*/ }
}
$(function(){processPoints();});
I've noticed watchPostion() seems to ultimately result in more accuracy (after a while), but I'm wondering if the position changes so fast that it results in a lot of thing being downloaded to my map canvas, with constant http requests that are soon out-of-date, replaced by the new ones coming in. When I use watchPosition(), it does take a while before the page loads.

After some serious testing, I have verified watchPosition() will give you an accurate location much more quickly than getCurrentPostion() over and over again. When using watchPostion(), the map behaves poorly if you redraw it over and over again every time the device updates your location. To get around this, I have added a listener to the tilesloaded event, which allows me to only redraw the map if there is not already a thread trying to draw on the map. Once the user is happy with the determined location, I will clear the watch. This will get me the best of both worlds, as far as battery consumption and accuracy are concerned.

Everytime you call getCurrentLocation the gps is spun up which takes a few moments.
If you call watchPosition and wait until you get a certain accuracy, then remove your watch you will have better results, without the overhead of a constant watch.

Related

Leaflet/JS: Adding polylines after pan/zoom

I am using a Leaflet map as an image viewer, and I'm trying to add a 'toggle crosshairs' function that will place a crosshair at the center of the image. I've got it working to a point where the crosshair appears correctly, and is 'coupled' to the image when doing pans/zooms, which is what I would expect to see.
The issue I'm having is that if I do the pan/zoom FIRST, and then toggle the crosshairs, the crosshairs appear in the center of the 'window', not in the center of the image.
The code I'm using for this is here:
toggleCrosshair(toggleVal) {
let map = this.map;
if (toggleVal == true) {
var north = map.getBounds().getNorth(); // these are in lat, lng
var south = map.getBounds().getSouth();
var east = map.getBounds().getEast();
var west = map.getBounds().getWest();
var center = map.getCenter();
var latlngHorizontalLeft = new L.latLng(center.lat, west);
var latlngHorizontalRight = new L.latLng(center.lat, east);
var horizLineList = [latlngHorizontalLeft, latlngHorizontalRight];
let horizLine = new L.Polyline(horizLineList, {
color: 'red',
weight: 2,
opacity: 0.5,
smoothFactor: 1
});
var latlngVerticalTop = new L.latLng(north, center.lng);
var latlngVerticalBottom = new L.latLng(south, center.lng);
var vertLineList = [latlngVerticalTop, latlngVerticalBottom];
let vertLine = new L.Polyline(vertLineList, {
color: '#A5CE3A',
weight: 2,
opacity: 0.5,
smoothFactor: 1
});
horizLine.addTo(map);
vertLine.addTo(map);
} else {
var i;
for (i in map._layers) {
if (map._layers[i].options.color == '#A5CE3A') {
try {
map.removeLayer(map._layers[i]);
} catch (e) {
console.log("problem with " + e + map._layers[i]);
}
} else if (map._layers[i].options.color == 'red') {
try {
map.removeLayer(map._layers[i]);
} catch (e) {
console.log("problem with " + e + map._layers[i]);
}
}
}
}
}
Does anyone have any hints on how to get the behavior I'm hoping for? Thanks!
map.getBounds() only gives you the geographical bounds visible in the current map view.
So in short, whatever you see on the screen.
Set it to the actual center lat lng coordinates.
https://leafletjs.com/reference-1.6.0.html#map-getbounds
Thanks for the responses. I ended up saving the original location/zoom value, moving the image to a known location/zoom value (using the SetView method), then adding the cross hairs, and then moving the image back to the original location/zoom values. I did have to set the animation to false in the SetView calls...the timing of the animation threw things off.
Probably not the most elegant solution, but its something I remember having to do something similar when using Canvas elements.

Show a moving marker on the map

I am trying to make a marker move(not disappear and appear again) on the map as a vehicle moves on the road.
I have two values of latLng and I want to move the marker between the two till the next point is sent by the vehicle. And then repeat the process again.
What I tried:[This is not a very efficient way, I know]
My thought was to implement the above using the technique in points below:
1) Draw a line between the two.
2) Get the latLng of each point on 1/10th fraction of the polyline.
3) Mark the 10 points on the map along with the polyline.
Here is my Code:
var isOpen = false;
var deviceID;
var accountID;
var displayNameOfVehicle;
var maps = {};
var lt_markers = {};
var lt_polyLine = {};
function drawMap(jsonData, mapObj, device, deleteMarker) {
var oldposition = null;
var oldimage = null;
var arrayOflatLng = [];
var lat = jsonData[0].latitude;
var lng = jsonData[0].longitude;
//alert(jsonData[0].imagePath);
var myLatLng = new google.maps.LatLng(lat, lng);
if (deleteMarker == true) {
if (lt_markers["marker" + device] != null) {
oldimage = lt_markers["marker" + device].getIcon().url;
oldposition = lt_markers["marker" + device].getPosition();
lt_markers["marker" + device].setMap(null);
lt_markers["marker" + device] = null;
}
else {
console.log('marker is null');
oldimage = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
oldposition = myLatLng;
}
}
var image = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
lt_markers["marker" + device] = new google.maps.Marker({
position: myLatLng,
icon: image,
title: jsonData[0].address
});
if (oldposition == myLatLng) {
alert('it is same');
lt_markers["marker" + device].setMap(mapObj);
mapObj.panTo(myLatLng);
}
else {
alert('it is not same');
var markMarker = null;
var i = 10;
for (i = 10; i <= 100; i + 10) {
//-------
// setTimeout(function() {
if (markMarker != null) {
markMarker.setMap(null);
markMarker = null;
}
alert('inside the loop');
var intermediatelatlng = mercatorInterpolate(mapObj, oldposition, myLatLng, i / 100);
alert('Intermediate Latlng is :' + intermediatelatlng);
arrayOflatLng.push(intermediatelatlng);
var flightPath = new google.maps.Polyline({
path: arrayOflatLng,
strokeColor: "#FFFFFF",
strokeOpacity: 1.0,
strokeWeight: 1
});
flightPath.setMap(mapObj);
if (i != 100) {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: image,
title: jsonData[0].address,
map: mapObj
});
}
else {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: oldimage,
title: jsonData[0].address,
map: mapObj
});
}
mapObj.panTo(intermediatelatlng);
//--------
// }, 1000);
}
}
}
function mercatorInterpolate(map, latLngFrom, latLngTo, fraction) {
// Get projected points
var projection = map.getProjection();
var pointFrom = projection.fromLatLngToPoint(latLngFrom);
var pointTo = projection.fromLatLngToPoint(latLngTo);
// Adjust for lines that cross the 180 meridian
if (Math.abs(pointTo.x - pointFrom.x) > 128) {
if (pointTo.x > pointFrom.x)
pointTo.x -= 256;
else
pointTo.x += 256;
}
// Calculate point between
var x = pointFrom.x + (pointTo.x - pointFrom.x) * fraction;
var y = pointFrom.y + (pointTo.y - pointFrom.y) * fraction;
var pointBetween = new google.maps.Point(x, y);
// Project back to lat/lng
var latLngBetween = projection.fromPointToLatLng(pointBetween);
return latLngBetween;
}
Problems Faced:
1) The marker is not showing up on the map because the process of plotting and removal of marker is so fast that the marker is not visisble on screen. I've tried setTimeOut, and It does not help at all.
2) if I alow the browser to run this code for more than 5 minutes, the browser crashes.
Note: The Above function is called every 10 seconds using setInterval.
What Can be a better solution? Please Help..
For the marker to move relatively smoothly, you need to
Update more than every 1/10 fraction of the polyline (at least every few pixels)
Call the update method more frequently
Don't delete and re-add the marker
For example, something like:
var counter = 0;
interval = window.setInterval(function() {
counter++;
// just pretend you were doing a real calculation of
// new position along the complex path
var pos = new google.maps.LatLng(35, -110 + counter / 100);
marker.setPosition(pos);
if (counter >= 1000) {
window.clearInterval(interval);
}
}, 10);
I made a simple example at http://jsfiddle.net/bmSbU/2/ which shows a marker moving along a straight path. If this is what you want, most of your code above regarding where along the line you are can be reused (or check out http://broady.github.io/maps-examples/points-along-line/along-directions.html )
You can use marker-animate-unobtrusive library to make markers
smoothly transition from one location to another (instead of reappearing).
You could initialize your marker like that:
var marker = new SlidingMarker({
//your original marker options
});
Just call marker.setPosition() each time new vehicle's coordinate arrive.
P.S. I'm the author of the library.
Why not keep the existing Marker/ MarkerImage and call setPosition() to move it, either on a timer or as the position changes?
Deleting it & recreating it is what causes it to flash/ flicker and eventually crash. If you keep the same instance but just move it, you should do much better.
See: Marker.setPosition()
https://developers.google.com/maps/documentation/javascript/reference#Marker

Extend Google Maps marker to animate smoothly on update?

Using the Google Maps API v3 I've been able to update multiple positions of markers via an AJAX call. However, it lacks any transition. Code below:
if ( !latlong.equals( point.latlong ) ) {
point.latlong = latlong;
point.marker.setPosition(latlong);
}
The drawback is that setPosition has no native animation method. Does anyone know any methods for extending setPosition so the marker can fluently "move" from it's old to new position? Or any methods available? I have not been able to find any documentation. Thanks!
I did not find any native way to create this animation. You can create your own animation by stepping the position from the current point to the final point using the setPosition. Here is a code snippet to give you an idea:
var map = undefined;
var marker = undefined;
var position = [43, -89];
function initialize() {
var latlng = new google.maps.LatLng(position[0], position[1]);
var myOptions = {
zoom: 8,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
marker = new google.maps.Marker({
position: latlng,
map: map,
title: "Your current location!"
});
google.maps.event.addListener(map, 'click', function(me) {
var result = [me.latLng.lat(), me.latLng.lng()];
transition(result);
});
}
var numDeltas = 100;
var delay = 10; //milliseconds
var i = 0;
var deltaLat;
var deltaLng;
function transition(result){
i = 0;
deltaLat = (result[0] - position[0])/numDeltas;
deltaLng = (result[1] - position[1])/numDeltas;
moveMarker();
}
function moveMarker(){
position[0] += deltaLat;
position[1] += deltaLng;
var latlng = new google.maps.LatLng(position[0], position[1]);
marker.setPosition(latlng);
if(i!=numDeltas){
i++;
setTimeout(moveMarker, delay);
}
}
This can probably be cleaned up a bit, but will give you a good start. I am using JavaScript's setTimeout method to create the animation. The initial call to 'transition' gets the animation started. The parameter to 'transition' is a two element array [lat, lng]. The 'transition' function calculates the step sizes for lat and lng based upon a couple of animation parametes (numDeltas, delay). It then calls 'moveMarker'. The function 'moveMarker' keeps a simple counter to indicate when the marker has reached the final destination. If not there, it calls itself again.
Here is a jsFiddle of the code working: https://jsfiddle.net/rcravens/RFHKd/2363/
Hope this helps.
Bob
In case you want smooth animations (with easing), these libraries should help:
https://github.com/terikon/marker-animate-unobtrusive
http://terikon.github.io/marker-animate-unobtrusive/demo/unobtrusive/markermove-sliding.html
I know its late but it might help the future SO wanderers.
Problem Statement: write a function(and not a library due to specific use-case) to animate a google maps marker to a new location.
Solution is based on this awesome library marker-animate-unobtrusive
function animateMarkerTo(marker, newPosition) {
var options = {
duration: 1000,
easing: function (x, t, b, c, d) { // jquery animation: swing (easeOutQuad)
return -c *(t/=d)*(t-2) + b;
}
};
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
// save current position. prefixed to avoid name collisions. separate for lat/lng to avoid calling lat()/lng() in every frame
marker.AT_startPosition_lat = marker.getPosition().lat();
marker.AT_startPosition_lng = marker.getPosition().lng();
var newPosition_lat = newPosition.lat();
var newPosition_lng = newPosition.lng();
// crossing the 180° meridian and going the long way around the earth?
if (Math.abs(newPosition_lng - marker.AT_startPosition_lng) > 180) {
if (newPosition_lng > marker.AT_startPosition_lng) {
newPosition_lng -= 360;
} else {
newPosition_lng += 360;
}
}
var animateStep = function(marker, startTime) {
var ellapsedTime = (new Date()).getTime() - startTime;
var durationRatio = ellapsedTime / options.duration; // 0 - 1
var easingDurationRatio = options.easing(durationRatio, ellapsedTime, 0, 1, options.duration);
if (durationRatio < 1) {
marker.setPosition({
lat: (
marker.AT_startPosition_lat +
(newPosition_lat - marker.AT_startPosition_lat)*easingDurationRatio
),
lng: (
marker.AT_startPosition_lng +
(newPosition_lng - marker.AT_startPosition_lng)*easingDurationRatio
)
});
// use requestAnimationFrame if it exists on this browser. If not, use setTimeout with ~60 fps
if (window.requestAnimationFrame) {
marker.AT_animationHandler = window.requestAnimationFrame(function() {animateStep(marker, startTime)});
} else {
marker.AT_animationHandler = setTimeout(function() {animateStep(marker, startTime)}, 17);
}
} else {
marker.setPosition(newPosition);
}
}
// stop possibly running animation
if (window.cancelAnimationFrame) {
window.cancelAnimationFrame(marker.AT_animationHandler);
} else {
clearTimeout(marker.AT_animationHandler);
}
animateStep(marker, (new Date()).getTime());
}

GLatLngBounds - wrong center and zoom level

I'm trying to use GLatLngBounds to make all the markers on the map visible. Below is a small example of what I'm doing.
INV.createMap = function(containerId) {
var map = null;
var geocoder = null;
var bounds = new GLatLngBounds();
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById(containerId), {
size: new GSize(600, 300)
});
map.setCenter(new GLatLng(54.729378425601766, 25.279541015625), 15);
map.addControl(new GSmallZoomControl());
geocoder = new GClientGeocoder();
}
return {
markAdress: function(address, infoContentHtml) {
if (map !== null && geocoder !== null) {
geocoder.getLatLng(address, function(point) {
if (point) {
var marker = new GMarker(point);
GEvent.addListener(marker, 'mouseover', function() {
if (!map.getInfoWindow().getPoint().equals(this.getLatLng())) {
this.openInfoWindowHtml(infoContentHtml);
}
});
map.addOverlay(marker);
bounds.extend(point);
}
});
}
},
finalize: function() {
map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
}
};
};
Usage:
var m = INV.createMap('whatever');
var addresses = ...
for (var i = 0, l = addresses.length; i < l; i++) {
m.markAdress('address...', 'htmlInfo...');
}
m.finalize();
The problem is that the zoom level is completely wrong (waaay too zoomed out) and the markers appear on the left top corner of the map for some reason (but all of them are visible).
What am I doing wrong?
EDIT: Ignore this question. I've made a stupid mistake - I overlooked the fact that GClientGeocoder makes asynchronous requests so the finalize() method is called too early.
The last argument of the function call below specifies the zoom level:
map.setCenter(new GLatLng(54.729378425601766, 25.279541015625), 15);
This article talks a bit about zooming. So the zooming level is a bit too high.
Further this tutorial tells you how to fit the map zooming to properly display a set of markers.
Hope this helps

How to animate a custom Google Maps marker along a route?

I have written a small application for a handheld device using JavaScript and Google Maps API's, now II need to move my marker icon anywhere on the map along a route using a timer function. I have a man icon and I need to move it automatically on the map. How can I do this?
A pretty cool example is here:
http://www.kmcgraphics.com/google/
Unfortunately, there is no automatic-marker-movement function in the official GMaps collection.
However, if you have a GRoute, that would mean you have a set of points. To loop through the route steps, you could use something like this:
for (var c = 0; c < yourroute.getNumSteps(); c++) {
yourmarker.setLatLng(yourroute.getStep(c).getLatLng());
}
Of course, you'll probably want to do this asynchronously using the timers:
function moveToStep(yourmarker,yourroute,c) {
if {yourroute.getNumSteps() > c) {
yourmarker.setLatLng(yourroute.getStep(c).getLatLng());
window.setTimeout(function(){
moveToStep(yourmarker,yourroute,c+1);
},500);
}
}
moveToStep(marker,route,0);
For even smoother movement, you could interpolate the points from those you already have.
Here is my solution that works with the v3 API. This animates the marker not with a fixed velocity, but based on the calculated route duration. There is a speed factor, so for example you can drive through the route 10x faster than in reality.
I've tried to do it as simple as possible. Feel free to use it.
var autoDriveSteps = new Array();
var speedFactor = 10; // 10x faster animated drive
function setAnimatedRoute(origin, destination, map) {
// init routing services
var directionsService = new google.maps.DirectionsService;
var directionsRenderer = new google.maps.DirectionsRenderer({
map: map
});
//calculate route
directionsService.route({
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
},
function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
// display the route
directionsRenderer.setDirections(response);
// calculate positions for the animation steps
// the result is an array of LatLng, stored in autoDriveSteps
autoDriveSteps = new Array();
var remainingSeconds = 0;
var leg = response.routes[0].legs[0]; // supporting single route, single legs currently
leg.steps.forEach(function(step) {
var stepSeconds = step.duration.value;
var nextStopSeconds = speedFactor - remainingSeconds;
while (nextStopSeconds <= stepSeconds) {
var nextStopLatLng = getPointBetween(step.start_location, step.end_location, nextStopSeconds / stepSeconds);
autoDriveSteps.push(nextStopLatLng);
nextStopSeconds += speedFactor;
}
remainingSeconds = stepSeconds + speedFactor - nextStopSeconds;
});
if (remainingSeconds > 0) {
autoDriveSteps.push(leg.end_location);
}
} else {
window.alert('Directions request failed due to ' + status);
}
});
}
// helper method to calculate a point between A and B at some ratio
function getPointBetween(a, b, ratio) {
return new google.maps.LatLng(a.lat() + (b.lat() - a.lat()) * ratio, a.lng() + (b.lng() - a.lng()) * ratio);
}
// start the route simulation
function startRouteAnimation(marker) {
var autoDriveTimer = setInterval(function () {
// stop the timer if the route is finished
if (autoDriveSteps.length === 0) {
clearInterval(autoDriveTimer);
} else {
// move marker to the next position (always the first in the array)
marker.setPosition(autoDriveSteps[0]);
// remove the processed position
autoDriveSteps.shift();
}
},
1000);
}
Usage:
setAnimatedRoute("source address or LatLng ...", "destination address or LatLng ...", map);
// start simulation on button click...
$("#simulateRouteButton").click(function() {
startRouteAnimation(agentMarker);
});

Categories

Resources