1 /***************************************************************************
2   qgsgeometry.cpp - Geometry (stored as Open Geospatial Consortium WKB)
3   -------------------------------------------------------------------
4 Date                 : 02 May 2005
5 Copyright            : (C) 2005 by Brendan Morley
6 email                : morb at ozemail dot com dot au
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include <limits>
17 #include <cstdarg>
18 #include <cstdio>
19 #include <cmath>
20 #include <nlohmann/json.hpp>
21 
22 #include "qgis.h"
23 #include "qgsgeometry.h"
24 #include "qgsgeometryeditutils.h"
25 #include "qgsgeometryfactory.h"
26 #include "qgsgeometrymakevalid.h"
27 #include "qgsgeometryutils.h"
28 #include "qgsinternalgeometryengine.h"
29 #include "qgsgeos.h"
30 #include "qgsapplication.h"
31 #include "qgslogger.h"
32 #include "qgsmaptopixel.h"
33 #include "qgsmessagelog.h"
34 #include "qgspointxy.h"
35 #include "qgsrectangle.h"
36 
37 #include "qgsvectorlayer.h"
38 #include "qgsgeometryvalidator.h"
39 
40 #include "qgsmulticurve.h"
41 #include "qgsmultilinestring.h"
42 #include "qgsmultipoint.h"
43 #include "qgsmultipolygon.h"
44 #include "qgsmultisurface.h"
45 #include "qgspoint.h"
46 #include "qgspolygon.h"
47 #include "qgslinestring.h"
48 #include "qgscircle.h"
49 #include "qgscurve.h"
50 
51 struct QgsGeometryPrivate
52 {
QgsGeometryPrivateQgsGeometryPrivate53   QgsGeometryPrivate(): ref( 1 ) {}
54   QAtomicInt ref;
55   std::unique_ptr< QgsAbstractGeometry > geometry;
56 };
57 
QgsGeometry()58 QgsGeometry::QgsGeometry()
59   : d( new QgsGeometryPrivate() )
60 {
61 }
62 
~QgsGeometry()63 QgsGeometry::~QgsGeometry()
64 {
65   if ( !d->ref.deref() )
66     delete d;
67 }
68 
QgsGeometry(QgsAbstractGeometry * geom)69 QgsGeometry::QgsGeometry( QgsAbstractGeometry *geom )
70   : d( new QgsGeometryPrivate() )
71 {
72   d->geometry.reset( geom );
73   d->ref = QAtomicInt( 1 );
74 }
75 
QgsGeometry(std::unique_ptr<QgsAbstractGeometry> geom)76 QgsGeometry::QgsGeometry( std::unique_ptr<QgsAbstractGeometry> geom )
77   : d( new QgsGeometryPrivate() )
78 {
79   d->geometry = std::move( geom );
80   d->ref = QAtomicInt( 1 );
81 }
82 
QgsGeometry(const QgsGeometry & other)83 QgsGeometry::QgsGeometry( const QgsGeometry &other )
84   : d( other.d )
85 {
86   mLastError = other.mLastError;
87   d->ref.ref();
88 }
89 
operator =(QgsGeometry const & other)90 QgsGeometry &QgsGeometry::operator=( QgsGeometry const &other )
91 {
92   if ( this != &other )
93   {
94     if ( !d->ref.deref() )
95     {
96       delete d;
97     }
98 
99     mLastError = other.mLastError;
100     d = other.d;
101     d->ref.ref();
102   }
103   return *this;
104 }
105 
detach()106 void QgsGeometry::detach()
107 {
108   if ( d->ref <= 1 )
109     return;
110 
111   std::unique_ptr< QgsAbstractGeometry > cGeom;
112   if ( d->geometry )
113     cGeom.reset( d->geometry->clone() );
114 
115   reset( std::move( cGeom ) );
116 }
117 
reset(std::unique_ptr<QgsAbstractGeometry> newGeometry)118 void QgsGeometry::reset( std::unique_ptr<QgsAbstractGeometry> newGeometry )
119 {
120   if ( d->ref > 1 )
121   {
122     ( void )d->ref.deref();
123     d = new QgsGeometryPrivate();
124   }
125   d->geometry = std::move( newGeometry );
126 }
127 
constGet() const128 const QgsAbstractGeometry *QgsGeometry::constGet() const
129 {
130   return d->geometry.get();
131 }
132 
get()133 QgsAbstractGeometry *QgsGeometry::get()
134 {
135   detach();
136   return d->geometry.get();
137 }
138 
set(QgsAbstractGeometry * geometry)139 void QgsGeometry::set( QgsAbstractGeometry *geometry )
140 {
141   if ( d->geometry.get() == geometry )
142   {
143     return;
144   }
145 
146   reset( std::unique_ptr< QgsAbstractGeometry >( geometry ) );
147 }
148 
isNull() const149 bool QgsGeometry::isNull() const
150 {
151   return !d->geometry;
152 }
153 
fromWkt(const QString & wkt)154 QgsGeometry QgsGeometry::fromWkt( const QString &wkt )
155 {
156   std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkt( wkt );
157   if ( !geom )
158   {
159     return QgsGeometry();
160   }
161   return QgsGeometry( std::move( geom ) );
162 }
163 
fromPointXY(const QgsPointXY & point)164 QgsGeometry QgsGeometry::fromPointXY( const QgsPointXY &point )
165 {
166   std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::fromPointXY( point ) );
167   if ( geom )
168   {
169     return QgsGeometry( geom.release() );
170   }
171   return QgsGeometry();
172 }
173 
fromPolylineXY(const QgsPolylineXY & polyline)174 QgsGeometry QgsGeometry::fromPolylineXY( const QgsPolylineXY &polyline )
175 {
176   std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolylineXY( polyline );
177   if ( geom )
178   {
179     return QgsGeometry( std::move( geom ) );
180   }
181   return QgsGeometry();
182 }
183 
fromPolyline(const QgsPolyline & polyline)184 QgsGeometry QgsGeometry::fromPolyline( const QgsPolyline &polyline )
185 {
186   return QgsGeometry( qgis::make_unique< QgsLineString >( polyline ) );
187 }
188 
fromPolygonXY(const QgsPolygonXY & polygon)189 QgsGeometry QgsGeometry::fromPolygonXY( const QgsPolygonXY &polygon )
190 {
191   std::unique_ptr< QgsPolygon > geom = QgsGeometryFactory::fromPolygonXY( polygon );
192   if ( geom )
193   {
194     return QgsGeometry( std::move( geom.release() ) );
195   }
196   return QgsGeometry();
197 }
198 
fromMultiPointXY(const QgsMultiPointXY & multipoint)199 QgsGeometry QgsGeometry::fromMultiPointXY( const QgsMultiPointXY &multipoint )
200 {
201   std::unique_ptr< QgsMultiPoint > geom = QgsGeometryFactory::fromMultiPointXY( multipoint );
202   if ( geom )
203   {
204     return QgsGeometry( std::move( geom ) );
205   }
206   return QgsGeometry();
207 }
208 
fromMultiPolylineXY(const QgsMultiPolylineXY & multiline)209 QgsGeometry QgsGeometry::fromMultiPolylineXY( const QgsMultiPolylineXY &multiline )
210 {
211   std::unique_ptr< QgsMultiLineString > geom = QgsGeometryFactory::fromMultiPolylineXY( multiline );
212   if ( geom )
213   {
214     return QgsGeometry( std::move( geom ) );
215   }
216   return QgsGeometry();
217 }
218 
fromMultiPolygonXY(const QgsMultiPolygonXY & multipoly)219 QgsGeometry QgsGeometry::fromMultiPolygonXY( const QgsMultiPolygonXY &multipoly )
220 {
221   std::unique_ptr< QgsMultiPolygon > geom = QgsGeometryFactory::fromMultiPolygonXY( multipoly );
222   if ( geom )
223   {
224     return QgsGeometry( std::move( geom ) );
225   }
226   return QgsGeometry();
227 }
228 
fromRect(const QgsRectangle & rect)229 QgsGeometry QgsGeometry::fromRect( const QgsRectangle &rect )
230 {
231   std::unique_ptr< QgsLineString > ext = qgis::make_unique< QgsLineString >(
232       QVector< double >() << rect.xMinimum()
233       << rect.xMaximum()
234       << rect.xMaximum()
235       << rect.xMinimum()
236       << rect.xMinimum(),
237       QVector< double >() << rect.yMinimum()
238       << rect.yMinimum()
239       << rect.yMaximum()
240       << rect.yMaximum()
241       << rect.yMinimum() );
242   std::unique_ptr< QgsPolygon > polygon = qgis::make_unique< QgsPolygon >();
243   polygon->setExteriorRing( ext.release() );
244   return QgsGeometry( std::move( polygon ) );
245 }
246 
collectGeometry(const QVector<QgsGeometry> & geometries)247 QgsGeometry QgsGeometry::collectGeometry( const QVector< QgsGeometry > &geometries )
248 {
249   QgsGeometry collected;
250 
251   for ( const QgsGeometry &g : geometries )
252   {
253     if ( collected.isNull() )
254     {
255       collected = g;
256       collected.convertToMultiType();
257     }
258     else
259     {
260       if ( g.isMultipart() )
261       {
262         for ( auto p = g.const_parts_begin(); p != g.const_parts_end(); ++p )
263         {
264           collected.addPart( ( *p )->clone() );
265         }
266       }
267       else
268       {
269         collected.addPart( g );
270       }
271     }
272   }
273   return collected;
274 }
275 
createWedgeBuffer(const QgsPoint & center,const double azimuth,const double angularWidth,const double outerRadius,const double innerRadius)276 QgsGeometry QgsGeometry::createWedgeBuffer( const QgsPoint &center, const double azimuth, const double angularWidth, const double outerRadius, const double innerRadius )
277 {
278   if ( std::abs( angularWidth ) >= 360.0 )
279   {
280     std::unique_ptr< QgsCompoundCurve > outerCc = qgis::make_unique< QgsCompoundCurve >();
281 
282     QgsCircle outerCircle = QgsCircle( center, outerRadius );
283     outerCc->addCurve( outerCircle.toCircularString() );
284 
285     std::unique_ptr< QgsCurvePolygon > cp = qgis::make_unique< QgsCurvePolygon >();
286     cp->setExteriorRing( outerCc.release() );
287 
288     if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
289     {
290       std::unique_ptr< QgsCompoundCurve > innerCc = qgis::make_unique< QgsCompoundCurve >();
291 
292       QgsCircle innerCircle = QgsCircle( center, innerRadius );
293       innerCc->addCurve( innerCircle.toCircularString() );
294 
295       cp->setInteriorRings( { innerCc.release() } );
296     }
297 
298     return QgsGeometry( std::move( cp ) );
299   }
300 
301   std::unique_ptr< QgsCompoundCurve > wedge = qgis::make_unique< QgsCompoundCurve >();
302 
303   const double startAngle = azimuth - angularWidth * 0.5;
304   const double endAngle = azimuth + angularWidth * 0.5;
305 
306   const QgsPoint outerP1 = center.project( outerRadius, startAngle );
307   const QgsPoint outerP2 = center.project( outerRadius, endAngle );
308 
309   const bool useShortestArc = angularWidth <= 180.0;
310 
311   wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( outerP1, outerP2, center, useShortestArc ) ) );
312 
313   if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
314   {
315     const QgsPoint innerP1 = center.project( innerRadius, startAngle );
316     const QgsPoint innerP2 = center.project( innerRadius, endAngle );
317     wedge->addCurve( new QgsLineString( outerP2, innerP2 ) );
318     wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( innerP2, innerP1, center, useShortestArc ) ) );
319     wedge->addCurve( new QgsLineString( innerP1, outerP1 ) );
320   }
321   else
322   {
323     wedge->addCurve( new QgsLineString( outerP2, center ) );
324     wedge->addCurve( new QgsLineString( center, outerP1 ) );
325   }
326 
327   std::unique_ptr< QgsCurvePolygon > cp = qgis::make_unique< QgsCurvePolygon >();
328   cp->setExteriorRing( wedge.release() );
329   return QgsGeometry( std::move( cp ) );
330 }
331 
fromWkb(unsigned char * wkb,int length)332 void QgsGeometry::fromWkb( unsigned char *wkb, int length )
333 {
334   QgsConstWkbPtr ptr( wkb, length );
335   reset( QgsGeometryFactory::geomFromWkb( ptr ) );
336   delete [] wkb;
337 }
338 
fromWkb(const QByteArray & wkb)339 void QgsGeometry::fromWkb( const QByteArray &wkb )
340 {
341   QgsConstWkbPtr ptr( wkb );
342   reset( QgsGeometryFactory::geomFromWkb( ptr ) );
343 }
344 
wkbType() const345 QgsWkbTypes::Type QgsGeometry::wkbType() const
346 {
347   if ( !d->geometry )
348   {
349     return QgsWkbTypes::Unknown;
350   }
351   else
352   {
353     return d->geometry->wkbType();
354   }
355 }
356 
357 
type() const358 QgsWkbTypes::GeometryType QgsGeometry::type() const
359 {
360   if ( !d->geometry )
361   {
362     return QgsWkbTypes::UnknownGeometry;
363   }
364   return static_cast< QgsWkbTypes::GeometryType >( QgsWkbTypes::geometryType( d->geometry->wkbType() ) );
365 }
366 
isEmpty() const367 bool QgsGeometry::isEmpty() const
368 {
369   if ( !d->geometry )
370   {
371     return true;
372   }
373 
374   return d->geometry->isEmpty();
375 }
376 
isMultipart() const377 bool QgsGeometry::isMultipart() const
378 {
379   if ( !d->geometry )
380   {
381     return false;
382   }
383   return QgsWkbTypes::isMultiType( d->geometry->wkbType() );
384 }
385 
closestVertex(const QgsPointXY & point,int & atVertex,int & beforeVertex,int & afterVertex,double & sqrDist) const386 QgsPointXY QgsGeometry::closestVertex( const QgsPointXY &point, int &atVertex, int &beforeVertex, int &afterVertex, double &sqrDist ) const
387 {
388   if ( !d->geometry )
389   {
390     sqrDist = -1;
391     return QgsPointXY();
392   }
393 
394   QgsPoint pt( point );
395   QgsVertexId id;
396 
397   QgsPoint vp = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, id );
398   if ( !id.isValid() )
399   {
400     sqrDist = -1;
401     return QgsPointXY();
402   }
403   sqrDist = QgsGeometryUtils::sqrDistance2D( pt, vp );
404 
405   QgsVertexId prevVertex;
406   QgsVertexId nextVertex;
407   d->geometry->adjacentVertices( id, prevVertex, nextVertex );
408   atVertex = vertexNrFromVertexId( id );
409   beforeVertex = vertexNrFromVertexId( prevVertex );
410   afterVertex = vertexNrFromVertexId( nextVertex );
411   return QgsPointXY( vp.x(), vp.y() );
412 }
413 
distanceToVertex(int vertex) const414 double QgsGeometry::distanceToVertex( int vertex ) const
415 {
416   if ( !d->geometry )
417   {
418     return -1;
419   }
420 
421   QgsVertexId id;
422   if ( !vertexIdFromVertexNr( vertex, id ) )
423   {
424     return -1;
425   }
426 
427   return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id );
428 }
429 
angleAtVertex(int vertex) const430 double QgsGeometry::angleAtVertex( int vertex ) const
431 {
432   if ( !d->geometry )
433   {
434     return 0;
435   }
436 
437   QgsVertexId v2;
438   if ( !vertexIdFromVertexNr( vertex, v2 ) )
439   {
440     return 0;
441   }
442 
443   return d->geometry->vertexAngle( v2 );
444 }
445 
adjacentVertices(int atVertex,int & beforeVertex,int & afterVertex) const446 void QgsGeometry::adjacentVertices( int atVertex, int &beforeVertex, int &afterVertex ) const
447 {
448   if ( !d->geometry )
449   {
450     return;
451   }
452 
453   QgsVertexId id;
454   if ( !vertexIdFromVertexNr( atVertex, id ) )
455   {
456     beforeVertex = -1;
457     afterVertex = -1;
458     return;
459   }
460 
461   QgsVertexId beforeVertexId, afterVertexId;
462   d->geometry->adjacentVertices( id, beforeVertexId, afterVertexId );
463   beforeVertex = vertexNrFromVertexId( beforeVertexId );
464   afterVertex = vertexNrFromVertexId( afterVertexId );
465 }
466 
moveVertex(double x,double y,int atVertex)467 bool QgsGeometry::moveVertex( double x, double y, int atVertex )
468 {
469   if ( !d->geometry )
470   {
471     return false;
472   }
473 
474   QgsVertexId id;
475   if ( !vertexIdFromVertexNr( atVertex, id ) )
476   {
477     return false;
478   }
479 
480   detach();
481 
482   return d->geometry->moveVertex( id, QgsPoint( x, y ) );
483 }
484 
moveVertex(const QgsPoint & p,int atVertex)485 bool QgsGeometry::moveVertex( const QgsPoint &p, int atVertex )
486 {
487   if ( !d->geometry )
488   {
489     return false;
490   }
491 
492   QgsVertexId id;
493   if ( !vertexIdFromVertexNr( atVertex, id ) )
494   {
495     return false;
496   }
497 
498   detach();
499 
500   return d->geometry->moveVertex( id, p );
501 }
502 
deleteVertex(int atVertex)503 bool QgsGeometry::deleteVertex( int atVertex )
504 {
505   if ( !d->geometry )
506   {
507     return false;
508   }
509 
510   //maintain compatibility with < 2.10 API
511   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::MultiPoint )
512   {
513     detach();
514     //delete geometry instead of point
515     return static_cast< QgsGeometryCollection * >( d->geometry.get() )->removeGeometry( atVertex );
516   }
517 
518   //if it is a point, set the geometry to nullptr
519   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
520   {
521     reset( nullptr );
522     return true;
523   }
524 
525   QgsVertexId id;
526   if ( !vertexIdFromVertexNr( atVertex, id ) )
527   {
528     return false;
529   }
530 
531   detach();
532 
533   return d->geometry->deleteVertex( id );
534 }
535 
insertVertex(double x,double y,int beforeVertex)536 bool QgsGeometry::insertVertex( double x, double y, int beforeVertex )
537 {
538   if ( !d->geometry )
539   {
540     return false;
541   }
542 
543   //maintain compatibility with < 2.10 API
544   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::MultiPoint )
545   {
546     detach();
547     //insert geometry instead of point
548     return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( x, y ), beforeVertex );
549   }
550 
551   QgsVertexId id;
552   if ( !vertexIdFromVertexNr( beforeVertex, id ) )
553   {
554     return false;
555   }
556 
557   detach();
558 
559   return d->geometry->insertVertex( id, QgsPoint( x, y ) );
560 }
561 
insertVertex(const QgsPoint & point,int beforeVertex)562 bool QgsGeometry::insertVertex( const QgsPoint &point, int beforeVertex )
563 {
564   if ( !d->geometry )
565   {
566     return false;
567   }
568 
569   //maintain compatibility with < 2.10 API
570   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::MultiPoint )
571   {
572     detach();
573     //insert geometry instead of point
574     return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( point ), beforeVertex );
575   }
576 
577   QgsVertexId id;
578   if ( !vertexIdFromVertexNr( beforeVertex, id ) )
579   {
580     return false;
581   }
582 
583   detach();
584 
585   return d->geometry->insertVertex( id, point );
586 }
587 
vertexAt(int atVertex) const588 QgsPoint QgsGeometry::vertexAt( int atVertex ) const
589 {
590   if ( !d->geometry )
591   {
592     return QgsPoint();
593   }
594 
595   QgsVertexId vId;
596   ( void )vertexIdFromVertexNr( atVertex, vId );
597   if ( vId.vertex < 0 )
598   {
599     return QgsPoint();
600   }
601   return d->geometry->vertexAt( vId );
602 }
603 
sqrDistToVertexAt(QgsPointXY & point,int atVertex) const604 double QgsGeometry::sqrDistToVertexAt( QgsPointXY &point, int atVertex ) const
605 {
606   QgsPointXY vertexPoint = vertexAt( atVertex );
607   return QgsGeometryUtils::sqrDistance2D( QgsPoint( vertexPoint ), QgsPoint( point ) );
608 }
609 
nearestPoint(const QgsGeometry & other) const610 QgsGeometry QgsGeometry::nearestPoint( const QgsGeometry &other ) const
611 {
612   // avoid calling geos for trivial point calculations
613   if ( d->geometry && QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
614   {
615     return QgsGeometry( qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->clone() );
616   }
617 
618   QgsGeos geos( d->geometry.get() );
619   mLastError.clear();
620   QgsGeometry result = geos.closestPoint( other );
621   result.mLastError = mLastError;
622   return result;
623 }
624 
shortestLine(const QgsGeometry & other) const625 QgsGeometry QgsGeometry::shortestLine( const QgsGeometry &other ) const
626 {
627   // avoid calling geos for trivial point-to-point line calculations
628   if ( d->geometry && QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point && QgsWkbTypes::flatType( other.wkbType() ) == QgsWkbTypes::Point )
629   {
630     return QgsGeometry( qgis::make_unique< QgsLineString >( *qgsgeometry_cast< const QgsPoint * >( d->geometry.get() ), *qgsgeometry_cast< const QgsPoint * >( other.constGet() ) ) );
631   }
632 
633   QgsGeos geos( d->geometry.get() );
634   mLastError.clear();
635   QgsGeometry result = geos.shortestLine( other, &mLastError );
636   result.mLastError = mLastError;
637   return result;
638 }
639 
closestVertexWithContext(const QgsPointXY & point,int & atVertex) const640 double QgsGeometry::closestVertexWithContext( const QgsPointXY &point, int &atVertex ) const
641 {
642   if ( !d->geometry )
643   {
644     return -1;
645   }
646 
647   QgsVertexId vId;
648   QgsPoint pt( point );
649   QgsPoint closestPoint = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, vId );
650   if ( !vId.isValid() )
651     return -1;
652   atVertex = vertexNrFromVertexId( vId );
653   return QgsGeometryUtils::sqrDistance2D( closestPoint, pt );
654 }
655 
closestSegmentWithContext(const QgsPointXY & point,QgsPointXY & minDistPoint,int & afterVertex,int * leftOf,double epsilon) const656 double QgsGeometry::closestSegmentWithContext( const QgsPointXY &point,
657     QgsPointXY &minDistPoint,
658     int &afterVertex,
659     int *leftOf,
660     double epsilon ) const
661 {
662   if ( !d->geometry )
663   {
664     return -1;
665   }
666 
667   QgsPoint segmentPt;
668   QgsVertexId vertexAfter;
669 
670   double sqrDist = d->geometry->closestSegment( QgsPoint( point ), segmentPt,  vertexAfter, leftOf, epsilon );
671   if ( sqrDist < 0 )
672     return -1;
673 
674   minDistPoint.setX( segmentPt.x() );
675   minDistPoint.setY( segmentPt.y() );
676   afterVertex = vertexNrFromVertexId( vertexAfter );
677   return sqrDist;
678 }
679 
addRing(const QVector<QgsPointXY> & ring)680 QgsGeometry::OperationResult QgsGeometry::addRing( const QVector<QgsPointXY> &ring )
681 {
682   std::unique_ptr< QgsLineString > ringLine = qgis::make_unique< QgsLineString >( ring );
683   return addRing( ringLine.release() );
684 }
685 
addRing(QgsCurve * ring)686 QgsGeometry::OperationResult QgsGeometry::addRing( QgsCurve *ring )
687 {
688   std::unique_ptr< QgsCurve > r( ring );
689   if ( !d->geometry )
690   {
691     return InvalidInputGeometryType;
692   }
693 
694   detach();
695 
696   return QgsGeometryEditUtils::addRing( d->geometry.get(), std::move( r ) );
697 }
698 
addPart(const QVector<QgsPointXY> & points,QgsWkbTypes::GeometryType geomType)699 QgsGeometry::OperationResult QgsGeometry::addPart( const QVector<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType )
700 {
701   QgsPointSequence l;
702   convertPointList( points, l );
703   return addPart( l, geomType );
704 }
705 
addPart(const QgsPointSequence & points,QgsWkbTypes::GeometryType geomType)706 QgsGeometry::OperationResult QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType )
707 {
708   std::unique_ptr< QgsAbstractGeometry > partGeom;
709   if ( points.size() == 1 )
710   {
711     partGeom = qgis::make_unique< QgsPoint >( points[0] );
712   }
713   else if ( points.size() > 1 )
714   {
715     std::unique_ptr< QgsLineString > ringLine = qgis::make_unique< QgsLineString >();
716     ringLine->setPoints( points );
717     partGeom = std::move( ringLine );
718   }
719   return addPart( partGeom.release(), geomType );
720 }
721 
addPart(QgsAbstractGeometry * part,QgsWkbTypes::GeometryType geomType)722 QgsGeometry::OperationResult QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType )
723 {
724   std::unique_ptr< QgsAbstractGeometry > p( part );
725   if ( !d->geometry )
726   {
727     switch ( geomType )
728     {
729       case QgsWkbTypes::PointGeometry:
730         reset( qgis::make_unique< QgsMultiPoint >() );
731         break;
732       case QgsWkbTypes::LineGeometry:
733         reset( qgis::make_unique< QgsMultiLineString >() );
734         break;
735       case QgsWkbTypes::PolygonGeometry:
736         reset( qgis::make_unique< QgsMultiPolygon >() );
737         break;
738       default:
739         reset( nullptr );
740         return QgsGeometry::OperationResult::AddPartNotMultiGeometry;
741     }
742   }
743   else
744   {
745     detach();
746   }
747 
748   convertToMultiType();
749   return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
750 }
751 
addPart(const QgsGeometry & newPart)752 QgsGeometry::OperationResult QgsGeometry::addPart( const QgsGeometry &newPart )
753 {
754   if ( !d->geometry )
755   {
756     return QgsGeometry::InvalidBaseGeometry;
757   }
758   if ( newPart.isNull() || !newPart.d->geometry )
759   {
760     return QgsGeometry::AddPartNotMultiGeometry;
761   }
762 
763   return addPart( newPart.d->geometry->clone() );
764 }
765 
removeInteriorRings(double minimumRingArea) const766 QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
767 {
768   if ( !d->geometry || type() != QgsWkbTypes::PolygonGeometry )
769   {
770     return QgsGeometry();
771   }
772 
773   if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
774   {
775     const QVector<QgsGeometry> parts = asGeometryCollection();
776     QVector<QgsGeometry> results;
777     results.reserve( parts.count() );
778     for ( const QgsGeometry &part : parts )
779     {
780       QgsGeometry result = part.removeInteriorRings( minimumRingArea );
781       if ( !result.isNull() )
782         results << result;
783     }
784     if ( results.isEmpty() )
785       return QgsGeometry();
786 
787     QgsGeometry first = results.takeAt( 0 );
788     for ( const QgsGeometry &result : qgis::as_const( results ) )
789     {
790       first.addPart( result );
791     }
792     return first;
793   }
794   else
795   {
796     std::unique_ptr< QgsCurvePolygon > newPoly( static_cast< QgsCurvePolygon * >( d->geometry->clone() ) );
797     newPoly->removeInteriorRings( minimumRingArea );
798     return QgsGeometry( std::move( newPoly ) );
799   }
800 }
801 
translate(double dx,double dy,double dz,double dm)802 QgsGeometry::OperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
803 {
804   if ( !d->geometry )
805   {
806     return QgsGeometry::InvalidBaseGeometry;
807   }
808 
809   detach();
810 
811   d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
812   return QgsGeometry::Success;
813 }
814 
rotate(double rotation,const QgsPointXY & center)815 QgsGeometry::OperationResult QgsGeometry::rotate( double rotation, const QgsPointXY &center )
816 {
817   if ( !d->geometry )
818   {
819     return QgsGeometry::InvalidBaseGeometry;
820   }
821 
822   detach();
823 
824   QTransform t = QTransform::fromTranslate( center.x(), center.y() );
825   t.rotate( -rotation );
826   t.translate( -center.x(), -center.y() );
827   d->geometry->transform( t );
828   return QgsGeometry::Success;
829 }
830 
splitGeometry(const QVector<QgsPointXY> & splitLine,QVector<QgsGeometry> & newGeometries,bool topological,QVector<QgsPointXY> & topologyTestPoints,bool splitFeature)831 QgsGeometry::OperationResult QgsGeometry::splitGeometry( const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature )
832 {
833   QgsPointSequence split, topology;
834   convertPointList( splitLine, split );
835   convertPointList( topologyTestPoints, topology );
836   QgsGeometry::OperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
837   convertPointList( topology, topologyTestPoints );
838   return result;
839 }
splitGeometry(const QgsPointSequence & splitLine,QVector<QgsGeometry> & newGeometries,bool topological,QgsPointSequence & topologyTestPoints,bool splitFeature,bool skipIntersectionTest)840 QgsGeometry::OperationResult QgsGeometry::splitGeometry( const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest )
841 {
842   if ( !d->geometry )
843   {
844     return QgsGeometry::OperationResult::InvalidBaseGeometry;
845   }
846 
847   QVector<QgsGeometry > newGeoms;
848   QgsLineString splitLineString( splitLine );
849 
850   /**
851    * QGIS uses GEOS algorithm to split geometries.
852    * Using 3D points in GEOS will returns an interpolation value which is the
853    * mean between geometries.
854    * On the contrary, in our logic, the interpolation is a linear interpolation
855    * on the split point. By dropping Z/M value, GEOS will returns the expected
856    * result. See https://github.com/qgis/QGIS/issues/33489
857    */
858   splitLineString.dropZValue();
859   splitLineString.dropMValue();
860 
861   QgsGeos geos( d->geometry.get() );
862   mLastError.clear();
863   QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
864 
865   if ( result == QgsGeometryEngine::Success )
866   {
867     if ( splitFeature )
868       *this = newGeoms.takeAt( 0 );
869     newGeometries = newGeoms;
870   }
871 
872   switch ( result )
873   {
874     case QgsGeometryEngine::Success:
875       return QgsGeometry::OperationResult::Success;
876     case QgsGeometryEngine::MethodNotImplemented:
877     case QgsGeometryEngine::EngineError:
878     case QgsGeometryEngine::NodedGeometryError:
879       return QgsGeometry::OperationResult::GeometryEngineError;
880     case QgsGeometryEngine::InvalidBaseGeometry:
881       return QgsGeometry::OperationResult::InvalidBaseGeometry;
882     case QgsGeometryEngine::InvalidInput:
883       return QgsGeometry::OperationResult::InvalidInputGeometryType;
884     case QgsGeometryEngine::SplitCannotSplitPoint:
885       return QgsGeometry::OperationResult::SplitCannotSplitPoint;
886     case QgsGeometryEngine::NothingHappened:
887       return QgsGeometry::OperationResult::NothingHappened;
888       //default: do not implement default to handle properly all cases
889   }
890 
891   // this should never be reached
892   Q_ASSERT( false );
893   return QgsGeometry::NothingHappened;
894 }
895 
splitGeometry(const QgsCurve * curve,QVector<QgsGeometry> & newGeometries,bool preserveCircular,bool topological,QgsPointSequence & topologyTestPoints,bool splitFeature)896 QgsGeometry::OperationResult QgsGeometry::splitGeometry( const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature )
897 {
898   std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
899   QgsPointSequence points;
900   segmentizedLine->points( points );
901   QgsGeometry::OperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
902 
903   if ( result == QgsGeometry::Success )
904   {
905     if ( preserveCircular )
906     {
907       for ( int i = 0; i < newGeometries.count(); ++i )
908         newGeometries[i] = newGeometries[i].convertToCurves();
909       *this = convertToCurves();
910     }
911   }
912 
913   return result;
914 }
915 
reshapeGeometry(const QgsLineString & reshapeLineString)916 QgsGeometry::OperationResult QgsGeometry::reshapeGeometry( const QgsLineString &reshapeLineString )
917 {
918   if ( !d->geometry )
919   {
920     return InvalidBaseGeometry;
921   }
922 
923   QgsGeos geos( d->geometry.get() );
924   QgsGeometryEngine::EngineOperationResult errorCode = QgsGeometryEngine::Success;
925   mLastError.clear();
926   std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
927   if ( errorCode == QgsGeometryEngine::Success && geom )
928   {
929     reset( std::move( geom ) );
930     return Success;
931   }
932 
933   switch ( errorCode )
934   {
935     case QgsGeometryEngine::Success:
936       return Success;
937     case QgsGeometryEngine::MethodNotImplemented:
938     case QgsGeometryEngine::EngineError:
939     case QgsGeometryEngine::NodedGeometryError:
940       return GeometryEngineError;
941     case QgsGeometryEngine::InvalidBaseGeometry:
942       return InvalidBaseGeometry;
943     case QgsGeometryEngine::InvalidInput:
944       return InvalidInputGeometryType;
945     case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
946       return GeometryEngineError;
947     case QgsGeometryEngine::NothingHappened:
948       return NothingHappened;
949   }
950 
951   // should not be reached
952   return GeometryEngineError;
953 }
954 
makeDifferenceInPlace(const QgsGeometry & other)955 int QgsGeometry::makeDifferenceInPlace( const QgsGeometry &other )
956 {
957   if ( !d->geometry || !other.d->geometry )
958   {
959     return 0;
960   }
961 
962   QgsGeos geos( d->geometry.get() );
963 
964   mLastError.clear();
965   std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
966   if ( !diffGeom )
967   {
968     return 1;
969   }
970 
971   reset( std::move( diffGeom ) );
972   return 0;
973 }
974 
makeDifference(const QgsGeometry & other) const975 QgsGeometry QgsGeometry::makeDifference( const QgsGeometry &other ) const
976 {
977   if ( !d->geometry || other.isNull() )
978   {
979     return QgsGeometry();
980   }
981 
982   QgsGeos geos( d->geometry.get() );
983 
984   mLastError.clear();
985   std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
986   if ( !diffGeom )
987   {
988     QgsGeometry result;
989     result.mLastError = mLastError;
990     return result;
991   }
992 
993   return QgsGeometry( diffGeom.release() );
994 }
995 
boundingBox() const996 QgsRectangle QgsGeometry::boundingBox() const
997 {
998   if ( d->geometry )
999   {
1000     return d->geometry->boundingBox();
1001   }
1002   return QgsRectangle();
1003 }
1004 
orientedMinimumBoundingBox(double & area,double & angle,double & width,double & height) const1005 QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1006 {
1007   mLastError.clear();
1008   QgsInternalGeometryEngine engine( *this );
1009   const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1010   if ( res.isNull() )
1011     mLastError = engine.lastError();
1012   return res;
1013 }
1014 
orientedMinimumBoundingBox() const1015 QgsGeometry QgsGeometry::orientedMinimumBoundingBox() const
1016 {
1017   double area, angle, width, height;
1018   return orientedMinimumBoundingBox( area, angle, width, height );
1019 }
1020 
__recMinimalEnclosingCircle(QgsMultiPointXY points,QgsMultiPointXY boundary)1021 static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1022 {
1023   auto l_boundary = boundary.length();
1024   QgsCircle circ_mec;
1025   if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1026   {
1027     switch ( l_boundary )
1028     {
1029       case 0:
1030         circ_mec = QgsCircle();
1031         break;
1032       case 1:
1033         circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1034         boundary.pop_back();
1035         break;
1036       case 2:
1037       {
1038         QgsPointXY p1 = boundary.last();
1039         boundary.pop_back();
1040         QgsPointXY p2 = boundary.last();
1041         boundary.pop_back();
1042         circ_mec = QgsCircle().from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1043       }
1044       break;
1045       default:
1046         QgsPoint p1( boundary.at( 0 ) );
1047         QgsPoint p2( boundary.at( 1 ) );
1048         QgsPoint p3( boundary.at( 2 ) );
1049         circ_mec = QgsCircle().minimalCircleFrom3Points( p1, p2, p3 );
1050         break;
1051     }
1052     return circ_mec;
1053   }
1054   else
1055   {
1056     QgsPointXY pxy = points.last();
1057     points.pop_back();
1058     circ_mec = __recMinimalEnclosingCircle( points, boundary );
1059     QgsPoint p( pxy );
1060     if ( !circ_mec.contains( p ) )
1061     {
1062       boundary.append( pxy );
1063       circ_mec = __recMinimalEnclosingCircle( points, boundary );
1064     }
1065   }
1066   return circ_mec;
1067 }
1068 
minimalEnclosingCircle(QgsPointXY & center,double & radius,unsigned int segments) const1069 QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1070 {
1071   center = QgsPointXY();
1072   radius = 0;
1073 
1074   if ( isEmpty() )
1075   {
1076     return QgsGeometry();
1077   }
1078 
1079   /* optimization */
1080   QgsGeometry hull = convexHull();
1081   if ( hull.isNull() )
1082     return QgsGeometry();
1083 
1084   QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1085   QgsMultiPointXY R;
1086 
1087   QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1088   center = QgsPointXY( circ.center() );
1089   radius = circ.radius();
1090   QgsGeometry geom;
1091   geom.set( circ.toPolygon( segments ) );
1092   return geom;
1093 
1094 }
1095 
minimalEnclosingCircle(unsigned int segments) const1096 QgsGeometry QgsGeometry::minimalEnclosingCircle( unsigned int segments ) const
1097 {
1098   QgsPointXY center;
1099   double radius;
1100   return minimalEnclosingCircle( center, radius, segments );
1101 
1102 }
1103 
orthogonalize(double tolerance,int maxIterations,double angleThreshold) const1104 QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1105 {
1106   QgsInternalGeometryEngine engine( *this );
1107 
1108   return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1109 }
1110 
snappedToGrid(double hSpacing,double vSpacing,double dSpacing,double mSpacing) const1111 QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1112 {
1113   if ( !d->geometry )
1114   {
1115     return QgsGeometry();
1116   }
1117   return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1118 }
1119 
removeDuplicateNodes(double epsilon,bool useZValues)1120 bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1121 {
1122   if ( !d->geometry )
1123     return false;
1124 
1125   detach();
1126   return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1127 }
1128 
intersects(const QgsRectangle & r) const1129 bool QgsGeometry::intersects( const QgsRectangle &r ) const
1130 {
1131   // fast case, check bounding boxes
1132   if ( !boundingBoxIntersects( r ) )
1133     return false;
1134 
1135   // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1136   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
1137   {
1138     return true;
1139   }
1140 
1141   QgsGeometry g = fromRect( r );
1142   return intersects( g );
1143 }
1144 
intersects(const QgsGeometry & geometry) const1145 bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1146 {
1147   if ( !d->geometry || geometry.isNull() )
1148   {
1149     return false;
1150   }
1151 
1152   QgsGeos geos( d->geometry.get() );
1153   mLastError.clear();
1154   return geos.intersects( geometry.d->geometry.get(), &mLastError );
1155 }
1156 
boundingBoxIntersects(const QgsRectangle & rectangle) const1157 bool QgsGeometry::boundingBoxIntersects( const QgsRectangle &rectangle ) const
1158 {
1159   if ( !d->geometry )
1160   {
1161     return false;
1162   }
1163 
1164   // optimise trivial case for point intersections
1165   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
1166   {
1167     const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( d->geometry.get() );
1168     return rectangle.contains( QgsPointXY( point->x(), point->y() ) );
1169   }
1170 
1171   return d->geometry->boundingBox().intersects( rectangle );
1172 }
1173 
boundingBoxIntersects(const QgsGeometry & geometry) const1174 bool QgsGeometry::boundingBoxIntersects( const QgsGeometry &geometry ) const
1175 {
1176   if ( !d->geometry || geometry.isNull() )
1177   {
1178     return false;
1179   }
1180 
1181   return d->geometry->boundingBox().intersects( geometry.constGet()->boundingBox() );
1182 }
1183 
contains(const QgsPointXY * p) const1184 bool QgsGeometry::contains( const QgsPointXY *p ) const
1185 {
1186   if ( !d->geometry || !p )
1187   {
1188     return false;
1189   }
1190 
1191   QgsPoint pt( p->x(), p->y() );
1192   QgsGeos geos( d->geometry.get() );
1193   mLastError.clear();
1194   return geos.contains( &pt, &mLastError );
1195 }
1196 
contains(const QgsGeometry & geometry) const1197 bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1198 {
1199   if ( !d->geometry || geometry.isNull() )
1200   {
1201     return false;
1202   }
1203 
1204   QgsGeos geos( d->geometry.get() );
1205   mLastError.clear();
1206   return geos.contains( geometry.d->geometry.get(), &mLastError );
1207 }
1208 
disjoint(const QgsGeometry & geometry) const1209 bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1210 {
1211   if ( !d->geometry || geometry.isNull() )
1212   {
1213     return false;
1214   }
1215 
1216   QgsGeos geos( d->geometry.get() );
1217   mLastError.clear();
1218   return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1219 }
1220 
equals(const QgsGeometry & geometry) const1221 bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1222 {
1223   if ( !d->geometry || geometry.isNull() )
1224   {
1225     return false;
1226   }
1227 
1228   // fast check - are they shared copies of the same underlying geometry?
1229   if ( d == geometry.d )
1230     return true;
1231 
1232   // fast check - distinct geometry types?
1233   if ( type() != geometry.type() )
1234     return false;
1235 
1236   // slower check - actually test the geometries
1237   return *d->geometry == *geometry.d->geometry;
1238 }
1239 
touches(const QgsGeometry & geometry) const1240 bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1241 {
1242   if ( !d->geometry || geometry.isNull() )
1243   {
1244     return false;
1245   }
1246 
1247   QgsGeos geos( d->geometry.get() );
1248   mLastError.clear();
1249   return geos.touches( geometry.d->geometry.get(), &mLastError );
1250 }
1251 
overlaps(const QgsGeometry & geometry) const1252 bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1253 {
1254   if ( !d->geometry || geometry.isNull() )
1255   {
1256     return false;
1257   }
1258 
1259   QgsGeos geos( d->geometry.get() );
1260   mLastError.clear();
1261   return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1262 }
1263 
within(const QgsGeometry & geometry) const1264 bool QgsGeometry::within( const QgsGeometry &geometry ) const
1265 {
1266   if ( !d->geometry || geometry.isNull() )
1267   {
1268     return false;
1269   }
1270 
1271   QgsGeos geos( d->geometry.get() );
1272   mLastError.clear();
1273   return geos.within( geometry.d->geometry.get(), &mLastError );
1274 }
1275 
crosses(const QgsGeometry & geometry) const1276 bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1277 {
1278   if ( !d->geometry || geometry.isNull() )
1279   {
1280     return false;
1281   }
1282 
1283   QgsGeos geos( d->geometry.get() );
1284   mLastError.clear();
1285   return geos.crosses( geometry.d->geometry.get(), &mLastError );
1286 }
1287 
asWkt(int precision) const1288 QString QgsGeometry::asWkt( int precision ) const
1289 {
1290   if ( !d->geometry )
1291   {
1292     return QString();
1293   }
1294   return d->geometry->asWkt( precision );
1295 }
1296 
asJson(int precision) const1297 QString QgsGeometry::asJson( int precision ) const
1298 {
1299   return QString::fromStdString( asJsonObject( precision ).dump() );
1300 }
1301 
asJsonObject(int precision) const1302 json QgsGeometry::asJsonObject( int precision ) const
1303 {
1304   if ( !d->geometry )
1305   {
1306     return nullptr;
1307   }
1308   return d->geometry->asJsonObject( precision );
1309 
1310 }
1311 
coerceToType(const QgsWkbTypes::Type type) const1312 QVector<QgsGeometry> QgsGeometry::coerceToType( const QgsWkbTypes::Type type ) const
1313 {
1314   QVector< QgsGeometry > res;
1315   if ( isNull() )
1316     return res;
1317 
1318   if ( wkbType() == type || type == QgsWkbTypes::Unknown )
1319   {
1320     res << *this;
1321     return res;
1322   }
1323 
1324   if ( type == QgsWkbTypes::NoGeometry )
1325   {
1326     return res;
1327   }
1328 
1329   QgsGeometry newGeom = *this;
1330 
1331   // Curved -> straight
1332   if ( !QgsWkbTypes::isCurvedType( type ) && QgsWkbTypes::isCurvedType( newGeom.wkbType() ) )
1333   {
1334     newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1335   }
1336 
1337   // polygon -> line
1338   if ( QgsWkbTypes::geometryType( type ) == QgsWkbTypes::LineGeometry &&
1339        newGeom.type() == QgsWkbTypes::PolygonGeometry )
1340   {
1341     // boundary gives us a (multi)line string of exterior + interior rings
1342     newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1343   }
1344   // line -> polygon
1345   if ( QgsWkbTypes::geometryType( type ) == QgsWkbTypes::PolygonGeometry &&
1346        newGeom.type() == QgsWkbTypes::LineGeometry )
1347   {
1348     std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1349     const QgsGeometry source = newGeom;
1350     for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1351     {
1352       std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1353       if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1354       {
1355         if ( QgsWkbTypes::isCurvedType( type ) )
1356         {
1357           std::unique_ptr< QgsCurvePolygon > cp = qgis::make_unique< QgsCurvePolygon >();
1358           cp->setExteriorRing( curve );
1359           exterior.release();
1360           gc->addGeometry( cp.release() );
1361         }
1362         else
1363         {
1364           std::unique_ptr< QgsPolygon > p = qgis::make_unique< QgsPolygon  >();
1365           p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1366           exterior.release();
1367           gc->addGeometry( p.release() );
1368         }
1369       }
1370     }
1371     newGeom = QgsGeometry( std::move( gc ) );
1372   }
1373 
1374   // line/polygon -> points
1375   if ( QgsWkbTypes::geometryType( type ) == QgsWkbTypes::PointGeometry &&
1376        ( newGeom.type() == QgsWkbTypes::LineGeometry ||
1377          newGeom.type() == QgsWkbTypes::PolygonGeometry ) )
1378   {
1379     // lines/polygons to a point layer, extract all vertices
1380     std::unique_ptr< QgsMultiPoint > mp = qgis::make_unique< QgsMultiPoint >();
1381     const QgsGeometry source = newGeom;
1382     QSet< QgsPoint > added;
1383     for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1384     {
1385       if ( added.contains( *vertex ) )
1386         continue; // avoid duplicate points, e.g. start/end of rings
1387       mp->addGeometry( ( *vertex ).clone() );
1388       added.insert( *vertex );
1389     }
1390     newGeom = QgsGeometry( std::move( mp ) );
1391   }
1392 
1393   // Single -> multi
1394   if ( QgsWkbTypes::isMultiType( type ) && ! newGeom.isMultipart( ) )
1395   {
1396     newGeom.convertToMultiType();
1397   }
1398   // Drop Z/M
1399   if ( newGeom.constGet()->is3D() && ! QgsWkbTypes::hasZ( type ) )
1400   {
1401     newGeom.get()->dropZValue();
1402   }
1403   if ( newGeom.constGet()->isMeasure() && ! QgsWkbTypes::hasM( type ) )
1404   {
1405     newGeom.get()->dropMValue();
1406   }
1407   // Add Z/M back, set to 0
1408   if ( ! newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
1409   {
1410     newGeom.get()->addZValue( 0.0 );
1411   }
1412   if ( ! newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
1413   {
1414     newGeom.get()->addMValue( 0.0 );
1415   }
1416 
1417   // Multi -> single
1418   if ( ! QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart( ) )
1419   {
1420     const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1421     QgsAttributeMap attrMap;
1422     res.reserve( parts->partCount() );
1423     for ( int i = 0; i < parts->partCount( ); i++ )
1424     {
1425       res << QgsGeometry( parts->geometryN( i )->clone() );
1426     }
1427   }
1428   else
1429   {
1430     res << newGeom;
1431   }
1432   return res;
1433 }
1434 
convertToType(QgsWkbTypes::GeometryType destType,bool destMultipart) const1435 QgsGeometry QgsGeometry::convertToType( QgsWkbTypes::GeometryType destType, bool destMultipart ) const
1436 {
1437   switch ( destType )
1438   {
1439     case QgsWkbTypes::PointGeometry:
1440       return convertToPoint( destMultipart );
1441 
1442     case QgsWkbTypes::LineGeometry:
1443       return convertToLine( destMultipart );
1444 
1445     case QgsWkbTypes::PolygonGeometry:
1446       return convertToPolygon( destMultipart );
1447 
1448     default:
1449       return QgsGeometry();
1450   }
1451 }
1452 
convertToMultiType()1453 bool QgsGeometry::convertToMultiType()
1454 {
1455   if ( !d->geometry )
1456   {
1457     return false;
1458   }
1459 
1460   if ( isMultipart() ) //already multitype, no need to convert
1461   {
1462     return true;
1463   }
1464 
1465   std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
1466   QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1467   if ( !multiGeom )
1468   {
1469     return false;
1470   }
1471 
1472   //try to avoid cloning existing geometry whenever we can
1473 
1474   //want to see a magic trick?... gather round kiddies...
1475   detach(); // maybe a clone, hopefully not if we're the only ref to the private data
1476   // now we cheat a bit and steal the private geometry and add it direct to the multigeom
1477   // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
1478   multiGeom->addGeometry( d->geometry.release() );
1479   // and replace it with the multi geometry.
1480   // TADA! a clone free conversion in some cases
1481   d->geometry = std::move( geom );
1482   return true;
1483 }
1484 
convertToSingleType()1485 bool QgsGeometry::convertToSingleType()
1486 {
1487   if ( !d->geometry )
1488   {
1489     return false;
1490   }
1491 
1492   if ( !isMultipart() ) //already single part, no need to convert
1493   {
1494     return true;
1495   }
1496 
1497   QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1498   if ( !multiGeom || multiGeom->partCount() < 1 )
1499     return false;
1500 
1501   std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
1502   reset( std::move( firstPart ) );
1503   return true;
1504 }
1505 
1506 
convertGeometryCollectionToSubclass(QgsWkbTypes::GeometryType geomType)1507 bool QgsGeometry::convertGeometryCollectionToSubclass( QgsWkbTypes::GeometryType geomType )
1508 {
1509   const QgsGeometryCollection *origGeom = qgsgeometry_cast<const QgsGeometryCollection *>( constGet() );
1510   if ( !origGeom )
1511     return false;
1512 
1513   std::unique_ptr<QgsGeometryCollection> resGeom;
1514   switch ( geomType )
1515   {
1516     case QgsWkbTypes::PointGeometry:
1517       resGeom = qgis::make_unique<QgsMultiPoint>();
1518       break;
1519     case QgsWkbTypes::LineGeometry:
1520       resGeom = qgis::make_unique<QgsMultiLineString>();
1521       break;
1522     case QgsWkbTypes::PolygonGeometry:
1523       resGeom = qgis::make_unique<QgsMultiPolygon>();
1524       break;
1525     default:
1526       break;
1527   }
1528   if ( !resGeom )
1529     return false;
1530 
1531   resGeom->reserve( origGeom->numGeometries() );
1532   for ( int i = 0; i < origGeom->numGeometries(); ++i )
1533   {
1534     const QgsAbstractGeometry *g = origGeom->geometryN( i );
1535     if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
1536       resGeom->addGeometry( g->clone() );
1537   }
1538 
1539   set( resGeom.release() );
1540   return true;
1541 }
1542 
1543 
asPoint() const1544 QgsPointXY QgsGeometry::asPoint() const
1545 {
1546   if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != QgsWkbTypes::Point )
1547   {
1548     return QgsPointXY();
1549   }
1550   QgsPoint *pt = qgsgeometry_cast<QgsPoint *>( d->geometry.get() );
1551   if ( !pt )
1552   {
1553     return QgsPointXY();
1554   }
1555 
1556   return QgsPointXY( pt->x(), pt->y() );
1557 }
1558 
asPolyline() const1559 QgsPolylineXY QgsGeometry::asPolyline() const
1560 {
1561   QgsPolylineXY polyLine;
1562   if ( !d->geometry )
1563   {
1564     return polyLine;
1565   }
1566 
1567   bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CompoundCurve
1568                           || QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CircularString );
1569   std::unique_ptr< QgsLineString > segmentizedLine;
1570   QgsLineString *line = nullptr;
1571   if ( doSegmentation )
1572   {
1573     QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
1574     if ( !curve )
1575     {
1576       return polyLine;
1577     }
1578     segmentizedLine.reset( curve->curveToLine() );
1579     line = segmentizedLine.get();
1580   }
1581   else
1582   {
1583     line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
1584     if ( !line )
1585     {
1586       return polyLine;
1587     }
1588   }
1589 
1590   int nVertices = line->numPoints();
1591   polyLine.resize( nVertices );
1592   QgsPointXY *data = polyLine.data();
1593   const double *xData = line->xData();
1594   const double *yData = line->yData();
1595   for ( int i = 0; i < nVertices; ++i )
1596   {
1597     data->setX( *xData++ );
1598     data->setY( *yData++ );
1599     data++;
1600   }
1601 
1602   return polyLine;
1603 }
1604 
asPolygon() const1605 QgsPolygonXY QgsGeometry::asPolygon() const
1606 {
1607   if ( !d->geometry )
1608     return QgsPolygonXY();
1609 
1610   bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CurvePolygon );
1611 
1612   QgsPolygon *p = nullptr;
1613   std::unique_ptr< QgsPolygon > segmentized;
1614   if ( doSegmentation )
1615   {
1616     QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
1617     if ( !curvePoly )
1618     {
1619       return QgsPolygonXY();
1620     }
1621     segmentized.reset( curvePoly->toPolygon() );
1622     p = segmentized.get();
1623   }
1624   else
1625   {
1626     p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
1627   }
1628 
1629   if ( !p )
1630   {
1631     return QgsPolygonXY();
1632   }
1633 
1634   QgsPolygonXY polygon;
1635   convertPolygon( *p, polygon );
1636 
1637   return polygon;
1638 }
1639 
asMultiPoint() const1640 QgsMultiPointXY QgsGeometry::asMultiPoint() const
1641 {
1642   if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != QgsWkbTypes::MultiPoint )
1643   {
1644     return QgsMultiPointXY();
1645   }
1646 
1647   const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
1648   if ( !mp )
1649   {
1650     return QgsMultiPointXY();
1651   }
1652 
1653   int nPoints = mp->numGeometries();
1654   QgsMultiPointXY multiPoint( nPoints );
1655   for ( int i = 0; i < nPoints; ++i )
1656   {
1657     const QgsPoint *pt = mp->pointN( i );
1658     multiPoint[i].setX( pt->x() );
1659     multiPoint[i].setY( pt->y() );
1660   }
1661   return multiPoint;
1662 }
1663 
asMultiPolyline() const1664 QgsMultiPolylineXY QgsGeometry::asMultiPolyline() const
1665 {
1666   if ( !d->geometry )
1667   {
1668     return QgsMultiPolylineXY();
1669   }
1670 
1671   QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1672   if ( !geomCollection )
1673   {
1674     return QgsMultiPolylineXY();
1675   }
1676 
1677   int nLines = geomCollection->numGeometries();
1678   if ( nLines < 1 )
1679   {
1680     return QgsMultiPolylineXY();
1681   }
1682 
1683   QgsMultiPolylineXY mpl;
1684   mpl.reserve( nLines );
1685   for ( int i = 0; i < nLines; ++i )
1686   {
1687     const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
1688     std::unique_ptr< QgsLineString > segmentized;
1689     if ( !line )
1690     {
1691       const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
1692       if ( !curve )
1693       {
1694         continue;
1695       }
1696       segmentized.reset( curve->curveToLine() );
1697       line = segmentized.get();
1698     }
1699 
1700     QgsPolylineXY polyLine;
1701     int nVertices = line->numPoints();
1702     polyLine.resize( nVertices );
1703     QgsPointXY *data = polyLine.data();
1704     const double *xData = line->xData();
1705     const double *yData = line->yData();
1706     for ( int i = 0; i < nVertices; ++i )
1707     {
1708       data->setX( *xData++ );
1709       data->setY( *yData++ );
1710       data++;
1711     }
1712     mpl.append( polyLine );
1713   }
1714   return mpl;
1715 }
1716 
asMultiPolygon() const1717 QgsMultiPolygonXY QgsGeometry::asMultiPolygon() const
1718 {
1719   if ( !d->geometry )
1720   {
1721     return QgsMultiPolygonXY();
1722   }
1723 
1724   QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1725   if ( !geomCollection )
1726   {
1727     return QgsMultiPolygonXY();
1728   }
1729 
1730   int nPolygons = geomCollection->numGeometries();
1731   if ( nPolygons < 1 )
1732   {
1733     return QgsMultiPolygonXY();
1734   }
1735 
1736   QgsMultiPolygonXY mp;
1737   for ( int i = 0; i < nPolygons; ++i )
1738   {
1739     const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
1740     if ( !polygon )
1741     {
1742       const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
1743       if ( cPolygon )
1744       {
1745         polygon = cPolygon->toPolygon();
1746       }
1747       else
1748       {
1749         continue;
1750       }
1751     }
1752 
1753     QgsPolygonXY poly;
1754     convertPolygon( *polygon, poly );
1755     mp.append( poly );
1756   }
1757   return mp;
1758 }
1759 
area() const1760 double QgsGeometry::area() const
1761 {
1762   if ( !d->geometry )
1763   {
1764     return -1.0;
1765   }
1766   QgsGeos g( d->geometry.get() );
1767 
1768 #if 0
1769   //debug: compare geos area with calculation in QGIS
1770   double geosArea = g.area();
1771   double qgisArea = 0;
1772   QgsSurface *surface = qgsgeometry_cast<QgsSurface *>( d->geometry );
1773   if ( surface )
1774   {
1775     qgisArea = surface->area();
1776   }
1777 #endif
1778 
1779   mLastError.clear();
1780   return g.area( &mLastError );
1781 }
1782 
length() const1783 double QgsGeometry::length() const
1784 {
1785   if ( !d->geometry )
1786   {
1787     return -1.0;
1788   }
1789   QgsGeos g( d->geometry.get() );
1790   mLastError.clear();
1791   return g.length( &mLastError );
1792 }
1793 
distance(const QgsGeometry & geom) const1794 double QgsGeometry::distance( const QgsGeometry &geom ) const
1795 {
1796   if ( !d->geometry || !geom.d->geometry )
1797   {
1798     return -1.0;
1799   }
1800 
1801   // avoid calling geos for trivial point-to-point distance calculations
1802   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point && QgsWkbTypes::flatType( geom.wkbType() ) == QgsWkbTypes::Point )
1803   {
1804     return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
1805   }
1806 
1807   QgsGeos g( d->geometry.get() );
1808   mLastError.clear();
1809   return g.distance( geom.d->geometry.get(), &mLastError );
1810 }
1811 
hausdorffDistance(const QgsGeometry & geom) const1812 double QgsGeometry::hausdorffDistance( const QgsGeometry &geom ) const
1813 {
1814   if ( !d->geometry || !geom.d->geometry )
1815   {
1816     return -1.0;
1817   }
1818 
1819   QgsGeos g( d->geometry.get() );
1820   mLastError.clear();
1821   return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
1822 }
1823 
hausdorffDistanceDensify(const QgsGeometry & geom,double densifyFraction) const1824 double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
1825 {
1826   if ( !d->geometry || !geom.d->geometry )
1827   {
1828     return -1.0;
1829   }
1830 
1831   QgsGeos g( d->geometry.get() );
1832   mLastError.clear();
1833   return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
1834 }
1835 
vertices_begin() const1836 QgsAbstractGeometry::vertex_iterator QgsGeometry::vertices_begin() const
1837 {
1838   if ( !d->geometry || d->geometry.get()->isEmpty() )
1839     return QgsAbstractGeometry::vertex_iterator();
1840   return d->geometry->vertices_begin();
1841 }
1842 
vertices_end() const1843 QgsAbstractGeometry::vertex_iterator QgsGeometry::vertices_end() const
1844 {
1845   if ( !d->geometry || d->geometry.get()->isEmpty() )
1846     return QgsAbstractGeometry::vertex_iterator();
1847   return d->geometry->vertices_end();
1848 }
1849 
vertices() const1850 QgsVertexIterator QgsGeometry::vertices() const
1851 {
1852   if ( !d->geometry || d->geometry.get()->isEmpty() )
1853     return QgsVertexIterator();
1854   return QgsVertexIterator( d->geometry.get() );
1855 }
1856 
parts_begin()1857 QgsAbstractGeometry::part_iterator QgsGeometry::parts_begin()
1858 {
1859   if ( !d->geometry )
1860     return QgsAbstractGeometry::part_iterator();
1861 
1862   detach();
1863   return d->geometry->parts_begin();
1864 }
1865 
parts_end()1866 QgsAbstractGeometry::part_iterator QgsGeometry::parts_end()
1867 {
1868   if ( !d->geometry )
1869     return QgsAbstractGeometry::part_iterator();
1870   return d->geometry->parts_end();
1871 }
1872 
const_parts_begin() const1873 QgsAbstractGeometry::const_part_iterator QgsGeometry::const_parts_begin() const
1874 {
1875   if ( !d->geometry )
1876     return QgsAbstractGeometry::const_part_iterator();
1877   return d->geometry->const_parts_begin();
1878 }
1879 
const_parts_end() const1880 QgsAbstractGeometry::const_part_iterator QgsGeometry::const_parts_end() const
1881 {
1882   if ( !d->geometry )
1883     return QgsAbstractGeometry::const_part_iterator();
1884   return d->geometry->const_parts_end();
1885 }
1886 
parts()1887 QgsGeometryPartIterator QgsGeometry::parts()
1888 {
1889   if ( !d->geometry )
1890     return QgsGeometryPartIterator();
1891 
1892   detach();
1893   return QgsGeometryPartIterator( d->geometry.get() );
1894 }
1895 
constParts() const1896 QgsGeometryConstPartIterator QgsGeometry::constParts() const
1897 {
1898   if ( !d->geometry )
1899     return QgsGeometryConstPartIterator();
1900 
1901   return QgsGeometryConstPartIterator( d->geometry.get() );
1902 }
1903 
buffer(double distance,int segments) const1904 QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
1905 {
1906   if ( !d->geometry )
1907   {
1908     return QgsGeometry();
1909   }
1910 
1911   QgsGeos g( d->geometry.get() );
1912   mLastError.clear();
1913   std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError ) );
1914   if ( !geom )
1915   {
1916     QgsGeometry result;
1917     result.mLastError = mLastError;
1918     return result;
1919   }
1920   return QgsGeometry( std::move( geom ) );
1921 }
1922 
buffer(double distance,int segments,EndCapStyle endCapStyle,JoinStyle joinStyle,double miterLimit) const1923 QgsGeometry QgsGeometry::buffer( double distance, int segments, EndCapStyle endCapStyle, JoinStyle joinStyle, double miterLimit ) const
1924 {
1925   if ( !d->geometry )
1926   {
1927     return QgsGeometry();
1928   }
1929 
1930   QgsGeos g( d->geometry.get() );
1931   mLastError.clear();
1932   QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError );
1933   if ( !geom )
1934   {
1935     QgsGeometry result;
1936     result.mLastError = mLastError;
1937     return result;
1938   }
1939   return QgsGeometry( geom );
1940 }
1941 
offsetCurve(double distance,int segments,JoinStyle joinStyle,double miterLimit) const1942 QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, JoinStyle joinStyle, double miterLimit ) const
1943 {
1944   if ( !d->geometry || type() != QgsWkbTypes::LineGeometry )
1945   {
1946     return QgsGeometry();
1947   }
1948 
1949   if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
1950   {
1951     const QVector<QgsGeometry> parts = asGeometryCollection();
1952     QVector<QgsGeometry> results;
1953     results.reserve( parts.count() );
1954     for ( const QgsGeometry &part : parts )
1955     {
1956       QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
1957       if ( !result.isNull() )
1958         results << result;
1959     }
1960     if ( results.isEmpty() )
1961       return QgsGeometry();
1962 
1963     QgsGeometry first = results.takeAt( 0 );
1964     for ( const QgsGeometry &result : qgis::as_const( results ) )
1965     {
1966       first.addPart( result );
1967     }
1968     return first;
1969   }
1970   else
1971   {
1972     QgsGeos geos( d->geometry.get() );
1973     mLastError.clear();
1974 
1975     // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
1976     const QgsCurve::Orientation prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
1977 
1978     std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
1979     if ( !offsetGeom )
1980     {
1981       QgsGeometry result;
1982       result.mLastError = mLastError;
1983       return result;
1984     }
1985 
1986     if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
1987     {
1988       const QgsCurve::Orientation newOrientation = offsetCurve->orientation();
1989       if ( newOrientation != prevOrientation )
1990       {
1991         // GEOS has flipped line orientation, flip it back
1992         std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
1993         offsetGeom = std::move( flipped );
1994       }
1995     }
1996     return QgsGeometry( std::move( offsetGeom ) );
1997   }
1998 }
1999 
singleSidedBuffer(double distance,int segments,BufferSide side,JoinStyle joinStyle,double miterLimit) const2000 QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, BufferSide side, JoinStyle joinStyle, double miterLimit ) const
2001 {
2002   if ( !d->geometry || type() != QgsWkbTypes::LineGeometry )
2003   {
2004     return QgsGeometry();
2005   }
2006 
2007   if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2008   {
2009     const QVector<QgsGeometry> parts = asGeometryCollection();
2010     QVector<QgsGeometry> results;
2011     results.reserve( parts.count() );
2012     for ( const QgsGeometry &part : parts )
2013     {
2014       QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2015       if ( !result.isNull() )
2016         results << result;
2017     }
2018     if ( results.isEmpty() )
2019       return QgsGeometry();
2020 
2021     QgsGeometry first = results.takeAt( 0 );
2022     for ( const QgsGeometry &result : qgis::as_const( results ) )
2023     {
2024       first.addPart( result );
2025     }
2026     return first;
2027   }
2028   else
2029   {
2030     QgsGeos geos( d->geometry.get() );
2031     mLastError.clear();
2032     std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side,
2033         joinStyle, miterLimit, &mLastError );
2034     if ( !bufferGeom )
2035     {
2036       QgsGeometry result;
2037       result.mLastError = mLastError;
2038       return result;
2039     }
2040     return QgsGeometry( std::move( bufferGeom ) );
2041   }
2042 }
2043 
taperedBuffer(double startWidth,double endWidth,int segments) const2044 QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2045 {
2046   QgsInternalGeometryEngine engine( *this );
2047 
2048   return engine.taperedBuffer( startWidth, endWidth, segments );
2049 }
2050 
variableWidthBufferByM(int segments) const2051 QgsGeometry QgsGeometry::variableWidthBufferByM( int segments ) const
2052 {
2053   QgsInternalGeometryEngine engine( *this );
2054 
2055   return engine.variableWidthBufferByM( segments );
2056 }
2057 
extendLine(double startDistance,double endDistance) const2058 QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2059 {
2060   if ( !d->geometry || type() != QgsWkbTypes::LineGeometry )
2061   {
2062     return QgsGeometry();
2063   }
2064 
2065   if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2066   {
2067     const QVector<QgsGeometry> parts = asGeometryCollection();
2068     QVector<QgsGeometry> results;
2069     results.reserve( parts.count() );
2070     for ( const QgsGeometry &part : parts )
2071     {
2072       QgsGeometry result = part.extendLine( startDistance, endDistance );
2073       if ( !result.isNull() )
2074         results << result;
2075     }
2076     if ( results.isEmpty() )
2077       return QgsGeometry();
2078 
2079     QgsGeometry first = results.takeAt( 0 );
2080     for ( const QgsGeometry &result : qgis::as_const( results ) )
2081     {
2082       first.addPart( result );
2083     }
2084     return first;
2085   }
2086   else
2087   {
2088     QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2089     if ( !line )
2090       return QgsGeometry();
2091 
2092     std::unique_ptr< QgsLineString > newLine( line->clone() );
2093     newLine->extend( startDistance, endDistance );
2094     return QgsGeometry( std::move( newLine ) );
2095   }
2096 }
2097 
simplify(double tolerance) const2098 QgsGeometry QgsGeometry::simplify( double tolerance ) const
2099 {
2100   if ( !d->geometry )
2101   {
2102     return QgsGeometry();
2103   }
2104 
2105   QgsGeos geos( d->geometry.get() );
2106   mLastError.clear();
2107   std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError ) );
2108   if ( !simplifiedGeom )
2109   {
2110     QgsGeometry result;
2111     result.mLastError = mLastError;
2112     return result;
2113   }
2114   return QgsGeometry( std::move( simplifiedGeom ) );
2115 }
2116 
densifyByCount(int extraNodesPerSegment) const2117 QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2118 {
2119   QgsInternalGeometryEngine engine( *this );
2120 
2121   return engine.densifyByCount( extraNodesPerSegment );
2122 }
2123 
densifyByDistance(double distance) const2124 QgsGeometry QgsGeometry::densifyByDistance( double distance ) const
2125 {
2126   QgsInternalGeometryEngine engine( *this );
2127 
2128   return engine.densifyByDistance( distance );
2129 }
2130 
convertToCurves(double distanceTolerance,double angleTolerance) const2131 QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2132 {
2133   QgsInternalGeometryEngine engine( *this );
2134 
2135   return engine.convertToCurves( distanceTolerance, angleTolerance );
2136 }
2137 
centroid() const2138 QgsGeometry QgsGeometry::centroid() const
2139 {
2140   if ( !d->geometry )
2141   {
2142     return QgsGeometry();
2143   }
2144 
2145   // avoid calling geos for trivial point centroids
2146   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
2147   {
2148     QgsGeometry c = *this;
2149     c.get()->dropZValue();
2150     c.get()->dropMValue();
2151     return c;
2152   }
2153 
2154   QgsGeos geos( d->geometry.get() );
2155 
2156   mLastError.clear();
2157   QgsGeometry result( geos.centroid( &mLastError ) );
2158   result.mLastError = mLastError;
2159   return result;
2160 }
2161 
pointOnSurface() const2162 QgsGeometry QgsGeometry::pointOnSurface() const
2163 {
2164   if ( !d->geometry )
2165   {
2166     return QgsGeometry();
2167   }
2168 
2169   QgsGeos geos( d->geometry.get() );
2170 
2171   mLastError.clear();
2172   QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2173   result.mLastError = mLastError;
2174   return result;
2175 }
2176 
poleOfInaccessibility(double precision,double * distanceToBoundary) const2177 QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2178 {
2179   QgsInternalGeometryEngine engine( *this );
2180 
2181   return engine.poleOfInaccessibility( precision, distanceToBoundary );
2182 }
2183 
convexHull() const2184 QgsGeometry QgsGeometry::convexHull() const
2185 {
2186   if ( !d->geometry )
2187   {
2188     return QgsGeometry();
2189   }
2190   QgsGeos geos( d->geometry.get() );
2191   mLastError.clear();
2192   std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
2193   if ( !cHull )
2194   {
2195     QgsGeometry geom;
2196     geom.mLastError = mLastError;
2197     return geom;
2198   }
2199   return QgsGeometry( std::move( cHull ) );
2200 }
2201 
voronoiDiagram(const QgsGeometry & extent,double tolerance,bool edgesOnly) const2202 QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
2203 {
2204   if ( !d->geometry )
2205   {
2206     return QgsGeometry();
2207   }
2208 
2209   QgsGeos geos( d->geometry.get() );
2210   mLastError.clear();
2211   QgsGeometry result = geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError );
2212   result.mLastError = mLastError;
2213   return result;
2214 }
2215 
delaunayTriangulation(double tolerance,bool edgesOnly) const2216 QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
2217 {
2218   if ( !d->geometry )
2219   {
2220     return QgsGeometry();
2221   }
2222 
2223   QgsGeos geos( d->geometry.get() );
2224   mLastError.clear();
2225   QgsGeometry result = geos.delaunayTriangulation( tolerance, edgesOnly );
2226   result.mLastError = mLastError;
2227   return result;
2228 }
2229 
subdivide(int maxNodes) const2230 QgsGeometry QgsGeometry::subdivide( int maxNodes ) const
2231 {
2232   if ( !d->geometry )
2233   {
2234     return QgsGeometry();
2235   }
2236 
2237   const QgsAbstractGeometry *geom = d->geometry.get();
2238   std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
2239   if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
2240   {
2241     segmentizedCopy.reset( d->geometry->segmentize() );
2242     geom = segmentizedCopy.get();
2243   }
2244 
2245   QgsGeos geos( geom );
2246   mLastError.clear();
2247   std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError ) );
2248   if ( !result )
2249   {
2250     QgsGeometry geom;
2251     geom.mLastError = mLastError;
2252     return geom;
2253   }
2254   return QgsGeometry( std::move( result ) );
2255 }
2256 
interpolate(double distance) const2257 QgsGeometry QgsGeometry::interpolate( double distance ) const
2258 {
2259   if ( !d->geometry )
2260   {
2261     return QgsGeometry();
2262   }
2263 
2264   QgsGeometry line = *this;
2265   if ( type() == QgsWkbTypes::PointGeometry )
2266     return QgsGeometry();
2267   else if ( type() == QgsWkbTypes::PolygonGeometry )
2268   {
2269     line = QgsGeometry( d->geometry->boundary() );
2270   }
2271 
2272   const QgsCurve *curve = nullptr;
2273   if ( line.isMultipart() )
2274   {
2275     // if multi part, iterate through parts to find target part
2276     const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( line.constGet() );
2277     for ( int part = 0; part < collection->numGeometries(); ++part )
2278     {
2279       const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
2280       if ( !candidate )
2281         continue;
2282       const double candidateLength = candidate->length();
2283       if ( candidateLength >= distance )
2284       {
2285         curve = candidate;
2286         break;
2287       }
2288 
2289       distance -= candidateLength;
2290     }
2291   }
2292   else
2293   {
2294     curve = qgsgeometry_cast< const QgsCurve * >( line.constGet() );
2295   }
2296   if ( !curve )
2297     return QgsGeometry();
2298 
2299   std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
2300   if ( !result )
2301   {
2302     return QgsGeometry();
2303   }
2304   return QgsGeometry( std::move( result ) );
2305 }
2306 
lineLocatePoint(const QgsGeometry & point) const2307 double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
2308 {
2309   if ( type() != QgsWkbTypes::LineGeometry )
2310     return -1;
2311 
2312   if ( QgsWkbTypes::flatType( point.wkbType() ) != QgsWkbTypes::Point )
2313     return -1;
2314 
2315   QgsGeometry segmentized = *this;
2316   if ( QgsWkbTypes::isCurvedType( wkbType() ) )
2317   {
2318     segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
2319   }
2320 
2321   QgsGeos geos( d->geometry.get() );
2322   mLastError.clear();
2323   return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
2324 }
2325 
interpolateAngle(double distance) const2326 double QgsGeometry::interpolateAngle( double distance ) const
2327 {
2328   if ( !d->geometry )
2329     return 0.0;
2330 
2331   // always operate on segmentized geometries
2332   QgsGeometry segmentized = *this;
2333   if ( QgsWkbTypes::isCurvedType( wkbType() ) )
2334   {
2335     segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
2336   }
2337 
2338   QgsVertexId previous;
2339   QgsVertexId next;
2340   if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
2341     return 0.0;
2342 
2343   if ( previous == next )
2344   {
2345     // distance coincided exactly with a vertex
2346     QgsVertexId v2 = previous;
2347     QgsVertexId v1;
2348     QgsVertexId v3;
2349     segmentized.constGet()->adjacentVertices( v2, v1, v3 );
2350     if ( v1.isValid() && v3.isValid() )
2351     {
2352       QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
2353       QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
2354       QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
2355       double angle1 = QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2356       double angle2 = QgsGeometryUtils::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
2357       return QgsGeometryUtils::averageAngle( angle1, angle2 );
2358     }
2359     else if ( v3.isValid() )
2360     {
2361       QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
2362       QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
2363       return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2364     }
2365     else
2366     {
2367       QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
2368       QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
2369       return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2370     }
2371   }
2372   else
2373   {
2374     QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
2375     QgsPoint p2 = segmentized.constGet()->vertexAt( next );
2376     return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2377   }
2378 }
2379 
intersection(const QgsGeometry & geometry) const2380 QgsGeometry QgsGeometry::intersection( const QgsGeometry &geometry ) const
2381 {
2382   if ( !d->geometry || geometry.isNull() )
2383   {
2384     return QgsGeometry();
2385   }
2386 
2387   QgsGeos geos( d->geometry.get() );
2388 
2389   mLastError.clear();
2390   std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError ) );
2391 
2392   if ( !resultGeom )
2393   {
2394     QgsGeometry geom;
2395     geom.mLastError = mLastError;
2396     return geom;
2397   }
2398 
2399   return QgsGeometry( std::move( resultGeom ) );
2400 }
2401 
combine(const QgsGeometry & geometry) const2402 QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry ) const
2403 {
2404   if ( !d->geometry || geometry.isNull() )
2405   {
2406     return QgsGeometry();
2407   }
2408 
2409   QgsGeos geos( d->geometry.get() );
2410   mLastError.clear();
2411   std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError ) );
2412   if ( !resultGeom )
2413   {
2414     QgsGeometry geom;
2415     geom.mLastError = mLastError;
2416     return geom;
2417   }
2418   return QgsGeometry( std::move( resultGeom ) );
2419 }
2420 
mergeLines() const2421 QgsGeometry QgsGeometry::mergeLines() const
2422 {
2423   if ( !d->geometry )
2424   {
2425     return QgsGeometry();
2426   }
2427 
2428   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::LineString )
2429   {
2430     // special case - a single linestring was passed
2431     return QgsGeometry( *this );
2432   }
2433 
2434   QgsGeos geos( d->geometry.get() );
2435   mLastError.clear();
2436   QgsGeometry result = geos.mergeLines( &mLastError );
2437   result.mLastError = mLastError;
2438   return result;
2439 }
2440 
difference(const QgsGeometry & geometry) const2441 QgsGeometry QgsGeometry::difference( const QgsGeometry &geometry ) const
2442 {
2443   if ( !d->geometry || geometry.isNull() )
2444   {
2445     return QgsGeometry();
2446   }
2447 
2448   QgsGeos geos( d->geometry.get() );
2449 
2450   mLastError.clear();
2451   std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError ) );
2452   if ( !resultGeom )
2453   {
2454     QgsGeometry geom;
2455     geom.mLastError = mLastError;
2456     return geom;
2457   }
2458   return QgsGeometry( std::move( resultGeom ) );
2459 }
2460 
symDifference(const QgsGeometry & geometry) const2461 QgsGeometry QgsGeometry::symDifference( const QgsGeometry &geometry ) const
2462 {
2463   if ( !d->geometry || geometry.isNull() )
2464   {
2465     return QgsGeometry();
2466   }
2467 
2468   QgsGeos geos( d->geometry.get() );
2469 
2470   mLastError.clear();
2471   std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError ) );
2472   if ( !resultGeom )
2473   {
2474     QgsGeometry geom;
2475     geom.mLastError = mLastError;
2476     return geom;
2477   }
2478   return QgsGeometry( std::move( resultGeom ) );
2479 }
2480 
extrude(double x,double y)2481 QgsGeometry QgsGeometry::extrude( double x, double y )
2482 {
2483   QgsInternalGeometryEngine engine( *this );
2484 
2485   return engine.extrude( x, y );
2486 }
2487 
2488 ///@cond PRIVATE // avoid dox warning
2489 
randomPointsInPolygon(int count,const std::function<bool (const QgsPointXY &)> & acceptPoint,unsigned long seed,QgsFeedback * feedback,int maxTriesPerPoint) const2490 QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
2491 {
2492   if ( type() != QgsWkbTypes::PolygonGeometry )
2493     return QVector< QgsPointXY >();
2494 
2495   return QgsInternalGeometryEngine::randomPointsInPolygon( *this, count, acceptPoint, seed, feedback, maxTriesPerPoint );
2496 }
2497 
randomPointsInPolygon(int count,unsigned long seed,QgsFeedback * feedback) const2498 QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
2499 {
2500   if ( type() != QgsWkbTypes::PolygonGeometry )
2501     return QVector< QgsPointXY >();
2502 
2503   return QgsInternalGeometryEngine::randomPointsInPolygon( *this, count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
2504 }
2505 ///@endcond
2506 
wkbSize(QgsAbstractGeometry::WkbFlags flags) const2507 int QgsGeometry::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
2508 {
2509   return d->geometry ? d->geometry->wkbSize( flags ) : 0;
2510 }
2511 
asWkb(QgsAbstractGeometry::WkbFlags flags) const2512 QByteArray QgsGeometry::asWkb( QgsAbstractGeometry::WkbFlags flags ) const
2513 {
2514   return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
2515 }
2516 
asGeometryCollection() const2517 QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
2518 {
2519   QVector<QgsGeometry> geometryList;
2520   if ( !d->geometry )
2521   {
2522     return geometryList;
2523   }
2524 
2525   QgsGeometryCollection *gc = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2526   if ( gc )
2527   {
2528     int numGeom = gc->numGeometries();
2529     geometryList.reserve( numGeom );
2530     for ( int i = 0; i < numGeom; ++i )
2531     {
2532       geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
2533     }
2534   }
2535   else //a singlepart geometry
2536   {
2537     geometryList.append( *this );
2538   }
2539 
2540   return geometryList;
2541 }
2542 
asQPointF() const2543 QPointF QgsGeometry::asQPointF() const
2544 {
2545   QgsPointXY point = asPoint();
2546   return point.toQPointF();
2547 }
2548 
asQPolygonF() const2549 QPolygonF QgsGeometry::asQPolygonF() const
2550 {
2551   const QgsAbstractGeometry *part = constGet();
2552 
2553   // if a geometry collection, get first part only
2554   if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( part ) )
2555   {
2556     if ( collection->numGeometries() > 0 )
2557       part = collection->geometryN( 0 );
2558     else
2559       return QPolygonF();
2560   }
2561 
2562   if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
2563     return curve->asQPolygonF();
2564   else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
2565     return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
2566   return QPolygonF();
2567 }
2568 
deleteRing(int ringNum,int partNum)2569 bool QgsGeometry::deleteRing( int ringNum, int partNum )
2570 {
2571   if ( !d->geometry )
2572   {
2573     return false;
2574   }
2575 
2576   detach();
2577   bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
2578   return ok;
2579 }
2580 
deletePart(int partNum)2581 bool QgsGeometry::deletePart( int partNum )
2582 {
2583   if ( !d->geometry )
2584   {
2585     return false;
2586   }
2587 
2588   if ( !isMultipart() && partNum < 1 )
2589   {
2590     set( nullptr );
2591     return true;
2592   }
2593 
2594   detach();
2595   bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
2596   return ok;
2597 }
2598 
avoidIntersections(const QList<QgsVectorLayer * > & avoidIntersectionsLayers,const QHash<QgsVectorLayer *,QSet<QgsFeatureId>> & ignoreFeatures)2599 int QgsGeometry::avoidIntersections( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
2600 {
2601   if ( !d->geometry )
2602   {
2603     return 1;
2604   }
2605 
2606   std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, ignoreFeatures );
2607   if ( diffGeom )
2608   {
2609     reset( std::move( diffGeom ) );
2610   }
2611   return 0;
2612 }
2613 
2614 
makeValid() const2615 QgsGeometry QgsGeometry::makeValid() const
2616 {
2617   if ( !d->geometry )
2618     return QgsGeometry();
2619 
2620   mLastError.clear();
2621   std::unique_ptr< QgsAbstractGeometry > g( _qgis_lwgeom_make_valid( d->geometry.get(), mLastError ) );
2622 
2623   QgsGeometry result = QgsGeometry( std::move( g ) );
2624   result.mLastError = mLastError;
2625   return result;
2626 }
2627 
forceRHR() const2628 QgsGeometry QgsGeometry::forceRHR() const
2629 {
2630   if ( !d->geometry )
2631     return QgsGeometry();
2632 
2633   if ( isMultipart() )
2634   {
2635     const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
2636     std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
2637     newCollection->reserve( collection->numGeometries() );
2638     for ( int i = 0; i < collection->numGeometries(); ++i )
2639     {
2640       const QgsAbstractGeometry *g = collection->geometryN( i );
2641       if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
2642       {
2643         std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2644         corrected->forceRHR();
2645         newCollection->addGeometry( corrected.release() );
2646       }
2647       else
2648       {
2649         newCollection->addGeometry( g->clone() );
2650       }
2651     }
2652     return QgsGeometry( std::move( newCollection ) );
2653   }
2654   else
2655   {
2656     if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
2657     {
2658       std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2659       corrected->forceRHR();
2660       return QgsGeometry( std::move( corrected ) );
2661     }
2662     else
2663     {
2664       // not a curve polygon, so return unchanged
2665       return *this;
2666     }
2667   }
2668 }
2669 
2670 
validateGeometry(QVector<QgsGeometry::Error> & errors,const ValidationMethod method,const QgsGeometry::ValidityFlags flags) const2671 void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const ValidationMethod method, const QgsGeometry::ValidityFlags flags ) const
2672 {
2673   errors.clear();
2674   if ( !d->geometry )
2675     return;
2676 
2677   // avoid expensive calcs for trivial point geometries
2678   if ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) == QgsWkbTypes::PointGeometry )
2679   {
2680     return;
2681   }
2682 
2683   switch ( method )
2684   {
2685     case ValidatorQgisInternal:
2686       QgsGeometryValidator::validateGeometry( *this, errors, method );
2687       return;
2688 
2689     case ValidatorGeos:
2690     {
2691       QgsGeos geos( d->geometry.get() );
2692       QString error;
2693       QgsGeometry errorLoc;
2694       if ( !geos.isValid( &error, flags & FlagAllowSelfTouchingHoles, &errorLoc ) )
2695       {
2696         if ( errorLoc.isNull() )
2697         {
2698           errors.append( QgsGeometry::Error( error ) );
2699         }
2700         else
2701         {
2702           const QgsPointXY point = errorLoc.asPoint();
2703           errors.append( QgsGeometry::Error( error, point ) );
2704         }
2705         return;
2706       }
2707     }
2708   }
2709 }
2710 
isGeosValid(const QgsGeometry::ValidityFlags flags) const2711 bool QgsGeometry::isGeosValid( const QgsGeometry::ValidityFlags flags ) const
2712 {
2713   if ( !d->geometry )
2714   {
2715     return false;
2716   }
2717 
2718   return d->geometry->isValid( mLastError, static_cast< int >( flags ) );
2719 }
2720 
isSimple() const2721 bool QgsGeometry::isSimple() const
2722 {
2723   if ( !d->geometry )
2724     return false;
2725 
2726   QgsGeos geos( d->geometry.get() );
2727   mLastError.clear();
2728   return geos.isSimple( &mLastError );
2729 }
2730 
isGeosEqual(const QgsGeometry & g) const2731 bool QgsGeometry::isGeosEqual( const QgsGeometry &g ) const
2732 {
2733   if ( !d->geometry || !g.d->geometry )
2734   {
2735     return false;
2736   }
2737 
2738   // fast check - are they shared copies of the same underlying geometry?
2739   if ( d == g.d )
2740     return true;
2741 
2742   // fast check - distinct geometry types?
2743   if ( type() != g.type() )
2744     return false;
2745 
2746   // avoid calling geos for trivial point case
2747   if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point
2748        && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == QgsWkbTypes::Point )
2749   {
2750     return equals( g );
2751   }
2752 
2753   //  another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
2754   if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
2755     return false;
2756 
2757   QgsGeos geos( d->geometry.get() );
2758   mLastError.clear();
2759   return geos.isEqual( g.d->geometry.get(), &mLastError );
2760 }
2761 
unaryUnion(const QVector<QgsGeometry> & geometries)2762 QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries )
2763 {
2764   QgsGeos geos( nullptr );
2765 
2766   QString error;
2767   std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error ) );
2768   QgsGeometry result( std::move( geom ) );
2769   result.mLastError = error;
2770   return result;
2771 }
2772 
polygonize(const QVector<QgsGeometry> & geometryList)2773 QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
2774 {
2775   QgsGeos geos( nullptr );
2776 
2777   QVector<const QgsAbstractGeometry *> geomV2List;
2778   for ( const QgsGeometry &g : geometryList )
2779   {
2780     if ( !( g.isNull() ) )
2781     {
2782       geomV2List.append( g.constGet() );
2783     }
2784   }
2785 
2786   QString error;
2787   QgsGeometry result = geos.polygonize( geomV2List, &error );
2788   result.mLastError = error;
2789   return result;
2790 }
2791 
convertToStraightSegment(double tolerance,QgsAbstractGeometry::SegmentationToleranceType toleranceType)2792 void QgsGeometry::convertToStraightSegment( double tolerance, QgsAbstractGeometry::SegmentationToleranceType toleranceType )
2793 {
2794   if ( !d->geometry || !requiresConversionToStraightSegments() )
2795   {
2796     return;
2797   }
2798 
2799   std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
2800   reset( std::move( straightGeom ) );
2801 }
2802 
requiresConversionToStraightSegments() const2803 bool QgsGeometry::requiresConversionToStraightSegments() const
2804 {
2805   if ( !d->geometry )
2806   {
2807     return false;
2808   }
2809 
2810   return d->geometry->hasCurvedSegments();
2811 }
2812 
transform(const QgsCoordinateTransform & ct,const QgsCoordinateTransform::TransformDirection direction,const bool transformZ)2813 QgsGeometry::OperationResult QgsGeometry::transform( const QgsCoordinateTransform &ct, const QgsCoordinateTransform::TransformDirection direction, const bool transformZ )
2814 {
2815   if ( !d->geometry )
2816   {
2817     return QgsGeometry::InvalidBaseGeometry;
2818   }
2819 
2820   detach();
2821   d->geometry->transform( ct, direction, transformZ );
2822   return QgsGeometry::Success;
2823 }
2824 
transform(const QTransform & ct,double zTranslate,double zScale,double mTranslate,double mScale)2825 QgsGeometry::OperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
2826 {
2827   if ( !d->geometry )
2828   {
2829     return QgsGeometry::InvalidBaseGeometry;
2830   }
2831 
2832   detach();
2833   d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
2834   return QgsGeometry::Success;
2835 }
2836 
mapToPixel(const QgsMapToPixel & mtp)2837 void QgsGeometry::mapToPixel( const QgsMapToPixel &mtp )
2838 {
2839   if ( d->geometry )
2840   {
2841     detach();
2842     d->geometry->transform( mtp.transform() );
2843   }
2844 }
2845 
clipped(const QgsRectangle & rectangle)2846 QgsGeometry QgsGeometry::clipped( const QgsRectangle &rectangle )
2847 {
2848   if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
2849   {
2850     return QgsGeometry();
2851   }
2852 
2853   QgsGeos geos( d->geometry.get() );
2854   mLastError.clear();
2855   std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError );
2856   if ( !resultGeom )
2857   {
2858     QgsGeometry result;
2859     result.mLastError = mLastError;
2860     return result;
2861   }
2862   return QgsGeometry( std::move( resultGeom ) );
2863 }
2864 
draw(QPainter & p) const2865 void QgsGeometry::draw( QPainter &p ) const
2866 {
2867   if ( d->geometry )
2868   {
2869     d->geometry->draw( p );
2870   }
2871 }
2872 
vertexIndexInfo(const QgsAbstractGeometry * g,int vertexIndex,int & partIndex,int & ringIndex,int & vertex)2873 static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
2874 {
2875   if ( vertexIndex < 0 )
2876     return false;  // clearly something wrong
2877 
2878   if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
2879   {
2880     partIndex = 0;
2881     int offset = 0;
2882     for ( int i = 0; i < geomCollection->numGeometries(); ++i )
2883     {
2884       const QgsAbstractGeometry *part = geomCollection->geometryN( i );
2885 
2886       // count total number of vertices in the part
2887       int numPoints = 0;
2888       for ( int k = 0; k < part->ringCount(); ++k )
2889         numPoints += part->vertexCount( 0, k );
2890 
2891       if ( vertexIndex < numPoints )
2892       {
2893         int nothing;
2894         return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
2895       }
2896       vertexIndex -= numPoints;
2897       offset += numPoints;
2898       partIndex++;
2899     }
2900   }
2901   else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
2902   {
2903     const QgsCurve *ring = curvePolygon->exteriorRing();
2904     if ( vertexIndex < ring->numPoints() )
2905     {
2906       partIndex = 0;
2907       ringIndex = 0;
2908       vertex = vertexIndex;
2909       return true;
2910     }
2911     vertexIndex -= ring->numPoints();
2912     ringIndex = 1;
2913     for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
2914     {
2915       const QgsCurve *ring = curvePolygon->interiorRing( i );
2916       if ( vertexIndex < ring->numPoints() )
2917       {
2918         partIndex = 0;
2919         vertex = vertexIndex;
2920         return true;
2921       }
2922       vertexIndex -= ring->numPoints();
2923       ringIndex += 1;
2924     }
2925   }
2926   else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
2927   {
2928     if ( vertexIndex < curve->numPoints() )
2929     {
2930       partIndex = 0;
2931       ringIndex = 0;
2932       vertex = vertexIndex;
2933       return true;
2934     }
2935   }
2936   else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
2937   {
2938     if ( vertexIndex == 0 )
2939     {
2940       partIndex = 0;
2941       ringIndex = 0;
2942       vertex = 0;
2943       return true;
2944     }
2945   }
2946 
2947   return false;
2948 }
2949 
vertexIdFromVertexNr(int nr,QgsVertexId & id) const2950 bool QgsGeometry::vertexIdFromVertexNr( int nr, QgsVertexId &id ) const
2951 {
2952   if ( !d->geometry )
2953   {
2954     return false;
2955   }
2956 
2957   id.type = QgsVertexId::SegmentVertex;
2958 
2959   bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
2960   if ( !res )
2961     return false;
2962 
2963   // now let's find out if it is a straight or circular segment
2964   const QgsAbstractGeometry *g = d->geometry.get();
2965   if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
2966   {
2967     g = geomCollection->geometryN( id.part );
2968   }
2969 
2970   if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
2971   {
2972     g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
2973   }
2974 
2975   if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
2976   {
2977     QgsPoint p;
2978     res = curve->pointAt( id.vertex, p, id.type );
2979     if ( !res )
2980       return false;
2981   }
2982 
2983   return true;
2984 }
2985 
vertexNrFromVertexId(QgsVertexId id) const2986 int QgsGeometry::vertexNrFromVertexId( QgsVertexId id ) const
2987 {
2988   if ( !d->geometry )
2989   {
2990     return -1;
2991   }
2992   return d->geometry->vertexNumberFromVertexId( id );
2993 }
2994 
lastError() const2995 QString QgsGeometry::lastError() const
2996 {
2997   return mLastError;
2998 }
2999 
filterVertices(const std::function<bool (const QgsPoint &)> & filter)3000 void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
3001 {
3002   if ( !d->geometry )
3003     return;
3004 
3005   detach();
3006 
3007   d->geometry->filterVertices( filter );
3008 }
3009 
transformVertices(const std::function<QgsPoint (const QgsPoint &)> & transform)3010 void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
3011 {
3012   if ( !d->geometry )
3013     return;
3014 
3015   detach();
3016 
3017   d->geometry->transformVertices( transform );
3018 }
3019 
convertPointList(const QVector<QgsPointXY> & input,QgsPointSequence & output)3020 void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
3021 {
3022   output.clear();
3023   for ( const QgsPointXY &p : input )
3024   {
3025     output.append( QgsPoint( p ) );
3026   }
3027 }
3028 
convertPointList(const QgsPointSequence & input,QVector<QgsPointXY> & output)3029 void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
3030 {
3031   output.clear();
3032   for ( const QgsPoint &p : input )
3033   {
3034     output.append( QgsPointXY( p.x(), p.y() ) );
3035   }
3036 }
3037 
convertToPolyline(const QgsPointSequence & input,QgsPolylineXY & output)3038 void QgsGeometry::convertToPolyline( const QgsPointSequence &input, QgsPolylineXY &output )
3039 {
3040   output.clear();
3041   output.resize( input.size() );
3042 
3043   for ( int i = 0; i < input.size(); ++i )
3044   {
3045     const QgsPoint &pt = input.at( i );
3046     output[i].setX( pt.x() );
3047     output[i].setY( pt.y() );
3048   }
3049 }
3050 
convertPolygon(const QgsPolygon & input,QgsPolygonXY & output)3051 void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
3052 {
3053   output.clear();
3054   QgsCoordinateSequence coords = input.coordinateSequence();
3055   if ( coords.empty() )
3056   {
3057     return;
3058   }
3059   const QgsRingSequence &rings = coords[0];
3060   output.resize( rings.size() );
3061   for ( int i = 0; i < rings.size(); ++i )
3062   {
3063     convertToPolyline( rings[i], output[i] );
3064   }
3065 }
3066 
fromQPointF(QPointF point)3067 QgsGeometry QgsGeometry::fromQPointF( QPointF point )
3068 {
3069   return QgsGeometry( qgis::make_unique< QgsPoint >( point.x(), point.y() ) );
3070 }
3071 
fromQPolygonF(const QPolygonF & polygon)3072 QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
3073 {
3074   std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
3075 
3076   if ( polygon.isClosed() )
3077   {
3078     std::unique_ptr< QgsPolygon > poly = qgis::make_unique< QgsPolygon >();
3079     poly->setExteriorRing( ring.release() );
3080     return QgsGeometry( std::move( poly ) );
3081   }
3082   else
3083   {
3084     return QgsGeometry( std::move( ring ) );
3085   }
3086 }
3087 
createPolygonFromQPolygonF(const QPolygonF & polygon)3088 QgsPolygonXY QgsGeometry::createPolygonFromQPolygonF( const QPolygonF &polygon )
3089 {
3090   Q_NOWARN_DEPRECATED_PUSH
3091   QgsPolygonXY result;
3092   result << createPolylineFromQPolygonF( polygon );
3093   return result;
3094   Q_NOWARN_DEPRECATED_POP
3095 }
3096 
createPolylineFromQPolygonF(const QPolygonF & polygon)3097 QgsPolylineXY QgsGeometry::createPolylineFromQPolygonF( const QPolygonF &polygon )
3098 {
3099   QgsPolylineXY result;
3100   result.reserve( polygon.count() );
3101   for ( const QPointF &p : polygon )
3102   {
3103     result.append( QgsPointXY( p ) );
3104   }
3105   return result;
3106 }
3107 
compare(const QgsPolylineXY & p1,const QgsPolylineXY & p2,double epsilon)3108 bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
3109 {
3110   if ( p1.count() != p2.count() )
3111     return false;
3112 
3113   for ( int i = 0; i < p1.count(); ++i )
3114   {
3115     if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
3116       return false;
3117   }
3118   return true;
3119 }
3120 
compare(const QgsPolygonXY & p1,const QgsPolygonXY & p2,double epsilon)3121 bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
3122 {
3123   if ( p1.count() != p2.count() )
3124     return false;
3125 
3126   for ( int i = 0; i < p1.count(); ++i )
3127   {
3128     if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3129       return false;
3130   }
3131   return true;
3132 }
3133 
3134 
compare(const QgsMultiPolygonXY & p1,const QgsMultiPolygonXY & p2,double epsilon)3135 bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
3136 {
3137   if ( p1.count() != p2.count() )
3138     return false;
3139 
3140   for ( int i = 0; i < p1.count(); ++i )
3141   {
3142     if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3143       return false;
3144   }
3145   return true;
3146 }
3147 
smooth(const unsigned int iterations,const double offset,double minimumDistance,double maxAngle) const3148 QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3149 {
3150   if ( !d->geometry || d->geometry->isEmpty() )
3151     return QgsGeometry();
3152 
3153   QgsGeometry geom = *this;
3154   if ( QgsWkbTypes::isCurvedType( wkbType() ) )
3155     geom = QgsGeometry( d->geometry->segmentize() );
3156 
3157   switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
3158   {
3159     case QgsWkbTypes::Point:
3160     case QgsWkbTypes::MultiPoint:
3161       //can't smooth a point based geometry
3162       return geom;
3163 
3164     case QgsWkbTypes::LineString:
3165     {
3166       const QgsLineString *lineString = qgsgeometry_cast< const QgsLineString * >( geom.constGet() );
3167       return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
3168     }
3169 
3170     case QgsWkbTypes::MultiLineString:
3171     {
3172       const QgsMultiLineString *multiLine = qgsgeometry_cast< const QgsMultiLineString * >( geom.constGet() );
3173 
3174       std::unique_ptr< QgsMultiLineString > resultMultiline = qgis::make_unique< QgsMultiLineString> ();
3175       resultMultiline->reserve( multiLine->numGeometries() );
3176       for ( int i = 0; i < multiLine->numGeometries(); ++i )
3177       {
3178         resultMultiline->addGeometry( smoothLine( *( multiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3179       }
3180       return QgsGeometry( std::move( resultMultiline ) );
3181     }
3182 
3183     case QgsWkbTypes::Polygon:
3184     {
3185       const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( geom.constGet() );
3186       return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
3187     }
3188 
3189     case QgsWkbTypes::MultiPolygon:
3190     {
3191       const QgsMultiPolygon *multiPoly = qgsgeometry_cast< const QgsMultiPolygon * >( geom.constGet() );
3192 
3193       std::unique_ptr< QgsMultiPolygon > resultMultiPoly = qgis::make_unique< QgsMultiPolygon >();
3194       resultMultiPoly->reserve( multiPoly->numGeometries() );
3195       for ( int i = 0; i < multiPoly->numGeometries(); ++i )
3196       {
3197         resultMultiPoly->addGeometry( smoothPolygon( *( multiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3198       }
3199       return QgsGeometry( std::move( resultMultiPoly ) );
3200     }
3201 
3202     case QgsWkbTypes::Unknown:
3203     default:
3204       return QgsGeometry( *this );
3205   }
3206 }
3207 
smoothCurve(const QgsLineString & line,const unsigned int iterations,const double offset,double squareDistThreshold,double maxAngleRads,bool isRing)3208 std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations,
3209     const double offset, double squareDistThreshold, double maxAngleRads,
3210     bool isRing )
3211 {
3212   std::unique_ptr< QgsLineString > result = qgis::make_unique< QgsLineString >( line );
3213   QgsPointSequence outputLine;
3214   for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
3215   {
3216     outputLine.resize( 0 );
3217     outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
3218     bool skipFirst = false;
3219     bool skipLast = false;
3220     if ( isRing )
3221     {
3222       QgsPoint p1 = result->pointN( result->numPoints() - 2 );
3223       QgsPoint p2 = result->pointN( 0 );
3224       QgsPoint p3 = result->pointN( 1 );
3225       double angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3226                      p3.x(), p3.y() );
3227       angle = std::fabs( M_PI - angle );
3228       skipFirst = angle > maxAngleRads;
3229     }
3230     for ( int i = 0; i < result->numPoints() - 1; i++ )
3231     {
3232       QgsPoint p1 = result->pointN( i );
3233       QgsPoint p2 = result->pointN( i + 1 );
3234 
3235       double angle = M_PI;
3236       if ( i == 0 && isRing )
3237       {
3238         QgsPoint p3 = result->pointN( result->numPoints() - 2 );
3239         angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3240                 p3.x(), p3.y() );
3241       }
3242       else if ( i < result->numPoints() - 2 )
3243       {
3244         QgsPoint p3 = result->pointN( i + 2 );
3245         angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3246                 p3.x(), p3.y() );
3247       }
3248       else if ( i == result->numPoints() - 2 && isRing )
3249       {
3250         QgsPoint p3 = result->pointN( 1 );
3251         angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3252                 p3.x(), p3.y() );
3253       }
3254 
3255       skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
3256 
3257       // don't apply distance threshold to first or last segment
3258       if ( i == 0 || i >= result->numPoints() - 2
3259            || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
3260       {
3261         if ( !isRing )
3262         {
3263           if ( !skipFirst )
3264             outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
3265           if ( !skipLast )
3266             outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
3267           else
3268             outputLine << p2;
3269         }
3270         else
3271         {
3272           // ring
3273           if ( !skipFirst )
3274             outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
3275           else if ( i == 0 )
3276             outputLine << p1;
3277           if ( !skipLast )
3278             outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
3279           else
3280             outputLine << p2;
3281         }
3282       }
3283       skipFirst = skipLast;
3284     }
3285 
3286     if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
3287       outputLine << outputLine.at( 0 );
3288 
3289     result->setPoints( outputLine );
3290   }
3291   return result;
3292 }
3293 
smoothLine(const QgsLineString & line,const unsigned int iterations,const double offset,double minimumDistance,double maxAngle) const3294 std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3295 {
3296   double maxAngleRads = maxAngle * M_PI / 180.0;
3297   double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
3298   return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
3299 }
3300 
smoothPolygon(const QgsPolygon & polygon,const unsigned int iterations,const double offset,double minimumDistance,double maxAngle) const3301 std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3302 {
3303   double maxAngleRads = maxAngle * M_PI / 180.0;
3304   double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
3305   std::unique_ptr< QgsPolygon > resultPoly = qgis::make_unique< QgsPolygon >();
3306 
3307   resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset,
3308                                squareDistThreshold, maxAngleRads, true ).release() );
3309 
3310   for ( int i = 0; i < polygon.numInteriorRings(); ++i )
3311   {
3312     resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset,
3313                                  squareDistThreshold, maxAngleRads, true ).release() );
3314   }
3315   return resultPoly;
3316 }
3317 
convertToPoint(bool destMultipart) const3318 QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
3319 {
3320   switch ( type() )
3321   {
3322     case QgsWkbTypes::PointGeometry:
3323     {
3324       bool srcIsMultipart = isMultipart();
3325 
3326       if ( ( destMultipart && srcIsMultipart ) ||
3327            ( !destMultipart && !srcIsMultipart ) )
3328       {
3329         // return a copy of the same geom
3330         return QgsGeometry( *this );
3331       }
3332       if ( destMultipart )
3333       {
3334         // layer is multipart => make a multipoint with a single point
3335         return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
3336       }
3337       else
3338       {
3339         // destination is singlepart => make a single part if possible
3340         QgsMultiPointXY multiPoint = asMultiPoint();
3341         if ( multiPoint.count() == 1 )
3342         {
3343           return fromPointXY( multiPoint[0] );
3344         }
3345       }
3346       return QgsGeometry();
3347     }
3348 
3349     case QgsWkbTypes::LineGeometry:
3350     {
3351       // only possible if destination is multipart
3352       if ( !destMultipart )
3353         return QgsGeometry();
3354 
3355       // input geometry is multipart
3356       if ( isMultipart() )
3357       {
3358         const QgsMultiPolylineXY multiLine = asMultiPolyline();
3359         QgsMultiPointXY multiPoint;
3360         for ( const QgsPolylineXY &l : multiLine )
3361           for ( const QgsPointXY &p : l )
3362             multiPoint << p;
3363         return fromMultiPointXY( multiPoint );
3364       }
3365       // input geometry is not multipart: copy directly the line into a multipoint
3366       else
3367       {
3368         QgsPolylineXY line = asPolyline();
3369         if ( !line.isEmpty() )
3370           return fromMultiPointXY( line );
3371       }
3372       return QgsGeometry();
3373     }
3374 
3375     case QgsWkbTypes::PolygonGeometry:
3376     {
3377       // can only transform if destination is multipoint
3378       if ( !destMultipart )
3379         return QgsGeometry();
3380 
3381       // input geometry is multipart: make a multipoint from multipolygon
3382       if ( isMultipart() )
3383       {
3384         const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3385         QgsMultiPointXY multiPoint;
3386         for ( const QgsPolygonXY &poly : multiPolygon )
3387           for ( const QgsPolylineXY &line : poly )
3388             for ( const QgsPointXY &pt : line )
3389               multiPoint << pt;
3390         return fromMultiPointXY( multiPoint );
3391       }
3392       // input geometry is not multipart: make a multipoint from polygon
3393       else
3394       {
3395         const QgsPolygonXY polygon = asPolygon();
3396         QgsMultiPointXY multiPoint;
3397         for ( const QgsPolylineXY &line : polygon )
3398           for ( const QgsPointXY &pt : line )
3399             multiPoint << pt;
3400         return fromMultiPointXY( multiPoint );
3401       }
3402     }
3403 
3404     default:
3405       return QgsGeometry();
3406   }
3407 }
3408 
convertToLine(bool destMultipart) const3409 QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
3410 {
3411   switch ( type() )
3412   {
3413     case QgsWkbTypes::PointGeometry:
3414     {
3415       if ( !isMultipart() )
3416         return QgsGeometry();
3417 
3418       QgsMultiPointXY multiPoint = asMultiPoint();
3419       if ( multiPoint.count() < 2 )
3420         return QgsGeometry();
3421 
3422       if ( destMultipart )
3423         return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
3424       else
3425         return fromPolylineXY( multiPoint );
3426     }
3427 
3428     case QgsWkbTypes::LineGeometry:
3429     {
3430       bool srcIsMultipart = isMultipart();
3431 
3432       if ( ( destMultipart && srcIsMultipart ) ||
3433            ( !destMultipart && ! srcIsMultipart ) )
3434       {
3435         // return a copy of the same geom
3436         return QgsGeometry( *this );
3437       }
3438       if ( destMultipart )
3439       {
3440         // destination is multipart => makes a multipoint with a single line
3441         QgsPolylineXY line = asPolyline();
3442         if ( !line.isEmpty() )
3443           return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
3444       }
3445       else
3446       {
3447         // destination is singlepart => make a single part if possible
3448         QgsMultiPolylineXY multiLine = asMultiPolyline();
3449         if ( multiLine.count() == 1 )
3450           return fromPolylineXY( multiLine[0] );
3451       }
3452       return QgsGeometry();
3453     }
3454 
3455     case QgsWkbTypes::PolygonGeometry:
3456     {
3457       // input geometry is multipolygon
3458       if ( isMultipart() )
3459       {
3460         const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3461         QgsMultiPolylineXY multiLine;
3462         for ( const QgsPolygonXY &poly : multiPolygon )
3463           for ( const QgsPolylineXY &line : poly )
3464             multiLine << line;
3465 
3466         if ( destMultipart )
3467         {
3468           // destination is multipart
3469           return fromMultiPolylineXY( multiLine );
3470         }
3471         else if ( multiLine.count() == 1 )
3472         {
3473           // destination is singlepart => make a single part if possible
3474           return fromPolylineXY( multiLine[0] );
3475         }
3476       }
3477       // input geometry is single polygon
3478       else
3479       {
3480         QgsPolygonXY polygon = asPolygon();
3481         // if polygon has rings
3482         if ( polygon.count() > 1 )
3483         {
3484           // cannot fit a polygon with rings in a single line layer
3485           // TODO: would it be better to remove rings?
3486           if ( destMultipart )
3487           {
3488             const QgsPolygonXY polygon = asPolygon();
3489             QgsMultiPolylineXY multiLine;
3490             multiLine.reserve( polygon.count() );
3491             for ( const QgsPolylineXY &line : polygon )
3492               multiLine << line;
3493             return fromMultiPolylineXY( multiLine );
3494           }
3495         }
3496         // no rings
3497         else if ( polygon.count() == 1 )
3498         {
3499           if ( destMultipart )
3500           {
3501             return fromMultiPolylineXY( polygon );
3502           }
3503           else
3504           {
3505             return fromPolylineXY( polygon[0] );
3506           }
3507         }
3508       }
3509       return QgsGeometry();
3510     }
3511 
3512     default:
3513       return QgsGeometry();
3514   }
3515 }
3516 
convertToPolygon(bool destMultipart) const3517 QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
3518 {
3519   switch ( type() )
3520   {
3521     case QgsWkbTypes::PointGeometry:
3522     {
3523       if ( !isMultipart() )
3524         return QgsGeometry();
3525 
3526       QgsMultiPointXY multiPoint = asMultiPoint();
3527       if ( multiPoint.count() < 3 )
3528         return QgsGeometry();
3529 
3530       if ( multiPoint.last() != multiPoint.first() )
3531         multiPoint << multiPoint.first();
3532 
3533       QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
3534       if ( destMultipart )
3535         return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
3536       else
3537         return fromPolygonXY( polygon );
3538     }
3539 
3540     case QgsWkbTypes::LineGeometry:
3541     {
3542       // input geometry is multiline
3543       if ( isMultipart() )
3544       {
3545         QgsMultiPolylineXY multiLine = asMultiPolyline();
3546         QgsMultiPolygonXY multiPolygon;
3547         for ( QgsMultiPolylineXY::iterator multiLineIt = multiLine.begin(); multiLineIt != multiLine.end(); ++multiLineIt )
3548         {
3549           // do not create polygon for a 1 segment line
3550           if ( ( *multiLineIt ).count() < 3 )
3551             return QgsGeometry();
3552           if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
3553             return QgsGeometry();
3554 
3555           // add closing node
3556           if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
3557             *multiLineIt << ( *multiLineIt ).first();
3558           multiPolygon << ( QgsPolygonXY() << *multiLineIt );
3559         }
3560         // check that polygons were inserted
3561         if ( !multiPolygon.isEmpty() )
3562         {
3563           if ( destMultipart )
3564           {
3565             return fromMultiPolygonXY( multiPolygon );
3566           }
3567           else if ( multiPolygon.count() == 1 )
3568           {
3569             // destination is singlepart => make a single part if possible
3570             return fromPolygonXY( multiPolygon[0] );
3571           }
3572         }
3573       }
3574       // input geometry is single line
3575       else
3576       {
3577         QgsPolylineXY line = asPolyline();
3578 
3579         // do not create polygon for a 1 segment line
3580         if ( line.count() < 3 )
3581           return QgsGeometry();
3582         if ( line.count() == 3 && line.first() == line.last() )
3583           return QgsGeometry();
3584 
3585         // add closing node
3586         if ( line.first() != line.last() )
3587           line << line.first();
3588 
3589         // destination is multipart
3590         if ( destMultipart )
3591         {
3592           return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
3593         }
3594         else
3595         {
3596           return fromPolygonXY( QgsPolygonXY() << line );
3597         }
3598       }
3599       return QgsGeometry();
3600     }
3601 
3602     case QgsWkbTypes::PolygonGeometry:
3603     {
3604       bool srcIsMultipart = isMultipart();
3605 
3606       if ( ( destMultipart && srcIsMultipart ) ||
3607            ( !destMultipart && ! srcIsMultipart ) )
3608       {
3609         // return a copy of the same geom
3610         return QgsGeometry( *this );
3611       }
3612       if ( destMultipart )
3613       {
3614         // destination is multipart => makes a multipoint with a single polygon
3615         QgsPolygonXY polygon = asPolygon();
3616         if ( !polygon.isEmpty() )
3617           return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
3618       }
3619       else
3620       {
3621         QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3622         if ( multiPolygon.count() == 1 )
3623         {
3624           // destination is singlepart => make a single part if possible
3625           return fromPolygonXY( multiPolygon[0] );
3626         }
3627       }
3628       return QgsGeometry();
3629     }
3630 
3631     default:
3632       return QgsGeometry();
3633   }
3634 }
3635 
createGeometryEngine(const QgsAbstractGeometry * geometry)3636 QgsGeometryEngine *QgsGeometry::createGeometryEngine( const QgsAbstractGeometry *geometry )
3637 {
3638   return new QgsGeos( geometry );
3639 }
3640 
operator <<(QDataStream & out,const QgsGeometry & geometry)3641 QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
3642 {
3643   out << geometry.asWkb();
3644   return out;
3645 }
3646 
operator >>(QDataStream & in,QgsGeometry & geometry)3647 QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
3648 {
3649   QByteArray byteArray;
3650   in >> byteArray;
3651   if ( byteArray.isEmpty() )
3652   {
3653     geometry.set( nullptr );
3654     return in;
3655   }
3656 
3657   geometry.fromWkb( byteArray );
3658   return in;
3659 }
3660 
3661 
what() const3662 QString QgsGeometry::Error::what() const
3663 {
3664   return mMessage;
3665 }
3666 
where() const3667 QgsPointXY QgsGeometry::Error::where() const
3668 {
3669   return mLocation;
3670 }
3671 
hasWhere() const3672 bool QgsGeometry::Error::hasWhere() const
3673 {
3674   return mHasLocation;
3675 }
3676