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 *
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 
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() )
360  p.addPath( partPath );
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 )
804  addZValue( 0 );
805  if ( hasM )
806  addMValue( 0 );
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  {
932  geom->addZValue( zValue );
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  {
947  geom->addMValue( mValue );
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  {
1018  newCollection->addGeometry( geom->toCurveType() );
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
QgsWkbTypes::Type readHeader() const
readHeader
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.
QString asKml(int precision=17) const override
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.
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.
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.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
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
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
static Type addZ(Type type) SIP_HOLDGIL
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
static Type addM(Type type) SIP_HOLDGIL
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.