I have a javascript code like below to calculate the total distance between n markers.
var distance = 0;
function updateTimeAndDistance(timeAndPath) {
realtracPath = timeAndPath.path;
getDistance();
console.log("calculated distance : " + distance);
}
function getDistance() {
for ( var i = 0; i < realtracPath.length - 1 ; i++) {
var startPos = new google.maps.LatLng(realtracPath[i].lat, realtracPath[i].lng);
var endPos = new google.maps.LatLng(realtracPath[i+1].lat, realtracPath[i+1].lng);
var request = {
origin : startPos,
destination : endPos,
travelMode : google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
distance += response.routes[0].legs[0].distance.value;
}
});
}
}
but I am worried that, will there be any inconsistency in the value of the distance calculated as the distance is calculated asynchronously.
EDIT: every time I run this, I am getting distance as zero. I am not understanding why, though I have used the global distance variable.
Thanks.
Try introducing an async callback function like so;
var distance = 0;
function updateTimeAndDistance(timeAndPath) {
realtracPath = timeAndPath.path;
getDistance(function(){
console.log("calculated distance : " + distance);
});
}
function getDistance(cb) {
var latch = realtrackPath.length;
for ( var i = 0; i < realtracPath.length - 1 ; i++) {
var startPos = new google.maps.LatLng(realtracPath[i].lat, realtracPath[i].lng);
var endPos = new google.maps.LatLng(realtracPath[i+1].lat, realtracPath[i+1].lng);
var request = {
origin : startPos,
destination : endPos,
travelMode : google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
distance += response.routes[0].legs[0].distance.value;
latch--
if (latch==0){
cb()
}
}
});
}
}
Note: If the status does not come back as OK nothing will happen. It can be quite handy to have callbacks with function signatures that pass booleans indicating errors etc.
Related
I have an asset i.e. a truck generating reads (lat, lng) at a specific interval and I am plotting those reads on maps using direction service api
But direction service is returning path which is not at all correct.
Highlighted area is a car parking and a truck can't go inside that.
Code provided below:
displayRoute(){
var path = new this.google.maps.MVCArray();
var service = new this.google.maps.DirectionsService();
var markers = this.state.markers;
var wayPoints = [];
if (markers.length >= 2) {
this.isContinueRouting = true;
for (var k = 0, parts = [], max = this.maxWayPointLimit - 1; k < markers.length; k = k + max){
parts.push(markers.slice(k, k + max + 1));
}
var TempParts = [].slice.call(parts);
var delayFactor = 0;
var continueRouting = () => {
if(this.isContinueRouting) {
if (!TempParts || TempParts.length === 0) {
this.refreshMap();
return;
}
var firstPart = TempParts[0];
TempParts = TempParts.slice(1);
wayPoints = [];
for (var j = 1; j < firstPart.length - 1; j++) {
wayPoints.push({location: firstPart[j].latLng, stopover: false});
}
var getDirectionRoute = (objRequest) => {
service.route(objRequest, (result, status) => {
if (status === this.google.maps.DirectionsStatus.OK) {
var renderer = new this.google.maps.DirectionsRenderer({
suppressMarkers: true,
polylineOptions:{strokeColor: '#4986E7', icons:[{icon:{path:this.google.maps.SymbolPath.FORWARD_CLOSED_ARROW}}]}
});
var gRenderers = this.state.gRenderers;
if(!gRenderers){
gRenderers = [];
}
gRenderers.push(renderer);
this.setState({
gRenderers: gRenderers
});
renderer.setMap(this.map);
renderer.setDirections(result);
continueRouting();
}
else if (status === this.google.maps.DirectionsStatus.OVER_QUERY_LIMIT) {
delayFactor++;
setTimeout(() => {
getDirectionRoute(objRequest);
}, delayFactor * 1000);
}
else {
console.log("Route: " + status);
}
});
};
getDirectionRoute({
origin: firstPart[0].latLng,
destination: firstPart[firstPart.length - 1].latLng,
waypoints: wayPoints,
optimizeWaypoints: false,
travelMode: this.google.maps.DirectionsTravelMode.DRIVING
});
}
}
continueRouting();
}
else{
this.refreshMap();
}
}
I am providing lat lng details below as well, separated by |
'31.7041718,-106.2919151|31.7041718,-106.2919151|31.7032561,-106.2934446|31.7076082,-106.2920445|31.6872383,-106.2983887|31.6135476,-106.2551746|31.6160641,-106.2541132|31.6185915,-106.2564508|31.6366903,-106.2750925|31.7042845,-106.2951952|31.7043367,-106.2925862|31.7025645,-106.2905912|31.7815772,-106.4109026|31.792458,-106.5084194|31.8825601,-106.5835285|31.8610442,-106.6844508|31.8685025,-106.7015895|31.8725584,-106.6778204|31.8025682,-106.5208837|31.7450037,-106.3330166|31.7037371,-106.2927402|31.7020747,-106.2912236|31.6605857,-106.2750073|31.6211992,-106.2589605|31.6146543,-106.2533209|31.7194213,-106.3032185|31.7039523,-106.2928624|31.702484,-106.2902215|31.7043125,-106.2952218|31.777505,-106.4241939|31.8764563,-106.5801106|31.7928081,-106.6849148|31.7890649,-106.6837816|31.8092266,-106.6847083|31.8576709,-106.5757679|31.7814772,-106.4071658|31.7040939,-106.2929278|31.7036424,-106.292041|31.7036424,-106.292041|31.7021739,-106.2920716|31.7021681,-106.292032|31.7021681,-106.292032|31.7021681,-106.292032|31.7021681,-106.292032|31.7021681,-106.292032|31.7021681,-106.292032|31.7021681,-106.292032|31.7021681,-106.292032'
I even tried implementing snap to road but that causes other issues as all reads are not near to each other.
Not sure what is the issue here.
I don't think you can use the Directions API to visualise historical routes. Given the start end end coordinates and the waypoints between, the API tries to PLAN and suggest a route for you to take LATER.
I think the only solution would be to fix the problems the Snap to Roads API and use that. If the results are too erratic, then maybe you should set the GPS transponders to report their coordinates more often, if possible.
Based on the user location and a store location I try to figure out what the distance is between this two. This is working, but what I want is an array with all the values and sort on the distance between the two points.
I have my add_stores_to_array function, wich add all stores to the array stores when it is looping through the JSON file.
add_stores_to_array = function(position) {
var user_latitude = position.coords.latitude;
var user_longitude = position.coords.longitude;
$.getJSON('/stores').done(function(data) {
$.each(data.features, function(i, item) {
var store_latitude = item.geometry.coordinates[1];
var store_longitude = item.geometry.coordinates[0];
var user = new google.maps.LatLng(user_latitude, user_longitude);
var store = new google.maps.LatLng(store_latitude, store_longitude);
var directionsService = new google.maps.DirectionsService();
var request = {
origin:user,
destination:store,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
// add distance and store id to the array stores
stores.push({distance: response, id: item.properties.Nid});
}
});
});
// call the sort function
sort_stores(stores);
console.log(stores);
});
};
After the $.each I call the sort function. But after logging it to the console, it is still not sorted.
My sort_stores function:
sort_stores = function(stores){
stores.sort(function(a, b){
return a.distance - b.distance;
});
};
First I thought it wasn't working because the $.each was still running, but after adding this code, it still doesn't working:
if (i == Object.keys(data.features).pop()) {
sort_stores(stores);
}
So, I tried something different. I call the sort_stores(stores) function in the $.each.
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
stores.push({distance: response, id: item.properties.Nid});
sort_stores(stores);
}
});
And it works.. the array is sorted based on the value distance in the array. But now he sorts the array after each added store.. not really effective.
Is there a proper way to call the sort_stores(stores) function one time, and sort it when all stores are added to the array?
EDIT:
If I place an alert() before the sort_stores(stores) it is working..
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
stores.push({distance: response, id: item.properties.Nid});
}
});
});
alert('Call the sort_stores(stores) function after the $.each, with an alert.. it is working?');
sort_stores(stores);
});
};
Edit 2:
Normally I call the function add_stores_to_array from here?
get_user_location = function(){
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(add_stores_to_array);
}
};
There's nothing wrong with your sort function. The problem is that directionsService.route is asynchronous call and the rest of the code will run even when all the calls are not completed yet.
You can use jQuery.when(). Here is the new add_stores_to_array() function
add_stores_to_array = function(position) {
var promises = []; //ADDED promise array
var user_latitude = position.coords.latitude;
var user_longitude = position.coords.longitude;
$.getJSON('/stores').done(function(data) {
$.each(data.features, function(i, item) {
var store_latitude = item.geometry.coordinates[1];
var store_longitude = item.geometry.coordinates[0];
var user = new google.maps.LatLng(user_latitude, user_longitude);
var store = new google.maps.LatLng(store_latitude, store_longitude);
var directionsService = new google.maps.DirectionsService();
var request = {
origin:user,
destination:store,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
var dfd = directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
// add distance and store id to the array stores
stores.push({distance: response, id: item.properties.Nid});
}
});
promises.push(dfd); //ADDED store each object in array
});
//Now you can do the following without having any async issue.
$.when.apply(null, promises).done(function() {
/* sort & do stuff here */
sort_stores(stores);
console.log(stores);
});
});
};
EDIT
Here is another approach. Since you need to wait until all responses are returned, you can customize your sort function to check for the response count. If it's equal to total (which means all calls have finished successfully) then sort the array.
sort_stores = function(stores, responseCount, totalCount ) {
if (responseCount == totalCount) {
stores.sort(function(a, b){
return a.distance - b.distance;
});
}
};
Then change the add_stores_to_array function as follows.
add_stores_to_array = function(position) {
var user_latitude = position.coords.latitude;
var user_longitude = position.coords.longitude;
$.getJSON('/stores').done(function(data) {
var totalCount = data.features.length; //ADDED Get total count
var responseCount = 0; //ADDED
$.each(data.features, function(i, item) {
var store_latitude = item.geometry.coordinates[1];
var store_longitude = item.geometry.coordinates[0];
var user = new google.maps.LatLng(user_latitude, user_longitude);
var store = new google.maps.LatLng(store_latitude, store_longitude);
var directionsService = new google.maps.DirectionsService();
var request = {
origin:user,
destination:store,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
// add distance and store id to the array stores
stores.push({distance: response, id: item.properties.Nid});
responseCount++; //ADDED
sort_stores(stores, responseCount, totalCount); //ADDED Call sort function here
}
});
});
});
};
i can use waypoints perfectyl.but i can not get shortest directions.i try to use route alternatives but it doesn't work. i need some thing like this : http://i58.tinypic.com/2vjbt6d.jpg
is there any way ?
my codes
function codeAddress(adres) {
var address = adres;
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
}
});
}
function calcRoute() {
var grid = document.getElementById('GridView1');
var start = document.getElementById('DropDownList_ilce').value + "," + document.getElementById('DropDownList_il').value;
var end = document.getElementById('GridView1').rows[grid.rows.length - 1].cells[4].innerHTML + "," + document.getElementById('GridView1').rows[grid.rows.length - 1].cells[3].innerHTML;
var waypts = [];
for (var i = 0; i < grid.rows.length - 2; i++) {
waypts.push({
location: document.getElementById('GridView1').rows[i+1].cells[4].innerHTML + "," + document.getElementById('GridView1').rows[i+1].cells[3].innerHTML,
stopover: true
});
}
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
As I said in my comment, the API seems to give no option parameter to return the shortest route by default.
The key here is to use provideRouteAlternatives: true as DirectionsRequest property:
var request = {
origin: 'cerkeş,çankırı',
destination: 'diyarbakır',
travelMode: google.maps.DirectionsTravelMode.DRIVING,
provideRouteAlternatives: true
};
For the given origin and destination, this will return 2 separate routes. One of 970km and one of 1137 km.
Then you will have to calculate which route is the shortest. You could do something like that:
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var distance = null;
var routeIndex = 0;
// Loop through the routes to find the shortest one
for (var i=0; i<response['routes'].length; i++) {
var routeDistance = response['routes'][i].legs[0].distance.value;
if (distance === null) {
distance = routeDistance;
routeIndex = i;
}
if (routeDistance < distance) {
distance = routeDistance;
routeIndex = i;
}
}
directionsDisplay.setDirections(response);
// Set route index
directionsDisplay.setOptions({
routeIndex: routeIndex
});
}
});
Note that when you have multiple routes, the key is to set the route index of the one you want to show.
// Set route index
directionsDisplay.setOptions({
routeIndex: routeIndex
});
This example doesn't use waypoints. I believe that if you use waypoints, you will end up with multiple DirectionsLeg legs. In which case, you will have to do a bit more calculation to add each leg distance to find the total route distance.
Hope this helps!
I have various origins/destinations, and am looping through all of them to calculate each distance. Pushing each to an array of the following object
routes {
distance: xx,
response:response
}
After all the routes have been added I calculate which has the smallest distance, and map that response. However I am outside the directionService now.. is there a way to map stored responses without calling directionService?
Thanks!!
var routes = [];
function route() {
var request = {
origin:start,
destination:end,
travelMode: google.maps.TravelMode.DRIVING
};
calcRoute(request);
//Here I'd like to display, say routes[0].response.
}
function calcRoute() {
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var route = response.routes[0];
// For each route, display summary information.
var distance = 0;
for (var i = 0; i < route.legs.length; i++) {
distance = distance + route.legs[i].distance.text;
};
//push to routes
routes.push({response:response,distance:distance});
}
});
}
I'm trying to make use of HTML5 geolocation for a project I'm working on.
It seems pretty straighforward to get the Lat and Long of where a user is, via geolocation.
Problem is, I need to convert this to a UK postcode, and am strugging as I'm trying to learn javascript.
The code I have working is:
if (navigator.geolocation) {
var timeoutVal = 10 * 1000 * 1000;
navigator.geolocation.getCurrentPosition(
displayPosition,
displayError,
{ enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 }
);
}
else {
alert("Geolocation is not supported by this browser");
}
function displayPosition(position) {
alert("Latitude: " + position.coords.latitude + ", Longitude: " + position.coords.longitude);
var Lat = position.coords.latitude;
var Long = position.coords.longitude;
var inputField = document.getElementById("addressInput");
inputField.value = Lat + Long;
}
function displayError(error) {
var errors = {
1: 'Permission denied',
2: 'Position unavailable',
3: 'Request timeout'
};
alert("Error: " + errors[error.code]);
}
I've found this site, whihc does exactly the kind of thing I want to achieve:
http://www.latlong.net/Show-Latitude-Longitude.html
Can anyone give me some tips on how to get this working?
Any advice would be great
thanks in advance
=========================
Amended code:
//var long = '50.**************', lat = '0.**************'
var Lat='';
var Long='';
var coordsObj = {coords:{latitude:Lat, longitude:Long}};
if (navigator.geolocation) {
var timeoutVal = 10 * 1000 * 1000;
navigator.geolocation.getCurrentPosition(
displayPosition,
displayError,
{ enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 }
);
}
else {
alert("Geolocation is not supported by this browser");
}
function displayPosition(position) {
console.log(position, position.coords)
console.log("Latitude: " + position.coords.latitude + ", Longitude: " + position.coords.longitude);
var Lat = position.coords.latitude;
alert(Lat);
var Long = position.coords.longitude;
alert(Long);
var inputField = document.getElementById("addressInput");
inputField.value = Lat + Long;
return [Lat, Long];
}
function displayError(error) {
var errors = {
1: 'Permission denied',
2: 'Position unavailable',
3: 'Request timeout'
};
alert("Error: " + errors[error.code]);
}
function reverseGeoLookup(lon, lat) {
var req = new XMLHttpRequest()
req.open("GET", "http://maps.googleapis.com/maps/api/geocode/json?latlng="+lat+","+lon+"&sensor=true", true)
req.onreadystatechange = function() {
if(req.readyState == 4) {
var result = JSON.parse(req.response).results
for(var i = 0, length = result.length; i < length; i++) {
for(var j = 0; j < result[i].address_components.length; j++) {
var component = result[i].address_components[j]
if(~component.types.indexOf("postal_code")) {
var out = document.getElementById('output')
out.innerHTML += component.long_name
}
}
}
}
}
req.send()
}
var latlng = displayPosition(coordsObj)
reverseGeoLookup.apply(this, latlng)
There's now a free UK government alternative to the other options listed here. Go to http://postcodes.io/ to see the details of the API and examples. It also supports reverse lookups which is what you're after
You could use the Google Maps reverse geocoding API. This allows you to map a lat, long pair to a set of addresses. For example:
function reverseGeoLookup(lon, lat) {
//make a ajax request -- in prod just use whatever libraryyou have to provide this
//probably jquery's $.get
var req = new XMLHttpRequest()
//put the longitude and latitude into the API query
req.open("GET", "http://maps.googleapis.com/maps/api/geocode/json?latlng="+lat+","+lon+"&sensor=true", true)
//this is just the result callback -- it's the function arg to $.get, essentially
req.onreadystatechange = function() {
if(req.readyState == 4) {
//again jquery will parse for you, but we want the results field
var result = JSON.parse(req.response).results
//the maps API returns a list of increasingly general results
//i.e. street, suburb, town, city, region, country
for(var i = 0, length = result.length; i < length; i++) {
//each result has an address with multiple parts (it's all in the reference)
for(var j = 0; j < result[i].address_components.length; j++) {
var component = result[i].address_components[j]
//if the address component has postal code then write it out
if(~component.types.indexOf("postal_code")) {
var out = document.getElementById('output')
out.innerHTML += component.long_name
}
}
}
}
}
//dispatch the XHR... just use jquery
req.send()
}
I put this example into a js fiddle too, here.
Hope this helps.
I've made some changes to kieran's fiddle to more to help fully answer the question of getting a UK postcode from an html5 geolocation.
var x=document.getElementById("output");
getLocation();
function getLocation()
{
if (navigator.geolocation)
{
navigator.geolocation.watchPosition(reverseGeoLookup);
}
else
{
x.innerHTML="Geolocation is not supported by this browser.";
}
}
function reverseGeoLookup(position) {
console.log(position);
var lat = position.coords.latitude;
var lon = position.coords.longitude;
var req = new XMLHttpRequest()
req.open("GET", "http://maps.googleapis.com/maps/api/geocode/json?latlng="+lat+","+lon+"&sensor=true", true)
req.onreadystatechange = function() {
if(req.readyState == 4) {
var result = JSON.parse(req.response).results
for(var i = 0, length = result.length; i < length; i++) {
for(var j = 0; j < result[i].address_components.length; j++) {
var component = result[i].address_components[j]
//console.log(component.long_name);
if(~component.types.indexOf("postal_code")) {
var out = document.getElementById('output');
out.innerHTML = 'Approximate Post Code for your location is ' + component.long_name;
return false;
}
}
}
}
}
req.send()
}
http://jsfiddle.net/ef72Q/28/