ベクターAPIチュートリアル

このドキュメントは、OGR C++クラスを使用してファイルからデータを読み書きする方法について説明することを目的としています.OGRの主要なクラスとその役割について説明した ベクターデーターモデル ドキュメントを最初に読むことを強くお勧めします.

また、それに対応するCおよびPythonの関数のコードスニペットも含まれています.

OGRからの読み込み

OGRでの読み込みをデモンストレーションするために、OGRデータソースからポイントレイヤーを標準出力にカンマ区切り形式でダンプする小さなユーティリティを構築します.

最初に,必要なすべてのフォーマットドライバを登録する必要があります. これは通常、GDAL/OGRに組み込まれたすべてのフォーマットドライバを登録する GDALAllRegister() を呼び出すことで行われます.

C++の場合 :

#include "ogrsf_frmts.h"

int main()

{
    GDALAllRegister();

Cの場合 :

#include "gdal.h"

int main()

{
    GDALAllRegister();

次に、入力のOGRデータソースを開く必要があります. データソースはファイル、RDBMS、ファイルでいっぱいのディレクトリ、または使用されているドライバによっては、リモートWebサービスである場合があります. ただし、データソース名は常に単一の文字列です. この場合、特定のシェープファイルを開くようにハードコードされています.2番目の引数 (GDAL_OF_VECTOR) は、OGROpen() メソッドに、ベクタードライバを使用することを指示し、更新アクセスは不要であることを示します. 失敗した場合はNULLが返され、エラーが報告されます.

C++の場合 :

GDALDataset       *poDS;

poDS = (GDALDataset*) GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL );
if( poDS == NULL )
{
    printf( "Open failed.\n" );
    exit( 1 );
}

Cの場合 :

GDALDatasetH hDS;

hDS = GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL );
if( hDS == NULL )
{
    printf( "Open failed.\n" );
    exit( 1 );
}

GDALDatasetには,関連付けられているレイヤーが多数存在する可能性があります. 利用可能なレイヤーの数は GDALDataset::GetLayerCount() で問い合わせることができ、個々のレイヤーは GDALDataset::GetLayer() を使用してインデックスで取得することができます. ただし、ここではレイヤーを名前で取得します.

C++の場合 :

OGRLayer  *poLayer;

poLayer = poDS->GetLayerByName( "point" );

Cの場合 :

OGRLayerH hLayer;

hLayer = GDALDatasetGetLayerByName( hDS, "point" );

次に,レイヤーから地物を読み始めたいと思います. 開始する前に,レイヤーに属性フィルタまたは空間フィルタを割り当てて,取得する地物のセットを制限することができますが,今のところはすべての地物を取得することに興味があります.

GDAL 2.3とC++11を使用する場合:

for( auto& poFeature: poLayer )
{

GDAL 2.3とCを使用する場合:

OGR_FOR_EACH_FEATURE_BEGIN(hFeature, hLayer)
{

古いGDALバージョンを使用する場合,レイヤーを新しく開始しているため厳密には必要ではありませんが,しばしば OGRLayer::ResetReading() を呼び出して,レイヤーの先頭から開始していることを確認することが賢明です. OGRLayer::GetNextFeature() を使用してレイヤー内のすべての地物を反復処理します.地物がなくなるとNULLが返されます.

GDAL < 2.3とC++を使用する場合:

OGRFeature *poFeature;

poLayer->ResetReading();
while( (poFeature = poLayer->GetNextFeature()) != NULL )
{

GDAL < 2.3とCを使用する場合:

OGRFeatureH hFeature;

OGR_L_ResetReading(hLayer);
while( (hFeature = OGR_L_GetNextFeature(hLayer)) != NULL )
{

地物のすべての属性フィールドをダンプするためには, OGRFeatureDefn を取得すると便利です.これは,レイヤーに関連付けられたオブジェクトで,すべてのフィールドの定義を含んでいます. すべてのフィールドを反復処理し,そのタイプに基づいて属性を取得して報告します.

GDAL 2.3とC++11を使用する場合:

for( auto&& oField: *poFeature )
{
    if( oField.IsUnset() )
    {
        printf("(unset),");
        continue;
    }
    if( oField.IsNull() )
    {
        printf("(null),");
        continue;
    }
    switch( oField.GetType() )
    {
        case OFTInteger:
            printf( "%d,", oField.GetInteger() );
            break;
        case OFTInteger64:
            printf( CPL_FRMT_GIB ",", oField.GetInteger64() );
            break;
        case OFTReal:
            printf( "%.3f,", oField.GetDouble() );
            break;
        case OFTString:
            // GetString() returns a C string
            printf( "%s,", oField.GetString() );
            break;
        default:
            // Note: we use GetAsString() and not GetString(), since
            // the later assumes the field type to be OFTString while the
            // former will do a conversion from the original type to string.
            printf( "%s,", oField.GetAsString() );
            break;
    }
}

GDAL < 2.3とC++を使用する場合:

OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
{
    if( !poFeature->IsFieldSet(iField) )
    {
        printf("(unset),");
        continue;
    }
    if( poFeature->IsFieldNull(iField) )
    {
        printf("(null),");
        continue;
    }
    OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn( iField );

    switch( poFieldDefn->GetType() )
    {
        case OFTInteger:
            printf( "%d,", poFeature->GetFieldAsInteger( iField ) );
            break;
        case OFTInteger64:
            printf( CPL_FRMT_GIB ",", poFeature->GetFieldAsInteger64( iField ) );
            break;
        case OFTReal:
            printf( "%.3f,", poFeature->GetFieldAsDouble(iField) );
            break;
        case OFTString:
            printf( "%s,", poFeature->GetFieldAsString(iField) );
            break;
        default:
            printf( "%s,", poFeature->GetFieldAsString(iField) );
            break;
    }
}

Cの場合 :

OGRFeatureDefnH hFDefn = OGR_L_GetLayerDefn(hLayer);
int iField;

for( iField = 0; iField < OGR_FD_GetFieldCount(hFDefn); iField++ )
{
    OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn( hFDefn, iField );

    if( !OGR_F_IsFieldSet(hFeature, iField) )
    {
        printf("(unset),");
        continue;
    }
    if( OGR_F_IsFieldNull(hFeature, iField) )
    {
        printf("(null),");
        continue;
    }

    switch( OGR_Fld_GetType(hFieldDefn) )
    {
        case OFTInteger:
            printf( "%d,", OGR_F_GetFieldAsInteger( hFeature, iField ) );
            break;
        case OFTInteger64:
            printf( CPL_FRMT_GIB ",", OGR_F_GetFieldAsInteger64( hFeature, iField ) );
            break;
        case OFTReal:
            printf( "%.3f,", OGR_F_GetFieldAsDouble( hFeature, iField) );
            break;
        case OFTString:
            printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) );
            break;
        default:
            printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) );
            break;
    }
}

