RFC 64:三角形、多面体表面和锡

作者:Avyav Kumar Singh,甚至鲁奥

联系人:gmail.com上的avyavkumar,spatialys.com上的even.rouault

状态:通过、实施

实现版本:GDAL 2.2

总结

到目前为止 OGRGeometry 类(派生所有子类型的基类)仅限于ogrcompondcurve、ogrcurgarstring、OGRLinearRing、OGRMultiLineString、OGRMultiPoint、OGRMultiPolygon、OGRMultiCurve、OGRSimpleCurve、OGRCurvePolygon和OGRPolygon。

本RFC解决了在几何测量中添加以下新几何图形的问题:

  • 三角形-多边形的一个子集,基本的区别是它只由3个节点组成(实际上是4个,最后一个是第一个节点的重复),只有一个外部边界,没有内部多边形。

  • 多面体表面-完全由多边形构成的三维图形。

  • 三角形曲面-多面体曲面的子集;仅由三角形组成的三维图形。

参考文件

核心变化

新的类层次结构如下所示,主要与SQL/MM第3部分保持一致

../../_images/classOGRGeometry_RFC64.png

在提出这项建议之前,已经做了一些初步工作,例如在<ogru core.h>中包括必要的WKB代码。

另外, SFCGAL 库是GDAL的一个新的可选依赖项(目前只对Unix提供构建支持)。测试的最低版本是1.2.2(如ubuntu16.04所示)。如在其主页中提到的,“SFCGAL是围绕CGAL的C++包装库,目的是支持ISO 19107:2013和OGC简单特征访问1.2用于3D操作。”它主要被用作PostGIS的潜在几何后端。它有一个C API,这就是我们使用的API。

只要几何操作数之一是三角形、多面体曲面或三角网,就可以通过几何测量法(当前是有效的()、距离()、凸度()、交集()、并集()、差分()、符号差分()、交叉点())使用SFCGAL函数。

两种新的几何测量方法被用于转换SFCGAL几何图形<->OGR几何图形。

static sfcgal_geometry_t* OGRexportToSFCGAL(OGRGeometry *poGeom);
static OGRGeometry* SFCGALexportToOGR(sfcgal_geometry_t* _geometry);

除了SFCGAL外,GEOS方法在某些情况下仍在使用,但有以下限制:三角形转换为带一个外环的多边形;多面体曲面和三角化曲面转换为多边形的几何集合。(三角化曲面中的每个三角形都转换为前面描述的多边形)

引入的新几何图形的API包括-

  • 在三角形API的情况下覆盖多边形的现有方法。下面提供了一个完整的API-

class CPL_DLL OGRTriangle : public OGRPolygon
{
  private:
    bool quickValidityCheck() const;

  protected:
//! @cond Doxygen_Suppress
    virtual OGRSurfaceCasterToPolygon   GetCasterToPolygon() const CPL_OVERRIDE;
    virtual OGRErr importFromWKTListOnly( char ** ppszInput, int bHasZ, int bHasM,
                                       OGRRawPoint*& paoPoints, int& nMaxPoints,
                                       double*& padfZ ) CPL_OVERRIDE;
//! @endcond

  public:
    OGRTriangle();
    OGRTriangle(const OGRPoint &p, const OGRPoint &q, const OGRPoint &r);
    OGRTriangle(const OGRTriangle &other);
    OGRTriangle(const OGRPolygon &other, OGRErr &eErr);
    OGRTriangle& operator=(const OGRTriangle& other);
    virtual ~OGRTriangle();
    virtual const char *getGeometryName() const CPL_OVERRIDE;
    virtual OGRwkbGeometryType getGeometryType() const CPL_OVERRIDE;

    // IWks Interface
    virtual OGRErr importFromWkb( unsigned char *, int = -1,
                                  OGRwkbVariant=wkbVariantOldOgc ) CPL_OVERRIDE;
    virtual OGRErr importFromWkt( char ** ) CPL_OVERRIDE;

    // New methods rewritten from OGRPolygon/OGRCurvePolygon/OGRGeometry
    virtual OGRErr addRingDirectly( OGRCurve * poNewRing ) CPL_OVERRIDE;

//! @cond Doxygen_Suppress
    static OGRGeometry* CastToPolygon(OGRGeometry* poGeom);
//! @endcond
};
  • 多面体API是从OGRSurface派生的。在内部,它使用OGRMultiPolygon存储包含多面体曲面的所有多边形。大多数方法的实现只引用相应的OGRMultiPolygon方法,并进行检查以确保条件得到维护。

