Skip to content

Commit

Permalink
Support for Oracle MultiCurve edition
Browse files Browse the repository at this point in the history
  • Loading branch information
troopa81 authored and nyalldawson committed Jan 13, 2020
1 parent ac7d365 commit bbcb90f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 31 deletions.
2 changes: 2 additions & 0 deletions src/providers/oracle/qgsoracleconn.cpp
Expand Up @@ -651,6 +651,8 @@ QString QgsOracleConn::databaseTypeFilter( const QString &alias, QString geomCol
case QgsWkbTypes::MultiLineString:
case QgsWkbTypes::MultiLineString25D:
case QgsWkbTypes::MultiLineStringZ:
case QgsWkbTypes::MultiCurve:
case QgsWkbTypes::MultiCurveZ:
return QStringLiteral( "mod(%1.sdo_gtype,100) IN (2,6)" ).arg( geomCol );
case QgsWkbTypes::Polygon:
case QgsWkbTypes::Polygon25D:
Expand Down
78 changes: 49 additions & 29 deletions src/providers/oracle/qgsoracleprovider.cpp
Expand Up @@ -2008,53 +2008,75 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry &geom, QSqlQuery &qry

case QgsWkbTypes::CircularStringZ:
case QgsWkbTypes::CompoundCurveZ:
case QgsWkbTypes::MultiCurveZ:
dim = 3;
FALLTHROUGH

case QgsWkbTypes::CircularString:
case QgsWkbTypes::CompoundCurve:
case QgsWkbTypes::MultiCurve:
{
g.gtype = SDO_GTYPE( dim, GtLine );
int nLines = 1;
QgsWkbTypes::Type lineType = type;
if ( type == QgsWkbTypes::CompoundCurve || type == QgsWkbTypes::CompoundCurveZ )
int nCurves = 1;
QgsWkbTypes::Type curveType = type;
if ( type == QgsWkbTypes::MultiCurve || type == QgsWkbTypes::MultiCurveZ )
{
g.gtype = SDO_GTYPE( dim, GtMultiLine );
nLines = *ptr.iPtr++;

g.eleminfo << iOrdinate << 4 << nLines;
nCurves = *ptr.iPtr++;

ptr.ucPtr++; // Skip endianness of first linestring
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of first linestring
ptr.ucPtr++; // Skip endianness of first curve
curveType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of first curve
}

for ( int iLine = 0; iLine < nLines; iLine++ )
for ( int iCurve = 0; iCurve < nCurves; iCurve++ )
{
bool circularString = lineType == QgsWkbTypes::CircularString || lineType == QgsWkbTypes::CircularStringZ;
int nLines = 1;
QgsWkbTypes::Type lineType = curveType;
if ( curveType == QgsWkbTypes::CompoundCurve || curveType == QgsWkbTypes::CompoundCurveZ )
{
g.gtype = SDO_GTYPE( dim, GtMultiLine );
nLines = *ptr.iPtr++;

g.eleminfo << iOrdinate << 2 << ( circularString ? 2 : 1 );
// Oracle don't store compound curve with only one line
if ( nLines > 1 )
{
g.eleminfo << iOrdinate << 4 << nLines;
}

for ( int i = 0, n = *ptr.iPtr++; i < n; i++ )
ptr.ucPtr++; // Skip endianness of first linestring
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of first linestring
}

for ( int iLine = 0; iLine < nLines; iLine++ )
{
// Inside a compound curve, two consecutives lines share start/end points
// We don't repeat this point in ordinates, so we skip the last point (except for last line)
if ( ( type == QgsWkbTypes::CompoundCurve || type == QgsWkbTypes::CompoundCurveZ )
&& i == n - 1 && iLine < nLines - 1 )
bool circularString = lineType == QgsWkbTypes::CircularString || lineType == QgsWkbTypes::CircularStringZ;

g.eleminfo << iOrdinate << 2 << ( circularString ? 2 : 1 );

for ( int i = 0, n = *ptr.iPtr++; i < n; i++ )
{
ptr.dPtr += dim;
continue;
}
// Inside a compound curve, two consecutives lines share start/end points
// We don't repeat this point in ordinates, so we skip the last point (except for last line)
if ( ( curveType == QgsWkbTypes::CompoundCurve || curveType == QgsWkbTypes::CompoundCurveZ )
&& i == n - 1 && iLine < nLines - 1 )
{
ptr.dPtr += dim;
continue;
}

g.ordinates << *ptr.dPtr++;
g.ordinates << *ptr.dPtr++;
if ( dim == 3 )
g.ordinates << *ptr.dPtr++;
g.ordinates << *ptr.dPtr++;
if ( dim == 3 )
g.ordinates << *ptr.dPtr++;

iOrdinate += dim;
iOrdinate += dim;
}

ptr.ucPtr++; // Skip endianness of next linestring
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of next linestring
}

ptr.ucPtr++; // Skip endianness of next linestring
lineType = ( QgsWkbTypes::Type ) * ptr.iPtr++; // type of next linestring
curveType = lineType; // type of next curve
}
}
break;
Expand All @@ -2065,10 +2087,6 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry &geom, QSqlQuery &qry
case QgsWkbTypes::CurvePolygonZ:
case QgsWkbTypes::CurvePolygonM:
case QgsWkbTypes::CurvePolygonZM:
case QgsWkbTypes::MultiCurve:
case QgsWkbTypes::MultiCurveZ:
case QgsWkbTypes::MultiCurveM:
case QgsWkbTypes::MultiCurveZM:
case QgsWkbTypes::MultiSurface:
case QgsWkbTypes::MultiSurfaceZ:
case QgsWkbTypes::MultiSurfaceM:
Expand All @@ -2091,6 +2109,8 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry &geom, QSqlQuery &qry
case QgsWkbTypes::CircularStringZM:
case QgsWkbTypes::CompoundCurveM:
case QgsWkbTypes::CompoundCurveZM:
case QgsWkbTypes::MultiCurveM:
case QgsWkbTypes::MultiCurveZM:

