QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsgeometry.cpp
Go to the documentation of this file.
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"
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 
58 {
60  QAtomicInt ref;
61  std::unique_ptr< QgsAbstractGeometry > geometry;
62 };
63 
65  : d( new QgsGeometryPrivate() )
66 {
67 }
68 
70 {
71  if ( !d->ref.deref() )
72  delete d;
73 }
74 
76  : d( new QgsGeometryPrivate() )
77 {
78  d->geometry.reset( geom );
79  d->ref = QAtomicInt( 1 );
80 }
81 
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 
90  : d( other.d )
91 {
92  mLastError = other.mLastError;
93  d->ref.ref();
94 }
95 
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 
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 
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 
135 {
136  return d->geometry.get();
137 }
138 
140 {
141  detach();
142  return d->geometry.get();
143 }
144 
146 {
147  if ( d->geometry.get() == geometry )
148  {
149  return;
150  }
151 
152  reset( std::unique_ptr< QgsAbstractGeometry >( geometry ) );
153 }
154 
156 {
157  return !d->geometry;
158 }
159 
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 
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 
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 
191 {
192  return QgsGeometry( std::make_unique< QgsLineString >( polyline ) );
193 }
194 
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 
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 
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 
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 
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 
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 
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 
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 
345 void QgsGeometry::fromWkb( const QByteArray &wkb )
346 {
347  QgsConstWkbPtr ptr( wkb );
348  reset( QgsGeometryFactory::geomFromWkb( ptr ) );
349 }
350 
352 {
353  if ( !d->geometry )
354  {
355  return QgsWkbTypes::Unknown;
356  }
357  else
358  {
359  return d->geometry->wkbType();
360  }
361 }
362 
363 
365 {
366  if ( !d->geometry )
367  {
369  }
370  return static_cast< QgsWkbTypes::GeometryType >( QgsWkbTypes::geometryType( d->geometry->wkbType() ) );
371 }
372 
374 {
375  if ( !d->geometry )
376  {
377  return true;
378  }
379 
380  return d->geometry->isEmpty();
381 }
382 
384 {
385  if ( !d->geometry )
386  {
387  return false;
388  }
389  return QgsWkbTypes::isMultiType( d->geometry->wkbType() );
390 }
391 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 
419 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 
435 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 
451 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 
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 
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 
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 
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 
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 
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 
677 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 
693 double QgsGeometry::sqrDistToVertexAt( QgsPointXY &point, int atVertex ) const
694 {
695  QgsPointXY vertexPoint = vertexAt( atVertex );
696  return QgsGeometryUtils::sqrDistance2D( QgsPoint( vertexPoint ), QgsPoint( point ) );
697 }
698 
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 
715 {
716  // avoid calling geos for trivial point-to-point line calculations
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 
729 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 
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 
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 
776 {
777  std::unique_ptr< QgsCurve > r( ring );
778  if ( !d->geometry )
779  {
781  }
782 
783  detach();
784 
785  return QgsGeometryEditUtils::addRing( d->geometry.get(), std::move( r ) );
786 }
787 
789 {
791  convertPointList( points, l );
792  return addPart( l, geomType );
793 }
794 
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 
812 {
813  std::unique_ptr< QgsAbstractGeometry > p( part );
814  if ( !d->geometry )
815  {
816  switch ( geomType )
817  {
819  reset( std::make_unique< QgsMultiPoint >() );
820  break;
822  reset( std::make_unique< QgsMultiLineString >() );
823  break;
825  reset( std::make_unique< QgsMultiPolygon >() );
826  break;
827  default:
828  reset( nullptr );
830  }
831  }
832  else
833  {
834  detach();
835  }
836 
838  return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
839 }
840 
842 {
843  if ( !d->geometry )
844  {
846  }
847  if ( newPart.isNull() || !newPart.d->geometry )
848  {
850  }
851 
852  return addPart( newPart.d->geometry->clone() );
853 }
854 
855 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 
891 Qgis::GeometryOperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
892 {
893  if ( !d->geometry )
894  {
896  }
897 
898  detach();
899 
900  d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
902 }
903 
905 {
906  if ( !d->geometry )
907  {
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 );
918 }
919 
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 }
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  {
934  }
935 
936  QVector<QgsGeometry > newGeoms;
937  QgsLineString splitLineString( splitLine );
938 
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  {
977  //default: do not implement default to handle properly all cases
978  }
979 
980  // this should never be reached
981  Q_ASSERT( false );
983 }
984 
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 
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 
1006 {
1007  if ( !d->geometry )
1008  {
1010  }
1011 
1012  QgsGeos geos( d->geometry.get() );
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 ) );
1020  }
1021 
1022  switch ( errorCode )
1023  {
1034  case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1038  }
1039 
1040  // should not be reached
1042 }
1043 
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 
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 
1086 {
1087  if ( d->geometry )
1088  {
1089  return d->geometry->boundingBox();
1090  }
1091  return QgsRectangle();
1092 }
1093 
1094 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 
1105 {
1106  double area, angle, width, height;
1107  return orientedMinimumBoundingBox( area, angle, width, height );
1108 }
1109 
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 
1158 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 
1185 QgsGeometry QgsGeometry::minimalEnclosingCircle( unsigned int segments ) const
1186 {
1187  QgsPointXY center;
1188  double radius;
1189  return minimalEnclosingCircle( center, radius, segments );
1190 
1191 }
1192 
1193 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 
1200 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 
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 
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 
1234 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 
1246 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 
1257 {
1258  if ( !d->geometry || geometry.isNull() )
1259  {
1260  return false;
1261  }
1262 
1263  return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1264 }
1265 
1266 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 
1279 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 
1291 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 
1303 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 
1322 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 
1334 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 
1346 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 
1358 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 
1370 QString QgsGeometry::asWkt( int precision ) const
1371 {
1372  if ( !d->geometry )
1373  {
1374  return QString();
1375  }
1376  return d->geometry->asWkt( precision );
1377 }
1378 
1379 QString QgsGeometry::asJson( int precision ) const
1380 {
1381  return QString::fromStdString( asJsonObject( precision ).dump() );
1382 }
1383 
1385 {
1386  if ( !d->geometry )
1387  {
1388  return nullptr;
1389  }
1390  return d->geometry->asJsonObject( precision );
1391 
1392 }
1393 
1394 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
1415  {
1416  newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1417  }
1418 
1419  // polygon -> line
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
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  {
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
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 
1518 {
1519  switch ( destType )
1520  {
1522  return convertToPoint( destMultipart );
1523 
1525  return convertToLine( destMultipart );
1526 
1528  return convertToPolygon( destMultipart );
1529 
1530  default:
1531  return QgsGeometry();
1532  }
1533 }
1534 
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 
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 
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  {
1599  resGeom = std::make_unique<QgsMultiPoint>();
1600  break;
1602  resGeom = std::make_unique<QgsMultiLineString>();
1603  break;
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 
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 
1642 {
1643  QgsPolylineXY polyLine;
1644  if ( !d->geometry )
1645  {
1646  return polyLine;
1647  }
1648 
1649  bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CompoundCurve
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 
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 
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 
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 
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 
1843 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 
1866 double QgsGeometry::length() const
1867 {
1868  if ( !d->geometry )
1869  {
1870  return -1.0;
1871  }
1872 
1873  // avoid calling geos for trivial geometry calculations
1875  {
1876  return d->geometry->length();
1877  }
1878 
1879  QgsGeos g( d->geometry.get() );
1880  mLastError.clear();
1881  return g.length( &mLastError );
1882 }
1883 
1884 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
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 
1902 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 
1914 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 
1927 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 
1939 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 
1952 {
1953  if ( !d->geometry || d->geometry.get()->isEmpty() )
1955  return d->geometry->vertices_begin();
1956 }
1957 
1959 {
1960  if ( !d->geometry || d->geometry.get()->isEmpty() )
1962  return d->geometry->vertices_end();
1963 }
1964 
1966 {
1967  if ( !d->geometry || d->geometry.get()->isEmpty() )
1968  return QgsVertexIterator();
1969  return QgsVertexIterator( d->geometry.get() );
1970 }
1971 
1973 {
1974  if ( !d->geometry )
1976 
1977  detach();
1978  return d->geometry->parts_begin();
1979 }
1980 
1982 {
1983  if ( !d->geometry )
1985  return d->geometry->parts_end();
1986 }
1987 
1989 {
1990  if ( !d->geometry )
1992  return d->geometry->const_parts_begin();
1993 }
1994 
1996 {
1997  if ( !d->geometry )
1999  return d->geometry->const_parts_end();
2000 }
2001 
2003 {
2004  if ( !d->geometry )
2005  return QgsGeometryPartIterator();
2006 
2007  detach();
2008  return QgsGeometryPartIterator( d->geometry.get() );
2009 }
2010 
2012 {
2013  if ( !d->geometry )
2015 
2016  return QgsGeometryConstPartIterator( d->geometry.get() );
2017 }
2018 
2019 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 
2038 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 
2057 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 
2115 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 
2159 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 
2167 {
2168  QgsInternalGeometryEngine engine( *this );
2169 
2170  return engine.variableWidthBufferByM( segments );
2171 }
2172 
2173 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 
2213 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 
2232 QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2233 {
2234  QgsInternalGeometryEngine engine( *this );
2235 
2236  return engine.densifyByCount( extraNodesPerSegment );
2237 }
2238 
2240 {
2241  QgsInternalGeometryEngine engine( *this );
2242 
2243  return engine.densifyByDistance( distance );
2244 }
2245 
2246 QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2247 {
2248  QgsInternalGeometryEngine engine( *this );
2249 
2250  return engine.convertToCurves( distanceTolerance, angleTolerance );
2251 }
2252 
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 
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 
2292 QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2293 {
2294  QgsInternalGeometryEngine engine( *this );
2295 
2296  return engine.poleOfInaccessibility( precision, distanceToBoundary );
2297 }
2298 
2299 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 
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 
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 
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 
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 
2375 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 
2389 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 
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 
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 
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 
2458 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 
2508 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;
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 
2527 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;
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 
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 
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 
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 
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 
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 
2682 QgsGeometry QgsGeometry::extrude( double x, double y )
2683 {
2684  QgsInternalGeometryEngine engine( *this );
2685 
2686  return engine.extrude( x, y );
2687 }
2688 
2690 
2691 QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
2692 {
2694  return QVector< QgsPointXY >();
2695 
2696  return QgsInternalGeometryEngine::randomPointsInPolygon( *this, count, acceptPoint, seed, feedback, maxTriesPerPoint );
2697 }
2698 
2699 QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
2700 {
2702  return QVector< QgsPointXY >();
2703 
2704  return QgsInternalGeometryEngine::randomPointsInPolygon( *this, count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
2705 }
2707 
2708 int QgsGeometry::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
2709 {
2710  return d->geometry ? d->geometry->wkbSize( flags ) : 0;
2711 }
2712 
2713 QByteArray QgsGeometry::asWkb( QgsAbstractGeometry::WkbFlags flags ) const
2714 {
2715  return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
2716 }
2717 
2718 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 
2744 QPointF QgsGeometry::asQPointF() const
2745 {
2746  QgsPointXY point = asPoint();
2747  return point.toQPointF();
2748 }
2749 
2750 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 
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 
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 
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 
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 
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 
2886 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
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 
2927 {
2928  if ( !d->geometry )
2929  {
2930  return;
2931  }
2932 
2933  detach();
2934  d->geometry->normalize();
2935 }
2936 
2937 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 
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 
2957 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 
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 
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 
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 
3028 {
3030  {
3031  return;
3032  }
3033 
3034  std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3035  reset( std::move( straightGeom ) );
3036 }
3037 
3039 {
3040  if ( !d->geometry )
3041  {
3042  return false;
3043  }
3044 
3045  return d->geometry->hasCurvedSegments();
3046 }
3047 
3049 {
3050  if ( !d->geometry )
3051  {
3053  }
3054 
3055  detach();
3056  d->geometry->transform( ct, direction, transformZ );
3058 }
3059 
3060 Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3061 {
3062  if ( !d->geometry )
3063  {
3065  }
3066 
3067  detach();
3068  d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3070 }
3071 
3073 {
3074  if ( d->geometry )
3075  {
3076  detach();
3077  d->geometry->transform( mtp.transform() );
3078  }
3079 }
3080 
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 
3100 void QgsGeometry::draw( QPainter &p ) const
3101 {
3102  if ( d->geometry )
3103  {
3104  d->geometry->draw( p );
3105  }
3106 }
3107 
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  for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3117  {
3118  const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3119 
3120  // count total number of vertices in the part
3121  int numPoints = 0;
3122  for ( int k = 0; k < part->ringCount(); ++k )
3123  numPoints += part->vertexCount( 0, k );
3124 
3125  if ( vertexIndex < numPoints )
3126  {
3127  int nothing;
3128  return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3129  }
3130  vertexIndex -= numPoints;
3131  partIndex++;
3132  }
3133  }
3134  else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3135  {
3136  const QgsCurve *ring = curvePolygon->exteriorRing();
3137  if ( vertexIndex < ring->numPoints() )
3138  {
3139  partIndex = 0;
3140  ringIndex = 0;
3141  vertex = vertexIndex;
3142  return true;
3143  }
3144  vertexIndex -= ring->numPoints();
3145  ringIndex = 1;
3146  for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
3147  {
3148  const QgsCurve *ring = curvePolygon->interiorRing( i );
3149  if ( vertexIndex < ring->numPoints() )
3150  {
3151  partIndex = 0;
3152  vertex = vertexIndex;
3153  return true;
3154  }
3155  vertexIndex -= ring->numPoints();
3156  ringIndex += 1;
3157  }
3158  }
3159  else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3160  {
3161  if ( vertexIndex < curve->numPoints() )
3162  {
3163  partIndex = 0;
3164  ringIndex = 0;
3165  vertex = vertexIndex;
3166  return true;
3167  }
3168  }
3169  else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
3170  {
3171  if ( vertexIndex == 0 )
3172  {
3173  partIndex = 0;
3174  ringIndex = 0;
3175  vertex = 0;
3176  return true;
3177  }
3178  }
3179 
3180  return false;
3181 }
3182 
3184 {
3185  if ( !d->geometry )
3186  {
3187  return false;
3188  }
3189 
3190  id.type = Qgis::VertexType::Segment;
3191 
3192  bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
3193  if ( !res )
3194  return false;
3195 
3196  // now let's find out if it is a straight or circular segment
3197  const QgsAbstractGeometry *g = d->geometry.get();
3198  if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3199  {
3200  g = geomCollection->geometryN( id.part );
3201  }
3202 
3203  if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3204  {
3205  g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
3206  }
3207 
3208  if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3209  {
3210  QgsPoint p;
3211  res = curve->pointAt( id.vertex, p, id.type );
3212  if ( !res )
3213  return false;
3214  }
3215 
3216  return true;
3217 }
3218 
3220 {
3221  if ( !d->geometry )
3222  {
3223  return -1;
3224  }
3225  return d->geometry->vertexNumberFromVertexId( id );
3226 }
3227 
3228 QString QgsGeometry::lastError() const
3229 {
3230  return mLastError;
3231 }
3232 
3233 void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
3234 {
3235  if ( !d->geometry )
3236  return;
3237 
3238  detach();
3239 
3240  d->geometry->filterVertices( filter );
3241 }
3242 
3243 void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
3244 {
3245  if ( !d->geometry )
3246  return;
3247 
3248  detach();
3249 
3250  d->geometry->transformVertices( transform );
3251 }
3252 
3253 void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
3254 {
3255  output.clear();
3256  for ( const QgsPointXY &p : input )
3257  {
3258  output.append( QgsPoint( p ) );
3259  }
3260 }
3261 
3262 void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
3263 {
3264  output.clear();
3265  for ( const QgsPoint &p : input )
3266  {
3267  output.append( QgsPointXY( p.x(), p.y() ) );
3268  }
3269 }
3270 
3271 void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
3272 {
3273  output.clear();
3274 
3275  auto convertRing = []( const QgsCurve * ring ) -> QgsPolylineXY
3276  {
3277  QgsPolylineXY res;
3278  bool doSegmentation = ( QgsWkbTypes::flatType( ring->wkbType() ) == QgsWkbTypes::CompoundCurve
3280  std::unique_ptr< QgsLineString > segmentizedLine;
3281  const QgsLineString *line = nullptr;
3282  if ( doSegmentation )
3283  {
3284  segmentizedLine.reset( ring->curveToLine() );
3285  line = segmentizedLine.get();
3286  }
3287  else
3288  {
3289  line = qgsgeometry_cast<const QgsLineString *>( ring );
3290  if ( !line )
3291  {
3292  return res;
3293  }
3294  }
3295 
3296  int nVertices = line->numPoints();
3297  res.resize( nVertices );
3298  QgsPointXY *data = res.data();
3299  const double *xData = line->xData();
3300  const double *yData = line->yData();
3301  for ( int i = 0; i < nVertices; ++i )
3302  {
3303  data->setX( *xData++ );
3304  data->setY( *yData++ );
3305  data++;
3306  }
3307  return res;
3308  };
3309 
3310  if ( const QgsCurve *exterior = input.exteriorRing() )
3311  {
3312  output.push_back( convertRing( exterior ) );
3313  }
3314 
3315  const int interiorRingCount = input.numInteriorRings();
3316  output.reserve( output.size() + interiorRingCount );
3317  for ( int n = 0; n < interiorRingCount; ++n )
3318  {
3319  output.push_back( convertRing( input.interiorRing( n ) ) );
3320  }
3321 }
3322 
3324 {
3325  return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
3326 }
3327 
3328 QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
3329 {
3330  std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
3331 
3332  if ( polygon.isClosed() )
3333  {
3334  std::unique_ptr< QgsPolygon > poly = std::make_unique< QgsPolygon >();
3335  poly->setExteriorRing( ring.release() );
3336  return QgsGeometry( std::move( poly ) );
3337  }
3338  else
3339  {
3340  return QgsGeometry( std::move( ring ) );
3341  }
3342 }
3343 
3345 {
3347  QgsPolygonXY result;
3348  result << createPolylineFromQPolygonF( polygon );
3349  return result;
3351 }
3352 
3354 {
3355  QgsPolylineXY result;
3356  result.reserve( polygon.count() );
3357  for ( const QPointF &p : polygon )
3358  {
3359  result.append( QgsPointXY( p ) );
3360  }
3361  return result;
3362 }
3363 
3364 bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
3365 {
3366  if ( p1.count() != p2.count() )
3367  return false;
3368 
3369  for ( int i = 0; i < p1.count(); ++i )
3370  {
3371  if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
3372  return false;
3373  }
3374  return true;
3375 }
3376 
3377 bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
3378 {
3379  if ( p1.count() != p2.count() )
3380  return false;
3381 
3382  for ( int i = 0; i < p1.count(); ++i )
3383  {
3384  if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3385  return false;
3386  }
3387  return true;
3388 }
3389 
3390 
3391 bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
3392 {
3393  if ( p1.count() != p2.count() )
3394  return false;
3395 
3396  for ( int i = 0; i < p1.count(); ++i )
3397  {
3398  if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3399  return false;
3400  }
3401  return true;
3402 }
3403 
3404 QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3405 {
3406  if ( !d->geometry || d->geometry->isEmpty() )
3407  return QgsGeometry();
3408 
3409  QgsGeometry geom = *this;
3411  geom = QgsGeometry( d->geometry->segmentize() );
3412 
3413  switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
3414  {
3415  case QgsWkbTypes::Point:
3417  //can't smooth a point based geometry
3418  return geom;
3419 
3421  {
3422  const QgsLineString *lineString = qgsgeometry_cast< const QgsLineString * >( geom.constGet() );
3423  return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
3424  }
3425 
3427  {
3428  const QgsMultiLineString *multiLine = qgsgeometry_cast< const QgsMultiLineString * >( geom.constGet() );
3429 
3430  std::unique_ptr< QgsMultiLineString > resultMultiline = std::make_unique< QgsMultiLineString> ();
3431  resultMultiline->reserve( multiLine->numGeometries() );
3432  for ( int i = 0; i < multiLine->numGeometries(); ++i )
3433  {
3434  resultMultiline->addGeometry( smoothLine( *( multiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3435  }
3436  return QgsGeometry( std::move( resultMultiline ) );
3437  }
3438 
3439  case QgsWkbTypes::Polygon:
3440  {
3441  const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( geom.constGet() );
3442  return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
3443  }
3444 
3446  {
3447  const QgsMultiPolygon *multiPoly = qgsgeometry_cast< const QgsMultiPolygon * >( geom.constGet() );
3448 
3449  std::unique_ptr< QgsMultiPolygon > resultMultiPoly = std::make_unique< QgsMultiPolygon >();
3450  resultMultiPoly->reserve( multiPoly->numGeometries() );
3451  for ( int i = 0; i < multiPoly->numGeometries(); ++i )
3452  {
3453  resultMultiPoly->addGeometry( smoothPolygon( *( multiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3454  }
3455  return QgsGeometry( std::move( resultMultiPoly ) );
3456  }
3457 
3458  case QgsWkbTypes::Unknown:
3459  default:
3460  return QgsGeometry( *this );
3461  }
3462 }
3463 
3464 std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations,
3465  const double offset, double squareDistThreshold, double maxAngleRads,
3466  bool isRing )
3467 {
3468  std::unique_ptr< QgsLineString > result = std::make_unique< QgsLineString >( line );
3469  QgsPointSequence outputLine;
3470  for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
3471  {
3472  outputLine.resize( 0 );
3473  outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
3474  bool skipFirst = false;
3475  bool skipLast = false;
3476  if ( isRing )
3477  {
3478  QgsPoint p1 = result->pointN( result->numPoints() - 2 );
3479  QgsPoint p2 = result->pointN( 0 );
3480  QgsPoint p3 = result->pointN( 1 );
3481  double angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3482  p3.x(), p3.y() );
3483  angle = std::fabs( M_PI - angle );
3484  skipFirst = angle > maxAngleRads;
3485  }
3486  for ( int i = 0; i < result->numPoints() - 1; i++ )
3487  {
3488  QgsPoint p1 = result->pointN( i );
3489  QgsPoint p2 = result->pointN( i + 1 );
3490 
3491  double angle = M_PI;
3492  if ( i == 0 && isRing )
3493  {
3494  QgsPoint p3 = result->pointN( result->numPoints() - 2 );
3495  angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3496  p3.x(), p3.y() );
3497  }
3498  else if ( i < result->numPoints() - 2 )
3499  {
3500  QgsPoint p3 = result->pointN( i + 2 );
3501  angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3502  p3.x(), p3.y() );
3503  }
3504  else if ( i == result->numPoints() - 2 && isRing )
3505  {
3506  QgsPoint p3 = result->pointN( 1 );
3507  angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3508  p3.x(), p3.y() );
3509  }
3510 
3511  skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
3512 
3513  // don't apply distance threshold to first or last segment
3514  if ( i == 0 || i >= result->numPoints() - 2
3515  || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
3516  {
3517  if ( !isRing )
3518  {
3519  if ( !skipFirst )
3520  outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
3521  if ( !skipLast )
3522  outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
3523  else
3524  outputLine << p2;
3525  }
3526  else
3527  {
3528  // ring
3529  if ( !skipFirst )
3530  outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
3531  else if ( i == 0 )
3532  outputLine << p1;
3533  if ( !skipLast )
3534  outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
3535  else
3536  outputLine << p2;
3537  }
3538  }
3539  skipFirst = skipLast;
3540  }
3541 
3542  if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
3543  outputLine << outputLine.at( 0 );
3544 
3545  result->setPoints( outputLine );
3546  }
3547  return result;
3548 }
3549 
3550 std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3551 {
3552  double maxAngleRads = maxAngle * M_PI / 180.0;
3553  double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
3554  return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
3555 }
3556 
3557 std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3558 {
3559  double maxAngleRads = maxAngle * M_PI / 180.0;
3560  double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
3561  std::unique_ptr< QgsPolygon > resultPoly = std::make_unique< QgsPolygon >();
3562 
3563  resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset,
3564  squareDistThreshold, maxAngleRads, true ).release() );
3565 
3566  for ( int i = 0; i < polygon.numInteriorRings(); ++i )
3567  {
3568  resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset,
3569  squareDistThreshold, maxAngleRads, true ).release() );
3570  }
3571  return resultPoly;
3572 }
3573 
3574 QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
3575 {
3576  switch ( type() )
3577  {
3579  {
3580  bool srcIsMultipart = isMultipart();
3581 
3582  if ( ( destMultipart && srcIsMultipart ) ||
3583  ( !destMultipart && !srcIsMultipart ) )
3584  {
3585  // return a copy of the same geom
3586  return QgsGeometry( *this );
3587  }
3588  if ( destMultipart )
3589  {
3590  // layer is multipart => make a multipoint with a single point
3591  return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
3592  }
3593  else
3594  {
3595  // destination is singlepart => make a single part if possible
3596  QgsMultiPointXY multiPoint = asMultiPoint();
3597  if ( multiPoint.count() == 1 )
3598  {
3599  return fromPointXY( multiPoint[0] );
3600  }
3601  }
3602  return QgsGeometry();
3603  }
3604 
3606  {
3607  // only possible if destination is multipart
3608  if ( !destMultipart )
3609  return QgsGeometry();
3610 
3611  // input geometry is multipart
3612  if ( isMultipart() )
3613  {
3614  const QgsMultiPolylineXY multiLine = asMultiPolyline();
3615  QgsMultiPointXY multiPoint;
3616  for ( const QgsPolylineXY &l : multiLine )
3617  for ( const QgsPointXY &p : l )
3618  multiPoint << p;
3619  return fromMultiPointXY( multiPoint );
3620  }
3621  // input geometry is not multipart: copy directly the line into a multipoint
3622  else
3623  {
3624  QgsPolylineXY line = asPolyline();
3625  if ( !line.isEmpty() )
3626  return fromMultiPointXY( line );
3627  }
3628  return QgsGeometry();
3629  }
3630 
3632  {
3633  // can only transform if destination is multipoint
3634  if ( !destMultipart )
3635  return QgsGeometry();
3636 
3637  // input geometry is multipart: make a multipoint from multipolygon
3638  if ( isMultipart() )
3639  {
3640  const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3641  QgsMultiPointXY multiPoint;
3642  for ( const QgsPolygonXY &poly : multiPolygon )
3643  for ( const QgsPolylineXY &line : poly )
3644  for ( const QgsPointXY &pt : line )
3645  multiPoint << pt;
3646  return fromMultiPointXY( multiPoint );
3647  }
3648  // input geometry is not multipart: make a multipoint from polygon
3649  else
3650  {
3651  const QgsPolygonXY polygon = asPolygon();
3652  QgsMultiPointXY multiPoint;
3653  for ( const QgsPolylineXY &line : polygon )
3654  for ( const QgsPointXY &pt : line )
3655  multiPoint << pt;
3656  return fromMultiPointXY( multiPoint );
3657  }
3658  }
3659 
3660  default:
3661  return QgsGeometry();
3662  }
3663 }
3664 
3665 QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
3666 {
3667  switch ( type() )
3668  {
3670  {
3671  if ( !isMultipart() )
3672  return QgsGeometry();
3673 
3674  QgsMultiPointXY multiPoint = asMultiPoint();
3675  if ( multiPoint.count() < 2 )
3676  return QgsGeometry();
3677 
3678  if ( destMultipart )
3679  return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
3680  else
3681  return fromPolylineXY( multiPoint );
3682  }
3683 
3685  {
3686  bool srcIsMultipart = isMultipart();
3687 
3688  if ( ( destMultipart && srcIsMultipart ) ||
3689  ( !destMultipart && ! srcIsMultipart ) )
3690  {
3691  // return a copy of the same geom
3692  return QgsGeometry( *this );
3693  }
3694  if ( destMultipart )
3695  {
3696  // destination is multipart => makes a multipoint with a single line
3697  QgsPolylineXY line = asPolyline();
3698  if ( !line.isEmpty() )
3699  return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
3700  }
3701  else
3702  {
3703  // destination is singlepart => make a single part if possible
3704  QgsMultiPolylineXY multiLine = asMultiPolyline();
3705  if ( multiLine.count() == 1 )
3706  return fromPolylineXY( multiLine[0] );
3707  }
3708  return QgsGeometry();
3709  }
3710 
3712  {
3713  // input geometry is multipolygon
3714  if ( isMultipart() )
3715  {
3716  const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3717  QgsMultiPolylineXY multiLine;
3718  for ( const QgsPolygonXY &poly : multiPolygon )
3719  for ( const QgsPolylineXY &line : poly )
3720  multiLine << line;
3721 
3722  if ( destMultipart )
3723  {
3724  // destination is multipart
3725  return fromMultiPolylineXY( multiLine );
3726  }
3727  else if ( multiLine.count() == 1 )
3728  {
3729  // destination is singlepart => make a single part if possible
3730  return fromPolylineXY( multiLine[0] );
3731  }
3732  }
3733  // input geometry is single polygon
3734  else
3735  {
3736  QgsPolygonXY polygon = asPolygon();
3737  // if polygon has rings
3738  if ( polygon.count() > 1 )
3739  {
3740  // cannot fit a polygon with rings in a single line layer
3741  // TODO: would it be better to remove rings?
3742  if ( destMultipart )
3743  {
3744  const QgsPolygonXY polygon = asPolygon();
3745  QgsMultiPolylineXY multiLine;
3746  multiLine.reserve( polygon.count() );
3747  for ( const QgsPolylineXY &line : polygon )
3748  multiLine << line;
3749  return fromMultiPolylineXY( multiLine );
3750  }
3751  }
3752  // no rings
3753  else if ( polygon.count() == 1 )
3754  {
3755  if ( destMultipart )
3756  {
3757  return fromMultiPolylineXY( polygon );
3758  }
3759  else
3760  {
3761  return fromPolylineXY( polygon[0] );
3762  }
3763  }
3764  }
3765  return QgsGeometry();
3766  }
3767 
3768  default:
3769  return QgsGeometry();
3770  }
3771 }
3772 
3773 QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
3774 {
3775  switch ( type() )
3776  {
3778  {
3779  if ( !isMultipart() )
3780  return QgsGeometry();
3781 
3782  QgsMultiPointXY multiPoint = asMultiPoint();
3783  if ( multiPoint.count() < 3 )
3784  return QgsGeometry();
3785 
3786  if ( multiPoint.last() != multiPoint.first() )
3787  multiPoint << multiPoint.first();
3788 
3789  QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
3790  if ( destMultipart )
3791  return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
3792  else
3793  return fromPolygonXY( polygon );
3794  }
3795 
3797  {
3798  // input geometry is multiline
3799  if ( isMultipart() )
3800  {
3801  QgsMultiPolylineXY multiLine = asMultiPolyline();
3802  QgsMultiPolygonXY multiPolygon;
3803  for ( QgsMultiPolylineXY::iterator multiLineIt = multiLine.begin(); multiLineIt != multiLine.end(); ++multiLineIt )
3804  {
3805  // do not create polygon for a 1 segment line
3806  if ( ( *multiLineIt ).count() < 3 )
3807  return QgsGeometry();
3808  if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
3809  return QgsGeometry();
3810 
3811  // add closing node
3812  if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
3813  *multiLineIt << ( *multiLineIt ).first();
3814  multiPolygon << ( QgsPolygonXY() << *multiLineIt );
3815  }
3816  // check that polygons were inserted
3817  if ( !multiPolygon.isEmpty() )
3818  {
3819  if ( destMultipart )
3820  {
3821  return fromMultiPolygonXY( multiPolygon );
3822  }
3823  else if ( multiPolygon.count() == 1 )
3824  {
3825  // destination is singlepart => make a single part if possible
3826  return fromPolygonXY( multiPolygon[0] );
3827  }
3828  }
3829  }
3830  // input geometry is single line
3831  else
3832  {
3833  QgsPolylineXY line = asPolyline();
3834 
3835  // do not create polygon for a 1 segment line
3836  if ( line.count() < 3 )
3837  return QgsGeometry();
3838  if ( line.count() == 3 && line.first() == line.last() )
3839  return QgsGeometry();
3840 
3841  // add closing node
3842  if ( line.first() != line.last() )
3843  line << line.first();
3844 
3845  // destination is multipart
3846  if ( destMultipart )
3847  {
3848  return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
3849  }
3850  else
3851  {
3852  return fromPolygonXY( QgsPolygonXY() << line );
3853  }
3854  }
3855  return QgsGeometry();
3856  }
3857 
3859  {
3860  bool srcIsMultipart = isMultipart();
3861 
3862  if ( ( destMultipart && srcIsMultipart ) ||
3863  ( !destMultipart && ! srcIsMultipart ) )
3864  {
3865  // return a copy of the same geom
3866  return QgsGeometry( *this );
3867  }
3868  if ( destMultipart )
3869  {
3870  // destination is multipart => makes a multipoint with a single polygon
3871  QgsPolygonXY polygon = asPolygon();
3872  if ( !polygon.isEmpty() )
3873  return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
3874  }
3875  else
3876  {
3877  QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3878  if ( multiPolygon.count() == 1 )
3879  {
3880  // destination is singlepart => make a single part if possible
3881  return fromPolygonXY( multiPolygon[0] );
3882  }
3883  }
3884  return QgsGeometry();
3885  }
3886 
3887  default:
3888  return QgsGeometry();
3889  }
3890 }
3891 
3893 {
3894  return new QgsGeos( geometry );
3895 }
3896 
3897 QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
3898 {
3899  out << geometry.asWkb();
3900  return out;
3901 }
3902 
3903 QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
3904 {
3905  QByteArray byteArray;
3906  in >> byteArray;
3907  if ( byteArray.isEmpty() )
3908  {
3909  geometry.set( nullptr );
3910  return in;
3911  }
3912 
3913  geometry.fromWkb( byteArray );
3914  return in;
3915 }
3916 
3917 
3919 {
3920  return mMessage;
3921 }
3922 
3924 {
3925  return mLocation;
3926 }
3927 
3929 {
3930  return mHasLocation;
3931 }
BufferSide
Side of line to buffer.
Definition: qgis.h:699
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:647
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
@ Success
Operation succeeded.
@ AddPartNotMultiGeometry
The source geometry is not multi.
@ SplitCannotSplitPoint
Cannot split points.
@ GeometryEngineError
Geometry engine misses a method implemented or an error occurred in the geometry engine.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
GeometryValidationEngine
Available engines for validating geometries.
Definition: qgis.h:687
JoinStyle
Join styles for buffers.
Definition: qgis.h:724
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:711
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:896
The part_iterator class provides STL-style iterator for const references to geometry parts.
The part_iterator class provides STL-style iterator for geometry parts.
The vertex_iterator class provides STL-style iterator for vertices.
Abstract base class for all geometries.
virtual int ringCount(int part=0) const =0
Returns the number of rings of which this geometry is built.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
virtual void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const =0
Returns the vertices adjacent to a specified vertex within a geometry.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
virtual double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Circle geometry type.
Definition: qgscircle.h:44
double radius() const SIP_HOLDGIL
Returns the radius of the circle.
Definition: qgscircle.h:311
static QgsCircle from2Points(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Constructs a circle by 2 points on the circle.
Definition: qgscircle.cpp:38
bool contains(const QgsPoint &point, double epsilon=1E-8) const
Returns true if the circle contains the point.
Definition: qgscircle.cpp:453
static QgsCircle minimalCircleFrom3Points(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon=1E-8) SIP_HOLDGIL
Constructs the smallest circle from 3 points.
Definition: qgscircle.cpp:326
QgsCircularString * toCircularString(bool oriented=false) const
Returns a circular string from the circle.
Definition: qgscircle.cpp:430
Circular string geometry type.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
Compound curve geometry type.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
A const WKB pointer.
Definition: qgswkbptr.h:138
Class for doing transforms between two map coordinate systems.
Curve polygon geometry type.
virtual QgsPolygon * toPolygon(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a new polygon geometry corresponding to a segmentized approximation of the curve.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership)
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
virtual QgsPoint * interpolatePoint(double distance) const =0
Returns an interpolated point on the curve at the specified distance.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
Orientation
Curve orientation.
Definition: qgscurve.h:265
QgsPoint center() const SIP_HOLDGIL
Returns the center point.
Definition: qgsellipse.h:121
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
Definition: qgsellipse.cpp:224
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
Geometry collection.
virtual bool insertGeometry(QgsAbstractGeometry *g, int index)
Inserts a geometry before a specified index and takes ownership.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
Java-style iterator for const traversal of parts of a geometry.
static Qgis::GeometryOperationResult addRing(QgsAbstractGeometry *geometry, std::unique_ptr< QgsCurve > ring)
Add an interior ring to a geometry.
static std::unique_ptr< QgsAbstractGeometry > avoidIntersections(const QgsAbstractGeometry &geom, const QList< QgsVectorLayer * > &avoidIntersectionsLayers, bool &haveInvalidGeometry, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Alters a geometry so that it avoids intersections with features from all open vector layers.
static bool deletePart(QgsAbstractGeometry *geom, int partNum)
Deletes a part from a geometry.
static bool deleteRing(QgsAbstractGeometry *geom, int ringNum, int partNum=0)
Deletes a ring from a geometry.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
A geometry engine is a low-level representation of a QgsAbstractGeometry object, optimised for use wi...
EngineOperationResult
Success or failure of a geometry operation.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
@ InvalidInput
The input is not valid.
@ NodedGeometryError
Error occurred while creating a noded geometry.
@ EngineError
Error occurred in the geometry engine.
@ SplitCannotSplitPoint
Points cannot be split.
@ Success
Operation succeeded.
@ MethodNotImplemented
Method not implemented in geometry engine.
static std::unique_ptr< QgsMultiPolygon > fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Construct geometry from a multipolygon.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
static std::unique_ptr< QgsAbstractGeometry > fromPolylineXY(const QgsPolylineXY &polyline)
Construct geometry from a polyline.
static std::unique_ptr< QgsMultiPoint > fromMultiPointXY(const QgsMultiPointXY &multipoint)
Construct geometry from a multipoint.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkt(const QString &text)
Construct geometry from a WKT string.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(QgsWkbTypes::Type t)
Returns empty geometry from wkb type.
static std::unique_ptr< QgsMultiLineString > fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Construct geometry from a multipolyline.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(QgsWkbTypes::Type type)
Returns a new geometry collection matching a specified WKB type.
static std::unique_ptr< QgsAbstractGeometry > fromPointXY(const QgsPointXY &point)
Construct geometry from a point.
static std::unique_ptr< QgsPolygon > fromPolygonXY(const QgsPolygonXY &polygon)
Construct geometry from a polygon.
Java-style iterator for traversal of parts of a geometry.
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns the squared 2D distance between two points.
static bool verticesAtDistance(const QgsAbstractGeometry &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
Retrieves the vertices which are before and after the interpolated point at a specified distance alon...
static double distanceToVertex(const QgsAbstractGeometry &geom, QgsVertexId id)
Returns the distance along a geometry from its first vertex to the specified vertex.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double lineAngle(double x1, double y1, double x2, double y2) SIP_HOLDGIL
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double angleBetweenThreePoints(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates the angle between the lines AB and BC, where AB and BC described by points a,...
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
static QgsPointXY interpolatePointOnLine(double x1, double y1, double x2, double y2, double fraction) SIP_HOLDGIL
Interpolates the position of a point a fraction of the way along the line from (x1,...
static void validateGeometry(const QgsGeometry &geometry, QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal)
Validate geometry and produce a list of geometry errors.
A geometry error.
Definition: qgsgeometry.h:2249
bool hasWhere() const
true if the location available from
QgsPointXY where() const
The coordinates at which the error is located and should be visualized.
QString what() const
A human readable error message containing details about the error.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
QgsGeometry() SIP_HOLDGIL
Constructor.
Definition: qgsgeometry.cpp:64
bool deleteRing(int ringNum, int partNum=0)
Deletes a ring in polygon or multipolygon.
QVector< QgsPointXY > randomPointsInPolygon(int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr, int maxTriesPerPoint=0) const
Returns a list of count random points generated inside a (multi)polygon geometry (if acceptPoint is s...
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
QgsGeometry clipped(const QgsRectangle &rectangle)
Clips the geometry using the specified rectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
int makeDifferenceInPlace(const QgsGeometry &other)
Changes this geometry such that it does not intersect the other geometry.
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
void adjacentVertices(int atVertex, int &beforeVertex, int &afterVertex) const
Returns the indexes of the vertices before and after the given vertex index.
QgsMultiPolygonXY asMultiPolygon() const
Returns the contents of the geometry as a multi-polygon.
bool deleteVertex(int atVertex)
Deletes the vertex at the given position number and item (first number is index 0)
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
static bool compare(const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compares two polylines for equality within a specified tolerance.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsGeometry largestEmptyCircle(double tolerance, const QgsGeometry &boundary=QgsGeometry()) const SIP_THROW(QgsNotSupportedException)
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry difference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other.
static QgsGeometry polygonize(const QVector< QgsGeometry > &geometries)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
static QgsGeometry fromQPointF(QPointF point) SIP_HOLDGIL
Construct geometry from a QPointF.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform)
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
QgsGeometry makeDifference(const QgsGeometry &other) const
Returns the geometry formed by modifying this geometry such that it does not intersect the other geom...
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QVector< QgsGeometry > coerceToType(QgsWkbTypes::Type type) const
Attempts to coerce this geometry into the specified destination type.
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
Qgis::GeometryOperationResult addPart(const QVector< QgsPointXY > &points, QgsWkbTypes::GeometryType geomType=QgsWkbTypes::UnknownGeometry)
Adds a new part to a the geometry.
void normalize()
Reorganizes the geometry into a normalized form (or "canonical" form).
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Returns the length of the QByteArray returned by asWkb()
QgsGeometry minimumWidth() const SIP_THROW(QgsNotSupportedException)
Returns a linestring geometry which represents the minimum diameter of the geometry.
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QgsGeometry combine(const QgsGeometry &geometry) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static Q_DECL_DEPRECATED QgsPolylineXY createPolylineFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolylineXY from a QPolygonF.
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
double minimumClearance() const SIP_THROW(QgsNotSupportedException)
Computes the minimum clearance of a geometry.
double sqrDistToVertexAt(QgsPointXY &point SIP_IN, int atVertex) const
Returns the squared Cartesian distance between the given point to the given vertex index (vertex at t...
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry convertToType(QgsWkbTypes::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
QgsAbstractGeometry::part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QgsGeometry snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const
Returns a new geometry with all points or vertices snapped to the closest point of the grid.
QgsGeometry minimumClearanceLine() const SIP_THROW(QgsNotSupportedException)
Returns a LineString whose endpoints define the minimum clearance of a geometry.
virtual json asJsonObject(int precision=17) const
Exports the geometry to a json object.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter)
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
bool insertVertex(double x, double y, int beforeVertex)
Insert a new vertex before the given vertex index, ring and item (first number is index 0) If the req...
static Q_DECL_DEPRECATED QgsPolygonXY createPolygonFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolygonXYfrom a QPolygonF.
bool convertToSingleType()
Converts multi type geometry into single type geometry e.g.
Qgis::GeometryOperationResult addRing(const QVector< QgsPointXY > &ring)
Adds a new ring to this geometry.
QPointF asQPointF() const SIP_HOLDGIL
Returns contents of the geometry as a QPointF if wkbType is WKBPoint, otherwise returns a null QPoint...
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
Creates a new geometry from a QgsPointXY object.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:128
bool requiresConversionToStraightSegments() const
Returns true if the geometry is a curved geometry type which requires conversion to display as straig...
bool isSimple() const
Determines whether the geometry is simple (according to OGC definition), i.e.
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new LineString geometry from a list of QgsPoint points.
void validateGeometry(QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Validates geometry and produces a list of geometry errors.
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
double frechetDistanceDensify(const QgsGeometry &geom, double densifyFraction) const SIP_THROW(QgsNotSupportedException)
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
void convertToStraightSegment(double tolerance=M_PI/180., QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle)
Converts the geometry to straight line segments, if it is a curved geometry type.
double area() const
Returns the planar, 2-dimensional area of the geometry.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
QgsGeometry & operator=(QgsGeometry const &rhs)
Creates a deep copy of the object.
Definition: qgsgeometry.cpp:96
QgsGeometry orthogonalize(double tolerance=1.0E-8, int maxIterations=1000, double angleThreshold=15.0) const
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries angl...
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QgsGeometry makeValid() const
Attempts to make an invalid geometry valid without losing vertices.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QString lastError() const SIP_HOLDGIL
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometryPartIterator parts()
Returns Java-style iterator for traversal of parts of the geometry.
QPolygonF asQPolygonF() const SIP_HOLDGIL
Returns contents of the geometry as a QPolygonF.
QgsGeometry convertToCurves(double distanceTolerance=1e-8, double angleTolerance=1e-8) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
QgsGeometry voronoiDiagram(const QgsGeometry &extent=QgsGeometry(), double tolerance=0.0, bool edgesOnly=false) const
Creates a Voronoi diagram for the nodes contained within the geometry.
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
virtual ~QgsGeometry()
Definition: qgsgeometry.cpp:69
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygonXY.
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsGeometry symDifference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry node() const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false)
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
QgsAbstractGeometry::part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QString asJson(int precision=17) const
Exports the geometry to a GeoJSON string.
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
QgsGeometry orientedMinimumBoundingBox() const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
QgsGeometryConstPartIterator constParts() const
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
Qgis::GeometryOperationResult reshapeGeometry(const QgsLineString &reshapeLineString)
Replaces a part of this geometry with another line.
double closestVertexWithContext(const QgsPointXY &point, int &atVertex) const
Searches for the closest vertex in this geometry to the given point.
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false) const
Returns the Delaunay triangulation for the vertices of the geometry.
void draw(QPainter &p) const
Draws the geometry onto a QPainter.
bool convertGeometryCollectionToSubclass(QgsWkbTypes::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
double frechetDistance(const QgsGeometry &geom) const SIP_THROW(QgsNotSupportedException)
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QString asWkt(int precision=17) const
Exports the geometry to WKT.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
bool toggleCircularAtVertex(int atVertex)
Converts the vertex at the given position from/to circular.
bool moveVertex(double x, double y, int atVertex)
Moves the vertex at the given position number and item (first number is index 0) to the given coordin...
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
QgsGeometry subdivide(int maxNodes=256) const
Subdivides the geometry.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool deletePart(int partNum)
Deletes part identified by the part number.
QgsGeometry removeInteriorRings(double minimumAllowedArea=-1) const
Removes the interior rings from a (multi)polygon geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
int avoidIntersections(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:104
double hausdorffDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:548
double hausdorffDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:525
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1684
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition: qgsgeos.cpp:444
double frechetDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
Definition: qgsgeos.cpp:571
double area(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:730
double length(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:747
double frechetDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
Definition: qgsgeos.cpp:600
This class offers geometry processing methods.
QgsGeometry poleOfInaccessibility(double precision, double *distanceFromBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer using the m-values from a (multi)line geometry.
QgsGeometry extrude(double x, double y) const
Will extrude a line or (segmentized) curve by a given offset and return a polygon representation of i...
QgsGeometry orthogonalize(double tolerance=1.0E-8, int maxIterations=1000, double angleThreshold=15.0) const
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries angl...
QString lastError() const
Returns an error string referring to the last error encountered.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a tapered width buffer for a (multi)curve geometry.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Densifies the geometry by adding the specified number of extra nodes within each segment of the geome...
static QVector< QgsPointXY > randomPointsInPolygon(const QgsGeometry &polygon, int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr, int maxTriesPerPoint=0)
Returns a list of count random points generated inside a polygon geometry (if acceptPoint is specifie...
QgsGeometry convertToCurves(double distanceTolerance, double angleTolerance) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
const double * yData() const
Returns a const pointer to the y vertex data.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
bool dropMValue() override
Drops any measure values which exist in the geometry.
const double * xData() const
Returns a const pointer to the x vertex data.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:90
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
Definition: qgsmultipoint.h:30
QgsPoint * pointN(int index)
Returns the point with the specified index.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
A class to represent a 2D point.
Definition: qgspointxy.h:59
void setX(double x) SIP_HOLDGIL
Sets the x value of the point.
Definition: qgspointxy.h:122
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
void setY(double y) SIP_HOLDGIL
Sets the y value of the point.
Definition: qgspointxy.h:132
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:169
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
Definition: qgspoint.h:280
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:735
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:34
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
Surface geometry type.
Definition: qgssurface.h:34
Represents a vector layer which manages a vector based data sets.
Java-style iterator for traversal of vertices of a geometry.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
static bool isMultiType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:862
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static bool isCurvedType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:911
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static Type multiType(Type type) SIP_HOLDGIL
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:304
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
Contains geos related utilities and functions.
Definition: qgsgeos.h:42
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:1742
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1741
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1246
QVector< QgsPoint > QgsPointSequence
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QDataStream & operator>>(QDataStream &in, QgsGeometry &geometry)
Reads a geometry from stream in into geometry. QGIS version compatibility is not guaranteed.
QDataStream & operator<<(QDataStream &out, const QgsGeometry &geometry)
Writes the geometry to stream out. QGIS version compatibility is not guaranteed.
std::unique_ptr< QgsLineString > smoothCurve(const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing)
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item.
Definition: qgsgeometry.h:76
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:86
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:82
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:52
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition: qgsgeometry.h:93
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition: qgsgeometry.h:72
int precision
std::unique_ptr< QgsAbstractGeometry > geometry
Definition: qgsgeometry.cpp:61
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
bool isValid() const SIP_HOLDGIL
Returns true if the vertex id is valid.
Definition: qgsvertexid.h:46