Funny question. Here's how I do it.
I gather whatever geometry I need in whatever formats they come in. I've been pulling data from USGS, so that amounts to a bunch of:
- SHP Files (ESRI Shapefile Technical Description)
- DBF Files (Xbase Data file (*.dbf))
I then wrote a program that "compiles" those shape definitions into a form that is efficient to render. This means doing any projections and data format conversions that are necessary to efficiently display the data. Some details:
- For a 2D application, you can use whatever projection you want: Map Projections.
- For 3D, you want to convert those latitude/longitudes into 3D coordinates. Here is some math on how to do that: transformation from
spherical coordinates to normal rectangular coordinates.
- Break up all the primitives into a quadtree/octree (2D/3D). Leaf nodes in this tree contain references to all geometry that intersects that leaf node's (axis-aligned) bounding-box. (This means that a piece of geometry can be referenced more than once.)
- The geometry is then split into a table of vertices and a table of drawing commands. This is an ideal format for OpenGL. Commands can be issued via glDrawArrays using vertex buffers (Vertex Buffer Objects).
- A general visitor pattern is used to walk the quadtree/octree. Walking involves testing whether the visitor intersects the given nodes of the tree until a leaf node is encountered. Visitors include: drawing, collision detection, and selection. (Because the tree leaves can contain duplicate references to geometry, the walker marks nodes as being visited and ignores them thereafter. These marks have to be reset or otherwise updated before doing the next walk.)
- Using a spatial partitioning system (one of the trees) and a drawing-efficient representation is crucial to achieving high framerates. I have found that in these types of applications, you want your frame rate as high as possible 20 fps at a minimum. Not to mention the fact that lots of performance will give you lots of opportunities to create a better looking map. (Mine's far from good looking, but will get there some day.)
- The spatial partitioning helps rendering performance by reducing the number of draw commands sent to the processor. However, there could come a time when the user actually wants to view the entire dataset (perhaps an arial view). In this case, you need a level of detail control system. Since my application deals with streets, I give priority to highways and larger roads. My drawing code knows about how many primitives I can draw before my framerate goes down. The primitives are also sorted by this priority. I draw only the first
x items where x is the number of primitives I can draw at my desired framerate.
The rest is camera control and animation of whatever data you want to display.
Here are some examples of my existing implementation:
Picture http://seabusmap.com/assets/Picture%205.png Picture http://seabusmap.com/assets/Picture%207.png