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