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