// other unsupported or missing geometry types
case QgsWkbTypes::GeometryCollection:
Expand Down
31 changes: 29 additions & 2 deletions tests/src/python/test_provider_oracle.py
Expand Up @@ -260,7 +260,7 @@ def testCurves(self):

def testEditCurves(self):

def test_geom(layer, pk, wkt):
def test_geom(layer, pk, wkt, wkt_ref=None):
# insert geom in layer
self.assertTrue(layer.startEditing())
feature = QgsFeature(layer.fields())
Expand All @@ -270,8 +270,9 @@ def test_geom(layer, pk, wkt):
self.assertTrue(layer.addFeature(feature))
self.assertTrue(layer.commitChanges())

expected_wkt = wkt if wkt_ref is None else wkt_ref
res_wkt = layer.getFeature(pk).geometry().asWkt()
self.assertTrue(compareWkt(res_wkt, wkt, 0.00001), "\nactual = {}\nexpected = {}".format(res_wkt, wkt))
self.assertTrue(compareWkt(res_wkt, expected_wkt, 0.00001), "\nactual = {}\nexpected = {}".format(res_wkt, expected_wkt))

self.execSQLCommand('DROP TABLE "QGIS"."EDIT_CURVE_DATA"', ignore_errors=True)
self.execSQLCommand("""CREATE TABLE QGIS.EDIT_CURVE_DATA ("pk" INTEGER PRIMARY KEY, GEOM SDO_GEOMETRY)""")
Expand Down Expand Up @@ -310,6 +311,32 @@ def test_geom(layer, pk, wkt):
test_geom(multi_lines_z, 9, 'MultiLineStringZ ((1 2 11, 3 4 -11),(5 6 9, 7 8 1, 9 10 -3))')
test_geom(multi_lines_z, 10, 'MultiLineStringZ ((1 2 1, 3 4 2),(5 6 3, 7 8 4, 9 10 5), (11 12 6, 13 14 7))')

