OGR API 使用向导
翻译:柴树杉(chaishushan@gmail.com)
原文:http://www.gdal.org/ogr/ogr_apitut.html
该文档讲述了怎么样用OGR的类读/写一个文件。其中侧重介绍了OGR中一些比较关键类的用法。
用OGR读
为了演示怎么用OGR读数据,我们创建了一个小程序:从数据源中读point层, 然后用逗号分隔格式输出到stdout。开始的时候一般需要注册所有格式的驱动, 可以通过调用OGRRegisterAll()函数实现。
#include "ogrsf_frmts.h" int main() { OGRRegisterAll();
接下来需要打开用于输入的数据源(Datasources)。Datasources可以是文件、RDBMSes、 目录中的所有文件或者是被连接到本机的 web地址。通常,datasource都对应一个字符串 的名字。在这个例子中,我们将要打开一个名point.shp为的shapefile格式的文件。 第二个参数FALSE表示OGRSFDriverRegistrar::Open()
方法并不需要更新的权限。 如果操作失败,则会返回NULL,我们则简单输出失败的信息。
OGRDataSource *poDS; poDS = OGRSFDriverRegistrar::Open( "point.shp", FALSE ); if( poDS == NULL ) { printf( "Open failed.\n" ); exit( 1 ); }
一个OGRDataSource一般含有许多层(layer)。OGRDataSource::GetLayerCount()
可以返回 OGRDataSource中层的总数目。OGRDataSource::GetLayer()
可以通过层 的索引编号获取一个层。当然,这个例子中我们是通过层的名字访问层。
OGRLayer *poLayer;
poLayer = poDS->GetLayerByName( "point" );
现在我们可以从获取的层中读取各种实体了(feature)。在读之前,我们可以设置 一个过滤器,那样的话后面顺序返回的将都是没有被过滤的实体。这里我们是获取层 中所有的实体。
在我们从层中读实体之前需要调用OGRLayer::ResetReading()
函数重新设置读操作 (即定位到开头)。然后OGRLayer::GetNextFeature()
依次返回下一个实体,如果 返回NULL的话表示全部读完。
OGRFeature *poFeature;
poLayer->ResetReading();
while( (poFeature = poLayer->GetNextFeature()) != NULL )
{
为了获取实体的每个属性字段的信息,可以获取OGRFeatureDefn。OGRFeatureDefn是 一个和当前层相关的对象,它包含了该层中定义的所有属性字段。我们循环访问所有的 属性字段,并且输出每个字段对应的值。
OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn(); int iField; for( iField = 0; iField < poFDefn->GetFieldCount(); iField++ ) { OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn( iField ); if( poFieldDefn->GetType() == OFTInteger ) printf( "%d,", poFeature->GetFieldAsInteger( iField ) ); else if( poFieldDefn->GetType() == OFTReal ) printf( "%.3f,", poFeature->GetFieldAsDouble(iField) ); else if( poFieldDefn->GetType() == OFTString ) printf( "%s,", poFeature->GetFieldAsString(iField) ); else printf( "%s,", poFeature->GetFieldAsString(iField) ); }
这里只有很少的几种字段类型。并且任何类型的字段都可以用OGRFeature::GetFieldAsString()
方法获取。接下来我们从实体中导出集合图元,并且输出点图元的坐标。几何图元以OGRGeometry 指针的方式返回。我们可以判断几何图元的类型,如果是点则输出坐标,如果不是则输出提示信息。
OGRGeometry *poGeometry; poGeometry = poFeature->GetGeometryRef(); if( poGeometry != NULL && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) { OGRPoint *poPoint = (OGRPoint *) poGeometry; printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() ); } else { printf( "no point geometry\n" ); }
上面代码中的wkbFlatten()宏用于将wkbPoint25D类型(含有z坐标的点)转换为2D的类型。 每个2D的集合图元类型都对应一个 2.5D的编码,然而这里只有一个C++类用于处理2D和3D 情形,因此我们在代码中可能需要对2D或3D情形做适当的处理。
需要注意的是OGRFeature::GetGeometryRef()
返回的是OGRFeature中数据的的指针, 因此用完之后我们不能删除 poGeometry指针对应内容。但是OGRLayer::GetNextFeature()
返回的是层中当前实体的一个拷贝,因此在使用完 poFeature之后必须手工释放。 释放poFeature不能直接delete,正确的方式是用OGR提供的 OGRFeature::DestroyFeature()
函数释放。
OGRFeature::DestroyFeature( poFeature ); }
OGRDataSource::GetLayerByName()
返回的OGRLayer是OGRDataSource中数据的引用, 因此我们可以不用delete它。但是我们需要删除OGRDataSource本身,这样才会关闭文件。 同样,我们用OGRFeature::DestroyFeature()
函数删除poDS(delete poDS在win32系统 上可能出现问题)。
OGRDataSource::DestroyDataSource( poDS ); }
所有的程序合到一起如下:
#include "ogrsf_frmts.h" int main() { OGRRegisterAll(); OGRDataSource *poDS; poDS = OGRSFDriverRegistrar::Open( "point.shp", FALSE ); if( poDS == NULL ) { printf( "Open failed.\n%s" ); exit( 1 ); } OGRLayer *poLayer; poLayer = poDS->GetLayerByName( "point" ); OGRFeature *poFeature; poLayer->ResetReading(); while( (poFeature = poLayer->GetNextFeature()) != NULL ) { OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn(); int iField; for( iField = 0; iField < poFDefn->GetFieldCount(); iField++ ) { OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn( iField ); if( poFieldDefn->GetType() == OFTInteger ) printf( "%d,", poFeature->GetFieldAsInteger( iField ) ); else if( poFieldDefn->GetType() == OFTReal ) printf( "%.3f,", poFeature->GetFieldAsDouble(iField) ); else if( poFieldDefn->GetType() == OFTString ) printf( "%s,", poFeature->GetFieldAsString(iField) ); else printf( "%s,", poFeature->GetFieldAsString(iField) ); } OGRGeometry *poGeometry; poGeometry = poFeature->GetGeometryRef(); if( poGeometry != NULL && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) { OGRPoint *poPoint = (OGRPoint *) poGeometry; printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() ); } else { printf( "no point geometry\n" ); } OGRFeature::DestroyFeature( poFeature ); } OGRDataSource::DestroyDataSource( poDS ); }
用OGR写
作为用OGR写数据的例子,下面的程序中stdin顺序读以逗号分割的点坐标,然后保存 到point_out.shp文件。和写一样,在程序开头需要注册所有的驱动。然后我们获取 一个相应格式的驱动用于创建输出文件。
#include "ogrsf_frmts.h" int main() { const char *pszDriverName = "ESRI Shapefile"; OGRSFDriver *poDriver; OGRRegisterAll(); poDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName( pszDriverName ); if( poDriver == NULL ) { printf( "%s driver not available.\n", pszDriverName ); exit( 1 ); }
接着我们创建一个datasource(和驱动是相同的格式)。ESRI Shapefile格式的驱动 可以支持创建多个shapefiles(在一个木中),也可以支持创建一个单一的shapefile。 这里,我们通过指定.shp扩展名表示创建一个单一的shapefile。其他格式的驱动调用规则 可能并不相同。第二个参数是一个选项列表(是一个以NULL结尾的字符串数组), 这里我们采用默认选项。选项列表的规则根据格式的不同而不同。
OGRDataSource *poDS; poDS = poDriver->CreateDataSource( "point_out.shp", NULL ); if( poDS == NULL ) { printf( "Creation of output file failed.\n" ); exit( 1 ); }
现在我们创建一个用于输出的层。在这个程序中,因为datasource只对应一个单一的 文件,因此只能创建一个层。参数wkbPoint表示该曾需要支持的类型。在该程序中, 同样不能指定任何坐标系统或者其他特殊的选项。
OGRLayer *poLayer; poLayer = poDS->CreateLayer( "point_out", NULL, wkbPoint, NULL ); if( poLayer == NULL ) { printf( "Layer creation failed.\n" ); exit( 1 ); }
层被创建之后,我们可以为层定义任何需要的属性字段。属性字段必须在任何 实体添加到层之前被写入。我们创建一个属性字段对象,并且初始化。在 Shapefiles中, width和precision是比较重要的被创建在.dbf文件中,这里我们对它单独设置。 在这个程序中我们只创建了一个属性,即点的名字。
需要注意的是CreateField()将OGRField重新复制了一份,因此我们仍然持有oField的所有权。
OGRFieldDefn oField( "Name", OFTString ); oField.SetWidth(32); if( poLayer->CreateField( &oField ) != OGRERR_NONE ) { printf( "Creating Name field failed.\n" ); exit( 1 ); } \encode 下面从stdin中循环读取"x,y,name"格式的点信息。 \code double x, y; char szName[33]; while( !feof(stdin) && fscanf( stdin, "%lf,%lf,%32s", &x, &y, szName ) == 3 ) {
为了将feature写入磁盘,我们需要创建一个OGRFeature对象,并且和层对应的属性 帮定(即需要和OGRFeatureDefn关联)。然后设置feature的相关属性。
OGRFeature *poFeature; poFeature = new OGRFeature( poLayer->GetLayerDefn() ); poFeature->SetField( "Name", szName );
现在我们创建一个具体的几何图元,然后将它的拷贝指定给feature。 OGRFeature::SetGeometryDirectly()
和 OGRFeature::SetGeometry()
函数的不同 是SetGeometryDirectly函数将几何图元的所有全给予feature。
OGRPoint pt; pt.setX( x ); pt.setY( y ); poFeature->SetGeometry( &pt );
创建一个feature。OGRLayer::CreateFeature()
只是重新复制了一个feature,因此 操作完成后需要清除feature对象。
if( poLayer->CreateFeature( poFeature ) != OGRERR_NONE ) { printf( "Failed to create feature in shapefile.\n" ); exit( 1 ); } OGRFeature::DestroyFeature( poFeature ); }
最后释放datasource资源,刷新所有的写操作。
OGRDataSource::DestroyDataSource( poDS ); }
所有的程序合到一起如下:
#include "ogrsf_frmts.h" int main() { const char *pszDriverName = "ESRI Shapefile"; OGRSFDriver *poDriver; OGRRegisterAll(); poDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName( pszDriverName ); if( poDriver == NULL ) { printf( "%s driver not available.\n", pszDriverName ); exit( 1 ); } OGRDataSource *poDS; poDS = poDriver->CreateDataSource( "point_out.shp", NULL ); if( poDS == NULL ) { printf( "Creation of output file failed.\n" ); exit( 1 ); } OGRLayer *poLayer; poLayer = poDS->CreateLayer( "point_out", NULL, wkbPoint, NULL ); if( poLayer == NULL ) { printf( "Layer creation failed.\n" ); exit( 1 ); } OGRFieldDefn oField( "Name", OFTString ); oField.SetWidth(32); if( poLayer->CreateField( &oField ) != OGRERR_NONE ) { printf( "Creating Name field failed.\n" ); exit( 1 ); } double x, y; char szName[33]; while( !feof(stdin) && fscanf( stdin, "%lf,%lf,%32s", &x, &y, szName ) == 3 ) { OGRFeature *poFeature; poFeature = new OGRFeature( poLayer->GetLayerDefn() ); poFeature->SetField( "Name", szName ); OGRPoint pt; pt.setX( x ); pt.setY( y ); poFeature->SetGeometry( &pt ); if( poLayer->CreateFeature( poFeature ) != OGRERR_NONE ) { printf( "Failed to create feature in shapefile.\n" ); exit( 1 ); } OGRFeature::DestroyFeature( poFeature ); } OGRDataSource::DestroyDataSource( poDS ); }