C/C++ Users Journal December, 2004
Many modern graphical applications require geographic maps to be displayed, either as the focus of the application or as a background for displaying static or dynamic objects on top of the map. Often, these applications also require that users be able to zoom and pan the display to view various HASH(0x86f744). Therefore, a static map image is rarely sufficient. The most common approach to the problem is to connect to a map server on the Internet, request a map, and receive an image that can be displayed on the screen. For just such a usage, the Open GIS Consortium [1] provides the Web Map Service specification for requesting maps from map servers and receiving images in return. The request is in the form of a CGI request to a web server hosting the map server. Each request contains information about the size of the desired image, the projection and coordinate system to be used when requesting specific HASH(0x86f744) of the globe, and a list of layers to be rendered into the image.
This is ideal if time is not critical and connections to the Internet are acceptable. However, in many GIS applications, connections to the outside network are not acceptable and display time has a real-time requirement. In this case, the use of a map server residing on the Internet falls out of the picture. The only remaining options are either to have an in-house map server that sits on the intranet to which the application has access, or a map server component that can be compiled into the application and used without any network connections.
Naturally, the two options have tradeoffs. Using a map server sitting on an intranet web server means that only one powerful server needs to be used, and the clients can be fairly minimal. However, most of the time it takes to generate map images is spent loading data, compressing images, and sending them over the network. On the other hand, if the client machines running the application are not minimal dumb terminals, then the second option is viable and takes load off the server. For example, the GLG Map Server from Generic Logic [2] can be used in the form of a compiled-in library or over a network via web servers. The advantage of using a map server as a library is that you minimize network usage and take advantage of caching data in memory, which is expensive to read from the disk.
Another important consideration is how fast GIS applications can be prototyped and, more specifically, how the interface with the map server that provides the underlying imagery is handled. In many cases, GIS applications have a map image in the background on which targets are displayed. More importantly, the map image is often not the focus of the application, but it is still a critical component. No one wants to have to spend time administering a web server just to develop a prototype GIS application. This becomes a particularly pressing consideration when multiple maps need to be displayed. With regards to the interface, if the targets on the screen are not fetched from the network, then the need for network connections just to fetch a background image becomes an unnecessary hindrance in the prototyping process.
Unlike some map server tools, the GLG Map Server is not built around a database. The data needed for generating map images resides in a directory structure on the filesystem. This approach has a number of advantages due to the way in which the data is used. First of all, storing the data directly on the filesystem lets users quickly modify or replace the data, should updated data become available or if an anomaly is found. The biggest advantage of a database solution to a data storage problem is related to searching for data. However, when utilizing data in the Map Server, no search is required. A simple hierarchical data storage mechanism such as a filesystem is sufficient. Another advantage of using a filesystem as it is used in the Map Server is that each directory is self-contained. If, for example, users have data for the entire state of Florida, but only want to utilize the Map Server to display a single county, that county is a single directory that can be copied or linked to elsewhere.
There exist two types of data that are used in the Map Server:
At the top level of the data hierarchy is the Server Dataset File (SDF), which describes where data for all layers and fonts are located and various global attributes. Listing 1 is a simple SDF file that defines three layers and two fonts and specifies the directories where layer and font information is to be found.
Notice that the SDF defined the earth layer as LAYER=earth earth.lif, where earth is the name of the layer as it is to be referred to, and earth.lif is the file in which the layer is described. This Layer Info File (LIF) defines how and when this image layer should be rendered. Listing 2 is an LIF that describes the earth layer. The LIF defines the type of the layer, what blending function to use when rendering it, and what file to read the data from. An LIF for vector data (Listing 3) has a similar format.
Besides networking and data organization, the biggest data-related problem presenting any map server is the problem of retrieving feature data for rendering and, more importantly, retrieving only the data it needs for a particular request. For example, satellite imagery of the earth that is freely available from NASA has a resolution of 1 sq. km per pixel. The file they provide is a 400-MB TIFF file. Also, this resolution is not even that high, considering that a small town might only be the size of a few pixels in such an image file. Clearly, it is not feasible for the Map Server to load such a file every time the earth layer needs to be rendered, or even store it in memory for faster access. More importantly, all this data is rarely (if ever) needed in a single image since most images show a small region of the globe. If it is, then it must be that users wish to see the entire globe, in which case the resolution provided in this file is much higher than needs to be used in the final image that is given to users. For example, the request image might be 600×400 pixels, while the source image is 26,000×13,000, which is clearly much higher than necessary.
The GLG Map Server's solution to this problem lies in its tiling mechanism for both image and vector data. The problem is easier to solve for image data, where a single image such as the earth satellite imagery data can be split into rectangular tiles. These tiles can then be loaded as necessary and cached in memory for future use. However, even with image data, there exists a fundamental limit on the amount of data that can be used at once. Again, if users request an image of the entire globe, loading 400 MB of data is not feasible, and also unnecessary. To solve this problem, the GLG Map Server provides a fallback mechanism by which alternate data is used when the amount of data necessary for a given map request exceeds a predefined amount. In the case of image data, if the entire earth image was split into 400 tiles, then the LIF for that layer can specify that any request that requires more than 20 tiles will use a smaller fallback image, which is on the order of a few megabytes. This mechanism avoids thrashing of the map server when attempting to display both small HASH(0x86f744) and the whole globe, and improves response time.
However, vector data often does not lend itself to such simple tiling as image data. While vector data can also be split into rectangular tiles, more often than not, vector data is not provided in a single file. For example, the U.S. Census Tiger Data is provided in a somewhat hierarchical structure [3]. Data for each state resides in a separate directory, as does each county and town. In such a case, the Map Server provides a mechanism for automatically generating a set of LIFs that lists the extent of all data files in all subdirectories of a given directory. This is done recursively, so that at any level of the hierarchy, it is possible to know what region of the globe that directory covers. Generating these extents is slow, since it requires all the data to be read. Once these files are generated, they can be used at runtime to recursively determine which data needs to be loaded to render a given request. This mechanism also lets individual subdirectories be moved or linked-to from anywhere and reused.
The easiest way to utilize the Map Server is to use the GLG GIS object. The GIS object transparently handles all invocations of the Map Server API, whether the Map Server is used as a compiled-in library or connected to through a web server. The toolkit also provides integrated panning and zooming capability so that you don't have to generate map image requests. The application I present here utilizes the GLG GIS object to display a map image and lets users zoom/pan around the map, displaying increasing levels of detail as users zoom in. The example also shows a dynamic target displayed on top of the map, updated with dummy data and displayed on proper latitude and longitude coordinates as the map is panned/zoomed.
If an Internet map server is used, then you are required to handle all communication with the map server. This involves generating a proper Open GIS request string, connecting to the map server, retrieving the image, and displaying the image on screen. Especially in the prototypical stages of an application, this process is cumbersome and unwieldy. However, the map is rarely the central focus of the application. Dynamic and/or static targets are usually the primary attraction, so you should not need to spend most of your time dealing with the map server.
The GLG Toolkit is primarily a dynamic graphics toolkit and the use of the integrated GIS object lets you add map functionality to applications you develop. A map server provides imagery that can be displayed, but all target rendering must be handled by you.
The toolkit also provides a number of coordinate conversion functions, which simplifies the process of positioning targets on top of the map. This set of functions will convert between screen coordinates and the geographical coordinates of a GIS object and vice versa.
Most importantly, the use of the integrated GIS object lets you utilize the Map Server within the GLG Graphics Builder, which is a graphical point-and-click editor. This way, an application can be designed graphically without wasting edit/compile cycles that plague most prototypes. If an Internet map server is used, you must manually test and fine-tune the output of the map server requests before integrating it into the application. With the toolkit's graphical editor, the output of the Map Server can be previewed and adjusted on the fly.
The example application (Listing 4) utilizes the GLG resource mechanism heavily and is described here for clarity. The GLG Toolkit uses a resource mechanism to access and manipulate objects. There exist three types of resources: D (double), G (geometrical), and S (String). The G type is essentially three doubles, and is used to represent an (x,y,z) coordinate or an (R,G,B) color triplet. Any given resource can be "get" or "set" through the API. Each resource is an object and, in turn, has its own resources. For example, to set the string of some text object, obj, you use this API call:
GlgSetSResource( obj, "String", "MyText" );
Similarly, to get the Visibility resource of the object:
GlgGetDResource( obj, "Visibility", &vis );
All resources of a top-level object can be accessed through the resource hierarchy. Each level of the hierarchy is delimited by a "/" like the UNIX filesystem. So, to set the name of a resource named MyRes:
GlgSetSResource( obj, "MyRes/Name", "MyOtherRes" );
I use the GLG GIS object, which has resources such as Projection, Layers, Extent, Center, and the like.
The application I present here displays a map in the background and a dynamic target on top. The target represents a plane that flies from London to San Francisco. Listing 4 is the complete C code. In this example, the GLG Map Server is used in the form of the GLG GIS object and a compiled-in library. The example lets users zoom and pan around the globe and toggle a grid layer.
First, the application's drawing was designed in the GLG Graphics Builder, then animated from a program. The design of the application in the Graphics Builder consisted of defining the user interface that the application uses, adding a GLG GIS object, and drawing a polygon in the shape of a plane. Finally, I added dynamics to the plane, so that its angle and location could be changed from the program.
When certain buttons (such as the zoom-in and zoom-out buttons) are pressed, the integrated zooming and panning features of the GLG Toolkit automatically set the center and extent of the map on screen. Zooming in/out requires only a single function call:
// zoom in (i) by factor of 2 else if( strcmp( origin, "ZoomInButton" ) == 0 ) GlgSetZoom( viewport, "MapViewport", 'i', 2. ); // zoom out (o) by factor of 2 else if( strcmp( origin, "ZoomOutButton" ) == 0 ) GlgSetZoom( viewport, "MapViewport", 'o', 2. );
Whenever the map is zoomed or panned, or the target moves, it needs to be repositioned and redisplayed. This code demonstrates the GLG coordinate conversion functions that let the target be easily positioned. Using the specified GIS object, the plane's latitude/longitude coordinates are converted into object coordinates:
GlgGISConvert( GISObject, ...
&Plane.lat_lon,
&Plane.xyz );
Using these coordinates, the plane's position can be set and the screen is updated to reflect the changes:
GlgSetGResource( Plane.graphics, "Position",
Plane.xyz.x,
Plane.xyz.y,
0. );
GlgUpdate( viewport );
Figure 1 is a screen capture of the application. Figure 2 shows a zoom of the map. (A more complex application utilizing the GLG Map Server in the form of a Java Applet is available at [4] and http://www.cuj.com/code/.)
The GIS component of an application is rarely the primary focus. Therefore, extra time should not be wasted worrying about communication with a map server, generating GIS requests, and positioning targets. Off-the-shelf tools such as the GLG Map Server, along with the GLG GIS object, provide a way of hiding the complexity of GIS components and let you spend time, especially in the prototyping stage, on developing functionality of more critical importance.
[3] http://www.census.gov/geo/www/tiger/index.html.
[4] http://www.genlogic.com/demos_java2.html#Map_Server_Air_Traffic.