Category Archives: Google Maps

google.maps.LatLng spits out NaN

Well, this is one of those examples that shows it pays off to sleep a night over certain problems.

Yesterday afternoon I scratched my head over the Google Maps V3 API’s behaviour. All I wanted to do is change the zoom level of a map using the fitBounds() method. The latitude and longitude values were coming from a jQuery Ajax request that returned an JSON object. The object looked something like this:

{
     "bounds": {
          "sw":"-45.99579575,170.05493469",
          "ne":"-45.48679779,170.77724915"
     }
}

I tried the following script to set the new bounds:

var obj = jQuery.parseJSON(ajaxResponse);
sw = new google.maps.LatLng(obj.bounds.sw);
ne = new google.maps.LatLng(obj.bounds.ne);
newbounds = new google.maps.LatLngBounds(sw,ne);

Now, the result was that it completely caked the Map. The map literally zoomed (or panned, who knows?) to uncharted territory after which any interactions with it would not result in any updates. The map had effectively crashed.

WTF, why? I asked myself. So I checked what values google.maps.LatLng were producing. NaN. Not numbers obviously, but why? I chewed on that for half an hour before I decided to leave it for the day.

Now this morning I had a look at it again and it quickly dawned on me… the JSON data was delivered as a string but the google.map.LatLng class expects floats. So I altered the script accordingly:

var obj = jQuery.parseJSON(ajaxResponse);
var sw=obj.bounds.sw.split(','); // split string to allow parseFloat of data
var ne=obj.bounds.ne.split(',');
sw = new google.maps.LatLng(parseFloat(sw[0]),parseFloat(sw[1]));
ne = new google.maps.LatLng(parseFloat(ne[0]),parseFloat(ne[1]));
newbounds = new google.maps.LatLngBounds(sw,ne);
map.fitBounds(newbounds);

And lo and behold… it works.

Create marker with custom labels in Google Maps API v3

Took me quite some time to get to the bottom of how to put custom labels on markers (aka “placemarks”) in Google Maps API v3.

I must admit that I am quite puzzled as to why Google hasn’t built in this functionality in their google.Maps.Marker class. You can do a lot with that class like define a title via setTitle (i.e. creating a mouseover-tooltip for markers). Why a setLabel feature is missing… I don’t know. Instead it is necessary to write your own class – or at the very least search for a solution on some developers blog.

Now, finding a code snippet to create marker labels turned out to be more complicated than I thought. I always ended up on pages that dealt with MarkerClusterer, which primarily focusses on creating, well, marker clusters in maps that hold lots of markers. At first I tried to dig through the source code of MarkerClusterer to see how the marker labels were done. But I gave up when I realised that I had stared at the code for an hour without making any progress.

So finally I came across Marc Ridey’s blog post “Label overlay example for Google Maps API v3″. The post has the solution I was looking for, albeit in a bit of a chopped up form as some of the info required to understand what he’s done is actually explained somewhere in the depths of the extensive comments section. Some vital info about how to manipulate the zIndex of the label (to have it on top of the marker) and setting the custom label are also only found in the comments. Furthemore, the code snippet wasn’t styled up which made it a bit tricky to follow the code in the context of the blog post.

