Introduction

In order to display anything to a computer screen, pixel level coordinates are required at the lowest level. The FooCanvas adds a level of abstraction by allowing the user to specify world coordinates. The difference between world and pixel coordinates using the FooCanvas are simple X and Y scaling factors. (The GnomeCanvas uses a more complex affine transformation matrix for translation, rotation, and scaling.) Neither scaling factors nor the affine transformation matrix by itself are sufficient for determining pixel locations as referenced to a geographic projection.

The GeoProjection object solves this problem by adding an additional level of abstraction between world coordinates and geographic coordinates. The GeoProjection acts as a manager for one or more GeoCanvas objects. It is the place to go to convert between world coordinates and geographic coordinates. Projection properties that can be changed are projection string, e.g. mercator, orthogonal, etc., the rotation, or the reference point. It signals canvas items when any of the projection properties have been changed.

An example use of GeoProjection is to convert cursor position to longitude and latitude:


double wx, wy, lon, lat;

foo_canvas_window_to_world(canvas, event->motion.x, event->motion.y, 
        &wx, &wy);

geo_projection_w2g(projection, wx, wy, &lon, &lat);

The GeoCanvas is derived from the FooCanvas. It adds fairly sophisticated 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. The GeoCanvas additionally adds some convenience functions such as box_zoom, set_viewport that allow easy connections to GTK widgets.

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


GtkWidget *canvas, *scrolledwindow;

scrolledwindow = gtk_scrolled_window_new (NULL, NULL);

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

gtk_widget_show (canvas);

gtk_container_add (GTK_CONTAINER (scrolledwindow), canvas);

The GeoCanvasGroup stock object provides most of what is necessary to simplify handling of the additional details imposed by the GeoProjection. The idea behind this is that everything you would want to plot is either a line or a place. The GeoCanvasLines class is a GeoCanvasGroup 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 with a single GeoCanvasLines object. If you decide you want to draw each country in a separate color, then you can add properties to each polygon canvas item as the data is built using the GeoCanvasGroup interface routines geo_canvas_group_begin, geo_canvas_group_add_point, geo_canvas_group_end_segment, and geo_canvas_group_end. The GeoCanvasPlaces object uses the same interface, but is designed for representing a data set where each element maps to a single point in space. The GeoCanvasShapes also uses the the same interface, but can be used to draw less homogenous groupings of primitive shapes and lines. The GeoCanvasPlace item is a FooCanvasGroup a single object that for representing non-groupable data elements. All geo-canvas.. objects assure that the geographic references are maintained on the canvas without requiring an explicit connection to GeoProjection.

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

GeoCanvasGroup *group;

group = geo_canvas_group_new(parent, GEO_TYPE_CANVAS_LINES,
        "itemtype", FOO_TYPE_CANVAS_LINE,
        "fill_color_rgba", 0xff0000ff, NULL);

geo_canvas_group_begin(group);
geo_canvas_group_add_point(group, -71.5, 41.0);
geo_canvas_group_add_point(group, -71.0, 41.0);
geo_canvas_group_add_point(group, -71.0, 40.5);
geo_canvas_group_add_point(group, -71.5, 40.5);
geo_canvas_group_end_segment(group);
geo_canvas_group_end(group);

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

FILE *fd;
char *fname;
double lon, lat;
char data[80];
GeoCanvas *canvas;
FooCanvasItem *item;

/* The GeoCanvas will create its own projection if one is not specified.
 * Here the panning is enabled continuously via mouse the middle mouse button.
 */
canvas = GEO_CANVAS(g_object_new (GEO_TYPE_CANVAS, 
        "pan_state", GEO_CONTINUOUS_ACTIVE,
        "pan_button", 2, 
        NULL));

/* Either the foo_canvas_item_new() or the geo_canvas_group_new() factory
 * constructor may be used to create canvas items.
 * This creates a GeoCanvasLines item for the land mass of the Earth.
 */
item = foo_canvas_item_new(foo_canvas_root (FOO_CANVAS(canvas)),
        GEO_TYPE_CANVAS_LINES, "itemtype", FOO_TYPE_CANVAS_POLYGON,
        "fill_color_rgba", 0x003400ff,
        NULL));

fname = geo_utilities_find_file("land.txt");
if (!fname || !(fd = fopen(fname, "ra"))) {
        fprintf(stderr, "Could not open %s\n", file);
        g_free(fname);
        return;
}
g_free(fname);

geo_canvas_group_begin(group);
while (fgets(data, 80, fd)) {
        if (sscanf(data, "%lf %lf", &lon, &lat) == 2)
                geo_canvas_group_add_point(group, lon, lat);
        else
                geo_canvas_group_end_segment(group);
}
geo_canvas_group_end(group);

fclose(fd);