Skip to content

Commit ed64f37

Browse files
committedSep 30, 2023
SPATIALITE: fix insert incompatible geometry types
Fix #54662 Logic shamelessly copied from potgres provider.
1 parent dece451 commit ed64f37

File tree

3 files changed

+156
-2
lines changed

3 files changed

+156
-2
lines changed
 

‎src/providers/spatialite/qgsspatialiteprovider.cpp

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ email : a.furieri@lqt.it
1818
#include "qgsfeature.h"
1919
#include "qgsfields.h"
2020
#include "qgsgeometry.h"
21+
#include "qgsgeometryfactory.h"
22+
#include "qgsgeometrycollection.h"
23+
#include "qgscompoundcurve.h"
24+
#include "qgscircularstring.h"
2125
#include "qgsrectangle.h"
2226
#include "qgscoordinatereferencesystem.h"
2327
#include "qgslogger.h"
@@ -127,6 +131,125 @@ bool QgsSpatiaLiteProvider::convertField( QgsField &field )
127131
return true;
128132
}
129133

134+
QgsGeometry QgsSpatiaLiteProvider::convertToProviderType( const QgsGeometry &geom ) const
135+
{
136+
if ( geom.isNull() )
137+
{
138+
return QgsGeometry();
139+
}
140+
141+
const QgsAbstractGeometry *geometry = geom.constGet();
142+
if ( !geometry )
143+
{
144+
return QgsGeometry();
145+
}
146+
147+
const Qgis::WkbType providerGeomType = wkbType();
148+
149+
//geom is already in the provider geometry type
150+
if ( geometry->wkbType() == providerGeomType )
151+
{
152+
return QgsGeometry();
153+
}
154+
155+
std::unique_ptr< QgsAbstractGeometry > outputGeom;
156+
157+
//convert compoundcurve to circularstring (possible if compoundcurve consists of one circular string)
158+
if ( QgsWkbTypes::flatType( providerGeomType ) == Qgis::WkbType::CircularString )
159+
{
160+
QgsCompoundCurve *compoundCurve = qgsgeometry_cast<QgsCompoundCurve *>( geometry );
161+
if ( compoundCurve )
162+
{
163+
if ( compoundCurve->nCurves() == 1 )
164+
{
165+
const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( compoundCurve->curveAt( 0 ) );
166+
if ( circularString )
167+
{
168+
outputGeom.reset( circularString->clone() );
169+
}
170+
}
171+
}
172+
}
173+
174+
//convert to curved type if necessary
175+
if ( !QgsWkbTypes::isCurvedType( geometry->wkbType() ) && QgsWkbTypes::isCurvedType( providerGeomType ) )
176+
{
177+
QgsAbstractGeometry *curveGeom = outputGeom ? outputGeom->toCurveType() : geometry->toCurveType();
178+
if ( curveGeom )
179+
{
180+
outputGeom.reset( curveGeom );
181+
}
182+
}
183+
184+
//convert to linear type from curved type
185+
if ( QgsWkbTypes::isCurvedType( geometry->wkbType() ) && !QgsWkbTypes::isCurvedType( providerGeomType ) )
186+
{
187+
QgsAbstractGeometry *segmentizedGeom = outputGeom ? outputGeom->segmentize() : geometry->segmentize();
188+
if ( segmentizedGeom )
189+
{
190+
outputGeom.reset( segmentizedGeom );
191+
}
192+
}
193+
194+
//convert to multitype if necessary
195+
if ( QgsWkbTypes::isMultiType( providerGeomType ) && !QgsWkbTypes::isMultiType( geometry->wkbType() ) )
196+
{
197+
std::unique_ptr< QgsAbstractGeometry > collGeom( QgsGeometryFactory::geomFromWkbType( providerGeomType ) );
198+
QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( collGeom.get() );
199+
if ( geomCollection )
200+
{
201+
if ( geomCollection->addGeometry( outputGeom ? outputGeom->clone() : geometry->clone() ) )
202+
{
203+
outputGeom.reset( collGeom.release() );
204+
}
205+
}
206+
}
207+
208+
//convert to single type if there's a single part of compatible type
209+
if ( !QgsWkbTypes::isMultiType( providerGeomType ) && QgsWkbTypes::isMultiType( geometry->wkbType() ) )
210+
{
211+
const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( geometry );
212+
if ( collection )
213+
{
214+
if ( collection->numGeometries() == 1 )
215+
{
216+
const QgsAbstractGeometry *firstGeom = collection->geometryN( 0 );
217+
if ( firstGeom && firstGeom->wkbType() == providerGeomType )
218+
{
219+
outputGeom.reset( firstGeom->clone() );
220+
}
221+
}
222+
}
223+
}
224+
225+
//set z/m types
226+
if ( QgsWkbTypes::hasZ( providerGeomType ) )
227+
{
228+
if ( !outputGeom )
229+
{
230+
outputGeom.reset( geometry->clone() );
231+
}
232+
outputGeom->addZValue();
233+
}
234+
235+
if ( QgsWkbTypes::hasM( providerGeomType ) )
236+
{
237+
if ( !outputGeom )
238+
{
239+
outputGeom.reset( geometry->clone() );
240+
}
241+
outputGeom->addMValue();
242+
}
243+
244+
if ( outputGeom )
245+
{
246+
return QgsGeometry( outputGeom.release() );
247+
}
248+
249+
return QgsGeometry();
250+
251+
}
252+
130253