After some fiddling I came up with a version that suited my needs – i.e. place marker labels on top of each marker (rather then below) and have a customizable text (rather than the automatic label generation from the markers geographic position. So here it is, the slightly altered Label class:

// Define the overlay, derived from google.maps.OverlayView
function Label(opt_options) {
     // Initialization
     this.setValues(opt_options);
 
     // Here go the label styles
     var span = this.span_ = document.createElement('span');
     span.style.cssText = 'position: relative; left: -50%; top: -10px; ' +
                          'white-space: nowrap;color:#ffffff;' +
                          'padding: 2px;font-family: Arial; font-weight: bold;' +
                          'font-size: 12px;';
 
     var div = this.div_ = document.createElement('div');
     div.appendChild(span);
     div.style.cssText = 'position: absolute; display: none';
};
 
Label.prototype = new google.maps.OverlayView;
 
Label.prototype.onAdd = function() {
     var pane = this.getPanes().overlayImage;
     pane.appendChild(this.div_);
 
     // Ensures the label is redrawn if the text or position is changed.
     var me = this;
     this.listeners_ = [
          google.maps.event.addListener(this, 'position_changed',
               function() { me.draw(); }),
          google.maps.event.addListener(this, 'text_changed',
               function() { me.draw(); }),
          google.maps.event.addListener(this, 'zindex_changed',
               function() { me.draw(); })
     ];
};
 
// Implement onRemove
Label.prototype.onRemove = function() {
     this.div_.parentNode.removeChild(this.div_);
 
     // Label is removed from the map, stop updating its position/text.
     for (var i = 0, I = this.listeners_.length; i < I; ++i) {
          google.maps.event.removeListener(this.listeners_[i]);
     }
};
 
// Implement draw
Label.prototype.draw = function() {
     var projection = this.getProjection();
     var position = projection.fromLatLngToDivPixel(this.get('position'));
     var div = this.div_;
     div.style.left = position.x + 'px';
     div.style.top = position.y + 'px';
     div.style.display = 'block';
     div.style.zIndex = this.get('zIndex'); //ALLOW LABEL TO OVERLAY MARKER
     this.span_.innerHTML = this.get('text').toString();
};

To print labels I used the setMarker functions from the Google Map documentation example “Complex Icons” with the added label functionality:

var beaches = [
     ['Bondi Beach', -33.890542, 151.274856, 4],
     ['Coogee Beach', -33.923036, 151.259052, 5],
     ['Cronulla Beach', -34.028249, 151.157507, 3],
     ['Manly Beach', -33.80010128657071, 151.28747820854187, 2],
     ['Maroubra Beach', -33.950198, 151.259302, 1]
];
 
function setMarkers(map, locations) {
     var image = new google.maps.MarkerImage('images/beachflag.png',
     new google.maps.Size(20, 32),
     new google.maps.Point(0,0),
     new google.maps.Point(0, 32));
     var shadow = new google.maps.MarkerImage('images/beachflag_shadow.png',
          new google.maps.Size(37, 32),
          new google.maps.Point(0,0),
          new google.maps.Point(0, 32));
     var shape = {
          coord: [1, 1, 1, 20, 18, 20, 18 , 1],
          type: 'poly'
     };
     for (var i = 0; i < locations.length; i++) {
          var beach = locations[i];
          var myLatLng = new google.maps.LatLng(beach[1], beach[2]);
          var marker = new google.maps.Marker({
               position: myLatLng,
               map: map,
               shadow: shadow,
               icon: image,
               shape: shape,
               title: beach[0],
              zIndex: beach[3]
          });
          var label = new Label({
               map: map
          });
          label.set('zIndex', 1234);
          label.bindTo('position', marker, 'position');
          label.set('text', beach[0]);
          //label.bindTo('text', marker, 'position');
     }
}

A working example of above code can be found here.

Of course, there’s also an alternative method:

Google Maps only using paper and scissors !

Reverse Geocoding via the Open Street Map and Google Maps APIs

How do you get the corresponding street address details for a geographic location expressed as latitude and longitude data only? Oh, and it should be a server side procdure too (i.e. no JavaScript). Well, here a a couple of solutions for PHP.

1. Google Maps

The following HTTP GET request…

http://maps.google.com/maps/geo?q=37.34,-121.94&output=json&sensor=false

…spits out a JSON response from the Google servers:

{
     "name": "37.34,-121.94",
     "Status": {
          "code": 200,
          "request": "geocode"
     },
     "Placemark": [ {
          "id": "p1",
          "address": "1060 Highland Ct, Santa Clara, CA 95050, USA",
          "AddressDetails": {
               "Accuracy" : 8,
               "Country" : {
                    "AdministrativeArea" : {
                         "AdministrativeAreaName" : "CA",
                         "SubAdministrativeArea" : {
                              "Locality" : {
                                   "LocalityName" : "Santa Clara",
                                   "PostalCode" : {
                                        "PostalCodeNumber" : "95050"
                                   },
                                   "Thoroughfare" : {
                                        "ThoroughfareName" : "1060 Highland Ct"
                                   }
                              },
                              "SubAdministrativeAreaName" : "Santa Clara"
                              }
                         },
                         "CountryName" : "USA",
                         "CountryNameCode" : "US"
                   }
               },
               "ExtendedData": {
               "LatLonBox": {
                    "north": 37.3431416,
                    "south": 37.3368464,
                    "east": -121.9368374,
                    "west": -121.9431326
               }
          },
          "Point": {
               "coordinates": [ -121.9399850, 37.3399940, 0 ]
          }
     } ]
}

Accessing the JSON info via PHP is fairly easy:

$url = 'http://maps.google.com/maps/geo?q=37.34,-121.94&amp;output=json&amp;sensor=false';
$data = @file_get_contents($url);          //read the HTTP request
$jsondata = json_decode($data,true);       //parse the JSOPN response
if(is_array($jsondata) &amp;&amp; $jsondata['Status']['code']==200)    //check data
{
     $addr = $jsondata['Placemark'][0]['address'];
}
echo $addr;

There you go. There’s one catch, though. Google’s terms and conditions disallow using the JSON requests outside the context of a Google Map. So it can’t be used to automatically populate a database for example.

2. Open Street Map (OSM)

Open Street Map offers a legal alternative, albeit a limited and not as accurate one as the Google API. The JSON request URL for OSM is as follows:

http://nominatim.openstreetmap.org/reverse?format=json&lat=37.34&lon=-121.94

This returns the following – compared to the Google output rather simple – output:

{
     "place_id":"16539418",
     "licence":"Data Copyright OpenStreetMap Contributors, Some Rights Reserved. CC-BY-SA 2.0.",
     "osm_type":"way","osm_id":"8936657",
     "display_name":"Highland Court, Santa Clara, Santa Clara County, California, 95050, United States of America",
     "address":{"road":"Highland Court",
          "city":"Santa Clara",
          "county":"Santa Clara County",
          "state":"California",
          "postcode":"95050",
          "country":"United States of America",
          "country_code":"us"
     }
}

The corresponding PHP code is pretty much the same as already stated above but for clarity here is the whole block

$url = 'http://nominatim.openstreetmap.org/reverse?format=json&amp;lat=37.34&amp;lon=-121.94';
$data = @file_get_contents($url);          //read the HTTP request
$jsondata = json_decode($data,true);       //parse the JSOPN response
if(is_array($jsondata))                    //check data
{
     $addr = $jsondata['display_name'];
}
echo $addr;

As I said, the OSM data is not as accurate and for some regions doesn’t feature house numbers or suburb names. That is not to say that this won’t get better in time.

Compared to the Google request, OSM responses are quite slow, so parsing lots of addresses takes time. So PHP script execution time limits need to be considered.