QGIS API Documentation 4.1.0-Master (31622b25bb0)
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 ) )
122 result = true;
123 }
124 return result;
125}
126
128{
129 return nullptr;
130}
131
133{
134 if ( vertex.part < 0 || vertex.part >= mGeometries.count() )
135 {
136 previousVertex = QgsVertexId();
138 return;
139 }
140
141 mGeometries.at( vertex.part )->adjacentVertices( vertex, previousVertex, nextVertex );
142}
143
145{
146 if ( id.part < 0 || id.part >= mGeometries.count() )
147 return -1;
148
149 int number = 0;
150 int part = 0;
151 for ( QgsAbstractGeometry *geometry : mGeometries )
152 {
153 if ( part == id.part )
154 {
155 int partNumber = geometry->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
156 if ( partNumber == -1 )
157 return -1;
158 return number + partNumber;
159 }
160 else
161 {
162 number += geometry->nCoordinates();
163 }
164
165 part++;
166 }
167 return -1; // should not happen
168}
169
171{
172 if ( mGeometries.empty() )
173 return false;
174
175 // if we already have the bounding box calculated, then this check is trivial!
176 if ( !mBoundingBox.isNull() )
177 {
178 return mBoundingBox.intersects( box3d );
179 }
180
181 // otherwise loop through each member geometry and test the bounding box intersection.
182 // This gives us a chance to use optimisations which may be present on the individual
183 // geometry subclasses, and at worst it will cause a calculation of the bounding box
184 // of each individual member geometry which we would have to do anyway... (and these
185 // bounding boxes are cached, so would be reused without additional expense)
186 for ( const QgsAbstractGeometry *geometry : mGeometries )
187 {
188 if ( geometry->boundingBoxIntersects( box3d ) )
189 return true;
190 }
191
192 // even if we don't intersect the bounding box of any member geometries, we may still intersect the
193 // bounding box of the overall collection.
194 // so here we fall back to the non-optimised base class check which has to first calculate
195 // the overall bounding box of the collection..
197}
198
200{
201 mGeometries.reserve( size );
202}
203
205{
206 clearCache();
207 return mGeometries.value( n );
208}
209
211{
212 if ( mGeometries.isEmpty() )
213 return true;
214
215 for ( QgsAbstractGeometry *geometry : mGeometries )
216 {
217 if ( !geometry->isEmpty() )
218 return false;
219 }
220 return true;
221}
222
224{
225 if ( !g )
226 {
227 return false;
228 }
229
230 mGeometries.append( g );
231 clearCache(); //set bounding box invalid
232 return true;
233}
234
235bool QgsGeometryCollection::addGeometries( const QVector<QgsAbstractGeometry *> &geometries )
236{
237 mGeometries.append( geometries );
238 clearCache(); //set bounding box invalid
239 return true;
240}
241
243{
244 if ( !g )
245 {
246 return false;
247 }
248
249 index = std::min( static_cast<int>( mGeometries.count() ), index );
250
251 mGeometries.insert( index, g );
252 clearCache(); //set bounding box invalid
253 return true;
254}
255
257{
258 if ( nr >= mGeometries.size() || nr < 0 )
259 {
260 return false;
261 }
262 delete mGeometries.at( nr );
263 mGeometries.remove( nr );
264 clearCache(); //set bounding box invalid
265 return true;
266}
267
268QVector<QgsAbstractGeometry *> QgsGeometryCollection::takeGeometries()
269{
270 QVector< QgsAbstractGeometry * > results = mGeometries;
271 mGeometries.clear();
272 clearCache();
273 return results;
274}
275
277{
278 for ( QgsAbstractGeometry *geometry : std::as_const( mGeometries ) )
279 {
280 geometry->normalize();
281 }
282 std::sort( mGeometries.begin(), mGeometries.end(), []( const QgsAbstractGeometry *a, const QgsAbstractGeometry *b ) { return a->compareTo( b ) > 0; } );
283}
284
286{
287 int maxDim = 0;
288 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
289 for ( ; it != mGeometries.constEnd(); ++it )
290 {
291 int dim = ( *it )->dimension();
292 if ( dim > maxDim )
293 {
294 maxDim = dim;
295 }
296 }
297 return maxDim;
298}
299
301{
302 return u"GeometryCollection"_s;
303}
304
306{
307 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
308 {
309 g->transform( ct, d, transformZ );
310 }
311 clearCache(); //set bounding box invalid
312}
313
314void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
315{
316 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
317 {
318 g->transform( t, zTranslate, zScale, mTranslate, mScale );
319 }
320 clearCache(); //set bounding box invalid
321}
322
323void QgsGeometryCollection::draw( QPainter &p ) const
324{
325 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
326 for ( ; it != mGeometries.constEnd(); ++it )
327 {
328 ( *it )->draw( p );
329 }
330}
331
333{
334 QPainterPath p;
335 for ( const QgsAbstractGeometry *geom : mGeometries )
336 {
337 QPainterPath partPath = geom->asQPainterPath();
338 if ( !partPath.isEmpty() )
339 p.addPath( partPath );
340 }
341 return p;
342}
343
345{
346 if ( !wkbPtr )
347 {
348 return false;
349 }
350
353 return false;
354
356
357 int nGeometries = 0;
358 wkbPtr >> nGeometries;
359
360 QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
361 mGeometries.clear();
362 mGeometries.reserve( nGeometries );
363 for ( int i = 0; i < nGeometries; ++i )
364 {
365 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
366 if ( geom )
367 {
368 if ( !addGeometry( geom.release() ) )
369 {
370 qDeleteAll( mGeometries );
371 mGeometries = geometryListBackup;
372 return false;
373 }
374 }
375 }
376 qDeleteAll( geometryListBackup );
377
378 clearCache(); //set bounding box invalid
379
380 return true;
381}
382
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 { { "type", "GeometryCollection" }, { "geometries", coordinates } };
506}
507
508QString QgsGeometryCollection::asKml( int precision ) const
509{
510 QString kml;
511 kml.append( "<MultiGeometry>"_L1 );
512 const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
513 for ( const QgsAbstractGeometry *geometry : geometries )
514 {
515 kml.append( geometry->asKml( precision ) );
516 }
517 kml.append( "</MultiGeometry>"_L1 );
518 return kml;
519}
520
522{
523 if ( mBoundingBox.isNull() )
524 {
525 mBoundingBox = calculateBoundingBox3D();
526 }
527 return mBoundingBox;
528}
529
531{
532 if ( mGeometries.empty() )
533 {
534 return QgsBox3D();
535 }
536
537 QgsBox3D bbox = mGeometries.at( 0 )->boundingBox3D();
538 for ( int i = 1; i < mGeometries.size(); ++i )
539 {
540 if ( mGeometries.at( i )->isEmpty() )
541 continue;
542
543 QgsBox3D geomBox = mGeometries.at( i )->boundingBox3D();
544 bbox.combineWith( geomBox );
545 }
546 return bbox;
547}
548
550{
551 mBoundingBox = QgsBox3D();
552 mHasCachedValidity = false;
553 mValidityFailureReason.clear();
555}
556
558{
559 QgsCoordinateSequence sequence;
560 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
561 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
562 {
563 QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
564
565 QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
566 for ( ; cIt != geomCoords.constEnd(); ++cIt )
567 {
568 sequence.push_back( *cIt );
569 }
570 }
571
572 return sequence;
573}
574
576{
577 int count = 0;
578
579 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
580 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
581 {
582 count += ( *geomIt )->nCoordinates();
583 }
584
585 return count;
586}
587
588double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
589{
590 return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
591}
592
594{
595 if ( id.part < 0 )
596 {
597 id.part = 0;
598 id.ring = -1;
599 id.vertex = -1;
600 }
601 if ( mGeometries.isEmpty() )
602 {
603 return false;
604 }
605
606 if ( id.part >= mGeometries.count() )
607 return false;
608
609 QgsAbstractGeometry *geom = mGeometries.at( id.part );
610 if ( geom->nextVertex( id, vertex ) )
611 {
612 return true;
613 }
614 if ( ( id.part + 1 ) >= numGeometries() )
615 {
616 return false;
617 }
618 ++id.part;
619 id.ring = -1;
620 id.vertex = -1;
621 return mGeometries.at( id.part )->nextVertex( id, vertex );
622}
623
625{
626 if ( position.part >= mGeometries.size() )
627 {
628 return false;
629 }
630
631 bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
632 if ( success )
633 {
634 clearCache(); //set bounding box invalid
635 }
636 return success;
637}
638
640{
641 if ( position.part < 0 || position.part >= mGeometries.size() )
642 {
643 return false;
644 }
645
646 bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
647 if ( success )
648 {
649 clearCache(); //set bounding box invalid
650 }
651 return success;
652}
653
655{
656 if ( position.part < 0 || position.part >= mGeometries.size() )
657 {
658 return false;
659 }
660
661 QgsAbstractGeometry *geom = mGeometries.at( position.part );
662 if ( !geom )
663 {
664 return false;
665 }
666
667 bool success = geom->deleteVertex( position );
668
669 //remove geometry if no vertices left
670 if ( geom->isEmpty() )
671 {
672 removeGeometry( position.part );
673 }
674
675 if ( success )
676 {
677 clearCache(); //set bounding box invalid
678 }
679 return success;
680}
681
682bool QgsGeometryCollection::deleteVertices( const QSet<QgsVertexId> &positions )
683{
684 QMap<int, QSet<QgsVertexId>> partVertices;
685 for ( QgsVertexId pos : positions )
686 {
687 if ( !hasVertex( pos ) )
688 return false;
689
690 partVertices[pos.part].insert( QgsVertexId( 0, pos.ring, pos.vertex ) );
691 }
692
693 QMapIterator<int, QSet<QgsVertexId>> partVerticesIt( partVertices );
694 partVerticesIt.toBack();
695 while ( partVerticesIt.hasPrevious() )
696 {
697 partVerticesIt.previous();
698 int part = partVerticesIt.key();
699 QSet<QgsVertexId> partVertices = partVerticesIt.value();
700 QgsAbstractGeometry *geom = mGeometries.at( part );
701
703 {
704 removeGeometry( part );
705 continue;
706 }
707
708 // quit if any vertex on any part fails to be deleted
709 if ( !geom->deleteVertices( partVertices ) )
710 {
711 Q_ASSERT( false );
712 return false;
713 }
714
715 // remove geometry if no vertices left
716 if ( geom->isEmpty() )
717 {
718 removeGeometry( part );
719 }
720 }
721 clearCache(); // set bounding box invalid
722 return true;
723}
724
726{
727 size_t parts = mGeometries.size();
728 if ( id.part < 0 || static_cast<size_t>( id.part ) >= parts )
729 return false;
730
731 QgsAbstractGeometry *geom = mGeometries.at( id.part );
732 if ( !geom )
733 return false;
734
735 return geom->hasVertex( QgsVertexId( 0, id.ring, id.vertex ) );
736}
737
739{
740 double length = 0.0;
741 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
742 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
743 {
744 length += ( *geomIt )->length();
745 }
746 return length;
747}
748
750{
751 double area = 0.0;
752 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
753 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
754 {
755 area += ( *geomIt )->area();
756 }
757 return area;
758}
759
761{
762 double area3D = 0.0;
763 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
764 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
765 {
766 area3D += ( *geomIt )->area3D();
767 }
768 return area3D;
769}
770
772{
773 double perimeter = 0.0;
774 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
775 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
776 {
777 perimeter += ( *geomIt )->perimeter();
778 }
779 return perimeter;
780}
781
782bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<Qgis::WkbType> &subtypes, const QString &defaultChildWkbType )
783{
784 clear();
785
786 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
787
789 {
790 return false;
791 }
792 mWkbType = parts.first;
793
794 QString secondWithoutParentheses = parts.second;
795 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
796 if ( ( parts.second.compare( "EMPTY"_L1, Qt::CaseInsensitive ) == 0 ) || secondWithoutParentheses.isEmpty() )
797 {
798 return true;
799 }
800
801 QString defChildWkbType = u"%1%2%3 "_s.arg( defaultChildWkbType, is3D() ? u"Z"_s : QString(), isMeasure() ? u"M"_s : QString() );
802
803 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
804 for ( const QString &childWkt : blocks )
805 {
806 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
807
808 bool success = false;
809 for ( const Qgis::WkbType subtype : subtypes )
810 {
811 if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( subtype ) )
812 {
813 mGeometries.append( QgsGeometryFactory::geomFromWkbType( subtype ).release() );
814 if ( mGeometries.back()->fromWkt( childWkt ) )
815 {
816 success = true;
817 break;
818 }
819 }
820 }
821 if ( !success )
822 {
823 clear();
824 return false;
825 }
826 }
827
828 //scan through geometries and check if dimensionality of geometries is different to collection.
829 //if so, update the type dimensionality of the collection to match
830 bool hasZ = false;
831 bool hasM = false;
832 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
833 {
834 hasZ = hasZ || geom->is3D();
835 hasM = hasM || geom->isMeasure();
836 if ( hasZ && hasM )
837 break;
838 }
839 if ( hasZ )
840 addZValue( 0 );
841 if ( hasM )
842 addMValue( 0 );
843
844 return true;
845}
846
848{
849 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
850 for ( ; it != mGeometries.constEnd(); ++it )
851 {
852 if ( ( *it )->hasCurvedSegments() )
853 {
854 return true;
855 }
856 }
857 return false;
858}
859
861{
862 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
864 if ( !geomCollection )
865 {
866 return clone();
867 }
868
869 geomCollection->reserve( mGeometries.size() );
870 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
871 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
872 {
873 geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
874 }
875 return geom.release();
876}
877
879{
880 if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
881 {
882 return 0.0;
883 }
884
885 QgsAbstractGeometry *geom = mGeometries[vertex.part];
886 if ( !geom )
887 {
888 return 0.0;
889 }
890
891 return geom->vertexAngle( vertex );
892}
893
895{
896 if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
897 {
898 return 0.0;
899 }
900
901 const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
902 if ( !geom )
903 {
904 return 0.0;
905 }
906
907 return geom->segmentLength( startVertex );
908}
909
910int QgsGeometryCollection::vertexCount( int part, int ring ) const
911{
912 if ( part < 0 || part >= mGeometries.size() )
913 {
914 return 0;
915 }
916
917 return mGeometries[part]->vertexCount( 0, ring );
918}
919
921{
922 if ( part < 0 || part >= mGeometries.size() )
923 {
924 return 0;
925 }
926
927 return mGeometries[part]->ringCount();
928}
929
931{
932 return mGeometries.size();
933}
934
936{
937 if ( id.part < 0 || id.part >= mGeometries.size() )
938 {
939 return QgsPoint();
940 }
941
942 const QgsAbstractGeometry *geom = mGeometries[id.part];
943 if ( !geom )
944 {
945 return QgsPoint();
946 }
947
948 return geom->vertexAt( id );
949}
950
952{
953 if ( flags == 0 && mHasCachedValidity )
954 {
955 // use cached validity results
956 error = mValidityFailureReason;
957 return error.isEmpty();
958 }
959
960 QgsGeos geos( this, /* precision = */ 0, /* flags = */ Qgis::GeosCreationFlag::RejectOnInvalidSubGeometry );
961 bool res = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
962 if ( flags == 0 )
963 {
964 mValidityFailureReason = !res ? error : QString();
965 mHasCachedValidity = true;
966 }
967 return res;
968}
969
971{
973 return false;
974
976
977 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
978 {
979 geom->addZValue( zValue );
980 }
981 clearCache();
982 return true;
983}
984
986{
988 return false;
989
991
992 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
993 {
994 geom->addMValue( mValue );
995 }
996 clearCache();
997 return true;
998}
999
1000
1002{
1004 return false;
1005
1007 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1008 {
1009 geom->dropZValue();
1010 }
1011 clearCache();
1012 return true;
1013}
1014
1016{
1018 return false;
1019
1021 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1022 {
1023 geom->dropMValue();
1024 }
1025 clearCache();
1026 return true;
1027}
1028
1029void QgsGeometryCollection::filterVertices( const std::function<bool( const QgsPoint & )> &filter )
1030{
1031 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1032 {
1033 if ( geom )
1034 geom->filterVertices( filter );
1035 }
1036 clearCache();
1037}
1038
1040{
1041 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1042 {
1043 if ( geom )
1044 geom->transformVertices( transform );
1045 }
1046 clearCache();
1047}
1048
1050{
1051 // be tolerant if caller passed a multi type as type argument
1052 const Qgis::WkbType filterSinglePartType = useFlatType ? QgsWkbTypes::flatType( QgsWkbTypes::singleType( type ) ) : QgsWkbTypes::singleType( type );
1053
1054 std::unique_ptr< QgsGeometryCollection > res;
1055 switch ( QgsWkbTypes::geometryType( type ) )
1056 {
1058 {
1059 if ( useFlatType )
1060 {
1061 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1063 return mp->clone();
1064 }
1065
1066 res = std::make_unique< QgsMultiPoint >();
1067 break;
1068 }
1070 {
1071 if ( useFlatType )
1072 {
1073 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1075 return ml->clone();
1076 }
1077
1078 res = std::make_unique< QgsMultiLineString >();
1079 break;
1080 }
1082 {
1083 if ( useFlatType )
1084 {
1085 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1087 return mp->clone();
1088 }
1089
1090 res = std::make_unique< QgsMultiPolygon>();
1091 break;
1092 }
1093
1096 return nullptr;
1097 }
1098
1099 // assume that the collection consists entirely of matching parts (ie optimize for a pessimistic scenario)
1100 res->reserve( mGeometries.size() );
1101
1102 for ( const QgsAbstractGeometry *part : mGeometries )
1103 {
1104 if ( !part )
1105 continue;
1106
1107 const QgsAbstractGeometry *simplifiedPartType = part->simplifiedTypeRef();
1108
1109 const Qgis::WkbType thisPartType = useFlatType ? QgsWkbTypes::flatType( simplifiedPartType->wkbType() ) : simplifiedPartType->wkbType();
1110 if ( thisPartType == filterSinglePartType )
1111 {
1112 res->addGeometry( part->clone() );
1113 }
1114 }
1115
1116 return res.release();
1117}
1118
1120{
1121 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1122 {
1123 if ( geom )
1124 geom->swapXy();
1125 }
1126 clearCache();
1127}
1128
1130{
1131 auto newCollection = std::make_unique<QgsGeometryCollection>();
1132 newCollection->reserve( mGeometries.size() );
1133 for ( QgsAbstractGeometry *geom : mGeometries )
1134 {
1135 newCollection->addGeometry( geom->toCurveType() );
1136 }
1137 return newCollection.release();
1138}
1139
1141{
1142 if ( mGeometries.size() == 1 )
1143 return mGeometries.at( 0 )->simplifiedTypeRef();
1144 else
1145 return this;
1146}
1147
1149{
1150 auto res = std::make_unique< QgsGeometryCollection >();
1151 res->reserve( mGeometries.size() );
1152 for ( int i = 0; i < mGeometries.size(); ++i )
1153 {
1154 res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
1155 }
1156 return res.release();
1157}
1158
1160{
1161 if ( !transformer )
1162 return false;
1163
1164 bool res = true;
1165 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1166 {
1167 if ( geom )
1168 res = geom->transform( transformer, feedback );
1169
1170 if ( feedback && feedback->isCanceled() )
1171 res = false;
1172
1173 if ( !res )
1174 break;
1175 }
1176 clearCache();
1177 return res;
1178}
1179
1181{
1182 return false;
1183}
1184
1186{
1187 return mGeometries.count();
1188}
1189
1191{
1192 if ( index < 0 || index >= mGeometries.count() )
1193 return nullptr;
1194
1195 return mGeometries.at( index );
1196}
1197
1199{
1201 if ( !otherCollection )
1202 return -1;
1203
1204 int i = 0;
1205 int j = 0;
1206 while ( i < mGeometries.size() && j < otherCollection->mGeometries.size() )
1207 {
1208 const QgsAbstractGeometry *aGeom = mGeometries[i];
1209 const QgsAbstractGeometry *bGeom = otherCollection->mGeometries[j];
1210 const int comparison = aGeom->compareTo( bGeom );
1211 if ( comparison != 0 )
1212 {
1213 return comparison;
1214 }
1215 i++;
1216 j++;
1217 }
1218 if ( i < mGeometries.size() )
1219 {
1220 return 1;
1221 }
1222 if ( j < otherCollection->mGeometries.size() )
1223 {
1224 return -1;
1225 }
1226 return 0;
1227}
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2192
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2196
@ RejectOnInvalidSubGeometry
Don't allow geometries with invalid sub-geometries to be created.
Definition qgis.h:2281
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ CompoundCurve
CompoundCurve.
Definition qgis.h:305
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ TIN
TIN.
Definition qgis.h:310
@ MultiPoint
MultiPoint.
Definition qgis.h:300
@ Polygon
Polygon.
Definition qgis.h:298
@ MultiPolygon
MultiPolygon.
Definition qgis.h:302
@ Triangle
Triangle.
Definition qgis.h:299
@ MultiLineString
MultiLineString.
Definition qgis.h:301
@ CircularString
CircularString.
Definition qgis.h:304
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ MultiCurve
MultiCurve.
Definition qgis.h:307
@ CurvePolygon
CurvePolygon.
Definition qgis.h:306
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:309
@ MultiSurface
MultiSurface.
Definition qgis.h:308
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2831
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
virtual bool hasVertex(QgsVertexId position) const =0
Returns true if the geometry contains a vertex matching the given position.
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 bool deleteVertices(const QSet< QgsVertexId > &positions)=0
Deletes vertices within the geometry.
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:211
A const WKB pointer.
Definition qgswkbptr.h:211
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:56
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.
bool deleteVertices(const QSet< QgsVertexId > &positions) override
Deletes vertices within the geometry.
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.
bool hasVertex(QgsVertexId position) const override
Returns true if the geometry contains a vertex matching the given position.
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:175
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:47
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:53
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:112
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:35
int part
Part number.
Definition qgsvertexid.h:94