class CPL_DLL OGRPolyhedralSurface : public OGRSurface
{
  protected:
//! @cond Doxygen_Suppress
    friend class OGRTriangulatedSurface;
    OGRMultiPolygon oMP;
    virtual OGRSurfaceCasterToPolygon      GetCasterToPolygon() const CPL_OVERRIDE;
    virtual OGRSurfaceCasterToCurvePolygon GetCasterToCurvePolygon() const CPL_OVERRIDE;
    virtual OGRBoolean         isCompatibleSubType( OGRwkbGeometryType ) const;
    virtual const char*        getSubGeometryName() const;
    virtual OGRwkbGeometryType getSubGeometryType() const;
    OGRErr exportToWktInternal (char ** ppszDstText, OGRwkbVariant eWkbVariant, const char* pszSkipPrefix ) const;

    virtual OGRPolyhedralSurfaceCastToMultiPolygon GetCasterToMultiPolygon() const;
    static OGRMultiPolygon* CastToMultiPolygonImpl(OGRPolyhedralSurface* poPS);
//! @endcond

  public:
    OGRPolyhedralSurface();
    OGRPolyhedralSurface(const OGRPolyhedralSurface &poGeom);
    virtual ~OGRPolyhedralSurface();
    OGRPolyhedralSurface& operator=(const OGRPolyhedralSurface& other);

    // IWks Interface
    virtual int WkbSize() const CPL_OVERRIDE;
    virtual const char *getGeometryName() const CPL_OVERRIDE;
    virtual OGRwkbGeometryType getGeometryType() const  CPL_OVERRIDE;
    virtual OGRErr importFromWkb( unsigned char *, int=-1, OGRwkbVariant=wkbVariantOldOgc ) CPL_OVERRIDE;
    virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const CPL_OVERRIDE;
    virtual OGRErr importFromWkt( char ** )  CPL_OVERRIDE;
    virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const  CPL_OVERRIDE;

    // IGeometry methods
    virtual int getDimension() const  CPL_OVERRIDE;

    virtual void empty()  CPL_OVERRIDE;

    virtual OGRGeometry *clone() const  CPL_OVERRIDE;
    virtual void getEnvelope(OGREnvelope * psEnvelope) const  CPL_OVERRIDE;
    virtual void getEnvelope(OGREnvelope3D * psEnvelope) const  CPL_OVERRIDE;

    virtual void flattenTo2D() CPL_OVERRIDE;
    virtual OGRErr transform(OGRCoordinateTransformation*) CPL_OVERRIDE;
    virtual OGRBoolean Equals(OGRGeometry*) const CPL_OVERRIDE;
    virtual double get_Area() const CPL_OVERRIDE;
    virtual OGRErr PointOnSurface(OGRPoint*) const CPL_OVERRIDE;

    static OGRMultiPolygon* CastToMultiPolygon(OGRPolyhedralSurface* poPS);
    virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const CPL_OVERRIDE;
    virtual OGRErr addGeometry( const OGRGeometry * );
    OGRErr addGeometryDirectly(OGRGeometry *poNewGeom);
    int getNumGeometries() const;
    OGRGeometry* getGeometryRef(int i);
    const OGRGeometry* getGeometryRef(int i) const;

    virtual OGRBoolean  IsEmpty() const CPL_OVERRIDE;
    virtual void setCoordinateDimension( int nDimension ) CPL_OVERRIDE;
    virtual void set3D( OGRBoolean bIs3D ) CPL_OVERRIDE;
    virtual void setMeasured( OGRBoolean bIsMeasured ) CPL_OVERRIDE;
    virtual void swapXY() CPL_OVERRIDE;
    OGRErr removeGeometry( int iIndex, int bDelete = TRUE );
};
  • 三角化曲面API类似于多面体曲面,并且对MultiPolygon类稍加调整,使其包含由形状三角形的子几何体组成的运行方法。(多多边形严格来说是多边形的集合)。这些方法是OGRMultiPolygon的内部方法,不能由公共用户访问。例如 OGRMultiPolygon::addGeometryDirectly 方法检查添加到它的子几何体是否应为多边形类型。已经编写了一个新的函数,它没有实现这个检查,而不是搅乱现有的函数-

/************************************************************************/
/*                         _addGeometryDirectly()                       */
/*      Only to be used in conjunction with OGRTriangulatedSurface.     */
/*                        DO NOT USE IT ELSEWHERE.                      */
/************************************************************************/

OGRErr OGRMultiPolygon::_addGeometryDirectly( OGRGeometry * poNewGeom )
{
    if ( wkbFlatten(poNewGeom->getGeometryType()) != wkbTriangle)
        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;

    if( poNewGeom->Is3D() && !Is3D() )
        set3D(TRUE);

    if( poNewGeom->IsMeasured() && !IsMeasured() )
        setMeasured(TRUE);

    if( !poNewGeom->Is3D() && Is3D() )
        poNewGeom->set3D(TRUE);

    if( !poNewGeom->IsMeasured() && IsMeasured() )
        poNewGeom->setMeasured(TRUE);

    OGRGeometry** papoNewGeoms = (OGRGeometry **) VSI_REALLOC_VERBOSE( papoGeoms,
                                             sizeof(void*) * (nGeomCount+1) );
    if( papoNewGeoms == NULL )
        return OGRERR_FAILURE;

    papoGeoms = papoNewGeoms;
    papoGeoms[nGeomCount] = poNewGeom;
    nGeomCount++;

    return OGRERR_NONE;
}
  • 三角曲面API如下-

