diff --git a/doc/api_break.dox b/doc/api_break.dox index 44cea05706d..529b0280c4d 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -1233,6 +1233,7 @@ empty geometry collection) - smoothLine and smoothPolygon are no longer public API (use smooth() instead) - avoidIntersections() got an extra argument: list of layers to include in the operation (previously read from active QgsProject) - isGeosEmpty() was removed. Use isEmpty() instead. +- reshapeGeometry() expects QgsLineString as a parameter instead of a list of 2D points (so that it can support 3D geometries) QgsGeometryAnalyzer {#qgis_api_break_3_0_QgsGeometryAnalyzer} diff --git a/python/core/geometry/qgsgeometry.sip b/python/core/geometry/qgsgeometry.sip index fca0129237e..434d7d6f322 100644 --- a/python/core/geometry/qgsgeometry.sip +++ b/python/core/geometry/qgsgeometry.sip @@ -488,7 +488,7 @@ not disjoint with existing polygons of the feature :rtype: int %End - int reshapeGeometry( const QList &reshapeWithLine ); + int reshapeGeometry( const QgsLineString &reshapeLineString ); %Docstring Replaces a part of this geometry with another line :return: 0 in case of success diff --git a/python/core/geometry/qgslinestring.sip b/python/core/geometry/qgslinestring.sip index 47d5a0cad68..84f31d31e18 100644 --- a/python/core/geometry/qgslinestring.sip +++ b/python/core/geometry/qgslinestring.sip @@ -26,6 +26,12 @@ class QgsLineString: QgsCurve public: QgsLineString(); + QgsLineString( const QVector &points ); +%Docstring + Construct a linestring from a vector of points. +.. versionadded:: 3.0 +%End + QgsLineString( const QVector &x, const QVector &y, const QVector &z = QVector(), const QVector &m = QVector() ); diff --git a/src/app/qgsmaptoolreshape.cpp b/src/app/qgsmaptoolreshape.cpp index 3600cb63784..392e651edeb 100644 --- a/src/app/qgsmaptoolreshape.cpp +++ b/src/app/qgsmaptoolreshape.cpp @@ -16,6 +16,7 @@ #include "qgsmaptoolreshape.h" #include "qgsfeatureiterator.h" #include "qgsgeometry.h" +#include "qgslinestring.h" #include "qgsmapcanvas.h" #include "qgsproject.h" #include "qgsvectorlayer.h" @@ -84,6 +85,10 @@ void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() ); } + QgsLineString reshapeLineString( points() ); + if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) ) + reshapeLineString.addZValue( defaultZValue() ); + //query all the features that intersect bounding box of capture line QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; @@ -99,7 +104,7 @@ void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) QgsGeometry geom = f.geometry(); if ( !geom.isNull() ) { - reshapeReturn = geom.reshapeGeometry( points() ); + reshapeReturn = geom.reshapeGeometry( reshapeLineString ); if ( reshapeReturn == 0 ) { //avoid intersections on polygon layers diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 38e975e3a24..9e710d50c70 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -816,15 +816,13 @@ int QgsGeometry::splitGeometry( const QList &splitLine, QList &reshapeWithLine ) +int QgsGeometry::reshapeGeometry( const QgsLineString &reshapeLineString ) { if ( !d->geometry ) { return 0; } - QgsLineString reshapeLineString( reshapeWithLine ); - QgsGeos geos( d->geometry ); int errorCode = 0; QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode ); diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index e130f77e2ea..20d54162071 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -491,7 +491,7 @@ class CORE_EXPORT QgsGeometry * \returns 0 in case of success * \since QGIS 1.3 */ - int reshapeGeometry( const QList &reshapeWithLine ); + int reshapeGeometry( const QgsLineString &reshapeLineString ); /** Changes this geometry such that it does not intersect the other geometry * \param other geometry that should not be intersect diff --git a/src/core/geometry/qgslinestring.cpp b/src/core/geometry/qgslinestring.cpp index 94b00d32b9b..212a4a423fe 100644 --- a/src/core/geometry/qgslinestring.cpp +++ b/src/core/geometry/qgslinestring.cpp @@ -40,6 +40,43 @@ QgsLineString::QgsLineString(): QgsCurve() mWkbType = QgsWkbTypes::LineString; } +QgsLineString::QgsLineString( const QVector &points ) +{ + if ( points.isEmpty() ) + { + mWkbType = QgsWkbTypes::LineString; + return; + } + QgsWkbTypes::Type ptType = points.at( 0 ).wkbType(); + mWkbType = QgsWkbTypes::zmType( QgsWkbTypes::LineString, QgsWkbTypes::hasZ( ptType ), QgsWkbTypes::hasM( ptType ) ); + mX.resize( points.count() ); + mY.resize( points.count() ); + double *x = mX.data(); + double *y = mY.data(); + double *z = nullptr; + double *m = nullptr; + if ( QgsWkbTypes::hasZ( mWkbType ) ) + { + mZ.resize( points.count() ); + z = mZ.data(); + } + if ( QgsWkbTypes::hasM( mWkbType ) ) + { + mM.resize( points.count() ); + m = mM.data(); + } + + Q_FOREACH ( const QgsPoint &pt, points ) + { + *x++ = pt.x(); + *y++ = pt.y(); + if ( z ) + *z++ = pt.z(); + if ( m ) + *m++ = pt.m(); + } +} + QgsLineString::QgsLineString( const QVector &x, const QVector &y, const QVector &z, const QVector &m ) { mWkbType = QgsWkbTypes::LineString; diff --git a/src/core/geometry/qgslinestring.h b/src/core/geometry/qgslinestring.h index 95231d5b90c..6e28c22479f 100644 --- a/src/core/geometry/qgslinestring.h +++ b/src/core/geometry/qgslinestring.h @@ -42,6 +42,12 @@ class CORE_EXPORT QgsLineString: public QgsCurve public: QgsLineString(); + /** + * Construct a linestring from a vector of points. + * \since QGIS 3.0 + */ + QgsLineString( const QVector &points ); + /** * Construct a linestring from arrays of coordinates. If the z or m * arrays are non-empty then the resultant linestring will have diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index d4ec73606b5..97d52eedbdb 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -126,6 +126,8 @@ class TestQgsGeometry : public QObject void isSimple(); + void reshapeGeometryLineMerge(); + private: //! A helper method to do a render check to see if the geometry op is as expected bool renderCheck( const QString &testName, const QString &comment = QLatin1String( QLatin1String( "" ) ), int mismatchCount = 0 ); @@ -1078,7 +1080,27 @@ void TestQgsGeometry::lineString() QCOMPARE( fromPts.xAt( 2 ), 21.0 ); QCOMPARE( fromPts.yAt( 2 ), 22.0 ); - + // from QVector + QVector ptsVector; + ptsVector << QgsPoint( 10, 20 ) << QgsPoint( 30, 40 ); + QgsLineString fromVector( ptsVector ); + QCOMPARE( fromVector.wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( fromVector.numPoints(), 2 ); + QCOMPARE( fromVector.xAt( 0 ), 10.0 ); + QCOMPARE( fromVector.yAt( 0 ), 20.0 ); + QCOMPARE( fromVector.xAt( 1 ), 30.0 ); + QCOMPARE( fromVector.yAt( 1 ), 40.0 ); + QVector ptsVector3D; + ptsVector3D << QgsPoint( QgsWkbTypes::PointZ, 10, 20, 100 ) << QgsPoint( QgsWkbTypes::PointZ, 30, 40, 200 ); + QgsLineString fromVector3D( ptsVector3D ); + QCOMPARE( fromVector3D.wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( fromVector3D.numPoints(), 2 ); + QCOMPARE( fromVector3D.xAt( 0 ), 10.0 ); + QCOMPARE( fromVector3D.yAt( 0 ), 20.0 ); + QCOMPARE( fromVector3D.zAt( 0 ), 100.0 ); + QCOMPARE( fromVector3D.xAt( 1 ), 30.0 ); + QCOMPARE( fromVector3D.yAt( 1 ), 40.0 ); + QCOMPARE( fromVector3D.zAt( 1 ), 200.0 ); //addVertex QgsLineString l2; @@ -5452,5 +5474,48 @@ void TestQgsGeometry::isSimple() } } +void TestQgsGeometry::reshapeGeometryLineMerge() +{ + int res; + QgsGeometry g2D = QgsGeometry::fromWkt( "LINESTRING(10 10, 20 20)" ); + QgsGeometry g3D = QgsGeometry::fromWkt( "LINESTRINGZ(10 10 1, 20 20 2)" ); + + // prepare 2D reshaping line + QVector v2D_1, v2D_2; + v2D_1 << QgsPoint( 20, 20 ) << QgsPoint( 30, 30 ); + v2D_2 << QgsPoint( 10, 10 ) << QgsPoint( -10, -10 ); + QgsLineString line2D_1( v2D_1 ), line2D_2( v2D_2 ); + + // prepare 3D reshaping line + QVector v3D_1, v3D_2; + v3D_1 << QgsPoint( QgsWkbTypes::PointZ, 20, 20, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 30, 30, 3 ); + v3D_2 << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZ, -10, -10, -1 ); + QgsLineString line3D_1( v3D_1 ), line3D_2( v3D_2 ); + + // append with 2D line + QgsGeometry g2D_1 = g2D; + res = g2D_1.reshapeGeometry( line2D_1 ); + QCOMPARE( res, 0 ); + QCOMPARE( g2D_1.exportToWkt(), QString( "LineString (10 10, 20 20, 30 30)" ) ); + + // prepend with 2D line + QgsGeometry g2D_2 = g2D; + res = g2D_2.reshapeGeometry( line2D_2 ); + QCOMPARE( res, 0 ); + QCOMPARE( g2D_2.exportToWkt(), QString( "LineString (-10 -10, 10 10, 20 20)" ) ); + + // append with 3D line + QgsGeometry g3D_1 = g3D; + res = g3D_1.reshapeGeometry( line3D_1 ); + QCOMPARE( res, 0 ); + QCOMPARE( g3D_1.exportToWkt(), QString( "LineStringZ (10 10 1, 20 20 2, 30 30 3)" ) ); + + // prepend with 3D line + QgsGeometry g3D_2 = g3D; + res = g3D_2.reshapeGeometry( line3D_2 ); + QCOMPARE( res, 0 ); + QCOMPARE( g3D_2.exportToWkt(), QString( "LineStringZ (-10 -10 -1, 10 10 1, 20 20 2)" ) ); +} + QGSTEST_MAIN( TestQgsGeometry ) #include "testqgsgeometry.moc"