X
Prev    Next

Creating a Google Photo Map (2007-05-27)

2007-05-27

Updated May 28, 2007

Recently I updated ImageIngesterPro to automatically tag images with GPS coordinates recorded from a GPS device, such as a Garmin 60CSx (many other devices work just as well). You can read the details on my ImageIngester Blog.

Naturally, that led me to think about putting the images on a Google map like this one, created from images I shot from the car this morning as I was driving around (click on a marker to see the photo; click on the photo to see it full-size):

(Map no longer available.)

Google Maps has a powerful API for creating maps like these, and it's pretty easy for a JavaScript programmer to figure out how to use it. There are some applications that claim to generate the necessary HTML and JavaScript (e.g., RoboGEO), and even some web sites that claim to do it. I didn't try the applications (I couldn't find one for the Mac anyway), but I did try some web sites such as Flickr and Pixagogo. The problem with the web sites is that they either want to put your photos on some giant community map, or they require you to enter in the coordinates for each photo, assuming that you have no way to tag the photos themselves. (But, of course, you do.)

So I decided to write the code myself. Unfortunately, while it's easy to use, it requires an environment that few photographers will have access to: A web server running PHP with EXIF support enabled. If you do have such a system, you can try this script. It takes the JPEGs in a subdirectory called "images", extracts the latitude and longitude, and builds the map. All you have to do to get it running is type in your own Google Maps API key, which you can get for free from Google (instructions at the top of the script, below).

I used the script to build the example above, but even my own hosting company doesn't have EXIF support enabled in their PHP system. (I'd be surprised if any do. Tell me if you know of one. Update: There are a bunch. See next Blog entry.) So, I ran the script on my Mac, which came with EXIF support already enabled. Unfortunately, although Macs come with PHP already installed, it's not configured by default into Apache (the web server), and that takes a few steps that are simple for people familiar with configuring PHP and are too difficult to explain (here, anyway) for anyone else.

In fact, you don't even need PHP installed into Apache if you're willing to put up with less automation. On any OS X system, set up the script in the parent of the "images" directory (as explained in the comment at the top of the script), make that directory (the parent) the current directory, open up a Terminal window, and then do this:


$ php gpsmapper.php >photomap.html

This gives you the HTML file photomap.html for the specific photos in the "images" directory. Copy that file and the images folder (with its contents) to any web server, even if it doesn't have PHP, and it will run OK.

Consider this script a work-in-progress. It's what I was able to pull together in a few hours today. I'm working on a much more convenient scheme that photographers who aren't also programmers can use. Stay tuned.

Note: Anyone who actually reads this script will be rewarded with a joke. Despite what you may have heard, programmers really do know how to have fun!


<?php
/*
To use this PHP script:

1. Get access to a web server running PHP with EXIF support enabled (compiled with --enable-exif).
The PHP on Mac OS X seems to come that way, but it isn't configured by default to run in the Apache
web server that comes with OS X. You'll have to do that yourself.
2. Put GPS tagged JPEGs in a directory on your Web server named "images".
3. Put this file in the parent directory of the "images" directory.
4. Get a free Google Maps API key from www.google.com/apis/maps/signup.html.
5. Replace the key below with your key (use a text editor).
6. Run this script.

What's needed is a way to do this without PHP or any other scripting approach that requires installation
and configuration. I'm working on that.

Copyright 2007 by Marc J. Rochkind. All rights reserved, except that this script may be used under
the BSD license posted at basepath.com/aup/copyright.html.

*/

mapit(scandir("images"));

function sexagesimal_to_float($x) {
eval('$f = ' . $x[0] . "+" . $x[1] . " / 60 + "  . $x[2] . " / 3600;");
return $f;
}

function mapit($files) 
<title>Google Photo Map (from www.basepath.com)</title>
<!--
****** Change the key below to your personal Google Maps API key.
****** You can get a free key from www.google.com/apis/maps/signup.html
****** Carefully replace the letters replace_this_with_your_key with your key.
-->
<script src="http://maps.google.com/maps?file=api&v=2&key=replace_this_with_your_key"
type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[

var images = new Array(
EOT;
// UFPs (Unidentified Flying Photos) are moved to Area 51.
$lat_area51 = 37.405;
$lon_area51 = -116.24;
$first = true;
$centerOnArea51 = false;
for ($i = 0; $i < count($files); $i++) .html"> empty($exif_lon)) {
$centerOnArea51 = true;
$lat = $lat_area51;
$lon = $lon_area51;

else {
$lat = sexagesimal_to_float($exif_lat);
if ($exif["GPSLatitudeRef"][0] == "S")
$lat = -$lat;
$lon = sexagesimal_to_float($exif_lon);
if ($exif["GPSLongitudeRef"][0] == "W")
$lon = -$lon;

if ($first)
$first = false;
else
echo ",";
echo "\n";
echo <<<EOT
new Array("$photo", $lat, $lon)
EOT;
}
echo <<<EOT
);
var area51 = null;

EOT;
if ($centerOnArea51)
echo <<<EOT
area51 = new GLatLng($lat_area51, $lon_area51);

EOT;
echo <<<EOT
function load() {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
for (i = 0; i < images.length; i++) {
p = new GLatLng(images[i][1], images[i][2]);
if (i == 0)
if (area51 == null)
map.setCenter(p, 15);
else
map.setCenter(area51, 13);
m = new GMarker(p);
m.image = images[i][0];
map.addOverlay(m);
}
map.setMapType(G_HYBRID_TYPE);

EOT;
echo <<<EOT
GEvent.addListener(map, "click", function(marker, point) {
marker.openInfoWindowHtml(
marker.getPoint().toString() +
"<p><a target=_blank href='" + marker.image + "'><img src='" + marker.image + "' width=200 /></a>");
});
}
}

//]]>
</script>
</head>
<body onload="load()" onunload="GUnload()">
<div id="map" style="width: 600; height: 600px"></div>
</body>
</html>

EOT;
}
?>