class CPL_DLL OGRTriangulatedSurface : public OGRPolyhedralSurface
{
  protected:
//! @cond Doxygen_Suppress
    virtual OGRBoolean         isCompatibleSubType( OGRwkbGeometryType ) const CPL_OVERRIDE;
    virtual const char*        getSubGeometryName() const CPL_OVERRIDE;
    virtual OGRwkbGeometryType getSubGeometryType() const CPL_OVERRIDE;

    virtual OGRPolyhedralSurfaceCastToMultiPolygon GetCasterToMultiPolygon() const CPL_OVERRIDE;
    static OGRMultiPolygon* CastToMultiPolygonImpl(OGRPolyhedralSurface* poPS);
//! @endcond

  public:
    OGRTriangulatedSurface();
    OGRTriangulatedSurface(const OGRTriangulatedSurface &other);
    ~OGRTriangulatedSurface();

    OGRTriangulatedSurface& operator=(const OGRTriangulatedSurface& other);
    virtual const char *getGeometryName() const CPL_OVERRIDE;
    virtual OGRwkbGeometryType getGeometryType() const CPL_OVERRIDE;

    // IWks Interface
    virtual OGRErr addGeometry( const OGRGeometry * ) CPL_OVERRIDE;

    static OGRPolyhedralSurface* CastToPolyhedralSurface(OGRTriangulatedSurface* poTS);
};

几何图形类型

新的几何WKB值如下所示-

几何类型

二维

Z

M

ZM

PolyhedralSurface

0015

1015

2015

3015

TIN

0016

1016

2016

3016

三角形

0017

1017

2017

3017

几何转换

OGRGeometryFactory::forceTo()和forceToMultiPolygon()方法已得到增强,以支持新几何类型之间的转换,并朝向多多边形。请注意,将三角网或多面体曲面转换为多多边形在语义上是不正确的,因为多多边形假设包含同一平面中的几何图形,但是,当将这些新的几何类型转换为不支持它们的格式时,它可能会有所帮助(例如,这种转换是在以前的shapefile驱动程序的读取端隐式完成的)

驱动因素的变化

邮政地理信息系统

未显式更改驱动程序,但已确保PG<->OGR兼容性得到维护。PostGIS三维功能在OGR上工作,简单的脚本工作,例如 autotest/ogr/ogr_pg.py ,我们有-

wkt_list = ['POLYHEDRALSURFACE (((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 0,1 1 1,1 0 1,1 0 0,1 1 0)),((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0)),((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)))',
                'TIN (((0 0 0,0 0 1,0 1 0,0 0 0)),((0 0 0,0 1 0,1 1 0,0 0 0)))',
                'TRIANGLE ((48 36 84,32 54 64,86 11 54,48 36 84))' ]

for i in range(0,3):
        gdaltest.pg_ds.ExecuteSQL( "INSERT INTO zgeoms (field_no, wkb_geometry) VALUES (%d,GeomFromEWKT('%s'))" % ( i, wkt_list[i] ) )

ShapeFile

shapefile有“MultiPatch”对象的概念。多通道可以由几个部分组成,这些部分是

  • 三角形分割是三角形的连接条,其中每个顶点(前两个顶点之后)完成一个新的三角形。一个新的三角形总是通过连接新顶点和它的两个前导顶点而形成的。

  • 三角形是三角形的连接扇形,每个顶点(前两个顶点之后)完成一个新的三角形。新的三角形总是通过将新顶点与其前一个顶点和零件的第一个顶点连接起来形成的。

  • 环(外环,内环,第一环,“非类型”环)到目前为止多通道被解读为多多边形。现在,一般来说,将返回一个GeometryCollection,其中零个或多个TIN对应于TriangleStrip/TriangleFan,零个或一个多多边形对应于所有环。如果只有一个TIN或一个MultiPolygon,它将作为顶级几何体返回。写入时层类型未知,扩展SHPT层创建选项以识别多路径值,扩展当前从层几何类型猜测形状类型的逻辑或扩展第一个特征的几何类型以支持多路径。在多匹配层上,接受类型为TIN、多面体曲面、多多边形或GEOMETRYCOLLECTION的几何图形(其子几何图形是前三种类型中的一种),并将其转换为多匹配对象,如果三角形的顺序符合预期,则尝试使用TriangleStrip和TriangleFan。

文件gdb,OpenFileGDB

