QGIS API Documentation 3.43.0-Master (261ee7da134)
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 "qgscircularstring.h"
22#include "qgscompoundcurve.h"
23#include "qgslinestring.h"
24#include "qgsmultilinestring.h"
25#include "qgspoint.h"
26#include "qgsmultipoint.h"
27#include "qgspolygon.h"
28#include "qgsmultipolygon.h"
29#include "qgstriangle.h"
32#include "qgswkbptr.h"
33#include "qgsgeos.h"
34#include "qgsfeedback.h"
35
36#include <nlohmann/json.hpp>
37#include <memory>
38
43
46 mBoundingBox( c.mBoundingBox ),
47 mHasCachedValidity( c.mHasCachedValidity ),
48 mValidityFailureReason( c.mValidityFailureReason )
49{
50 int nGeoms = c.mGeometries.size();
51 mGeometries.resize( nGeoms );
52 for ( int i = 0; i < nGeoms; ++i )
53 {
54 mGeometries[i] = c.mGeometries.at( i )->clone();
55 }
56}
57
58// cppcheck-suppress operatorEqVarError
60{
61 if ( &c != this )
62 {
64 int nGeoms = c.mGeometries.size();
65 mGeometries.resize( nGeoms );
66 for ( int i = 0; i < nGeoms; ++i )
67 {
68 mGeometries[i] = c.mGeometries.at( i )->clone();
69 }
70 }
71 return *this;
72}
73
78
80{
81 auto result = std::make_unique< QgsGeometryCollection >();
82 result->mWkbType = mWkbType;
83 return result.release();
84}
85
90
92{
93 qDeleteAll( mGeometries );
94 mGeometries.clear();
95 clearCache(); //set bounding box invalid
96}
97
98QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
99{
100 std::unique_ptr<QgsGeometryCollection> result;
101
102 for ( auto geom : mGeometries )
103 {
104 std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) };
105 if ( gridified )
106 {
107 if ( !result )
108 result = std::unique_ptr<QgsGeometryCollection> { createEmptyWithSameType() };
109
110 result->mGeometries.append( gridified.release() );
111 }
112 }
113
114 return result.release();
115}
116
117bool QgsGeometryCollection::removeDuplicateNodes( double epsilon, bool useZValues )
118{
119 bool result = false;
120 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
121 {
122 if ( geom->removeDuplicateNodes( epsilon, useZValues ) ) result = true;
123 }
124 return result;
125}
126
128{
129 return nullptr;
130}
131
132void QgsGeometryCollection::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
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 )
283 {
284 return a->compareTo( b ) > 0;
285 } );
286}
287
289{
290 int maxDim = 0;
291 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
292 for ( ; it != mGeometries.constEnd(); ++it )
293 {
294 int dim = ( *it )->dimension();
295 if ( dim > maxDim )
296 {
297 maxDim = dim;
298 }
299 }
300 return maxDim;
301}
302
304{
305 return QStringLiteral( "GeometryCollection" );
306}
307
309{
310 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
311 {
312 g->transform( ct, d, transformZ );
313 }
314 clearCache(); //set bounding box invalid
315}
316
317void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
318{
319 for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
320 {
321 g->transform( t, zTranslate, zScale, mTranslate, mScale );
322 }
323 clearCache(); //set bounding box invalid
324}
325
326void QgsGeometryCollection::draw( QPainter &p ) const
327{
328 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
329 for ( ; it != mGeometries.constEnd(); ++it )
330 {
331 ( *it )->draw( p );
332 }
333}
334
336{
337 QPainterPath p;
338 for ( const QgsAbstractGeometry *geom : mGeometries )
339 {
340 QPainterPath partPath = geom->asQPainterPath();
341 if ( !partPath.isEmpty() )
342 p.addPath( partPath );
343 }
344 return p;
345}
346
348{
349 if ( !wkbPtr )
350 {
351 return false;
352 }
353
356 return false;
357
359
360 int nGeometries = 0;
361 wkbPtr >> nGeometries;
362
363 QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
364 mGeometries.clear();
365 mGeometries.reserve( nGeometries );
366 for ( int i = 0; i < nGeometries; ++i )
367 {
368 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
369 if ( geom )
370 {
371 if ( !addGeometry( geom.release() ) )
372 {
373 qDeleteAll( mGeometries );
374 mGeometries = geometryListBackup;
375 return false;
376 }
377 }
378 }
379 qDeleteAll( geometryListBackup );
380
381 clearCache(); //set bounding box invalid
382
383 return true;
384}
385
386bool QgsGeometryCollection::fromWkt( const QString &wkt )
387{
388 return fromCollectionWkt( wkt, QVector<QgsAbstractGeometry *>() << new QgsPoint << new QgsLineString << new QgsPolygon //
389 << new QgsCircularString << new QgsCompoundCurve //
390 << new QgsCurvePolygon //
391 << new QgsMultiPoint << new QgsMultiLineString //
393 << new QgsMultiCurve << new QgsMultiSurface //
394 << new QgsTriangle << new QgsPolyhedralSurface //
395 << new QgsTriangulatedSurface //
396 ,
397 QStringLiteral( "GeometryCollection" ) );
398}
399
401{
402 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
403 for ( const QgsAbstractGeometry *geom : mGeometries )
404 {
405 if ( geom )
406 {
407 binarySize += geom->wkbSize( flags );
408 }
409 }
410
411 return binarySize;
412}
413
414QByteArray QgsGeometryCollection::asWkb( WkbFlags flags ) const
415{
416 int countNonNull = 0;
417 for ( const QgsAbstractGeometry *geom : mGeometries )
418 {
419 if ( geom )
420 {
421 countNonNull ++;
422 }
423 }
424
425 QByteArray wkbArray;
426 wkbArray.resize( QgsGeometryCollection::wkbSize( flags ) );
427 QgsWkbPtr wkb( wkbArray );
428 wkb << static_cast<char>( QgsApplication::endian() );
429 wkb << static_cast<quint32>( wkbType() );
430 wkb << static_cast<quint32>( countNonNull );
431 for ( const QgsAbstractGeometry *geom : mGeometries )
432 {
433 if ( geom )
434 {
435 wkb << geom->asWkb( flags );
436 }
437 }
438 return wkbArray;
439}
440
442{
443 QString wkt = wktTypeStr();
444
445 if ( isEmpty() )
446 wkt += QLatin1String( " EMPTY" );
447 else
448 {
449 wkt += QLatin1String( " (" );
450 for ( const QgsAbstractGeometry *geom : mGeometries )
451 {
452 QString childWkt = geom->asWkt( precision );
453 if ( wktOmitChildType() )
454 {
455 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
456 }
457 wkt += childWkt + ',';
458 }
459 if ( wkt.endsWith( ',' ) )
460 {
461 wkt.chop( 1 ); // Remove last ','
462 }
463 wkt += ')';
464 }
465 return wkt;
466}
467
468QDomElement QgsGeometryCollection::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
469{
470 QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
471 for ( const QgsAbstractGeometry *geom : mGeometries )
472 {
473 QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
474 elemGeometryMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
475 elemMultiGeometry.appendChild( elemGeometryMember );
476 }
477 return elemMultiGeometry;
478}
479
480QDomElement QgsGeometryCollection::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
481{
482 QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
483 for ( const QgsAbstractGeometry *geom : mGeometries )
484 {
485 QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
486 elemGeometryMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
487 elemMultiGeometry.appendChild( elemGeometryMember );
488 }
489 return elemMultiGeometry;
490}
491
493{
494 json coordinates( json::array( ) );
495 for ( const QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
496 {
497 coordinates.push_back( geom->asJsonObject( precision ) );
498 }
499 return
500 {
501 { "type", "GeometryCollection" },
502 { "geometries", coordinates }
503 };
504}
505
507{
508 QString kml;
509 kml.append( QLatin1String( "<MultiGeometry>" ) );
510 const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
511 for ( const QgsAbstractGeometry *geometry : geometries )
512 {
513 kml.append( geometry->asKml( precision ) );
514 }
515 kml.append( QLatin1String( "</MultiGeometry>" ) );
516 return kml;
517}
518
520{
521 if ( mBoundingBox.isNull() )
522 {
523 mBoundingBox = calculateBoundingBox3D();
524 }
525 return mBoundingBox;
526}
527
529{
530 if ( mGeometries.empty() )
531 {
532 return QgsBox3D();
533 }
534
535 QgsBox3D bbox = mGeometries.at( 0 )->boundingBox3D();
536 for ( int i = 1; i < mGeometries.size(); ++i )
537 {
538 if ( mGeometries.at( i )->isEmpty() )
539 continue;
540
541 QgsBox3D geomBox = mGeometries.at( i )->boundingBox3D();
542 bbox.combineWith( geomBox );
543 }
544 return bbox;
545}
546
548{
549 mBoundingBox = QgsBox3D();
550 mHasCachedValidity = false;
551 mValidityFailureReason.clear();
553}
554
556{
557 QgsCoordinateSequence sequence;
558 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
559 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
560 {
561 QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
562
563 QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
564 for ( ; cIt != geomCoords.constEnd(); ++cIt )
565 {
566 sequence.push_back( *cIt );
567 }
568 }
569
570 return sequence;
571}
572
574{
575 int count = 0;
576
577 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
578 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
579 {
580 count += ( *geomIt )->nCoordinates();
581 }
582
583 return count;
584}
585
586double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
587{
588 return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
589}
590
592{
593 if ( id.part < 0 )
594 {
595 id.part = 0;
596 id.ring = -1;
597 id.vertex = -1;
598 }
599 if ( mGeometries.isEmpty() )
600 {
601 return false;
602 }
603
604 if ( id.part >= mGeometries.count() )
605 return false;
606
607 QgsAbstractGeometry *geom = mGeometries.at( id.part );
608 if ( geom->nextVertex( id, vertex ) )
609 {
610 return true;
611 }
612 if ( ( id.part + 1 ) >= numGeometries() )
613 {
614 return false;
615 }
616 ++id.part;
617 id.ring = -1;
618 id.vertex = -1;
619 return mGeometries.at( id.part )->nextVertex( id, vertex );
620}
621
623{
624 if ( position.part >= mGeometries.size() )
625 {
626 return false;
627 }
628
629 bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
630 if ( success )
631 {
632 clearCache(); //set bounding box invalid
633 }
634 return success;
635}
636
638{
639 if ( position.part < 0 || position.part >= mGeometries.size() )
640 {
641 return false;
642 }
643
644 bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
645 if ( success )
646 {
647 clearCache(); //set bounding box invalid
648 }
649 return success;
650}
651
653{
654 if ( position.part < 0 || position.part >= mGeometries.size() )
655 {
656 return false;
657 }
658
659 QgsAbstractGeometry *geom = mGeometries.at( position.part );
660 if ( !geom )
661 {
662 return false;
663 }
664
665 bool success = geom->deleteVertex( position );
666
667 //remove geometry if no vertices left
668 if ( geom->isEmpty() )
669 {
670 removeGeometry( position.part );
671 }
672
673 if ( success )
674 {
675 clearCache(); //set bounding box invalid
676 }
677 return success;
678}
679
681{
682 double length = 0.0;
683 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
684 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
685 {
686 length += ( *geomIt )->length();
687 }
688 return length;
689}
690
692{
693 double area = 0.0;
694 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
695 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
696 {
697 area += ( *geomIt )->area();
698 }
699 return area;
700}
701
703{
704 double perimeter = 0.0;
705 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
706 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
707 {
708 perimeter += ( *geomIt )->perimeter();
709 }
710 return perimeter;
711}
712
713bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<QgsAbstractGeometry *> &subtypes, const QString &defaultChildWkbType )
714{
715 clear();
716
717 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
718
720 {
721 qDeleteAll( subtypes );
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 qDeleteAll( subtypes );
732 return true;
733 }
734
735 QString defChildWkbType = QStringLiteral( "%1%2%3 " ).arg( defaultChildWkbType, is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
736
737 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
738 for ( const QString &childWkt : blocks )
739 {
740 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
741
742 bool success = false;
743 for ( const QgsAbstractGeometry *geom : subtypes )
744 {
745 if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( geom->wkbType() ) )
746 {
747 mGeometries.append( geom->clone() );
748 if ( mGeometries.back()->fromWkt( childWkt ) )
749 {
750 success = true;
751 break;
752 }
753 }
754 }
755 if ( !success )
756 {
757 clear();
758 qDeleteAll( subtypes );
759 return false;
760 }
761 }
762 qDeleteAll( subtypes );
763
764 //scan through geometries and check if dimensionality of geometries is different to collection.
765 //if so, update the type dimensionality of the collection to match
766 bool hasZ = false;
767 bool hasM = false;
768 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
769 {
770 hasZ = hasZ || geom->is3D();
771 hasM = hasM || geom->isMeasure();
772 if ( hasZ && hasM )
773 break;
774 }
775 if ( hasZ )
776 addZValue( 0 );
777 if ( hasM )
778 addMValue( 0 );
779
780 return true;
781}
782
784{
785 QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
786 for ( ; it != mGeometries.constEnd(); ++it )
787 {
788 if ( ( *it )->hasCurvedSegments() )
789 {
790 return true;
791 }
792 }
793 return false;
794}
795
797{
798 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
799 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
800 if ( !geomCollection )
801 {
802 return clone();
803 }
804
805 geomCollection->reserve( mGeometries.size() );
806 QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
807 for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
808 {
809 geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
810 }
811 return geom.release();
812}
813
815{
816 if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
817 {
818 return 0.0;
819 }
820
821 QgsAbstractGeometry *geom = mGeometries[vertex.part];
822 if ( !geom )
823 {
824 return 0.0;
825 }
826
827 return geom->vertexAngle( vertex );
828}
829
831{
832 if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
833 {
834 return 0.0;
835 }
836
837 const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
838 if ( !geom )
839 {
840 return 0.0;
841 }
842
843 return geom->segmentLength( startVertex );
844}
845
846int QgsGeometryCollection::vertexCount( int part, int ring ) const
847{
848 if ( part < 0 || part >= mGeometries.size() )
849 {
850 return 0;
851 }
852
853 return mGeometries[part]->vertexCount( 0, ring );
854}
855
857{
858 if ( part < 0 || part >= mGeometries.size() )
859 {
860 return 0;
861 }
862
863 return mGeometries[part]->ringCount();
864}
865
867{
868 return mGeometries.size();
869}
870
872{
873 if ( id.part < 0 || id.part >= mGeometries.size() )
874 {
875 return QgsPoint();
876 }
877
878 const QgsAbstractGeometry *geom = mGeometries[id.part];
879 if ( !geom )
880 {
881 return QgsPoint();
882 }
883
884 return geom->vertexAt( id );
885}
886
888{
889 if ( flags == 0 && mHasCachedValidity )
890 {
891 // use cached validity results
892 error = mValidityFailureReason;
893 return error.isEmpty();
894 }
895
896 QgsGeos geos( this, /* precision = */ 0, /* flags = */ Qgis::GeosCreationFlag::RejectOnInvalidSubGeometry );
897 bool res = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
898 if ( flags == 0 )
899 {
900 mValidityFailureReason = !res ? error : QString();
901 mHasCachedValidity = true;
902 }
903 return res;
904}
905
907{
909 return false;
910
912
913 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
914 {
915 geom->addZValue( zValue );
916 }
917 clearCache();
918 return true;
919}
920
922{
924 return false;
925
927
928 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
929 {
930 geom->addMValue( mValue );
931 }
932 clearCache();
933 return true;
934}
935
936
938{
940 return false;
941
943 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
944 {
945 geom->dropZValue();
946 }
947 clearCache();
948 return true;
949}
950
952{
954 return false;
955
957 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
958 {
959 geom->dropMValue();
960 }
961 clearCache();
962 return true;
963}
964
965void QgsGeometryCollection::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
966{
967 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
968 {
969 if ( geom )
970 geom->filterVertices( filter );
971 }
972 clearCache();
973}
974
975void QgsGeometryCollection::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
976{
977 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
978 {
979 if ( geom )
980 geom->transformVertices( transform );
981 }
982 clearCache();
983}
984
986{
987 // be tolerant if caller passed a multi type as type argument
988 const Qgis::WkbType filterSinglePartType = useFlatType ? QgsWkbTypes::flatType( QgsWkbTypes::singleType( type ) ) : QgsWkbTypes::singleType( type );
989
990 std::unique_ptr< QgsGeometryCollection > res;
991 switch ( QgsWkbTypes::geometryType( type ) )
992 {
994 {
995 if ( useFlatType )
996 {
997 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
998 if ( const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint *>( this ) )
999 return mp->clone();
1000 }
1001
1002 res = std::make_unique< QgsMultiPoint >();
1003 break;
1004 }
1006 {
1007 if ( useFlatType )
1008 {
1009 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1010 if ( const QgsMultiLineString *ml = qgsgeometry_cast< const QgsMultiLineString *>( this ) )
1011 return ml->clone();
1012 }
1013
1014 res = std::make_unique< QgsMultiLineString >();
1015 break;
1016 }
1018 {
1019 if ( useFlatType )
1020 {
1021 // potential shortcut if we're already a matching subclass of QgsGeometryCollection
1022 if ( const QgsMultiPolygon *mp = qgsgeometry_cast< const QgsMultiPolygon *>( this ) )
1023 return mp->clone();
1024 }
1025
1026 res = std::make_unique< QgsMultiPolygon>();
1027 break;
1028 }
1029
1032 return nullptr;
1033 }
1034
1035 // assume that the collection consists entirely of matching parts (ie optimize for a pessimistic scenario)
1036 res->reserve( mGeometries.size() );
1037
1038 for ( const QgsAbstractGeometry *part : mGeometries )
1039 {
1040 if ( !part )
1041 continue;
1042
1043 const QgsAbstractGeometry *simplifiedPartType = part->simplifiedTypeRef();
1044
1045 const Qgis::WkbType thisPartType = useFlatType ? QgsWkbTypes::flatType( simplifiedPartType->wkbType() ) : simplifiedPartType->wkbType();
1046 if ( thisPartType == filterSinglePartType )
1047 {
1048 res->addGeometry( part->clone() );
1049 }
1050 }
1051
1052 return res.release();
1053}
1054
1056{
1057 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1058 {
1059 if ( geom )
1060 geom->swapXy();
1061 }
1062 clearCache();
1063}
1064
1066{
1067 auto newCollection = std::make_unique<QgsGeometryCollection>();
1068 newCollection->reserve( mGeometries.size() );
1069 for ( QgsAbstractGeometry *geom : mGeometries )
1070 {
1071 newCollection->addGeometry( geom->toCurveType() );
1072 }
1073 return newCollection.release();
1074}
1075
1077{
1078 if ( mGeometries.size() == 1 )
1079 return mGeometries.at( 0 )->simplifiedTypeRef();
1080 else
1081 return this;
1082}
1083
1085{
1086 auto res = std::make_unique< QgsGeometryCollection >();
1087 res->reserve( mGeometries.size() );
1088 for ( int i = 0; i < mGeometries.size(); ++i )
1089 {
1090 res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
1091 }
1092 return res.release();
1093}
1094
1096{
1097 if ( !transformer )
1098 return false;
1099
1100 bool res = true;
1101 for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1102 {
1103 if ( geom )
1104 res = geom->transform( transformer, feedback );
1105
1106 if ( feedback && feedback->isCanceled() )
1107 res = false;
1108
1109 if ( !res )
1110 break;
1111 }
1112 clearCache();
1113 return res;
1114}
1115
1117{
1118 return false;
1119}
1120
1122{
1123 return mGeometries.count();
1124}
1125
1127{
1128 if ( index < 0 || index >= mGeometries.count() )
1129 return nullptr;
1130
1131 return mGeometries.at( index );
1132}
1133
1135{
1136 const QgsGeometryCollection *otherCollection = qgsgeometry_cast<const QgsGeometryCollection *>( other );
1137 if ( !otherCollection )
1138 return -1;
1139
1140 int i = 0;
1141 int j = 0;
1142 while ( i < mGeometries.size() && j < otherCollection->mGeometries.size() )
1143 {
1144 const QgsAbstractGeometry *aGeom = mGeometries[i];
1145 const QgsAbstractGeometry *bGeom = otherCollection->mGeometries[j];
1146 const int comparison = aGeom->compareTo( bGeom );
1147 if ( comparison != 0 )
1148 {
1149 return comparison;
1150 }
1151 i++;
1152 j++;
1153 }
1154 if ( i < mGeometries.size() )
1155 {
1156 return 1;
1157 }
1158 if ( j < otherCollection->mGeometries.size() )
1159 {
1160 return -1;
1161 }
1162 return 0;
1163}
@ 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
@ GeometryCollection
GeometryCollection.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2619
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
Circular string geometry type.
Compound curve geometry type.
A const WKB pointer.
Definition qgswkbptr.h:138
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:55
Handles coordinate transforms between two coordinate systems.
Curve polygon geometry type.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h: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 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.
bool fromCollectionWkt(const QString &wkt, const QVector< QgsAbstractGeometry * > &subtypes, const QString &defaultChildWkbType=QString())
Reads a collection from a WKT string.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
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
Line string geometry type, with support for z-dimension and m-values.
Multi curve geometry collection.
Multi line string geometry collection.
Multi point geometry collection.
Multi polygon geometry collection.
Multi surface geometry collection.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Polygon geometry type.
Definition qgspolygon.h:33
Polyhedral surface geometry type.
Triangle geometry type.
Definition qgstriangle.h:33
Triangulated surface geometry type.
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