RFC 41:支持OGR中的多个几何字段
总结
在OGR数据模型中为具有多个几何字段的要素添加读/写支持。
动机
OGR数据模型当前绑定到每个要素、要素定义和图层的单个几何体字段。但许多数据格式支持多个几何字段。OGC简单特性规范也不限于每层一个几何字段(例如 OGC 06-104r4 "OpenGIS® Implementation Standard for Geographic information - Simple feature access -Part 2: SQL option )
有一些解决方法:使用GEOMETRYCOLLECTION类型的几何图形,或根据图层中的几何列(如PostGIS或SQLite驱动程序中当前所做的那样)播发尽可能多的图层。所有这些方法充其量都是受到限制的变通方法:
GEOMETRYCOLLECTION方法:无法知道每个子几何体的名称/语义。所有子几何图形必须用相同的SRS表示。无法保证GEOMETRYCOLLECTION始终具有相同数量的子几何图形或具有一致的几何图形类型。
每几何列一层方法:仅适用于只读方案。无法在写方案中工作。
此RFC的目的是在OGR数据模型中适当考虑每个特征对多个几何字段的支持。
建议的解决方案
(注:还研究了替代解决方案。它们将在本RFC的下一节中解释。)
总而言之,几何体字段的处理方式与属性字段在OGRFeatureDefn和OGRFeature级别的处理方式类似,但它们将保持独立。属性字段和几何字段在要素定义中将有各自的单独索引。
这种选择主要是为了最大限度地提高向后兼容性,同时提供新的功能。
它包括创建一个ogrgeomfieldefn类,以及OGRFieldDefn、OGRFeatureDefn、OGRFeature和OGRLayer类中的更改。
ogrgeomfieldefn类
ogrgeomfieldefn是一个新类。它的结构直接来自OGRFieldDefn类。
class CPL_DLL OGRGeomFieldDefn
{
protected:
char *pszName;
OGRwkbGeometryType eGeomType; /* all values possible except wkbNone */
OGRSpatialReference* poSRS;
int bIgnore;
public:
OGRGeomFieldDefn(char *pszName,
OGRwkbGeometryType eGeomType);
virtual ~OGRGeomFieldDefn();
void SetName( const char * );
const char *GetNameRef();
OGRwkbGeometryType GetType();
void SetType( OGRwkbGeometryType eTypeIn );
virtual OGRSpatialReference* GetSpatialRef();
void SetSpatialRef(OGRSpatialReference* poSRS);
int IsIgnored();
void SetIgnored( int bIgnoreIn );
};
可以注意到,成员变量以前是在OGRLayer级别找到的。
SRS对象已计数为ref。引用计数在构造函数和SetSpatialRef()中增加,在析构函数中减少。
GetSpatialRef()是故意设置为虚拟的,因此可以实现延迟计算(在某些驱动程序实现中,获取SRS可能会有明显的开销,例如读取额外的文件或发出SQL请求)。
OGRFeatureDefn类
OGRFeatureDefn类将扩展如下:
class CPL_DLL OGRFeatureDefn
{
protected:
// Remove OGRwkbGeometryType eGeomType and bIgnoreGeometry and
// add instead the following :
int nGeomFieldCount;
OGRGeomFieldDefn* papoGeomFieldDefn;
public:
virtual int GetGeomFieldCount();
virtual OGRGeomFieldDefn *GetGeomFieldDefn( int i );
virtual int GetGeomFieldIndex( const char * );
virtual void AddGeomFieldDefn( OGRGeomFieldDefn * );
virtual OGRErr DeleteGeomFieldDefn( int iGeomField );
// Route OGRwkbGeometryType GetGeomType() and void SetGeomType()
// on the first geometry field definition.
// Same for IsGeometryIgnored() and SetGeometryIgnored()
}
在实例化时,OGRFeatureDefn将创建名为“”的默认几何体字段定义,并键入wkbUnknown。如果调用SetGeomType(),则将在papoGeomFieldDefn上路由 [0] . 如果只存在一个几何字段定义,SetGeomType(wkbNone)将删除它。
GetGeomType()将在papoGeomFieldDefn上路由 [0] 如果它存在的话。否则它将返回wkbNone。
强烈建议在规则字段名和几何字段名的组合集合中存在名称唯一性。否则将导致SQL查询中的未指定行为。代码将不会检查此建议(目前没有对常规字段进行检查)。
另一个更改是使OGRFeatureDefn的所有现有方法都是虚拟的(并将private visibility更改为protected),因此如果需要,可以对该类进行子类化。这将启用对象的延迟创建。理由:建立完整的特性定义可能会很昂贵。但应用程序可能希望列出数据源的所有层,并且只提供一些重要的信息,但建立起来成本较低。在过去,为了解决这个问题,引入了OGRLayer::GetName()和OGRLayer::GetGeomType()。
还要注意,目前还没有预见到reordgeomfielddefns()。如果需要,可以在后面的步骤中添加。当调用SetGeomType(wkbNone)时,DeleteGeomFieldDefn()主要是为了OGRFeatureDefn本身的好处。
OGRFeature类
OGRFeature类将扩展如下:
class CPL_DLL OGRFeature
{
private:
// Remove poGeometry field and add instead
OGRGeometry** papoGeometries; /* size is given by poFDefn->GetGeomFieldCount() */
public:
int GetGeomFieldCount();
OGRGeomFieldDefn *GetGeomFieldDefnRef( int iField );
int GetGeomFieldIndex( const char * pszName);
OGRGeometry* GetGeomFieldRef(int iField);
OGRErr SetGeomFieldDirectly( int iField, OGRGeometry * );
OGRErr SetGeomField( int iField, OGRGeometry * );
// Route SetGeometryDirectly(), SetGeometry(), GetGeometryRef(),
// StealGeometry() on the first geometry field in the array
// Modify implementation of SetFrom() to replicate all geometries
}
注意:在RFC41之前,SetGeometry()或SetGeometryDirectly()可以处理其功能定义为getGeomeType()==wkbNone(不一致)的功能。因为papoGeometries数组的大小现在基于GetGeomFieldCount(),当GetGeomType()==wkbNone时,geometry字段计数为0。VRT和CSV驱动程序将被修复以一致地声明其几何类型。
OGRLayer类
对OGRLayer类的影响:
空间过滤器:考虑的选项是一次只允许一个空间过滤器。
同时应用于多个几何领域的空间滤波器的需求并不明显。
m_poFilterGeom protected成员在OGR代码库中使用了250多次,因此将其转换为数组将是一项乏味的任务。。。
添加物:
protected:
int m_iGeomFieldFilter // specify the index on which the spatial
// filter is active.
public:
virtual void SetSpatialFilter( int iGeomField, OGRGeometry * );
virtual void SetSpatialFilterRect( int iGeomField,
double dfMinX, double dfMinY,
double dfMaxX, double dfMaxY );
GetNextFeature() implementation must check the m_iGeomFieldFilter index
in order to select the appropriate geometry field.
GetGeomType():未更改。对于其他字段,使用GetLayerDefn()->GetGeomField(i)->GetType()
GetSpatialRef():当前默认实现返回空值。它将被更改为返回GetLayerDefn()->GetGeomField(0)->GetSpatialRef()(如果至少有一个几何字段)。鼓励新的驱动程序不再专门化GetSpatialRef(),而是适当地设置其第一个几何字段的SRS。对于其他字段,请使用GetLayerDefn()->GetGeomField(i)->GetSpatialRef()。
注意:由于SRS以前没有存储在OGRFeatureDefn级别,所以所有现有的驱动程序如果没有更新,将使GetGeomField(0)->GetSpatialRef()返回NULL。test_ogrsf实用程序将对此进行检查并发出警告。将逐步更新现有驱动程序。同时,建议使用OGRLayer::GetSpatialRef()以可靠的方式获取第一个几何字段的SRS。
添加:
virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent,
int bForce = TRUE);
Default implementation would call GetExtent() if iGeomField == 0
添加:
virtual OGRErr CreateGeomField(OGRGeomFieldDefn *poField);
暂时没有DeleteGeomField()、ReorderGeomFields()或AlterGeomFieldDefn()。如果需要,可以稍后添加。
GetGeometryColumn():未更改。路由到第一个几何体字段。对于其他字段,使用GetLayerDefn()->GetGeomField(i)->GetNameRef()
SetIgnoredFields():除了常规字段外,还迭代几何字段。特殊的“OGR_GEOMETRY”值仅适用于第一个几何字段。
交集()、并集()等。。。:不变。以后的改进可以使用papszOptions参数来指定一个替代的几何字段
TestCapability():添加OLCCreateGeomField功能以通知是否实现了CreateGeomField()。
OGRDataSource类
对OGRDataSource类的影响:
CreateLayer():签名将保持不变。如果需要多个几何体字段,则必须使用OGRLayer::CreateGeomField()。如果必须指定第一个几何字段的名称,则对于支持ODsCCreateGeomFieldAfterCreateLayer的数据源,使用代码应使用eGType=wkbNone调用CreateLayer(),然后使用OGRLayer::CreateGeomField()添加所有几何字段。
CopyLayer():适用于复制所有几何体字段(如果目标层支持)
ExecuteSQL():接受空间筛选器。对于一般的OGR SQL实现,这个过滤器是一个工具。它也可以应用于返回的layer对象。因此,实际上不需要在ExecuteSQL()API级别添加指定几何体字段的方法。
TestCapability():添加一个ODsCCreateGeomFieldAfterCreateLayer功能,以通知CreateGeomField()是否在创建层之后实现,以及CreateLayer()是否可以使用eGType=wkbNone安全调用。
探索替代解决方案
(如果您完全相信上述建议的方法,可以跳过本段:—))
另一种可能的解决方案是使用与几何体相关的信息扩展现有的OGRFieldDefn对象。这将涉及在OGRFieldType枚举中添加oftgometry值,并添加OGRwkbGeometryType eGeomType和OGRSpatialReference * poSRS成员到OGRFieldDefn。在OGRFeature类级别,OGRField联合可以通过ogrgometry扩展 * 字段。类似地,在OGRLayer级别,CreateField()可以用于创建新的几何体字段。
这种方法的主要缺点是向后兼容,这似乎是最自然的方法。这将影响OGR自身代码或外部代码中检索字段且不需要几何图形的所有位置。例如,在如下代码中(在大多数驱动程序的CreateFeature()中非常常见,或者在使用GetNextFeature()返回的功能的用户代码中非常常见):
switch( poFieldDefn->GetType() )
{
case OFTInteger: something1(poField->GetFieldAsInteger()); break;
case OFTReal: something2(poField->GetFieldAsDouble()): break;
default: something3(poField->GetFieldAsString()); break;
}
对于遗留代码,这将导致将几何体作为常规字段处理。我们可以想象GetFieldAsString()将几何体转换为WKT,但这确实是需要的。基本上,属性和几何字段的处理在大多数用例中是不同的。
(另一方面,如果我们引入64位整数作为OGR类型(这是一个正在等待实现的RFC…),那么上面的代码仍然会产生一个有意义的结果。64位整数的字符串重租并不像默认行为那么糟糕。)
GetFieldCount()也会考虑几何字段,但在大多数情况下,需要减去它们。
避免上述兼容性问题的一种可能方法是在OGRFeatureDefn和OGRFeature级别拥有两组API。当前的,将忽略几何字段,以及一个“扩展”的,将其考虑在内。例如,OGRFeatureDefn::GetFieldCountEx()、OGRFeatureDefn::GetFieldIndexEx()、OGRFeatureDefn::GetFieldDefnEx()、OGRFeature::GetFieldEx()、OGRFeature::SetFieldAsXXXEx()将同时考虑属性和几何字段。这种方法的恼人之处在于OGRFeature中的20个方法GetField()和SetFieldXXX()重复。
计算机辅助编程接口
以下函数将添加到C API中:
/* OGRGeomFieldDefnH */
typedef struct OGRGeomFieldDefnHS *OGRGeomFieldDefnH;
OGRGeomFieldDefnH CPL_DLL OGR_GFld_Create( const char *, OGRwkbGeometryType ) CPL_WARN_UNUSED_RESULT;
void CPL_DLL OGR_GFld_Destroy( OGRGeomFieldDefnH );
void CPL_DLL OGR_GFld_SetName( OGRGeomFieldDefnH, const char * );
const char CPL_DLL *OGR_GFld_GetNameRef( OGRGeomFieldDefnH );
OGRwkbGeometryType CPL_DLL OGR_GFld_GetType( OGRGeomFieldDefnH );
void CPL_DLL OGR_GFld_SetType( OGRGeomFieldDefnH, OGRwkbGeometryType );
OGRSpatialReferenceH CPL_DLL OGR_GFld_GetSpatialRef( OGRGeomFieldDefnH );
void CPL_DLL OGR_GFld_SetSpatialRef( OGRGeomFieldDefnH,
OGRSpatialReferenceH hSRS );
int CPL_DLL OGR_GFld_IsIgnored( OGRGeomFieldDefnH hDefn );
void CPL_DLL OGR_GFld_SetIgnored( OGRGeomFieldDefnH hDefn, int );
/* OGRFeatureDefnH */
int CPL_DLL OGR_FD_GetGeomFieldCount( OGRFeatureDefnH hFDefn );
OGRGeomFieldDefnH CPL_DLL OGR_FD_GetGeomFieldDefn( OGRFeatureDefnH hFDefn, int i );
int CPL_DLL OGR_FD_GetGeomFieldIndex( OGRFeatureDefnH hFDefn, const char * );
void CPL_DLL OGR_FD_AddGeomFieldDefn( OGRFeatureDefnH hFDefn, OGRGeomFieldDefnH );
OGRErr CPL_DLL OGR_FD_DeleteGeomFieldDefn( OGRFeatureDefnH hFDefn, int iGeomField );
/* OGRFeatureH */
int CPL_DLL OGR_F_GetGeomFieldCount( OGRFeatureH hFeat );
OGRGeomFieldDefnH CPL_DLL OGR_F_GetGeomFieldDefnRef( OGRFeatureH hFeat, int iField );
int CPL_DLL OGR_F_GetGeomFieldIndex( OGRFeatureH hFeat, const char * pszName);
OGRGeometryH CPL_DLL OGR_F_GetGeomFieldRef( OGRFeatureH hFeat, int iField );
OGRErr CPL_DLL OGR_F_SetGeomFieldDirectly( OGRFeatureH hFeat, int iField, OGRGeometryH );
OGRErr CPL_DLL OGR_F_SetGeomField( OGRFeatureH hFeat, int iField, OGRGeometryH );
/* OGRLayerH */
void CPL_DLL OGR_L_SetSpatialFilterEx( OGRLayerH, int iGeomField, OGRGeometryH );
void CPL_DLL OGR_L_SetSpatialFilterRectEx( OGRLayerH, int iGeomField,
double dfMinX, double dfMinY,
double dfMaxX, double dfMaxY );
OGRErr CPL_DLL OGR_L_GetExtentEx( OGRLayerH, int iGeomField,
OGREnvelope *psExtent, int bForce );
OGRErr CPL_DLL OGR_L_CreateGeomField( OGRLayerH, OGRGeomFieldDefnH hFieldDefn );
OGR SQL引擎
当前,“选择fieldname1 [,…菲尔德纳曼] “FROM layername”返回指定的字段以及关联的几何图形。这种行为显然没有遵循空间RDBMS的行为,在空间RDBMS中必须显式指定geometry字段。
在向后兼容性和本RFC的新功能之间采用了以下折衷方案:
如果在SELECT子句中没有显式指定几何体字段,并且只有一个几何体字段与图层关联,则隐式返回该字段
否则,仅返回显式提到的几何体字段(如果使用“*”,则返回所有几何体字段)。
局限性
与当前一样,不会从连接的图层获取几何图形。
UNION ALL将只处理默认几何体,与当前一样。(可以在以后的工作中扩展。)
在第一个几何字段上操作OGR_GEOMETRY、OGR_GEOM_WKT和OGR_GEOM_AREA的特殊字段。扩展这种特殊语法似乎并不明智。一个更好的替代方案是OGR SQLite方言(支持Spatialite),一旦它被更新为支持多几何体(不在本RFC的范围内)
驱动程序
在此RFC上下文中更新的驱动程序
邮政地理信息系统:
已经存在特殊形式的支持。具有多个几何图形的表当前报告为称为“表u名称(几何图形u列u名称)”的层(与几何图形列一样多的层)。此行为将被更改,以便表作为OGR层只报告一次。
PGDump(PGDump):下载
添加对多几何表格的写入支持。
内存:
更新为新功能的简单说明。
中间带:
更新以支持多个几何字段(以及与此RFC无关的其他更改)
其他候选驱动程序(本RFC最初未涵盖的升级)
GML驱动程序:目前,每个功能只报告一个几何体。手动编辑GDAL 1.11中的.gfs文件-->实现的post RFC,更改此选项的可能性
SQLite驱动程序:
当前,与当前PostGIS驱动程序的行为相同。
驱动程序和SQLite方言都可以更新以支持多几何层。-->在GDAL 2.0中实现后RFC
Google Fusion表驱动程序:目前,只使用了第一个找到的几何列。可以将“table_name(geometry_column_name)”指定为传递给GetLayerByName()的层名称。
VRT:需要找到支持多种几何图形的语法的一些想法。受影响的XML语法:。在OGRVRTLayer元素级别:GeometryType、layers、geomefield、SrcRegion、ExtentXMin/YMin/XMax/YMax。在OGRVRTWarpedLayer元素级别:添加新元素以选择几何体字段。在OGRVRTUnionLayer元素级别:GeometryType、layers、extendxmin/YMin/XMax/YMax-->在GDAL 1.11中实现后RFC
CSV:当前,从名为“WKT”的列中获取几何图形。扩展以支持多个几何列。不值得这么做。可以用扩展的VRT驱动程序完成。-->在GDAL 1.11中实现后RFC
WFS:目前,只支持单个几何图层。标准允许多个几何图形。首先需要GML驱动程序支持。
其他基于RDBMS的驱动程序:MySQL?,空间小姐?甲骨文空间?
公用事业
ogrinfo
将更新ogrinfo以报告与多几何体支持相关的信息。在单一几何数据源的情况下,输出应保持w.r.t电流输出不变。
多几何数据源的预期输出:
$ ogrinfo PG:dbname=mydb
INFO: Open of `PG:dbname=mydb'
using driver `PostgreSQL' successful.
1: test_multi_geom (Polygon, Point)
$ ogrinfo PG:dbname=mydb -al
INFO: Open of `PG:dbname=mydb'
using driver `PostgreSQL' successful.
Layer name: test_multi_geom
Geometry (polygon_geometry): Polygon
Geometry (centroid_geometry): Point
Feature Count: 10
Extent (polygon_geometry): (400000,4500000) - (500000, 5000000)
Extent (centroid_geometry): (2,48) - (3,49)
Layer SRS WKT (polygon_geometry):
PROJCS["WGS 84 / UTM zone 31N",
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4326"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["latitude_of_origin",0],
PARAMETER["central_meridian",3],
PARAMETER["scale_factor",0.9996],
PARAMETER["false_easting",500000],
PARAMETER["false_northing",0],
UNIT["metre",1,
AUTHORITY["EPSG","9001"]],
AXIS["Easting",EAST],
AXIS["Northing",NORTH],
AUTHORITY["EPSG","32631"]]
Layer SRS WKT (centroid_geometry):
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4326"]]
FID Column = ogc_fid
Geometry Column 1 = polygon_geometry
Geometry Column 2 = centroid_geometry
area: Real
OGRFeature(test_multi_geom):1
area (Real) = 500
polygon_geometry = POLYGON ((400000 4500000,400000 5000000,500000 5000000,500000 4500000,400000 4500000))
centroid_geometry = POINT(2.5 48.5)
将添加“-geomfield”选项以指定-spat选项应用于哪个字段。
ogr2ogr
Enhancements :
如果输出层(OLCCreateGeomField功能)支持,将多个几何层转换为多个几何层。如果不支持,则只转换第一个几何体。
“-选择”选项。如果只指定属性字段名,则将隐式选择所有输入几何图形(向后兼容行为)。如果指定了一个或多个几何体字段名称,则只会选择这些名称。
添加“-geomfield”选项以指定-spat选项应用于哪个字段
各种几何变换(重投影、剪裁等)将应用于所有几何字段。
test_ogrsf
将通过一些一致性检查进行增强:
OGRLayer::GetSpatialRef()==OGRFeatureDefn::GetGeomField(0)->GetSpatialRef()
OGRLayer::GetGeomType()==OGRFeatureDefn::GetGeomField(0)->GetGeomType()
OGRLayer::GetGeometryColumn()==OGRFeatureDefn::GetGeomField(0)->GetNameRef()
空间过滤测试将在所有几何字段上循环。
文档
Python和其他语言绑定
新的C API将映射到SWIG绑定。它将只使用Python绑定进行测试。不需要新的类型映射,因此这应该以简单的方式与其他语言一起工作。
兼容性
更改只是对现有API的添加,并且应该保留现有行为,因此这将是向后兼容的。
C++ ABI变化
PostGIS驱动程序w.r.t GDAL 1.10中具有多个几何图形的表的行为更改。
实施
即使是Rouault也将实现GDAL 1.11版本的上述更改,但Interlis驱动程序的升级将由Pirmin Kalberer完成。
基金
投票历史
+来自Evner,FrankW,HowardB,DanielM和TamasS