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