Using Google Maps to Show Geocaches

For some time, I've wanted to be able to build my own maps showing cache locations. One of the biggest obstacles was finding a good source of map information. There are plenty of online map sources, but many of them are very old (like the USGS Topographic maps), or really aren't very pleasing to look at (Mapquest, etc.)

Recently, however, Google opened up their beta mapping service (at maps.google.com. It's fast, it's easy, it has terrific-looking maps, and it's actually very easy to grab individual map images directly from Google. Of course, these images are copyrighted -- the data comes from NAVTEQ and TeleAtlas, and the renderings are (presumably) created and copyrighted by Google. So you shouldn't abuse these maps, lest they become harder to grab. And you absolutely shouldn't use them commercially, in any way.

Update 4/29 - The first part of this hack, the map grabber, is now available. Check out the amazingly-underutilized downloads page.

Fetching Maps

So I began digging into how the maps worked, and it turns out that it's actually fairly simple. The helpful details are buried deep within a javascript file that's downloaded when you load a map. Essentially, Google set a position as the "Center" of the US Map (it may actually be close to the "geographical center" of the continental US, but I'm not sure). This point is defined as the origin on a cartesian plane. Locations east of the center have a positive X value, points west, negative. North is also negative, while south is positive. (a little bit of a flip from traditional geometry).

Google maps are available in several different zoom levels -- from 0 (very very close to the street) to 14 (showing almost all of North America). These zooms are exponentially related -- zoom level 2, for a given size image, shows twice as much area as zoom 1, which shows twice as much as zoom 0. For each level, there are a certain, predictable number of "pixels per degree." Technically, this varies slightly for longitudinal measurements, as the distance between lines of longitude decreases as you approach the poles, but Google uses an approximation that works well for the continental US (and, now that I think of it, is probably a result of the map projection used).

So now you've got 14 different map images, from a fairly small one about 300 pixels across for zoom level 14, to a massive image, several million pixels across, for zoom level 0. Obviously, you can't send the entire image to the browser and expect it to scroll around, so they break the image up into 128x128 pixel tiles. When you drag the map around in your browser, new tiles are added to the edge of the image as necessary, giving the impression of a single, continuous image.

So how does the browser know which tiles to request? That's the magic. Let's walk through it for a map centered on 38 57.000 N by 77 23.000 W. Of course, we should convert that to a decimal measure, which works out to about 38.95 N, 77.383 W.

First, we figure out the relative location for that position (the distance between the desired point and the map center). To make things simple, I'm dropping the sign from all longitude components -- of course, this'll break any tools we build once Google expands beyond the Western Hemisphere, but I'm willing to live with that. Given the pre-defined map center of 98.35 degrees west longitude and 39.5 degrees north latitude, we find that our target location is 20.967 degrees east of center (98.35 - 77.383), and 0.55 degrees south of center (39.5 - 38.95). So the Google coordinates are (20.967, 0.55) degrees.

Now we must convert that measurement into pixels. To do that, we have to know what zoom level we want for our map. Neighborhood streets show up on the map at about zoom level 5, but for this demonstration I want something a little closer, so we'll go with zoom 4 (which shows townhome / apartment complex streets). At zoom level 4, there are 8192 pixels per degree. How'd I get that? There's a nifty little formula buried in Google's javascript. The number of pixels per degree, at zoom Z, is given by 2^(17-Z). In this case, zoom 4 means we're at 2^(17-4) == 2^13 == 8192 pixels per degree. Multiply this number by the Google Coordinate and you get (20.967, 0.55) * (8192) == (171761, 4505) (I dropped the fractions).

But that's not entirely correct, because the map projection that Google uses isn't exactly square. Degrees longitude (X pixels) are slightly closer together than degrees latitude, and Google defines a 14-digit fraction for that ratio. I'm just going to call it 0.771 for now, 'cause I don't feel like typing out the whole thing over and over again. So we multiply the X coordinate by 0.771, and end up with (132427, 4505) as the final location for our target coordinate, on the massive zoom 4 map image.

Finally, we have to figure out what tile that position is on. To get that, we divide each coordinate by 128 (because each tile is 128 pixels on a side). We end up, after all this work, with (1034, 35). So if you try to fetch that tile from Google, like this, you should find a small map image with your target coordinate somewhere inside it (it won't necessarily be in the exact center of the tile, btw).

Now, it's just a question of grabbing tiles around your target tile, and assembling them into a single image. Some perl magic (along with ImageMagick's montage tool) will eventually get you something that looks like this:

Locating Caches

Now for the fun part! Geocaching.com offers something called "Pocket Queries," which are pre-defined queries that run automatically and send you, in email, a big horkin' GPX file. Contained within that GPX file are all the caches that match that particular query's parameters. Since it's an XML-formatted file, it's easy to parse with any of a million XML tools. However, since I'm lazy (and not releasing this ugly hack anyway), I just parse the files by reading line-by-line.

A waypoint entry in the GPX file starts with the tag "wpt", which has attributes for the latitude and longitude for that waypoint. Subsequent tags include "time" (the date the cache was placed), "name" (the waypoint ID), and a "groundspeak:cache" tag (that includes an "available" attribute). Also contained within the top-level wpt tag are multiple "groundspeak:log" entries, including "groundspeak:finder", with an "id" attribute identifying the finder for that entry.

By parsing all these, I end up with a simple comma-separated file that looks like this:

GCJD40, 38.997733, -77.0618, 285, True
GC6781, 38.89325, -77.17225, 971, Found
GCGPEK, 38.885717, -77.1735, 548, False

These lines show a waypoint, its coordinates, the age of the cache in days, and a status field. In this case, "True" is for any normal, available cache, "False" means that the cache is for some reason unavailable (maybe it got muggled or washed away), and "Found" means that I personally have found that cache.

Plotting the Caches

Now, finally, I need to plot these points on my maps. To make things easy, my "Grab Map" script embeds the coordinates and zoom level inside the map's comment field. My "Map Caches" script opens up each map in turn, reads the parameters for that map, and figures out which of the caches from my "Parse GPX" script actually appear on that particular map. Then, it uses the GD graphic library module for perl to open the map into memory and add little colored circles wherever I need 'em. The coordinates for each cache is figured along the same lines as determining Google map tiles -- find the distance, in degrees, from the map edge to a point, convert that to pixels, and then draw a circle at that pixel location.

I've also color coded the map. Right now, I'm using green for caches I've found, red for those I haven't, and yellow for caches that are currently unavailable. I also thought it'd be good to see cache age displayed somehow, so for caches I haven't found, blue is brand new (less than 15 days old), cyan is relatively recent (placed within the last 90 days), and purple is for caches placed in the last year. I think I'll also try to display locationless finds as a triangle or something, because I've already confused myself trying to remember what some of the marks represent. Also, I need a better color for unavailable caches (it matches the large roads too closely).

UPDATE: I now use triangles (the international "Lookey Here" symbol) for unavailable caches, with the same red / blue / green color scheme. Not sure what I'll use for locationless, then...

The final result looks something like this:

What's Next?

This was a fun hack, and could actually be relatively useful for me. I currently have a big map of Fairfax County as my computer background, and already I'm planning which caches to visit for my upcoming "March Madness" cache-a-thon (trying to do a cache a day for the month of March). I'd like to get these automated, so every week when new PocketQueries arrive in my email, it automatically generates new maps and updates my desktop background. I'm also thinking about wrapping an HTML file around the map image, so I can hover the mouse over each cache icon and get the cache's name and other useful information. It might also be fun to build an animated map showing caches found over time -- as new caches appear and as I knock them down. If I end up building anything else cool, I'll try to post it here...

UPDATE: I just found, on another site, that you can specify latitude and longitude directly in a URL: http://maps.google.com/?sll=38.950,-77.383. You can't specify the zoom right off (it seems to jump straight to level 4), and there's no marker to show where your requested location actually is, but it's an easy way to get a map based on lat/lon. Even more simply, you can just enter a decimal coordinate in the search field, like so: 38.950, -77.383. Still no marker, but it gets you in the ballpark.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

URLs for Google maps

You can actually do all kinds of neat stuff with the URLs for Google maps other than just embed the lat and lon. Here is a sample URL:

http://maps.google.com/maps?q=45.895638+-66.589333+(Capital+City+Paintball)&spn=0.05,0.05&hl=en&t=0&iwloc=A

and here are the parts:

lat and lon: q=45.895638+-66.589333
Comment for info window: (Capital+City+Paintball)
Zoom Level (width of map): &spn=0.05,0.05
language of map (en=english, fr=french): &hl=en
map type (0=street map, k=satellite, h=hybrid): &t=0
Info window status (A=open, B=closed): &iwloc=A

I like to use Excel to build URLs for Google Maps. Send me an e-mail and I'll send you a copy of my Excel Spreadsheet.

Bernie.

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Excel spreadsheet to build URLs for Google Maps

Bernie-Could you please email me a copy of your spreadsheet? Just take the "deleteme" section out of my Yahoo email. Thank you!

Freddie

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Hoping to get Excel Spreadsheets

Hi, Ben. Hi, Bernie. I wonder if I could get a copy of each of your Excel sheets? I'm just starting to get into Google Maps and I'm really in the market for a more automated approach to creating valid URL's.

Additionally, if either of you (or anyone else) knows any other info about the syntax of the URL, it would be greatly appreciated if you could enlighten me (or others?).

My email address is fdwojo@REMOVE.gmail.com

Just get rid of the "REMOVE." portion, please. Thank you (in advance) very much!

Frank

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Does anyone know the syntax

Does anyone know the syntax for the (Comment) trick to label the info window with mutliple lines of text, e.g.

http://maps.google.com/maps?f=q&hl=en&q=4+Yawkey+Way+boston,ma(Fenway%20Park%20Go%20Red%20Sox%20)

Where Fenway Park would be on one line and
Go Red Sox would be on the second (in the info window)?

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Send the excel please!

Bernie,
That looks like a good way to do it because my stuff is already in Excel. I'd appreciate seeing yours. Ben

benlev@aol.com

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

URL

Nice work! I'd like a copy of your Excel. I've been calling google maps from my webpage to get driving directions to various bridges. I use Excel to produce the hyperlink but hadn't realized you could add a comment. Nice.

Do you know if its possible to put an image in the info window this way?

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Man, I do

Man, I do the same thing whith google maps, hehe.
Nice work! I'd like a copy of your Excel. I've been calling google maps from my webpage to get driving directions to various bridges. I use Excel to produce the hyperlink but hadn't realized you could add a comment.

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Excel Spreadsheet for Google Map URLs

Frank,

you didn't include your e-mail address in your posting.

Bernie.

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Great resource. I've looked

Great resource. I've looked through the GrapMap script, but I can't determine how you know the coordinates of a map tile's corner/edge. (The retrieved tile just has the requested coordinate somwhere inside.) So I'm not sure how to do the "find the distance, in degrees, from the map edge to a point" part to achieve something similar to your "MapCaches" script.

Can someone explain this, or David, would you mind sharing the solution or MapCaches script?

Thanks again for the resource,
James

Got it

Apologies for being "that guy" who asks a question then figures it our a few minutes later. Anyway, mabye it'll give the page a bump.

TL, TR, BL, BR are the corners in the script... Seems to work like a charm. I was converting the script to C# and made one of those int/double errors.

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Latest Version

So, after hours of coding and scouring the google source code...here is the code for getting the latest coordinates for the tiles based on lat & lon

first we need to fill a few arrays

c = 256

for (d=17;d>=0;--d) {
pixelsPerLonDegree[d] = c / 360
pixelsPerLatDegree[d] = c * (2 * Math.Pi)
e = c / 2
bitmapOrigin[d] = New Point(e,e)
c*=2
}

after we fill this we can get the coordinates by plugging in

d = New Point(0,0)
d.X = Math.Floor(bitmapOrigin[zoom].x + lon * pixelsPerLonDegree[zoom])

e = Math.Sin(x * (Math.Pi/180))

if (e > .9999) e = .9999
if (e < -.9999) e = -.9999

d.Y = Math.Floor(bitmapOrigin[zoom].y + .5 * Math.Log((1+e) / (1-e)) * -pixelsPerLonRadian[zoom])
d.X = Math.Floor(d.x / 256) //tileSize
d.Y = Math.Floor(d.y / 256)

d.X = the bitmaps X value, d.Y = bitmaps Y value

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Sorry...

e = Math.Sin(x * (Math.Pi/180) shoudl read...
e = Math.Sin(lat * (Math.PI / 180))

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

getting maps

This page is obsolete. Most of the techniques no longer work. For some reason, Google changed their query system. This is possibly because they don't want people to get the images, but maybe because they changed the coordinate system to support the entire earth. Any one know the new ways of querying the images?

Any help would be MUCH appreciated!!!

Blaine

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

It works if you change the

It works if you change the address form:
http://mt.google.com/mt?v=.1&x=1034&y=35&zoom=4
To this:
http://mt.google.com/mt?v=.3&x=1034&y=35&zoom=4

I only change the 'v' value. I think the v is the version.

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Yeah, I know...

...I've been toying with the code, off and on, but haven't really done much lately. I'd like to update it to work with the new maps, but they've really changed a lot. For one thing, they've changed the map projection (or so I've read), so the math I used to figure out where coordinates lie is a bit more complex.

Also, the second half of the hack (the plotting of the geocaches) was never really cleanly done. I still want to fix that up somewhat, maybe split the pocketquery parsing and map generation into two different bits. I'd like to get it to where you could use any map image, no matter what the source, and if you input the data for the map (resolution, coordinate boundaries, etc.), it would then plot stuff. That way, you could use any of the various Google maps, or Terraserver, or even hand-scanned maps.

Anyway, that's the plan. Along with all the million other things I have going right now... :) Best I can say, is check back every month or so...

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

I wrote a simple program whic

I wrote a simple program which:
1. converts latitude and longitude to image number for kh.google.com
2. downloads images from kh.google.com
3. stitches downloaded images to one large satellite image
4. stamps the latitude and longitude of image corners

If you are interested you can find it here
http://www.livejournal.com/users/piterpennet/94693.html#cutid1

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Getting a marker for a given latitude and longitude

One hack to get google maps to display a marker at a given latitude and longitude is to ask google for driving directions with from and to being the same place.

http://maps.google.com/?q=from+40.09086%2c-82.95061+to+40.09086%2c-82.95061

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Another approach...

I've recently setup a page on my site that maps all the geocaches I've found with google maps...here's a link to how I made it work: http://www.cplee.org/archives/000092.html

Enjoy!
Casey

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Satellite images?

David, your website/blog have been a BIG help! Thanks!! By the way, do you know what I need to change to the google map query to recieve each individual satellite image?

Thanks in advance!

Blaine

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Keyhole Image URLs

How do you make a keyhole image URL? Well, it's a bit weird. The URL looks like this: http://kh.google.com/kh?v=1&t=tqstqrtqqrsrqs. What that really represents is a quad-tree descent path to the selected tile. Put in English, it's like this:

It's all a bit weird. Here is a simple web page that demonstrates it. Just click in the image to zoom into that quarter of the image.

Anyway, how do you convert a lat/lon to a tile coordinate? I've no idea. I haven't even sat down to figure out how to walk back and forth around a given tile, once you're there. I saw a posting on some blog right after Google added the satellite images, but I can't find it this morning. If I manage to dig it up, I'll try and post it here.

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Any plan to add the rest of y

Any plan to add the rest of your scripts?

Thanks! Love the grabber script.

--deathster
deathster@dodgeit.com

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

more scripts

Yeah, I'm still hoping to get to it. As you can see from the rest of my site, I've been a little busy lately. :)

Right now, the scripts are pretty ugly and non-modular. I've been kicking around some ideas for breaking it into smaller, cleaner chunks, and just stringing them all together in a big pipeline.

best wishes

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot

Zoom in query string

You can specify both zoom and lat/long in the query string... the parameter for zoom is simply "z".. so:

http://maps.google.com/?sll=37.616%2C-115.816&z=10

Amal ;)
www.amal.net

good day

Anti-Anxiety Aromatherapy Recipe Blend:3 drops neroli,2 drops patchouli,2 drops geranium,2 drops rose,2 drops ylang-ylang,1 drop frankincense,1 drop bergamot