/*************************************************************************** qgsgeometry.cpp - Geometry (stored as Open Geospatial Consortium WKB) ------------------------------------------------------------------- Date : 02 May 2005 Copyright : (C) 2005 by Brendan Morley email : morb at ozemail dot com dot au *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include "qgis.h" #include "qgsgeometry.h" #include "qgsgeometryeditutils.h" #include "qgsgeometryfactory.h" #include "qgsgeometryutils.h" #include "qgsinternalgeometryengine.h" #include "qgsgeos.h" #include "qgsapplication.h" #include "qgslogger.h" #include "qgsmaptopixel.h" #include "qgsmessagelog.h" #include "qgspoint.h" #include "qgsrectangle.h" #include "qgsvectorlayer.h" #include "qgsgeometryvalidator.h" #include "qgsmulticurve.h" #include "qgsmultilinestring.h" #include "qgsmultipoint.h" #include "qgsmultipolygon.h" #include "qgsmultisurface.h" #include "qgspointv2.h" #include "qgspolygon.h" #include "qgslinestring.h" struct QgsGeometryPrivate { QgsGeometryPrivate(): ref( 1 ), geometry( nullptr ) {} ~QgsGeometryPrivate() { delete geometry; } QAtomicInt ref; QgsAbstractGeometry* geometry; }; QgsGeometry::QgsGeometry(): d( new QgsGeometryPrivate() ) { } QgsGeometry::~QgsGeometry() { if ( !d->ref.deref() ) delete d; } QgsGeometry::QgsGeometry( QgsAbstractGeometry* geom ): d( new QgsGeometryPrivate() ) { d->geometry = geom; d->ref = QAtomicInt( 1 ); } QgsGeometry::QgsGeometry( const QgsGeometry& other ) { d = other.d; d->ref.ref(); } QgsGeometry& QgsGeometry::operator=( QgsGeometry const & other ) { if ( !d->ref.deref() ) { delete d; } d = other.d; d->ref.ref(); return *this; } void QgsGeometry::detach( bool cloneGeom ) { if ( d->ref > 1 ) { ( void )d->ref.deref(); QgsAbstractGeometry* cGeom = nullptr; if ( d->geometry && cloneGeom ) { cGeom = d->geometry->clone(); } d = new QgsGeometryPrivate(); d->geometry = cGeom; } } QgsAbstractGeometry* QgsGeometry::geometry() const { return d->geometry; } void QgsGeometry::setGeometry( QgsAbstractGeometry* geometry ) { if ( d->geometry == geometry ) { return; } detach( false ); if ( d->geometry ) { delete d->geometry; d->geometry = nullptr; } d->geometry = geometry; } bool QgsGeometry::isEmpty() const { return !d->geometry; } QgsGeometry QgsGeometry::fromWkt( const QString& wkt ) { QgsAbstractGeometry* geom = QgsGeometryFactory::geomFromWkt( wkt ); if ( !geom ) { return QgsGeometry(); } return QgsGeometry( geom ); } QgsGeometry QgsGeometry::fromPoint( const QgsPoint& point ) { QgsAbstractGeometry* geom = QgsGeometryFactory::fromPoint( point ); if ( geom ) { return QgsGeometry( geom ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromPolyline( const QgsPolyline& polyline ) { QgsAbstractGeometry* geom = QgsGeometryFactory::fromPolyline( polyline ); if ( geom ) { return QgsGeometry( geom ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromPolygon( const QgsPolygon& polygon ) { QgsAbstractGeometry* geom = QgsGeometryFactory::fromPolygon( polygon ); if ( geom ) { return QgsGeometry( geom ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromMultiPoint( const QgsMultiPoint& multipoint ) { QgsAbstractGeometry* geom = QgsGeometryFactory::fromMultiPoint( multipoint ); if ( geom ) { return QgsGeometry( geom ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromMultiPolyline( const QgsMultiPolyline& multiline ) { QgsAbstractGeometry* geom = QgsGeometryFactory::fromMultiPolyline( multiline ); if ( geom ) { return QgsGeometry( geom ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromMultiPolygon( const QgsMultiPolygon& multipoly ) { QgsAbstractGeometry* geom = QgsGeometryFactory::fromMultiPolygon( multipoly ); if ( geom ) { return QgsGeometry( geom ); } return QgsGeometry(); } QgsGeometry QgsGeometry::fromRect( const QgsRectangle& rect ) { QgsPolyline ring; ring.append( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); ring.append( QgsPoint( rect.xMaximum(), rect.yMinimum() ) ); ring.append( QgsPoint( rect.xMaximum(), rect.yMaximum() ) ); ring.append( QgsPoint( rect.xMinimum(), rect.yMaximum() ) ); ring.append( QgsPoint( rect.xMinimum(), rect.yMinimum() ) ); QgsPolygon polygon; polygon.append( ring ); return fromPolygon( polygon ); } QgsGeometry QgsGeometry::collectGeometry( const QList< QgsGeometry >& geometries ) { QgsGeometry collected; QList< QgsGeometry >::const_iterator git = geometries.constBegin(); for ( ; git != geometries.constEnd(); ++git ) { if ( collected.isEmpty() ) { collected = QgsGeometry( *git ); collected.convertToMultiType(); } else { collected.addPart( *git ); } } return collected; } void QgsGeometry::fromWkb( unsigned char *wkb, int length ) { detach( false ); if ( d->geometry ) { delete d->geometry; } QgsConstWkbPtr ptr( wkb, length ); d->geometry = QgsGeometryFactory::geomFromWkb( ptr ); delete [] wkb; } void QgsGeometry::fromWkb( const QByteArray &wkb ) { detach( false ); if ( d->geometry ) { delete d->geometry; } QgsConstWkbPtr ptr( wkb ); d->geometry = QgsGeometryFactory::geomFromWkb( ptr ); } GEOSGeometry* QgsGeometry::exportToGeos( double precision ) const { if ( !d->geometry ) { return nullptr; } return QgsGeos::asGeos( d->geometry, precision ); } QgsWkbTypes::Type QgsGeometry::wkbType() const { if ( !d->geometry ) { return QgsWkbTypes::Unknown; } else { return d->geometry->wkbType(); } } QgsWkbTypes::GeometryType QgsGeometry::type() const { if ( !d->geometry ) { return QgsWkbTypes::UnknownGeometry; } return static_cast< QgsWkbTypes::GeometryType >( QgsWkbTypes::geometryType( d->geometry->wkbType() ) ); } bool QgsGeometry::isMultipart() const { if ( !d->geometry ) { return false; } return QgsWkbTypes::isMultiType( d->geometry->wkbType() ); } void QgsGeometry::fromGeos( GEOSGeometry *geos ) { detach( false ); delete d->geometry; d->geometry = QgsGeos::fromGeos( geos ); GEOSGeom_destroy_r( QgsGeos::getGEOSHandler(), geos ); } QgsPoint QgsGeometry::closestVertex( const QgsPoint& point, int& atVertex, int& beforeVertex, int& afterVertex, double& sqrDist ) const { if ( !d->geometry ) { sqrDist = -1; return QgsPoint( 0, 0 ); } QgsPointV2 pt( point.x(), point.y() ); QgsVertexId id; QgsPointV2 vp = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, id ); if ( !id.isValid() ) { sqrDist = -1; return QgsPoint( 0, 0 ); } sqrDist = QgsGeometryUtils::sqrDistance2D( pt, vp ); atVertex = vertexNrFromVertexId( id ); adjacentVertices( atVertex, beforeVertex, afterVertex ); return QgsPoint( vp.x(), vp.y() ); } double QgsGeometry::distanceToVertex( int vertex ) const { if ( !d->geometry ) { return -1; } QgsVertexId id; if ( !vertexIdFromVertexNr( vertex, id ) ) { return -1; } return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id ); } double QgsGeometry::angleAtVertex( int vertex ) const { if ( !d->geometry ) { return 0; } QgsVertexId v2; if ( !vertexIdFromVertexNr( vertex, v2 ) ) { return 0; } QgsVertexId v1; QgsVertexId v3; QgsGeometryUtils::adjacentVertices( *d->geometry, v2, v1, v3 ); if ( v1.isValid() && v3.isValid() ) { QgsPointV2 p1 = d->geometry->vertexAt( v1 ); QgsPointV2 p2 = d->geometry->vertexAt( v2 ); QgsPointV2 p3 = d->geometry->vertexAt( v3 ); double angle1 = QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() ); double angle2 = QgsGeometryUtils::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() ); return QgsGeometryUtils::averageAngle( angle1, angle2 ); } else if ( v3.isValid() ) { QgsPointV2 p1 = d->geometry->vertexAt( v2 ); QgsPointV2 p2 = d->geometry->vertexAt( v3 ); return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() ); } else if ( v1.isValid() ) { QgsPointV2 p1 = d->geometry->vertexAt( v1 ); QgsPointV2 p2 = d->geometry->vertexAt( v2 ); return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() ); } return 0.0; } void QgsGeometry::adjacentVertices( int atVertex, int& beforeVertex, int& afterVertex ) const { if ( !d->geometry ) { return; } QgsVertexId id; if ( !vertexIdFromVertexNr( atVertex, id ) ) { beforeVertex = -1; afterVertex = -1; return; } QgsVertexId beforeVertexId, afterVertexId; QgsGeometryUtils::adjacentVertices( *( d->geometry ), id, beforeVertexId, afterVertexId ); beforeVertex = vertexNrFromVertexId( beforeVertexId ); afterVertex = vertexNrFromVertexId( afterVertexId ); } bool QgsGeometry::moveVertex( double x, double y, int atVertex ) { if ( !d->geometry ) { return false; } QgsVertexId id; if ( !vertexIdFromVertexNr( atVertex, id ) ) { return false; } detach( true ); return d->geometry->moveVertex( id, QgsPointV2( x, y ) ); } bool QgsGeometry::moveVertex( const QgsPointV2& p, int atVertex ) { if ( !d->geometry ) { return false; } QgsVertexId id; if ( !vertexIdFromVertexNr( atVertex, id ) ) { return false; } detach( true ); return d->geometry->moveVertex( id, p ); } bool QgsGeometry::deleteVertex( int atVertex ) { if ( !d->geometry ) { return false; } //maintain compatibility with < 2.10 API if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::MultiPoint ) { detach( true ); //delete geometry instead of point return static_cast< QgsGeometryCollection* >( d->geometry )->removeGeometry( atVertex ); } //if it is a point, set the geometry to nullptr if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point ) { detach( false ); delete d->geometry; d->geometry = nullptr; return true; } QgsVertexId id; if ( !vertexIdFromVertexNr( atVertex, id ) ) { return false; } detach( true ); return d->geometry->deleteVertex( id ); } bool QgsGeometry::insertVertex( double x, double y, int beforeVertex ) { if ( !d->geometry ) { return false; } //maintain compatibility with < 2.10 API if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::MultiPoint ) { detach( true ); //insert geometry instead of point return static_cast< QgsGeometryCollection* >( d->geometry )->insertGeometry( new QgsPointV2( x, y ), beforeVertex ); } QgsVertexId id; if ( !vertexIdFromVertexNr( beforeVertex, id ) ) { return false; } detach( true ); return d->geometry->insertVertex( id, QgsPointV2( x, y ) ); } QgsPoint QgsGeometry::vertexAt( int atVertex ) const { if ( !d->geometry ) { return QgsPoint( 0, 0 ); } QgsVertexId vId; ( void )vertexIdFromVertexNr( atVertex, vId ); if ( vId.vertex < 0 ) { return QgsPoint( 0, 0 ); } QgsPointV2 pt = d->geometry->vertexAt( vId ); return QgsPoint( pt.x(), pt.y() ); } double QgsGeometry::sqrDistToVertexAt( QgsPoint& point, int atVertex ) const { QgsPoint vertexPoint = vertexAt( atVertex ); return QgsGeometryUtils::sqrDistance2D( QgsPointV2( vertexPoint.x(), vertexPoint.y() ), QgsPointV2( point.x(), point.y() ) ); } QgsGeometry QgsGeometry::nearestPoint( const QgsGeometry& other ) const { QgsGeos geos( d->geometry ); return geos.closestPoint( other ); } QgsGeometry QgsGeometry::shortestLine( const QgsGeometry& other ) const { QgsGeos geos( d->geometry ); return geos.shortestLine( other ); } double QgsGeometry::closestVertexWithContext( const QgsPoint& point, int& atVertex ) const { if ( !d->geometry ) { return -1; } QgsVertexId vId; QgsPointV2 pt( point.x(), point.y() ); QgsPointV2 closestPoint = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, vId ); if ( !vId.isValid() ) return -1; atVertex = vertexNrFromVertexId( vId ); return QgsGeometryUtils::sqrDistance2D( closestPoint, pt ); } double QgsGeometry::closestSegmentWithContext( const QgsPoint& point, QgsPoint& minDistPoint, int& afterVertex, double *leftOf, double epsilon ) const { if ( !d->geometry ) { return -1; } QgsPointV2 segmentPt; QgsVertexId vertexAfter; bool leftOfBool; double sqrDist = d->geometry->closestSegment( QgsPointV2( point.x(), point.y() ), segmentPt, vertexAfter, &leftOfBool, epsilon ); if ( sqrDist < 0 ) return -1; minDistPoint.setX( segmentPt.x() ); minDistPoint.setY( segmentPt.y() ); afterVertex = vertexNrFromVertexId( vertexAfter ); if ( leftOf ) { *leftOf = leftOfBool ? 1.0 : -1.0; } return sqrDist; } int QgsGeometry::addRing( const QList &ring ) { detach( true ); QgsLineString* ringLine = new QgsLineString(); QgsPointSequence ringPoints; convertPointList( ring, ringPoints ); ringLine->setPoints( ringPoints ); return addRing( ringLine ); } int QgsGeometry::addRing( QgsCurve* ring ) { if ( !d->geometry ) { delete ring; return 1; } detach( true ); return QgsGeometryEditUtils::addRing( d->geometry, ring ); } int QgsGeometry::addPart( const QList &points, QgsWkbTypes::GeometryType geomType ) { QgsPointSequence l; convertPointList( points, l ); return addPart( l, geomType ); } int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType ) { QgsAbstractGeometry* partGeom = nullptr; if ( points.size() == 1 ) { partGeom = new QgsPointV2( points[0] ); } else if ( points.size() > 1 ) { QgsLineString* ringLine = new QgsLineString(); ringLine->setPoints( points ); partGeom = ringLine; } return addPart( partGeom, geomType ); } int QgsGeometry::addPart( QgsAbstractGeometry* part, QgsWkbTypes::GeometryType geomType ) { if ( !d->geometry ) { detach( false ); switch ( geomType ) { case QgsWkbTypes::PointGeometry: d->geometry = new QgsMultiPointV2(); break; case QgsWkbTypes::LineGeometry: d->geometry = new QgsMultiLineString(); break; case QgsWkbTypes::PolygonGeometry: d->geometry = new QgsMultiPolygonV2(); break; default: return 1; } } else { detach( true ); } convertToMultiType(); return QgsGeometryEditUtils::addPart( d->geometry, part ); } int QgsGeometry::addPart( const QgsGeometry& newPart ) { if ( !d->geometry || !newPart.d || !newPart.d->geometry ) { return 1; } return addPart( newPart.d->geometry->clone() ); } QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const { if ( !d->geometry || type() != QgsWkbTypes::PolygonGeometry ) { return QgsGeometry(); } if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) ) { QList parts = asGeometryCollection(); QList results; Q_FOREACH ( const QgsGeometry& part, parts ) { QgsGeometry result = part.removeInteriorRings( minimumRingArea ); if ( result ) results << result; } if ( results.isEmpty() ) return QgsGeometry(); QgsGeometry first = results.takeAt( 0 ); Q_FOREACH ( const QgsGeometry& result, results ) { first.addPart( result ); } return first; } else { QgsCurvePolygon* newPoly = static_cast< QgsCurvePolygon* >( d->geometry->clone() ); newPoly->removeInteriorRings( minimumRingArea ); return QgsGeometry( newPoly ); } } int QgsGeometry::addPart( GEOSGeometry *newPart ) { if ( !d->geometry || !newPart ) { return 1; } detach( true ); QgsAbstractGeometry* geom = QgsGeos::fromGeos( newPart ); return QgsGeometryEditUtils::addPart( d->geometry, geom ); } int QgsGeometry::translate( double dx, double dy ) { if ( !d->geometry ) { return 1; } detach( true ); d->geometry->transform( QTransform::fromTranslate( dx, dy ) ); return 0; } int QgsGeometry::rotate( double rotation, const QgsPoint& center ) { if ( !d->geometry ) { return 1; } detach( true ); QTransform t = QTransform::fromTranslate( center.x(), center.y() ); t.rotate( -rotation ); t.translate( -center.x(), -center.y() ); d->geometry->transform( t ); return 0; } int QgsGeometry::splitGeometry( const QList& splitLine, QList& newGeometries, bool topological, QList &topologyTestPoints ) { if ( !d->geometry ) { return 0; } QList newGeoms; QgsLineString splitLineString; QgsPointSequence splitLinePointsV2; convertPointList( splitLine, splitLinePointsV2 ); splitLineString.setPoints( splitLinePointsV2 ); QgsPointSequence tp; QgsGeos geos( d->geometry ); int result = geos.splitGeometry( splitLineString, newGeoms, topological, tp ); if ( result == 0 ) { detach( false ); d->geometry = newGeoms.at( 0 ); newGeometries.clear(); for ( int i = 1; i < newGeoms.size(); ++i ) { newGeometries.push_back( QgsGeometry( newGeoms.at( i ) ) ); } } convertPointList( tp, topologyTestPoints ); return result; } //! Replaces a part of this geometry with another line int QgsGeometry::reshapeGeometry( const QList& reshapeWithLine ) { if ( !d->geometry ) { return 0; } QgsPointSequence reshapeLine; convertPointList( reshapeWithLine, reshapeLine ); QgsLineString reshapeLineString; reshapeLineString.setPoints( reshapeLine ); QgsGeos geos( d->geometry ); int errorCode = 0; QgsAbstractGeometry* geom = geos.reshapeGeometry( reshapeLineString, &errorCode ); if ( errorCode == 0 && geom ) { detach( false ); delete d->geometry; d->geometry = geom; return 0; } return errorCode; } int QgsGeometry::makeDifference( const QgsGeometry* other ) { if ( !d->geometry || !other->d->geometry ) { return 0; } QgsGeos geos( d->geometry ); QgsAbstractGeometry* diffGeom = geos.intersection( *( other->geometry() ) ); if ( !diffGeom ) { return 1; } detach( false ); delete d->geometry; d->geometry = diffGeom; return 0; } QgsGeometry QgsGeometry::makeDifference( const QgsGeometry& other ) const { if ( !d->geometry || other.isEmpty() ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsAbstractGeometry* diffGeom = geos.intersection( *other.geometry() ); if ( !diffGeom ) { return QgsGeometry(); } return QgsGeometry( diffGeom ); } QgsRectangle QgsGeometry::boundingBox() const { if ( d->geometry ) { return d->geometry->boundingBox(); } return QgsRectangle(); } QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double& area, double &angle, double& width, double& height ) const { QgsRectangle minRect; area = DBL_MAX; angle = 0; width = DBL_MAX; height = DBL_MAX; if ( !d->geometry || d->geometry->nCoordinates() < 2 ) return QgsGeometry(); QgsGeometry hull = convexHull(); if ( hull.isEmpty() ) return QgsGeometry(); QgsVertexId vertexId; QgsPointV2 pt0; QgsPointV2 pt1; QgsPointV2 pt2; // get first point hull.geometry()->nextVertex( vertexId, pt0 ); pt1 = pt0; double prevAngle = 0.0; while ( hull.geometry()->nextVertex( vertexId, pt2 ) ) { double currentAngle = QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() ); double rotateAngle = 180.0 / M_PI * ( currentAngle - prevAngle ); prevAngle = currentAngle; QTransform t = QTransform::fromTranslate( pt0.x(), pt0.y() ); t.rotate( rotateAngle ); t.translate( -pt0.x(), -pt0.y() ); hull.geometry()->transform( t ); QgsRectangle bounds = hull.geometry()->boundingBox(); double currentArea = bounds.width() * bounds.height(); if ( currentArea < area ) { minRect = bounds; area = currentArea; angle = 180.0 / M_PI * currentAngle; width = bounds.width(); height = bounds.height(); } pt2 = pt1; } QgsGeometry minBounds = QgsGeometry::fromRect( minRect ); minBounds.rotate( angle, QgsPoint( pt0.x(), pt0.y() ) ); // constrain angle to 0 - 180 if ( angle > 180.0 ) angle = fmod( angle, 180.0 ); return minBounds; } QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const { QgsInternalGeometryEngine engine( *this ); return engine.orthogonalize( tolerance, maxIterations, angleThreshold ); } bool QgsGeometry::intersects( const QgsRectangle& r ) const { QgsGeometry g = fromRect( r ); return intersects( g ); } bool QgsGeometry::intersects( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.intersects( *geometry.d->geometry ); } bool QgsGeometry::contains( const QgsPoint* p ) const { if ( !d->geometry || !p ) { return false; } QgsPointV2 pt( p->x(), p->y() ); QgsGeos geos( d->geometry ); return geos.contains( pt ); } bool QgsGeometry::contains( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.contains( *( geometry.d->geometry ) ); } bool QgsGeometry::disjoint( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.disjoint( *( geometry.d->geometry ) ); } bool QgsGeometry::equals( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.isEqual( *( geometry.d->geometry ) ); } bool QgsGeometry::touches( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.touches( *( geometry.d->geometry ) ); } bool QgsGeometry::overlaps( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.overlaps( *( geometry.d->geometry ) ); } bool QgsGeometry::within( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.within( *( geometry.d->geometry ) ); } bool QgsGeometry::crosses( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return false; } QgsGeos geos( d->geometry ); return geos.crosses( *( geometry.d->geometry ) ); } QString QgsGeometry::exportToWkt( int precision ) const { if ( !d->geometry ) { return QString(); } return d->geometry->asWkt( precision ); } QString QgsGeometry::exportToGeoJSON( int precision ) const { if ( !d->geometry ) { return QStringLiteral( "null" ); } return d->geometry->asJSON( precision ); } QgsGeometry QgsGeometry::convertToType( QgsWkbTypes::GeometryType destType, bool destMultipart ) const { switch ( destType ) { case QgsWkbTypes::PointGeometry: return convertToPoint( destMultipart ); case QgsWkbTypes::LineGeometry: return convertToLine( destMultipart ); case QgsWkbTypes::PolygonGeometry: return convertToPolygon( destMultipart ); default: return QgsGeometry(); } } bool QgsGeometry::convertToMultiType() { if ( !d->geometry ) { return false; } if ( isMultipart() ) //already multitype, no need to convert { return true; } QgsGeometryCollection* multiGeom = dynamic_cast ( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) ); if ( !multiGeom ) { return false; } detach( true ); multiGeom->addGeometry( d->geometry ); d->geometry = multiGeom; return true; } bool QgsGeometry::convertToSingleType() { if ( !d->geometry ) { return false; } if ( !isMultipart() ) //already single part, no need to convert { return true; } QgsGeometryCollection* multiGeom = dynamic_cast( d->geometry ); if ( !multiGeom || multiGeom->partCount() < 1 ) return false; QgsAbstractGeometry* firstPart = multiGeom->geometryN( 0 )->clone(); detach( false ); d->geometry = firstPart; return true; } QgsPoint QgsGeometry::asPoint() const { if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != QgsWkbTypes::Point ) { return QgsPoint(); } QgsPointV2* pt = dynamic_cast( d->geometry ); if ( !pt ) { return QgsPoint(); } return QgsPoint( pt->x(), pt->y() ); } QgsPolyline QgsGeometry::asPolyline() const { QgsPolyline polyLine; if ( !d->geometry ) { return polyLine; } bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CompoundCurve || QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CircularString ); QgsLineString* line = nullptr; if ( doSegmentation ) { QgsCurve* curve = dynamic_cast( d->geometry ); if ( !curve ) { return polyLine; } line = curve->curveToLine(); } else { line = dynamic_cast( d->geometry ); if ( !line ) { return polyLine; } } int nVertices = line->numPoints(); polyLine.resize( nVertices ); for ( int i = 0; i < nVertices; ++i ) { QgsPointV2 pt = line->pointN( i ); polyLine[i].setX( pt.x() ); polyLine[i].setY( pt.y() ); } if ( doSegmentation ) { delete line; } return polyLine; } QgsPolygon QgsGeometry::asPolygon() const { if ( !d->geometry ) return QgsPolygon(); bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CurvePolygon ); QgsPolygonV2* p = nullptr; if ( doSegmentation ) { QgsCurvePolygon* curvePoly = dynamic_cast( d->geometry ); if ( !curvePoly ) { return QgsPolygon(); } p = curvePoly->toPolygon(); } else { p = dynamic_cast( d->geometry ); } if ( !p ) { return QgsPolygon(); } QgsPolygon polygon; convertPolygon( *p, polygon ); if ( doSegmentation ) { delete p; } return polygon; } QgsMultiPoint QgsGeometry::asMultiPoint() const { if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != QgsWkbTypes::MultiPoint ) { return QgsMultiPoint(); } const QgsMultiPointV2* mp = dynamic_cast( d->geometry ); if ( !mp ) { return QgsMultiPoint(); } int nPoints = mp->numGeometries(); QgsMultiPoint multiPoint( nPoints ); for ( int i = 0; i < nPoints; ++i ) { const QgsPointV2* pt = static_cast( mp->geometryN( i ) ); multiPoint[i].setX( pt->x() ); multiPoint[i].setY( pt->y() ); } return multiPoint; } QgsMultiPolyline QgsGeometry::asMultiPolyline() const { if ( !d->geometry ) { return QgsMultiPolyline(); } QgsGeometryCollection* geomCollection = dynamic_cast( d->geometry ); if ( !geomCollection ) { return QgsMultiPolyline(); } int nLines = geomCollection->numGeometries(); if ( nLines < 1 ) { return QgsMultiPolyline(); } QgsMultiPolyline mpl; for ( int i = 0; i < nLines; ++i ) { bool deleteLine = false; const QgsLineString* line = dynamic_cast( geomCollection->geometryN( i ) ); if ( !line ) { const QgsCurve* curve = dynamic_cast( geomCollection->geometryN( i ) ); if ( !curve ) { continue; } deleteLine = true; line = curve->curveToLine(); } QgsPointSequence lineCoords; line->points( lineCoords ); QgsPolyline polyLine; convertToPolyline( lineCoords, polyLine ); mpl.append( polyLine ); if ( deleteLine ) { delete line; } } return mpl; } QgsMultiPolygon QgsGeometry::asMultiPolygon() const { if ( !d->geometry ) { return QgsMultiPolygon(); } QgsGeometryCollection* geomCollection = dynamic_cast( d->geometry ); if ( !geomCollection ) { return QgsMultiPolygon(); } int nPolygons = geomCollection->numGeometries(); if ( nPolygons < 1 ) { return QgsMultiPolygon(); } QgsMultiPolygon mp; for ( int i = 0; i < nPolygons; ++i ) { const QgsPolygonV2* polygon = dynamic_cast( geomCollection->geometryN( i ) ); if ( !polygon ) { const QgsCurvePolygon* cPolygon = dynamic_cast( geomCollection->geometryN( i ) ); if ( cPolygon ) { polygon = cPolygon->toPolygon(); } else { continue; } } QgsPolygon poly; convertPolygon( *polygon, poly ); mp.append( poly ); } return mp; } double QgsGeometry::area() const { if ( !d->geometry ) { return -1.0; } QgsGeos g( d->geometry ); #if 0 //debug: compare geos area with calculation in QGIS double geosArea = g.area(); double qgisArea = 0; QgsSurface* surface = dynamic_cast( d->geometry ); if ( surface ) { qgisArea = surface->area(); } #endif return g.area(); } double QgsGeometry::length() const { if ( !d->geometry ) { return -1.0; } QgsGeos g( d->geometry ); return g.length(); } double QgsGeometry::distance( const QgsGeometry& geom ) const { if ( !d->geometry || !geom.d->geometry ) { return -1.0; } QgsGeos g( d->geometry ); return g.distance( *( geom.d->geometry ) ); } QgsGeometry QgsGeometry::buffer( double distance, int segments ) const { if ( !d->geometry ) { return QgsGeometry(); } QgsGeos g( d->geometry ); QgsAbstractGeometry* geom = g.buffer( distance, segments ); if ( !geom ) { return QgsGeometry(); } return QgsGeometry( geom ); } QgsGeometry QgsGeometry::buffer( double distance, int segments, EndCapStyle endCapStyle, JoinStyle joinStyle, double mitreLimit ) const { if ( !d->geometry ) { return QgsGeometry(); } QgsGeos g( d->geometry ); QgsAbstractGeometry* geom = g.buffer( distance, segments, endCapStyle, joinStyle, mitreLimit ); if ( !geom ) { return QgsGeometry(); } return QgsGeometry( geom ); } QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, JoinStyle joinStyle, double mitreLimit ) const { if ( !d->geometry || type() != QgsWkbTypes::LineGeometry ) { return QgsGeometry(); } if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) ) { QList parts = asGeometryCollection(); QList results; Q_FOREACH ( const QgsGeometry& part, parts ) { QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, mitreLimit ); if ( result ) results << result; } if ( results.isEmpty() ) return QgsGeometry(); QgsGeometry first = results.takeAt( 0 ); Q_FOREACH ( const QgsGeometry& result, results ) { first.addPart( result ); } return first; } else { QgsGeos geos( d->geometry ); QgsAbstractGeometry* offsetGeom = geos.offsetCurve( distance, segments, joinStyle, mitreLimit ); if ( !offsetGeom ) { return QgsGeometry(); } return QgsGeometry( offsetGeom ); } } QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, BufferSide side , JoinStyle joinStyle, double mitreLimit ) const { if ( !d->geometry || type() != QgsWkbTypes::LineGeometry ) { return QgsGeometry(); } if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) ) { QList parts = asGeometryCollection(); QList results; Q_FOREACH ( const QgsGeometry& part, parts ) { QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, mitreLimit ); if ( result ) results << result; } if ( results.isEmpty() ) return QgsGeometry(); QgsGeometry first = results.takeAt( 0 ); Q_FOREACH ( const QgsGeometry& result, results ) { first.addPart( result ); } return first; } else { QgsGeos geos( d->geometry ); QgsAbstractGeometry* bufferGeom = geos.singleSidedBuffer( distance, segments, side, joinStyle, mitreLimit ); if ( !bufferGeom ) { return QgsGeometry(); } return QgsGeometry( bufferGeom ); } } QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const { if ( !d->geometry || type() != QgsWkbTypes::LineGeometry ) { return QgsGeometry(); } if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) ) { QList parts = asGeometryCollection(); QList results; Q_FOREACH ( const QgsGeometry& part, parts ) { QgsGeometry result = part.extendLine( startDistance, endDistance ); if ( result ) results << result; } if ( results.isEmpty() ) return QgsGeometry(); QgsGeometry first = results.takeAt( 0 ); Q_FOREACH ( const QgsGeometry& result, results ) { first.addPart( result ); } return first; } else { QgsLineString* line = dynamic_cast< QgsLineString* >( d->geometry ); if ( !line ) return QgsGeometry(); QgsLineString* newLine = line->clone(); newLine->extend( startDistance, endDistance ); return QgsGeometry( newLine ); } } QgsGeometry QgsGeometry::simplify( double tolerance ) const { if ( !d->geometry ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsAbstractGeometry* simplifiedGeom = geos.simplify( tolerance ); if ( !simplifiedGeom ) { return QgsGeometry(); } return QgsGeometry( simplifiedGeom ); } QgsGeometry QgsGeometry::centroid() const { if ( !d->geometry ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsPointV2 centroid; bool ok = geos.centroid( centroid ); if ( !ok ) { return QgsGeometry(); } return QgsGeometry( centroid.clone() ); } QgsGeometry QgsGeometry::pointOnSurface() const { if ( !d->geometry ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsPointV2 pt; bool ok = geos.pointOnSurface( pt ); if ( !ok ) { return QgsGeometry(); } return QgsGeometry( pt.clone() ); } QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double* distanceToBoundary ) const { QgsInternalGeometryEngine engine( *this ); return engine.poleOfInaccessibility( precision, distanceToBoundary ); } QgsGeometry QgsGeometry::convexHull() const { if ( !d->geometry ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsAbstractGeometry* cHull = geos.convexHull(); if ( !cHull ) { return QgsGeometry(); } return QgsGeometry( cHull ); } QgsGeometry QgsGeometry::interpolate( double distance ) const { if ( !d->geometry ) { return QgsGeometry(); } QgsGeometry line = *this; if ( type() == QgsWkbTypes::PolygonGeometry ) line = QgsGeometry( d->geometry->boundary() ); QgsGeos geos( line.geometry() ); QgsAbstractGeometry* result = geos.interpolate( distance ); if ( !result ) { return QgsGeometry(); } return QgsGeometry( result ); } double QgsGeometry::lineLocatePoint( const QgsGeometry& point ) const { if ( type() != QgsWkbTypes::LineGeometry ) return -1; if ( QgsWkbTypes::flatType( point.wkbType() ) != QgsWkbTypes::Point ) return -1; QgsGeometry segmentized = *this; if ( QgsWkbTypes::isCurvedType( wkbType() ) ) { segmentized = QgsGeometry( static_cast< QgsCurve* >( d->geometry )->segmentize() ); } QgsGeos geos( d->geometry ); return geos.lineLocatePoint( *( static_cast< QgsPointV2* >( point.d->geometry ) ) ); } double QgsGeometry::interpolateAngle( double distance ) const { if ( !d->geometry ) return 0.0; // always operate on segmentized geometries QgsGeometry segmentized = *this; if ( QgsWkbTypes::isCurvedType( wkbType() ) ) { segmentized = QgsGeometry( static_cast< QgsCurve* >( d->geometry )->segmentize() ); } QgsVertexId previous; QgsVertexId next; if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.geometry(), distance, previous, next ) ) return 0.0; if ( previous == next ) { // distance coincided exactly with a vertex QgsVertexId v2 = previous; QgsVertexId v1; QgsVertexId v3; QgsGeometryUtils::adjacentVertices( *segmentized.geometry(), v2, v1, v3 ); if ( v1.isValid() && v3.isValid() ) { QgsPointV2 p1 = segmentized.geometry()->vertexAt( v1 ); QgsPointV2 p2 = segmentized.geometry()->vertexAt( v2 ); QgsPointV2 p3 = segmentized.geometry()->vertexAt( v3 ); double angle1 = QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() ); double angle2 = QgsGeometryUtils::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() ); return QgsGeometryUtils::averageAngle( angle1, angle2 ); } else if ( v3.isValid() ) { QgsPointV2 p1 = segmentized.geometry()->vertexAt( v2 ); QgsPointV2 p2 = segmentized.geometry()->vertexAt( v3 ); return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() ); } else { QgsPointV2 p1 = segmentized.geometry()->vertexAt( v1 ); QgsPointV2 p2 = segmentized.geometry()->vertexAt( v2 ); return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() ); } } else { QgsPointV2 p1 = segmentized.geometry()->vertexAt( previous ); QgsPointV2 p2 = segmentized.geometry()->vertexAt( next ); return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() ); } } QgsGeometry QgsGeometry::intersection( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsAbstractGeometry* resultGeom = geos.intersection( *( geometry.d->geometry ) ); return QgsGeometry( resultGeom ); } QgsGeometry QgsGeometry::combine( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsAbstractGeometry* resultGeom = geos.combine( *( geometry.d->geometry ) ); if ( !resultGeom ) { return QgsGeometry(); } return QgsGeometry( resultGeom ); } QgsGeometry QgsGeometry::mergeLines() const { if ( !d->geometry ) { return QgsGeometry(); } if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::LineString ) { // special case - a single linestring was passed return QgsGeometry( *this ); } QgsGeos geos( d->geometry ); return geos.mergeLines(); } QgsGeometry QgsGeometry::difference( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsAbstractGeometry* resultGeom = geos.difference( *( geometry.d->geometry ) ); if ( !resultGeom ) { return QgsGeometry(); } return QgsGeometry( resultGeom ); } QgsGeometry QgsGeometry::symDifference( const QgsGeometry& geometry ) const { if ( !d->geometry || geometry.isEmpty() ) { return QgsGeometry(); } QgsGeos geos( d->geometry ); QgsAbstractGeometry* resultGeom = geos.symDifference( *( geometry.d->geometry ) ); if ( !resultGeom ) { return QgsGeometry(); } return QgsGeometry( resultGeom ); } QgsGeometry QgsGeometry::extrude( double x, double y ) { QgsInternalGeometryEngine engine( *this ); return engine.extrude( x, y ); } QByteArray QgsGeometry::exportToWkb() const { return d->geometry ? d->geometry->asWkb() : QByteArray(); } QList QgsGeometry::asGeometryCollection() const { QList geometryList; if ( !d->geometry ) { return geometryList; } QgsGeometryCollection* gc = dynamic_cast( d->geometry ); if ( gc ) { int numGeom = gc->numGeometries(); geometryList.reserve( numGeom ); for ( int i = 0; i < numGeom; ++i ) { geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) ); } } else //a singlepart geometry { geometryList.append( QgsGeometry( d->geometry->clone() ) ); } return geometryList; } QPointF QgsGeometry::asQPointF() const { QgsPoint point = asPoint(); return point.toQPointF(); } QPolygonF QgsGeometry::asQPolygonF() const { QPolygonF result; QgsPolyline polyline; QgsWkbTypes::Type type = wkbType(); if ( type == QgsWkbTypes::LineString || type == QgsWkbTypes::LineString25D ) { polyline = asPolyline(); } else if ( type == QgsWkbTypes::Polygon || type == QgsWkbTypes::Polygon25D ) { QgsPolygon polygon = asPolygon(); if ( polygon.size() < 1 ) return result; polyline = polygon.at( 0 ); } else { return result; } QgsPolyline::const_iterator lineIt = polyline.constBegin(); for ( ; lineIt != polyline.constEnd(); ++lineIt ) { result << lineIt->toQPointF(); } return result; } bool QgsGeometry::deleteRing( int ringNum, int partNum ) { if ( !d->geometry ) { return false; } detach( true ); bool ok = QgsGeometryEditUtils::deleteRing( d->geometry, ringNum, partNum ); return ok; } bool QgsGeometry::deletePart( int partNum ) { if ( !d->geometry ) { return false; } if ( !isMultipart() && partNum < 1 ) { setGeometry( nullptr ); return true; } detach( true ); bool ok = QgsGeometryEditUtils::deletePart( d->geometry, partNum ); return ok; } int QgsGeometry::avoidIntersections( const QList& avoidIntersectionsLayers, const QHash > &ignoreFeatures ) { if ( !d->geometry ) { return 1; } QgsAbstractGeometry* diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, ignoreFeatures ); if ( diffGeom ) { detach( false ); d->geometry = diffGeom; } return 0; } void QgsGeometry::validateGeometry( QList &errors ) { QgsGeometryValidator::validateGeometry( this, errors ); } bool QgsGeometry::isGeosValid() const { if ( !d->geometry ) { return false; } QgsGeos geos( d->geometry ); return geos.isValid(); } bool QgsGeometry::isGeosEqual( const QgsGeometry& g ) const { if ( !d->geometry || !g.d->geometry ) { return false; } QgsGeos geos( d->geometry ); return geos.isEqual( *( g.d->geometry ) ); } bool QgsGeometry::isGeosEmpty() const { if ( !d->geometry ) { return false; } QgsGeos geos( d->geometry ); return geos.isEmpty(); } QgsGeometry QgsGeometry::unaryUnion( const QList& geometryList ) { QgsGeos geos( nullptr ); QList geomV2List; QList::const_iterator it = geometryList.constBegin(); for ( ; it != geometryList.constEnd(); ++it ) { if ( !(( *it ).isEmpty() ) ) { geomV2List.append(( *it ).geometry() ); } } QgsAbstractGeometry* geom = geos.combine( geomV2List ); return QgsGeometry( geom ); } void QgsGeometry::convertToStraightSegment() { if ( !d->geometry || !requiresConversionToStraightSegments() ) { return; } QgsAbstractGeometry* straightGeom = d->geometry->segmentize(); detach( false ); d->geometry = straightGeom; } bool QgsGeometry::requiresConversionToStraightSegments() const { if ( !d->geometry ) { return false; } return d->geometry->hasCurvedSegments(); } int QgsGeometry::transform( const QgsCoordinateTransform& ct ) { if ( !d->geometry ) { return 1; } detach(); d->geometry->transform( ct ); return 0; } int QgsGeometry::transform( const QTransform& ct ) { if ( !d->geometry ) { return 1; } detach(); d->geometry->transform( ct ); return 0; } void QgsGeometry::mapToPixel( const QgsMapToPixel& mtp ) { if ( d->geometry ) { detach(); d->geometry->transform( mtp.transform() ); } } #if 0 void QgsGeometry::clip( const QgsRectangle& rect ) { if ( d->geometry ) { detach(); d->geometry->clip( rect ); removeWkbGeos(); } } #endif void QgsGeometry::draw( QPainter& p ) const { if ( d->geometry ) { d->geometry->draw( p ); } } bool QgsGeometry::vertexIdFromVertexNr( int nr, QgsVertexId& id ) const { if ( !d->geometry ) { return false; } QgsCoordinateSequence coords = d->geometry->coordinateSequence(); int vertexCount = 0; for ( int part = 0; part < coords.size(); ++part ) { const QgsRingSequence &featureCoords = coords.at( part ); for ( int ring = 0; ring < featureCoords.size(); ++ring ) { const QgsPointSequence &ringCoords = featureCoords.at( ring ); for ( int vertex = 0; vertex < ringCoords.size(); ++vertex ) { if ( vertexCount == nr ) { id.part = part; id.ring = ring; id.vertex = vertex; return true; } ++vertexCount; } } } return false; } int QgsGeometry::vertexNrFromVertexId( QgsVertexId id ) const { if ( !d->geometry ) { return -1; } QgsCoordinateSequence coords = d->geometry->coordinateSequence(); int vertexCount = 0; for ( int part = 0; part < coords.size(); ++part ) { const QgsRingSequence &featureCoords = coords.at( part ); for ( int ring = 0; ring < featureCoords.size(); ++ring ) { const QgsPointSequence &ringCoords = featureCoords.at( ring ); for ( int vertex = 0; vertex < ringCoords.size(); ++vertex ) { if ( vertex == id.vertex && ring == id.ring && part == id.part ) { return vertexCount; } ++vertexCount; } } } return -1; } void QgsGeometry::convertPointList( const QList &input, QgsPointSequence &output ) { output.clear(); QList::const_iterator it = input.constBegin(); for ( ; it != input.constEnd(); ++it ) { output.append( QgsPointV2( it->x(), it->y() ) ); } } void QgsGeometry::convertPointList( const QgsPointSequence &input, QList &output ) { output.clear(); QgsPointSequence::const_iterator it = input.constBegin(); for ( ; it != input.constEnd(); ++it ) { output.append( QgsPoint( it->x(), it->y() ) ); } } QgsGeometry::operator bool() const { return d->geometry; } void QgsGeometry::convertToPolyline( const QgsPointSequence &input, QgsPolyline& output ) { output.clear(); output.resize( input.size() ); for ( int i = 0; i < input.size(); ++i ) { const QgsPointV2& pt = input.at( i ); output[i].setX( pt.x() ); output[i].setY( pt.y() ); } } void QgsGeometry::convertPolygon( const QgsPolygonV2& input, QgsPolygon& output ) { output.clear(); QgsCoordinateSequence coords = input.coordinateSequence(); if ( coords.size() < 1 ) { return; } const QgsRingSequence &rings = coords[0]; output.resize( rings.size() ); for ( int i = 0; i < rings.size(); ++i ) { convertToPolyline( rings[i], output[i] ); } } GEOSContextHandle_t QgsGeometry::getGEOSHandler() { return QgsGeos::getGEOSHandler(); } QgsGeometry QgsGeometry::fromQPointF( QPointF point ) { return QgsGeometry( new QgsPointV2( point.x(), point.y() ) ); } QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon ) { if ( polygon.isClosed() ) { return QgsGeometry::fromPolygon( createPolygonFromQPolygonF( polygon ) ); } else { return QgsGeometry::fromPolyline( createPolylineFromQPolygonF( polygon ) ); } } QgsPolygon QgsGeometry::createPolygonFromQPolygonF( const QPolygonF &polygon ) { QgsPolygon result; result << createPolylineFromQPolygonF( polygon ); return result; } QgsPolyline QgsGeometry::createPolylineFromQPolygonF( const QPolygonF &polygon ) { QgsPolyline result; QPolygonF::const_iterator it = polygon.constBegin(); for ( ; it != polygon.constEnd(); ++it ) { result.append( QgsPoint( *it ) ); } return result; } bool QgsGeometry::compare( const QgsPolyline &p1, const QgsPolyline &p2, double epsilon ) { if ( p1.count() != p2.count() ) return false; for ( int i = 0; i < p1.count(); ++i ) { if ( !p1.at( i ).compare( p2.at( i ), epsilon ) ) return false; } return true; } bool QgsGeometry::compare( const QgsPolygon &p1, const QgsPolygon &p2, double epsilon ) { if ( p1.count() != p2.count() ) return false; for ( int i = 0; i < p1.count(); ++i ) { if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) ) return false; } return true; } bool QgsGeometry::compare( const QgsMultiPolygon &p1, const QgsMultiPolygon &p2, double epsilon ) { if ( p1.count() != p2.count() ) return false; for ( int i = 0; i < p1.count(); ++i ) { if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) ) return false; } return true; } QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const { if ( d->geometry->isEmpty() ) return QgsGeometry(); QgsGeometry geom = *this; if ( QgsWkbTypes::isCurvedType( wkbType() ) ) geom = QgsGeometry( d->geometry->segmentize() ); switch ( QgsWkbTypes::flatType( geom.wkbType() ) ) { case QgsWkbTypes::Point: case QgsWkbTypes::MultiPoint: //can't smooth a point based geometry return geom; case QgsWkbTypes::LineString: { QgsLineString* lineString = static_cast< QgsLineString* >( d->geometry ); return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) ); } case QgsWkbTypes::MultiLineString: { QgsMultiLineString* multiLine = static_cast< QgsMultiLineString* >( d->geometry ); QgsMultiLineString* resultMultiline = new QgsMultiLineString(); for ( int i = 0; i < multiLine->numGeometries(); ++i ) { resultMultiline->addGeometry( smoothLine( *( static_cast< QgsLineString* >( multiLine->geometryN( i ) ) ), iterations, offset, minimumDistance, maxAngle ) ); } return QgsGeometry( resultMultiline ); } case QgsWkbTypes::Polygon: { QgsPolygonV2* poly = static_cast< QgsPolygonV2* >( d->geometry ); return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) ); } case QgsWkbTypes::MultiPolygon: { QgsMultiPolygonV2* multiPoly = static_cast< QgsMultiPolygonV2* >( d->geometry ); QgsMultiPolygonV2* resultMultiPoly = new QgsMultiPolygonV2(); for ( int i = 0; i < multiPoly->numGeometries(); ++i ) { resultMultiPoly->addGeometry( smoothPolygon( *( static_cast< QgsPolygonV2* >( multiPoly->geometryN( i ) ) ), iterations, offset, minimumDistance, maxAngle ) ); } return QgsGeometry( resultMultiPoly ); } case QgsWkbTypes::Unknown: default: return QgsGeometry( *this ); } } inline QgsPointV2 interpolatePointOnLine( const QgsPointV2& p1, const QgsPointV2& p2, const double offset ) { double deltaX = p2.x() - p1.x(); double deltaY = p2.y() - p1.y(); return QgsPointV2( p1.x() + deltaX * offset, p1.y() + deltaY * offset ); } QgsLineString* smoothCurve( const QgsLineString& line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing ) { QScopedPointer< QgsLineString > result( new QgsLineString( line ) ); for ( unsigned int iteration = 0; iteration < iterations; ++iteration ) { QgsPointSequence outputLine; outputLine.reserve( 2 * ( result->numPoints() - 1 ) ); bool skipFirst = false; bool skipLast = false; if ( isRing ) { QgsPointV2 p1 = result->pointN( result->numPoints() - 2 ); QgsPointV2 p2 = result->pointN( 0 ); QgsPointV2 p3 = result->pointN( 1 ); double angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ); angle = qAbs( M_PI - angle ); skipFirst = angle > maxAngleRads; } for ( int i = 0; i < result->numPoints() - 1; i++ ) { QgsPointV2 p1 = result->pointN( i ); QgsPointV2 p2 = result->pointN( i + 1 ); double angle = M_PI; if ( i == 0 && isRing ) { QgsPointV2 p3 = result->pointN( result->numPoints() - 2 ); angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ); } else if ( i < result->numPoints() - 2 ) { QgsPointV2 p3 = result->pointN( i + 2 ); angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ); } else if ( i == result->numPoints() - 2 && isRing ) { QgsPointV2 p3 = result->pointN( 1 ); angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ); } skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads; // don't apply distance threshold to first or last segment if ( i == 0 || i >= result->numPoints() - 2 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold ) { if ( !isRing ) { if ( !skipFirst ) outputLine << ( i == 0 ? result->pointN( i ) : interpolatePointOnLine( p1, p2, offset ) ); if ( !skipLast ) outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : interpolatePointOnLine( p1, p2, 1.0 - offset ) ); else outputLine << p2; } else { // ring if ( !skipFirst ) outputLine << interpolatePointOnLine( p1, p2, offset ); else if ( i == 0 ) outputLine << p1; if ( !skipLast ) outputLine << interpolatePointOnLine( p1, p2, 1.0 - offset ); else outputLine << p2; } } skipFirst = skipLast; } if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) ) outputLine << outputLine.at( 0 ); result->setPoints( outputLine ); } return result.take(); } QgsLineString* QgsGeometry::smoothLine( const QgsLineString& line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const { double maxAngleRads = maxAngle * M_PI / 180.0; double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1; return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false ); } QgsPolygonV2* QgsGeometry::smoothPolygon( const QgsPolygonV2& polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const { double maxAngleRads = maxAngle * M_PI / 180.0; double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1; QScopedPointer< QgsPolygonV2 > resultPoly( new QgsPolygonV2 ); resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString*>( polygon.exteriorRing() ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ) ); for ( int i = 0; i < polygon.numInteriorRings(); ++i ) { resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString*>( polygon.interiorRing( i ) ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ) ); } return resultPoly.take(); } QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const { switch ( type() ) { case QgsWkbTypes::PointGeometry: { bool srcIsMultipart = isMultipart(); if (( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) ) { // return a copy of the same geom return QgsGeometry( *this ); } if ( destMultipart ) { // layer is multipart => make a multipoint with a single point return fromMultiPoint( QgsMultiPoint() << asPoint() ); } else { // destination is singlepart => make a single part if possible QgsMultiPoint multiPoint = asMultiPoint(); if ( multiPoint.count() == 1 ) { return fromPoint( multiPoint[0] ); } } return QgsGeometry(); } case QgsWkbTypes::LineGeometry: { // only possible if destination is multipart if ( !destMultipart ) return QgsGeometry(); // input geometry is multipart if ( isMultipart() ) { QgsMultiPolyline multiLine = asMultiPolyline(); QgsMultiPoint multiPoint; for ( QgsMultiPolyline::const_iterator multiLineIt = multiLine.constBegin(); multiLineIt != multiLine.constEnd(); ++multiLineIt ) for ( QgsPolyline::const_iterator lineIt = ( *multiLineIt ).constBegin(); lineIt != ( *multiLineIt ).constEnd(); ++lineIt ) multiPoint << *lineIt; return fromMultiPoint( multiPoint ); } // input geometry is not multipart: copy directly the line into a multipoint else { QgsPolyline line = asPolyline(); if ( !line.isEmpty() ) return fromMultiPoint( line ); } return QgsGeometry(); } case QgsWkbTypes::PolygonGeometry: { // can only transform if destination is multipoint if ( !destMultipart ) return QgsGeometry(); // input geometry is multipart: make a multipoint from multipolygon if ( isMultipart() ) { QgsMultiPolygon multiPolygon = asMultiPolygon(); QgsMultiPoint multiPoint; for ( QgsMultiPolygon::const_iterator polygonIt = multiPolygon.constBegin(); polygonIt != multiPolygon.constEnd(); ++polygonIt ) for ( QgsMultiPolyline::const_iterator multiLineIt = ( *polygonIt ).constBegin(); multiLineIt != ( *polygonIt ).constEnd(); ++multiLineIt ) for ( QgsPolyline::const_iterator lineIt = ( *multiLineIt ).constBegin(); lineIt != ( *multiLineIt ).constEnd(); ++lineIt ) multiPoint << *lineIt; return fromMultiPoint( multiPoint ); } // input geometry is not multipart: make a multipoint from polygon else { QgsPolygon polygon = asPolygon(); QgsMultiPoint multiPoint; for ( QgsMultiPolyline::const_iterator multiLineIt = polygon.constBegin(); multiLineIt != polygon.constEnd(); ++multiLineIt ) for ( QgsPolyline::const_iterator lineIt = ( *multiLineIt ).constBegin(); lineIt != ( *multiLineIt ).constEnd(); ++lineIt ) multiPoint << *lineIt; return fromMultiPoint( multiPoint ); } } default: return QgsGeometry(); } } QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const { switch ( type() ) { case QgsWkbTypes::PointGeometry: { if ( !isMultipart() ) return QgsGeometry(); QgsMultiPoint multiPoint = asMultiPoint(); if ( multiPoint.count() < 2 ) return QgsGeometry(); if ( destMultipart ) return fromMultiPolyline( QgsMultiPolyline() << multiPoint ); else return fromPolyline( multiPoint ); } case QgsWkbTypes::LineGeometry: { bool srcIsMultipart = isMultipart(); if (( destMultipart && srcIsMultipart ) || ( !destMultipart && ! srcIsMultipart ) ) { // return a copy of the same geom return QgsGeometry( *this ); } if ( destMultipart ) { // destination is multipart => makes a multipoint with a single line QgsPolyline line = asPolyline(); if ( !line.isEmpty() ) return fromMultiPolyline( QgsMultiPolyline() << line ); } else { // destination is singlepart => make a single part if possible QgsMultiPolyline multiLine = asMultiPolyline(); if ( multiLine.count() == 1 ) return fromPolyline( multiLine[0] ); } return QgsGeometry(); } case QgsWkbTypes::PolygonGeometry: { // input geometry is multipolygon if ( isMultipart() ) { QgsMultiPolygon multiPolygon = asMultiPolygon(); QgsMultiPolyline multiLine; for ( QgsMultiPolygon::const_iterator polygonIt = multiPolygon.constBegin(); polygonIt != multiPolygon.constEnd(); ++polygonIt ) for ( QgsMultiPolyline::const_iterator multiLineIt = ( *polygonIt ).constBegin(); multiLineIt != ( *polygonIt ).constEnd(); ++multiLineIt ) multiLine << *multiLineIt; if ( destMultipart ) { // destination is multipart return fromMultiPolyline( multiLine ); } else if ( multiLine.count() == 1 ) { // destination is singlepart => make a single part if possible return fromPolyline( multiLine[0] ); } } // input geometry is single polygon else { QgsPolygon polygon = asPolygon(); // if polygon has rings if ( polygon.count() > 1 ) { // cannot fit a polygon with rings in a single line layer // TODO: would it be better to remove rings? if ( destMultipart ) { QgsPolygon polygon = asPolygon(); QgsMultiPolyline multiLine; for ( QgsMultiPolyline::const_iterator multiLineIt = polygon.constBegin(); multiLineIt != polygon.constEnd(); ++multiLineIt ) multiLine << *multiLineIt; return fromMultiPolyline( multiLine ); } } // no rings else if ( polygon.count() == 1 ) { if ( destMultipart ) { return fromMultiPolyline( polygon ); } else { return fromPolyline( polygon[0] ); } } } return QgsGeometry(); } default: return QgsGeometry(); } } QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const { switch ( type() ) { case QgsWkbTypes::PointGeometry: { if ( !isMultipart() ) return QgsGeometry(); QgsMultiPoint multiPoint = asMultiPoint(); if ( multiPoint.count() < 3 ) return QgsGeometry(); if ( multiPoint.last() != multiPoint.first() ) multiPoint << multiPoint.first(); QgsPolygon polygon = QgsPolygon() << multiPoint; if ( destMultipart ) return fromMultiPolygon( QgsMultiPolygon() << polygon ); else return fromPolygon( polygon ); } case QgsWkbTypes::LineGeometry: { // input geometry is multiline if ( isMultipart() ) { QgsMultiPolyline multiLine = asMultiPolyline(); QgsMultiPolygon multiPolygon; for ( QgsMultiPolyline::iterator multiLineIt = multiLine.begin(); multiLineIt != multiLine.end(); ++multiLineIt ) { // do not create polygon for a 1 segment line if (( *multiLineIt ).count() < 3 ) return QgsGeometry(); if (( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() ) return QgsGeometry(); // add closing node if (( *multiLineIt ).first() != ( *multiLineIt ).last() ) *multiLineIt << ( *multiLineIt ).first(); multiPolygon << ( QgsPolygon() << *multiLineIt ); } // check that polygons were inserted if ( !multiPolygon.isEmpty() ) { if ( destMultipart ) { return fromMultiPolygon( multiPolygon ); } else if ( multiPolygon.count() == 1 ) { // destination is singlepart => make a single part if possible return fromPolygon( multiPolygon[0] ); } } } // input geometry is single line else { QgsPolyline line = asPolyline(); // do not create polygon for a 1 segment line if ( line.count() < 3 ) return QgsGeometry(); if ( line.count() == 3 && line.first() == line.last() ) return QgsGeometry(); // add closing node if ( line.first() != line.last() ) line << line.first(); // destination is multipart if ( destMultipart ) { return fromMultiPolygon( QgsMultiPolygon() << ( QgsPolygon() << line ) ); } else { return fromPolygon( QgsPolygon() << line ); } } return QgsGeometry(); } case QgsWkbTypes::PolygonGeometry: { bool srcIsMultipart = isMultipart(); if (( destMultipart && srcIsMultipart ) || ( !destMultipart && ! srcIsMultipart ) ) { // return a copy of the same geom return QgsGeometry( *this ); } if ( destMultipart ) { // destination is multipart => makes a multipoint with a single polygon QgsPolygon polygon = asPolygon(); if ( !polygon.isEmpty() ) return fromMultiPolygon( QgsMultiPolygon() << polygon ); } else { QgsMultiPolygon multiPolygon = asMultiPolygon(); if ( multiPolygon.count() == 1 ) { // destination is singlepart => make a single part if possible return fromPolygon( multiPolygon[0] ); } } return QgsGeometry(); } default: return QgsGeometry(); } } QgsGeometryEngine* QgsGeometry::createGeometryEngine( const QgsAbstractGeometry* geometry ) { return new QgsGeos( geometry ); } QDataStream& operator<<( QDataStream& out, const QgsGeometry& geometry ) { out << geometry.exportToWkb(); return out; } QDataStream& operator>>( QDataStream& in, QgsGeometry& geometry ) { QByteArray byteArray; in >> byteArray; if ( byteArray.isEmpty() ) { geometry.setGeometry( nullptr ); return in; } geometry.fromWkb( byteArray ); return in; }