QGIS API Documentation  3.17.0-Master (a035f434f4)
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 
31 #include <nlohmann/json.hpp>
32 #include <memory>
33 
35 {
37 }
38 
41  mBoundingBox( c.mBoundingBox ),
42  mHasCachedValidity( c.mHasCachedValidity ),
43  mValidityFailureReason( c.mValidityFailureReason )
44 {
45  int nGeoms = c.mGeometries.size();
46  mGeometries.resize( nGeoms );
47  for ( int i = 0; i < nGeoms; ++i )
48  {
49  mGeometries[i] = c.mGeometries.at( i )->clone();
50  }
51 }
52 
54 {
55  if ( &c != this )
56  {
57  clearCache();
59  int nGeoms = c.mGeometries.size();
60  mGeometries.resize( nGeoms );
61  for ( int i = 0; i < nGeoms; ++i )
62  {
63  mGeometries[i] = c.mGeometries.at( i )->clone();
64  }
65  }
66  return *this;
67 }
68 
70 {
71  clear();
72 }
73 
75 {
76  const QgsGeometryCollection *otherCollection = qgsgeometry_cast< const QgsGeometryCollection * >( &other );
77  if ( !otherCollection )
78  return false;
79 
80  if ( mWkbType != otherCollection->mWkbType )
81  return false;
82 
83  if ( mGeometries.count() != otherCollection->mGeometries.count() )
84  return false;
85 
86  for ( int i = 0; i < mGeometries.count(); ++i )
87  {
88  QgsAbstractGeometry *g1 = mGeometries.at( i );
89  QgsAbstractGeometry *g2 = otherCollection->mGeometries.at( i );
90 
91  // Quick check if the geometries are exactly the same
92  if ( g1 != g2 )
93  {
94  if ( !g1 || !g2 )
95  return false;
96 
97  // Slower check, compare the contents of the geometries
98  if ( *g1 != *g2 )
99  return false;
100  }
101  }
102 
103  return true;
104 }
105 
107 {
108  return !operator==( other );
109 }
110 
112 {
113  auto result = qgis::make_unique< QgsGeometryCollection >();
114  result->mWkbType = mWkbType;
115  return result.release();
116 }
117 
119 {
120  return new QgsGeometryCollection( *this );
121 }
122 
124 {
125  qDeleteAll( mGeometries );
126  mGeometries.clear();
127  clearCache(); //set bounding box invalid
128 }
129 
130 QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
131 {
132  std::unique_ptr<QgsGeometryCollection> result;
133 
134  for ( auto geom : mGeometries )
135  {
136  std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) };
137  if ( gridified )
138  {
139  if ( !result )
140  result = std::unique_ptr<QgsGeometryCollection> { createEmptyWithSameType() };
141 
142  result->mGeometries.append( gridified.release() );
143  }
144  }
145 
146  return result.release();
147 }
148 
149 bool QgsGeometryCollection::removeDuplicateNodes( double epsilon, bool useZValues )
150 {
151  bool result = false;
152  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
153  {
154  if ( geom->removeDuplicateNodes( epsilon, useZValues ) ) result = true;
155  }
156  return result;
157 }
158 
160 {
161  return nullptr;
162 }
163 
165 {
166  if ( vertex.part < 0 || vertex.part >= mGeometries.count() )
167  {
168  previousVertex = QgsVertexId();
169  nextVertex = QgsVertexId();
170  return;
171  }
172 
173  mGeometries.at( vertex.part )->adjacentVertices( vertex, previousVertex, nextVertex );
174 }
175 
177 {
178  if ( id.part < 0 || id.part >= mGeometries.count() )
179  return -1;
180 
181  int number = 0;
182  int part = 0;
183  for ( QgsAbstractGeometry *geometry : mGeometries )
184  {
185  if ( part == id.part )
186  {
187  int partNumber = geometry->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
188  if ( partNumber == -1 )
189  return -1;
190  return number + partNumber;
191  }
192  else
193  {
194  number += geometry->nCoordinates();
195  }
196 
197  part++;
198  }
199  return -1; // should not happen
200 }
201 
203 {
204  mGeometries.reserve( size );
205 }
206 
208 {
209  clearCache();
210  return mGeometries.value( n );
211 }
212 
214 {
215  if ( mGeometries.isEmpty() )
216  return true;
217 
218  for ( QgsAbstractGeometry *geometry : mGeometries )
219  {
220  if ( !geometry->isEmpty() )
221  return false;
222  }
223  return true;
224 }
225 
227 {
228  if ( !g )
229  {
230  return false;
231  }
232 
233  mGeometries.append( g );
234  clearCache(); //set bounding box invalid
235  return true;
236 }
237 
239 {
240  if ( !g )
241  {
242  return false;
243  }
244 
245  index = std::min( mGeometries.count(), index );
246 
247  mGeometries.insert( index, g );
248  clearCache(); //set bounding box invalid
249  return true;
250 }
251 
253 {
254  if ( nr >= mGeometries.size() || nr < 0 )
255  {
256  return false;
257  }
258  delete mGeometries.at( nr );
259  mGeometries.remove( nr );
260  clearCache(); //set bounding box invalid
261  return true;
262 }
263 
265 {
266  int maxDim = 0;
267  QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
268  for ( ; it != mGeometries.constEnd(); ++it )
269  {
270  int dim = ( *it )->dimension();
271  if ( dim > maxDim )
272  {
273  maxDim = dim;
274  }
275  }
276  return maxDim;
277 }
278 
280 {
281  return QStringLiteral( "GeometryCollection" );
282 }
283 
285 {
286  for ( QgsAbstractGeometry *g : qgis::as_const( mGeometries ) )
287  {
288  g->transform( ct, d, transformZ );
289  }
290  clearCache(); //set bounding box invalid
291 }
292 
293 void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
294 {
295  for ( QgsAbstractGeometry *g : qgis::as_const( mGeometries ) )
296  {
297  g->transform( t, zTranslate, zScale, mTranslate, mScale );
298  }
299  clearCache(); //set bounding box invalid
300 }
301 
302 void QgsGeometryCollection::draw( QPainter &p ) const
303 {
304  QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
305  for ( ; it != mGeometries.constEnd(); ++it )
306  {
307  ( *it )->draw( p );
308  }
309 }
310 
312 {
313  QPainterPath p;
314  for ( const QgsAbstractGeometry *geom : mGeometries )
315  {
316  QPainterPath partPath = geom->asQPainterPath();
317  if ( !partPath.isEmpty() )
318  p.addPath( partPath );
319  }
320  return p;
321 }
322 
324 {
325  if ( !wkbPtr )
326  {
327  return false;
328  }
329 
332  return false;
333 
334  mWkbType = wkbType;
335 
336  int nGeometries = 0;
337  wkbPtr >> nGeometries;
338 
339  QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
340  mGeometries.clear();
341  mGeometries.reserve( nGeometries );
342  for ( int i = 0; i < nGeometries; ++i )
343  {
344  std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
345  if ( geom )
346  {
347  if ( !addGeometry( geom.release() ) )
348  {
349  qDeleteAll( mGeometries );
350  mGeometries = geometryListBackup;
351  return false;
352  }
353  }
354  }
355  qDeleteAll( geometryListBackup );
356 
357  clearCache(); //set bounding box invalid
358 
359  return true;
360 }
361 
362 bool QgsGeometryCollection::fromWkt( const QString &wkt )
363 {
364  return fromCollectionWkt( wkt, QVector<QgsAbstractGeometry *>() << new QgsPoint << new QgsLineString << new QgsPolygon
365  << new QgsCircularString << new QgsCompoundCurve
366  << new QgsCurvePolygon
367  << new QgsMultiPoint << new QgsMultiLineString
369  << new QgsMultiCurve << new QgsMultiSurface, QStringLiteral( "GeometryCollection" ) );
370 }
371 
372 int QgsGeometryCollection::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
373 {
374  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
375  for ( const QgsAbstractGeometry *geom : mGeometries )
376  {
377  if ( geom )
378  {
379  binarySize += geom->wkbSize( flags );
380  }
381  }
382 
383  return binarySize;
384 }
385 
386 QByteArray QgsGeometryCollection::asWkb( WkbFlags flags ) const
387 {
388  int countNonNull = 0;
389  for ( const QgsAbstractGeometry *geom : mGeometries )
390  {
391  if ( geom )
392  {
393  countNonNull ++;
394  }
395  }
396 
397  QByteArray wkbArray;
398  wkbArray.resize( QgsGeometryCollection::wkbSize( flags ) );
399  QgsWkbPtr wkb( wkbArray );
400  wkb << static_cast<char>( QgsApplication::endian() );
401  wkb << static_cast<quint32>( wkbType() );
402  wkb << static_cast<quint32>( countNonNull );
403  for ( const QgsAbstractGeometry *geom : mGeometries )
404  {
405  if ( geom )
406  {
407  wkb << geom->asWkb( flags );
408  }
409  }
410  return wkbArray;
411 }
412 
414 {
415  QString wkt = wktTypeStr();
416 
417  if ( isEmpty() )
418  wkt += QLatin1String( " EMPTY" );
419  else
420  {
421  wkt += QLatin1String( " (" );
422  for ( const QgsAbstractGeometry *geom : mGeometries )
423  {
424  QString childWkt = geom->asWkt( precision );
425  if ( wktOmitChildType() )
426  {
427  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
428  }
429  wkt += childWkt + ',';
430  }
431  if ( wkt.endsWith( ',' ) )
432  {
433  wkt.chop( 1 ); // Remove last ','
434  }
435  wkt += ')';
436  }
437  return wkt;
438 }
439 
440 QDomElement QgsGeometryCollection::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
441 {
442  QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
443  for ( const QgsAbstractGeometry *geom : mGeometries )
444  {
445  QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
446  elemGeometryMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
447  elemMultiGeometry.appendChild( elemGeometryMember );
448  }
449  return elemMultiGeometry;
450 }
451 
452 QDomElement QgsGeometryCollection::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
453 {
454  QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
455  for ( const QgsAbstractGeometry *geom : mGeometries )
456  {
457  QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
458  elemGeometryMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
459  elemMultiGeometry.appendChild( elemGeometryMember );
460  }
461  return elemMultiGeometry;
462 }
463 
465 {
466  json coordinates( json::array( ) );
467  for ( const QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
468  {
469  coordinates.push_back( geom->asJsonObject( precision ) );
470  }
471  return
472  {
473  { "type", "GeometryCollection" },
474  { "geometries", coordinates }
475  };
476 }
477 
479 {
480  QString kml;
481  kml.append( QLatin1String( "<MultiGeometry>" ) );
482  const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
483  for ( const QgsAbstractGeometry *geometry : geometries )
484  {
485  kml.append( geometry->asKml( precision ) );
486  }
487  kml.append( QLatin1String( "</MultiGeometry>" ) );
488  return kml;
489 }
490 
492 {
493  if ( mBoundingBox.isNull() )
494  {
495  mBoundingBox = calculateBoundingBox();
496  }
497  return mBoundingBox;
498 }
499 
501 {
502  if ( mGeometries.empty() )
503  {
504  return QgsRectangle();
505  }
506 
507  QgsRectangle bbox = mGeometries.at( 0 )->boundingBox();
508  for ( int i = 1; i < mGeometries.size(); ++i )
509  {
510  if ( mGeometries.at( i )->isEmpty() )
511  continue;
512 
513  QgsRectangle geomBox = mGeometries.at( i )->boundingBox();
514  if ( bbox.isNull() )
515  {
516  // workaround treatment of a QgsRectangle(0,0,0,0) as a "null"/invalid rectangle
517  // if bbox is null, then the first geometry must have returned a bounding box of (0,0,0,0)
518  // so just manually include that as a point... ew.
519  geomBox.combineExtentWith( QPointF( 0, 0 ) );
520  bbox = geomBox;
521  }
522  else if ( geomBox.isNull() )
523  {
524  // ...as above... this part must have a bounding box of (0,0,0,0).
525  // if we try to combine the extent with this "null" box it will just be ignored.
526  bbox.combineExtentWith( QPointF( 0, 0 ) );
527  }
528  else
529  {
530  bbox.combineExtentWith( geomBox );
531  }
532  }
533  return bbox;
534 }
535 
537 {
538  mBoundingBox = QgsRectangle();
539  mHasCachedValidity = false;
540  mValidityFailureReason.clear();
542 }
543 
545 {
546  QgsCoordinateSequence sequence;
547  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
548  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
549  {
550  QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
551 
552  QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
553  for ( ; cIt != geomCoords.constEnd(); ++cIt )
554  {
555  sequence.push_back( *cIt );
556  }
557  }
558 
559  return sequence;
560 }
561 
563 {
564  int count = 0;
565 
566  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
567  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
568  {
569  count += ( *geomIt )->nCoordinates();
570  }
571 
572  return count;
573 }
574 
575 double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
576 {
577  return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
578 }
579 
581 {
582  if ( id.part < 0 )
583  {
584  id.part = 0;
585  id.ring = -1;
586  id.vertex = -1;
587  }
588  if ( mGeometries.isEmpty() )
589  {
590  return false;
591  }
592 
593  if ( id.part >= mGeometries.count() )
594  return false;
595 
596  QgsAbstractGeometry *geom = mGeometries.at( id.part );
597  if ( geom->nextVertex( id, vertex ) )
598  {
599  return true;
600  }
601  if ( ( id.part + 1 ) >= numGeometries() )
602  {
603  return false;
604  }
605  ++id.part;
606  id.ring = -1;
607  id.vertex = -1;
608  return mGeometries.at( id.part )->nextVertex( id, vertex );
609 }
610 
612 {
613  if ( position.part >= mGeometries.size() )
614  {
615  return false;
616  }
617 
618  bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
619  if ( success )
620  {
621  clearCache(); //set bounding box invalid
622  }
623  return success;
624 }
625 
627 {
628  if ( position.part < 0 || position.part >= mGeometries.size() )
629  {
630  return false;
631  }
632 
633  bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
634  if ( success )
635  {
636  clearCache(); //set bounding box invalid
637  }
638  return success;
639 }
640 
642 {
643  if ( position.part < 0 || position.part >= mGeometries.size() )
644  {
645  return false;
646  }
647 
648  QgsAbstractGeometry *geom = mGeometries.at( position.part );
649  if ( !geom )
650  {
651  return false;
652  }
653 
654  bool success = geom->deleteVertex( position );
655 
656  //remove geometry if no vertices left
657  if ( geom->isEmpty() )
658  {
659  removeGeometry( position.part );
660  }
661 
662  if ( success )
663  {
664  clearCache(); //set bounding box invalid
665  }
666  return success;
667 }
668 
670 {
671  double length = 0.0;
672  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
673  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
674  {
675  length += ( *geomIt )->length();
676  }
677  return length;
678 }
679 
681 {
682  double area = 0.0;
683  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
684  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
685  {
686  area += ( *geomIt )->area();
687  }
688  return area;
689 }
690 
692 {
693  double perimeter = 0.0;
694  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
695  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
696  {
697  perimeter += ( *geomIt )->perimeter();
698  }
699  return perimeter;
700 }
701 
702 bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<QgsAbstractGeometry *> &subtypes, const QString &defaultChildWkbType )
703 {
704  clear();
705 
706  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
707 
708  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::flatType( wkbType() ) )
709  {
710  qDeleteAll( subtypes );
711  return false;
712  }
713  mWkbType = parts.first;
714 
715  QString secondWithoutParentheses = parts.second;
716  secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
717  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
718  secondWithoutParentheses.isEmpty() )
719  return true;
720 
721  QString defChildWkbType = QStringLiteral( "%1%2%3 " ).arg( defaultChildWkbType, is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
722 
723  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
724  for ( const QString &childWkt : blocks )
725  {
726  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
727 
728  bool success = false;
729  for ( const QgsAbstractGeometry *geom : subtypes )
730  {
731  if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( geom->wkbType() ) )
732  {
733  mGeometries.append( geom->clone() );
734  if ( mGeometries.back()->fromWkt( childWkt ) )
735  {
736  success = true;
737  break;
738  }
739  }
740  }
741  if ( !success )
742  {
743  clear();
744  qDeleteAll( subtypes );
745  return false;
746  }
747  }
748  qDeleteAll( subtypes );
749 
750  //scan through geometries and check if dimensionality of geometries is different to collection.
751  //if so, update the type dimensionality of the collection to match
752  bool hasZ = false;
753  bool hasM = false;
754  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
755  {
756  hasZ = hasZ || geom->is3D();
757  hasM = hasM || geom->isMeasure();
758  if ( hasZ && hasM )
759  break;
760  }
761  if ( hasZ )
762  addZValue( 0 );
763  if ( hasM )
764  addMValue( 0 );
765 
766  return true;
767 }
768 
770 {
771  QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
772  for ( ; it != mGeometries.constEnd(); ++it )
773  {
774  if ( ( *it )->hasCurvedSegments() )
775  {
776  return true;
777  }
778  }
779  return false;
780 }
781 
783 {
784  std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
785  QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
786  if ( !geomCollection )
787  {
788  return clone();
789  }
790 
791  geomCollection->reserve( mGeometries.size() );
792  QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
793  for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
794  {
795  geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
796  }
797  return geom.release();
798 }
799 
801 {
802  if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
803  {
804  return 0.0;
805  }
806 
807  QgsAbstractGeometry *geom = mGeometries[vertex.part];
808  if ( !geom )
809  {
810  return 0.0;
811  }
812 
813  return geom->vertexAngle( vertex );
814 }
815 
817 {
818  if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
819  {
820  return 0.0;
821  }
822 
823  const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
824  if ( !geom )
825  {
826  return 0.0;
827  }
828 
829  return geom->segmentLength( startVertex );
830 }
831 
832 int QgsGeometryCollection::vertexCount( int part, int ring ) const
833 {
834  if ( part < 0 || part >= mGeometries.size() )
835  {
836  return 0;
837  }
838 
839  return mGeometries[part]->vertexCount( 0, ring );
840 }
841 
842 int QgsGeometryCollection::ringCount( int part ) const
843 {
844  if ( part < 0 || part >= mGeometries.size() )
845  {
846  return 0;
847  }
848 
849  return mGeometries[part]->ringCount();
850 }
851 
853 {
854  return mGeometries.size();
855 }
856 
858 {
859  return mGeometries[id.part]->vertexAt( id );
860 }
861 
862 bool QgsGeometryCollection::isValid( QString &error, int flags ) const
863 {
864  if ( flags == 0 && mHasCachedValidity )
865  {
866  // use cached validity results
867  error = mValidityFailureReason;
868  return error.isEmpty();
869  }
870 
871  QgsGeos geos( this );
872  bool res = geos.isValid( &error, flags & QgsGeometry::FlagAllowSelfTouchingHoles, nullptr );
873  if ( flags == 0 )
874  {
875  mValidityFailureReason = !res ? error : QString();
876  mHasCachedValidity = true;
877  }
878  return res;
879 }
880 
881 bool QgsGeometryCollection::addZValue( double zValue )
882 {
883  if ( QgsWkbTypes::hasZ( mWkbType ) )
884  return false;
885 
887 
888  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
889  {
890  geom->addZValue( zValue );
891  }
892  clearCache();
893  return true;
894 }
895 
896 bool QgsGeometryCollection::addMValue( double mValue )
897 {
898  if ( QgsWkbTypes::hasM( mWkbType ) )
899  return false;
900 
902 
903  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
904  {
905  geom->addMValue( mValue );
906  }
907  clearCache();
908  return true;
909 }
910 
911 
913 {
915  return false;
916 
918  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
919  {
920  geom->dropZValue();
921  }
922  clearCache();
923  return true;
924 }
925 
927 {
929  return false;
930 
932  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
933  {
934  geom->dropMValue();
935  }
936  clearCache();
937  return true;
938 }
939 
940 void QgsGeometryCollection::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
941 {
942  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
943  {
944  if ( geom )
945  geom->filterVertices( filter );
946  }
947  clearCache();
948 }
949 
950 void QgsGeometryCollection::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
951 {
952  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
953  {
954  if ( geom )
955  geom->transformVertices( transform );
956  }
957  clearCache();
958 }
959 
961 {
962  for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
963  {
964  if ( geom )
965  geom->swapXy();
966  }
967  clearCache();
968 }
969 
971 {
972  std::unique_ptr< QgsGeometryCollection > newCollection( new QgsGeometryCollection() );
973  newCollection->reserve( mGeometries.size() );
974  for ( QgsAbstractGeometry *geom : mGeometries )
975  {
976  newCollection->addGeometry( geom->toCurveType() );
977  }
978  return newCollection.release();
979 }
980 
982 {
983  return false;
984 }
985 
987 {
988  return mGeometries.count();
989 }
990 
992 {
993  if ( index < 0 || index > mGeometries.count() )
994  return nullptr;
995  return mGeometries.at( index );
996 }
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
int precision
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
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...
bool operator!=(const QgsAbstractGeometry &other) const override
virtual bool isEmpty() const
Returns true if the geometry is empty.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
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.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(QgsWkbTypes::Type t)
Returns empty geometry from wkb type.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
bool operator==(const QgsAbstractGeometry &other) const override
void clear() override
Clears the geometry, ie reset it to a null geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
Multi point geometry collection.
Definition: qgsmultipoint.h:29
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
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 (...
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
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...
QVector< QgsRingSequence > QgsCoordinateSequence
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
bool isValid(QString &error, int flags=0) const override
Checks validity of the geometry, and returns true if the geometry is valid.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
bool isValid(QString *errorMsg=nullptr, bool allowSelfTouchingHoles=false, QgsGeometry *errorLoc=nullptr) const override
Returns true if the geometry is valid.
Definition: qgsgeos.cpp:1703
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
Multi line string geometry collection.
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
Curve polygon geometry type.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual bool wktOmitChildType() const
Returns whether child type names are omitted from Wkt representations of the collection.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QgsGeometryCollection & operator=(const QgsGeometryCollection &c)
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
bool dropMValue() override
Drops any measure values which exist in the geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
Multi surface geometry collection.
int part
Part number.
QgsWkbTypes::Type mWkbType
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1171
virtual bool insertGeometry(QgsAbstractGeometry *g, int index)
Inserts a geometry before a specified index and takes ownership.
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...
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
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition: qgsgeometry.h:369
QString wktTypeStr() const
Returns the WKT type string of the geometry.
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void swapXy() override
Swaps the x and y coordinates from the geometry.
virtual void clearCache() const
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
Utility class for identifying a unique vertex within a geometry.
Geometry collection.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
QgsGeometryCollection() SIP_HOLDGIL
Constructor for an empty geometry collection.
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
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 linearType(Type type) SIP_HOLDGIL
Returns the linear type for a WKB type.
Definition: qgswkbtypes.h:592
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
QgsAbstractGeometry * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
Abstract base class for all geometries.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Does vector analysis using the geos library and handles import, export, exception handling*...
Definition: qgsgeos.h:103
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
bool fromCollectionWkt(const QString &wkt, const QVector< QgsAbstractGeometry *> &subtypes, const QString &defaultChildWkbType=QString())
Reads a collection from a WKT string.
double perimeter() const override SIP_HOLDGIL
Returns the planar, 2-dimensional perimeter of the geometry.
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
AxisOrder
Axis order for GML generation.
Contains geos related utilities and functions.
Definition: qgsgeos.h:41
void reserve(int size) SIP_HOLDGIL
Attempts to allocate memory for at least size geometries.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:359
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...
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
int partCount() const override
Returns count of parts contained in the geometry.
QVector< QgsAbstractGeometry *> mGeometries
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.
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.
Multi polygon geometry collection.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsGeometryCollection * toCurveType() const override
Returns the geometry converted to the more generic curve type.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:447
Class for doing transforms between two map coordinate systems.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
Compound curve geometry type.
Circular string geometry type.
QgsGeometryCollection * clone() const override
Clones the geometry by performing a deep copy.
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
Polygon geometry type.
Definition: qgspolygon.h:33
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negative values mean left ...
Definition: MathUtils.cpp:292
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
bool hasCurvedSegments() const override SIP_HOLDGIL
Returns true if the geometry contains curved segments.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override