I am using javascript, google direction service
I have multiple source points on the map and one destination point. I need to find the closest source point to the destination point. As direction service works asynchronously I need to map points with their lat/lang. However the problem is that, the response format sent by direction service changes.
function calcRoute(start, end) {
var request = {
origin:start,
destination:end,
travelMode:google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request,
function (response, status) {
distanceCount++;
if (status == google.maps.DirectionsStatus.OK) {
var dist = computeTotalDistance(response);
console.log(dist);
if (dist < minDistance) {
minDistance = dist;
minDistanceIndex = locationsMap[response.ug.origin.Ua+""+response.ug.origin.Va];
}
if (distanceCount == vl.length) {
drawWay(vl[minDistanceIndex], fsl);
}
}
}
);
}
The problem occurs on the line
minDistanceIndex = locationsMap[response.ug.origin.Ua+""+response.ug.origin.Va];
response.ug.origin.Va and response.ug.origin.Ua comes from google direction service, however it sometimes changes to response.ug.origin.Xa and response.ug.origin.Ya.
Do you have any ideas why it changes from time to time? or any ideas how to deal with that problem?
You don't have to access those internal properties, they are not a part of the API so they may change.
Use only the properties/methods defined inside the specification, all data provided by the response are accessible through them,
example:
response.routes[0].legs[0].start_location
//returns latLng of the origin
Apart from that:
Your description sounds as if you better should use a DistanceMatrixRequest
Related
Hey guys i'm developing a tracking system in which i have to use the API of google maps to calculate ETA (estimated time of arrival).
I have the driving distance.
But I need the kilometers away to the arrival point in order to be able to predict the time of arrival.
How do I call the driving distance value below?
directionsService.route(request,
function(response, status) {
if ( status == google.maps.DirectionsStatus.OK ) {
(response.routes[0].legs[0].distance.value); // the distance in metres
return((response.routes[0].legs[0].distance.value)/1000); //distance e kilometres
}
else {
// oops, there's no route between these two locations
// every time this happens, a kitten dies
// so please, ensure your address is formatted properly
}
});
function getArrivalTime(distance, vel) {
return distance / vel;
};
console.log(getArrivalTime(distance,vel));
You can actually put your directions request in your function, getArrivalTime. Then you could simply create a var to hold the distance and set it equal to response.routes[0].legs[0].distance.value)/1000 Please see code sample here:
function getArrivalTime(vel) {
var distance;
directionsService.route(request,
function(response, status) {
if ( status == google.maps.DirectionsStatus.OK ) {
(response.routes[0].legs[0].distance.value); // the distance in metres
distance = ((response.routes[0].legs[0].distance.value)/1000); //distance e kilometres
}
else {
// oops, there's no route between these two locations
// every time this happens, a kitten dies
// so please, ensure your address is formatted properly
}
});
return distance / vel;
};
console.log(getArrivalTime(distance,vel));
You may need to put a timeout around return(distance,vel) to ensure the API call has had time to respond to the request before you return the data. One other option would be to create a global variable to hold distance so that you can access it in any function. It's typically best to avoid global variables though, so I'd recommend the first way I mentioned.
I hope this helps!
Here is my fiddle . Here i have displayed current location and end location by balloons.
And i am trying to put directions between start and end point in the map. In this case no markers for the directions are displayed may be because both shares common location. But the zoom is too great , results in it is not covering start and end point. User have to make double right click to see both start and location.
I have also tried,
new_boundary = new google.maps.LatLngBounds();
new_boundary.extend(new google.maps.LatLng(start));
new_boundary.extend(new google.maps.LatLng(end));
map.fitBounds(new_boundary);
But it is not working. Whats wrong with my map configuration ?
your script breaks (at least for me) at this line:
dir=((Math.atan2(z.lng()-a.lng(),z.lat()-a.lat())*180)/Math.PI)+360
take a look at this line:
z=(step.lat_lngs.length)?step.lat_lngs[1]:step.end_point,
for the first step lat_lngs.length is 1 , so step.lat_lngs[1] is undefined, the call of z.lng() fails with "Cannot call method 'lng' of undefined"
Possible fix for that error:
z=(step.lat_lngs.length>1)?step.lat_lngs[step.lat_lngs.length-1]:step.end_point,
Related to the zoom:
When you wouldn't have disabled the complete UI you would have seen that the result (zoom ) is correct.
The DirectionsRenderer by default will refresh the viewport of the map so that the complete route is visible.
This will be done(the bounds initally set in your script will be discarded).
To have another result(preserve the current viewport, but extend it that also the route is visible), you must:
set the preserveViewPort-option of the DirectionsRenderer to true(default is false)
extend the bounds of the map with the bounds of the route
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.set('preserveViewport',true);
map.fitBounds(map.getBounds().union(response.routes[0].bounds));
//continue with your code
This code is correct for example
Your map change resolution when you add
directionsDisplay.setDirections(response);
setDirections(directions:DirectionsResult) None Set the renderer to
use the result from the DirectionsService. Setting a valid set of
directions in this manner will display the directions on the
renderer's designated map and panel.
Hope, that i understand problem right, English i not my native language
Try to add validation as
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
if(!response.routes[0].bounds.getNorthEast().equals(response.routes[0].bounds.getSouthWest())){
directionsDisplay.setDirections(response);
}
addDirections(response.routes[0]);
}
});
To check if answer contains different point
I'm trying to use this function to geocode a string passed to it into a google maps result.
function codeAddress(address) {
var firstResult;
geocoder.geocode( { 'address': address}, function(results, status) {
if(status == google.maps.GeocoderStatus.OK) {
firstResult = results[0];
} else {
firstResult = "failed";
}
});
return firstResult;
}
The problem is that when I try to debug it using the debugger from chrome and insert a breakpoint outside and inside the ceocoder.geocode statement, I can clearly see the program execution go at the third line, but it skips the inner lines and goes straight to the return value (returning an undefined value). Some other times, it goes through the if statement within it, but it doesn't go to the return statement although I have set up a breakpoint there.
Am I trying to do this the wrong way? How can I fix this?
I found some possible answers on StackOverflow, they might help:
Aleem Saadullah on SO posted:
Finally figured it out. It was a silly error. I had linked my Javascript file before linking the jQuery. The code works fine now.
Mikko Ohtamaa answered on SO:
To be strict, whatever custom CGI you are using does not conform
JavaScript syntax.
What I suggest is that you make your custom dynamic processing into
JavaScript comments, so that it doesn't affect the normal JavaScript
parsing. This is much easier than writing your custom JavaScript
parser to cater your custom syntax.
E.g.
// %import and other custom commands here
The best approach is that you would not put any non-JavaScript to JS
files at all. If you need importing and such there are some more
generic JavaScript solutions for them.
http://browserify.org/
http://requirejs.org/
EDIT 2:
Found an answer on Engineer's answer on SO:
geocoder.geocode works asynchronously, so you need to wait until its
response will be delivered from google's servers, and only then use
responded data. Put your loop inside of callback:
geocoder.geocode( { 'address': zip }, function(results, status) { // status is empty
if (status == google.maps.GeocoderStatus.OK) {
var userLat = results[0].geometry.location.lat();
var userLng = results[0].geometry.location.lng();
userLatLng = results[0].geometry.location;
for (var i = data.length-1; i--;) {
//loop body
}
}});//end geocode
I have an odd problem that I've been beating my head into a wall over for the past few hours.
I'm working on an iPhone app in Appcelerator Titanium, and it currently wants to wait until other code has been run before getting the results of a couple of HTTPClient requests, despite the fact that I'm calling them before I try to use their results.
function getMarkers(e, miles){ //The function with the HTTPClient calls that are firing last, trimmed to have only the relevant code.
var markers = [];
Ti.API.info("Getting markers");
xhr.onload = function()
{
var data = Ti.XML.parseString(this.responseText);
var ref = data.documentElement.getElementsByTagName("reference");
for(var i =0; i < ref.length; i++){
var marker = new Object();
marker.ref = ref.item(i).text;
var request = Titanium.Network.createHTTPClient();
request.setTimeout(10000);
request.onload = function(){
var data = Ti.XML.parseString(this.responseText);
marker.address = data.documentElement.getElementsByTagName("formatted_address").item(0).text;
if(data.documentElement.getElementsByTagName("formatted_phone_number") != null){
marker.phone = data.documentElement.getElementsByTagName("formatted_phone_number").item(0).text;
} else {
marker.phone = null;
}
marker.icon = data.documentElement.getElementsByTagName("icon").item(0).text;
marker.lat = data.documentElement.getElementsByTagName("lat").item(0).text;
marker.lng = data.documentElement.getElementsByTagName("lng").item(0).text;
marker.name = data.documentElement.getElementsByTagName("name").item(0).text;
if(data.documentElement.getElementsByTagName("url") != null) {
marker.url = data.documentElement.getElementsByTagName("url").item(0).text;
} else {
marker.url = null;
}
markers.push(marker);
Ti.API.info(markers.length);
}
request.open("GET","https://maps.googleapis.com/maps/api/place/details/xml?reference=" + marker.ref + "&sensor=true&key=" + Ti.App.apiKey);
request.send();
}
};
xhr.open("GET","https://maps.googleapis.com/maps/api/place/search/xml?location=" + googleLatLng + "&radius=" + radius + "&types=" + Ti.App.types + "&sensor=true&key=" + Ti.App.apiKey);
xhr.send();
return markers;
}
// actually draw the markers on the map
function drawMap(markers, currentLoc)
{
var i;
Ti.API.info("Adding markers...");
for(i=0;i<markers.length;i++)
{
Ti.API.info("Marker " + i);
Ti.API.info(markers[i].name);
var ann = Titanium.Map.createAnnotation({
image:markers[i].icon,
animate:true,
latitude:markers[i].lat,
longitude:markers[i].lng,
title:markers[i].name,
subtitle:markers[i].address
});
if(markers[i].url != null){
ann.rightButton = markers[i].url;
}
mapview.addAnnotation(ann);
}
Ti.API.info("Markers added"); //When this block is called, markers.length == 0
}
// find the user's location and mark it on the map
function waitForLocation(e)
{
//Do stuff about finding current location and marking it on the map. This stuff works and a pin drops for the current location
drawMap(getMarkers(e), currentLoc);
}
waitForLocation gets called first, which then calls the others. Xcode outputs the following:
[INFO] Getting markers
[INFO] Adding markers...
[INFO] Markers added
[INFO] 1
[INFO] 2
[INFO] 3
[INFO] 4
This means that it's going into the getMarkers function (first line), then leaving it (next two lines), then going back to the getMarkers function to actually get the markers (last four lines, output of markers.length as each marker is added). Knowing that
I moved the .open() call from before .onload() call based on an answer I found here, but I get the same whether .open() is before or after .onload().
I found information that the httpClient call performs its task asynchronously (an important bit of information that is lacking from the API reference). Knowing this, it makes sense that it leaves the function, but it does screw with how information is being handled, as I need the markers downloaded before trying to add them.
On talking with my iPhone developer coworker, he mentioned that he handles them using a delegate and a delegate.connectionDidFinishLoading call. Is there, perhaps, either a way to hook into this, or a Titanium implementation of this that I could use?
Is there another good way that I can make sure it doesn't try to load the markers before the app has actually downloaded them? It only needs to work for iPhone, so iPhone-specific options are fine.
After a lot of searching and hair pulling, I finally managed to get it working the way I needed.
.open() has a third, boolean, parameter that forces the HTTPClient to run synchronously (another tidbit not mentioned in the documentation). Setting it to false will make it run synchronously. Doing that allowed me to test the code in the order I expected it to run.
I also found that I couldn't make an array of all the markers and load them at once, so I adjusted my addMarker() function to only take one marker, and called it inside the loop that gets the marker data. Once I got that working, I was able to make the HTTPClient calls asynchronous again.
On Android, any task started from the UI thread especially Network activity is required to be asynchronous from SDK 2.3 on.
Basically whenever someones opens up my (Google) map I want it default to their approximate location.
Is there an easy way to do it with Google's API or do I have to write a custom code (this is python based app)?
You can use Google API's built-in ClientLocation object:
if (GBrowserIsCompatible())
{
var map = new google.maps.Map2(document.getElementById("mapdiv"));
if (google.loader.ClientLocation)
{
var center = new google.maps.LatLng(
google.loader.ClientLocation.latitude,
google.loader.ClientLocation.longitude
);
var zoom = 8;
map.setCenter(center, zoom);
}
}
Check out http://www.ipinfodb.com/. You can get a latitude and longitude value by passing their services an IP address. I did something recently where I created a simple service that grabbed the current IP address and then passed it to the service ("api/location/city" is just a service that curls the ipinfodb service). Using jquery:
$.get("api/location/city", null, function(data, textStatus)
{
if (data != null)
{
if (data.Status == "OK")
{
var lat = parseFloat(data.Latitude);
var lng = parseFloat(data.Longitude);
$.setCenter(lat, lng, $.settings.defaultCityZoom);
manager = new MarkerManager(map, {trackMarkers : true });
var e = $.createUserMarker(map.getCenter());
e.bindInfoWindowHtml($("#marker-content-event").html());
var m = [];
m.push(e);
// map.addOverlay(e);
manager.addMarkers(m, 10);
manager.refresh();
}
else
{
$.setCenter($.settings.defaultLat, $.settings.defaultLng, $.settings.defaultZoom);
}
}
}, "json");
The key here is this line:
$.setCenter(lat, lng, $.settings.defaultCityZoom);
Just setting the center to the lat/lng of the result of the service call.
Per the docs, just map.setCenter(new GLatLng(37.4419, -122.1419), 13); or whatever other coordinates. Doing it in the page's Javascript is normally preferred.
If you mean translating an IP to lat and long, I don't think the Google API supports that, but there are other web services that do, such as maxmind, hostip, and many, many others. I don't know which one(s) to recommend -- try out a few, would be my suggestion!
If the user uses FireFox 3.5/google gears, you can retrieve the lat and lng from the browser itself.
You'll find details on another stackoverflow post here
IP Address Geocoding API for Google Maps: http://web3o.blogspot.com/2011/06/ip-address-geocoding-api-for-google.html