上記で明示的に処理されているフィールドタイプ以外にもいくつかのフィールドタイプがありますが, OGRFeature::GetFieldAsString() メソッドを使用してそれらの合理的な表現を取得することができます.実際,GetFieldAsString() をすべてのタイプに使用して上記を短縮することができます.

次に,地物からジオメトリを抽出し,ポイントジオメトリのxとyを書き出します. ジオメトリは一般的な OGRGeometry ポインタとして返されます. 次に,特定のジオメトリタイプを決定し,ポイントである場合はポイントにキャストして操作します. それ以外の場合はプレースホルダを書き込みます.

C++の場合 :

OGRGeometry *poGeometry;

poGeometry = poFeature->GetGeometryRef();
if( poGeometry != NULL
        && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
{
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,3,0)
    OGRPoint *poPoint = poGeometry->toPoint();
#else
    OGRPoint *poPoint = (OGRPoint *) poGeometry;
#endif

    printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() );
}
else
{
    printf( "no point geometry\n" );
}

Cの場合 :

OGRGeometryH hGeometry;

hGeometry = OGR_F_GetGeometryRef(hFeature);
if( hGeometry != NULL
        && wkbFlatten(OGR_G_GetGeometryType(hGeometry)) == wkbPoint )
{
    printf( "%.3f,%3.f\n", OGR_G_GetX(hGeometry, 0), OGR_G_GetY(hGeometry, 0) );
}
else
{
    printf( "no point geometry\n" );
}

上記で wkbFlatten() マクロを使用して, wkbPoint25D (z座標を持つポイント) のタイプを基本の2Dジオメトリタイプコード (wkbPoint) に変換します. 各2Dジオメトリタイプには対応する2.5Dタイプコードがあります.2Dおよび2.5Dジオメトリケースは同じC++クラスで処理されるため,コードは2Dまたは3Dケースを適切に処理します.

複数のジオメトリフィールドを地物に関連付けることができます.

C++の場合 :

OGRGeometry *poGeometry;
int iGeomField;
int nGeomFieldCount;

nGeomFieldCount = poFeature->GetGeomFieldCount();
for(iGeomField = 0; iGeomField < nGeomFieldCount; iGeomField ++ )
{
    poGeometry = poFeature->GetGeomFieldRef(iGeomField);
    if( poGeometry != NULL
            && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
    {
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,3,0)
        OGRPoint *poPoint = poGeometry->toPoint();
#else
        OGRPoint *poPoint = (OGRPoint *) poGeometry;
#endif

        printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() );
    }
    else
    {
        printf( "no point geometry\n" );
    }
}

Cの場合 :

OGRGeometryH hGeometry;
int iGeomField;
int nGeomFieldCount;

nGeomFieldCount = OGR_F_GetGeomFieldCount(hFeature);
for(iGeomField = 0; iGeomField < nGeomFieldCount; iGeomField ++ )
{
    hGeometry = OGR_F_GetGeomFieldRef(hFeature, iGeomField);
    if( hGeometry != NULL
            && wkbFlatten(OGR_G_GetGeometryType(hGeometry)) == wkbPoint )
    {
        printf( "%.3f,%3.f\n", OGR_G_GetX(hGeometry, 0),
                OGR_G_GetY(hGeometry, 0) );
    }
    else
    {
        printf( "no point geometry\n" );
    }
}

Pythonの場合:

nGeomFieldCount = feat.GetGeomFieldCount()
for iGeomField in range(nGeomFieldCount):
    geom = feat.GetGeomFieldRef(iGeomField)
    if geom is not None and geom.GetGeometryType() == ogr.wkbPoint:
        print "%.3f, %.3f" % ( geom.GetX(), geom.GetY() )
    else:
        print "no point geometry\n"

OGRFeature::GetGeometryRef()OGRFeature::GetGeomFieldRef() は, OGRFeatureが所有する内部ジオメトリへのポインタを返します. そこで,実際には返されたジオメトリを削除しません.

GDAL 2.3とC++11を使用する場合,地物の反復処理は単純に閉じカーリーブラケットで終了します.

}

GDAL 2.3とCを使用する場合,地物の反復処理は単純に以下で終了します.

}
OGR_FOR_EACH_FEATURE_END(hFeature)

GDAL < 2.3の場合, OGRLayer::GetNextFeature() メソッドは,現在所有している地物のコピーを返します.そのため,使用の最後に地物を解放する必要があります. 単に "delete" することもできますが,これはGDAL DLLがメインプログラムと異なる "ヒープ" を持つWindowsビルドで問題を引き起こす可能性があります. 安全を期すために,GDAL関数を使用して地物を削除します.

C++の場合 :

    OGRFeature::DestroyFeature( poFeature );
}

Cの場合 :

    OGR_F_Destroy( hFeature );
}

GDALDataset::GetLayerByName() で返されるOGRLayerも,GDALDatasetが所有する内部レイヤーへの参照です.そのため,削除する必要はありません. ただし,入力ファイルを閉じるためにデータソースを削除する必要があります.再び,特別なwin32ヒープの問題を回避するために,これをカスタム削除メソッドで行います.

C/C++の場合 :

    GDALClose( poDS );
}

すべてのプログラムは次のようになります.

GDAL 2.3とC++11を使用する場合 :

#include "ogrsf_frmts.h"

int main()

{
    GDALAllRegister();

    GDALDatasetUniquePtr poDS(GDALDataset::Open( "point.shp", GDAL_OF_VECTOR));
    if( poDS == nullptr )
    {
        printf( "Open failed.\n" );
        exit( 1 );
    }

    for( const OGRLayer* poLayer: poDS->GetLayers() )
    {
        for( const auto& poFeature: *poLayer )
        {
            for( const auto& oField: *poFeature )
            {
                if( oField.IsUnset() )
                {
                    printf("(unset),");
                    continue;
                }
                if( oField.IsNull() )
                {
                    printf("(null),");
                    continue;
                }
                switch( oField.GetType() )
                {
                    case OFTInteger:
                        printf( "%d,", oField.GetInteger() );
                        break;
                    case OFTInteger64:
                        printf( CPL_FRMT_GIB ",", oField.GetInteger64() );
                        break;
                    case OFTReal:
                        printf( "%.3f,", oField.GetDouble() );
                        break;
                    case OFTString:
                        // GetString() returns a C string
                        printf( "%s,", oField.GetString() );
                        break;
                    default:
                        // Note: we use GetAsString() and not GetString(), since
                        // the later assumes the field type to be OFTString while the
                        // former will do a conversion from the original type to string.
                        printf( "%s,", oField.GetAsString() );
                        break;
                }
            }

            const OGRGeometry *poGeometry = poFeature->GetGeometryRef();
            if( poGeometry != nullptr
                    && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
            {
                const OGRPoint *poPoint = poGeometry->toPoint();

                printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() );
            }
            else
            {
                printf( "no point geometry\n" );
            }
        }
    }
    return 0;
}

