RoadHog: Publishing

This document describes the RoadHog CGI scripts which are written in Perl.

Design Goal

The CGI scripts and the compiler are closely related. Many things could be done in either the CGI script or the compiler; in fact, it would be possible to do away with the compiler altogether and generate every smallest bit of content dynamically.

My aim was to create as much static content as possible, and to keep the CGI script as simple (dumb) as possible. That way, I minimize the resource usage on the computer running the CGI, which in turn will be able to handle many parallel requests efficiently. (This goal did not, however, keep the CGI from growing to more than 1,000 lines...)

For each incoming request, the CGI only opens one file - the control file - which is a simple text file that can be read and parsed in a straightforward way. In many cases it does not even have to be read in its entirety.

Here is a control file for a very short journey:

Shieldaig:Lochcarron::A896:4
256:192:mm:1200:1100:40:43:[some omissions]
tiny:small:medium:large
tiny:small:medium:large:huge

01.jpg:217:863:31x8:N57°31.43':W005°38.74':989162962:38:191
02.jpg:217:864:31x8:N57°31.22':W005°38.78':989162993:51:236
03.jpg:217:866:31x8,32x8:N57°31.04':W005°38.89':989163019:55:171
04.jpg:216:868:31x8,32x8:N57°31.00':W005°38.86':989163024:55:163

You are at a junction.
d:1.0::360:A896:Lochcarron:kenmorejunction-shieldaig:f7f4

There is some configuration information at the beginning - where the journey is from, where it is to, on what road etc., and there's information about the available map and picture sizes. After that, one line for each image, with information about the map pixel position, the map segments affected, the co-ordinates, timestamp, speed, and heading. At the end there's a status message and a list of possible onward connections (only one in this case).

This is enough for the CGI script to create the HTML pages for this journey.

The Red Dot

In standard mode, the CGI script just generates a completely new HTML page for every new position. The map display is actually an array of five rows and five columns, each with a tiny map section displayed; those affected by the red dot will be replaced by the special versions that the compiler has generated. For example, the centre map piece for image 2 in this journey would be row 31 column 8 with the dot at 217;866 (the file name will be 31x8+217+866.gif). For image 3, two custom map tiles are required - 31x8+217+866.gif and 32x8+217+866.gif; this is a case where the red dot is painted on the border between two map pieces. The control file only specifies the central map piece(s); the CGI script finds out the surrounding ones on its own.

There's also a CSS mode in which the CGI will do away with the custom painted map tiles and just use CSS positioning to put the dot where it should be. This reduced the bandwidth used and enables us to select different dot images based on the heading.

The disadvantage of this "standard mode" is that many browsers visibly "clear" the page in between reloads, resulting in a sometimes rather annoying flicker. (Opera handles it perfectly flicker-free, though!)

Frame Mode

When frame mode is selected, the script is called twice - once to establish the frameset, and then targeted to an invisible frame which I call the control frame. The script now resorts to generating a stream of Javascript commands for each photo which is sent to the control frame and from there updates all the other frames. This is more efficient than having the browser update each frame separately (which would require an extra network connection for each frame during each update!).

This procedure means that the frame mode will only work when Javascript is enabled.

Java Mode

The Java mode works almost like the frame mode, with the exception that the central area of the display is now occupied by a very simple Java applet (the source code for the MapApplet is included in the RoadHog Java download). The CGI script still sends its output to the invisible control frame, but now, instead of rebuilding the map and photo area itself, it calls a method on the applet, asking it to update the photo and map. This eliminates flicker.

Preload and Image Caching

To improve the viewing experience, the CGI script always looks ahead to the next photo and generates a small Javascript program in the header of the page which, after all the images are fully loaded, already starts to load the images that will be required for displaying the next page. This relies on efficient image caching by the browser - when the next page arrives and with it the command to load certain images, the browser must "remember" that it has loaded them already. Otherwise this preload function is just a waste of bandwidth. (If Javascript is off or unsupported, the preload function has no effect, which doesn't hurt.)

Also, the map display, being segmented into 5x5 individual images, relies on browser image caching. If the map "scrolls", the browser should only load the new row or column of images and "remember" that the others are loaded already. The only exception is the Java applet which caches the images on its own.

Search Script

The search CGI supports two modes - searching for a geographic location, and searching for a place name. Both modes depend on a special text file generated by the compiler. The file contains a list of all journeys with one line of description, and afterwards a list of co-ordinates (or a list of places) with pointers to the journeys. An example from the location.txt file:

252c:Journey from North Erradale to Melvaig 
ae83:Journey from Inverlael to Blarnalearoch
86dc:Journey from North Erradale to Gairloch / Melvaig
f207:Journey from Kerrysdale to Kinlochewe
99e6:Journey from Badcaul to Badluarach
[...]

-5.812468577402863:58.15879941547619:59249.58824709085:111120.0
34263:0:209740:923408:97
34188:82:209663:923329:97
34180:88:209654:923324:97
34108:284:209574:923130:97
[...]

The journey list is really just the journey IDs and a textual designator; then there's an empty line, followed by a coordinate conversion line and then one line for every photo in the whole of the application.

The photo lines consist of two co-ordinate pairs; the first is a simple "metres from the top left corner of the coverage area", the second is the British Grid. The self-made "metre" pair is based on a primitive conversion where one degree of latitude equals 111120 metres, and one degree of longitude equals 111120 * cos (average latitude) metres. This is sufficient for the distance search performed by the search script. The search script uses the first pair when searching for a latitude/longitude pair and the second when searching for a British Grid reference, as the conversion between the two was too nasty to added to the Perl script. The fifth value on the photo line is a pointer to a journey, in this example it's the "jonurney on line 97".

The places.txt file starts out the same, but after the empty line you have something like this:

Dundonnell River::27,63
Black Water::29,131
Contin::36,83,114,130
Loch Carron::65,120
Loch Bad á Chrótha::11,15
Badachro::11,15
[...]

This is just a list of terms found either in the names of start and end vertices of a journey or in one of the "via" tags of the matching edge; again, with pointers to journeys. The search script uses the String::Similarity module to extract those names from the list that look similar to what the user entered.

Back to the index


  Frederik Ramm, 2001-05-24