Tools | | Programming Example

Programming API

Overview of the core modules and their purpose:

  • grid_value: class to represent multi-channel cell values with arithmetic operators
  • grid_data: generic container for raster data living in normalized coordinates
  • grid_pyramid: the pyramidal stack down-sampled from the top-level raster data
  • grid_layer: geo-referenced layer with data stack and geo-spatial extents plus interpolators
  • grid_layers: set of referenced layers
  • grid_list: set of managed layers with loaders from gdal/pnm/db
  • grid_tileset: resampling core to resample/reproject/crop terrain data
  • grid_merge: merging core to merge/process imagery channels

A grid_value encapsulates one cell value with multiple channels.

For single channel data like height fields, a cell value is created with the constructor grid_value(value,1) or as an abbreviation with data_value(value). Invalid or unspecified cell values are represented with nodata_value(). Multi-channel cell values can be created from single-channel cell values by appending additional channels via value.append(channel_value).

A cell value is undefined or said to be no-data if one ore more channel values are not-a-number (IEEE quiet NAN), which can be tested with is_nodata(value). Operators are defined for the standard arithmetic operations + - and == != as well as operators for linear combinations via * and /. As an example, the average of two rgb values can be computed as follows:

#include <grid/grid_value.h>

grid_value rgb1(0.0,3),rgb2(1.0,3);
grid_value avg=(rgb1+rgb2)/2.0;
std::cout << avg << std::endl;

-> avg = (0.5,0.5,0.5)

A grid_data object contains multi-channel raster data of various types. Possible types are byte, unsigned short, signed short or float. The types come in two flavors with an absolute value range or a normalized range of 0..1. In both cases the values can be scaled and biased and for all types there is the possibility to identify a specific value with no-data. The raster data is initialized to no-data by default.

A typical rgb image with sx columns, sy rows and with black representing no-data values is setup with

#include <grid/grid_data.h>

grid_data image;
image.init(sx,sy,3, // grid size and number of image channels
           GRID_TYPE_BYTE, // normalized unsigned byte
           TRUE, // cell-centric (image)
           1.0,0.0, // scale and bias
           0.0); // no-data value

A typical height field is setup with

grid_data hfield;
hfield.init(sx,sy,1, // single elevation channel
            GRID_TYPE_SIGNED_SHORT_INT, // absolute signed short
            FALSE, // grid-centric (height field)
            1.0,0.0, // scale and bias
            NAN); // no-data value

For all but the byte and unsigned short type, no-data values can also be represented with NAN. The float raster type stores the no-data values directly as NAN, whereas the value range of signed short raster data ranges from −32767 to 32767 with −32768 being a reserved value that is interpreted as NAN.

The data representation can be either cell-centric or grid-centric, indicating whether a cell value should be assumed to represent a sampling over the region of a cell (area orientated) or a lattice point (point orientated). The convention of libGrid is that imagery is cell-centric and height fields are grid-centric.

The following line sets an image pixel at raster position (x,y) to a constant brightness c. The raster positions are in the range 0<x<sx-1, 0<y<sy-1 with the origin at the bottom left corner.


The number of channels of the value parameter is expanded to fit the raster data. Likewise, the accessors to the raster data return a ‘grid_value object that reflects the number of present channels:

grid_value value=hfield.get(x,y);

The same is true for the bi-linear interpolator that returns a grid_value for a specific coordinate (u,v)

grid_value value=hfield.interpolate(u,v);

except that it operates on a normalized coordinate range of [0..1,0..1]. For cell-centric data (that is imagery) the coordinate ((0.5+x)/sx,(0.5+y)/sy) maps to the center of the according cell at raster position (x,y). The coordinates range from (0.0,0.0) at the bottom left corner of the bottom left pixel to (1.0,1.0) at the top right corner of the top right pixel. For grid-centric data (that is elevation) the coordinate (x/(sx-1),y/(sy-1)) maps to the according lattice point (x,y). The coordinates range from (0.0,0.0) at the bottom left lattice point to (1.0,1.0) at the top right lattice point.

A grid_layer is a superset of a grid_data object. It additionally has a geospatial extent (grid_extent) which geo-references the raster data with 4 specific corner points on earth. In contrast to grid_data objects, the interpolators of a grid_layer do not work with normalized coordinates but with geo-referenced coordinates. Each geo-referenced coordinate is represented as a minicoord vector. The layer extent is defined to exactly enclose the

  • area of all cells in case the data is cell-centric (cell is area).
  • lattice points in case the data is grid-centric (cell is point).

Please note that this is different from the GDAL convention that a geo-referenced extent is always area oriented as outlined in the GDAL data model. The distinction of libGrid between the two cell layouts enforces the correct interpretation of cell values.

The following common geospatial coordinate reference systems (CRS) are supported for defining the layer extents:

  • ECEF (Earth Centered Earth Fixed)
  • LatLon (Geographic Coordinates)
  • UTM (Universal Transverse Mercator)
  • Mercator (Cylindrical)

