QGIS API Documentation 3.43.0-Master (6c62b930b02)
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 "qgsbox3d.h"
19#include "qgsgeometryfactory.h"
20#include "qgsgeometryutils.h"
21#include "qgslinestring.h"
22#include "qgsmultilinestring.h"
23#include "qgspoint.h"
24#include "qgsmultipoint.h"
25#include "qgsmultipolygon.h"
26#include "qgswkbptr.h"
27#include "qgsgeos.h"
28#include "qgsfeedback.h"
29
30#include <nlohmann/json.hpp>
31#include <memory>
32
37
40 mBoundingBox( c.mBoundingBox ),
41 mHasCachedValidity( c.mHasCachedValidity ),
42 mValidityFailureReason( c.mValidityFailureReason )
43{
44 int nGeoms = c.mGeometries.size();
45 mGeometries.resize( nGeoms );
46 for ( int i = 0; i < nGeoms; ++i )
47 {
48 mGeometries[i] = c.mGeometries.at( i )->clone();
49 }
50}
51
52// cppcheck-suppress operatorEqVarError
54{
55 if ( &c != this )
56 {
58 int nGeoms = c.mGeometries.size();
59 mGeometries.resize( nGeoms );
60 for ( int i = 0; i < nGeoms; ++i )
61 {
62 mGeometries[i] = c.mGeometries.at( i )->clone();
63 }
64 }
65 return *this;
66}
67
72
74{
75 auto result = std::make_unique< QgsGeometryCollection >();
76 result->mWkbType = mWkbType;
77 return result.release();
78}
79
84
86{
87 qDeleteAll( mGeometries );
88 mGeometries.clear();
89 clearCache(); //set bounding box invalid
90}
91
92QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
93{
94 std::unique_ptr<QgsGeometryCollection> result;
95
96 for ( auto geom : mGeometries )
97 {
98 std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) };
99 if ( gridified )
100 {
101 if ( !result )
102 result = std::unique_ptr<QgsGeometryCollection> { createEmptyWithSameType() };
103
104 result->mGeometries.append( gridified.release() );
105 }
106 }
107
108 return result.release();
109}
110
111bool QgsGeometryCollection::removeDuplicateNodes( double epsilon, bool useZValues )
112{
113 bool result = false;
114 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
115 {
116 if ( geom->removeDuplicateNodes( epsilon, useZValues ) ) result = true;
117 }
118 return result;
119}
120
122{
123 return nullptr;
124}
125
126void QgsGeometryCollection::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
127{
128 if ( vertex.part < 0 || vertex.part >= mGeometries.count() )
129 {
130 previousVertex = QgsVertexId();
132 return;
133 }
134
135 mGeometries.at( vertex.part )->adjacentVertices( vertex, previousVertex, nextVertex );
136}
137
139{
140 if ( id.part < 0 || id.part >= mGeometries.count() )
141 return -1;
142
143 int number = 0;
144 int part = 0;
145 for ( QgsAbstractGeometry *geometry : mGeometries )
146 {
147 if ( part == id.part )
148 {
149 int partNumber = geometry->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
150 if ( partNumber == -1 )
151 return -1;
152 return number + partNumber;
153 }
154 else
155 {
156 number += geometry->nCoordinates();
157 }
158
159 part++;
160 }
161 return -1; // should not happen
162}
163
165{
166 if ( mGeometries.empty() )
167 return false;
168
169 // if we already have the bounding box calculated, then this check is trivial!
170 if ( !mBoundingBox.isNull() )
171 {
172 return mBoundingBox.intersects( box3d );
173 }
174
175 // otherwise loop through each member geometry and test the bounding box intersection.
176 // This gives us a chance to use optimisations which may be present on the individual
177 // geometry subclasses, and at worst it will cause a calculation of the bounding box
178 // of each individual member geometry which we would have to do anyway... (and these
179 // bounding boxes are cached, so would be reused without additional expense)
180 for ( const QgsAbstractGeometry *geometry : mGeometries )
181 {
182 if ( geometry->boundingBoxIntersects( box3d ) )
183 return true;
184 }
185
186 // even if we don't intersect the bounding box of any member geometries, we may still intersect the
187 // bounding box of the overall collection.
188 // so here we fall back to the non-optimised base class check which has to first calculate
189 // the overall bounding box of the collection..
191}
192
194{
195 mGeometries.reserve( size );
196}
197
199{
200 clearCache();
201 return mGeometries.value( n );
202}
203
205{
206 if ( mGeometries.isEmpty() )
207 return true;
208
209 for ( QgsAbstractGeometry *geometry : mGeometries )
210 {
211 if ( !geometry->isEmpty() )
212 return false;
213 }
214 return true;
215}
216
218{
219 if ( !g )
220 {
221 return false;
222 }
223
224 mGeometries.append( g );
225 clearCache(); //set bounding box invalid
226 return true;
227}
228
229bool QgsGeometryCollection::addGeometries( const QVector<QgsAbstractGeometry *> &geometries )
230{
231 mGeometries.append( geometries );
232 clearCache(); //set bounding box invalid
233 return true;
234}
235
237{
238 if ( !g )
239 {
240 return false;
241 }
242
243 index = std::min( static_cast<int>( mGeometries.count() ), index );
244
245 mGeometries.insert( index, g );
246 clearCache(); //set bounding box invalid
247 return true;
248}
249
251{
252 if ( nr >= mGeometries.size() || nr < 0 )
253 {
254 return false;
255 }
256 delete mGeometries.at( nr );
257 mGeometries.remove( nr );
258 clearCache(); //set bounding box invalid
259 return true;
260}
261
262QVector<QgsAbstractGeometry *> QgsGeometryCollection::takeGeometries()
263{
264 QVector< QgsAbstractGeometry * > results = mGeometries;
265 mGeometries.clear();
266 clearCache();
267 return results;
268}
269
271{
272 for ( QgsAbstractGeometry *geometry : std::as_const( mGeometries ) )
273 {
274 geometry->normalize();
275 }
276 std::sort( mGeometries.begin(), mGeometries.end(), []( const QgsAbstractGeometry * a, const QgsAbstractGeometry * b )
277 {
278 return a->compareTo( b ) > 0;
279 } );
280}
281
283{
284 int maxDim = 0;
285 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
286 for ( ; it != mGeometries.constEnd(); ++it )
287 {
288 int dim = ( *it )->dimension();
289 if ( dim > maxDim )
290 {
291 maxDim = dim;
292 }
293 }
294 return maxDim;
295}
296
298{
299 return QStringLiteral( "GeometryCollection" );
300}
301
303{
304 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
305 {
306 g->transform( ct, d, transformZ );
307 }
308 clearCache(); //set bounding box invalid
309}
310
311void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
312{
313 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
314 {
315 g->transform( t, zTranslate, zScale, mTranslate, mScale );
316 }
317 clearCache(); //set bounding box invalid
318}
319
320void QgsGeometryCollection::draw( QPainter &p ) const
321{
322 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
323 for ( ; it != mGeometries.constEnd(); ++it )
324 {
325 ( *it )->draw( p );
326 }
327}
328
330{
331 QPainterPath p;
332 for ( const QgsAbstractGeometry *geom : mGeometries )
333 {
334 QPainterPath partPath = geom->asQPainterPath();
335 if ( !partPath.isEmpty() )
336 p.addPath( partPath );
337 }
338 return p;
339}
340
342{
343 if ( !wkbPtr )
344 {
345 return false;
346 }
347
350 return false;
351
353
354 int nGeometries = 0;
355 wkbPtr >> nGeometries;
356
357 QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
358 mGeometries.clear();
359 mGeometries.reserve( nGeometries );
360 for ( int i = 0; i < nGeometries; ++i )
361 {
362 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
363 if ( geom )
364 {
365 if ( !addGeometry( geom.release() ) )
366 {
367 qDeleteAll( mGeometries );
368 mGeometries = geometryListBackup;
369 return false;
370 }
371 }
372 }
373 qDeleteAll( geometryListBackup );
374
375 clearCache(); //set bounding box invalid
376
377 return true;
378}
379
400
402{
403 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
404 for ( const QgsAbstractGeometry *geom : mGeometries )
405 {
406 if ( geom )
407 {
408 binarySize += geom->wkbSize( flags );
409 }
410 }
411
412 return binarySize;
413}
414
415QByteArray QgsGeometryCollection::asWkb( WkbFlags flags ) const
416{
417 int countNonNull = 0;
418 for ( const QgsAbstractGeometry *geom : mGeometries )
419 {
420 if ( geom )
421 {
422 countNonNull ++;
423 }
424 }
425
426 QByteArray wkbArray;
427 wkbArray.resize( QgsGeometryCollection::wkbSize( flags ) );
428 QgsWkbPtr wkb( wkbArray );
429 wkb << static_cast<char>( QgsApplication::endian() );
430 wkb << static_cast<quint32>( wkbType() );
431 wkb << static_cast<quint32>( countNonNull );
432 for ( const QgsAbstractGeometry *geom : mGeometries )
433 {
434 if ( geom )
435 {
436 wkb << geom->asWkb( flags );
437 }
438 }
439 return wkbArray;
440}
441
443{
444 QString wkt = wktTypeStr();
445
446 if ( isEmpty() )
447 wkt += QLatin1String( " EMPTY" );
448 else
449 {
450 wkt += QLatin1String( " (" );
451 for ( const QgsAbstractGeometry *geom : mGeometries )
452 {
453 QString childWkt = geom->asWkt( precision );
454 if ( wktOmitChildType() )
455 {
456 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
457 }
458 wkt += childWkt + ',';
459 }
460 if ( wkt.endsWith( ',' ) )
461 {
462 wkt.chop( 1 ); // Remove last ','
463 }
464 wkt += ')';
465 }
466 return wkt;
467}
468
469QDomElement QgsGeometryCollection::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
470{
471 QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
472 for ( const QgsAbstractGeometry *geom : mGeometries )
473 {
474 QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
475 elemGeometryMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
476 elemMultiGeometry.appendChild( elemGeometryMember );
477 }
478 return elemMultiGeometry;
479}
480
481QDomElement QgsGeometryCollection::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
482{
483 QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
484 for ( const QgsAbstractGeometry *geom : mGeometries )
485 {
486 QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
487 elemGeometryMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
488 elemMultiGeometry.appendChild( elemGeometryMember );
489 }
490 return elemMultiGeometry;
491}
492
494{
495 json coordinates( json::array( ) );
496 for ( const QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
497 {
498 coordinates.push_back( geom->asJsonObject( precision ) );
499 }
500 return
501 {
502 { "type", "GeometryCollection" },
503 { "geometries", coordinates }
504 };
505}
506
508{
509 QString kml;
510 kml.append( QLatin1String( "<MultiGeometry>" ) );
511 const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
512 for ( const QgsAbstractGeometry *geometry : geometries )
513 {
514 kml.append( geometry->asKml( precision ) );
515 }
516 kml.append( QLatin1String( "</MultiGeometry>" ) );
517 return kml;
518}
519
521{
522 if ( mBoundingBox.isNull() )
523 {
524 mBoundingBox = calculateBoundingBox3D();
525 }
526 return mBoundingBox;
527}
528
530{
531 if ( mGeometries.empty() )
532 {
533 return QgsBox3D();
534 }
535
536 QgsBox3D bbox = mGeometries.at( 0 )->boundingBox3D();
537 for ( int i = 1; i < mGeometries.size(); ++i )
538 {
539 if ( mGeometries.at( i )->isEmpty() )
540 continue;
541
542 QgsBox3D geomBox = mGeometries.at( i )->boundingBox3D();
543 bbox.combineWith( geomBox );
544 }
545 return bbox;
546}
547
549{
550 mBoundingBox = QgsBox3D();
551 mHasCachedValidity = false;
552 mValidityFailureReason.clear();
554}
555
557{
558 QgsCoordinateSequence sequence;
559 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
560 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
561 {
562 QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
563
564 QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
565 for ( ; cIt != geomCoords.constEnd(); ++cIt )
566 {
567 sequence.push_back( *cIt );
568 }
569 }
570
571 return sequence;
572}
573
575{
576 int count = 0;
577
578 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
579 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
580 {
581 count += ( *geomIt )->nCoordinates();
582 }
583
584 return count;
585}
586
587double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
588{
589 return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
590}
591
593{
594 if ( id.part < 0 )
595 {
596 id.part = 0;
597 id.ring = -1;
598 id.vertex = -1;
599 }
600 if ( mGeometries.isEmpty() )
601 {
602 return false;
603 }
604
605 if ( id.part >= mGeometries.count() )
606 return false;
607
608 QgsAbstractGeometry *geom = mGeometries.at( id.part );
609 if ( geom->nextVertex( id, vertex ) )
610 {
611 return true;
612 }
613 if ( ( id.part + 1 ) >= numGeometries() )
614 {
615 return false;
616 }
617 ++id.part;
618 id.ring = -1;
619 id.vertex = -1;
620 return mGeometries.at( id.part )->nextVertex( id, vertex );
621}
622
624{
625 if ( position.part >= mGeometries.size() )
626 {
627 return false;
628 }
629
630 bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
631 if ( success )
632 {
633 clearCache(); //set bounding box invalid
634 }
635 return success;
636}
637
639{
640 if ( position.part < 0 || position.part >= mGeometries.size() )
641 {
642 return false;
643 }
644
645 bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
646 if ( success )
647 {
648 clearCache(); //set bounding box invalid
649 }
650 return success;
651}
652
654{
655 if ( position.part < 0 || position.part >= mGeometries.size() )
656 {
657 return false;
658 }
659
660 QgsAbstractGeometry *geom = mGeometries.at( position.part );
661 if ( !geom )
662 {
663 return false;
664 }
665
666 bool success = geom->deleteVertex( position );
667
668 //remove geometry if no vertices left
669 if ( geom->isEmpty() )
670 {
671 removeGeometry( position.part );
672 }
673
674 if ( success )
675 {
676 clearCache(); //set bounding box invalid
677 }
678 return success;
679}
680
682{
683 double length = 0.0;
684 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
685 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
686 {
687 length += ( *geomIt )->length();
688 }
689 return length;
690}
691
693{
694 double area = 0.0;
695 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
696 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
697 {
698 area += ( *geomIt )->area();
699 }
700 return area;
701}
702
704{
705 double perimeter = 0.0;
706 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
707 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
708 {
709 perimeter += ( *geomIt )->perimeter();
710 }
711 return perimeter;
712}
713
714bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<Qgis::WkbType> &subtypes, const QString &defaultChildWkbType )
715{
716 clear();
717
718 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
719
721 {
722 return false;
723 }
724 mWkbType = parts.first;
725
726 QString secondWithoutParentheses = parts.second;
727 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
728 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
729 secondWithoutParentheses.isEmpty() )
730 {
731 return true;
732 }
733
734 QString defChildWkbType = QStringLiteral( "%1%2%3 " ).arg( defaultChildWkbType, is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
735
736 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
737 for ( const QString &childWkt : blocks )
738 {
739 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
740
741 bool success = false;
742 for ( const Qgis::WkbType subtype : subtypes )
743 {
744 if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( subtype ) )
745 {
746 mGeometries.append(
747 QgsGeometryFactory::geomFromWkbType( subtype ).release()
748 );
749 if ( mGeometries.back()->fromWkt( childWkt ) )
750 {
751 success = true;
752 break;
753 }
754 }
755 }
756 if ( !success )
757 {
758 clear();
759 return false;
760 }
761 }
762
763 //scan through geometries and check if dimensionality of geometries is different to collection.
764 //if so, update the type dimensionality of the collection to match
765 bool hasZ = false;
766 bool hasM = false;
767 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
768 {
769 hasZ = hasZ || geom->is3D();
770 hasM = hasM || geom->isMeasure();
771 if ( hasZ && hasM )
772 break;
773 }
774 if ( hasZ )
775 addZValue( 0 );
776 if ( hasM )
777 addMValue( 0 );
778
779 return true;
780}
781
783{
784 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
785 for ( ; it != mGeometries.constEnd(); ++it )
786 {
787 if ( ( *it )->hasCurvedSegments() )
788 {
789 return true;
790 }
791 }
792 return false;
793}
794
796{
797 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
798 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
799 if ( !geomCollection )
800 {
801 return clone();
802 }
803
804 geomCollection->reserve( mGeometries.size() );
805 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
806 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
807 {
808 geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
809 }
810 return geom.release();
811}
812
814{
815 if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
816 {
817 return 0.0;
818 }
819
820 QgsAbstractGeometry *geom = mGeometries[vertex.part];
821 if ( !geom )
822 {
823 return 0.0;
824 }
825
826 return geom->vertexAngle( vertex );
827}
828
830{
831 if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
832 {
833 return 0.0;
834 }
835
836 const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
837 if ( !geom )
838 {
839 return 0.0;
840 }
841
842 return geom->segmentLength( startVertex );
843}
844
845int QgsGeometryCollection::vertexCount( int part, int ring ) const
846{
847 if ( part < 0 || part >= mGeometries.size() )
848 {
849 return 0;
850 }
851
852 return mGeometries[part]->vertexCount( 0, ring );
853}
854
856{
857 if ( part < 0 || part >= mGeometries.size() )
858 {
859 return 0;
860 }
861
862 return mGeometries[part]->ringCount();
863}
864
866{
867 return mGeometries.size();
868}
869
871{
872 if ( id.part < 0 || id.part >= mGeometries.size() )
873 {
874 return QgsPoint();
875 }
876
877 const QgsAbstractGeometry *geom = mGeometries[id.part];
878 if ( !geom )
879 {
880 return QgsPoint();
881 }
882
883 return geom->vertexAt( id );
884}
885
887{
888 if ( flags == 0 && mHasCachedValidity )
889 {
890 // use cached validity results
891 error = mValidityFailureReason;
892 return error.isEmpty();
893 }
894
895 QgsGeos geos( this, /* precision = */ 0, /* flags = */ Qgis::GeosCreationFlag::RejectOnInvalidSubGeometry );
896 bool res = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
897 if ( flags == 0 )
898 {
899 mValidityFailureReason = !res ? error : QString();
900 mHasCachedValidity = true;
901 }
902 return res;
903}
904
906{
908 return false;
909
911
912 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
913 {
914 geom->addZValue( zValue );
915 }
916 clearCache();
917 return true;
918}
919
921{
923 return false;
924
926
927 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
928 {
929 geom->addMValue( mValue );
930 }
931 clearCache();
932 return true;
933}
934
935
937{
939 return false;
940
942 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
943 {
944 geom->dropZValue();
945 }
946 clearCache();
947 return true;
948}
949
951{
953 return false;
954
956 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
957 {
958 geom->dropMValue();
959 }
960 clearCache();
961 return true;
962}
963
964void QgsGeometryCollection::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
965{
966 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
967 {
968 if ( geom )
969 geom->filterVertices( filter );
970 }
971 clearCache();
972}
973
974void QgsGeometryCollection::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
975{
976 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
977 {
978 if ( geom )
979 geom->transformVertices( transform );
980 }
981 clearCache();
982}
983
985{
986 // be tolerant if caller passed a multi type as type argument
987 const Qgis::WkbType filterSinglePartType = useFlatType ? QgsWkbTypes::flatType( QgsWkbTypes::singleType( type ) ) : QgsWkbTypes::singleType( type );
988
989 std::unique_ptr< QgsGeometryCollection > res;
990 switch ( QgsWkbTypes::geometryType( type ) )
991 {
993 {
994 if ( useFlatType )
995 {
996 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
997 if ( const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint *>( this ) )
998 return mp->clone();
999 }
1000
1001 res = std::make_unique< QgsMultiPoint >();
1002 break;
1003 }
1005 {
1006 if ( useFlatType )
1007 {
1008 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1009 if ( const QgsMultiLineString *ml = qgsgeometry_cast< const QgsMultiLineString *>( this ) )
1010 return ml->clone();
1011 }
1012
1013 res = std::make_unique< QgsMultiLineString >();
1014 break;
1015 }
1017 {
1018 if ( useFlatType )
1019 {
1020 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1021 if ( const QgsMultiPolygon *mp = qgsgeometry_cast< const QgsMultiPolygon *>( this ) )
1022 return mp->clone();
1023 }
1024
1025 res = std::make_unique< QgsMultiPolygon>();
1026 break;
1027 }
1028
1031 return nullptr;
1032 }
1033
1034 // assume that the collection consists entirely of matching parts (ie optimize for a pessimistic scenario)
1035 res->reserve( mGeometries.size() );
1036
1037 for ( const QgsAbstractGeometry *part : mGeometries )
1038 {
1039 if ( !part )
1040 continue;
1041
1042 const QgsAbstractGeometry *simplifiedPartType = part->simplifiedTypeRef();
1043
1044 const Qgis::WkbType thisPartType = useFlatType ? QgsWkbTypes::flatType( simplifiedPartType->wkbType() ) : simplifiedPartType->wkbType();
1045 if ( thisPartType == filterSinglePartType )
1046 {
1047 res->addGeometry( part->clone() );
1048 }
1049 }
1050
1051 return res.release();
1052}
1053
1055{
1056 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1057 {
1058 if ( geom )
1059 geom->swapXy();
1060 }
1061 clearCache();
1062}
1063
1065{
1066 auto newCollection = std::make_unique<QgsGeometryCollection>();
1067 newCollection->reserve( mGeometries.size() );
1068 for ( QgsAbstractGeometry *geom : mGeometries )
1069 {
1070 newCollection->addGeometry( geom->toCurveType() );
1071 }
1072 return newCollection.release();
1073}
1074
1076{
1077 if ( mGeometries.size() == 1 )
1078 return mGeometries.at( 0 )->simplifiedTypeRef();
1079 else
1080 return this;
1081}
1082
1084{
1085 auto res = std::make_unique< QgsGeometryCollection >();
1086 res->reserve( mGeometries.size() );
1087 for ( int i = 0; i < mGeometries.size(); ++i )
1088 {
1089 res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
1090 }
1091 return res.release();
1092}
1093
1095{
1096 if ( !transformer )
1097 return false;
1098
1099 bool res = true;
1100 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1101 {
1102 if ( geom )
1103 res = geom->transform( transformer, feedback );
1104
1105 if ( feedback && feedback->isCanceled() )
1106 res = false;
1107
1108 if ( !res )
1109 break;
1110 }
1111 clearCache();
1112 return res;
1113}
1114
1116{
1117 return false;
1118}
1119
1121{
1122 return mGeometries.count();
1123}
1124
1126{
1127 if ( index < 0 || index >= mGeometries.count() )
1128 return nullptr;
1129
1130 return mGeometries.at( index );
1131}
1132
1134{
1135 const QgsGeometryCollection *otherCollection = qgsgeometry_cast<const QgsGeometryCollection *>( other );
1136 if ( !otherCollection )
1137 return -1;
1138
1139 int i = 0;
1140 int j = 0;
1141 while ( i < mGeometries.size() && j < otherCollection->mGeometries.size() )
1142 {
1143 const QgsAbstractGeometry *aGeom = mGeometries[i];
1144 const QgsAbstractGeometry *bGeom = otherCollection->mGeometries[j];
1145 const int comparison = aGeom->compareTo( bGeom );
1146 if ( comparison != 0 )
1147 {
1148 return comparison;
1149 }
1150 i++;
1151 j++;
1152 }
1153 if ( i < mGeometries.size() )
1154 {
1155 return 1;
1156 }
1157 if ( j < otherCollection->mGeometries.size() )
1158 {
1159 return -1;
1160 }
1161 return 0;
1162}
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2038
@ RejectOnInvalidSubGeometry
Don't allow geometries with invalid sub-geometries to be created.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ MultiPoint
MultiPoint.
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
@ Triangle
Triangle.
@ MultiLineString
MultiLineString.
@ CircularString
CircularString.
@ GeometryCollection
GeometryCollection.
@ MultiCurve
MultiCurve.
@ CurvePolygon
CurvePolygon.
@ PolyhedralSurface
PolyhedralSurface.
@ MultiSurface
MultiSurface.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2621
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.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual void clearCache() const
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool isEmpty() const
Returns true if the geometry is empty.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within 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.
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.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp:144
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition qgsbox3d.cpp:208
bool isNull() const
Test if the box is null (holding no spatial information).
Definition qgsbox3d.cpp:310
A const WKB pointer.
Definition qgswkbptr.h:138
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:55
Handles coordinate transforms between two coordinate systems.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
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.
double perimeter() const override
Returns the planar, 2-dimensional perimeter of the geometry.
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.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
bool fromCollectionWkt(const QString &wkt, const QVector< Qgis::WkbType > &subtypes, const QString &defaultChildWkbType=QString())
Reads a collection 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.
void reserve(int size)
Attempts to allocate memory for at least size geometries.
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.
QString geometryType() const override
Returns a unique string representing the geometry type.
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.
virtual QgsGeometryCollection * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
bool isEmpty() const override
Returns true if the geometry is empty.
QgsGeometryCollection()
Constructor for an empty geometry collection.
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.
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.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
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.
void normalize() final
Reorganizes the geometry into a normalized form (or "canonical" form).
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.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
QgsGeometryCollection * extractPartsByType(Qgis::WkbType type, bool useFlatType=true) const
Returns a new QgsGeometryCollection subclass which consists of the parts of this collection which mat...
QgsBox3D boundingBox3D() const override
Returns the 3D bounding box for the geometry.
QVector< QgsAbstractGeometry * > takeGeometries()
Removes all geometries from the collection, returning them and their ownership to the caller.
QgsGeometryCollection * clone() const override
Clones the geometry by performing a deep copy.
virtual bool addGeometries(const QVector< QgsAbstractGeometry * > &geometries)
Adds a list of geometries to the collection, transferring ownership to the collection.
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
int dimension() const override
Returns the inherent dimension 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.
bool boundingBoxIntersects(const QgsBox3D &box3d) const override
Returns true if the bounding box of this geometry intersects with a box3d.
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.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
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
Returns a WKB representation of the geometry.
int partCount() const override
Returns count of parts contained in the geometry.
virtual bool wktOmitChildType() const
Returns whether child type names are omitted from Wkt representations of the collection.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
int numGeometries() const
Returns the number of geometries within the collection.
QgsGeometryCollection * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
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 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.
const QgsAbstractGeometry * simplifiedTypeRef() const override
Returns a reference to the simplest lossless representation of this geometry, e.g.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(Qgis::WkbType t)
Returns empty geometry from wkb type.
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 QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
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, and exception handling.
Definition qgsgeos.h:139
Multi line string geometry collection.
Multi point geometry collection.
Multi polygon geometry collection.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
WKB pointer handler.
Definition qgswkbptr.h:44
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType linearType(Qgis::WkbType type)
Returns the linear type for a WKB type.
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition qgswkbtypes.h:53
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Contains geos related utilities and functions.
Definition qgsgeos.h:75
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:30
int part
Part number.
Definition qgsvertexid.h:88