C++の場合 :

#include "ogrsf_frmts.h"

int main()

{
    GDALAllRegister();

    GDALDataset *poDS = static_cast<GDALDataset*>(
        GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL ));
    if( poDS == NULL )
    {
        printf( "Open failed.\n" );
        exit( 1 );
    }

    OGRLayer  *poLayer = poDS->GetLayerByName( "point" );
    OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();

    poLayer->ResetReading();
    OGRFeature *poFeature;
    while( (poFeature = poLayer->GetNextFeature()) != NULL )
    {
        for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
        {
            if( !poFeature->IsFieldSet(iField) )
            {
                printf("(unset),");
                continue;
            }
            if( poFeature->IsFieldNull(iField) )
            {
                printf("(null),");
                continue;
            }
            OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn( iField );

            switch( poFieldDefn->GetType() )
            {
                case OFTInteger:
                    printf( "%d,", poFeature->GetFieldAsInteger( iField ) );
                    break;
                case OFTInteger64:
                    printf( CPL_FRMT_GIB ",", poFeature->GetFieldAsInteger64( iField ) );
                    break;
                case OFTReal:
                    printf( "%.3f,", poFeature->GetFieldAsDouble(iField) );
                    break;
                case OFTString:
                    printf( "%s,", poFeature->GetFieldAsString(iField) );
                    break;
                default:
                    printf( "%s,", poFeature->GetFieldAsString(iField) );
                    break;
            }
        }

        OGRGeometry *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 );
    }

    GDALClose( poDS );
}

Cの場合 :

#include "gdal.h"

int main()

{
    GDALAllRegister();

    GDALDatasetH hDS;
    OGRLayerH hLayer;
    OGRFeatureH hFeature;
    OGRFeatureDefnH hFDefn;

    hDS = GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL );
    if( hDS == NULL )
    {
        printf( "Open failed.\n" );
        exit( 1 );
    }

    hLayer = GDALDatasetGetLayerByName( hDS, "point" );
    hFDefn = OGR_L_GetLayerDefn(hLayer);

    OGR_L_ResetReading(hLayer);
    while( (hFeature = OGR_L_GetNextFeature(hLayer)) != NULL )
    {
        int iField;
        OGRGeometryH hGeometry;

        for( iField = 0; iField < OGR_FD_GetFieldCount(hFDefn); iField++ )
        {
            OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn( hFDefn, iField );

            if( !OGR_F_IsFieldSet(hFeature, iField) )
            {
                printf("(unset),");
                continue;
            }
            if( OGR_F_IsFieldNull(hFeature, iField) )
            {
                printf("(null),");
                continue;
            }

            switch( OGR_Fld_GetType(hFieldDefn) )
            {
                case OFTInteger:
                    printf( "%d,", OGR_F_GetFieldAsInteger( hFeature, iField ) );
                    break;
                case OFTInteger64:
                    printf( CPL_FRMT_GIB ",", OGR_F_GetFieldAsInteger64( hFeature, iField ) );
                    break;
                case OFTReal:
                    printf( "%.3f,", OGR_F_GetFieldAsDouble( hFeature, iField) );
                    break;
                case OFTString:
                    printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) );
                    break;
                default:
                    printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) );
                    break;
            }
        }

        hGeometry = OGR_F_GetGeometryRef(hFeature);
        if( hGeometry != NULL
            && wkbFlatten(OGR_G_GetGeometryType(hGeometry)) == wkbPoint )
        {
            printf( "%.3f,%3.f\n", OGR_G_GetX(hGeometry, 0), OGR_G_GetY(hGeometry, 0) );
        }
        else
        {
            printf( "no point geometry\n" );
        }

        OGR_F_Destroy( hFeature );
    }

    GDALClose( hDS );
}

Pythonの場合:

import sys
from osgeo import gdal

ds = gdal.OpenEx( "point.shp", gdal.OF_VECTOR )
if ds is None:
    print "Open failed.\n"
    sys.exit( 1 )

lyr = ds.GetLayerByName( "point" )

lyr.ResetReading()

for feat in lyr:

    feat_defn = lyr.GetLayerDefn()
    for i in range(feat_defn.GetFieldCount()):
        field_defn = feat_defn.GetFieldDefn(i)

        # Tests below can be simplified with just :
        # print feat.GetField(i)
        if field_defn.GetType() == ogr.OFTInteger or field_defn.GetType() == ogr.OFTInteger64:
            print "%d" % feat.GetFieldAsInteger64(i)
        elif field_defn.GetType() == ogr.OFTReal:
            print "%.3f" % feat.GetFieldAsDouble(i)
        elif field_defn.GetType() == ogr.OFTString:
            print "%s" % feat.GetFieldAsString(i)
        else:
            print "%s" % feat.GetFieldAsString(i)

    geom = feat.GetGeometryRef()
    if geom is not None and geom.GetGeometryType() == ogr.wkbPoint:
        print "%.3f, %.3f" % ( geom.GetX(), geom.GetY() )
    else:
        print "no point geometry\n"

ds = None

Arrow C Streamデータインターフェースを使用してOGRから読み込む

Added in version 3.6.

地物を1つずつ取得する代わりに,列指向のメモリレイアウトを使用してバッチで取得することも可能であり, OGRLayer::GetArrowStream() メソッドを使用します. このメソッドは,伝統的な OGRLayer::GetNextFeature() アプローチよりも使用が難しく, Apache Arrow C Stream interface との互換性が必要な場合や,レイヤーの列指向の利用が必要な場合にのみ推奨されます.

ヘルパーライブラリを使用するまで,Arrow C Streamインターフェースの利用には以下のドキュメントの読み取りが必要です:

Arrow C Streamインターフェースは,2つの主要なコールバックを提供する一連のC構造体,ArrowArrayStreamで構成されています:

  • get_schema() コールバックでArrowSchemaを取得します. ArrowSchemaは,フィールドの説明 (名前,タイプ,メタデータ) のセットを記述します. すべてのOGRデータ型には対応するArrowデータ型があります.

  • get_next() コールバックでArrowArrayのシーケンスを取得します. ArrowArrayは,特定の列/フィールドの値のセットを特定の地物のサブセットでキャプチャします. これはPandas DataFrameの Series の相当物です. これは,サブ配列を集約できる可能性のある階層構造であり, OGRの使用では,主な配列はOGR属性およびジオメトリフィールドのコレクションであるStructArrayになります.データ型ごとのバッファと子配列のレイアウトは, Arrow Columnar Format で詳細に説明されています.

