Skip to content

Commit 1ba2bc0

Browse files
committedApr 23, 2016
[OGR provider] Report curve geometry types. Do geometry type conversions when needed on feature creation/modification
1 parent 6c21b1c commit 1ba2bc0

File tree

4 files changed

+165
-7
lines changed

4 files changed

+165
-7
lines changed
 

‎src/providers/ogr/qgsogrprovider.cpp

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
280280
, mOgrGeometryTypeFilter( wkbUnknown )
281281
, ogrDriver( nullptr )
282282
, mValid( false )
283-
, geomType( wkbUnknown )
283+
, mOGRGeomType( wkbUnknown )
284284
, mFeaturesCounted( -1 )
285285
, mWriteAccess( false )
286286
, mShapefileMayBeCorrupted( false )
@@ -497,6 +497,38 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
497497
case wkbGeometryCollection:
498498
geom = "GeometryCollection";
499499
break;
500+
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
501+
case wkbCircularString:
502+
geom = "CircularString";
503+
break;
504+
case wkbCompoundCurve:
505+
geom = "CompoundCurve";
506+
break;
507+
case wkbCurvePolygon:
508+
geom = "CurvePolygon";
509+
break;
510+
case wkbMultiCurve:
511+
geom = "MultiCurve";
512+
break;
513+
case wkbMultiSurface:
514+
geom = "MultiSurface";
515+
break;
516+
case wkbCircularStringZ:
517+
geom = "CircularStringZ";
518+
break;
519+
case wkbCompoundCurveZ:
520+
geom = "CompoundCurveZ";
521+
break;
522+
case wkbCurvePolygonZ:
523+
geom = "CurvePolygonZ";
524+
break;
525+
case wkbMultiCurveZ:
526+
geom = "MultiCurveZ";
527+
break;
528+
case wkbMultiSurfaceZ:
529+
geom = "MultiSurfaceZ";
530+
break;
531+
#endif
500532
case wkbNone:
501533
geom = "None";
502534
break;
@@ -525,7 +557,8 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
525557
geom = "GeometryCollection25D";
526558
break;
527559
default:
528-
geom = QString( "Unknown WKB: %1" ).arg( type );
560+
// Do not use ':', as it will mess with the separator used by QgsSublayersDialog::populateLayers()
561+
geom = QString( "Unknown WKB (%1)" ).arg( type );
529562
}
530563
return geom;
531564
}
@@ -691,11 +724,11 @@ void QgsOgrProvider::loadFields()
691724

692725
if ( mOgrGeometryTypeFilter != wkbUnknown )
693726
{
694-
geomType = mOgrGeometryTypeFilter;
727+
mOGRGeomType = mOgrGeometryTypeFilter;
695728
}
696729
else
697730
{
698-
geomType = getOgrGeomType( ogrLayer );
731+
mOGRGeomType = getOgrGeomType( ogrLayer );
699732
}
700733
OGRFeatureDefnH fdef = OGR_L_GetLayerDefn( ogrLayer );
701734
if ( fdef )
@@ -907,7 +940,7 @@ size_t QgsOgrProvider::layerCount() const
907940
*/
908941
QGis::WkbType QgsOgrProvider::geometryType() const
909942
{
910-
return static_cast<QGis::WkbType>( geomType );
943+
return static_cast<QGis::WkbType>( mOGRGeomType );
911944
}
912945

913946
/**
@@ -933,6 +966,36 @@ bool QgsOgrProvider::isValid()
933966
return mValid;
934967
}
935968

969+
// Drivers may be more tolerant than we really wish (e.g. GeoPackage driver
970+
// may accept any geometry type)
971+
OGRGeometryH QgsOgrProvider::ConvertGeometryIfNecessary( OGRGeometryH hGeom )
972+
{
973+
if ( hGeom == nullptr )
974+
return hGeom;
975+
OGRwkbGeometryType layerGeomType = OGR_L_GetGeomType( ogrLayer );
976+
OGRwkbGeometryType flattenLayerGeomType = wkbFlatten( layerGeomType );
977+
OGRwkbGeometryType geomType = OGR_G_GetGeometryType( hGeom );
978+
OGRwkbGeometryType flattenGeomType = wkbFlatten( geomType );
979+
980+
if ( flattenLayerGeomType == wkbUnknown || flattenLayerGeomType == flattenGeomType )
981+
{
982+
return hGeom;
983+
}
984+
if ( flattenLayerGeomType == wkbMultiPolygon && flattenGeomType == wkbPolygon )
985+
{
986+
return OGR_G_ForceToMultiPolygon( hGeom );
987+
}
988+
if ( flattenLayerGeomType == wkbMultiLineString && flattenGeomType == wkbLineString )
989+
{
990+
return OGR_G_ForceToMultiLineString( hGeom );
991+
}
992+
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
993+
return OGR_G_ForceTo( hGeom, layerGeomType, nullptr );
994+
#else
995+
return hGeom;
996+
#endif
997+
}
998+
936999
bool QgsOgrProvider::addFeature( QgsFeature& f )
9371000
{
9381001
bool returnValue = true;
@@ -951,6 +1014,9 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
9511014
pushError( tr( "OGR error creating wkb for feature %1: %2" ).arg( f.id() ).arg( CPLGetLastErrorMsg() ) );
9521015
return false;
9531016
}
1017+
1018+
geom = ConvertGeometryIfNecessary( geom );
1019+
9541020
OGR_F_SetGeometryDirectly( feature, geom );
9551021
}
9561022
}
@@ -1320,6 +1386,8 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
13201386
continue;
13211387
}
13221388

1389+
theNewGeometry = ConvertGeometryIfNecessary( theNewGeometry );
1390+
13231391
//set the new geometry
13241392
if ( OGR_F_SetGeometryDirectly( theOGRFeature, theNewGeometry ) != OGRERR_NONE )
13251393
{
@@ -2677,7 +2745,7 @@ void QgsOgrProvider::recalculateFeatureCount()
26772745
bool QgsOgrProvider::doesStrictFeatureTypeCheck() const
26782746
{
26792747
// FIXME probably other drivers too...
2680-
return ogrDriverName != "ESRI Shapefile" || ( geomType == wkbPoint || geomType == wkbPoint25D );
2748+
return ogrDriverName != "ESRI Shapefile" || ( mOGRGeomType == wkbPoint || mOGRGeomType == wkbPoint25D );
26812749
}
26822750

26832751
OGRwkbGeometryType QgsOgrProvider::ogrWkbSingleFlatten( OGRwkbGeometryType type )

‎src/providers/ogr/qgsogrprovider.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ class QgsOgrProvider : public QgsVectorDataProvider
335335

336336
bool mValid;
337337

338-
OGRwkbGeometryType geomType;
338+
OGRwkbGeometryType mOGRGeomType;
339339
long mFeaturesCounted;
340340

341341
mutable QStringList mSubLayerList;
@@ -356,6 +356,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
356356
bool mWriteAccess;
357357

358358
bool mShapefileMayBeCorrupted;
359+
360+
/** Converts the geometry to the layer type if necessary. Takes ownership of the passed geometry */
361+
OGRGeometryH ConvertGeometryIfNecessary( OGRGeometryH );
359362
};
360363

