Skip to content

Commit abaeb9e

Browse files
committed
[Feature][OGR] Support Triangle, TIN and PolyhedralSurface geometry types by mapping them to Triangle and MultiPolygon respectively
1 parent e38eec1 commit abaeb9e

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

src/core/qgsogrutils.cpp

+58
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,64 @@ QgsGeometry QgsOgrUtils::ogrGeometryToQgsGeometry( OGRGeometryH geom )
221221
unsigned char *wkb = new unsigned char[memorySize];
222222
OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );
223223

224+
// Read original geometry type
225+
uint32_t origGeomType;
226+
memcpy( &origGeomType, wkb + 1, sizeof( uint32_t ) );
227+
bool hasZ = ( origGeomType >= 1000 && origGeomType < 2000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
228+
bool hasM = ( origGeomType >= 2000 && origGeomType < 3000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
229+
230+
// PolyhedralSurface and TINs are not supported, map them to multipolygons...
231+
if ( origGeomType % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM
232+
{
233+
// TIN has the same wkb layout as a multipolygon, just need to overwrite the geom types...
234+
int nDims = 2 + hasZ + hasM;
235+
uint32_t newMultiType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
236+
uint32_t newSingleType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::Polygon, hasZ, hasM ) );
237+
unsigned char *wkbptr = wkb;
238+
239+
// Endianness
240+
wkbptr += 1;
241+
242+
// Overwrite geom type
243+
memcpy( wkbptr, &newMultiType, sizeof( uint32_t ) );
244+
wkbptr += 4;
245+
246+
// Geom count
247+
uint32_t numGeoms;
248+
memcpy( &numGeoms, wkb + 5, sizeof( uint32_t ) );
249+
wkbptr += 4;
250+
251+
// For each part, overwrite the geometry type to polygon (Z|M)
252+
for ( uint32_t i = 0; i < numGeoms; ++i )
253+
{
254+
// Endianness
255+
wkbptr += 1;
256+
257+
// Overwrite geom type
258+
memcpy( wkbptr, &newSingleType, sizeof( uint32_t ) );
259+
wkbptr += sizeof( uint32_t );
260+
261+
// skip coordinates
262+
uint32_t nRings;
263+
memcpy( &nRings, wkbptr, sizeof( uint32_t ) );
264+
wkbptr += sizeof( uint32_t );
265+
266+
for ( uint32_t j = 0; j < nRings; ++j )
267+
{
268+
uint32_t nPoints;
269+
memcpy( &nPoints, wkbptr, sizeof( uint32_t ) );
270+
wkbptr += sizeof( uint32_t ) + sizeof( double ) * nDims * nPoints;
271+
}
272+
}
273+
}
274+
else if ( origGeomType % 1000 == 15 ) // PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM
275+
{
276+
// PolyhedralSurface has the same wkb layout as a MultiPolygon, just need to overwrite the geom type...
277+
uint32_t newType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
278+
// Overwrite geom type
279+
memcpy( wkb + 1, &newType, sizeof( uint32_t ) );
280+
}
281+
224282
QgsGeometry g;
225283
g.fromWkb( wkb, memorySize );
226284
return g;

src/providers/ogr/qgsogrprovider.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,17 @@ QStringList QgsOgrProvider::subLayers() const
765765
fCount[wkbUnknown] = 0;
766766
}
767767

768+
// List TIN and PolyhedralSurface as MultiPolygon
769+
if ( fCount.contains( wkbTIN ) )
770+
{
771+
fCount[wkbMultiPolygon] = fCount.value( wkbMultiPolygon ) + fCount[wkbTIN];
772+
fCount.remove( wkbTIN );
773+
}
774+
if ( fCount.contains( wkbPolyhedralSurface ) )
775+
{
776+
fCount[wkbMultiPolygon] = fCount.value( wkbMultiPolygon ) + fCount[wkbPolyhedralSurface];
777+
fCount.remove( wkbPolyhedralSurface );
778+
}
768779
// When there are CurvePolygons, promote Polygons
769780
if ( fCount.contains( wkbPolygon ) && fCount.contains( wkbCurvePolygon ) )
770781
{
@@ -1164,6 +1175,14 @@ QgsWkbTypes::Type QgsOgrProvider::wkbType() const
11641175
{
11651176
wkb = QgsWkbTypes::multiType( wkb );
11661177
}
1178+
if ( wkb % 1000 == 15 ) // is PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM => map to MultiPolygon
1179+
{
1180+
wkb = static_cast<QgsWkbTypes::Type>( wkb - 9 );
1181+
}
1182+
else if ( wkb % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM => map to MultiPolygon
1183+
{
1184+
wkb = static_cast<QgsWkbTypes::Type>( wkb - 10 );
1185+
}
11671186
return wkb;
11681187
}
11691188

tests/src/python/test_provider_ogr.py

+38-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import sys
1818
import tempfile
1919

20-
from qgis.core import QgsVectorLayer, QgsVectorDataProvider, QgsWkbTypes, QgsFeature
20+
from qgis.core import QgsVectorLayer, QgsVectorDataProvider, QgsWkbTypes, QgsFeature, QgsFeatureRequest
2121
from qgis.testing import (
2222
start_app,
2323
unittest
@@ -255,6 +255,43 @@ def testGdbFilter(self):
255255
while it.nextFeature(f):
256256
self.assertTrue(f.attribute("text") == "shape 2")
257257

258+
def testTriangleTINPolyhedralSurface(self):
259+
""" Test support for Triangles (mapped to Polygons) """
260+
testsets = (
261+
("Triangle((0 0, 0 1, 1 1, 0 0))", QgsWkbTypes.Triangle, "Triangle ((0 0, 0 1, 1 1, 0 0))"),
262+
("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))"),
263+
("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))"),
264+
("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))"),
265+
266+
("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)))"),
267+
("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)))"),
268+
("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)))"),
269+
("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)))"),
270+
271+
("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)))"),
272+
("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)))"),
273+
("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)))"),
274+
("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)))")
275+
)
276+
for row in testsets:
277+
datasource = os.path.join(self.basetestpath, 'test.csv')
278+
with open(datasource, 'wt') as f:
279+
f.write('id,WKT\n')
280+
f.write('1,"%s"' % row[0])
281+
282+
vl = QgsVectorLayer(datasource, 'test', 'ogr')
283+
self.assertTrue(vl.isValid())
284+
self.assertEqual(vl.wkbType(), row[1])
285+
286+
f = QgsFeature()
287+
self.assertTrue(vl.getFeatures(QgsFeatureRequest(1)).nextFeature(f))
288+
self.assertTrue(f.geometry())
289+
self.assertEqual(f.geometry().geometry().asWkt(), row[2])
290+
291+
"""PolyhedralSurface, Tin => mapped to MultiPolygon
292+
Triangle => mapped to Polygon
293+
"""
294+
258295

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

0 commit comments

Comments
 (0)