RFC66:OGR随机层读/写功能

作者:连鲁奥

联系人:even.rouault,网址:spatialys.com

状态:已实施

实现版本:2.2

总结

这个RFC引入了一个新的API,可以在数据集级别迭代向量特征,此外还可以在层级别进行迭代。大多数具有输出功能的驱动程序都支持按随机顺序分层编写特性的现有功能,该功能是用新的数据集功能标志形式化的。

理论基础

一些矢量格式以交错的方式混合属于不同层的特征,这使得每层的当前特征迭代效率相当低(这要求每层读取整个文件)。这种驱动程序的一个例子是OSM驱动程序。对于这个驱动程序,过去已经开发了一个能够使用OGRLayer::GetNextFeature()方法的hack,但是它有一个非常特殊的语义。参见 OSM-OpenStreetMap XML和PBF 更多细节。随着一种新的驱动程序GMLAS(用于GML应用程序模式)的开发,也出现了类似的需求,GMLAS以任意元素嵌套的方式读取GML文件,因此可以以明显的随机顺序返回它们,因为它以流方式工作。例如,让我们考虑以下简化的XML内容:

<A>
    ...
    <B>
        ...
    </B>
    ...
</A>

驱动程序将首先能够在发出特征A之前完成特征B的构建。因此,当读取此模式的序列时,驱动程序将按B、A、B、A……的顺序发出特征,。。。

变化

C++ API

在GDALDataset级别添加了两个新方法:

GetNextFeature():

/**
 \brief Fetch the next available feature from this dataset.

 The returned feature becomes the responsibility of the caller to
 delete with OGRFeature::DestroyFeature().

 Depending on the driver, this method may return features from layers in a
 non sequential way. This is what may happen when the
 ODsCRandomLayerRead capability is declared (for example for the
 OSM and GMLAS drivers). When datasets declare this capability, it is strongly
 advised to use GDALDataset::GetNextFeature() instead of
 OGRLayer::GetNextFeature(), as the later might have a slow, incomplete or stub
 implementation.

 The default implementation, used by most drivers, will
 however iterate over each layer, and then over each feature within this
 layer.

 This method takes into account spatial and attribute filters set on layers that
 will be iterated upon.

 The ResetReading() method can be used to start at the beginning again.

 Depending on drivers, this may also have the side effect of calling
 OGRLayer::GetNextFeature() on the layers of this dataset.

 This method is the same as the C function GDALDatasetGetNextFeature().

 @param ppoBelongingLayer a pointer to a OGRLayer* variable to receive the
                          layer to which the object belongs to, or NULL.
                          It is possible that the output of *ppoBelongingLayer
                          to be NULL despite the feature not being NULL.
 @param pdfProgressPct    a pointer to a double variable to receive the
                          percentage progress (in [0,1] range), or NULL.
                          On return, the pointed value might be negative if
                          determining the progress is not possible.
 @param pfnProgress       a progress callback to report progress (for
                          GetNextFeature() calls that might have a long duration)
                          and offer cancellation possibility, or NULL
 @param pProgressData     user data provided to pfnProgress, or NULL
 @return a feature, or NULL if no more features are available.
 @since GDAL 2.2
*/

OGRFeature* GDALDataset::GetNextFeature( OGRLayer** ppoBelongingLayer,
                                         double* pdfProgressPct,
                                         GDALProgressFunc pfnProgress,
                                         void* pProgressData )

和ResetReading():

/**
 \brief Reset feature reading to start on the first feature.

 This affects GetNextFeature().

 Depending on drivers, this may also have the side effect of calling
 OGRLayer::ResetReading() on the layers of this dataset.

 This method is the same as the C function GDALDatasetResetReading().

 @since GDAL 2.2
*/
void        GDALDataset::ResetReading();

新功能

添加了以下两个新的数据集功能:

#define ODsCRandomLayerRead     "RandomLayerRead"   /**< Dataset capability for GetNextFeature() returning features from random layers */
#define ODsCRandomLayerWrite    "RandomLayerWrite " /**< Dataset capability for supporting CreateFeature on layer in random order */

计算机辅助编程接口

上述两种新方法在C API中可用:

OGRFeatureH CPL_DLL GDALDatasetGetNextFeature( GDALDatasetH hDS,
                                               OGRLayerH* phBelongingLayer,
                                               double* pdfProgressPct,
                                               GDALProgressFunc pfnProgress,
                                               void* pProgressData )

void CPL_DLL GDALDatasetResetReading( GDALDatasetH hDS );

浅谈新API的几种设计选择

