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