FileGDB格式也支持MultiPatch对象,只有一个扩展名。有一种新的零件类型,它是由几个三角形组成的,这些三角形的组织不是三角形网格或三角形网格。两个驱动程序都已升级,可以像读取端的ShapeFile驱动程序一样工作。在写入端,如果图层几何类型是TIN或多面体曲面,FileGDB驱动程序将自动写入多个匹配项。在CREATE_MULTIPATCH=YES之前存在的layer选项仍然可以用于强制写入为MULTIPATCH

GML

GML驱动程序已修改为输入和输出->三角形、多面体曲面和三角形曲面能够从GML文档读取/写入GML文档。示例包括-

'TRIANGLE ((0 0,0 1,0 1,0 0))' is parsed to -
'<gml:Triangle>
    <gml:exterior>
        <gml:LinearRing>
            <gml:posList>0 0 0 1 0 1 0 0</gml:posList>
        </gml:LinearRing>
    </gml:exterior>
</gml:Triangle>'

<gml:PolyhedralSurface>
   <gml:polygonPatches>
       <gml:PolygonPatch>
           <gml:exterior>
               <gml:LinearRing>
                   <gml:posList srsDimension="3">1 2 3 4 5 6 7 8 9 1 2 3</gml:posList>
               </gml:LinearRing>
           </gml:exterior>
       </gml:PolygonPatch>
       <gml:PolygonPatch>
           <gml:exterior>
               <gml:LinearRing>
                   <gml:posList srsDimension="3">10 11 12 13 14 15 16 17 18 10 11 12</gml:posList>
               </gml:LinearRing>
           </gml:exterior>
           <gml:interior>
               <gml:LinearRing>
                   <gml:posList srsDimension="3">19 20 21 22 23 24 25 26 27 19 20 21</gml:posList>
               </gml:LinearRing>
           </gml:interior>
       </gml:PolygonPatch>
   </gml:polygonPatches>
</gml:PolyhedralSurface>"""

gets parsed to 'POLYHEDRALSURFACE Z (((1 2 3,4 5 6,7 8 9,1 2 3)),((10 11 12,13 14 15,16 17 18,10 11 12),(19 20 21,22 23 24,25 26 27,19 20 21)))'

Each PolygonPatch/Patch corresponds to one Polygon in a PolyhedralSurface.

Finally, 'POLYHEDRALSURFACE EMPTY' parses to
'<gml:PolyhedralSurface>
    <gml:polygonPatches>
    </gml:polygonPatches>
</gml:PolyhedralSurface>'

注意,在编写端,这些几何图形仅为GML 3输出生成。

DXF

DXF驱动程序中的更改包括将多面体网格(多段线的子类型)转换为多面体曲面。GDAL trac上的一个bug说明了这一点- https://trac.osgeo.org/gdal/ticket/6246 . 多面网格由最初使用特定代码定义的点组成,然后这些点被描述为多边形的一部分(多边形最多可以有四个点)。从现在起,OGR支持读取多面网格,但现在也应该支持对它的写支持(尽管我在这个变更集中没有实现)。

GeoPackage

地质包规范支持 [多个] 点, [多个] 线串, [多个] 多边形及其核心的几何集合。曲线几何图形类型被称为已注册的扩展名。但三角形、多面体曲面或三角网却没有提及。然而,GeoPackage geometry blob格式基于ISO WKB,对新几何类型的支持实际上并不需要新代码。因此,我们有可能读取/写入3种新的几何体类型,但发出警告,将在写入端使用非标准扩展。

其他司机

CSV、VRT、PGDump、SQLite(但不是Spatialite)驱动程序支持新的几何体类型。一些驱动程序已经过修改,以便在提供新的几何图形类型时不会在写入端崩溃。除了前面提到的驱动程序外,以下驱动程序已经过验证,不会崩溃(但可能出错,或跳过无法识别的几何体):MySQL、OCI、KML、LIBKML、GeoJSON、MapInfo

文档

使用标准的Doxygen文档程序。

兼容性

许多应用程序将无法正确处理某些驱动程序现在可能返回的新几何体类型。在gdal2.1中,引入了新的类型,并提到将来GDAL可能会返回这些类型。代码应该跳过新的几何体,正确地处理它们,或者使用OGRu Gu ForceTo()函数将其转换为支持的几何体类型。

测试

已经做了很少的更改,所以现有的自动测试套件仍然可以通过。新的几何类和转换方法已经添加到ogr_geom.py和ogr_gml_geom.py中。更新的驱动程序也收到了新的测试。

实施

由Avyav Kumar Singh完成,在Google代码之夏2016计划下,由Even Rouault进行微调/扩展/集成。

建议的实现位于 https://github.com/rouault/gdal2/tree/gsoc-triangle-ps-tin-rebased 储存库。

投票历史

+1名来自朱卡尔、丹尼尔姆、霍华德和埃文