QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsgeometrycollection.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometrycollection.cpp
3 -------------------------------------------------------------------
4Date : 28 Oct 2014
5Copyright : (C) 2014 by Marco Hugentobler
6email : marco.hugentobler at sourcepole dot com
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
17#include "qgsapplication.h"
18#include "qgsgeometryfactory.h"
19#include "qgsgeometryutils.h"
20#include "qgscircularstring.h"
21#include "qgscompoundcurve.h"
22#include "qgslinestring.h"
23#include "qgsmultilinestring.h"
24#include "qgspoint.h"
25#include "qgsmultipoint.h"
26#include "qgspolygon.h"
27#include "qgsmultipolygon.h"
28#include "qgswkbptr.h"
29#include "qgsgeos.h"
30#include "qgsfeedback.h"
31
32#include <nlohmann/json.hpp>
33#include <memory>
34
36{
38}
39
42 mBoundingBox( c.mBoundingBox ),
43 mHasCachedValidity( c.mHasCachedValidity ),
44 mValidityFailureReason( c.mValidityFailureReason )
45{
46 int nGeoms = c.mGeometries.size();
47 mGeometries.resize( nGeoms );
48 for ( int i = 0; i < nGeoms; ++i )
49 {
50 mGeometries[i] = c.mGeometries.at( i )->clone();
51 }
52}
53
55{
56 if ( &c != this )
57 {
58 clearCache();
60 int nGeoms = c.mGeometries.size();
61 mGeometries.resize( nGeoms );
62 for ( int i = 0; i < nGeoms; ++i )
63 {
64 mGeometries[i] = c.mGeometries.at( i )->clone();
65 }
66 }
67 return *this;
68}
69
71{
72 clear();
73}
74
76{
77 const QgsGeometryCollection *otherCollection = qgsgeometry_cast< const QgsGeometryCollection * >( &other );
78 if ( !otherCollection )
79 return false;
80
81 if ( mWkbType != otherCollection->mWkbType )
82 return false;
83
84 if ( mGeometries.count() != otherCollection->mGeometries.count() )
85 return false;
86
87 for ( int i = 0; i < mGeometries.count(); ++i )
88 {
89 QgsAbstractGeometry *g1 = mGeometries.at( i );
90 QgsAbstractGeometry *g2 = otherCollection->mGeometries.at( i );
91
92 // Quick check if the geometries are exactly the same
93 if ( g1 != g2 )
94 {
95 if ( !g1 || !g2 )
96 return false;
97
98 // Slower check, compare the contents of the geometries
99 if ( *g1 != *g2 )
100 return false;
101 }
102 }
103
104 return true;
105}
106
108{
109 return !operator==( other );
110}
111
113{
114 auto result = std::make_unique< QgsGeometryCollection >();
115 result->mWkbType = mWkbType;
116 return result.release();
117}
118
120{
121 return new QgsGeometryCollection( *this );
122}
123
125{
126 qDeleteAll( mGeometries );
127 mGeometries.clear();
128 clearCache(); //set bounding box invalid
129}
130
131QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
132{
133 std::unique_ptr<QgsGeometryCollection> result;
134
135 for ( auto geom : mGeometries )
136 {
137 std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) };
138 if ( gridified )
139 {
140 if ( !result )
141 result = std::unique_ptr<QgsGeometryCollection> { createEmptyWithSameType() };
142
143 result->mGeometries.append( gridified.release() );
144 }
145 }
146
147 return result.release();
148}
149
150bool QgsGeometryCollection::removeDuplicateNodes( double epsilon, bool useZValues )
151{
152 bool result = false;
153 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
154 {
155 if ( geom->removeDuplicateNodes( epsilon, useZValues ) ) result = true;
156 }
157 return result;
158}
159
161{
162 return nullptr;
163}
164
165void QgsGeometryCollection::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
166{
167 if ( vertex.part < 0 || vertex.part >= mGeometries.count() )
168 {
169 previousVertex = QgsVertexId();
171 return;
172 }
173
174 mGeometries.at( vertex.part )->adjacentVertices( vertex, previousVertex, nextVertex );
175}
176
178{
179 if ( id.part < 0 || id.part >= mGeometries.count() )
180 return -1;
181
182 int number = 0;
183 int part = 0;
184 for ( QgsAbstractGeometry *geometry : mGeometries )
185 {
186 if ( part == id.part )
187 {
188 int partNumber = geometry->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
189 if ( partNumber == -1 )
190 return -1;
191 return number + partNumber;
192 }
193 else
194 {
195 number += geometry->nCoordinates();
196 }
197
198 part++;
199 }
200 return -1; // should not happen
201}
202
204{
205 if ( mGeometries.empty() )
206 return false;
207
208 // if we already have the bounding box calculated, then this check is trivial!
209 if ( !mBoundingBox.isNull() )
210 {
211 return mBoundingBox.intersects( rectangle );
212 }
213
214 // otherwise loop through each member geometry and test the bounding box intersection.
215 // This gives us a chance to use optimisations which may be present on the individual
216 // geometry subclasses, and at worst it will cause a calculation of the bounding box
217 // of each individual member geometry which we would have to do anyway... (and these
218 // bounding boxes are cached, so would be reused without additional expense)
219 for ( const QgsAbstractGeometry *geometry : mGeometries )
220 {
221 if ( geometry->boundingBoxIntersects( rectangle ) )
222 return true;
223 }
224
225 // even if we don't intersect the bounding box of any member geometries, we may still intersect the
226 // bounding box of the overall collection.
227 // so here we fall back to the non-optimised base class check which has to first calculate
228 // the overall bounding box of the collection..
230}
231
233{
234 mGeometries.reserve( size );
235}
236
238{
239 clearCache();
240 return mGeometries.value( n );
241}
242
244{
245 if ( mGeometries.isEmpty() )
246 return true;
247
248 for ( QgsAbstractGeometry *geometry : mGeometries )
249 {
250 if ( !geometry->isEmpty() )
251 return false;
252 }
253 return true;
254}
255
257{
258 if ( !g )
259 {
260 return false;
261 }
262
263 mGeometries.append( g );
264 clearCache(); //set bounding box invalid
265 return true;
266}
267
269{
270 if ( !g )
271 {
272 return false;
273 }
274
275 index = std::min( static_cast<int>( mGeometries.count() ), index );
276
277 mGeometries.insert( index, g );
278 clearCache(); //set bounding box invalid
279 return true;
280}
281
283{
284 if ( nr >= mGeometries.size() || nr < 0 )
285 {
286 return false;
287 }
288 delete mGeometries.at( nr );
289 mGeometries.remove( nr );
290 clearCache(); //set bounding box invalid
291 return true;
292}
293
295{
296 for ( QgsAbstractGeometry *geometry : std::as_const( mGeometries ) )
297 {
298 geometry->normalize();
299 }
300 std::sort( mGeometries.begin(), mGeometries.end(), []( const QgsAbstractGeometry * a, const QgsAbstractGeometry * b )
301 {
302 return a->compareTo( b ) > 0;
303 } );
304}
305
307{
308 int maxDim = 0;
309 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
310 for ( ; it != mGeometries.constEnd(); ++it )
311 {
312 int dim = ( *it )->dimension();
313 if ( dim > maxDim )
314 {
315 maxDim = dim;
316 }
317 }
318 return maxDim;
319}
320
322{
323 return QStringLiteral( "GeometryCollection" );
324}
325
327{
328 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
329 {
330 g->transform( ct, d, transformZ );
331 }
332 clearCache(); //set bounding box invalid
333}
334
335void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
336{
337 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
338 {
339 g->transform( t, zTranslate, zScale, mTranslate, mScale );
340 }
341 clearCache(); //set bounding box invalid
342}
343
344void QgsGeometryCollection::draw( QPainter &p ) const
345{
346 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
347 for ( ; it != mGeometries.constEnd(); ++it )
348 {
349 ( *it )->draw( p );
350 }
351}
352
354{
355 QPainterPath p;
356 for ( const QgsAbstractGeometry *geom : mGeometries )
357 {
358 QPainterPath partPath = geom->asQPainterPath();
359 if ( !partPath.isEmpty() )
360 p.addPath( partPath );
361 }
362 return p;
363}
364
366{
367 if ( !wkbPtr )
368 {
369 return false;
370 }
371
374 return false;
375
377
378 int nGeometries = 0;
379 wkbPtr >> nGeometries;
380
381 QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
382 mGeometries.clear();
383 mGeometries.reserve( nGeometries );
384 for ( int i = 0; i < nGeometries; ++i )
385 {
386 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
387 if ( geom )
388 {
389 if ( !addGeometry( geom.release() ) )
390 {
391 qDeleteAll( mGeometries );
392 mGeometries = geometryListBackup;
393 return false;
394 }
395 }
396 }
397 qDeleteAll( geometryListBackup );
398
399 clearCache(); //set bounding box invalid
400
401 return true;
402}
403
404bool QgsGeometryCollection::fromWkt( const QString &wkt )
405{
406 return fromCollectionWkt( wkt, QVector<QgsAbstractGeometry *>() << new QgsPoint << new QgsLineString << new QgsPolygon
408 << new QgsCurvePolygon
411 << new QgsMultiCurve << new QgsMultiSurface, QStringLiteral( "GeometryCollection" ) );
412}
413
414int QgsGeometryCollection::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
415{
416 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
417 for ( const QgsAbstractGeometry *geom : mGeometries )
418 {
419 if ( geom )
420 {
421 binarySize += geom->wkbSize( flags );
422 }
423 }
424
425 return binarySize;
426}
427
428QByteArray QgsGeometryCollection::asWkb( WkbFlags flags ) const
429{
430 int countNonNull = 0;
431 for ( const QgsAbstractGeometry *geom : mGeometries )
432 {
433 if ( geom )
434 {
435 countNonNull ++;
436 }
437 }
438
439 QByteArray wkbArray;
440 wkbArray.resize( QgsGeometryCollection::wkbSize( flags ) );
441 QgsWkbPtr wkb( wkbArray );
442 wkb << static_cast<char>( QgsApplication::endian() );
443 wkb << static_cast<quint32>( wkbType() );
444 wkb << static_cast<quint32>( countNonNull );
445 for ( const QgsAbstractGeometry *geom : mGeometries )
446 {
447 if ( geom )
448 {
449 wkb << geom->asWkb( flags );
450 }
451 }
452 return wkbArray;
453}
454
456{
457 QString wkt = wktTypeStr();
458
459 if ( isEmpty() )
460 wkt += QLatin1String( " EMPTY" );
461 else
462 {
463 wkt += QLatin1String( " (" );
464 for ( const QgsAbstractGeometry *geom : mGeometries )
465 {
466 QString childWkt = geom->asWkt( precision );
467 if ( wktOmitChildType() )
468 {
469 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
470 }
471 wkt += childWkt + ',';
472 }
473 if ( wkt.endsWith( ',' ) )
474 {
475 wkt.chop( 1 ); // Remove last ','
476 }
477 wkt += ')';
478 }
479 return wkt;
480}
481
482QDomElement QgsGeometryCollection::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
483{
484 QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
485 for ( const QgsAbstractGeometry *geom : mGeometries )
486 {
487 QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
488 elemGeometryMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
489 elemMultiGeometry.appendChild( elemGeometryMember );
490 }
491 return elemMultiGeometry;
492}
493
494QDomElement QgsGeometryCollection::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
495{
496 QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
497 for ( const QgsAbstractGeometry *geom : mGeometries )
498 {
499 QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
500 elemGeometryMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
501 elemMultiGeometry.appendChild( elemGeometryMember );
502 }
503 return elemMultiGeometry;
504}
505
507{
508 json coordinates( json::array( ) );
509 for ( const QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
510 {
511 coordinates.push_back( geom->asJsonObject( precision ) );
512 }
513 return
514 {
515 { "type", "GeometryCollection" },
516 { "geometries", coordinates }
517 };
518}
519
521{
522 QString kml;
523 kml.append( QLatin1String( "<MultiGeometry>" ) );
524 const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
525 for ( const QgsAbstractGeometry *geometry : geometries )
526 {
527 kml.append( geometry->asKml( precision ) );
528 }
529 kml.append( QLatin1String( "</MultiGeometry>" ) );
530 return kml;
531}
532
534{
535 if ( mBoundingBox.isNull() )
536 {
537 mBoundingBox = calculateBoundingBox();
538 }
539 return mBoundingBox;
540}
541
543{
544 if ( mGeometries.empty() )
545 {
546 return QgsRectangle();
547 }
548
549 QgsRectangle bbox = mGeometries.at( 0 )->boundingBox();
550 for ( int i = 1; i < mGeometries.size(); ++i )
551 {
552 if ( mGeometries.at( i )->isEmpty() )
553 continue;
554
555 QgsRectangle geomBox = mGeometries.at( i )->boundingBox();
556 if ( bbox.isNull() )
557 {
558 // workaround treatment of a QgsRectangle(0,0,0,0) as a "null"/invalid rectangle
559 // if bbox is null, then the first geometry must have returned a bounding box of (0,0,0,0)
560 // so just manually include that as a point... ew.
561 geomBox.combineExtentWith( QPointF( 0, 0 ) );
562 bbox = geomBox;
563 }
564 else if ( geomBox.isNull() )
565 {
566 // ...as above... this part must have a bounding box of (0,0,0,0).
567 // if we try to combine the extent with this "null" box it will just be ignored.
568 bbox.combineExtentWith( QPointF( 0, 0 ) );
569 }
570 else
571 {
572 bbox.combineExtentWith( geomBox );
573 }
574 }
575 return bbox;
576}
577
579{
580 mBoundingBox = QgsRectangle();
581 mHasCachedValidity = false;
582 mValidityFailureReason.clear();
584}
585
587{
588 QgsCoordinateSequence sequence;
589 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
590 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
591 {
592 QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
593
594 QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
595 for ( ; cIt != geomCoords.constEnd(); ++cIt )
596 {
597 sequence.push_back( *cIt );
598 }
599 }
600
601 return sequence;
602}
603
605{
606 int count = 0;
607
608 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
609 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
610 {
611 count += ( *geomIt )->nCoordinates();
612 }
613
614 return count;
615}
616
617double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
618{
619 return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
620}
621
623{
624 if ( id.part < 0 )
625 {
626 id.part = 0;
627 id.ring = -1;
628 id.vertex = -1;
629 }
630 if ( mGeometries.isEmpty() )
631 {
632 return false;
633 }
634
635 if ( id.part >= mGeometries.count() )
636 return false;
637
638 QgsAbstractGeometry *geom = mGeometries.at( id.part );
639 if ( geom->nextVertex( id, vertex ) )
640 {
641 return true;
642 }
643 if ( ( id.part + 1 ) >= numGeometries() )
644 {
645 return false;
646 }
647 ++id.part;
648 id.ring = -1;
649 id.vertex = -1;
650 return mGeometries.at( id.part )->nextVertex( id, vertex );
651}
652
654{
655 if ( position.part >= mGeometries.size() )
656 {
657 return false;
658 }
659
660 bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
661 if ( success )
662 {
663 clearCache(); //set bounding box invalid
664 }
665 return success;
666}
667
669{
670 if ( position.part < 0 || position.part >= mGeometries.size() )
671 {
672 return false;
673 }
674
675 bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
676 if ( success )
677 {
678 clearCache(); //set bounding box invalid
679 }
680 return success;
681}
682
684{
685 if ( position.part < 0 || position.part >= mGeometries.size() )
686 {
687 return false;
688 }
689
690 QgsAbstractGeometry *geom = mGeometries.at( position.part );
691 if ( !geom )
692 {
693 return false;
694 }
695
696 bool success = geom->deleteVertex( position );
697
698 //remove geometry if no vertices left
699 if ( geom->isEmpty() )
700 {
701 removeGeometry( position.part );
702 }
703
704 if ( success )
705 {
706 clearCache(); //set bounding box invalid
707 }
708 return success;
709}
710
712{
713 double length = 0.0;
714 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
715 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
716 {
717 length += ( *geomIt )->length();
718 }
719 return length;
720}
721
723{
724 double area = 0.0;
725 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
726 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
727 {
728 area += ( *geomIt )->area();
729 }
730 return area;
731}
732
734{
735 double perimeter = 0.0;
736 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
737 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
738 {
739 perimeter += ( *geomIt )->perimeter();
740 }
741 return perimeter;
742}
743
744bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<QgsAbstractGeometry *> &subtypes, const QString &defaultChildWkbType )
745{
746 clear();
747
748 QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
749
751 {
752 qDeleteAll( subtypes );
753 return false;
754 }
755 mWkbType = parts.first;
756
757 QString secondWithoutParentheses = parts.second;
758 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
759 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
760 secondWithoutParentheses.isEmpty() )
761 return true;
762
763 QString defChildWkbType = QStringLiteral( "%1%2%3 " ).arg( defaultChildWkbType, is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
764
765 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
766 for ( const QString &childWkt : blocks )
767 {
768 QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
769
770 bool success = false;
771 for ( const QgsAbstractGeometry *geom : subtypes )
772 {
773 if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( geom->wkbType() ) )
774 {
775 mGeometries.append( geom->clone() );
776 if ( mGeometries.back()->fromWkt( childWkt ) )
777 {
778 success = true;
779 break;
780 }
781 }
782 }
783 if ( !success )
784 {
785 clear();
786 qDeleteAll( subtypes );
787 return false;
788 }
789 }
790 qDeleteAll( subtypes );
791
792 //scan through geometries and check if dimensionality of geometries is different to collection.
793 //if so, update the type dimensionality of the collection to match
794 bool hasZ = false;
795 bool hasM = false;
796 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
797 {
798 hasZ = hasZ || geom->is3D();
799 hasM = hasM || geom->isMeasure();
800 if ( hasZ && hasM )
801 break;
802 }
803 if ( hasZ )
804 addZValue( 0 );
805 if ( hasM )
806 addMValue( 0 );
807
808 return true;
809}
810
812{
813 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
814 for ( ; it != mGeometries.constEnd(); ++it )
815 {
816 if ( ( *it )->hasCurvedSegments() )
817 {
818 return true;
819 }
820 }
821 return false;
822}
823
825{
826 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
827 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
828 if ( !geomCollection )
829 {
830 return clone();
831 }
832
833 geomCollection->reserve( mGeometries.size() );
834 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
835 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
836 {
837 geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
838 }
839 return geom.release();
840}
841
843{
844 if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
845 {
846 return 0.0;
847 }
848
849 QgsAbstractGeometry *geom = mGeometries[vertex.part];
850 if ( !geom )
851 {
852 return 0.0;
853 }
854
855 return geom->vertexAngle( vertex );
856}
857
859{
860 if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
861 {
862 return 0.0;
863 }
864
865 const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
866 if ( !geom )
867 {
868 return 0.0;
869 }
870
871 return geom->segmentLength( startVertex );
872}
873
874int QgsGeometryCollection::vertexCount( int part, int ring ) const
875{
876 if ( part < 0 || part >= mGeometries.size() )
877 {
878 return 0;
879 }
880
881 return mGeometries[part]->vertexCount( 0, ring );
882}
883
885{
886 if ( part < 0 || part >= mGeometries.size() )
887 {
888 return 0;
889 }
890
891 return mGeometries[part]->ringCount();
892}
893
895{
896 return mGeometries.size();
897}
898
900{
901 return mGeometries[id.part]->vertexAt( id );
902}
903
904bool QgsGeometryCollection::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
905{
906 if ( flags == 0 && mHasCachedValidity )
907 {
908 // use cached validity results
909 error = mValidityFailureReason;
910 return error.isEmpty();
911 }
912
913 QgsGeos geos( this );
914 bool res = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
915 if ( flags == 0 )
916 {
917 mValidityFailureReason = !res ? error : QString();
918 mHasCachedValidity = true;
919 }
920 return res;
921}
922
924{
926 return false;
927
929
930 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
931 {
932 geom->addZValue( zValue );
933 }
934 clearCache();
935 return true;
936}
937
939{
941 return false;
942
944
945 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
946 {
947 geom->addMValue( mValue );
948 }
949 clearCache();
950 return true;
951}
952
953
955{
957 return false;
958
960 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
961 {
962 geom->dropZValue();
963 }
964 clearCache();
965 return true;
966}
967
969{
971 return false;
972
974 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
975 {
976 geom->dropMValue();
977 }
978 clearCache();
979 return true;
980}
981
982void QgsGeometryCollection::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
983{
984 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
985 {
986 if ( geom )
987 geom->filterVertices( filter );
988 }
989 clearCache();
990}
991
992void QgsGeometryCollection::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
993{
994 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
995 {
996 if ( geom )
997 geom->transformVertices( transform );
998 }
999 clearCache();
1000}
1001
1003{
1004 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1005 {
1006 if ( geom )
1007 geom->swapXy();
1008 }
1009 clearCache();
1010}
1011
1013{
1014 std::unique_ptr< QgsGeometryCollection > newCollection( new QgsGeometryCollection() );
1015 newCollection->reserve( mGeometries.size() );
1016 for ( QgsAbstractGeometry *geom : mGeometries )
1017 {
1018 newCollection->addGeometry( geom->toCurveType() );
1019 }
1020 return newCollection.release();
1021}
1022
1024{
1025 if ( mGeometries.size() == 1 )
1026 return mGeometries.at( 0 )->simplifiedTypeRef();
1027 else
1028 return this;
1029}
1030
1032{
1033 if ( !transformer )
1034 return false;
1035
1036 bool res = true;
1037 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1038 {
1039 if ( geom )
1040 res = geom->transform( transformer, feedback );
1041
1042 if ( feedback && feedback->isCanceled() )
1043 res = false;
1044
1045 if ( !res )
1046 break;
1047 }
1048 clearCache();
1049 return res;
1050}
1051
1053{
1054 return false;
1055}
1056
1058{
1059 return mGeometries.count();
1060}
1061
1063{
1064 if ( index < 0 || index > mGeometries.count() )
1065 return nullptr;
1066 return mGeometries.at( index );
1067}
1068
1070{
1071 const QgsGeometryCollection *otherCollection = qgsgeometry_cast<const QgsGeometryCollection *>( other );
1072 if ( !otherCollection )
1073 return -1;
1074
1075 int i = 0;
1076 int j = 0;
1077 while ( i < mGeometries.size() && j < otherCollection->mGeometries.size() )
1078 {
1079 const QgsAbstractGeometry *aGeom = mGeometries[i];
1080 const QgsAbstractGeometry *bGeom = otherCollection->mGeometries[j];
1081 const int comparison = aGeom->compareTo( bGeom );
1082 if ( comparison != 0 )
1083 {
1084 return comparison;
1085 }
1086 i++;
1087 j++;
1088 }
1089 if ( i < mGeometries.size() )
1090 {
1091 return 1;
1092 }
1093 if ( j < otherCollection->mGeometries.size() )
1094 {
1095 return -1;
1096 }
1097 return 0;
1098}
TransformDirection
Flags for raster layer temporal capabilities.
Definition: qgis.h:1320
An abstract base class for classes which transform geometries by transforming input points to output ...
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
virtual void clearCache() const
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
virtual bool isEmpty() const
Returns true if the geometry is empty.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
virtual int compareTo(const QgsAbstractGeometry *other) const
Comparator for sorting of geometry.
QgsWkbTypes::Type mWkbType
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
static endian_t endian()
Returns whether this machine uses big or little endian.
Circular string geometry type.
Compound curve geometry type.
A const WKB pointer.
Definition: qgswkbptr.h:138
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
Class for doing transforms between two map coordinate systems.
Curve polygon geometry type.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Geometry collection.
QgsGeometryCollection * toCurveType() const override
Returns the geometry converted to the more generic curve type.
bool dropMValue() override
Drops any measure values which exist in the geometry.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
QVector< QgsAbstractGeometry * > mGeometries
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
QgsGeometryCollection * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
const QgsAbstractGeometry * simplifiedTypeRef() const override SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
QgsAbstractGeometry * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
QgsGeometryCollection & operator=(const QgsGeometryCollection &c)
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
virtual bool insertGeometry(QgsAbstractGeometry *g, int index)
Inserts a geometry before a specified index and takes ownership.
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
double perimeter() const override SIP_HOLDGIL
Returns the planar, 2-dimensional perimeter of the geometry.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
void reserve(int size) SIP_HOLDGIL
Attempts to allocate memory for at least size geometries.
QgsGeometryCollection() SIP_HOLDGIL
Constructor for an empty geometry collection.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
void normalize() final SIP_HOLDGIL
Reorganizes the geometry into a normalized form (or "canonical" form).
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
bool operator==(const QgsAbstractGeometry &other) const override
QgsGeometryCollection * clone() const override
Clones the geometry by performing a deep copy.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
void swapXy() override
Swaps the x and y coordinates from the geometry.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
int partCount() const override
Returns count of parts contained in the geometry.
bool hasCurvedSegments() const override SIP_HOLDGIL
Returns true if the geometry contains curved segments.
virtual bool wktOmitChildType() const
Returns whether child type names are omitted from Wkt representations of the collection.
bool fromCollectionWkt(const QString &wkt, const QVector< QgsAbstractGeometry * > &subtypes, const QString &defaultChildWkbType=QString())
Reads a collection from a WKT string.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
bool operator!=(const QgsAbstractGeometry &other) const override
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(QgsWkbTypes::Type t)
Returns empty geometry from wkb type.
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:99
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
Multi curve geometry collection.
Definition: qgsmulticurve.h:30
Multi line string geometry collection.
Multi point geometry collection.
Definition: qgsmultipoint.h:30
Multi polygon geometry collection.
Multi surface geometry collection.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Polygon geometry type.
Definition: qgspolygon.h:34
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool intersects(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle intersects with other rectangle.
Definition: qgsrectangle.h:349
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
WKB pointer handler.
Definition: qgswkbptr.h:44
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
static Type linearType(Type type) SIP_HOLDGIL
Returns the linear type for a WKB type.
Definition: qgswkbtypes.h:622
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
@ GeometryCollection
Definition: qgswkbtypes.h:79
static Type dropZ(Type type) SIP_HOLDGIL
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1237
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1201
static Type dropM(Type type) SIP_HOLDGIL
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1255
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
Definition: MathUtils.cpp:292
Contains geos related utilities and functions.
Definition: qgsgeos.h:37
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QVector< QgsRingSequence > QgsCoordinateSequence
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int part
Part number.
Definition: qgsvertexid.h:89