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 );
}
posted @ 2011-12-07 08:56  bigbigtree  阅读(3262)  评论(0编辑  收藏  举报