レイヤーが2つのフィールド (1つは整数型,1つは浮動小数点型) の4つの地物で構成されている場合,ArrowArrayとしての表現は,*概念的に* 以下のようになります:

array.children[0].buffers[1] = { 1, 2, 3, 4 };
array.children[1].buffers[1] = { 1.2, 2.3, 3.4, 4.5 };

レイヤー全体の内容は,各レコードバッチが地物のサブセットのArrowArrayであるレコードバッチのシーケンスとして見ることができます.個々の地物を反復処理する代わりに,一度に複数の地物のバッチを反復処理します.

ArrowArrayStream, ArrowSchema, ArrowArray構造体は,API/ABI互換性をApache Arrow C++と取得するために, ogr_recordbatch.h パブリックヘッダファイルに直接派生させて定義されています. このヘッダファイルは,関連する配列バッチAPIが使用される場合に明示的に含める必要があります.

GetArrowStream() メソッドのシグネチャは次のようになります:

virtual bool OGRLayer::GetArrowStream(struct ArrowArrayStream* out_stream,
                                      CSLConstList papszOptions = nullptr);

C APIでも OGR_L_GetArrowStream() として利用可能です.

out_streamは,初期化されていない状態のArrowArrayStream構造体へのポインタであり,メソッドは初期内容を無視します.

成功した場合,およびストリームインターフェイスが不要になった場合, out_stream->release(out_stream) で解放する必要があります.

特定のドライバ実装によって指定されていない限り,ArrowArrayStream構造体およびそのコールバックが返したArrowSchemaまたはArrowArrayオブジェクトは,初期化されたOGRLayerが破棄された後 (通常はデータセットのクローズ時) には使用しないでください(解放される可能性があります). さらに,特定のドライバ実装によって指定されていない限り,特定のレイヤーで同時に1つのArrowArrayStreamのみがアクティブになります (つまり,次のものを要求する前に最後にアクティブになったものを明示的に解放する必要があります). ArrowArrayStreamを使用している間にフィルタ状態を変更したり,無視される列を変更したり,スキーマを変更したり,ResetReading()/GetNextFeature()を使用したりすることは強く推奨されず,予期しない結果につながる可能性があります. 一般的なルールとして,ArrowArrayStreamがアクティブな間にレイヤーの状態に影響を与えるOGRLayerメソッドは呼び出されるべきではありません.

提供される可能性のあるpapszOptionsは,キー=値の文字列のNULL終端リストであり,ドライバに固有のものである可能性があります.

OGRLayerには,次のようなGetArrowStream()の基本実装があります:

  • get_schema() コールバックは,トップレベルオブジェクトがStruct型であり,子がFID列,すべてのOGR属性フィールドおよびジオメトリフィールドをArrowフィールドに変換したスキーマを返します. FID列は,INCLUDE_FID=NOオプションを指定することで省略することができます.

    get_schema() が0を返し,スキーマが不要になった場合,以下の手順で解放する必要があります. これは,Arrow Cデータインターフェースで文書化されているように,他のコードによって解放されている可能性があることを考慮に入れるためです:

    if( out_schema->release )
        out_schema->release(out_schema)
    
  • get_next() コールバックは,レイヤー上の次のレコードバッチを取得します.

    out_arrayは,初期化されていない状態のArrowArray構造体へのポインタであり,メソッドは初期内容を無視します.

    デフォルトの実装は,GetNextFeature()を内部的に使用して,最大65,536地物のバッチを取得します (MAX_FEATURES_IN_BATCH=numオプションで設定可能). デフォルトの実装によって割り当てられたバッファの開始アドレスは,64バイトの境界に整列されます.

    デフォルトの実装は,スキーマの対応するエントリが ARROW:extension:name メタデータ項目に ogc.wkb と設定されている,バイナリフィールド内のWKBとしてジオメトリを出力します. 専門の実装は,他のフォーマットをデフォルトで出力する場合があります(特に,GeoArrow仕様に従ってエンコードされたジオメトリを返すArrowドライバ). GEOMETRY_ENCODING=WKBオプションを渡すことで,WKBの使用を強制することができます (デフォルトの実装を介して).

    メソッドは,SetIgnoredFields() で設定された無視されるフィールドを考慮に入れることができます (デフォルトの実装は行います),SetSpatialFilter() および SetAttributeFilter() で設定されたフィルタを考慮に入れる必要があります. ただし,特定の実装は,フィルタが設定されている場合にデフォルト (遅い) 実装にフォールバックする場合があります.

    GetNextFeature() と get_next() の呼び出しを混在させることは推奨されません. 動作は未指定です (ただし,クラッシュすることはありません).

    get_next() が0を返し,配列が不要になった場合,以下の手順で解放する必要があります. これは,Arrow Cデータインターフェースで文書化されているように,他のコードによって解放されている可能性があることを考慮に入れるためです:

    if( out_array->release )
        out_array->release(out_array)
    

専門の実装を持つドライバは,新しいOLCFastGetArrowStreamレイヤー機能を表明します.

ArrowArrayを直接使用する (プロデューサまたはコンシューマとして) ことは確かに簡単ではなく,Arrow Cデータインターフェースと列配列仕様についての深い理解が必要です. どのバッファからデータを読み取るか,どのデータ型のvoid*バッファをキャストするか,null/not_null情報を含むバッファを使用する方法,リスト型のデータ型のオフセットバッファを使用する方法などを知る必要があります.SWIG Pythonバインディング (gdal_array.i ) のgdal_array._RecordBatchAsNumpy() メソッドの学習は,関連するArrowSchemaと共にArrowArrayオブジェクトを使用する方法のヒントを提供するかもしれません.

以下の例は,整数フィールドとジオメトリフィールドで構成されるレイヤーの内容を読み取る方法を示しています:

#include "gdal_priv.h"
#include "ogr_api.h"
#include "ogrsf_frmts.h"
#include "ogr_recordbatch.h"
#include <cassert>

