Simple Tiles is an image generation library for spatial data written in C. At it's core it is a thin wrapper on top of OGR for spatial data and Cairo for image generation.

The library's source is over at github. Please report issues or ask questions in the issue tracker, or head on over to #newsapps or #propublica on Freenode IRC.

There are currently Ruby bindings in Simpler Tiles, and two sets of Python Bindings, python-simple-tiles by Sasha Hart, and simpyl_tiles by Albert Sun.

Download version 0.4.0: ZIP tar.gz

Dependencies

Simple Tiles relies on GDAL and OGR for abstractions over geospatial data. You'll need at least version 1.9.0 of GDAL to use Simple Tiles. On Ubuntu you can install the library using the ubuntugis ppa. On OS X the version from homebrew works well.

Simple Tiles uses Cairo (version >= 1.12) and Pango (version >= 1.30) for the final png output, you'll need an installed and working version of both. On Ubuntu apt-get installs a perfectly fine version of both, and OS X users can use homebrew.

Installation

To install Simple Tiles run:

$ git clone git@github.com:propublica/simple-tiles.git
$ cd simple-tiles
$ ./configure
$ make && make install

You'll probably also want to run:

$ make test

to ensure the library and its dependencies are playing nice with one another.

Compilation and Linking

Once you have a working copy of Simple Tiles, you'll want to use pkg-config to link to the library. For example, to compile a file with Simple Tiles use:

cc `pkg-config --cflags simple-tiles` `gdal-config --cflags` -c -o main.o main.c
cc main.o `pkg-config --libs simple-tiles` `gdal-config --libs` -o main

And your program should be linked correctly.

Overview

Simple Tiles is written in an object oriented fashion with thin data structures. The hierarchy of structures consists of:

For a real-world example on how this all fits together, here is a demo that draws the map in the Simple Tiles logo:

#include <simple-tiles/simple_tiles.h>
#include <simple-tiles/query.h>
#include <simple-tiles/layer.h>

int
main(){
  simplet_map_t *map = simplet_map_new();
  simplet_map_set_srs(map, "EPSG:3083");
  simplet_map_set_bgcolor(map, "#ffffff");
  simplet_map_set_bounds(map, -585080.885134, 6849466.721081, 4161303.603672, 9587780.816356);
  simplet_map_set_size(map, 423, 260);
  simplet_layer_t *layer = simplet_map_add_layer(map, "PG:dbname=simple_tiles_roads");
  simplet_query_t *query = simplet_layer_add_query(layer, "select * from roads");
  simplet_query_add_style(query, "stroke", "#11111166");
  simplet_query_add_style(query, "line-join", "round");
  simplet_query_add_style(query, "weight", "0.1");

  simplet_map_render_to_png(map, "./out.png");
  simplet_map_free(map);
}

Another example is the repo's test/api.c file.

Maps

The simplet_map_t structure is the root of the Simple Tiles heirarchy and has methods that set and get information on the final output.

simplet_map_t* simplet_map_new()

Allocates and returns a pointer to a new instance of simplet_map_t. Returns NULL or the instance; the new instance should be freed with simplet_map_free.

void simplet_map_free(simplet_map_t *map)

Deallocates the memory allocated for a particular simplet_map_t, and its child structures. This should be the only deallocation function you'll need.

simplet_status_t simplet_map_set_srs(simplet_map_t *map, const char *proj)

Creates and assigns a particular projection to the map you may use any projection string that OGRSpatialReference::SetFromUserInput understands. Returns a simplet_status_t object. If you set a new srs and you have set the map's bounds in a previous projection, they will be updated for you.

void simplet_map_get_srs(simplet_map_t *map, char **srs)

Sets srs to the map's srs, will allocate space for srs and srs should be deallocated with free.

simplet_status_t simplet_map_set_size(simplet_map_t *map, unsigned int width, unsigned int height)

Sets the physical size of the output image. For tile generation you'll want to use simplet_map_set_slippy to set this automatically for web mercator.

unsigned int simplet_map_get_width(simplet_map_t *map)

Return the width of the map.

unsigned int simplet_map_get_height(simplet_map_t *map)

Return the height of the map.

simplet_status_t simplet_map_set_width(simplet_map_t *map, unsigned int width)

Set the width of the map.

simplet_status_t simplet_map_set_height(simplet_map_t *map, unsigned int height)

