Skip to content

Commit

Permalink
[Feature][OGR] Support Triangle, TIN and PolyhedralSurface geometry t…
Browse files Browse the repository at this point in the history
…ypes by mapping them to Triangle and MultiPolygon respectively
  • Loading branch information
manisandro committed Oct 4, 2017
1 parent e38eec1 commit abaeb9e
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 1 deletion.
58 changes: 58 additions & 0 deletions src/core/qgsogrutils.cpp
Expand Up @@ -221,6 +221,64 @@ QgsGeometry QgsOgrUtils::ogrGeometryToQgsGeometry( OGRGeometryH geom )
unsigned char *wkb = new unsigned char[memorySize];
OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );

// Read original geometry type
uint32_t origGeomType;
memcpy( &origGeomType, wkb + 1, sizeof( uint32_t ) );
bool hasZ = ( origGeomType >= 1000 && origGeomType < 2000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
bool hasM = ( origGeomType >= 2000 && origGeomType < 3000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );

// PolyhedralSurface and TINs are not supported, map them to multipolygons...
if ( origGeomType % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM
{
// TIN has the same wkb layout as a multipolygon, just need to overwrite the geom types...
int nDims = 2 + hasZ + hasM;
uint32_t newMultiType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
uint32_t newSingleType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::Polygon, hasZ, hasM ) );
unsigned char *wkbptr = wkb;

// Endianness
wkbptr += 1;

// Overwrite geom type
memcpy( wkbptr, &newMultiType, sizeof( uint32_t ) );
wkbptr += 4;

// Geom count
uint32_t numGeoms;
memcpy( &numGeoms, wkb + 5, sizeof( uint32_t ) );
wkbptr += 4;

// For each part, overwrite the geometry type to polygon (Z|M)
for ( uint32_t i = 0; i < numGeoms; ++i )
{
// Endianness
wkbptr += 1;

// Overwrite geom type
memcpy( wkbptr, &newSingleType, sizeof( uint32_t ) );
wkbptr += sizeof( uint32_t );

// skip coordinates
uint32_t nRings;
memcpy( &nRings, wkbptr, sizeof( uint32_t ) );
wkbptr += sizeof( uint32_t );

for ( uint32_t j = 0; j < nRings; ++j )
{
uint32_t nPoints;
memcpy( &nPoints, wkbptr, sizeof( uint32_t ) );
wkbptr += sizeof( uint32_t ) + sizeof( double ) * nDims * nPoints;
}
}
}
else if ( origGeomType % 1000 == 15 ) // PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM
{
// PolyhedralSurface has the same wkb layout as a MultiPolygon, just need to overwrite the geom type...
uint32_t newType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
// Overwrite geom type
memcpy( wkb + 1, &newType, sizeof( uint32_t ) );
}

QgsGeometry g;
g.fromWkb( wkb, memorySize );
return g;
Expand Down
19 changes: 19 additions & 0 deletions src/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -765,6 +765,17 @@ QStringList QgsOgrProvider::subLayers() const
fCount[wkbUnknown] = 0;
}