与OGRLayer::GetNextFeature()相比,GDALDataset::GetNextFeature()有一些不同:

  • 它返回特征所属的图层。事实上,从一个特性到知道它属于哪一层也不容易(因为在数据模型中,特性可以存在于任何层之外)。一种可能是将OGRFeatureDefn * 对象的特性和层之一,但这有点不方便做(理论上,可以想象几个层共享同一个特性定义对象,尽管这可能从来不会发生在任何树内驱动程序中)。

  • 即使返回的功能不为空,返回的层也可能为空。这只是目前的一项规定,因为目前不可能这样做。这对于解决没有模式的数据源可能很有意思,因为基本上每个特性都可以有一个不同的模式(例如GeoJSON),而不真正属于一个明确标识的层。

  • 它返回一个进度百分比。使用OGRLayer API时,必须使用GetFeatureCount()返回的总数来计算返回的功能数。对于我们希望快速了解数据集的特征总数的用例来说是不可行的。但是知道文件指针相对于大小的总大小的位置是很容易的。因此,决定让GetNextFeature()返回进度百分比。关于范围的选择 [0,1] ,这与GDAL进程函数接受的范围一致。

  • 它接受进度和取消回调。考虑到GetNextFeature()是一个“基本”方法,并且它已经可以返回进度百分比,人们可能会想为什么需要这样做。但是,在某些情况下,完成GetNextFeature()调用可能需要相当长的时间。例如,在OSM驱动程序的情况下,作为优化,您可以要求驱动程序返回层子集的特征。例如,除节点外的所有层。但通常节点位于文件的开头,因此在获得第一个特性之前,通常必须处理整个文件的70%。在GMLAS驱动程序中,第一个GetNextFeature()调用也是对文件进行初步快速扫描以确定几何列的SRS的机会,因此欢迎有进度反馈。

进度百分比输出与进度回调机制是冗余的,后者可用于获取前者,但可能有点复杂。它需要做如下事情:

int MyProgress(double pct, const char* msg, void* user_data)
{
    *(double*)user_data = pct;
    return TRUE;
}

myDS->GetNextFeature(&poLayer, MyProgress, &pct)

SWIG绑定(Python/Java/C#/Perl)更改

gdal Dataset GetNextFeature映射为gdal::Dataset::GetNextFeature(),gdal Dataset ResetReading映射为gdal::Dataset::ResetReading()。

关于gdal::Dataset::GetNextFeature(),目前只有Python被修改为同时返回特性及其所属层。其他绑定现在只返回这个特性(需要专门的类型映射)

驱动程序

更新OSM和GMLAS驱动程序以实现新的API。

支持ODsCRandomLayerWrite的现有驱动程序将更新以公布它(即大多数具有层创建功能的驱动程序,KML、JML和GeoJSON除外)。

公用事业

ogr2ogr / GDALVectorTranslate() is changed internally to remove the hack that was used for the OSM driver to use the new API, when ODsCRandomLayerRead is advertized. It checks if the output driver advertises ODsCRandomLayerWrite, and if it does not, emit a warning, but still goes on proceeding with the conversion using random layer reading/writing.

ogrinfo被扩展为接受-rl(用于随机层)标志,该标志指示ogrinfo使用GDALDataset::GetNextFeature()API。当ODsCRandomLayerRead被广告时,它被认为是自动使用的,但是输出可以相当。。。随机的,因此对用户来说不太实用。

文档

所有新的方法/功能都记录在案。

测试套件

OSM和GMLAS驱动程序的专用GetNextFeature()实现在各自的测试中进行测试。GDALDataset::GetNextFeature()的默认实现在MEM驱动程序测试中测试。

兼容性问题

对C/C++ API的现有用户没有。

由于存在默认实现,新函数/方法可以安全地用于没有专门实现的驱动程序。

添加新的虚拟方法GDALDataset::ResetReading()和GDALDataset::GetNextFeature()可能会导致树外驱动程序出现问题,这些驱动程序可能已在内部使用此类方法名称,但具有不同的语义或签名。我们在一些树内驱动程序中遇到了这样的问题,并修复了它们。

实施

实施工作将由卢奥负责,主要由新的GMLAS驱动程序(由欧洲地球观测计划哥白尼资助的初步开发)的需求触发。

提议的实施 https://github.com/rouault/gdal2/tree/gmlas_randomreadwrite (承诺: https://github.com/rouault/gdal2/commit/8447606d68b9fac571aa4d381181ecfffed6d72c

投票历史

+来自塔马斯、霍华德、朱卡尔、丹尼尔姆和埃文的1名。