int main(int argc, char* argv[])
{
    GDALAllRegister();
    GDALDataset* poDS = GDALDataset::Open(argv[1]);
    if( poDS == nullptr )
    {
        CPLError(CE_Failure, CPLE_AppDefined, "Open() failed\n");
        exit(1);
    }
    OGRLayer* poLayer = poDS->GetLayer(0);
    OGRLayerH hLayer = OGRLayer::ToHandle(poLayer);

    // Get the Arrow stream
    struct ArrowArrayStream stream;
    if( !OGR_L_GetArrowStream(hLayer, &stream, nullptr))
    {
        CPLError(CE_Failure, CPLE_AppDefined, "OGR_L_GetArrowStream() failed\n");
        delete poDS;
        exit(1);
    }

    // Get the schema
    struct ArrowSchema schema;
    if( stream.get_schema(&stream, &schema) != 0 )
    {
        CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed\n");
        stream.release(&stream);
        delete poDS;
        exit(1);
    }

    // Check that the returned schema consists of one int64 field (for FID),
    // one int32 field and one binary/wkb field
    if( schema.n_children != 3 ||
        strcmp(schema.children[0]->format, "l") != 0 || // int64 -> FID
        strcmp(schema.children[1]->format, "i") != 0 || // int32
        strcmp(schema.children[2]->format, "z") != 0 )  // binary for WKB
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Layer has not the expected schema required by this example.");
        schema.release(&schema);
        stream.release(&stream);
        delete poDS;
        exit(1);
    }
    schema.release(&schema);

    // Iterate over batches
    while( true )
    {
        struct ArrowArray array;
        if( stream.get_next(&stream, &array) != 0 ||
            array.release == nullptr )
        {
            break;
        }

        assert(array.n_children == 3);

        // Cast the array->children[].buffers[] to the appropriate data types
        const auto int_child = array.children[1];
        assert(int_child->n_buffers == 2);
        const uint8_t* int_field_not_null = static_cast<const uint8_t*>(int_child->buffers[0]);
        const int32_t* int_field = static_cast<const int32_t*>(int_child->buffers[1]);

        const auto wkb_child = array.children[2];
        assert(wkb_child->n_buffers == 3);
        const uint8_t* wkb_field_not_null = static_cast<const uint8_t*>(wkb_child->buffers[0]);
        const int32_t* wkb_offset = static_cast<const int32_t*>(wkb_child->buffers[1]);
        const uint8_t* wkb_field = static_cast<const uint8_t*>(wkb_child->buffers[2]);

        // Lambda to check if a field is set for a given feature index
        const auto IsSet = [](const uint8_t* buffer_not_null, int i)
        {
            return buffer_not_null == nullptr || (buffer_not_null[i/8] >> (i%8)) != 0;
        };

        // Iterate through features of a batch
        for( long long i = 0; i < array.length; i++ )
        {
            if( IsSet(int_field_not_null, i) )
                printf("int_field[%lld] = %d\n", i, int_field[i]);
            else
                printf("int_field[%lld] = null\n", i);

            if( IsSet(wkb_field_not_null, i) )
            {
                const void* wkb = wkb_field + wkb_offset[i];
                const int32_t length = wkb_offset[i+1] - wkb_offset[i];
                char* wkt = nullptr;
                OGRGeometry* geom = nullptr;
                OGRGeometryFactory::createFromWkb(wkb, nullptr, &geom, length);
                if( geom )
                {
                    geom->exportToWkt(&wkt);
                }
                printf("wkb_field[%lld] = %s\n", i, wkt ? wkt : "invalid geometry");
                CPLFree(wkt);
                delete geom;
            }
            else
            {
                printf("wkb_field[%lld] = null\n", i);
            }
        }

        // Release memory taken by the batch
        array.release(&array);
    }

    // Release stream and dataset
    stream.release(&stream);
    delete poDS;
    return 0;
}

ArrowArrayを使用して地物をバッチで書き込む場合は, Arrow Cデータインターフェースを使用してOGRに書き込む を参照してください.

OGRへの書き込み

OGRを介して書き込む例として,上記の逆のことを行います. 入力テキストからカンマ区切り値を読み取り, OGRを介してポイントシェープファイルに書き込む短いプログラムを作成します.

通常通り,すべてのドライバを登録してから,出力ファイルを作成するためにShapefileドライバを取得します.

C++の場合 :

#include "ogrsf_frmts.h"