Set the height of the map.

simplet_status_t simplet_map_set_bounds(simplet_map_t *map, double maxx, double maxy, double minx, double miny)

Sets the boundary of the map in the current projection. Again, simplet_map_set_slippy is an easy way to set this for tiled maps.

simplet_status_t simplet_map_set_slippy(simplet_map_t *map, unsigned int x, unsigned int y, unsigned int z)

Takes a series of google map tile coordinates and sets the bounds, projection width and height of the simplet_map_t instance accordingly.

simplet_status_t simplet_map_set_bgcolor(simplet_map_t *map, const char *str)

Stores a copy of str as the map's background color in #ffffffAA format AA is the alpha index of the color.

void simplet_map_get_bgcolor(simplet_map_t *map, char **str)

Stores a copy of the map's background color in str. str is then owned by the caller and should be free'd.

simplet_layer_t* simplet_map_add_layer(simplet_map_t *map, const char *datastring)

Adds a layer to the maps layer's list. datastring represents a resource that is readable by OGR, and returns a simplet_layer_t which is owned by the map and will be freed when the map is. Returns NULL on failure.

simplet_layer_t* simplet_map_add_layer_directly(simplet_map_t *map, simplet_layer_t *layer)

Add a layer object created by simplet_layer_new directly the map's list of layers.

simplet_status_t simplet_map_get_status(simplet_map_t *map)

Returns the current status of the map object. Status codes are:

typedef enum {
  SIMPLET_ERR = 0,   // Generic error
  SIMPLET_OOM,       // Out of memory for allocation
  SIMPLET_CAIRO_ERR, // Cairo error
  SIMPLET_OGR_ERR,   // OGR Error
  SIMPLET_OK         // OK
} simplet_status_t;

const char* simplet_map_status_to_string(simplet_map_t *map)

Returns a pointer to an internal english version of the map's status. Useful for error reporting.

simplet_status_t simplet_map_is_valid(simplet_map_t *map)

Returns a simplet_status_t of the current state of the map that indicates if the requirements for successfully rendering the map are met. In other words, if a map has bounds, width and height, projection and at least one layer.

void simplet_map_render_to_png(simplet_map_t *map, const char *path)

Renders a png to the path based on the specification defined in map. Layers are painted on the image in order of insertion, that is, the oldest layers are drawn first.

void simplet_map_render_to_stream(simplet_map_t *map, void *stream, cairo_status_t (*cb)(void *closure, const unsigned char *data, unsigned int length))

Renders the map to a png stream by repeatedly calling the closure. The closure is a cairo_write_func_t and must conform to that API.

void simplet_map_set_buffer(simplet_map_t *map, double buffer)

Sets the buffer on the map. Buffers are a kind of overprinting where you can include more shapes in rendering in order to set type correctly. Michal Migurski has a good post on why this is useful.

double simplet_map_get_buffer(simplet_map_t *map)

Returns the map's buffer.

Bounds

Bounds store the boundary of map data. Mostly the simplet_map_t functions handle the bounds, but simplet_bounds_to_wkt is useful to limit queries in postgresql.

simplet_bounds_t* simplet_bounds_new()

Create a new simplet_bounds_t, returns NULL on failure.

void simplet_bounds_free(simplet_bounds_t *bounds)

Free the bounds.

void simplet_bounds_extend(simplet_bounds_t *bounds, double x, double y)

Extend the bounds to include the point x, y.

simplet_status_t simplet_bounds_to_wkt(simplet_bounds_t *bounds, char **wkt)

Store a Well Known Text representation of the bounds in the string pointing to wkt. Returns SIMPLET_ERROR on failure.

Layers

Each layer maps to a OGR datasource, and contains a list of queries to run against that source.

simplet_layer_t* simplet_layer_new(const char *datastring)

Create a new simplet_layer_t, returns NULL on failure.

void simplet_layer_free(simplet_layer_t *layer)

Deallocate the simplet_layer_t.

void simplet_layer_set_source(simplet_layer_t *layer, char *source)

Set the layer's source attribute. source needs to be a string pointing to an OGR readable resource.

void simplet_layer_get_source(simplet_layer_t *layer, char **source)

Stores a copy of the layer's source attribute in source, and allocates space for the copy. source should be freed when no longer needed.

simplet_query_t* simplet_layer_add_query(simplet_layer_t *layer, const char *ogrsql)

