Google Maps Without JavaScript

Static Maps

We recently did a small Google Maps application for ERGO insurance. It consisted of submitting all their offices to Google Maps. KML export of office data was used to create map at ERGO autoabi campaign site. First version used all JavaScript approach to create the sidebar and map on the page. I was not happy with it. Map page was empty for browsers with JavaScript disabled. Page also took too long to render. Two problems I could not ignore.

Second version was mixture of PHP, JavaScript and new static maps API. Map now works without JavaScript. It renders much faster too. Check the working demo to see yourself.

Importing KML

Google offers GGeoXML library for KML parsing. That I ditched in the beginning. It was impossible to create sidebar navigation with it. First I ended up using EGeoXML. It got the job done but as I said earlier was too slow.

To speed things up I parsed KML files and outputted sidebar HTML with PHP. Simplified HTML and PHP below. Do not mind about # hrefs. They will be replaced with something meaningfull later.

<h3><a id="cityname" class="city" href="#">ERGO cityname</a></h3>
<ul>
    <li><a  class="office" href="#" id="marker-1">Office 1</a></li>
    <li><a  class="office" href="#" id="marker-2">Office 2</a></li>
    <li><a  class="office" href="#" id="marker-3">Office 3</a></li>
</ul>
$kml = array("tartu.kml", "parnu.kml");

/* Used in id's so we can later bind click to correct marker. */
$counter  = 0;
foreach($kml as $file) {
    $city = str_replace('.kml', '', $file);
    $xml  = $xml = simplexml_load_file($file, null, LIBXML_NOCDATA);

    /* Print cityname as headline. */
    printf('<h3><a id="%s" class="city" href="#">%s</a></h3>',
            $city, $xml->Document->name);
    foreach ($xml->Document->Placemark as $placemark) {
        $coordinates = $placemark->Point->coordinates;
        list($longtitude, $latitude, $discard) = explode(',', $coordinates, 3);

        /* Save parsed KML as simpler array. We output this as JSON later. */
        /* Save also id so we can match clicked link ad marker.            */
        $markers[] = array("latitude"    => $latitude,
                           "longtitude"  => $longtitude,
                           "name"        => (string)$placemark->name,
                           "description" => (string)$placemark->description,
                           "id"          => "marker-$counter");

        /* Print officename as link to single office. */
        printf('<li><a class="office" href="#" id="marker-%d">%s</a></li>',
                $latitude, $longtitude, $counter, $placemark->name);
        print "\n";
        $counter++;
    }
    print "</ul>\n";
}

Putting Markers on Map With JavaScript

I gave the PHP parsed KML array back to JavaScript using JSON. Array is then looped passing data to addMarker() method. This method creates new marker, puts it on map and binds event to open new infowindow when marker is clicked. Simplified code below:

$(function() {

    /* Output parsed KML files as JSON */
    var markers     = <?php print json_encode($markers) ?>;
    var marker_hash = {};

    if (GBrowserIsCompatible()) {
        /* Init and center at Tartu. */
        var map = new GMap2(document.getElementById("map"));
        map.addControl(new GSmallMapControl());
        map.setCenter(new GLatLng(58.38133351447725, 24.516592025756836), 12);

        for (var i=0; i<markers.length; i++) {
            var current = markers[i];
            var marker  = addMarker(current);
            marker_hash[current.id] = {marker : marker};
        }
    }

    function addMarker(current) {
      var marker  = new GMarker(new GLatLng(current.latitude,
                                            current.longtitude));
      map.addOverlay(marker);
      GEvent.addListener(marker, 'click', function() {
          var html = '<h3>' + current.name + '</h3><p>' +
                     current.description + '</p>';
          marker.openInfoWindowHtml(html);
      });
      return marker;
    }

});

Make It Work With JavaScript Challenged Browsers

Inside the map <div> there is an <img> tag which points to Google Static maps api. You can pass needed parameters in URL and Google generates map as static image. For example:

http://maps.google.com/staticmap?&zoom=12&size=688x300
&center=58.378700,26.731110&key=GOOGLE_API_KEY

Generates the following map:

What we do here is really simple. In sidebar links we pass needed parameters in querystring. PHP then writes these parameters into <img> tag pointing to static maps api. Instead of center we pass markers parameter. It contains coordinates to one or more markers. Map api then centers map automatically. This comes especially handy when there is multiple markers.

For example link:

<a href="?markers=58.384933,24.499811,red|58.384731,24.507816,red|">

becomes the following img tag:

<img src="http://maps.google.com/staticmap?size=688x300
&markers=58.384933,24.499811,red|58.384731,24.507816,red|
&key=GOOGLE_API_KEY" />

which looks like this:

What we need to do is to update the code which generates sidebar html. I cut out lots of code for sake of clarity here. Important line is one with printf().

foreach($kml as $file) {
    ...
    foreach ($xml->Document->Placemark as $placemark) {
        ...
        printf('<li><a  class="office" href="?zoom=14&markers=%s,%s,red"
        id="marker-%d">%s</a></li>', $latitude, $longtitude, $counter,
        $placemark->name);
        ...
    }
    ...
}

Each link now shows you map with single marker centered on map. Creating link for each city is requires one extra step. Each city contains multiple markers. Before creating the sidebar we loop through KML once to to build a querystring for marker parameter.

/* Prebuild querystring for all markers in a city. This is needed  */
/* to make a link which contains all markers in query string.      */
foreach($kml as $file) {
    $city = str_replace('.kml', '', $file);
    $temp = 'markers=';
    $xml = $xml = simplexml_load_file($file, null, LIBXML_NOCDATA);
    foreach ($xml->Document->Placemark as $placemark) {
        $coordinates = $placemark->Point->coordinates;
        list($longtitude, $latitude, $discard) = explode(',', $coordinates, 3);
        $temp .= sprintf('%s,%s,red|', $latitude, $longtitude);
    }
    $markers_string[$city] = $temp;
}

We also need to make small change code building the sidebar.

foreach($kml as $file) {
    ...
    /* Use prebuild querystring in city links. */
    printf('<h3><a id="%s" class="city" href="?%s">%s</a></h3>',
    $city, $markers_string[$city], $xml->Document->name);
    ...
}

Now we have working map with sidebar. Biggest difference is missing infowindows (bubbles). They would be perfectly doable but I left them out for a reason now.

You can find source code to the working demo from svn. There was couple of cosmetic things I did not describe here so check the source. ERGO Autoabi minisite still contains both original and improved versions of the map.

Static maps api is really great. I have only one complaint. Maximum size of 512x512 is a bit too limiting to my taste.

UPDATE: Google recently increased the maximum size of static map to 640x640. Thank you!

Related entries: Google Maps Without JavaScript Part 2, Clickable Markers With Google Static Maps, Infowindows With Google Static Maps.

Posted in

PHP Maps