QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 = qgis::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 : qgis::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  mGeometries.reserve( size );
206 }
207 
209 {
210  clearCache();
211  return mGeometries.value( n );
212 }
213 
215 {
216  if ( mGeometries.isEmpty() )
217  return true;
218 
219  for ( QgsAbstractGeometry *geometry : mGeometries )
220  {
221  if ( !geometry->isEmpty() )
222  return false;
223  }
224  return true;
225 }
226 
228 {
229  if ( !g )
230  {
231  return false;
232  }
233 
234  mGeometries.append( g );
235  clearCache(); //set bounding box invalid
236  return true;
237 }
238 
240 {
241  if ( !g )
242  {
243  return false;
244  }
245 
246  index = std::min( mGeometries.count(), index );
247 
248  mGeometries.insert( index, g );
249  clearCache(); //set bounding box invalid
250  return true;
251 }
252 
254 {
255  if ( nr >= mGeometries.size() || nr < 0 )
256  {
257  return false;
258  }
259  delete mGeometries.at( nr );
260  mGeometries.remove( nr );
261  clearCache(); //set bounding box invalid
262  return true;
263 }
264 
266 {
267  int maxDim = 0;
268  QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
269  for ( ; it != mGeometries.constEnd(); ++it )
270  {
271  int dim = ( *it )->dimension();
272  if ( dim > maxDim )
273  {
274  maxDim = dim;
275  }
276  }
277  return maxDim;
278 }
279 
281 {
282  return QStringLiteral( "GeometryCollection" );
283 }
284 
286 {
287  for ( QgsAbstractGeometry *g : qgis::as_const( mGeometries ) )
288  {
289  g->transform( ct, d, transformZ );
290  }
291  clearCache(); //set bounding box invalid
292 }
293 
294 void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
295 {
296  for ( QgsAbstractGeometry *g : qgis::as_const( mGeometries ) )
297  {
298  g->transform( t, zTranslate, zScale, mTranslate, mScale );
299  }
300  clearCache(); //set bounding box invalid
301 }
302 
303 void QgsGeometryCollection::draw( QPainter &p ) const
304 {
305  QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
306  for ( ; it != mGeometries.constEnd(); ++it )
307  {
308  ( *it )->draw( p );
309  }
310 }
311 
313 {
314  QPainterPath p;
315  for ( const QgsAbstractGeometry *geom : mGeometries )
316  {
317  QPainterPath partPath = geom->asQPainterPath();
318  if ( !partPath.isEmpty() )
319  p.addPath( partPath );
320  }
321  return p;
322 }
323 
325 {
326  if ( !wkbPtr )
327  {
328  return false;
329  }
330 
333  return false;
334 
335  mWkbType = wkbType;
336 
337  int nGeometries = 0;
338  wkbPtr >> nGeometries;
339 
340  QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
341  mGeometries.clear();
342  mGeometries.reserve( nGeometries );
343  for ( int i = 0; i < nGeometries; ++i )
344  {
345  std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
346  if ( geom )
347  {
348  if ( !addGeometry( geom.release() ) )
349  {
350  qDeleteAll( mGeometries );
351  mGeometries = geometryListBackup;
352  return false;
353  }
354  }
355  }
356  qDeleteAll( geometryListBackup );
357 
358  clearCache(); //set bounding box invalid
359 
360  return true;
361 }
362 
363 bool QgsGeometryCollection::fromWkt( const QString &wkt )
364 {
365  return fromCollectionWkt( wkt, QVector<QgsAbstractGeometry *>() << new QgsPoint << new QgsLineString << new QgsPolygon
366  << new QgsCircularString << new QgsCompoundCurve
367  << new QgsCurvePolygon
368  << new QgsMultiPoint << new QgsMultiLineString
370  << new QgsMultiCurve << new QgsMultiSurface, QStringLiteral( "GeometryCollection" ) );
371 }
372 
373 int QgsGeometryCollection::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
374 {
375  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
376  for ( const QgsAbstractGeometry *geom : mGeometries )
377  {
378  if ( geom )
379  {
380  binarySize += geom->wkbSize( flags );
381  }
382  }
383 
384  return binarySize;
385 }
386 
387 QByteArray QgsGeometryCollection::asWkb( WkbFlags flags ) const
388 {
389  int countNonNull = 0;
390  for ( const QgsAbstractGeometry *geom : mGeometries )
391  {
392  if ( geom )
393  {
394  countNonNull ++;
395  }
396  }
397 
398  QByteArray wkbArray;
399  wkbArray.resize( QgsGeometryCollection::wkbSize( flags ) );
400  QgsWkbPtr wkb( wkbArray );
401  wkb << static_cast<char>( QgsApplication::endian() );
402  wkb << static_cast<quint32>( wkbType() );
403  wkb << static_cast<quint32>( countNonNull );
404  for ( const QgsAbstractGeometry *geom : mGeometries )
405  {
406  if ( geom )
407  {
408  wkb << geom->asWkb( flags );
409  }
410  }
411  return wkbArray;
412 }
413 
415 {
416  QString wkt = wktTypeStr();
417 
418  if ( isEmpty() )
419  wkt += QLatin1String( " EMPTY" );
420  else
421  {
422  wkt += QLatin1String( " (" );
423  for ( const QgsAbstractGeometry *geom : mGeometries )
424  {
425  QString childWkt = geom->asWkt( precision );
426  if ( wktOmitChildType() )
427  {
428  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
429  }
430  wkt += childWkt + ',';
431  }
432  if ( wkt.endsWith( ',' ) )
433  {
434  wkt.chop( 1 ); // Remove last ','
435  }
436  wkt += ')';
437  }
438  return wkt;
439 }
440 
441 QDomElement QgsGeometryCollection::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
442 {
443  QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
444  for ( const QgsAbstractGeometry *geom : mGeometries )
445  {
446  QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
447  elemGeometryMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
448  elemMultiGeometry.appendChild( elemGeometryMember );
449  }
450  return elemMultiGeometry;
451 }
452 
453 QDomElement QgsGeometryCollection::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
454 {
455  QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
456  for ( const QgsAbstractGeometry *geom : mGeometries )
457  {
458  QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
459  elemGeometryMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
460  elemMultiGeometry.appendChild( elemGeometryMember );
461  }
462  return elemMultiGeometry;
463 }
464 
466 {
467  json coordinates( json::array( ) );
468  for ( const QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
469  {
470  coordinates.push_back( geom->asJsonObject( precision ) );
471  }
472  return
473  {
474  { "type", "GeometryCollection" },
475  { "geometries", coordinates }
476  };
477 }
478 
480 {
481  QString kml;
482  kml.append( QLatin1String( "<MultiGeometry>" ) );
483  const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
484  for ( const QgsAbstractGeometry *geometry : geometries )
485  {
486  kml.append( geometry->asKml( precision ) );
487  }
488  kml.append( QLatin1String( "</MultiGeometry>" ) );
489  return kml;
490 }
491 
493 {
494  if ( mBoundingBox.isNull() )
495  {
496  mBoundingBox = calculateBoundingBox();
497  }
498  return mBoundingBox;
499 }
500 
502 {
503  if ( mGeometries.empty() )
504  {
505  return QgsRectangle();
506  }
507 
508  QgsRectangle bbox = mGeometries.at( 0 )->boundingBox();
509  for ( int i = 1; i < mGeometries.size(); ++i )
510  {
511  if ( mGeometries.at( i )->isEmpty() )
512  continue;
513 
514  QgsRectangle geomBox = mGeometries.at( i )->boundingBox();
515  if ( bbox.isNull() )
516  {
517  // workaround treatment of a QgsRectangle(0,0,0,0) as a "null"/invalid rectangle
518  // if bbox is null, then the first geometry must have returned a bounding box of (0,0,0,0)
519  // so just manually include that as a point... ew.
520  geomBox.combineExtentWith( QPointF( 0, 0 ) );
521  bbox = geomBox;
522  }
523  else if ( geomBox.isNull() )
524  {
525  // ...as above... this part must have a bounding box of (0,0,0,0).
526  // if we try to combine the extent with this "null" box it will just be ignored.
527  bbox.combineExtentWith( QPointF( 0, 0 ) );
528  }
529  else
530  {
531  bbox.combineExtentWith( geomBox );
532  }
533  }
534  return bbox;
535 }
536 
538 {
539  mBoundingBox = QgsRectangle();
540  mHasCachedValidity = false;
541  mValidityFailureReason.clear();
543 }
544 
546 {
547  QgsCoordinateSequence sequence;
548  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
549  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
550  {
551  QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
552 
553  QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
554  for ( ; cIt != geomCoords.constEnd(); ++cIt )
555  {
556  sequence.push_back( *cIt );
557  }
558  }
559 
560  return sequence;
561 }
562 
564 {
565  int count = 0;
566 
567  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
568  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
569  {
570  count += ( *geomIt )->nCoordinates();
571  }
572 
573  return count;
574 }
575 
576 double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
577 {
578  return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
579 }
580 
582 {
583  if ( id.part < 0 )
584  {
585  id.part = 0;
586  id.ring = -1;
587  id.vertex = -1;
588  }
589  if ( mGeometries.isEmpty() )
590  {
591  return false;
592  }
593 
594  if ( id.part >= mGeometries.count() )
595  return false;
596 
597  QgsAbstractGeometry *geom = mGeometries.at( id.part );
598  if ( geom->nextVertex( id, vertex ) )
599  {
600  return true;
601  }
602  if ( ( id.part + 1 ) >= numGeometries() )
603  {
604  return false;
605  }
606  ++id.part;
607  id.ring = -1;
608  id.vertex = -1;
609  return mGeometries.at( id.part )->nextVertex( id, vertex );
610 }
611 
613 {
614  if ( position.part >= mGeometries.size() )
615  {
616  return false;
617  }
618 
619  bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
620  if ( success )
621  {
622  clearCache(); //set bounding box invalid
623  }
624  return success;
625 }
626 
628 {
629  if ( position.part < 0 || position.part >= mGeometries.size() )
630  {
631  return false;
632  }
633 
634  bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
635  if ( success )
636  {
637  clearCache(); //set bounding box invalid
638  }
639  return success;
640 }
641 
643 {
644  if ( position.part < 0 || position.part >= mGeometries.size() )
645  {
646  return false;
647  }
648 
649  QgsAbstractGeometry *geom = mGeometries.at( position.part );
650  if ( !geom )
651  {
652  return false;
653  }
654 
655  bool success = geom->deleteVertex( position );
656 
657  //remove geometry if no vertices left
658  if ( geom->isEmpty() )
659  {
660  removeGeometry( position.part );
661  }
662 
663  if ( success )
664  {
665  clearCache(); //set bounding box invalid
666  }
667  return success;
668 }
669 
671 {
672  double length = 0.0;
673  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
674  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
675  {
676  length += ( *geomIt )->length();
677  }
678  return length;
679 }
680 
682 {
683  double area = 0.0;
684  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
685  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
686  {
687  area += ( *geomIt )->area();
688  }
689  return area;
690 }
691 
693 {
694  double perimeter = 0.0;
695  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
696  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
697  {
698  perimeter += ( *geomIt )->perimeter();
699  }
700  return perimeter;
701 }
702 
703 bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<QgsAbstractGeometry *> &subtypes, const QString &defaultChildWkbType )
704 {
705  clear();
706 
707  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
708 
710  {
711  qDeleteAll( subtypes );
712  return false;
713  }
714  mWkbType = parts.first;
715 
716  QString secondWithoutParentheses = parts.second;
717  secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
718  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
719  secondWithoutParentheses.isEmpty() )
720  return true;
721 
722  QString defChildWkbType = QStringLiteral( "%1%2%3 " ).arg( defaultChildWkbType, is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
723 
724  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
725  for ( const QString &childWkt : blocks )
726  {
727  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
728 
729  bool success = false;
730  for ( const QgsAbstractGeometry *geom : subtypes )
731  {
732  if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( geom->wkbType() ) )
733  {
734  mGeometries.append( geom->clone() );
735  if ( mGeometries.back()->fromWkt( childWkt ) )
736  {
737  success = true;
738  break;
739  }
740  }
741  }
742  if ( !success )
743  {
744  clear();
745  qDeleteAll( subtypes );
746  return false;
747  }
748  }
749  qDeleteAll( subtypes );
750 
751  //scan through geometries and check if dimensionality of geometries is different to collection.
752  //if so, update the type dimensionality of the collection to match
753  bool hasZ = false;
754  bool hasM = false;
755  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
756  {
757  hasZ = hasZ || geom->is3D();
758  hasM = hasM || geom->isMeasure();
759  if ( hasZ && hasM )
760  break;
761  }
762  if ( hasZ )
763  addZValue( 0 );
764  if ( hasM )
765  addMValue( 0 );
766 
767  return true;
768 }
769 
771 {
772  QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
773  for ( ; it != mGeometries.constEnd(); ++it )
774  {
775  if ( ( *it )->hasCurvedSegments() )
776  {
777  return true;
778  }
779  }
780  return false;
781 }
782 
784 {
785  std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
786  QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
787  if ( !geomCollection )
788  {
789  return clone();
790  }
791 
792  geomCollection->reserve( mGeometries.size() );
793  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
794  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
795  {
796  geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
797  }
798  return geom.release();
799 }
800 
802 {
803  if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
804  {
805  return 0.0;
806  }
807 
808  QgsAbstractGeometry *geom = mGeometries[vertex.part];
809  if ( !geom )
810  {
811  return 0.0;
812  }
813 
814  return geom->vertexAngle( vertex );
815 }
816 
818 {
819  if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
820  {
821  return 0.0;
822  }
823 
824  const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
825  if ( !geom )
826  {
827  return 0.0;
828  }
829 
830  return geom->segmentLength( startVertex );
831 }
832 
833 int QgsGeometryCollection::vertexCount( int part, int ring ) const
834 {
835  if ( part < 0 || part >= mGeometries.size() )
836  {
837  return 0;
838  }
839 
840  return mGeometries[part]->vertexCount( 0, ring );
841 }
842 
843 int QgsGeometryCollection::ringCount( int part ) const
844 {
845  if ( part < 0 || part >= mGeometries.size() )
846  {
847  return 0;
848  }
849 
850  return mGeometries[part]->ringCount();
851 }
852 
854 {
855  return mGeometries.size();
856 }
857 
859 {
860  return mGeometries[id.part]->vertexAt( id );
861 }
862 
863 bool QgsGeometryCollection::isValid( QString &error, int flags ) const
864 {
865  if ( flags == 0 && mHasCachedValidity )
866  {
867  // use cached validity results
868  error = mValidityFailureReason;
869  return error.isEmpty();
870  }
871 
872  QgsGeos geos( this );
873  bool res = geos.isValid( &error, flags & QgsGeometry::FlagAllowSelfTouchingHoles, nullptr );
874  if ( flags == 0 )
875  {
876  mValidityFailureReason = !res ? error : QString();
877  mHasCachedValidity = true;
878  }
879  return res;
880 }
881 
882 bool QgsGeometryCollection::addZValue( double zValue )
883 {
884  if ( QgsWkbTypes::hasZ( mWkbType ) )
885  return false;
886 
888 
889  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
890  {
891  geom->addZValue( zValue );
892  }
893  clearCache();
894  return true;
895 }
896 
897 bool QgsGeometryCollection::addMValue( double mValue )
898 {
899  if ( QgsWkbTypes::hasM( mWkbType ) )
900  return false;
901 
903 
904  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
905  {
906  geom->addMValue( mValue );
907  }
908  clearCache();
909  return true;
910 }
911 
912 
914 {
916  return false;
917 
919  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
920  {
921  geom->dropZValue();
922  }
923  clearCache();
924  return true;
925 }
926 
928 {
930  return false;
931 
933  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
934  {
935  geom->dropMValue();
936  }
937  clearCache();
938  return true;
939 }
940 
941 void QgsGeometryCollection::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
942 {
943  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
944  {
945  if ( geom )
946  geom->filterVertices( filter );
947  }
948  clearCache();
949 }
950 
951 void QgsGeometryCollection::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
952 {
953  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
954  {
955  if ( geom )
956  geom->transformVertices( transform );
957  }
958  clearCache();
959 }
960 
962 {
963  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
964  {
965  if ( geom )
966  geom->swapXy();
967  }
968  clearCache();
969 }
970 
972 {
973  std::unique_ptr< QgsGeometryCollection > newCollection( new QgsGeometryCollection() );
974  newCollection->reserve( mGeometries.size() );
975  for ( QgsAbstractGeometry *geom : mGeometries )
976  {
977  newCollection->addGeometry( geom->toCurveType() );
978  }
979  return newCollection.release();
980 }
981 
983 {
984  if ( !transformer )
985  return false;
986 
987  bool res = true;
988  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
989  {
990  if ( geom )
991  res = geom->transform( transformer, feedback );
992 
993  if ( feedback && feedback->isCanceled() )
994  res = false;
995 
996  if ( !res )
997  break;
998  }
999  clearCache();
1000  return res;
1001 }
1002 
1004 {
1005  return false;
1006 }
1007 
1009 {
1010  return mGeometries.count();
1011 }
1012 
1014 {
1015  if ( index < 0 || index > mGeometries.count() )
1016  return nullptr;
1017  return mGeometries.at( index );
1018 }
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.
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.
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:130
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
Class for doing transforms between two map coordinate systems.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
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
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.
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 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 transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
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 isValid(QString &error, int flags=0) const override
Checks validity of the geometry, and returns true if the geometry is valid.
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.
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.
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)
@ FlagAllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition: qgsgeometry.h:369
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:38
Polygon geometry type.
Definition: qgspolygon.h:34
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:447
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
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.