int main()
{
    const char *pszDriverName = "ESRI Shapefile";
    GDALDriver *poDriver;

    GDALAllRegister();

    poDriver = GetGDALDriverManager()->GetDriverByName(pszDriverName );
    if( poDriver == NULL )
    {
        printf( "%s driver not available.\n", pszDriverName );
        exit( 1 );
    }

Cの場合 :

#include "ogr_api.h"

int main()
{
    const char *pszDriverName = "ESRI Shapefile";
    GDALDriver *poDriver;

    GDALAllRegister();

    poDriver = (GDALDriver*) GDALGetDriverByName(pszDriverName );
    if( poDriver == NULL )
    {
        printf( "%s driver not available.\n", pszDriverName );
        exit( 1 );
    }

次に,データソースを作成します. ESRI Shapefileドライバを使用すると,シェープファイルのディレクトリ全体を作成するか,データソースとして単一のシェープファイルを作成することができます. この場合,名前に拡張子を含めて単一のファイルを明示的に作成します. 他のドライバは異なる動作をします. 2番目,3番目,4番目,5番目の引数はラスターの次元に関連しています(ドライバにラスター機能がある場合). 呼び出しの最後の引数はオプション値のリストですが,この場合はデフォルト値を使用します.サポートされているオプションの詳細は,フォーマットに固有です.

C++の場合 :

GDALDataset *poDS;

poDS = poDriver->Create( "point_out.shp", 0, 0, 0, GDT_Unknown, NULL );
if( poDS == NULL )
{
    printf( "Creation of output file failed.\n" );
    exit( 1 );
}

Cの場合 :

GDALDatasetH hDS;

hDS = GDALCreate( hDriver, "point_out.shp", 0, 0, 0, GDT_Unknown, NULL );
if( hDS == NULL )
{
    printf( "Creation of output file failed.\n" );
    exit( 1 );
}

次に,出力レイヤーを作成します. この場合,データソースが単一のファイルであるため,1つのレイヤーのみを持つことができます.wkbPointを渡して,このレイヤーでサポートされるジオメトリのタイプを指定します. この場合,座標系情報やその他の特別なレイヤー作成オプションを渡していません.

C++の場合 :

OGRLayer *poLayer;

poLayer = poDS->CreateLayer( "point_out", NULL, wkbPoint, NULL );
if( poLayer == NULL )
{
    printf( "Layer creation failed.\n" );
    exit( 1 );
}

Cの場合 :

OGRLayerH hLayer;

hLayer = GDALDatasetCreateLayer( hDS, "point_out", NULL, wkbPoint, NULL );
if( hLayer == NULL )
{
    printf( "Layer creation failed.\n" );
    exit( 1 );
}

レイヤーが存在するため,レイヤーに表示される必要がある属性フィールドを作成する必要があります. 地物が書き込まれる前に,フィールドをレイヤーに追加する必要があります. フィールドを作成するには,フィールドに関する情報を持つ OGRField オブジェクトを初期化します. シェープファイルの場合,フィールドの幅と精度は出力.dbfファイルの作成に重要です. そのため,特に設定しますが,一般的にはデフォルトが適しています. この例では,1つの属性のみ, x,yポイントに関連付けられた名前文字列を持つことにします.

OGRLayer::CreateField() に渡すテンプレートOGRFieldは内部でコピーされます. オブジェクトの所有権は保持されます.

C++の場合:

OGRFieldDefn oField( "Name", OFTString );

oField.SetWidth(32);

if( poLayer->CreateField( &oField ) != OGRERR_NONE )
{
    printf( "Creating Name field failed.\n" );
    exit( 1 );
}

Cの場合:

OGRFieldDefnH hFieldDefn;

hFieldDefn = OGR_Fld_Create( "Name", OFTString );

OGR_Fld_SetWidth( hFieldDefn, 32);

if( OGR_L_CreateField( hLayer, hFieldDefn, TRUE ) != OGRERR_NONE )
{
    printf( "Creating Name field failed.\n" );
    exit( 1 );
}

OGR_Fld_Destroy(hFieldDefn);

以下のスニペットは,stdinから "x,y,name" 形式の行を読み取り,解析するループです.

C++およびCの場合:

double x, y;
char szName[33];

while( !feof(stdin)
       && fscanf( stdin, "%lf,%lf,%32s", &x, &y, szName ) == 3 )
{

地物をディスクに書き込むためには,ローカルOGRFeatureを作成し,属性を設定し,ジオメトリを添付してから,レイヤーに書き込もうとします.この地物は,書き込まれるレイヤーに関連付けられたOGRFeatureDefnからインスタンス化されている必要があります.

C++の場合 :

OGRFeature *poFeature;

poFeature = OGRFeature::CreateFeature( poLayer->GetLayerDefn() );
poFeature->SetField( "Name", szName );

Cの場合 :

OGRFeatureH hFeature;

hFeature = OGR_F_Create( OGR_L_GetLayerDefn( hLayer ) );
OGR_F_SetFieldString( hFeature, OGR_F_GetFieldIndex(hFeature, "Name"), szName );

ローカルジオメトリオブジェクトを作成し,そのコピーを地物に割り当てます (間接的に). OGRFeature::SetGeometryDirectly() は, OGRFeature::SetGeometry() と異なり,直接的なメソッドはジオメトリの所有権を地物に与えます. これは,ジオメトリの余分な深いオブジェクトコピーを回避するため,一般的に効率的です.

C++の場合 :

OGRPoint pt;
pt.setX( x );
pt.setY( y );

poFeature->SetGeometry( &pt );

Cの場合 :

OGRGeometryH hPt;
hPt = OGR_G_CreateGeometry(wkbPoint);
OGR_G_SetPoint_2D(hPt, 0, x, y);

OGR_F_SetGeometry( hFeature, hPt );
OGR_G_DestroyGeometry(hPt);

次に,ファイル内の地物を作成します. OGRLayer::CreateFeature() は,地物の所有権を取得しないため,使用が終わったらクリーンアップします.

C++の場合 :

     if( poLayer->CreateFeature( poFeature ) != OGRERR_NONE )
     {
         printf( "Failed to create feature in shapefile.\n" );
        exit( 1 );
     }

     OGRFeature::DestroyFeature( poFeature );
}

Cの場合 :

     if( OGR_L_CreateFeature( hLayer, hFeature ) != OGRERR_NONE )
     {
         printf( "Failed to create feature in shapefile.\n" );
        exit( 1 );
     }

     OGR_F_Destroy( hFeature );
}

最後に,ヘッダが整然に書き出され,すべてのリソースが回収されるように,データソースを閉じる必要があります.

C/C++の場合 :

    GDALClose( poDS );
}

同じプログラムを1つのブロックにまとめると,次のようになります:

C++の場合 :

#include "ogrsf_frmts.h"

int main()
{
    const char *pszDriverName = "ESRI Shapefile";
    GDALDriver *poDriver;

    GDALAllRegister();

    poDriver = GetGDALDriverManager()->GetDriverByName(pszDriverName );
    if( poDriver == NULL )
    {
        printf( "%s driver not available.\n", pszDriverName );
        exit( 1 );
    }

    GDALDataset *poDS;

    poDS = poDriver->Create( "point_out.shp", 0, 0, 0, GDT_Unknown, 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 = OGRFeature::CreateFeature( 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 );
    }

    GDALClose( poDS );
}

Cの場合 :

#include "gdal.h"

int main()
{
    const char *pszDriverName = "ESRI Shapefile";
    GDALDriverH hDriver;
    GDALDatasetH hDS;
    OGRLayerH hLayer;
    OGRFieldDefnH hFieldDefn;
    double x, y;
    char szName[33];

    GDALAllRegister();

    hDriver = GDALGetDriverByName( pszDriverName );
    if( hDriver == NULL )
    {
        printf( "%s driver not available.\n", pszDriverName );
        exit( 1 );
    }

    hDS = GDALCreate( hDriver, "point_out.shp", 0, 0, 0, GDT_Unknown, NULL );
    if( hDS == NULL )
    {
        printf( "Creation of output file failed.\n" );
        exit( 1 );
    }

    hLayer = GDALDatasetCreateLayer( hDS, "point_out", NULL, wkbPoint, NULL );
    if( hLayer == NULL )
    {
        printf( "Layer creation failed.\n" );
        exit( 1 );
    }

    hFieldDefn = OGR_Fld_Create( "Name", OFTString );

    OGR_Fld_SetWidth( hFieldDefn, 32);

    if( OGR_L_CreateField( hLayer, hFieldDefn, TRUE ) != OGRERR_NONE )
    {
        printf( "Creating Name field failed.\n" );
        exit( 1 );
    }

    OGR_Fld_Destroy(hFieldDefn);

    while( !feof(stdin)
        && fscanf( stdin, "%lf,%lf,%32s", &x, &y, szName ) == 3 )
    {
        OGRFeatureH hFeature;
        OGRGeometryH hPt;

        hFeature = OGR_F_Create( OGR_L_GetLayerDefn( hLayer ) );
        OGR_F_SetFieldString( hFeature, OGR_F_GetFieldIndex(hFeature, "Name"), szName );

        hPt = OGR_G_CreateGeometry(wkbPoint);
        OGR_G_SetPoint_2D(hPt, 0, x, y);

        OGR_F_SetGeometry( hFeature, hPt );
        OGR_G_DestroyGeometry(hPt);

        if( OGR_L_CreateFeature( hLayer, hFeature ) != OGRERR_NONE )
        {
        printf( "Failed to create feature in shapefile.\n" );
        exit( 1 );
        }

        OGR_F_Destroy( hFeature );
    }

    GDALClose( hDS );
}

Pythonの場合:

import sys
from osgeo import gdal
from osgeo import ogr
import string

driverName = "ESRI Shapefile"
drv = gdal.GetDriverByName( driverName )
if drv is None:
    print "%s driver not available.\n" % driverName
    sys.exit( 1 )