// List TIN and PolyhedralSurface as MultiPolygon
if ( fCount.contains( wkbTIN ) )
{
fCount[wkbMultiPolygon] = fCount.value( wkbMultiPolygon ) + fCount[wkbTIN];
fCount.remove( wkbTIN );
}
if ( fCount.contains( wkbPolyhedralSurface ) )
{
fCount[wkbMultiPolygon] = fCount.value( wkbMultiPolygon ) + fCount[wkbPolyhedralSurface];
fCount.remove( wkbPolyhedralSurface );
}
// When there are CurvePolygons, promote Polygons
if ( fCount.contains( wkbPolygon ) && fCount.contains( wkbCurvePolygon ) )
{
Expand Down Expand Up @@ -1164,6 +1175,14 @@ QgsWkbTypes::Type QgsOgrProvider::wkbType() const
{
wkb = QgsWkbTypes::multiType( wkb );
}
if ( wkb % 1000 == 15 ) // is PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM => map to MultiPolygon
{
wkb = static_cast<QgsWkbTypes::Type>( wkb - 9 );
}
else if ( wkb % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM => map to MultiPolygon
{
wkb = static_cast<QgsWkbTypes::Type>( wkb - 10 );
}
return wkb;
}

Expand Down
39 changes: 38 additions & 1 deletion tests/src/python/test_provider_ogr.py
Expand Up @@ -17,7 +17,7 @@
import sys
import tempfile

from qgis.core import QgsVectorLayer, QgsVectorDataProvider, QgsWkbTypes, QgsFeature
from qgis.core import QgsVectorLayer, QgsVectorDataProvider, QgsWkbTypes, QgsFeature, QgsFeatureRequest
from qgis.testing import (
start_app,
unittest
Expand Down Expand Up @@ -255,6 +255,43 @@ def testGdbFilter(self):
while it.nextFeature(f):
self.assertTrue(f.attribute("text") == "shape 2")

def testTriangleTINPolyhedralSurface(self):
""" Test support for Triangles (mapped to Polygons) """
testsets = (
("Triangle((0 0, 0 1, 1 1, 0 0))", QgsWkbTypes.Triangle, "Triangle ((0 0, 0 1, 1 1, 0 0))"),
("Triangle Z((0 0 1, 0 1 2, 1 1 3, 0 0 1))", QgsWkbTypes.TriangleZ, "TriangleZ ((0 0 1, 0 1 2, 1 1 3, 0 0 1))"),
("Triangle M((0 0 4, 0 1 5, 1 1 6, 0 0 4))", QgsWkbTypes.TriangleM, "TriangleM ((0 0 4, 0 1 5, 1 1 6, 0 0 4))"),
("Triangle ZM((0 0 0 1, 0 1 2 3, 1 1 4 5, 0 0 0 1))", QgsWkbTypes.TriangleZM, "TriangleZM ((0 0 0 1, 0 1 2 3, 1 1 4 5, 0 0 0 1))"),

("TIN (((0 0, 0 1, 1 1, 0 0)),((0 0, 1 0, 1 1, 0 0)))", QgsWkbTypes.MultiPolygon, "MultiPolygon (((0 0, 0 1, 1 1, 0 0)),((0 0, 1 0, 1 1, 0 0)))"),
("TIN Z(((0 0 0, 0 1 1, 1 1 1, 0 0 0)),((0 0 0, 1 0 0, 1 1 1, 0 0 0)))", QgsWkbTypes.MultiPolygonZ, "MultiPolygonZ (((0 0 0, 0 1 1, 1 1 1, 0 0 0)),((0 0 0, 1 0 0, 1 1 1, 0 0 0)))"),
("TIN M(((0 0 0, 0 1 2, 1 1 3, 0 0 0)),((0 0 0, 1 0 4, 1 1 3, 0 0 0)))", QgsWkbTypes.MultiPolygonM, "MultiPolygonM (((0 0 0, 0 1 2, 1 1 3, 0 0 0)),((0 0 0, 1 0 4, 1 1 3, 0 0 0)))"),
("TIN ZM(((0 0 0 0, 0 1 1 2, 1 1 1 3, 0 0 0 0)),((0 0 0 0, 1 0 0 4, 1 1 1 3, 0 0 0 0)))", QgsWkbTypes.MultiPolygonZM, "MultiPolygonZM (((0 0 0 0, 0 1 1 2, 1 1 1 3, 0 0 0 0)),((0 0 0 0, 1 0 0 4, 1 1 1 3, 0 0 0 0)))"),

("PolyhedralSurface (((0 0, 0 1, 1 1, 0 0)),((0 0, 1 0, 1 1, 0 0)))", QgsWkbTypes.MultiPolygon, "MultiPolygon (((0 0, 0 1, 1 1, 0 0)),((0 0, 1 0, 1 1, 0 0)))"),
("PolyhedralSurface Z(((0 0 0, 0 1 1, 1 1 1, 0 0 0)),((0 0 0, 1 0 0, 1 1 1, 0 0 0)))", QgsWkbTypes.MultiPolygonZ, "MultiPolygonZ (((0 0 0, 0 1 1, 1 1 1, 0 0 0)),((0 0 0, 1 0 0, 1 1 1, 0 0 0)))"),
("PolyhedralSurface M(((0 0 0, 0 1 2, 1 1 3, 0 0 0)),((0 0 0, 1 0 4, 1 1 3, 0 0 0)))", QgsWkbTypes.MultiPolygonM, "MultiPolygonM (((0 0 0, 0 1 2, 1 1 3, 0 0 0)),((0 0 0, 1 0 4, 1 1 3, 0 0 0)))"),
("PolyhedralSurface ZM(((0 0 0 0, 0 1 1 2, 1 1 1 3, 0 0 0 0)),((0 0 0 0, 1 0 0 4, 1 1 1 3, 0 0 0 0)))", QgsWkbTypes.MultiPolygonZM, "MultiPolygonZM (((0 0 0 0, 0 1 1 2, 1 1 1 3, 0 0 0 0)),((0 0 0 0, 1 0 0 4, 1 1 1 3, 0 0 0 0)))")
)
for row in testsets:
datasource = os.path.join(self.basetestpath, 'test.csv')
with open(datasource, 'wt') as f:
f.write('id,WKT\n')
f.write('1,"%s"' % row[0])

vl = QgsVectorLayer(datasource, 'test', 'ogr')
self.assertTrue(vl.isValid())
self.assertEqual(vl.wkbType(), row[1])

f = QgsFeature()
self.assertTrue(vl.getFeatures(QgsFeatureRequest(1)).nextFeature(f))
self.assertTrue(f.geometry())
self.assertEqual(f.geometry().geometry().asWkt(), row[2])

"""PolyhedralSurface, Tin => mapped to MultiPolygon
Triangle => mapped to Polygon
"""


if __name__ == '__main__':
unittest.main()

0 comments on commit abaeb9e

Please sign in to comment.