Skip to content
Permalink
Browse files

Merge pull request #5298 from manisandro/ogr_tin_triangle

[Feature][OGR] Support Triangle, TIN and PolyhedralSurface geometry types by mapping them to Polygon and MultiPolygon respectively
  • Loading branch information
manisandro committed Oct 5, 2017
2 parents a725dae + de126a1 commit 2f44d52ee547a306df39a0a2c9914020b5d49fca
@@ -4358,13 +4358,13 @@ void QgisApp::askUserForOGRSublayers( QgsVectorLayer *layer )

QStringList elements = sublayer.split( QStringLiteral( ":" ) );
// merge back parts of the name that may have been split
while ( elements.size() > 4 )
while ( elements.size() > 5 )
{
elements[1] += ":" + elements[2];
elements.removeAt( 2 );
}

if ( elements.count() == 4 )
if ( elements.count() >= 4 )
{
QgsSublayersDialog::LayerDefinition def;
def.layerId = elements[0].toInt();
@@ -27,6 +27,7 @@
#include "qgsmultipoint.h"
#include "qgsmultipolygon.h"
#include "qgsmultisurface.h"
#include "qgstriangle.h"
#include "qgswkbtypes.h"
#include "qgslogger.h"

@@ -241,6 +242,8 @@ std::unique_ptr<QgsAbstractGeometry> QgsGeometryFactory::geomFromWkbType( QgsWkb
return std::unique_ptr<QgsAbstractGeometry>( new QgsMultiSurface() );
case QgsWkbTypes::GeometryCollection:
return std::unique_ptr<QgsAbstractGeometry>( new QgsGeometryCollection() );
case QgsWkbTypes::Triangle:
return std::unique_ptr<QgsAbstractGeometry>( new QgsTriangle() );
default:
return nullptr;
}
@@ -161,10 +161,7 @@ bool QgsTriangle::fromWkb( QgsConstWkbPtr &wkbPtr )

QgsLineString *line = new QgsLineString();
line->fromWkbPoints( ringType, wkbPtr );
if ( !mExteriorRing )
{
mExteriorRing.reset( line );
}
mExteriorRing.reset( line );

return true;
}
@@ -257,7 +254,7 @@ bool QgsTriangle::insertVertex( QgsVertexId position, const QgsPoint &vertex )
Q_UNUSED( vertex );
return false;
}
#include <iostream>

bool QgsTriangle::moveVertex( QgsVertexId vId, const QgsPoint &newPos )
{
if ( !mExteriorRing || vId.part != 0 || vId.ring != 0 || vId.vertex < 0 || vId.vertex > 4 )
@@ -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;
@@ -775,6 +775,7 @@ void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &cont
}
break;
case QgsWkbTypes::Polygon:
case QgsWkbTypes::Triangle:
{
QPolygonF pts;
QList<QPolygonF> holes;
@@ -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 ) )
{
@@ -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;
}

@@ -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
@@ -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 2f44d52

Please sign in to comment.
You can’t perform that action at this time.