I want to use the google geocode via HTTP functionality to translate a city name into longitude and latitude in for my AJAX web application.
However, it appears that no callback function exists for the HTTP geocoder functionality
http://code.google.com/apis/maps/documentation/geocoding/index.html
Is that true, no callback function exists?
Because if that is true, it essentially means that the Google geocode via HTTP api is useless when used with AJAX because JavaScript will throw a crossdomain exception error.
Any ideas on how I can use the geocode via HTTP api in my AJAX web application in JavaScript?
Note: I do not want to use the full blown Google Maps API which is approx 200kb download (i.e. GClientGeocoder). I want to use the HTTP api b/c of it's super quick responsiveness and lack of needing my web users from having to download the huge full blown interactive google maps api.
E.g.
http://maps.google.com/maps/geo?output=json&sensor=false&key={API_KEY}&q={CITY,STATE}&CALLBACK=????
Thanks
Here is an example that uses the Google Maps Geocoder. The geocoder function getLocation takes a callback function as the second argument.
function findAddress(street, city, state, zip) {
var address = [
street,
city.toLowerCase(),
state.toLowerCase(),
zip
].join(', ');
if (!geocoder) {
geocoder = new GClientGeocoder();
}
if (geocoder) {
geocoder.getLocations(
address,
function(result) {
var dialog, len, point;
if (result.Status.code != G_GEO_SUCCESS) {
alert("Error: "+result.Status.code)
} else {
len = result.Placemark.length;
if (len > 1) {
alert("Multiple matches were found. I'll leave it as an exercise to handle this condition");
} else {
point = new GLatLng(
result.Placemark[0].Point.coordinates[1],
result.Placemark[0].Point.coordinates[0]
);
}
}
}
);
}
}
hmm....I think you'd have to have your AJAX call back to your own server, and then call Google's Geocode from your server.
Thats how I do AJAX geocoding, it all goes through my ASP.NET code.
EDIT:
In the ASP.NET webforms environment I might implements this as a lightweight ASHX file, but for the purposes of simplicity, here's an ASPX example:
public partial class GoogleHandler : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e) {
Response.Write(GetGoogleXML("http://pseudo_googlegeocode?parameter=" + parametersFromQuerystring);
}
}
In the example above, the .NET page is only passing the request along.
But in a real environment, I'd rather my .NET code do more than just pass the data over. This way I can do error handling, filtering, validation, business logic, all on the server, before sending that data over to the client.
Also, this allows for greater abstraction. i.e, I might change from google to yahoo geocoding. In this way I'd only have to change my serve logic, and leave the client to just receive a generic set of coordinates/location data.
Also, using that abstraction I could actually aggregate multiple data from various geocoding data sources. Again, the server takes care of aggregating, the client just receives and displays the filtered data.
As others noted, you didn't read the full page. You want what that page calls the JavaScript Client Geocode.
Here's a simplified version of a script I wrote a while back. It also uses a Google Map control, but feel free to ignore that. The delay function hack is because it seemed Google was occasionally returning null when I hit their servers too fast. I don't know if this is still an issue, so don't put it in unless you have to.
<script type="text/javascript">
//<![CDATA[
var freezeLocations;
var coder;
var map;
function load() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng(38.479395, -98.349609), 4);
map.addControl(new GLargeMapControl());
}
coder = new GClientGeocoder();
missionLocations = new Array();
missionLocationsDelayed = new Array();
addMissionLocation("Atlanta, Georgia", "http://improveverywhere.ning.com/group/atlanta");
//etc.
}
function addMissionLocation(newLocation, url)
{
var successful = false;
var counter = 0;
while(!successful && counter < 3)
{
coder.getLatLng(
newLocation,
function(point) {
if (!point) {
//alert(newLocation + " not found");
successful = false;
} else {
missionLocations.push(new GMarker(point, { title:newLocation}));
//alert(missionLocations.length);
map.addOverlay(missionLocations[missionLocations.length - 1]);
missionLocations[missionLocations.length - 1].bindInfoWindowHtml("<a href='" + url + "'>" + newLocation + "</a>");
successful = true;
}
}
);
if(!successful)
{
delayGeocode();
}
counter++;
}
}
function delayGeocode()
{
for(var i = 0; i < 2000000; i++)
{
}
}
//]]>
</script>
You could use Yahoo Query language as outlined in my blog post http://jawtek.blogspot.com/2009/03/unwritten-guide-to-yahoo-query-langauge.html
You would be able to use a yql statement like: select * from json where
url="http://maps.google.com/maps/geo?output=json&sensor=false&q=Indianapolis,In"
Then you would add a script tag to your html (can be done with document.createElement('script')) with a src http://query.yahooapis.com/v1/public/yql?q={your yql here}&format=json&callback={your function here} where {your yql here} is replace with a URI Encoded version of you yql statment.
If you have looked at the
documentation and not found it and
both Andrew and Mike have not said
"yes", and told you how to do it, I
suspect you have your answer.
lol
and lets all read the service's documentation:
10.13 hide or mask from Google the identity of your service as it uses
the Service, including by failing to
follow the identification conventions
listed in the Maps APIs Documentation;
or
10.14 violate any policies in the Maps APIs Documentation or violate Google's
Software Principles (...)
Also
This service is designed for geocoding static (known) addresses
using a REST interface, for placement
of application content on a map. For
dynamic geocoding of user-defined
addresses (for example, within a user
interface element), consult the
documentation for the JavaScript
Client Geocoder or the Maps API for
Flash Client Geocoder. Geocoding is a
time and resource intensive task.
Whenever possible, pre-geocode known
addresses (using the Geocoding Service
described here or another geocoding
service), and store your results in a
temporary cache of your own design.
But then again you could try Google Maps API V3 Geocoder
Look at the Google Maps API. It has some functions with callback that uses it's geocoding service.
http://code.google.com/apis/maps/documentation/reference.html#GClientGeocoder.getLatLng
I second the suggestion to create a server-side page to access the geocoder. I am doing something similar and it works great. There's a good article about working with the geocoder in PHP here.
Also note that technically you're not permitted to use Google's geocoder unless you'll be displaying the data on a Google Map - but I don't know if they'll actually check on you.
I too encountered the challenges you described above. As you indicated, Google prevents cross-domain HTTP access to the Geocode API URL:
Same origin policy
JSONP and Google Maps API Geocoder - Not a Bug
This does severely diminish its usefulness when using client-side scripting. The only solution I found to this challenge was to create a server-side proxy service that relays the responses from the Google Maps Geocode API to my client-side script.
I wrote an extremely long-winded blog post describing this process.
Related
I'm creating a little web page containing a map using the Google Maps API and the OpenLayers JS library (v2.13). The base layer of the map is Google Street Maps, and that works fine, I can pan around the map, zoom in etc.
I'm adding a number of layers to the map, which come from a Web Mapping Service hosted on a remote server that I don't own or have access to (the code, I mean). And that works fine too, the layers overlay onto the Google Map just fine.
The problem is when I click on the layer features on the map, the WMSGetFeatureInfo controls are not working. No request is sent to the specified URL, no callback function is triggered, nothing happens at all. Here's some code:
var remoteGeoServer = "https://www.remotegeoserver.com/geoserver/wms";
var options = {
controls: [],
maxExtent: new OpenLayers.Bounds(420000, 485000, 770000, 985000),
projection: "EPSG:2157",
units: 'm',
scales: [5000000, 2500000, 1250000, 600000, 400000, 200000, 100000, 50000, 25000]
};
var map = new OpenLayers.Map('map-container', options);
// ... define styles, rules etc.
var overlayLayer = new OpenLayers.Layer.WMS("Overlayed Layer", remoteGeoServer, {<some_options>}); // other layers are created too...
var infoControls = {
click: new OpenLayers.Control.WMSGetFeatureInfo({
url: "/geoserver/wms",
title: 'Identify features by clicking',
layers: [overlayedLayer],
infoFormat: 'text/plain',
queryVisible: true
})
};
var baseLayer = new OpenLayers.Layer.Google("Google Streets", {numZoomLevels: 20});
map.addLayers( [baseLayer, overlayLayer] );
map.setBaseLayer( baseLayer );
for (var i in infoControls) {
infoControls[i].events.register("getfeatureinfo", this, showInfo);
map.addControl(infoControls[i]);
}
// ... and later
function showInfo(evt) {
console.log("Hurray!");
}
infoControls.click.activate();
I should explain that, even though the url in the infoControls is called "/geoserver/wms", there is no web mapping server running on my server. I just forward on the GetFeatureInfo request to the remote server that runs the actual geoserver. Otherwise the same-origin policy kicks in and the GetFeatureInfo requests are rejected.
But nothing happens, my "/geoserver/wms" endpoint never gets called. Am I doing something wrong? Also, and this isn't my main question, but why am I able to successfully give OpenLayers the remote server URL when creating the layers (should same-origin policy not apply - the map still works fine though, and the GET image requests to the remote server are successful).
Something else worth noting is that when I replace the local url path in infoControls with the remote server url, clicking the map triggers the same-origin policy error in the debug console.
I found a few different potential solutions for this problem that other people were having but none of them worked for me. In the end, the only thing that did work was this:
The URL used in the layer creation constructor(s) (in OpenLayers.Layer.WMS) MUST be the same as the URL used for the GetFeatureInfo constructor (in OpenLayers.Control.WMSGetFeatureInfo)
Otherwise, the map layers will work but the GetFeatureInfo requests will fail. I don't know if just the domain needs to be the same, or the full URL.
I'm not particularly happy about this. It means that I have to create an endpoint on my server that acts as a proxy and routes GetMap requests to the actual remote geoserver. I didn't need to do this before (why not - same-origin policy ??) and now it drives up latency for the layers showing up in the map (not to mention my server gets hammered with GetMap requests).
This seems to be an issue with OpenLayers (but I'm not sure of that). I'd be very interested in hearing about a solution if anyone knows of one.
What I'm doing and what I've tried
I'm using Google Geo coding web service to get the latitude and longitude of an address I pass, like this:
$.getJSON("http://maps.googleapis.com/maps/api/geocode/json?address=Boston&sensors=true",function(data){
//Do my programming here
});
It was, for some reason working perfectly, initially, but then it stopped working and and I discovered this was the issue, that is using Google client side API.
I also read the answer on that SO question, which says the <script> tags help get over the cross site scripting issue.
My problem :
I'm using RequireJs, so I include the files like this :
var Google = require("http://maps.google.com/maps/api/js?sensor=false");
So, how do I get my Google API working again and how do I do that with requireJS
requirejs will load the Maps-API asynchronous, but the URL you have used cannot be used for asynchronous loading(see async loading javascript with document.write) .
What to do: add the callback-parameter to the URL to be able to load the Maps-API asynchronously and execute the google-maps-related code in a function with a name equal to the value of the provided callback-parameter.
function geocodeaddr(){
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': 'Boston' }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
console.log(results[0].geometry.location);
}
else {
console.log("Geocoding failed: " + status);
}
});
}
require(["http://maps.google.com/maps/api/js?sensor=false&callback=geocodeaddr"]);
I am faced with a situation where the user does not want to allow the system to use the google location api to center the map on them.
I need to set a default value if the user opts out of using the location. Is there a way to accomplish this?
Yes there is! Google maps uses navigator.geolocation.getCurrentPosition() method to get your current location. This method allows a success callback (when a user says yes) and error callback (when user says no). It is also good to check if the browser supports geolocation. The following shows how to use it
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(hasGeolocation, noGeolocation)
} else {
geolocationNotSupported();
}
function hasGeolocation() {
console.log("yeah!")
}
function noGeolocation() {
console.log("user said no!")
}
function geolocationNotSupported() {
console.log("this browser does not support geolocation")
}
There is also an example on google maps api docs to show this in context of google maps usage
https://developers.google.com/maps/articles/geolocation
when you are initializing your map you are defining map options. Just define the 'center' option in this moment.
Sorry for a big edit. I am starting over as I am not stating my question correctly.
I am trying to write a client side app in HTML5. I do not want it to be hosted on a website. I am not even sure this is possible, I am fairly new to this type of application.
Anyway, I want to access Google services, which requires authenticate such as OAuth. Being that it is javascript, it sounds like OAuth2 is what I need.
I am trying to open up the google authentication in a popup (I have this part), let the user allow access, and then pass flow back to my application which can then query Google services. Problem is either 1. it asks the user to copy/paste a token into the app whenever I use response_type=code, but if I use response_type=token it requires that I redirect back to a valid URL which, since this is not hosted on a webserver, there is none.
So how can I use OAuth, and let the user grant access seamlessly?
You should have some Redirect URL defined for Google to redirect to after the authentication is done. If you cant host your pages on any web site, you can very well host it in local host.
Regarding getting the access token from the popup to the main parent window, you can setup a timer in parent window which keeps on checking the document location of the popup. Once the document location matches the Redirect URL, u can parse the access token which will will be in the URL itself.
I wrote a tutorial on exactly the same problem (using local host) yesterday and here is the link:
http://www.gethugames.in/2012/04/authentication-and-authorization-for-google-apis-in-javascript-popup-window-tutorial.html
Hope you will find it useful.
To avoid a potential click jacking, Google authentication forces you to go to a full page login. I don't think you can control that.
EDIT after comment, here is a code extracted from the Google OAuth2 page that does it:
<body>
<a href="javascript:poptastic('https://accounts.google.com/o/oauth2/auth?scope=https://www.google.com/m8/feeds&client_id=21302922996.apps.googleusercontent.com&redirect_uri=https://www.example.com/back&response_type=token');">Try
out that example URL now</a>
<script>
function poptastic(url) {
var newWindow = window.open(url, 'name', 'height=600,width=450');
if (window.focus) {
newWindow.focus();
}
}
</script>
</body>
I believe you can use google api (gapi) for Oauth in Javascript.
Here is the documentation: Authentication using the Google APIs Client Library for JavaScript
You will not require the user to copy/paste any codes and you will not require to provide a redirect uri
All you need to do is: Go to your project in Google Developers Console and generate the following:
1. Generate new Client Id and choose options 'Installed Application' and 'Other'.
2. Generate a Public API Key
Sample Code from the above documentation:
// Set the required information
var clientId = 'YOUR CLIENT ID';
var apiKey = 'YOUR API KEY';
var scopes = 'https://www.googleapis.com/auth/plus.me';
// call the checkAuth method to begin authorization
function handleClientLoad() {
gapi.client.setApiKey(apiKey); // api key goes here
window.setTimeout(checkAuth,1);
}
// checkAuth calls the gapi authorize method with required parameters
function checkAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult); // scope and client id go here
}
// check that there is no error and makeApi call
function handleAuthResult(authResult) {
var authorizeButton = document.getElementById('authorize-button');
if (authResult && !authResult.error) {
makeApiCall();
}
}
// API call can be made like this:
function makeApiCall() {
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function(resp) {
var heading = document.createElement('h4');
var image = document.createElement('img');
image.src = resp.image.url;
heading.appendChild(image);
heading.appendChild(document.createTextNode(resp.displayName));
document.getElementById('content').appendChild(heading);
});
});
}
I've written a mini JS library for the task, take it and see if it works for you.
https://github.com/timdream/wordcloud/blob/6d483cd91378e35b54e54efbc6f46ad2dd634113/go2.js
I am recently developing another project that rely on the same script, so I am isolating this one into an independent library project ... check the progress follows (if there are).
I have a page that renders a simple google map for a specified location. This map renders without any problems at all when I run it locally on localhost, however, when I deploy this code to our live web servers (using our LIVE google API key for the appropriate domain) it fails to render, and upon putting a series of alerts within the javascript on the page, it appears that the 'Initialize' method (which should be called within body onLoad) is not being called.
When I view the HTML source that is rendered on the live server it appears exactly as per the local version of the site (including the call to initialize() within the body onLoad event), albeit with the different maps API key.
I have output the host (alert(window.location.host);) to ensure that the key I generated via the google maps api site, corresponds exactly to the live server, which it does.
Does anyone have any ideas why it would be working locally but not when deployed to the live servers? The live site is hosted on 2 load-balanced web servers.
This is the javascript that is rendered:
<script src="http://maps.google.com/maps?file=api&v=2&sensor=false&key=ABQIAAAA-BU8POZj19wRlTaKIXVM9xTz76xxk4yAELG9u79oXrhnLTB5NRRvAZ-bkKn1x8J68nfRTVOIWNPJEA" type="text/javascript"></script>
<script type="text/javascript">
var map;
var geocoder;
alert(window.location.host);
function initialize() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("businessMap"));
map.setUIToDefault();
geocoder = new GClientGeocoder();
showAddress('St Margarets Street SW1P 3 London');
}
}
function showAddress(address) {
geocoder.getLatLng(
address,
function(point) {
if (!point) {
// Address could not be located.
jQuery('#googleMap').hide();
} else {
map.setCenter(point, 13);
var marker = new GMarker(point);
map.addOverlay(marker);
var html = 'Address info for the marker';
marker.openInfoWindow(html);
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml(html);
});
}
}
);
}
</script>
Any help would be much appreciated.
Thanks.
I've located the problem. It was some conflicting javascript related to Facebook Connect that was calling an FB Connect related page on window.onload, therefore not calling the initialize function within the body onload event.
I should have noticed this conflicting javascript earlier!