Google Maps - Geocode 100 addresses and calibrate the view port? - google-maps-api-3

I'm sure I'm dealing with a fairly common problem that's been solved many times before.
My web application requests about 100 line-delimited addresses of buildings from another service. I must now plot all these as gmarkers on a google map (with api version 3). I must also calibrate the view port to display all the gmarkers, that is determine the map center and the appropriate zoom value.
I found some code from the Google Maps API and tweaked it to plot one point:
function codeAddress() {
var address = '1 Yonge Street, Toronto, ON';
geocoder.geocode( { 'address': address}, geocodeCallBack);
}
function geocodeCallBack(results, status)
{
if (status == google.maps.GeocoderStatus.OK) {
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);
}
}
However, I suspect that performing 100 asynchronous geocoding calls may be slow. Does anyone have suggestion on the best way to achieve what I need?

Performing 100 Geocodes each time your page is loaded will take tens of seconds, so I suggest geocoding in advance.
If the addresses are always the same, or rarely change, you can geocode them in advance using the Geocoding Service (http://code.google.com/apis/maps/documentation/javascript/services.html) and temporarily store the resulting Lat/Lngs on your server as long as they are only ever displayed on a Maps API map.
Temporarily means that you must update these Lat/Lngs periodically (e.g. once every 30 days).
(See 10.1.3b for details: http://code.google.com/apis/maps/terms.html)

Related

Google Maps Reverse Geocoding for Natural Landmarks

On my webapp, I have a page with a Google Map. On the map I want the user to be able to click anywhere in the world, take the lat/long returned by the click event, and using the Google Maps API geocoding service reverse geocode it as is shown here to return whatever information Google has about that location. In the case of towns, streets, and countries; this works very well, but it does not work well for natural landmarks like oceans, rivers, etc. If you try to Reverse Geocode a lat and long in the middle of a lake , it will return information like the what county or state the lake is located in rather than the name of the Lake, which would be more helpful in my situation. To be more explicit, if I give a lat/lng in the middle of Lake Michigan, the reverse geocoding will return: "Mason County, MI, USA" rather than "Lake Michigan". However, if I click on a spot in the middle of Lake Michigan on https://www.google.com/maps, it will create a marker labeled "Lake Michigan".
Is there a google maps api service that I should be using besides the Geocode Service that will return Lake Michigan instead of "Mason County, MI, USA" if I give it a lat/long. Or is there another way I can get "Lake Michigan" returned from a click event without having to pass in a Lat/Long. The Places service seems to make a distinction between "natural" and "political" places (home addresses, business addresses, countries, cities, etc all seem to fall under the category of "political" places). So I am wondering if there are parameters I can pass to the Places Service from a click event that would return Places objects since the Geocode service does not seem to make the distinction between "political" and "natural" places.
Here is the code I have written so far that using the GeoCode Service:
const initMap = () => {
var map = new google.maps.Map(document.getElementById('location-map'), {
zoom: 3,
center: {lat: .433014, lng: 0.752724 }
});
var marker = new google.maps.Marker({
map: map,
anchorPoint: new google.maps.Point(0, -29)
});
var geocoder = new google.maps.Geocoder;
var infowindow = new google.maps.InfoWindow;
function geocodeLatLng(latLng) {
geocoder.geocode({'location': latLng}, function(results, status) {
console.log(results);
if (status === 'OK') {
if (results[0]) {
map.setZoom(10);
var marker = new google.maps.Marker({
position: latLng,
map: map
});
infowindow.setContent(results[0].formatted_address);
infowindow.open(map, marker);
}
}
})
}
map.addListener('click', function(e) {
geocodeLatLng(e.latLng);
});
}
The reverse geocoding ability of the Geocoding API as stated in the documentation:
The process of doing the opposite, translating a location on the map into a human-readable address, is known as reverse geocoding.
So the API not returning an address in the middle of a lake is working as intended. Similarly, the Places API is designed for finding businesses and points of interest, as answered in this bug on Google's Public Issue Tracker.
That said, there is a long-standing feature request to have the functionality added to the Geocoding API, similar to how maps.google.com returns geographic feature names such as "North Atlantic Ocean" when the map is clicked. There's no timeline if/when this will be implemented, but you can star the issue to show more interest in the feature and to receive updates.

Google Maps Street View "No Imagery" with status OK

I have been working with the Google Street View Image API and have an issue when api responds with "we have no imagery". I implemented a few solutions I found from previous posts, mostly from detecting "we have no imagery" of google maps street view static images. Here is my code:
var address = thisItem.addr + "," + thisItem.city + "," + thisItem.state + " " + thisItem.zip;
var url = "https://maps.googleapis.com/maps/api/streetview?size=500x500&location=" + address + "&heading=&pitch=&key=";
var itemLat = parseFloat(thisItem.lat);
var itemLng = parseFloat(thisItem.lng);
var sv = new google.maps.StreetViewService();
sv.getPanorama({location: {lat: itemLat, lng: itemLng}, radius: 1}, processSVData);
function processSVData(data, status) {
if (status === google.maps.StreetViewStatus.OK) {
console.log('status ok');
} else {
console.log('status not ok')
}
}
I am going to plug the url into an img src if there is a street view.
Because I am using the address and not lat lng in the url call, and lat lng in the getPanorama, when an image returns with "no imagery found" the StreetViewStatus is still OK so I am not able to set a stock image. I also changed to use the lat lng in the url and then an image will always show up even if the status is not OK. Any suggestions?
As discussed in Directly Accessing Street View Data
You may initiate two types of requests to the StreetViewService:
Request with a StreetViewPanoRequest, this returns panorama data given a reference ID which uniquely identifies the panorama. Note that these reference IDs are only stable for the lifetime of the imagery of that panorama.
Request with a StreetViewLocationRequest this searches for panorama data over a given area, given a passed LatLng.
So, for addresses that you used, I suggest that you should consider converting them using Geocoding Service.
Aside from that, these references might help you too
Using Street View Imagery
This Thread in Google Maps Help Forum
SO post - How can I tell if Google's Streetview Image API Returns “Sorry, we have no imagery here” (ie. NULL) Result?

Google maps direction renderer exact match only

I am using DirectionRenderer(gmap3) to show the user directions. The problem is it shows a match even if it cannot find an exact match. Eg: SomeFakePlace, myRealCity will match myRealCity even if it cannot match SomeFakePlace.
So it shows the directions from City's center to the place, instead. The destination is fixed(myLatLng)
I want it to return null and not show a route if cannot find one. I have decent error display to handle that.
$("#map-canvas-single").gmap3({
getroute:{
options:{
origin:$("#directions-from").val(),
destination:myLatlng,
travelMode: google.maps.DirectionsTravelMode.DRIVING
},
callback: function(results){
console.log(results);
var point= results.routes[0].overview_path[0]
window.directionMarker = new google.maps.Marker({
position: new google.maps.LatLng(point.jb,point.kb),
title:$("#directions-from").val(),
//icon:"http://maps.google.com/mapfiles/ms/icons/<?php if($this->listing->type=="pg"):?>green<?php else: ?>purple<?php endif;?>-dot.png"
});
window.directionMarker.setMap($(this).gmap3("get"));
if(!results)
noty({text:"Place not found!",type:"error"});
else
{
$(this).gmap3({
directionsrenderer:{
container: $("#directions-container"),
id:"directions",
options:{
directions:results,
suppressMarkers :true //<<Look here>>
}
}
});
}
}
}
});
The code works fine and all. I think this the fault of direction renderer service, not gmaps. I am sure htere must be some parameter for an exact match
I'm not familiar with Google Maps API, but what I'd do is do a geocode lookup on the source address and find the lat, lng. You'll usually get coordinates with levels of confidence, so you can have a minimum threshold below which to throw an error.
https://developers.google.com/maps/documentation/geocoding/
Also, don't forget mapquest.
http://developer.mapquest.com/web/products/dev-services/geocoding-ws

How to use Viewport from Google Geocoder in Leaflet JS?

I have successfully set up a Leaflet JS map that uses the Google maps geocoder to pan to a geocoded address. But I am also trying to use "map.fitBounds" to get the appropriate zoom level from the Viewport, but it does not seem to be working. The code I am using is:
map.panTo([results[0].geometry.location.lat(),results[0].geometry.location.lng()]);
map.fitBounds([[results[0].geometry.viewport.southwest.lat(), results[0].geometry.viewport.southwest.lng()],[results[0].geometry.viewport.northeast.lat(), results[0].geometry.viewport.northeast.lng()]]);
See example here: http://chrismccreath.hostzi.com/geocode_test.html
How can I fix it so that it zooms to the appropriate viewport returned by the google maps geocoder result?
There are no properties like results[0].geometry.viewport.southwest/northeast(I guess you were watching at the network-traffic inside the console, but what you see there will not be passed to the callback-function directly). To get the southwest/northeast use the methods getSouthWest() and getNorthEast() of google.maps.LatLngBounds .
results[0].geometry.viewport is a google.maps.LatLngBounds-object, but you can't use it directly in Leaflet.
You must "convert" it to an array or an Leaflet.LatLngBounds-object.
This should work:
map.fitBounds([
[results[0].geometry.viewport.getSouthWest().lat(),
results[0].geometry.viewport.getSouthWest().lng()],
[results[0].geometry.viewport.getNorthEast().lat(),
results[0].geometry.viewport.getNorthEast().lng()]
]);
Thanks for posting the working example. It helped a lot. It didn't work immediately for me however, had to do some tweaking for it to work.
function codeAddress() {
var address = document.getElementById('address').value;
geocoder.geocode( {'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0].geometry.viewport) {
map.fitBounds([[results[0].geometry.viewport.getSouthWest().lat(),
results[0].geometry.viewport.getSouthWest().lng()],
[results[0].geometry.viewport.getNorthEast().lat(),
results[0].geometry.viewport.getNorthEast().lng()]]);
map.setZoom(18);
} else if (results[0].geometry.bounds) {
map.fitBounds([[results[0].geometry.bounds.getSouthWest().lat(),
results[0].geometry.bounds.getSouthWest().lng()],
[results[0].geometry.bounds.getNorthEast().lat(),
results[0].geometry.bounds.getNorthEast().lng()]]);
} else { // give up, pick an arbitrary zoom level
map.panTo(results[0].geometry.location);
map.setZoom(15);
}
} else {
$('#result').html('Geocode was not successful for the following reason: ' + status);
}
});
}
Just thought I'd post it if anybody else wasn't able to figure out how to get it to work.
Thanks again!
DrMolle pointed out that leaflet map objects are not Google Maps API v3 objects (in the other question you reference). This works:
map.panTo([results[0].geometry.location.lat(),results[0].geometry.location.lng()]);
map.fitBounds([[results[0].geometry.viewport.getNorthEast()],
[results[0].geometry.viewport.getSouthWest()]]);
working example
As described in the documentation:
results[].geometry.location is a google.maps.LatLng, which panTo requires as an argument.
results[].geometry.viewport is a google.maps.LatLngBounds, which fitBounds requires as an argument.

