Shapefile Load-On-Demand

The Maps module provides classes to perform the load-on-demand on Shapefiles. This is achieved by using specific spatial index files. These files, usually having an .idx extension, store relations between tile and object identifiers that belong to these tiles. A class and a tool example are provided to generate these spatial index files.

The load-on-demand mechanism involves two classes in addition to the shape reader and the dbf reader: the IlvShapeFileIndex class and the IlvShapeSpatialIndex class. A utility class is also provided to generate the spatial index for a given Shapefile: the IlvShapeFileTiler class.

The following diagram illustrates the mechanism used to store and retrieve objects by tiles:

The Spatial Index file holds object identifiers for each tile. Object identifiers have their ordinal place in the Index File. Geometries are retrieved in the Shapefile using the Index File. In this diagram, for example, the tile [2, 1] (tile indices begin at 0) contains identifiers 2, 5, and 9 referring to the geometries g2, g5, and g9 respectively.

The following classes are used to perform load-on-demand on the Shapefiles:

The IlvShapeFileIndex Class

This class allows you to directly access geometries in a Shapefile. The spatial index and the Shapefile must correspond to the same theme:

IlvMapsError status;

// Open the index file.

IlvShapeFileIndex* index =

new IlvShapeFileIndex(shxFileName);

status = index->getInitStatus();

if(status != IlvMaps::NoError())

return status;

// Open the corresponding Shapefile.

IlvShapeSHPReader* shape =

new IlvShapeSHPReader(shpFileName);

status = shape->getInitStatus();

if(status != IlvMaps::NoError())

return status;

// Construct a reader from the Shapefile reader and the Shapefile index.

IlvShapeFileReader* reader =

new IlvShapeFileReader(shape, 0, index);

status = reader->getInitStatus();

if(status != IlvMaps::NoError())

return status;

 

// Retrieve the feature for each index.

IlInt count = index->getRecordCount();

for(IlInt i = 0; i < count; i++) {

const IlvMapFeature* feature = reader->getFeatureAt(i, status);

if(status != IlvMaps::NoError())

return status;

}

The IlvShapeSpatialIndex Class

This class stores tile information: tile size and count, identifiers of the objects belonging to each tile. To retrieve objects from a tile specified by its row and column, use the getIdArray() method:

// Create a reader with the Shapefile name and the index file name.

IlvShapeFileReader* reader =

new IlvShapeFileReader(shpFilename, 0, shxFilename);

// Open the spatial index.

IlvShapeSpatialIndex* spindex =

new IlvShapeSpatialIndex(idxFileName);

status = spindex->getInitStatus();

if(status != IlvMaps::NoError())

return status;

// Loop on all columns and rows.

for(int c = 0; c < spindex->getColumnCount(); c++) {

for(int r = 0; r < spindex->getRowCount(); r++) {

IlInt *ret;

IlUInt size;

// Retrieve the IDs of objects belonging to the tile

// at column 'c' and row 'w'.

status = spindex->getIdArray(c, r, ret, size);

if(status != IlvMaps::NoError())

return status;

// Loop on these IDs and retrieve the corresponding map feature.

for(int i = 0; i < size; i++) {

const IlvMapFeature* feature =

reader->getFeatureAt(ret[i], status);

if(status != IlvMaps::NoError())

return status;

}

// Free allocated array.

if(ret)

delete[] ret;

}

}

This class can also be used to generate your own tiling information for a given Shapefile:

IlvShapeSpatialIndex* tilerIndex = new

IlvShapeSpatialIndex(getColumnCount(),

getRowCount(),

getOrigin(),

getTileWidth(),

getTileHeight());

status = tilerIndex->getInitStatus();

if(status != IlvMaps::NoError())

return status;

IlvMapFeature* feature = (IlvMapFeature*)reader->getNextFeature(status);

if(status != IlvMaps::NoError())

return status;

int id = 0;

while(feature) {

// Determine to which tile(s) the object must belong.

int row = getRow(feature);

int col = getColumn(feature);

// Add it to the spatial index.

tilerIndex->add(row, col, id);

feature = (IlvMapFeature*)reader->getNextFeature(status);

if(status != IlvMaps::NoError())

return status;

id++;

}

// Write the spatial index.

tilerIndex->save("spatialIndex.idx");

The IlvShapeFileTiler Class

This class is used to generate tiling information from a given Shapefile. To use this class you have to provide the Shapefile to tile, the Spatial Index File to write to, and the tile size.

IlvShapeFileTiler::CreateShapeSpatialIndex("example.shp",

"example.idx",

(IlDouble)5, (IlDouble)10);

The above code extract will produce a Spatial Index File named example.idx with a tile size of width 5 and height 10. For example, if the upper left corner of the example.shp file is (x = -5, y = 20), and if the lower right corner is (x = 35, y = -30), the resulting tiling array will be 8 columns by 5 rows

IlvShapeFileTiler::CreateShapeSpatialIndex("example.shp",

"example.idx",

(IlInt)20, (IlInt)30);

The above code extract will produce a Spatial Index File of 600 tiles, 20 columns, and 30 rows.

The IlvShapeFileTileLoader Class

This class implements load-on-demand for tiled Shapefiles. When associated with an IlvTiledLayer, this class automatically handles tile loading if the Shapefile name, the Index File name, and the Spatial Index File name are provided. An optional Dbase file name can also be provided to load object attributes.

IlvMapAdapter a(0.001);

IlvShapeFileTileLoader* tileLoader =

new IlvShapeFileTileLoader(shpFileName,

dbfFileName, // or null if attribute

// loading is not wanted.

shxFileName,

idxFileName,

&a);

IlvTiledLayer* tiledLayer = new IlvTiledLayer(getTileOrigin());

tiledLayer->setTileLoader(tileLoader);

The IlvShapeLayer Class

The IlvShapeFileLayer class is an IlvTiledLayer that can be saved in an .ilv file. In particular, it handles all the mechanisms to properly restart the load-on-demand layer when this layer is read from an .ilv file.

IlvMapAdapter a(0.001);

IlvShapeFileTileLoader* tileLoader =

new IlvShapeFileTileLoader(shpFileName,

dbfFileName, // or null if attribute

// loading is not wanted.

shxFileName,

idxFileName,

&a);

IlvShapeFileLayer* shapeLayer = new IlvShapeFileLayer(tileLoader);

IlvManager* manager = new IlvManager(0);

manager->addLayer(shapeLayer);

manager->save(ofstream("out.ilv"));