multi_curves = QgsVectorLayer(
self.dbconn + ' sslmode=disable key=\'pk\' srid=3857 type=MultiCurve table="QGIS"."EDIT_CURVE_DATA" (GEOM) sql=',
'test_multicurves', 'oracle')
self.assertTrue(multi_curves.isValid())

# There is no way to represent a compound curve with only one LineString or CircularString in Oracle database
# if SDO_ETYPE = 4, n must be greater than 1 : https://docs.oracle.com/database/121/SPATL/sdo_geometry-object-type.htm#GUID-270AE39D-7B83-46D0-9DD6-E5D99C045021__BGHDGCCE
# So, this two different WKTs inputs generate the same data in Oracle database, and so the same WKT
# output representation (with CompoundCurve() around each MultiCurve parts)
test_geom(multi_curves, 11,
'MultiCurve (CompoundCurve ((-1 -5, 1 2),CircularString (1 2, 5 4, 7 2.2, 10 0.1, 13 4),(13 4, 17 -6)),CompoundCurve (CircularString (1 3, 5 5, 7 3.2, 10 1.1, 13 5)),CompoundCurve ((-11 -3, 5 7, 10 -1)))')
test_geom(multi_curves, 12,
'MultiCurve (CompoundCurve ((-1 -5, 1 2),CircularString (1 2, 5 4, 7 2.2, 10 0.1, 13 4),(13 4, 17 -6)),CompoundCurve (CircularString (1 3, 5 5, 7 3.2, 10 1.1, 13 5)),(-11 -3, 5 7, 10 -1))',
'MultiCurve (CompoundCurve ((-1 -5, 1 2),CircularString (1 2, 5 4, 7 2.2, 10 0.1, 13 4),(13 4, 17 -6)),CompoundCurve (CircularString (1 3, 5 5, 7 3.2, 10 1.1, 13 5)),CompoundCurve ((-11 -3, 5 7, 10 -1)))')

multi_curves_z = QgsVectorLayer(
self.dbconn + ' sslmode=disable key=\'pk\' srid=3857 type=MultiCurveZ table="QGIS"."EDIT_CURVE_DATA" (GEOM) sql=',
'test_multicurves_z', 'oracle')
self.assertTrue(multi_curves_z.isValid())

test_geom(multi_curves_z, 13,
'MultiCurveZ (CompoundCurveZ ((-1 -5 3, 1 2 4),CircularStringZ (1 2 4, 5 4 5, 7 2.2 6, 10 0.1 7, 13 4 8),(13 4 8, 17 -6 9)),CompoundCurveZ (CircularStringZ (1 3 2, 5 5 3, 7 3.2 4, 10 1.1 5, 13 5 6)),CompoundCurveZ ((-11 -3 1, 5 7 2, 10 -1 3)))')
test_geom(multi_curves_z, 14,
'MultiCurveZ (CompoundCurveZ ((-1 -5 3, 1 2 4),CircularStringZ (1 2 4, 5 4 5, 7 2.2 6, 10 0.1 7, 13 4 8),(13 4 8, 17 -6 9)),CompoundCurveZ (CircularStringZ (1 3 2, 5 5 3, 7 3.2 4, 10 1.1 5, 13 5 6)),(-11 -3 1, 5 7 2, 10 -1 3))',
'MultiCurveZ (CompoundCurveZ ((-1 -5 3, 1 2 4),CircularStringZ (1 2 4, 5 4 5, 7 2.2 6, 10 0.1 7, 13 4 8),(13 4 8, 17 -6 9)),CompoundCurveZ (CircularStringZ (1 3 2, 5 5 3, 7 3.2 4, 10 1.1 5, 13 5 6)),CompoundCurveZ ((-11 -3 1, 5 7 2, 10 -1 3)))')

def testSurfaces(self):
vl = QgsVectorLayer('%s table="QGIS"."POLY_DATA" (GEOM) srid=4326 type=POLYGON sql=' %
(self.dbconn), "testpoly", "oracle")
Expand Down

0 comments on commit bbcb90f

Please sign in to comment.