How to search for cities in an area using Google Maps v3?

I tried to search for all cities within a visible map's bounds. How can I do that?
Below is what I tried to do:
$.fn.gmap3.geocoder.geocode({ 'address': 'Georgia' }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
globalMap.setCenter(results[0].geometry.location);
var resultBounds = new google.maps.LatLngBounds(
results[0].geometry.viewport.getSouthWest(),
results[0].geometry.viewport.getNorthEast()
);
globalMap.fitBounds(resultBounds);
// get cities in the map
var service = new google.maps.places.PlacesService(globalMap);
var request = {
bounds: resultBounds,
types: ['locality']
};
service.search(request, function (results, status) {
debugger;
});
}
});
But the result is ZERO_RESULTS. Maybe the reason is that the results are restricted to a radius of 50.000 meters?
Anyone knows how to solve my problem? Thanks a lot.
--UPDATE--
Thank, Sean, for reading my post carefully and give detail feedback.
This is how I refer to the lib:
src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=places"
I also added more detail to the geocode function to get more precise result. But I still don't get the results I want.
Check the list in this page: https://developers.google.com/places/documentation/supported_types, I realize that almost all items in the first list returns values but not for the second list. The only item return value is 'political' and it returns only 1 instead of 20.
This is my code after modifing:
this.setCenterByAddress = function (address, region) {
$.fn.gmap3.geocoder.geocode({ 'address': address, 'region': region }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
globalMap.setCenter(results[0].geometry.location);
var resultBounds = new google.maps.LatLngBounds(
results[0].geometry.viewport.getSouthWest(),
results[0].geometry.viewport.getNorthEast()
);
globalMap.fitBounds(resultBounds);
// get cities in the map
var service = new google.maps.places.PlacesService(globalMap);
var request = {
bounds: resultBounds,
types: ['country',
'administrative_area_level_1',
'administrative_area_level_2',
'administrative_area_level_3',
'colloquial_area',
'country',
'floor',
'geocode',
'intersection',
'locality',
'natural_feature',
'neighborhood',
'political',
'point_of_interest',
'post_box',
'postal_code',
'postal_code_prefix',
'postal_town',
'premise',
'room',
'route',
'street_address',
'street_number',
'sublocality',
'sublocality_level_4',
'sublocality_level_5',
'sublocality_level_3',
'sublocality_level_2',
'sublocality_level_1',
'subpremise',
'transit_station']
};
service.search(request, function (results, status) {
debugger;
});
}
});
}
MORE INFO: The same value return even when location and radius is used. And I use free map and receive "OVER_QUERY_LIMIT" all the time.
You shouldn't be limited to 50,000 meters unless you use the location and radius option, which you are not; you are using bounds. I suggest backing out a level and digging into the results that are returned from the starting call to geocode, because your usage of the PlacesService appears to be correct. What are the values internal to the resultBounds object? I also notice that you aren't using region biasing when you call the geocoder and it may be that "Georgia" is not sufficiently specific. For example, do you mean the region within the Russian Federation or the US state? And I'm not sure what URL you are using to load the Google Maps API & places library, but that could also be effecting your results.
I'd double-check the results coming back from the geocoder, because unless I am missing something, it looks like your basic approach is sound.
You can't return more than two near by localities around a particular latitude-longitude with Geocoding API/Places API
Please see this thread on Google Places API forum
This is not possible with the Google Places API as political results
such as locality, neighbourhood and sublocality are returned to
identify the area of the request and are limited to two per request.

Resources