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