Modest Mapsness

What's common across the various Modest Maps ports?

There are currently five ports in various stages of completeness / usefulness:

  • ActionScript 2.0 was the original interactive code base, and pages such as ClassDictionary mainly refer to this code.
  • ActionScript 3.0 started as a direct port of AS2, but has since become the leading edge of interactive development.
  • Python produces non-interactive map images, and has been a playground for the design of printed materials and overlaid data.
  • Javascript is an extremely minimal adaptation of the Python library for static maps in a browser context.
  • Processing is a somewhat minimal adaptation of the Python library, as well an interactive map which also served as a prototype for the subsequent as3 TileGrid rewrite. See here for more details.

Three Kinds of Geometry

There are several threads that run through all these codebases. The most important is the division of labor as it relates to geometry and geography. Every Modest Maps port has a concept of Location, Coordinate, and Point. Location is geographical, and generally consists of a simple (latitude, longitude) pair. Point is a screen coordinate in the rendered output map, and generally consists of a simple (x, y) pair. Coordinate is what binds Points and Locations together, and is a (row, column, zoom) triplet that corresponds to a specific geographical location at a specific zoom level.

The Map

The outermost class is Map, a basic rectangular container for a map image. At instantiation, a Map instance should know how big it is in screen dimensions, what map imagery it's showing, and what geographical spot it's centered on.

The correspondence between Point and Location is governed by the Map class. It exposes these methods:

  • pointLocation() converts from an (x, y) Point to a (lat, lon) Location.
  • locationPoint() converts from a (lat, lon) Location to an (x, y) Point.

Under the hood, this conversion is mediated by a map provider class and tile grid code, each of which speaks Coordinate as a shared language.

We generally use a set of functions with names like calculateMapCenter() and calculateMapExtent() to perform the initial map arrangement based on inputs like a Location and a zoom, or a set of Locations that must all be visible. These are somewhat free-form, but it can't hurt to start with the Python implementation and work out from there.

Map Providers

The correspondence between Coordinate and Location is handled by the Provider, an interface that converts between them with two methods:

  • locationCoordinate() converts from a (lat, lon) Location to a (row, column, zoom) Coordinate.
  • coordinateLocation() converts from a (row, column, zoom) Coordinate to a (lat, lon) Location.

Providers know what map projection they're dealing with, and what tile graphic corresponds to each Coordinate. In the AS3 and Python implementations, we're settling on Provider.getTileURLs(Coordinate) as a way to determine how to populate a given tile with appropriate images. Actual fetching and placing of the returned image URL(s) is left up to the tile grid or its equivalent. This is assuming that the provider corresponds to a simple tiled image map, which is usually (but not always) the case.

Providers also know about map bounds, how far it can be zoomed or panned in any direction.

The internal structure of each Provider classes doesn't change much from codebase to codebase, e.g. Microsoft VEarth road providers in Python and AS3.

Tile Grids

The correspondence between Point and Coordinate is most variable from implementation to implementation. Both ActionScript? codebases have a TileGrid class that places tiles on the screen, animates them when the map is moved, and generally does the heavy geometry involved in assembling and displaying the map you see. The code that performs the tile arrangement should be kept strictly separate from the code that understands geography. In the AS2 and AS3 libraries, the TileGrid exposes these methods:

  • pointCoordinate() converts from an (x, y) Point to a (row, column, zoom) Coordinate.
  • coordinatePoint() converts from a (row, column, zoom) Coordinate to an (x, y) Point.

This part of the code should be maximally responsive to its platform. Our ActionScript? implementations have large, complicated, formal TileGrid classes. The Python implementation just blits out a bunch of tiles to an image and forgets about them. Future platforms might have their own internal best practices for composing and controlling imagery, these should be used.