ECEF and LatLon coordinates assume to be referenced to the WGS84 datum. For UTM coordinates a variety of other datums (not to be mixed up with ellipsoids) are supported as listed below:

  • NAD27 (North American 1927)
  • WGS72 (World Geodetic System 1972)
  • WGS84 (World Geodetic System 1984)
  • NAD83 (North American 1983)
  • SPHERE (Sphere with mean earth radius of 6370997m)
  • ED50 (European 1950)
  • ED87 (European 1987)
  • OldHawaiian (Mean datum for main Hawai’ian islands)
  • Luzon (Phillipines)
  • Tokyo (Japan)
  • OSGB1936 (Great Britain)
  • Australian1984
  • NewZealand1949
  • SouthAmerican1969

For other not so common coordinate systems or datums please refer to GDAL’s utilities (in particular cs2cs and gdalwarp) to convert those to a supported CRS (UTM/WGS84 recommended).

The following code snippet illustrates how to setup a geo-referenced layer:

#include <grid/grid_layer.h>

minicoord::MINICOORD_TYPE type=minicoord::MINICOORD_UTM;
int zone=4;
minicoord::MINICOORD_DATUM datum=minicoord::MINICOORD_DATUM_NAD83;
double base=0.0;

minicoord leftbottom(miniv3d(601498.6948,2371702.575,base),type,zone,datum);
minicoord rightbottom(miniv3d(603513.7721,2371702.575,base),type,zone,datum);
minicoord lefttop(miniv3d(601498.6948,2373737.497,base),type,zone,datum);
minicoord righttop(miniv3d(603513.7721,2373737.497,base),type,zone,datum);

grid_extent ext(leftbottom,lefttop,rightbottom,righttop);

grid_layer layer;

The layer API supports a variety of file formats through a generic interface. The most common used format is geotiff as supported by the GDAL implementation of the layer API, the grid_layer_gdal class. Besides geotiff the GDAL implementation also adds support for BT (Binary Terrain) and a slew of other common geospatial file formats. For a complete list of GDAL supported formats see here.

The libGrid package contains two other implementations of the layer API, that is grid_layer_db for the DB (DataBuf) format of libMini and grid_layer_pnm for the PNM (Portable Any Map) format with geospatial extensions as supported by libMini.

With the layer API it is almost a one-liner to load a layer from a file, for example with GDAL:

#include <grid/grid_layer_gdal.h>

grid_layer_gdal layer_gdal;
bool success=layer.load("filename.tif");

The layer API will only load the header information when opening a file, unless the raster data of a layer is accessed explicitly.

Satellite raster data often comes as multiple files, that is a tileset, mosaic or multiple channels. In order to group those layers together logically and perform certain operations on them such as interpolation or resampling, the layers can be stored in a dynamic array, the grid_layers class. This is done by appending pointers to each layer to the array:

#include <grid/grid_layer.h>
#include <grid/grid_layers.h>

grid_layer *a=new grid_layer;
grid_layer *b=new grid_layer;

grid_layers *layers=new grid_layers;


layers->do_something(); // e.g. interpolation, resampling etc.

delete layers;

delete a;
delete b;

A grid_layers object doesn’t manage the stored layers, it just keeps references to them. If we need to store a collection of managed layers that are automatically released when going out of scope, we use the grid_list container class. Layers appended to the container, will be released when the container is destroyed.

#include <grid/grid_list.h>

grid_layer *a=new grid_layer;
grid_layer *b=new grid_layer;

grid_list *list=new grid_list;

list->append(a); // list takes control over a
list->append(b); // list takes control over b


delete list; // implicitly releases managed layers

The grid_list class also offers a convenient loader wrapper for all known layer API implementations. As an example we load some height fields and interpolate the elevation at a well-known location given by a minicoord geo-referenced position:

#include <grid/grid_list.h>

grid_list list;

// load elevation of Oahu

// define coordinate at T.C.'s Helicopter Platform on Makai Pier
minicoord crd=minicoord(miniv3d(638128,2358101,0),

// query elevation at coordinate
double spacing=list.get_spacing();
double elev=list.interpolate(crd,spacing);

std::cout << "elevation=" << elev << "m" << std::endl;

-> elevation=2.49258m

The interpolate method returns the interpolated value of the first layer that has valid elevation (no no-data) at the specified coordinate. The spacing parameter defines which level of detail of the underlying raster data pyramid (grid_pyramid) is accessed. A spacing of zero will access the highest available resolution. The data pyramid is generated automatically on demand.

The most common use case of the grid_list class is to drive the resampling core of its superset the grid_tileset. With the resampling core one can reproject, resample and transform collections of raster data into tilesets that can be viewed with the libMini terrain renderer.

Another common use case is the merging core. As an example, see the PanSharpening or ColorMatching page.

Tools | | Programming Example