ds = drv.Create( "point_out.shp", 0, 0, 0, gdal.GDT_Unknown )
if ds is None:
    print "Creation of output file failed.\n"
    sys.exit( 1 )

lyr = ds.CreateLayer( "point_out", None, ogr.wkbPoint )
if lyr is None:
    print "Layer creation failed.\n"
    sys.exit( 1 )

field_defn = ogr.FieldDefn( "Name", ogr.OFTString )
field_defn.SetWidth( 32 )

if lyr.CreateField ( field_defn ) != 0:
    print "Creating Name field failed.\n"
    sys.exit( 1 )

# Expected format of user input: x y name
linestring = raw_input()
linelist = string.split(linestring)

while len(linelist) == 3:
    x = float(linelist[0])
    y = float(linelist[1])
    name = linelist[2]

    feat = ogr.Feature( lyr.GetLayerDefn())
    feat.SetField( "Name", name )

    pt = ogr.Geometry(ogr.wkbPoint)
    pt.SetPoint_2D(0, x, y)

    feat.SetGeometry(pt)

    if lyr.CreateFeature(feat) != 0:
        print "Failed to create feature in shapefile.\n"
        sys.exit( 1 )

    feat.Destroy()

    linestring = raw_input()
    linelist = string.split(linestring)

ds = None

複数のジオメトリフィールドを地物に関連付けることができます. この機能は,PostGISなどの一部のファイルフォーマットでのみ利用可能です.

このようなデータソースを作成するには,最初にジオメトリフィールドを作成する必要があります. 空間参照システムオブジェクトを,各ジオメトリフィールドに関連付けることができます.

C++の場合 :

OGRGeomFieldDefn oPointField( "PointField", wkbPoint );
OGRSpatialReference* poSRS = new OGRSpatialReference();
poSRS->importFromEPSG(4326);
oPointField.SetSpatialRef(poSRS);
poSRS->Release();

if( poLayer->CreateGeomField( &oPointField ) != OGRERR_NONE )
{
    printf( "Creating field PointField failed.\n" );
    exit( 1 );
}

OGRGeomFieldDefn oFieldPoint2( "PointField2", wkbPoint );
poSRS = new OGRSpatialReference();
poSRS->importFromEPSG(32631);
oPointField2.SetSpatialRef(poSRS);
poSRS->Release();

if( poLayer->CreateGeomField( &oPointField2 ) != OGRERR_NONE )
{
    printf( "Creating field PointField2 failed.\n" );
    exit( 1 );
}

Cの場合 :

OGRGeomFieldDefnH hPointField;
OGRGeomFieldDefnH hPointField2;
OGRSpatialReferenceH hSRS;

hPointField = OGR_GFld_Create( "PointField", wkbPoint );
hSRS = OSRNewSpatialReference( NULL );
OSRImportFromEPSG(hSRS, 4326);
OGR_GFld_SetSpatialRef(hPointField, hSRS);
OSRRelease(hSRS);

if( OGR_L_CreateGeomField( hLayer, hPointField ) != OGRERR_NONE )
{
    printf( "Creating field PointField failed.\n" );
    exit( 1 );
}

OGR_GFld_Destroy( hPointField );

hPointField2 = OGR_GFld_Create( "PointField2", wkbPoint );
OSRImportFromEPSG(hSRS, 32631);
OGR_GFld_SetSpatialRef(hPointField2, hSRS);
OSRRelease(hSRS);

if( OGR_L_CreateGeomField( hLayer, hPointField2 ) != OGRERR_NONE )
{
    printf( "Creating field PointField2 failed.\n" );
    exit( 1 );
}

OGR_GFld_Destroy( hPointField2 );

地物をディスクに書き込むためには,ローカルOGRFeatureを作成し,属性を設定し,ジオメトリを添付してから,レイヤーに書き込もうとします.この地物は,書き込まれるレイヤーに関連付けられたOGRFeatureDefnからインスタンス化されている必要があります.

C++の場合 :

OGRFeature *poFeature;
OGRGeometry *poGeometry;
char* pszWKT;

poFeature = OGRFeature::CreateFeature( poLayer->GetLayerDefn() );

pszWKT = (char*) "POINT (2 49)";
OGRGeometryFactory::createFromWkt( &pszWKT, NULL, &poGeometry );
poFeature->SetGeomFieldDirectly( "PointField", poGeometry );

pszWKT = (char*) "POINT (500000 4500000)";
OGRGeometryFactory::createFromWkt( &pszWKT, NULL, &poGeometry );
poFeature->SetGeomFieldDirectly( "PointField2", poGeometry );

if( poLayer->CreateFeature( poFeature ) != OGRERR_NONE )
{
    printf( "Failed to create feature.\n" );
    exit( 1 );
}

OGRFeature::DestroyFeature( poFeature );

Cの場合 :

OGRFeatureH hFeature;
OGRGeometryH hGeometry;
char* pszWKT;

poFeature = OGR_F_Create( OGR_L_GetLayerDefn(hLayer) );

pszWKT = (char*) "POINT (2 49)";
OGR_G_CreateFromWkt( &pszWKT, NULL, &hGeometry );
OGR_F_SetGeomFieldDirectly( hFeature,
    OGR_F_GetGeomFieldIndex(hFeature, "PointField"), hGeometry );

pszWKT = (char*) "POINT (500000 4500000)";
OGR_G_CreateFromWkt( &pszWKT, NULL, &hGeometry );
OGR_F_SetGeomFieldDirectly( hFeature,
    OGR_F_GetGeomFieldIndex(hFeature, "PointField2"), hGeometry );

if( OGR_L_CreateFeature( hFeature ) != OGRERR_NONE )
{
    printf( "Failed to create feature.\n" );
    exit( 1 );
}

OGR_F_Destroy( hFeature );

Pythonの場合:

feat = ogr.Feature( lyr.GetLayerDefn() )

feat.SetGeomFieldDirectly( "PointField",
    ogr.CreateGeometryFromWkt( "POINT (2 49)" ) )
feat.SetGeomFieldDirectly( "PointField2",
    ogr.CreateGeometryFromWkt( "POINT (500000 4500000)" ) )

if lyr.CreateFeature( feat ) != 0:
    print( "Failed to create feature.\n" );
    sys.exit( 1 );

Arrow Cデータインターフェースを使用してOGRに書き込む

Added in version 3.8.

地物を1つずつ書き込む代わりに,列指向のメモリレイアウトを使用してバッチで書き込むことも可能です. これには, OGRLayer::WriteArrowBatch() メソッドを使用します. このメソッドは,従来の OGRLayer::CreateFeature() アプローチよりも使用が難しく, Apache Arrow Cデータインターフェース との互換性が必要な場合や,レイヤーの列指向書き込みが必要な場合にのみ推奨されます.