131254
Qgis::VectorExportResult QgsSpatiaLiteProvider::createEmptyLayer( const QString &uri,
132255
const QgsFields &fields,
@@ -4230,7 +4353,8 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
42304353
{
42314354
unsigned char *wkb = nullptr;
42324355
int wkb_size;
4233-
QByteArray featureWkb = feature->geometry().asWkb();
4356+
const QgsGeometry convertedGeom( convertToProviderType( feature->geometry() ) );
4357+
const QByteArray featureWkb{ !convertedGeom.isNull() ? convertedGeom.asWkb() : feature->geometry().asWkb() };
42344358
convertFromGeosWKB( reinterpret_cast<const unsigned char *>( featureWkb.constData() ),
42354359
featureWkb.length(),
42364360
&wkb, &wkb_size, nDims );

‎src/providers/spatialite/qgsspatialiteprovider.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ class QgsSpatiaLiteProvider final: public QgsVectorDataProvider
218218
//! Convert a QgsField to work with SL
219219
static bool convertField( QgsField &field );
220220

221+
//! Convert a geometry to a compatible type, borrowed from Posgres provider.
222+
QgsGeometry convertToProviderType( const QgsGeometry &geom ) const;
223+
221224
QString geomParam() const;
222225

223226
//! Gets SpatiaLite version string

‎tests/src/python/test_provider_spatialite.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from qgis.utils import spatialite_connect
4646

4747
from providertestbase import ProviderTestCase
48-
from utilities import unitTestDataPath
48+
from utilities import unitTestDataPath, compareWkt
4949

5050
# Pass no_exit=True: for some reason this crashes sometimes on exit on Travis
5151
start_app(True)
@@ -1885,6 +1885,33 @@ def test_absolute_relative_uri(self):
18851885
self.assertEqual(meta.absoluteToRelativeUri(absolute_uri, context), relative_uri)
18861886
self.assertEqual(meta.relativeToAbsoluteUri(relative_uri, context), absolute_uri)
18871887

1888+
def testRegression54622Multisurface(self):
1889+
1890+
con = spatialite_connect(self.dbname, isolation_level=None)
1891+
cur = con.cursor()
1892+
cur.execute("BEGIN")
1893+
sql = sql = """CREATE TABLE table54622 (
1894+
_id INTEGER PRIMARY KEY AUTOINCREMENT)"""
1895+
cur.execute(sql)
1896+
sql = "SELECT AddGeometryColumn('table54622', 'geometry', 25832, 'MULTIPOLYGON', 'XY', 0)"
1897+
cur.execute(sql)
1898+
cur.execute("COMMIT")
1899+
con.close()
1900+
1901+
layer = QgsVectorLayer(
1902+
'dbname=\'{}\' table="table54622" (geometry) sql='.format(self.dbname), 'test', 'spatialite')
1903+
1904+
self.assertTrue(layer.isValid())
1905+
feature = QgsFeature(layer.fields())
1906+
feature.setGeometry(QgsGeometry.fromWkt('MULTISURFACE(CURVEPOLYGON(COMPOUNDCURVE((-0.886 0.135,-0.886 -0.038,-0.448 -0.070,-0.427 0.144,-0.886 0.135))))'))
1907+
self.assertTrue(layer.dataProvider().addFeatures([feature]))
1908+
1909+
layer = QgsVectorLayer(
1910+
'dbname=\'{}\' table="table54622" (geometry) sql='.format(self.dbname), 'test', 'spatialite')
1911+
feature = next(layer.getFeatures())
1912+
self.assertFalse(feature.geometry().isNull())
1913+
self.assertTrue(compareWkt(feature.geometry().asWkt(), 'MultiPolygon (((-0.886 0.135, -0.886 -0.038, -0.448 -0.070, -0.426 0.143, -0.886 0.135)))', 0.01))
1914+
18881915

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

0 commit comments

Comments
 (0)
Please sign in to comment.