I'm using Google Distance Matrix API to calculate the driving distance + time from one point to another.
I would like to add if..elseif..else statements to the result of the distance search to vary the answers according to how big the distances (e.g. < or > 10 km) are but I'm a newbie to JS and can't seem to figure out where to stick the statements into my code. Any tips?
Here's my code:
$(function(){
function calculateDistance(origin, destination) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [origin],
destinations: [destination],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, callback);
}
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
$('#result').html(err);
} else {
var origin = response.originAddresses[0];
var destination = response.destinationAddresses[0];
if (response.rows[0].elements[0].status === "ZERO_RESULTS") {
$('#result').html("We can't seem to find "
+ origin + ". Are you sure you entered a valid postcode and place?");
} else {
var distance = response.rows[0].elements[0].distance;
var duration = response.rows[0].elements[0].duration;
var distance_value = distance.value;
var distance_text = distance.text;
var duration_value = duration.value;
var duration_text = duration.text;
var kilometer = distance_text.substring(0, distance_text.length - 3);
$('#result').html("It is " + kilometer + " kilometer from " + origin + " to " + destination + " and it takes " + duration_text + " to drive.");
}
}
}
$('#distance_form').submit(function(e){
event.preventDefault();
var origin = $('#origin').val();
var destination = $('#destination').val();
var distance_text = calculateDistance(origin, destination);
});
});
One option is to have the conditional logic in your callback function like this:
function callback(response, status) {
if (status !== google.maps.DistanceMatrixStatus.OK) {
$('#result').html(err);
return;
}
if (response.rows[0].elements[0].status !== "OK") {
$('#result').html("We can't seem to find " + origin + ". Are you sure you entered a valid postcode and place?");
return;
}
var origin = response.originAddresses[0];
var destination = response.destinationAddresses[0];
var distance = response.rows[0].elements[0].distance;
var duration = response.rows[0].elements[0].duration;
var distance_value = distance.value;
var distance_text = distance.text;
var duration_value = duration.value;
var duration_text = duration.text;
var kilometer = distance_text.substring(0, distance_text.length - 3);
if (distance_value > 10000) {
$('#result').html('Distance is greater than 10km');
} else {
$('#result').html('Distance is less than 10km');
}
}
The response validation is done at the beginning of the function and if the request does not return the desired status, you return early and stop the execution of the function. Once these validation statements are out of the way, you can extract all the necessary data from the response and then perform your conditional statements based on any of the values you extracted.
In my example, this is what that looks like:
if (distance_value > 10000) {
$('#result').html('Distance is greater than 10km');
} else {
$('#result').html('Distance is less than 10km');
}
I check to see if the distance value is greater than 10000m (10km) and display a different result based on that.
Here is a JSBin with a working example.
Related
I'm using this code as a starting point but it only calculates the driving distance between two places and not the driving time.
What would I need to add to the js to get the driving time as well?
I have, btw, already looked at Google Developer's Guide but haven't been able to figure it out.
https://codepen.io/youfoundron/pen/GIlvp
JS:
$(function() {
function calculateDistance(origin, destination) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [origin],
destinations: [destination],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, callback);
}
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
$('#result').html(err);
} else {
var origin = response.originAddresses[0];
var destination = response.destinationAddresses[0];
if (response.rows[0].elements[0].status === "ZERO_RESULTS") {
$('#result').html("Better get on a plane. There are no roads between "
+ origin + " and " + destination);
} else {
var distance = response.rows[0].elements[0].distance;
var distance_value = distance.value;
var distance_text = distance.text;
var kilometer = distance_text.substring(0, distance_text.length - 3);
$('#result').html("It is " + kilometer + " kilometer from " + origin + " to " + destination + " and it takes " + " to drive.");
}
}
}
$('#distance_form').submit(function(e){
event.preventDefault();
var origin = $('#origin').val();
var destination = $('#destination').val();
var distance_text = calculateDistance(origin, destination);
});
});
Per the documentation the response element that you get the distance from also includes the duration:
duration
Type: Duration
The duration for this origin-destination pairing. This property may be undefined as the duration may be unknown.
That property has the following properties:
Properties
text
Type: string
A string representation of the duration value.
value
Type: number
The duration in seconds.
Add the version you like to the callback function (probably want text):
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
$('#result').html(err);
} else {
var origin = response.originAddresses[0];
var destination = response.destinationAddresses[0];
if (response.rows[0].elements[0].status === "ZERO_RESULTS") {
$('#result').html("Better get on a plane. There are no roads between "
+ origin + " and " + destination);
} else {
var distance = response.rows[0].elements[0].distance;
var duration = response.rows[0].elements[0].duration;
var distance_value = distance.value;
var distance_text = distance.text;
var duration_value = duration.value;
var duration_text = duration.text;
var kilometer = distance_text.substring(0, distance_text.length - 3);
$('#result').html("It is " + kilometer + " kilometer from " + origin + " to " + destination + " and it takes " + duration_text + " to drive.");
}
}
}
proof of concept fiddle
You are missing the duration.text in the DistanceMatrixResponse' Object so change your else block from:
else {
var distance = response.rows[0].elements[0].distance;
var distance_value = distance.value;
var distance_text = distance.text;
var kilometer = distance_text.substring(0, distance_text.length - 3);
$('#result').html("It is " + kilometer + " kilometer from " + origin + " to " + destination + " and it takes " + " to drive.");
}
To:
else {
var distance = response.rows[0].elements[0].distance;
var distance_value = distance.value;
var distance_text = distance.text;
// Add a variable here to store the duration
var duration_time = duration.text;
var kilometer = distance_text.substring(0, distance_text.length - 3);
$('#result').html("It is " + kilometer + " kilometer from " + origin + " to " + destination + " and it takes " + duration_time " to drive.");
}
duration.text must be added to the ResponseObject as:
result[0].duration.text
I'm using a function from google api to draw a way on the map it works good heres the mehode:
calculateAndDisplayRoute(directionsService, directionsDisplay) {
var waypts = [];
// var jsonData = JSON.parse(this.city);
if (!this.isArrayEmpty(this.stations)) {
for (var i = 0; i < this.stations.length; i++) {
waypts.push({
location: this.stations[i].station,
stopover: true
});
}
}
directionsService.route({
origin: this.depart,
destination: this.arrivee,
waypoints: waypts,
optimizeWaypoints: false,
travelMode: 'DRIVING'
}, function (response, status) {
if (status === 'OK') {
directionsDisplay.setDirections(response);
var route = response.routes[0];
let momo;
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
alert('coco');
let dt = new Date(route.legs[i].duration.value*1000);
let hr = dt.getHours();
let m = "0" + dt.getMinutes();
let s = "0" + dt.getSeconds();
let durationn= hr+ ':' + m.substr(-2) + ':' + s.substr(-2); //this gives 02:35:45 for exemple
/*
//I tried this this code in comment but it doesnt work.
let time1=durationn;
momo=time1;
let [hours1, minutes1, seconds1] = time1.split(':');
let [hours2, minutes2, seconds2] = momo.split(':');
momo=moment({ hours: hours1, minutes: minutes1, seconds: seconds1 })
.add({ hours: hours2, minutes: minutes2, seconds: seconds2 })
.format('h:mm:ss')
*/
console.log('mm'+ route.legs[i].start_address + ' '+route.legs[i].end_address +' '+route.legs[i].distance.text+' '+route.legs[i].duration.text);
console.log( momo);
}
} else {
window.alert('Directions request failed due to ' + status);
}
});
}
but my proble is on the duration, i will explain, i want every time this fuction get called, i need the duration to be sumed(sum of the duration).
I tried this this code in comment but it doesnt work.
PS:
please dont lose your time to understand the code just focus what is inside the second for loop block.
You can do something like that:
var sum = {h: 0, m: 0, s: 0};
var allTimes = ["02:35:45", "11:40:30", "12:55:39"];
allTimes.forEach(time => sum = sumUp(sum, time)); // Just sum up hour, minute and second
console.log(parseSum(sum)); // Do formatting in the end
function sumUp(sum, time) {
var times = time.split(":");
sum.h += +times[0];
sum.m += +times[1];
sum.s += +times[2];
return sum;
}
function parseSum(sum) {
var totSec = sum.s;
var s = totSec % 60;
var totMin = sum.m + parseInt(totSec/60);
var m = totMin % 60;
var totHour = sum.h + parseInt(totMin/60);
var h = totHour;
return `${h}:${m}:${s}`;
}
I have built a grid of coordinates to calculate the distances and apply some logic, row by row.
I have to calculate the distance between point A and point B, or if I have a point C, the distance between point A and point B and between point B and point C, and add the two values.
I tried to use a callback function, but the return value received in the calculation of the distance between point B and point C is always the last pair of coordinates and not that of the current line.
What is the error in the attached function? And how can I make my call less "cumbersome".
Thank you
function GetDistance(img, txtRimborso, hfImportoRimborso, hfLastOfTheDay) {
var latA = img.attributes['latA'].value;
var lngA = img.attributes['lngA'].value;
var latB = img.attributes['latB'].value;
var lngB = img.attributes['lngB'].value;
var latC = img.attributes['latC'].value;
var lngC = img.attributes['lngC'].value;
var isLastOfTheDay = hfLastOfTheDay.value;
var distance;
source = new google.maps.LatLng(latA, lngA);
destination = new google.maps.LatLng(latB, lngB);
GetDist(source, destination, function () {
distanceA = Number(this);
if (latC == '' && lngC == '')
{
console.log('latC = \'\' lngC = \'\'');
source = new google.maps.LatLng(latB, lngB);
destination = new google.maps.LatLng(latA, lngA);
GetDist(source, destination, function () {
distanceB = Number(this);
var fullDistance = Number(((Math.floor(distanceA / 1000))) + Number((Math.floor(distanceB / 1000))));
console.log('A: ' + Math.floor(distanceA / 1000) + ' B: ' + Math.floor(distanceB / 1000) + ' = ' + fullDistance);
});
} else if (isLastOfTheDay == 'True') {
console.log('isLastOfTheDay: ' + isLastOfTheDay);
source = new google.maps.LatLng(latB, lngB);
destination = new google.maps.LatLng(latC, lngC);
GetDist(source, destination, function () {
distanceB = Number(this);
var fullDistance = Number(((Math.floor(distanceA / 1000))) + Number((Math.floor(distanceB / 1000))));
console.log('A: ' + Math.floor(distanceA / 1000) + ' B: ' + Math.floor(distanceB / 1000) + ' = ' + fullDistance);
});
} else {
var fullDistance = (Math.floor(distanceA / 1000));
}
});
}
function GetDist(source, destination, fn) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
origins: [source],
destinations: [destination],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: true,
avoidTolls: true
}, function (response, status) {
if (status == google.maps.DistanceMatrixStatus.OK && response.rows[0].elements[0].status != "ZERO_RESULTS") {
fn.call(response.rows[0].elements[0].distance.value);
}
else
fn.call(0);
});
}
I have found the solution, using waypoint
function GetDistance(img, lRimborsoKm, txtRimborso, hfImportoRimborso, hfLastOfTheDay) {
var latA = img.attributes['latA'].value;
var lngA = img.attributes['lngA'].value;
var latB = img.attributes['latB'].value;
var lngB = img.attributes['lngB'].value;
var latC = img.attributes['latC'].value;
var lngC = img.attributes['lngC'].value;
var isLastOfTheDay = hfLastOfTheDay.value;
if (latC == '' && lngC == '') // A -> B + B -> A
{
source = new google.maps.LatLng(latA, lngA);
destination = new google.maps.LatLng(latA, lngA);
var waypts = [];
waypts.push({
location: new google.maps.LatLng(latB, lngB),
stopover: true
})
GetDist(source, destination, waypts, function () {
distance = Number(this);
var fullDistance = Number(distance);
});
} else if (isLastOfTheDay == 'True') { // A -> B + B -> C
source = new google.maps.LatLng(latA, lngA);
destination = new google.maps.LatLng(latC, lngC);
var waypts = [];
waypts.push({
location: new google.maps.LatLng(latB, lngB),
stopover: true
})
GetDist(source, destination, waypts, function () {
distance = Number(this);
var fullDistance = Number(distance);
});
} else { // A -> B
source = new google.maps.LatLng(latA, lngA);
destination = new google.maps.LatLng(latB, lngB);
var waypts = [];
GetDist(source, destination, waypts, function () {
distance = Number(this);
var fullDistance = Number(distance);
});
}
}
function GetDist(source, destination, waypts, fn) {
directionsService.route({
origin: source,
destination: destination,
waypoints: waypts,
optimizeWaypoints: true,
avoidHighways: true,
avoidTolls: true,
travelMode: google.maps.TravelMode.DRIVING
}, function (response, status) {
if (status === google.maps.DirectionsStatus.OK) {
var route = response.routes[0];
var distance = 0;
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
distance += Math.floor(route.legs[i].distance.value / 1000);
}
fn.call(distance);
} else {
fn.call(0);
console.log(status);
console.log('latA: ' + source.lat() + ', lngA: ' + source.lng() + ', latB: ' + destination.lat() + ',lngB: ' + destination.lng());
}
});
}
I' m having this problem , when I load my page and insert Origin and Destination, after clicking the button "locate" it doesn't show anything in the google map, because it says Response is not an object , so I tried to stamp it with console.log and it says Response=null , but if I reload the page and click fast on Locate , then it draws the route.
Here's the code
function init(){
var latlng = new google.maps.LatLng(40.635636, 17.942414);
var mapOptions = { zoom: 12, center: latlng };
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}
function updateMap(){
init();
var originGeocoder = new google.maps.Geocoder();
var destinationGeocoder = new google.maps.Geocoder();
var origin = document.getElementById( "origin" ).value + " Brindisi 72100";
var destination = document.getElementById( "destination" ).value + " Brindisi 72100";
var directionsService2 = new google.maps.DirectionsService();
originGeocoder.geocode( { 'address': origin }, function(results, status) {
if ( status == google.maps.GeocoderStatus.OK ) {
var startLatLng = results[0].geometry.location;
var oLat = startLatLng.lat();
var oLng = startLatLng.lng();
document.getElementById('cStart').innerHTML = oLat + " " + oLng;
}
else{
alert("Geocode was not successful for the following reason: " + status);
}
});
//Chiamata asincrona alle API per ottenere Lat e Lng dell' indirizzo di destinazione
destinationGeocoder.geocode( { 'address': destination }, function(results, status) {
if ( status == google.maps.GeocoderStatus.OK ) {
var destLatLng = results[0].geometry.location;
var dLat = destLatLng.lat();
var dLng = destLatLng.lng();
document.getElementById('cDestination').innerHTML = typeof dLat;
document.getElementById('cDestination').innerHTML = dLat + " " + dLng;
}
else{
alert("Geocode was not successful for the following reason: " + status);
}
});
//Salva in req[] le varie distanze tra le paline e la destinazione
singleObjToStop(origin,destination,function(paline,req,reqO){
console.log("1");
//Trova la palina piĆ¹ vicina alla destinazione
calcSingleDis(paline,req,reqO,function(w2,w1){
console.log("2");
//Disegna i waypoints(?)
reqEnd(origin,destination,w1,w2,function(request){
console.log("3");
directionsService2.route(request, function(response, status) {
console.log("4");
console.log(response);
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById("distance");
summaryPanel.innerHTML = "";
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += "<b>Route Segment: " + routeSegment + "</b><br />";
summaryPanel.innerHTML += route.legs[i].start_address + " to ";
summaryPanel.innerHTML += route.legs[i].end_address + "<br />";
summaryPanel.innerHTML += route.legs[i].distance.text + " ";
summaryPanel.innerHTML += route.legs[i].duration.text + "<br /><br />" ;
}
computeTotalDistance(response);
}
else{
console.log("ENTRA QUA STRONZO");
console.log("Fermata partenza = " + w1);
console.log("Fermata arrivo = " + w2);
}
});
directionsDisplay.setMap(map);
});
});
});
}
function singleObjToStop(origin,destination,callback){
var data=<?php echo $data; ?>;
var a,b,i=0;
var paline = new Array();
var req = new Array();
var reqO = new Array();
var num = <?php echo $n; ?>;
$.each(data, function(fieldName, fieldValue) {
a=fieldValue.geoLat;
b=fieldValue.geoLong;
a=parseFloat(a);
b=parseFloat(b);
paline[i]=new google.maps.LatLng(a,b);
req[i] = {
origin:paline[i],
destination:destination,
travelMode: google.maps.TravelMode.WALKING
};
reqO[i] = {
origin:origin,
destination:paline[i],
travelMode: google.maps.TravelMode.WALKING
};
i++;
if(i==num){
callback(paline,req,reqO);
}
});
}
function calcSingleDis(paline, req, reqO, callback) {
var directionsService = new google.maps.DirectionsService();
var c = 10000000;
var w2 = new google.maps.LatLng(0, 0);
var w1 = new google.maps.LatLng(0, 0);
var num = <?php echo $n; ?>;
var j = (num - 1);
var t;
var cO = 10000000;
var numO = <?php echo $n; ?>;
var jO = 0;
for (j = 0; j < num; j++) {
t = 0;
directionsService.route(req[j], function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
//directionsDisplay.setDirections(response);
var troute = response.routes[0];
var dis = parseFloat((troute.legs[0].distance.text).replace(",", "."));
document.getElementById('test').innerHTML = dis;
//se distanza minore di quella minore trovata fin ora la cambia
if (dis < c) {
w2 = paline[j - num];
c = dis;
}
if (t == (num - 1)) {
console.log("QUA ENTRA LOL");
for (jO = 0; jO < numO; jO++) {
console.log("E NON ENTRA MANNAC");
t = 0;
directionsService.route(reqO[jO], function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
console.log("E NON ENTRA MANNAC22222");
//directionsDisplay.setDirections(response);
var troute = response.routes[0];
var disO = parseFloat((troute.legs[0].distance.text).replace(",", "."));
document.getElementById('test').innerHTML = dis;
//se distanza minore di quella minore trovata fin ora la cambia
if (disO < cO) {
w1 = paline[jO - numO];
cO = disO;
}
if (t == (numO - 1)) {
console.log("W1 = " + w1);
console.log(response);
callback(w2, w1);
}
}
jO++;
t++;
});
}
}
}
j++;
t++;
});
}
}
function reqEnd(origin,destination,w1,w2,callback){
var request = {
origin:origin,
destination:destination,
waypoints: [{location: w1} , {location: w2}],
//waypoints: [{location: w2}],
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.WALKING
};
callback(request);
}
function computeTotalDistance(result) {
var totalDist = 0;
var totalTime = 0;
var myroute = result.routes[0];
for (i = 0; i < myroute.legs.length; i++) {
totalDist += myroute.legs[i].distance.value;
totalTime += myroute.legs[i].duration.value;
}
totalDist = totalDist / 1000.
document.getElementById("total").innerHTML = "total distance is: "+ totalDist + " km<br>total time is: " + (totalTime / 60).toFixed(2) + " minutes";
}
google.maps.event.addDomListener( window, 'load', init );
The problem is related to the limit of query that you can use with Google Maps API v3.
You can take a look here: https://developers.google.com/maps/documentation/business/faq#google_maps_api_services
You probably do lots of requests to the API with your program, while you have quite restrictive limits as you can see from google Q&A.
Applications should throttle requests to avoid exceeding usage limits,
bearing in mind these apply to each client ID regardless of how many
IP addresses the requests are sent from.
You can throttle requests by putting them through a queue that keeps
track of when are requests sent. With a rate limit or 10 QPS (queries
per second), on sending the 11th request your application should check
the timestamp of the first request and wait until 1 second has passed.
The same should be applied to daily limits.
Even if throttling is implemented correctly applications should watch
out for responses with status code OVER_QUERY_LIMIT. If such response
is received, use the pause-and-retry mechanism explained in the Usage
limits exceeded section above to detect which limit has been exceeded.
You could find useful: How do I Geocode 20 addresses without receiving an OVER_QUERY_LIMIT response?
The Google Maps API provides a geocoder class for geocoding and reverse geocoding dynamically from user input. read more check Geolocation demo here and more HTML5 Geolocation to check here
Let's say you have a map to display KmlLayer objects on it, with toggle checkbox for each KmlLayer object, located in a panel.
var Province = new google.maps.LatLng(47.112754, -70.815223);
var map = new google.maps.Map( document.getElementById('map'), {zoom:7,center:Province} );
And you store in KML files a limited number of routes, to optimize the speed of the map.
Direction's requests are made using Google Maps API v3.
And let's say you have a page containing a Google Map in a hidden div, and you make Direction requests through this API.
The array 'waypoints' holds the coordinates of each start and end point of each polyline needed.
var geo_coords = ''; // holds the results
var y; // counter
var delay_per_request = 1000; // default delay in milliseconds between requests
var current_delay = delay_per_request; // current delay between requests
function calcRoute(start,end) {
setTimeout(function(){
var directionsService = new google.maps.DirectionsService();
var request = {
origin: start,
destination: end,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
// Error?
if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
document.getElementById('error_text').innerHTML += "<br>" + start + "/" + end
+ "("+status+") :: Interval:"+current_delay+"ms";
window.scrollTo(0, document.body.scrollHeight);
if (current_delay < 2600) {
current_delay += 200;
}
if (current_delay >= 1600) {
delay_per_request = 1600;
}
retry++;
if (retry < 20) {
calcRoute(start,end);
} else {
document.getElementById('error_text').innerHTML += "<br>The script stopped running after "+retry+" retries"
+ " sur " + start + "/" + end + "("+status+") :: Interval:"+current_delay+"ms";
}
// Positive result?
} else if (status == google.maps.DirectionsStatus.OK) {
var myRoute = response.routes[0].overview_path;
for (i in myRoute) {
if (myRoute[i] != undefined) {
geo_coords += myRoute[i].lat() + "," + myRoute[i].lng() + "_";
}
}
geo_coords += "!";
document.getElementById('error_text').innerHTML += "<br><font color=\"green\">" + start + "/" + end
+ "("+status+") :: Interval:"+current_delay+"ms</font>";
window.scrollTo(0, document.body.scrollHeight);
current_delay = delay_per_request;
retry = 0;
Next();
}
});
},current_delay);
}
function Next() {
if (y <= waypoints.length) {
destination = "from:" + waypoints[y] + " to:" + waypoints[y + 1];
geo_desc += description[y] + " => " + description[y+1] + "|";
calcRoute(waypoints[y],waypoints[y+1]);
y = y +2;
} else {
document.getElementById("information").setAttribute('value',geo_coords);
document.getElementById("show_debug_text").setAttribute('value',geo_desc);
document.getElementById("error").setAttribute('value',document.getElementById('error_text').innerHTML);
document.forms["kml"].submit();
}
}
Next();
My question is the following:
Has anyone noticed that Google lower the priority of your requests after you made some request in batch?
The more request you do, the bigger is the number of OVER_QUERY_LIMIT response you get, the bigger is the delay between requests before you have a successful result, and the bigger is the number of requests before you have a successful result.
Is there any workaround or any reason?
(I know I should use the Direction API Webservice to make my requests.)
Thank you!