## Tom MacWright

tom@macwright.org

# Interacting with Image Maps

Update: This post was published in 2012. As of 2017,
• I ended official support for TileMill in 2016. The Mapbox way to make this kind of map is now Studio. TileStream Hosting is now just called Mapbox.
• The math in this post is still relevant, but the library I'm using here - Modest Maps - no longer is. You would use Mapbox GL JS instead now.

Last week I wrote Images as Maps, which talked about one technique of tricking spatial software like TileMill into displaying plain old images as maps. The effect is making commonplace images accessible with stuff like MapBox. Versus a ‘commercial solution’ like Zoomify, it’s a bit of a stretch but has potential benefits - cross-browser, open-source Javascript libraries, strong infrastructure. Plus, misusing systems is fun.

The first step is input, or data: and this is handled by the `togeo.py` script. It tells TileMill that your source image is in Spherical Mercator, and thus doesn’t need to be skewed or stretched at all.

## But what about navigating within these maps?

So, the vast majority of map navigation is via latitude and longitude values. To create and center a map, you usually do something like

If the map was in the ugly plate carrée projection, then just using pixel values scaled into latitude-longitude would work, but nearly all web maps are made in good old spherical mercator, which does not have a 1:1 relationship between lines of latitude and pixels.

The first issue is what to do about bounds. The bounds of Spherical Mercator are the corners `-20037508.34, -20037508.34, 20037508.34, 20037508.34` - which are technically expressed in ‘meters’ but don’t take that literally. My script chooses to fit the image as best it can within the bounds, and center it in the other direction, if the aspect ratio is not 1:1.

the three basic placements of togeo

And so now given one measurement - the original pixel size of the image, we can derive the new geographical location of any pixel location in that image - what I’ll call an ‘image pixel’. The word ‘Pixel’ is a bit overloaded, since there are screen pixels, the `MM.Point` class for any x,y value, etc. Image pixels will refer to absolute pixel locations on an image, like you’d see in Photoshop’s info window. We do this with coordinates and just a little bit of magic.

## Coordinates

I mentioned coordinates in how web maps work: they’re a data type that tells you the column, row, and zoom level of places in a map, like you might have seen in `/0/0/0.png` URLs. The great thing about coordinates is that they’re simple - they’re in simple x/y space, where the ‘0’ tile - the tile that represents the whole world in one 256x256 image - has potential x & y values from 0 to 1 - so the center of that tile is the coordinate `{ row: 0.5, column: 0.5, zoom: 0 }`.

So, let’s start coding. First this code needs to know about the size of the image. We could create an object to do this, but it’s simpler to use a javascript closure - in this case a function that returns another function having scoped a few values.

I’ll split up the implementation into two functions. The first converts an image pixel into a map coordinate:

The second function converts an image pixel into a geographical location with lat/lon values.

Why split this into two sections? The `map.coordinateLocation` call is somewhat expensive - it creates four objects per call. This shouldn’t matter for 95% of uses, but if you’re doing tons of calls per second, you’ll start to run into garbage collection hassles.

Anyway, on to a demo:

See this inline with source on bl.ocks.org