Creates and adds a query to the layer's query list. The returned query is owned by the layer and should not be freed with simplet_query_free. ogrsql should be a query to query the layer's data source using OGR SQL. Returns NULL on failure.

simplet_query_t* simplet_layer_add_query_directly(simplet_layer_t *layer, simplet_query_t *query)

Adds a previously initialized query to the layer's query list. The returned query is then owned by the layer and should not be freed with simplet_query_free.

Filters

Each simplet_query_t contains OGR SQL to query the data in its parent layer. Like layers they are drawn in order of insertion on the map canvas.

simplet_query_t* simplet_query_new(const char *sqlquery)

Creates and returns a new simplet_query_t. Returns NULL on failure. sqlquery should be OGR SQL.

void simplet_query_free(simplet_query_t *query)

Frees the passed in query. If the query has been added to a layer you shouldn't use this function.

simplet_status_t simplet_query_set(simplet_query_t *query, const char *sql)

Sets the sql on query. sql should be OGR SQL.

simplet_status_t simplet_query_get(simplet_query_t *query, char **sql)

Stores a copy of the query's sql attribute in sql, and allocates space for the copy. sql should be freed when no longer needed.

simplet_style_t* simplet_query_add_style(simplet_query_t *query, const char *key, const char *arg)

Creates and returns a new simplet_style_t. Returns NULL on failure. key should be a style name, and value should be a corresponding value.

Styles

Styles are where you'll define the visual appearance of the data emitted by the simplet_query_t. They mimic CSS in that they operate on individual data elements in the spatial source. All colors are in 8 digit hex format with the last 2 digits defining the Alpha value of the color (e.g. #fffffaa). These are the styles, and there are planned additions:

fill
The fill color for each shape.
stroke
The stroke color for each line.
weight
The width of each line in pixels.
line-cap
How lines are ended, one of: "butt", "round", "square"
line-join
How lines are joined together, one of: "miter", "round", "bevel"
text-field
The field to grab labels from.
text-stroke-color
Color for the outline of the text labels.
text-stroke-width
The size of the outline fo the text labels.
font
A font description that pango can read to set labels in (i.e. "Helvetica 12px"). You can use fc-list on your system to browse the available font families.
letter-spacing
How far apart to space the letters in labels.
radius
For point rendering only, the radius in pixels of the circle.

simplet_style_t* simplet_style_new(const char *key, const char *arg)

Returns a new simplet_style_t or NULL on failure. key and arg define the style according to the list of styles.

void simplet_style_free(simplet_style_t* style)

Frees the memory associated with the style.

void simplet_style_set_key(simplet_style_t *style, char *key)

Makes a copy of key to store in the style.

void simplet_style_set_arg(simplet_style_t *style, char *arg)

Makes a copy of arg to store in the style.

void simplet_style_get_key(simplet_style_t* style, char **key)

Allocates and stores a copy of the style's key in key. key should be freed when no longer needed.

void simplet_style_get_arg(simplet_style_t* style, char **arg)

Allocates and stores a copy of the style's arg in arg. arg should be freed when no longer needed.

User Data Interface

simplet_map_t, simplet_layer_t, simplet_query_t and simplet_style_t each implement an interface for storing user data. This is useful for storing references in language bindings.

void simplet_##type##_set_user_data(simplet_##type##_t *obj, void *data)

Sets the user data on obj.

void * simplet_##type##_get_user_data(simplet_##type##_t *obj)

Returns the user data pointer associated with obj.

void simplet_##type##_free_user_data(simplet_##type##_t *obj, simplet_user_data_free free)

Accepts a function pointer free, type: void (*simplet_user_data_free)(void *val), that will free the user data stored in the object, and frees the user data.

Demo

Here is a small demo of the area surrounding New Orleans built with Tiger/Line data from the U.S. Census.

Change Log

0.4.1
Fix debug flag.
0.4.0
New build system -- waf -- and now using OGR_ENABLE_PARTIAL_REPROJECTION so that shapes with unprojectable coordinates render correctly.
0.3.2
Fix for config.h
0.3.1
Define GNU_SOURCE
0.3.0
Switch the build system to autotools and fix a big pango bug (thanks Adam Trilling.

License

Copyright (c) 2012, ProPublica

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

Simple Tiles is a project of ProPublica.