361364

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ ADD_PYTHON_TEST(PyQgsMultiEditToolButton test_qgsmultiedittoolbutton.py)
4747
ADD_PYTHON_TEST(PyQgsNetworkContentFetcher test_qgsnetworkcontentfetcher.py)
4848
ADD_PYTHON_TEST(PyQgsNullSymbolRenderer test_qgsnullsymbolrenderer.py)
4949
ADD_PYTHON_TEST(PyQgsNewGeoPackageLayerDialog test_qgsnewgeopackagelayerdialog.py)
50+
ADD_PYTHON_TEST(PyQgsOGRProviderGpkg test_provider_ogr_gpkg.py)
5051
ADD_PYTHON_TEST(PyQgsPalLabelingBase test_qgspallabeling_base.py)
5152
ADD_PYTHON_TEST(PyQgsPalLabelingCanvas test_qgspallabeling_canvas.py)
5253
ADD_PYTHON_TEST(PyQgsPalLabelingComposer test_qgspallabeling_composer.py)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for the OGR/GPKG provider.
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Even Rouault'
10+
__date__ = '2016-04-21'
11+
__copyright__ = 'Copyright 2016, Even Rouault'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
import os
18+
import tempfile
19+
import shutil
20+
import glob
21+
from osgeo import gdal, ogr
22+
23+
from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry
24+
from qgis.testing import start_app, unittest
25+
from utilities import unitTestDataPath
26+
27+
start_app()
28+
29+
30+
def GDAL_COMPUTE_VERSION(maj, min, rev):
31+
return ((maj) * 1000000 + (min) * 10000 + (rev) * 100)
32+
33+
34+
class TestPyQgsOGRProviderGpkg(unittest.TestCase):
35+
36+
@classmethod
37+
def setUpClass(cls):
38+
"""Run before all tests"""
39+
# Create test layer
40+
cls.basetestpath = tempfile.mkdtemp()
41+
42+
@classmethod
43+
def tearDownClass(cls):
44+
"""Run after all tests"""
45+
shutil.rmtree(cls.basetestpath, True)
46+
47+
def testSingleToMultiPolygonPromotion(self):
48+
49+
version_num = int(gdal.VersionInfo('VERSION_NUM'))
50+
if version_num < GDAL_COMPUTE_VERSION(1, 11, 0):
51+
return
52+
53+
tmpfile = os.path.join(self.basetestpath, 'testSingleToMultiPolygonPromotion.gpkg')
54+
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
55+
lyr = ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon)
56+
ds = None
57+
58+
vl = QgsVectorLayer(u'{}|layerid=0'.format(tmpfile), u'test', u'ogr')
59+
f = QgsFeature()
60+
f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))'))
61+
vl.dataProvider().addFeatures([f])
62+
got = [f.geometry().exportToWkt(0) for f in vl.getFeatures()][0]
63+
self.assertEqual(got, 'MultiPolygon (((0 0, 0 1, 1 1, 0 0)))')
64+
65+
def testCurveGeometryType(self):
66+
67+
version_num = int(gdal.VersionInfo('VERSION_NUM'))
68+
if version_num < GDAL_COMPUTE_VERSION(2, 0, 0):
69+
return
70+
71+
tmpfile = os.path.join(self.basetestpath, 'testCurveGeometryType.gpkg')
72+
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
73+
lyr = ds.CreateLayer('test', geom_type=ogr.wkbCurvePolygon)
74+
ds = None
75+
76+
vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr')
77+
self.assertEqual(vl.dataProvider().subLayers(), [u'0:test:0:CurvePolygon'])
78+
f = QgsFeature()
79+
f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))'))
80+
vl.dataProvider().addFeatures([f])
81+
got = [f.geometry().exportToWkt(0) for f in vl.getFeatures()][0]
82+
self.assertEqual(got, 'CurvePolygon ((0 0, 0 1, 1 1, 0 0))')
83+
84+
85+
if __name__ == '__main__':
86+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.