I have some jQuery code that uses Google Maps Geocoding API to convert an address to coordinates, then use alert() to show the result in popup window. Here is the code which works fine:
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
}
});
});
However now I want jQuery to submit the form $searchbox_form after the user closes the alert box. However adding $("#searchbox_form").submit(); at the end of the code chunk submits the form before the alert box shows up. This also causes the form to be submitted before the Google Maps geocoder returns the result.
How can I allow the geocoder to return the result before the form gets submitted?
Same as the code above, but with 1 additional line to submit form at the end:
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
}
});
$("#searchbox_form").submit(); //THIS IS THE ADDED LINE OF CODE!!
});
You need to move the submit within the callback to the geocode function. The reasoning is, it's asynchronous and not running in direct order, so it's calling the geocode and then immediately firing the submit. If you put it like below, the form will submit on callback and after the alerts (as alerts will block the thread).
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
// This function is async
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
$("#searchbox_form").submit();
}
});
});
Why don't you just submit it after you're done with your callback?
$("#searchbox_form #search_button").click(function(e){
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
$("#searchbox_form").submit();
}
});
});
I think geocoder.geocode is an asynchronous function. Therefore you need the submit after the alert boxes.
$("#searchbox_form #search_button").click(function (e) {
e.preventDefault();
var address = $("#location").val();
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("input#user_lat").val(results[0].geometry.location.lat());
$("input#user_lng").val(results[0].geometry.location.lng());
alert("lat: " + $("input[name='user_lat']").val());
alert("lng: " + $("input[name='user_lng']").val());
$("#searchbox_form").submit();
}
});
});
Related
I am trying to get lat and long info from Google Geocoder, but although the information is properly extracted on the call that is being done, for some reason the js variable gets empty imediately after getting to my second alert on this piece of code (the one outside "if" condition):
var map, marker, latLong;
var geocoderlat = new google.maps.Geocoder();
var addresslat = '<?php echo str_replace(" ","+",$address);?>';
function initMap() {
geocoderlat.geocode( { 'address': addresslat}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
latLong = results[0].geometry.location;
alert("Inside IF: "+latLong);
} else {
latLong = '<?php echo $latitude.",".$longitude;?>';
}
})
alert("Outside: "+latLong);
var isDraggable = !('ontouchstart' in document.documentElement);
var mapOptions = {
center: new google.maps.LatLng(latLong),
zoom: 12,
(...)
Obviously there's a lot of code besides this part, but the rest of the code doesn't matter in this case because this variable is never again used anywhere in my code (I even searched with Agent Ransack all over the files so be sure that this variable was not being used anywhere else).
EDIT:
Ok, this is really, really weird...
function getGeoLatLong(addresslat) {
geocoderlat.geocode( { 'address': addresslat}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
return results[0].geometry.location;
} else {
return '<?php echo $latitude.",".$longitude;?>';
}
})
}
alert("Outside function: "+getGeoLatLong(addresslat));
This still returns "undefined" in the alert.
geocoderlat.geocode is taking more time to execute while the code execution has already reached your second alert. you need to use setTimeout to check the results or try this.
function initMap() {
geocoderlat.geocode( { 'address': addresslat}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
latLong = results[0].geometry.location;
alert("Inside IF: "+latLong);
alert("Outside: "+latLong);
var isDraggable = !('ontouchstart' in document.documentElement);
var mapOptions = {
center: new google.maps.LatLng(latLong),
zoom: 12,
(...)
} else {
latLong = '<?php echo $latitude.",".$longitude;?>';
}
})
Also, if you want to use latLong = '<?php echo $latitude.",".$longitude;?>'; as the default latLong you need to declare it at the top. then it will be anyways overridden when geocode finishes loading
EDIT
Try this.
function getGeoLatLong(addresslat) {
geocoderlat.geocode( { 'address': addresslat}, checkresults)
}
function checkresults(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
alert("Outside function: " + results[0].geometry.location);
//declare the big chunk of code as a function and make a call to it.
} else {
alert("Outside function: <?php echo $latitude.','.$longitude;?>");
//declare the big chunk of code as a function and make a call to it.
}
}
getGeoLatLong(addresslat);
So I am working with google maps on a project
I need to get lat&lng from a place name (not city but a, lets say, caffee in a city, or a mall in a city). So I went with this code
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': 'SCC, Sarajevo'}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
alert("location : " + results[0].geometry.location.lat() + " " +results[0].geometry.location.lng());
$("#pretragaBarInput").val("location : " + results[0].geometry.location.lat() + " " +results[0].geometry.location.lng());
} else {
alert("Something got wrong " + status);
}
});
(SCC is a mall in Sarajevo)
But I dont get the exact lat&lng from the place but rather lat&lng from the city center. I've tried with other places but got the exact same coords that point to the city center
P.s. This is just a debug script to see if its working...
Geocoding is for addresses(although it also may return the desired result for prominent places).
When you are searching for places uses the places-library, e.g. a PlacesService.textSearch
(returns: Sarajevo City Center, Vrbanja 1, Sarajevo 71000)
One of my form fields is address. Before sending it to the server I want to transform it to latitude and longitude. This requires sending asynchronous request to some external service (e.g. Google Maps API). What's the proper way to do this?
My geocoding snippet:
function geocodeAddress() {
var address = document.getElementById("address").value;
geocoder.geocode({"address": address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
var lat = results[0].geometry.location.lat()
document.getElementById("latitude").value = lat
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
I see several options:
Use onsubmit event handler - first return false (to wait for geocoding to complete), then in a separate callback set latitude field and submit form.
Use onchange event handler for address field - this requires blocking submit button until we geocode the address; it's also tricky if we prepopulate form values after submit it (e.g. filter rows)
Obviously I can also parse it on the server side, but this may result in one user using all my "quota" in the external service and I'd have to implement some kind of fairness, which I'd like to avoid.
So what's the proper way to solve this kind of pre-processing of form values in javascript?
You could consider the following approach:
prevent default behavior of button via Event.preventDefault()
resolve address via google.maps.Geocoder.geocoder function
once the address is resolved resume form submit: form.submit()
Example
function saveData(e) {
e.preventDefault(); //1.stop default behaviour of button
var address = document.getElementById("txtAddress").value;
resolveAddress(address,
function(results) {
document.getElementById("txtLat").value = results[0].geometry.location.lat();
document.getElementById("txtLng").value = results[0].geometry.location.lng();
document.getElementsByTagName('form')[0].submit(); //2.resume form submit
},
function(status) {
console.log('An error occured while resolving an address');
});
}
function resolveAddress(address,success,error) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ "address": address }, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
success(results);
} else {
error(status);
}
});
}
Plunker
I want users to do the following:
1) Enter their Street Address and Zip Code and hit "Submit", which will trigger Google Maps to geocode the address and place a marker on the map. I'm using the below code for this (which is working fine and getting me all the address info I need):
var geocoder;
var map;
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(37.7789, -122.3917);
var mapOptions = {
center: latlng,
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
}
function codeAddress() {
var address = document.getElementById('streetAddress').value +", "+document.getElementById('zipCode').value;
geocoder.geocode( { 'address': address, 'region': 'US'}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0].address_components) {
for (var i in results[0].address_components) {
if (typeof(results[0].address_components[i]) === "object" && results[0].address_components[i].types[0] == "street_number") {
var streetNumber= results[0].address_components[i].long_name;
} else if (typeof(results[0].address_components[i]) === "object" && results[0].address_components[i].types[0] == "route") {
var streetName= results[0].address_components[i].short_name;
} else if (typeof(results[0].address_components[i]) === "object" && results[0].address_components[i].types[0] == "neighborhood") {
var neighborhood= results[0].address_components[i].short_name;
} else if (typeof(results[0].address_components[i]) === "object" && results[0].address_components[i].types[0] == "administrative_area_level_1") {
var state= results[0].address_components[i].short_name;
} else if (typeof(results[0].address_components[i]) === "object" && results[0].address_components[i].types[0] == "postal_code") {
var zipCode= results[0].address_components[i].long_name;
}
}
}
console.log(streetNumber+", "+streetName+", "+neighborhood+", "+state+", "+zipCode);
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
2) After the user sees the marker, I want them to hit a "Confirm" button to submit this data to my server (I'm using Firebase via their Javascript API). The question I have is how do I best store the variables 'streetNumber', 'streetName', 'city', 'state', 'zipCode', and 'neighborhood' between the time that Google Maps returns the data and when the user hits "Confirm" button? The only thing I can think of is storing it on the browser window (e.g. window.streetName, window.streetNumber, etc), but I know that's not best practice.
You may e.g. store these data as properties of the button, it will be very easy to access them later(but it's also not a good practice).
The best thing you can do is to store all your objects in a single "class" (with all I mean everything that is global, currently also map, geocoder, codeAddress and initialize are global )
Here a sample code(ready to use, it doesn't expose any global property):
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false">
</script>
<script type="text/javascript">
//a self-executing, anonymous function
(function(opts,ns){
//"that" is our object, it holds all properties and methods
//and is only visible within the scope of this function
var that = {geocoder: null,
map: null,
opts: opts,
ns: ns,
postData: null},
goo = google.maps,
byId = function(id){return document.getElementById(id);};
//initialize the map
that.initialize=function() {
var latlng = new goo.LatLng(this.opts.lat,this.opts.lng);
var mapOptions = {
center: latlng,
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
this.map = new goo.Map(byId(this.opts.ids.map), mapOptions);
//assign click-handlers to the buttons
goo.event.addDomListener(byId(this.opts.ids.btnGeo),'click',function(){
that.codeAddress.call(that);});
goo.event.addDomListener(byId(this.opts.ids.btnSend),'click',function(){
that.onSubmit.call(that);});
}
//send the data here
that.onSubmit=function(){
alert(JSON.stringify(that.postData));
}
//parse the address-components into an object
that.parseAddressComponents=function(ac){
var components={
street_number: ['streetNumber', 'long_name'],
route: ['streetName', 'short_name'],
administrative_area_level_1:['state', 'short_name'],
neighborhood: ['neighborhood', 'short_name'],
postal_code: ['zipCode', 'long_name']
},o={};
for(var i=0;i<ac.length;++i){
inner:for(var c in components){
if(ac[i].types[0]==c){
o[components[c][0]]=ac[i][components[c][1]];
break inner;
}
}
}
return o;
}
//geocoding-callback
that.geoCallback=function(results, status){
if (status == goo.GeocoderStatus.OK) {
//store the parsed address-components as property of "that"
that.postData=that.parseAddressComponents(results[0].address_components);
that.map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: that.map,
position: results[0].geometry.location
});
} else {
that.postData={};
alert(status);
}
}
//geocoding
that.codeAddress=function() {
var address = [byId(this.opts.ids.uiAddr).value,
byId(this.opts.ids.uiZip).value].join(',');
//initialize geocoder on first run
if(!this.geocoder){this.geocoder=new goo.Geocoder();}
this.geocoder.geocode( { 'address': address, 'region': this.opts.region},
this.geoCallback);
}
//make "that" global when needed
if(ns)window[ns]=that;
//load-handler
goo.event.addDomListener(window, 'load',function(){
that.initialize();});
})
( //some properties for the object
{
lat:37.7789,
lng:-122.3917,
region:'US',
ids:{ map :'map_canvas',
btnGeo :'geobutton',
btnSend :'sendbutton',
uiAddr :'streetAddress',
uiZip :'zipCode'}
},
//supply a name for the object here when you want to make it global
null
);
/*]]>*/
</script>
<fieldset>
streetAddress:<input id="streetAddress" value="paisley park">
zipCode:<input id="zipCode" value="55422">
<input id="geobutton" type="button" value="geocode">
<input id="sendbutton" type="button" value="send">
</fieldset>
<div id="map_canvas" style="height:300px;"></div>
For a website where a user enters his address, I'm trying to find the location closest to him where the user can collect the ordered goods.
Based on the user's address I can narrow down the possible pick up locations to between 2 and 5. So I'd like to calculate the distance between user's address (point A) and the possible pick up locations.
The demo here works fine with just two addresses. I've adapted the code as much as I can to work with more than two addresses. I posted my JS code here since I can't seem to properly format it in SO.
In the code are two alerts. The first alert correctly shows the different pick up locations. But the second alert always shows the LAST pickup location.
Can anyone explain why?
HTML:
<p id="hello">Hello World</p>
JavaScript:
var geocoder, location1, location2, gDir;
function initialize(counter) {
if( counter == 0 ){
geocoder = new GClientGeocoder();
gDir = new GDirections();
}
GEvent.addListener(gDir, "load", function() {
var drivingDistanceMiles = gDir.getDistance().meters / 1609.344;
var drivingDistanceKilometers = gDir.getDistance().meters / 1000;
$("#results").append('<strong>Driving Distance: </strong>' + drivingDistanceKilometers + ' kilometers<br /><br />');
});
}
function getDistance(agency_add, counter) {
initialize(counter);
geocoder.getLocations(agency_add, function (response) {
if (!response || response.Status.code != 200) {
alert("Sorry, we were unable to geocode the address" + agency_add);
}
else {
location1 = {lat: response.Placemark[0].Point.coordinates[1], lon: response.Placemark[0].Point.coordinates[0], address: response.Placemark[0].address};
//alert("ONE: "+location1.address);
geocoder.getLocations(document.forms[0].address1.value, function (response) {
//alert("TWO: "+location1.address);
if (!response || response.Status.code != 200) {alert("Sorry, we were unable to geocode the second address");}
else {
location2 = {lat: response.Placemark[0].Point.coordinates[1], lon: response.Placemark[0].Point.coordinates[0], address: response.Placemark[0].address};
gDir.load('from: ' + location1.address + ' to: ' + location2.address);
}
});
}
});
}
$(document).ready(function(){
//put each agency address in an array
var agencies = [];
$(".agency_field").each(function(index) {
agencies.push($(this).val());
});
for (var i = 0; i < agencies.length; i++){
var res = getDistance(agencies[i], i);
}
});
you are calling geocoder.getLocations inside a loop. geocoder.getLocations runs asynchronously. when it receives the 2nd request while still processing the first, it cancels the first request.
If you want to multi-thread geocoder.getLocations you need to create multiple instances of it.