ヘルパーライブラリを使用するまで,Arrow Cデータインターフェースの生成には,以下のドキュメントの読み取りが必要です:

バッチ書き込みに関連するArrowSchemaおよびArrowArray基本型の紹介については, Arrow C Streamデータインターフェースを使用してOGRから読み込む を参照してください.

WriteArrowBatch() メソッドのシグネチャは次のとおりです:

/** Writes a batch of rows from an ArrowArray.
 *
 * @param schema Schema of array
 * @param array Array of type struct. It may be released (array->release==NULL)
 *              after calling this method.
 * @param papszOptions Options. Null terminated list, or nullptr.
 * @return true in case of success
 */
virtual bool OGRLayer::WriteArrowBatch(const struct ArrowSchema *schema,
                                       struct ArrowArray *array,
                                       CSLConstList papszOptions = nullptr);

C APIでも OGR_L_WriteArrowBatch() として利用可能です.

これは,一度に複数の地物を持って OGRLayer::CreateFeature() を呼び出すことに意味的に近いです.

ArrowArrayは,struct (format=+s) 型である必要があり,その子は一般的にOGR属性またはジオメトリフィールドにマップします(struct自体でない限り).

メソッド OGRLayer::IsArrowSchemaSupported() を呼び出して,スキーマがWriteArrowBatch() でサポートされるかどうかを判断できます.

対応する子配列のOGRフィールドは存在し,互換性のあるタイプである必要があります. 属性フィールドの場合, OGRLayer::CreateFieldFromArrowSchema() で作成する必要があります.

ジオメトリ列の配列は,バイナリまたは大きなバイナリ型である必要があり, WKBジオメトリを含んでいる必要があります.

この呼び出し後,渡された配列は解放された状態に設定される可能性があることに注意してください(ベース実装ではなく,ParquetやArrowなどの専門実装で行われることがあります)

ベース実装のサポートされているオプションは次のとおりです:

  • FID=name. 配列内のFID列の名前. 指定されていない場合,GetFIDColumn() がそれを決定するために使用されます. 特別な名前OGRLayer::DEFAULT_ARROW_FID_NAME も認識されます. FID または GetFIDColumn() のどちらも設定されていない場合には,対応するArrowArrayはint32 (i) または int64 (l) 型である必要があります. 入力時に,FID列の値は地物を作成するために使用されます.出力時には,FID列の値は作成された地物のFIDで設定される可能性があります (配列が解放されていない場合).

  • GEOMETRY_NAME=name. ジオメトリ列の名前. 指定されていない場合,GetGeometryColumn() が使用されます. GEOMETRY_NAME または GetGeometryColumn() のどちらも設定されていない場合,特別な名前OGRLayer::DEFAULT_ARROW_GEOMETRY_NAME も認識されます.ジオメトリ列は,フィールドメタデータとしてARROW:extension:name=ogc.wkb を持っている場合にも識別されます. 対応するArrowArrayは,binary (w) または large binary (W) 型である必要があります.

専門の実装を持つドライバ ( (Geo)Parquet および (Geo)Arrow IPC File Format / Stream など) は,OLCFastWriteArrowBatch レイヤー機能を表明します.

以下のPythonの例は,1つのジオメトリ列を持つレイヤーを別のフォーマットにコピーする方法を示しています:

def copy_layer(src_lyr, out_filename, out_format, lcos = {}):
    stream = src_lyr.GetArrowStream()
    schema = stream.GetSchema()

    # If the source layer has a FID column and the output driver supports
    # a FID layer creation option, set it to the source FID column name.
    if src_lyr.GetFIDColumn():
        creationOptions = gdal.GetDriverByName(out_format).GetMetadataItem(
            "DS_LAYER_CREATIONOPTIONLIST"
        )
        if creationOptions and '"FID"' in creationOptions:
            lcos["FID"] = src_lyr.GetFIDColumn()

    with ogr.GetDriverByName(out_format).CreateDataSource(out_filename) as out_ds:
        if src_lyr.GetLayerDefn().GetGeomFieldCount() > 1:
            out_lyr = out_ds.CreateLayer(
                src_lyr.GetName(), geom_type=ogr.wkbNone, options=lcos
            )
            for i in range(src_lyr.GetLayerDefn().GetGeomFieldCount()):
                out_lyr.CreateGeomField(src_lyr.GetLayerDefn().GetGeomFieldDefn(i))
        else:
            out_lyr = out_ds.CreateLayer(
                src_lyr.GetName(),
                geom_type=src_lyr.GetGeomType(),
                srs=src_lyr.GetSpatialRef(),
                options=lcos,
            )

        success, error_msg = out_lyr.IsArrowSchemaSupported(schema)
        assert success, error_msg

        src_geom_field_names = [
            src_lyr.GetLayerDefn().GetGeomFieldDefn(i).GetName()
            for i in range(src_lyr.GetLayerDefn().GetGeomFieldCount())
        ]
        for i in range(schema.GetChildrenCount()):
            # GetArrowStream() may return "OGC_FID" for a unnamed source FID
            # column and "wkb_geometry" for a unnamed source geometry column.
            # Also test GetFIDColumn() and src_geom_field_names if they are
            # named.
            if (
                schema.GetChild(i).GetName()
                not in ("OGC_FID", "wkb_geometry", src_lyr.GetFIDColumn())
                and schema.GetChild(i).GetName() not in src_geom_field_names
            ):
                out_lyr.CreateFieldFromArrowSchema(schema.GetChild(i))

        write_options = []
        if src_lyr.GetFIDColumn():
            write_options.append("FID=" + src_lyr.GetFIDColumn())
        if (
            src_lyr.GetLayerDefn().GetGeomFieldCount() == 1
            and src_lyr.GetGeometryColumn()
        ):
            write_options.append("GEOMETRY_NAME=" + src_lyr.GetGeometryColumn())

        while True:
            array = stream.GetNextRecordBatch()
            if array is None:
                break
            out_lyr.WriteArrowBatch(schema, array, write_options)

Pythonバインディングの場合,上記の ogr.Layer.IsArrowSchemaSupported(), ogr.Layer.CreateFieldFromArrowSchema() およびogr.Layer.WriteArrowBatch() メソッドに加えて,3つの類似したメソッドが PyArrow データ型を使用して存在します:

class Layer:

    def IsPyArrowSchemaSupported(self, pa_schema, options=[]):
        """Returns whether the passed pyarrow Schema is supported by the layer, as a tuple (success: bool, errorMsg: str).

    def CreateFieldFromPyArrowSchema(self, pa_schema, options=[]):
        """Create a field from the passed pyarrow Schema."""

    def WritePyArrow(self, pa_batch, options=[]):
        """Write the content of the passed PyArrow batch (either a pyarrow.Table, a pyarrow.RecordBatch or a pyarrow.StructArray) into the layer."""