Introduction

In order to display anything to a computer screen, device level coordinates (pixels) are required at the lowest level. The CrCanvas adds a level of abstraction by allowing the user to specify user coordinates. The difference between user and device coordinates using the CrCanvas are defined by one or more levels of cairo_matrix_t affine transformations. However, affine transformations alone are not enough to determine pixel device locations as referenced to a geographic projection.

The GeocProjection object solves this problem by adding an additional level of abstraction between user coordinates and geographic coordinates. The GeocProjection acts as a manager for one or more GeocCanvas objects. It is the place to go to convert between user coordinates and geographic coordinates. Projection properties that can be changed are projection string, e.g. mercator, orthogonal, etc., the rotation, or the reference point.

An typical use of GeocProjection is to convert cursor position to longitude and latitude:


double ux, uy, lon, lat;

ux = event->motion.x;
uy = event->motion.y;

/* Using the inverse of the current transformation matrix (CTM) will
 * convert from device to user coordinates. */
cairo_matrix_transform_point(inverse_matrix, &ux, &uy);

/* This part converts from user coordinates to geographic coordinates.
 * It is not possible to convert directly between device and geographic
 * coordinates.*/
geo_projection_w2g(projection, ux, uy, &lon, &lat);

The GeocCanvas is derived from the CrCanvas. It adds geographically referenced viewport and scroll region management to the canvas. The additional management is necessary to allow the canvas to dynamically scale between a view that represents the whole Earth and a view that represents less than a square mile in area. For a case where you are looking at Tokyo, you would not expect to be able to scroll to New York. Another benefit to managing the viewport area is in performance. By connecting to the #viewport-changed signal, it is possible to swap in and out high resolution maps.

Creating the canvas is essentially the same as creating a CrCanvas.


GtkWidget *canvas, *scrolledwindow;

scrolledwindow = gtk_scrolled_window_new (NULL, NULL);

canvas = GTK_WIDGET(g_object_new(GEOC_TYPE_CANVAS, NULL));

gtk_widget_show (canvas);

gtk_container_add (GTK_CONTAINER (scrolledwindow), canvas);

The GeocItem stock object provides most of what is necessary to simplify handling of the additional details imposed by the GeocProjection. The idea behind this is that everything you would want to plot is either a line or a place. The GeocLines class is a GeocItem of either poly-lines or polygons that all share the same properties. You might want to draw all Earth land mass as green polygons. This can be accomplished using a single GeocLines object. If you decide you want to draw each country in a separate color, then you can add properties to each #CrLine canvas item as the data is built using the GeocItem interface routines #geoc_item_begin, #geoc_item_add_point, #geoc_item_end_segment, and #geoc_item_end. The GeocPlaces object uses the same interface, but is designed for representing a data set where each element maps to a single point in space. The GeocShapes does not use this interface. It can be used to draw less homogenous groupings of primitive shapes and lines. All Geoc.. canvas items assure that the geographic references are maintained on the canvas without requiring an explicit connection to GeocProjection.

Here is an example of how to use the GeocItem interface to draw a rectangle onto the canvas:

GeocItem *group;

group = geoc_item_new(parent, GEOC_TYPE_LINES,
        "itemtype", CR_TYPE_LINE,
        "fill_color", "red", NULL);

geoc_item_begin(group);
geoc_item_add_point(group, -71.5, 41.0);
geoc_item_add_point(group, -71.0, 41.0);
geoc_item_add_point(group, -71.0, 40.5);
geoc_item_add_point(group, -71.5, 40.5);
geoc_item_end_segment(group);
geoc_item_end(group);

This example draws a set of polygons defined in the "land.txt" file:

GeocCanvas *canvas;
char data[80];
double lon, lat;

/* The GeocCanvas will create its own projection if one is not specified.
 */
canvas = GEO_CANVAS(g_object_new (GEO_TYPE_CANVAS, NULL));

/* Using the #geoc_item_new constructor vice g_object_new, will cause the the
 * GeocItem to be parented and unreferenced in one step.
 */
item = geoc_item_new(GEOC_ITEM(CR_CANVAS(canvas)->root),
        /* This tells the constructor what derived object */
        GEOC_TYPE_LINES,
        "fill-color", "green",
        NULL);

geoc_item_begin(item);
while (fgets(data, 80, fd)) {
        if (sscanf(data, "%f %f", &lon, &lat) == 2)
                geoc_item_add_point(item, lon, lat, NULL);
        else
                geoc_item_end_segment(item, NULL);
}
geoc_item_end(item);

/* That's it.*/