QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
18#include <memory>
19#include <nlohmann/json.hpp>
20
21#include "qgsapplication.h"
22#include "qgsbox3d.h"
23#include "qgsfeedback.h"
24#include "qgsgeometryfactory.h"
25#include "qgsgeometryutils.h"
26#include "qgsgeos.h"
27#include "qgslinestring.h"
28#include "qgsmultilinestring.h"
29#include "qgsmultipoint.h"
30#include "qgsmultipolygon.h"
31#include "qgspoint.h"
32#include "qgswkbptr.h"
33
34#include <QString>
35
36using namespace Qt::StringLiterals;
37
42
45 mBoundingBox( c.mBoundingBox ),
46 mHasCachedValidity( c.mHasCachedValidity ),
47 mValidityFailureReason( c.mValidityFailureReason )
48{
49 int nGeoms = c.mGeometries.size();
50 mGeometries.resize( nGeoms );
51 for ( int i = 0; i < nGeoms; ++i )
52 {
53 mGeometries[i] = c.mGeometries.at( i )->clone();
54 }
55}
56
57// cppcheck-suppress operatorEqVarError
59{
60 if ( &c != this )
61 {
63 int nGeoms = c.mGeometries.size();
64 mGeometries.resize( nGeoms );
65 for ( int i = 0; i < nGeoms; ++i )
66 {
67 mGeometries[i] = c.mGeometries.at( i )->clone();
68 }
69 }
70 return *this;
71}
72
77
79{
80 auto result = std::make_unique< QgsGeometryCollection >();
81 result->mWkbType = mWkbType;
82 return result.release();
83}
84
89
91{
92 qDeleteAll( mGeometries );
93 mGeometries.clear();
94 clearCache(); //set bounding box invalid
95}
96
97QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
98{
99 std::unique_ptr<QgsGeometryCollection> result;
100
101 for ( auto geom : mGeometries )
102 {
103 std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) };
104 if ( gridified )
105 {
106 if ( !result )
107 result = std::unique_ptr<QgsGeometryCollection> { createEmptyWithSameType() };
108
109 result->mGeometries.append( gridified.release() );
110 }
111 }
112
113 return result.release();
114}
115
116bool QgsGeometryCollection::removeDuplicateNodes( double epsilon, bool useZValues )
117{
118 bool result = false;
119 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
120 {
121 if ( geom->removeDuplicateNodes( epsilon, useZValues ) ) result = true;
122 }
123 return result;
124}
125
127{
128 return nullptr;
129}
130
132{
133 if ( vertex.part < 0 || vertex.part >= mGeometries.count() )
134 {
135 previousVertex = QgsVertexId();
137 return;
138 }
139
140 mGeometries.at( vertex.part )->adjacentVertices( vertex, previousVertex, nextVertex );
141}
142
144{
145 if ( id.part < 0 || id.part >= mGeometries.count() )
146 return -1;
147
148 int number = 0;
149 int part = 0;
150 for ( QgsAbstractGeometry *geometry : mGeometries )
151 {
152 if ( part == id.part )
153 {
154 int partNumber = geometry->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
155 if ( partNumber == -1 )
156 return -1;
157 return number + partNumber;
158 }
159 else
160 {
161 number += geometry->nCoordinates();
162 }
163
164 part++;
165 }
166 return -1; // should not happen
167}
168
170{
171 if ( mGeometries.empty() )
172 return false;
173
174 // if we already have the bounding box calculated, then this check is trivial!
175 if ( !mBoundingBox.isNull() )
176 {
177 return mBoundingBox.intersects( box3d );
178 }
179
180 // otherwise loop through each member geometry and test the bounding box intersection.
181 // This gives us a chance to use optimisations which may be present on the individual
182 // geometry subclasses, and at worst it will cause a calculation of the bounding box
183 // of each individual member geometry which we would have to do anyway... (and these
184 // bounding boxes are cached, so would be reused without additional expense)
185 for ( const QgsAbstractGeometry *geometry : mGeometries )
186 {
187 if ( geometry->boundingBoxIntersects( box3d ) )
188 return true;
189 }
190
191 // even if we don't intersect the bounding box of any member geometries, we may still intersect the
192 // bounding box of the overall collection.
193 // so here we fall back to the non-optimised base class check which has to first calculate
194 // the overall bounding box of the collection..
196}
197
199{
200 mGeometries.reserve( size );
201}
202
204{
205 clearCache();
206 return mGeometries.value( n );
207}
208
210{
211 if ( mGeometries.isEmpty() )
212 return true;
213
214 for ( QgsAbstractGeometry *geometry : mGeometries )
215 {
216 if ( !geometry->isEmpty() )
217 return false;
218 }
219 return true;
220}
221
223{
224 if ( !g )
225 {
226 return false;
227 }
228
229 mGeometries.append( g );
230 clearCache(); //set bounding box invalid
231 return true;
232}
233
234bool QgsGeometryCollection::addGeometries( const QVector<QgsAbstractGeometry *> &geometries )
235{
236 mGeometries.append( geometries );
237 clearCache(); //set bounding box invalid
238 return true;
239}
240
242{
243 if ( !g )
244 {
245 return false;
246 }
247
248 index = std::min( static_cast<int>( mGeometries.count() ), index );
249
250 mGeometries.insert( index, g );
251 clearCache(); //set bounding box invalid
252 return true;
253}
254
256{
257 if ( nr >= mGeometries.size() || nr < 0 )
258 {
259 return false;
260 }
261 delete mGeometries.at( nr );
262 mGeometries.remove( nr );
263 clearCache(); //set bounding box invalid
264 return true;
265}
266
267QVector<QgsAbstractGeometry *> QgsGeometryCollection::takeGeometries()
268{
269 QVector< QgsAbstractGeometry * > results = mGeometries;
270 mGeometries.clear();
271 clearCache();
272 return results;
273}
274
276{
277 for ( QgsAbstractGeometry *geometry : std::as_const( mGeometries ) )
278 {
279 geometry->normalize();
280 }
281 std::sort( mGeometries.begin(), mGeometries.end(), []( const QgsAbstractGeometry * a, const QgsAbstractGeometry * b )
282 {
283 return a->compareTo( b ) > 0;
284 } );
285}
286
288{
289 int maxDim = 0;
290 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
291 for ( ; it != mGeometries.constEnd(); ++it )
292 {
293 int dim = ( *it )->dimension();
294 if ( dim > maxDim )
295 {
296 maxDim = dim;
297 }
298 }
299 return maxDim;
300}
301
303{
304 return u"GeometryCollection"_s;
305}
306
308{
309 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
310 {
311 g->transform( ct, d, transformZ );
312 }
313 clearCache(); //set bounding box invalid
314}
315
316void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
317{
318 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
319 {
320 g->transform( t, zTranslate, zScale, mTranslate, mScale );
321 }
322 clearCache(); //set bounding box invalid
323}
324
325void QgsGeometryCollection::draw( QPainter &p ) const
326{
327 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
328 for ( ; it != mGeometries.constEnd(); ++it )
329 {
330 ( *it )->draw( p );
331 }
332}
333
335{
336 QPainterPath p;
337 for ( const QgsAbstractGeometry *geom : mGeometries )
338 {
339 QPainterPath partPath = geom->asQPainterPath();
340 if ( !partPath.isEmpty() )
341 p.addPath( partPath );
342 }
343 return p;
344}
345
347{
348 if ( !wkbPtr )
349 {
350 return false;
351 }
352
355 return false;
356
358
359 int nGeometries = 0;
360 wkbPtr >> nGeometries;
361
362 QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
363 mGeometries.clear();
364 mGeometries.reserve( nGeometries );
365 for ( int i = 0; i < nGeometries; ++i )
366 {
367 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
368 if ( geom )
369 {
370 if ( !addGeometry( geom.release() ) )
371 {
372 qDeleteAll( mGeometries );
373 mGeometries = geometryListBackup;
374 return false;
375 }
376 }
377 }
378 qDeleteAll( geometryListBackup );
379
380 clearCache(); //set bounding box invalid
381
382 return true;
383}
384
405
407{
408 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
409 for ( const QgsAbstractGeometry *geom : mGeometries )
410 {
411 if ( geom )
412 {
413 binarySize += geom->wkbSize( flags );
414 }
415 }
416
417 return binarySize;
418}
419
420QByteArray QgsGeometryCollection::asWkb( WkbFlags flags ) const
421{
422 int countNonNull = 0;
423 for ( const QgsAbstractGeometry *geom : mGeometries )
424 {
425 if ( geom )
426 {
427 countNonNull ++;
428 }
429 }
430
431 QByteArray wkbArray;
432 wkbArray.resize( QgsGeometryCollection::wkbSize( flags ) );
433 QgsWkbPtr wkb( wkbArray );
434 wkb << static_cast<char>( QgsApplication::endian() );
435 wkb << static_cast<quint32>( wkbType() );
436 wkb << static_cast<quint32>( countNonNull );
437 for ( const QgsAbstractGeometry *geom : mGeometries )
438 {
439 if ( geom )
440 {
441 wkb << geom->asWkb( flags );
442 }
443 }
444 return wkbArray;
445}
446
447QString QgsGeometryCollection::asWkt( int precision ) const
448{
449 QString wkt = wktTypeStr();
450
451 if ( isEmpty() )
452 wkt += " EMPTY"_L1;
453 else
454 {
455 wkt += " ("_L1;
456 for ( const QgsAbstractGeometry *geom : mGeometries )
457 {
458 QString childWkt = geom->asWkt( precision );
459 if ( wktOmitChildType() )
460 {
461 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
462 }
463 wkt += childWkt + ',';
464 }
465 if ( wkt.endsWith( ',' ) )
466 {
467 wkt.chop( 1 ); // Remove last ','
468 }
469 wkt += ')';
470 }
471 return wkt;
472}
473
474QDomElement QgsGeometryCollection::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
475{
476 QDomElement elemMultiGeometry = doc.createElementNS( ns, u"MultiGeometry"_s );
477 for ( const QgsAbstractGeometry *geom : mGeometries )
478 {
479 QDomElement elemGeometryMember = doc.createElementNS( ns, u"geometryMember"_s );
480 elemGeometryMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
481 elemMultiGeometry.appendChild( elemGeometryMember );
482 }
483 return elemMultiGeometry;
484}
485
486QDomElement QgsGeometryCollection::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
487{
488 QDomElement elemMultiGeometry = doc.createElementNS( ns, u"MultiGeometry"_s );
489 for ( const QgsAbstractGeometry *geom : mGeometries )
490 {
491 QDomElement elemGeometryMember = doc.createElementNS( ns, u"geometryMember"_s );
492 elemGeometryMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
493 elemMultiGeometry.appendChild( elemGeometryMember );
494 }
495 return elemMultiGeometry;
496}
497
498json QgsGeometryCollection::asJsonObject( int precision ) const
499{
500 json coordinates( json::array( ) );
501 for ( const QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
502 {
503 coordinates.push_back( geom->asJsonObject( precision ) );
504 }
505 return
506 {
507 { "type", "GeometryCollection" },
508 { "geometries", coordinates }
509 };
510}
511
512QString QgsGeometryCollection::asKml( int precision ) const
513{
514 QString kml;
515 kml.append( "<MultiGeometry>"_L1 );
516 const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
517 for ( const QgsAbstractGeometry *geometry : geometries )
518 {
519 kml.append( geometry->asKml( precision ) );
520 }
521 kml.append( "</MultiGeometry>"_L1 );
522 return kml;
523}
524
526{
527 if ( mBoundingBox.isNull() )
528 {
529 mBoundingBox = calculateBoundingBox3D();
530 }
531 return mBoundingBox;
532}
533
535{
536 if ( mGeometries.empty() )
537 {
538 return QgsBox3D();
539 }
540
541 QgsBox3D bbox = mGeometries.at( 0 )->boundingBox3D();
542 for ( int i = 1; i < mGeometries.size(); ++i )
543 {
544 if ( mGeometries.at( i )->isEmpty() )
545 continue;
546
547 QgsBox3D geomBox = mGeometries.at( i )->boundingBox3D();
548 bbox.combineWith( geomBox );
549 }
550 return bbox;
551}
552
554{
555 mBoundingBox = QgsBox3D();
556 mHasCachedValidity = false;
557 mValidityFailureReason.clear();
559}
560
562{
563 QgsCoordinateSequence sequence;
564 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
565 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
566 {
567 QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
568
569 QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
570 for ( ; cIt != geomCoords.constEnd(); ++cIt )
571 {
572 sequence.push_back( *cIt );
573 }
574 }
575
576 return sequence;
577}
578
580{
581 int count = 0;
582
583 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
584 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
585 {
586 count += ( *geomIt )->nCoordinates();
587 }
588
589 return count;
590}
591
592double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
593{
594 return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
595}
596
598{
599 if ( id.part < 0 )
600 {
601 id.part = 0;
602 id.ring = -1;
603 id.vertex = -1;
604 }
605 if ( mGeometries.isEmpty() )
606 {
607 return false;
608 }
609
610 if ( id.part >= mGeometries.count() )
611 return false;
612
613 QgsAbstractGeometry *geom = mGeometries.at( id.part );
614 if ( geom->nextVertex( id, vertex ) )
615 {
616 return true;
617 }
618 if ( ( id.part + 1 ) >= numGeometries() )
619 {
620 return false;
621 }
622 ++id.part;
623 id.ring = -1;
624 id.vertex = -1;
625 return mGeometries.at( id.part )->nextVertex( id, vertex );
626}
627
629{
630 if ( position.part >= mGeometries.size() )
631 {
632 return false;
633 }
634
635 bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
636 if ( success )
637 {
638 clearCache(); //set bounding box invalid
639 }
640 return success;
641}
642
644{
645 if ( position.part < 0 || position.part >= mGeometries.size() )
646 {
647 return false;
648 }
649
650 bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
651 if ( success )
652 {
653 clearCache(); //set bounding box invalid
654 }
655 return success;
656}
657
659{
660 if ( position.part < 0 || position.part >= mGeometries.size() )
661 {
662 return false;
663 }
664
665 QgsAbstractGeometry *geom = mGeometries.at( position.part );
666 if ( !geom )
667 {
668 return false;
669 }
670
671 bool success = geom->deleteVertex( position );
672
673 //remove geometry if no vertices left
674 if ( geom->isEmpty() )
675 {
676 removeGeometry( position.part );
677 }
678
679 if ( success )
680 {
681 clearCache(); //set bounding box invalid
682 }
683 return success;
684}
685
687{
688 double length = 0.0;
689 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
690 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
691 {
692 length += ( *geomIt )->length();
693 }
694 return length;
695}
696
698{
699 double area = 0.0;
700 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
701 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
702 {
703 area += ( *geomIt )->area();
704 }
705 return area;
706}
707
709{
710 double area3D = 0.0;
711 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
712 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
713 {
714 area3D += ( *geomIt )->area3D();
715 }
716 return area3D;
717}
718
720{
721 double perimeter = 0.0;
722 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
723 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
724 {
725 perimeter += ( *geomIt )->perimeter();
726 }
727 return perimeter;
728}
729
730bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<Qgis::WkbType> &subtypes, const QString &defaultChildWkbType )
731{
732 clear();
733
734 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
735
737 {
738 return false;
739 }
740 mWkbType = parts.first;
741
742 QString secondWithoutParentheses = parts.second;
743 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
744 if ( ( parts.second.compare( "EMPTY"_L1, Qt::CaseInsensitive ) == 0 ) ||
745 secondWithoutParentheses.isEmpty() )
746 {
747 return true;
748 }
749
750 QString defChildWkbType = u"%1%2%3 "_s.arg( defaultChildWkbType, is3D() ? u"Z"_s : QString(), isMeasure() ? u"M"_s : QString() );
751
752 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
753 for ( const QString &childWkt : blocks )
754 {
755 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
756
757 bool success = false;
758 for ( const Qgis::WkbType subtype : subtypes )
759 {
760 if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( subtype ) )
761 {
762 mGeometries.append(
763 QgsGeometryFactory::geomFromWkbType( subtype ).release()
764 );
765 if ( mGeometries.back()->fromWkt( childWkt ) )
766 {
767 success = true;
768 break;
769 }
770 }
771 }
772 if ( !success )
773 {
774 clear();
775 return false;
776 }
777 }
778
779 //scan through geometries and check if dimensionality of geometries is different to collection.
780 //if so, update the type dimensionality of the collection to match
781 bool hasZ = false;
782 bool hasM = false;
783 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
784 {
785 hasZ = hasZ || geom->is3D();
786 hasM = hasM || geom->isMeasure();
787 if ( hasZ && hasM )
788 break;
789 }
790 if ( hasZ )
791 addZValue( 0 );
792 if ( hasM )
793 addMValue( 0 );
794
795 return true;
796}
797
799{
800 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
801 for ( ; it != mGeometries.constEnd(); ++it )
802 {
803 if ( ( *it )->hasCurvedSegments() )
804 {
805 return true;
806 }
807 }
808 return false;
809}
810
812{
813 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
815 if ( !geomCollection )
816 {
817 return clone();
818 }
819
820 geomCollection->reserve( mGeometries.size() );
821 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
822 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
823 {
824 geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
825 }
826 return geom.release();
827}
828
830{
831 if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
832 {
833 return 0.0;
834 }
835
836 QgsAbstractGeometry *geom = mGeometries[vertex.part];
837 if ( !geom )
838 {
839 return 0.0;
840 }
841
842 return geom->vertexAngle( vertex );
843}
844
846{
847 if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
848 {
849 return 0.0;
850 }
851
852 const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
853 if ( !geom )
854 {
855 return 0.0;
856 }
857
858 return geom->segmentLength( startVertex );
859}
860
861int QgsGeometryCollection::vertexCount( int part, int ring ) const
862{
863 if ( part < 0 || part >= mGeometries.size() )
864 {
865 return 0;
866 }
867
868 return mGeometries[part]->vertexCount( 0, ring );
869}
870
872{
873 if ( part < 0 || part >= mGeometries.size() )
874 {
875 return 0;
876 }
877
878 return mGeometries[part]->ringCount();
879}
880
882{
883 return mGeometries.size();
884}
885
887{
888 if ( id.part < 0 || id.part >= mGeometries.size() )
889 {
890 return QgsPoint();
891 }
892
893 const QgsAbstractGeometry *geom = mGeometries[id.part];
894 if ( !geom )
895 {
896 return QgsPoint();
897 }
898
899 return geom->vertexAt( id );
900}
901
903{
904 if ( flags == 0 && mHasCachedValidity )
905 {
906 // use cached validity results
907 error = mValidityFailureReason;
908 return error.isEmpty();
909 }
910
911 QgsGeos geos( this, /* precision = */ 0, /* flags = */ Qgis::GeosCreationFlag::RejectOnInvalidSubGeometry );
912 bool res = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
913 if ( flags == 0 )
914 {
915 mValidityFailureReason = !res ? error : QString();
916 mHasCachedValidity = true;
917 }
918 return res;
919}
920
922{
924 return false;
925
927
928 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
929 {
930 geom->addZValue( zValue );
931 }
932 clearCache();
933 return true;
934}
935
937{
939 return false;
940
942
943 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
944 {
945 geom->addMValue( mValue );
946 }
947 clearCache();
948 return true;
949}
950
951
953{
955 return false;
956
958 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
959 {
960 geom->dropZValue();
961 }
962 clearCache();
963 return true;
964}
965
967{
969 return false;
970
972 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
973 {
974 geom->dropMValue();
975 }
976 clearCache();
977 return true;
978}
979
980void QgsGeometryCollection::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
981{
982 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
983 {
984 if ( geom )
985 geom->filterVertices( filter );
986 }
987 clearCache();
988}
989
991{
992 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
993 {
994 if ( geom )
995 geom->transformVertices( transform );
996 }
997 clearCache();
998}
999
1001{
1002 // be tolerant if caller passed a multi type as type argument
1003 const Qgis::WkbType filterSinglePartType = useFlatType ? QgsWkbTypes::flatType( QgsWkbTypes::singleType( type ) ) : QgsWkbTypes::singleType( type );
1004
1005 std::unique_ptr< QgsGeometryCollection > res;
1006 switch ( QgsWkbTypes::geometryType( type ) )
1007 {
1009 {
1010 if ( useFlatType )
1011 {
1012 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1014 return mp->clone();
1015 }
1016
1017 res = std::make_unique< QgsMultiPoint >();
1018 break;
1019 }
1021 {
1022 if ( useFlatType )
1023 {
1024 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1026 return ml->clone();
1027 }
1028
1029 res = std::make_unique< QgsMultiLineString >();
1030 break;
1031 }
1033 {
1034 if ( useFlatType )
1035 {
1036 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1038 return mp->clone();
1039 }
1040
1041 res = std::make_unique< QgsMultiPolygon>();
1042 break;
1043 }
1044
1047 return nullptr;
1048 }
1049
1050 // assume that the collection consists entirely of matching parts (ie optimize for a pessimistic scenario)
1051 res->reserve( mGeometries.size() );
1052
1053 for ( const QgsAbstractGeometry *part : mGeometries )
1054 {
1055 if ( !part )
1056 continue;
1057
1058 const QgsAbstractGeometry *simplifiedPartType = part->simplifiedTypeRef();
1059
1060 const Qgis::WkbType thisPartType = useFlatType ? QgsWkbTypes::flatType( simplifiedPartType->wkbType() ) : simplifiedPartType->wkbType();
1061 if ( thisPartType == filterSinglePartType )
1062 {
1063 res->addGeometry( part->clone() );
1064 }
1065 }
1066
1067 return res.release();
1068}
1069
1071{
1072 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1073 {
1074 if ( geom )
1075 geom->swapXy();
1076 }
1077 clearCache();
1078}
1079
1081{
1082 auto newCollection = std::make_unique<QgsGeometryCollection>();
1083 newCollection->reserve( mGeometries.size() );
1084 for ( QgsAbstractGeometry *geom : mGeometries )
1085 {
1086 newCollection->addGeometry( geom->toCurveType() );
1087 }
1088 return newCollection.release();
1089}
1090
1092{
1093 if ( mGeometries.size() == 1 )
1094 return mGeometries.at( 0 )->simplifiedTypeRef();
1095 else
1096 return this;
1097}
1098
1100{
1101 auto res = std::make_unique< QgsGeometryCollection >();
1102 res->reserve( mGeometries.size() );
1103 for ( int i = 0; i < mGeometries.size(); ++i )
1104 {
1105 res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
1106 }
1107 return res.release();
1108}
1109
1111{
1112 if ( !transformer )
1113 return false;
1114
1115 bool res = true;
1116 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1117 {
1118 if ( geom )
1119 res = geom->transform( transformer, feedback );
1120
1121 if ( feedback && feedback->isCanceled() )
1122 res = false;
1123
1124 if ( !res )
1125 break;
1126 }
1127 clearCache();
1128 return res;
1129}
1130
1132{
1133 return false;
1134}
1135
1137{
1138 return mGeometries.count();
1139}
1140
1142{
1143 if ( index < 0 || index >= mGeometries.count() )
1144 return nullptr;
1145
1146 return mGeometries.at( index );
1147}
1148
1150{
1152 if ( !otherCollection )
1153 return -1;
1154
1155 int i = 0;
1156 int j = 0;
1157 while ( i < mGeometries.size() && j < otherCollection->mGeometries.size() )
1158 {
1159 const QgsAbstractGeometry *aGeom = mGeometries[i];
1160 const QgsAbstractGeometry *bGeom = otherCollection->mGeometries[j];
1161 const int comparison = aGeom->compareTo( bGeom );
1162 if ( comparison != 0 )
1163 {
1164 return comparison;
1165 }
1166 i++;
1167 j++;
1168 }
1169 if ( i < mGeometries.size() )
1170 {
1171 return 1;
1172 }
1173 if ( j < otherCollection->mGeometries.size() )
1174 {
1175 return -1;
1176 }
1177 return 0;
1178}
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2130
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2133
@ RejectOnInvalidSubGeometry
Don't allow geometries with invalid sub-geometries to be created.
Definition qgis.h:2206
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
@ Unknown
Unknown types.
Definition qgis.h:369
@ Null
No geometry.
Definition qgis.h:370
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ CompoundCurve
CompoundCurve.
Definition qgis.h:291
@ Point
Point.
Definition qgis.h:282
@ LineString
LineString.
Definition qgis.h:283
@ TIN
TIN.
Definition qgis.h:296
@ MultiPoint
MultiPoint.
Definition qgis.h:286
@ Polygon
Polygon.
Definition qgis.h:284
@ MultiPolygon
MultiPolygon.
Definition qgis.h:288
@ Triangle
Triangle.
Definition qgis.h:285
@ MultiLineString
MultiLineString.
Definition qgis.h:287
@ CircularString
CircularString.
Definition qgis.h:290
@ GeometryCollection
GeometryCollection.
Definition qgis.h:289
@ MultiCurve
MultiCurve.
Definition qgis.h:293
@ CurvePolygon
CurvePolygon.
Definition qgis.h:292
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:295
@ MultiSurface
MultiSurface.
Definition qgis.h:294
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2729
An abstract base class for classes which transform geometries by transforming input points to output ...
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 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.
QgsAbstractGeometry()=default
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.
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:45
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition qgsbox3d.cpp:214
A const WKB pointer.
Definition qgswkbptr.h:139
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:60
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:55
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.
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.
double area3D() const override
Returns the 3-dimensional surface area of 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:141
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:53
WKB pointer handler.
Definition qgswkbptr.h:45
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 Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition qgswkbtypes.h:52
static Q_INVOKABLE 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:77
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
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsRingSequence > QgsCoordinateSequence
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:34
int part
Part number.
Definition qgsvertexid.h:92