QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgspolyhedralsurface.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspolyhedralsurface.cpp
3 ---------------------
4 begin : August 2024
5 copyright : (C) 2024 by Jean Felder
6 email : jean dot felder at oslandia dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <memory>
21#include <nlohmann/json.hpp>
22
23#include "qgsapplication.h"
24#include "qgscurve.h"
25#include "qgsfeedback.h"
26#include "qgsgeometryutils.h"
27#include "qgsgeos.h"
28#include "qgslinestring.h"
29#include "qgslogger.h"
30#include "qgsmultilinestring.h"
31#include "qgsmultipolygon.h"
32#include "qgsmultisurface.h"
33#include "qgspolygon.h"
34#include "qgsvertexid.h"
35#include "qgswkbptr.h"
36
37#include <QPainter>
38#include <QPainterPath>
39#include <QString>
40
41using namespace Qt::StringLiterals;
42
47
49{
50 if ( multiPolygon->isEmpty() )
51 {
52 return;
53 }
54
55 mPatches.reserve( multiPolygon->numGeometries() );
56 for ( int i = 0; i < multiPolygon->numGeometries(); ++i )
57 {
58 mPatches.append( multiPolygon->polygonN( i )->clone() );
59 }
60
62}
63
68
70{
71 auto result = std::make_unique< QgsPolyhedralSurface >();
72 result->mWkbType = mWkbType;
73 return result.release();
74}
75
77{
78 return u"PolyhedralSurface"_s;
79}
80
82{
83 return 2;
84}
85
87 : QgsSurface( p )
88
89{
91 mPatches.reserve( p.mPatches.size() );
92
93 for ( const QgsPolygon *patch : p.mPatches )
94 {
95 mPatches.push_back( patch->clone() );
96 }
97
101}
102
103// cppcheck-suppress operatorEqVarError
105{
106 if ( &p != this )
107 {
109 mPatches.reserve( p.mPatches.size() );
110
111 for ( const QgsPolygon *patch : p.mPatches )
112 {
113 mPatches.push_back( patch->clone() );
114 }
115 }
116 return *this;
117}
118
123
125{
127 qDeleteAll( mPatches );
128 mPatches.clear();
129 clearCache();
130}
131
132
134{
135 clear();
136
137 if ( !wkbPtr )
138 {
139 return false;
140 }
141
142 Qgis::WkbType type = wkbPtr.readHeader();
144 {
145 return false;
146 }
147 mWkbType = type;
148
149 int nPatches;
150 wkbPtr >> nPatches;
151 std::unique_ptr< QgsPolygon > currentPatch;
152 for ( int i = 0; i < nPatches; ++i )
153 {
154 Qgis::WkbType polygonType = wkbPtr.readHeader();
155 wkbPtr -= 1 + sizeof( int );
156 Qgis::WkbType flatPolygonType = QgsWkbTypes::flatType( polygonType );
157 if ( flatPolygonType == Qgis::WkbType::Polygon )
158 {
159 currentPatch = std::make_unique<QgsPolygon>( );
160 }
161 else
162 {
163 return false;
164 }
165 currentPatch->fromWkb( wkbPtr ); // also updates wkbPtr
166 mPatches.append( currentPatch.release() );
167 }
168
169 return true;
170}
171
172bool QgsPolyhedralSurface::fromWkt( const QString &wkt )
173{
174 clear();
175
176 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
177
179 return false;
180
181 mWkbType = parts.first;
182
183 QString secondWithoutParentheses = parts.second;
184 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
185 if ( ( parts.second.compare( "EMPTY"_L1, Qt::CaseInsensitive ) == 0 ) ||
186 secondWithoutParentheses.isEmpty() )
187 return true;
188
189 QString defaultChildWkbType = u"Polygon%1%2"_s.arg( is3D() ? u"Z"_s : QString(), isMeasure() ? u"M"_s : QString() );
190
191 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
192 for ( const QString &childWkt : blocks )
193 {
194 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
195
196 if ( QgsWkbTypes::flatType( childParts.first ) == Qgis::WkbType::Polygon )
197 {
198 mPatches.append( new QgsPolygon() );
199 }
200 else
201 {
202 clear();
203 return false;
204 }
205
206 if ( !mPatches.back()->fromWkt( childWkt ) )
207 {
208 clear();
209 return false;
210 }
211 }
212
213 return true;
214}
215
217{
218 if ( mPatches.empty() )
219 {
220 return QgsBox3D();
221 }
222
223 QgsBox3D bbox = mPatches.at( 0 )->boundingBox3D();
224 for ( int i = 1; i < mPatches.size(); ++i )
225 {
226 QgsBox3D polygonBox = mPatches.at( i )->boundingBox3D();
227 bbox.combineWith( polygonBox );
228 }
229 return bbox;
230}
231
233{
234 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
235 for ( const QgsPolygon *patch : mPatches )
236 {
237 binarySize += patch->wkbSize( flags );
238 }
239 return binarySize;
240}
241
242QByteArray QgsPolyhedralSurface::asWkb( WkbFlags flags ) const
243{
244 QByteArray wkbArray;
245 wkbArray.resize( QgsPolyhedralSurface::wkbSize( flags ) );
246 QgsWkbPtr wkb( wkbArray );
247 wkb << static_cast<char>( QgsApplication::endian() );
248 wkb << static_cast<quint32>( wkbType() );
249 wkb << static_cast<quint32>( mPatches.size() );
250 for ( const QgsPolygon *patch : mPatches )
251 {
252 wkb << patch->asWkb( flags );
253 }
254 return wkbArray;
255}
256
257QString QgsPolyhedralSurface::asWkt( int precision ) const
258{
259 QString wkt = wktTypeStr();
260
261 if ( isEmpty() )
262 wkt += " EMPTY"_L1;
263 else
264 {
265 wkt += " ("_L1;
266 for ( const QgsPolygon *patch : mPatches )
267 {
268 QString childWkt = patch->asWkt( precision );
270 {
271 // Type names of linear geometries are omitted
272 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
273 }
274 wkt += childWkt + ',';
275 }
276 if ( wkt.endsWith( ',' ) )
277 {
278 wkt.chop( 1 ); // Remove last ','
279 }
280 wkt += ')';
281 }
282 return wkt;
283}
284
285QDomElement QgsPolyhedralSurface::asGml2( QDomDocument &, int, const QString &, const AxisOrder ) const
286{
287 QgsDebugError( u"gml version 2 does not support PolyhedralSurface geometry"_s );
288 return QDomElement();
289}
290
291QDomElement QgsPolyhedralSurface::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
292{
293 QDomElement elemPolyhedralSurface = doc.createElementNS( ns, u"PolyhedralSurface"_s );
294
295 if ( isEmpty() )
296 return elemPolyhedralSurface;
297
298 QDomElement elemPolygonPatches = doc.createElementNS( ns, u"polygonPatches"_s );
299
300 for ( QgsPolygon *patch : mPatches )
301 {
302 QDomElement elemPolygonPatch = patch->asGml3( doc, precision, ns, axisOrder );
303 elemPolygonPatch.setTagName( "PolygonPatch" );
304
305 elemPolygonPatches.appendChild( elemPolygonPatch );
306 }
307 elemPolyhedralSurface.appendChild( elemPolygonPatches );
308
309 return elemPolyhedralSurface;
310}
311
312json QgsPolyhedralSurface::asJsonObject( int precision ) const
313{
314 // GeoJSON format does not support PolyhedralSurface geometry
315 // Return a multipolygon instead;
316 std::unique_ptr<QgsMultiPolygon> multiPolygon( toMultiPolygon() );
317 return multiPolygon->asJsonObject( precision );
318}
319
320QString QgsPolyhedralSurface::asKml( int ) const
321{
322 QgsDebugError( u"kml format does not support PolyhedralSurface geometry"_s );
323 return QString( "" );
324}
325
327{
328 for ( QgsPolygon *patch : mPatches )
329 {
330 QgsCurve *exteriorRing = patch->exteriorRing();
331 if ( !exteriorRing )
332 continue;
333
334 exteriorRing->normalize();
335
336 if ( patch->numInteriorRings() > 0 )
337 {
338 QVector<QgsCurve *> interiorRings;
339 for ( int i = 0, n = patch->numInteriorRings(); i < n; ++i )
340 {
341 interiorRings.push_back( patch->interiorRing( i )->clone() );
342 }
343
344 // sort rings
345 std::sort( interiorRings.begin(), interiorRings.end(), []( const QgsCurve * a, const QgsCurve * b )
346 {
347 return a->compareTo( b ) > 0;
348 } );
349
350 patch->removeInteriorRings();
351 for ( QgsCurve *curve : interiorRings )
352 patch->addInteriorRing( curve );
353 }
354 }
355}
356
358{
359 // sum area of patches
360 double area = 0.0;
361 for ( const QgsPolygon *patch : mPatches )
362 {
363 area += patch->area();
364 }
365
366 return area;
367}
368
370{
371 // sum area 3D of patches
372 double area = 0.0;
373 for ( const QgsPolygon *patch : mPatches )
374 {
375 area += patch->area3D();
376 }
377
378 return area;
379}
380
382{
383 // sum perimeter of patches
384 double perimeter = 0.0;
385 for ( const QgsPolygon *patch : mPatches )
386 {
387 perimeter += patch->perimeter();
388 }
389
390 return perimeter;
391}
392
394{
395 auto multiLine = std::make_unique<QgsMultiLineString>();
396 multiLine->reserve( mPatches.size() );
397 for ( QgsPolygon *polygon : mPatches )
398 {
399 std::unique_ptr<QgsAbstractGeometry> polygonBoundary( polygon->boundary() );
400 if ( QgsLineString *lineStringBoundary = qgsgeometry_cast< QgsLineString * >( polygonBoundary.get() ) )
401 {
402 multiLine->addGeometry( lineStringBoundary->clone() );
403 }
404 else if ( QgsMultiLineString *multiLineStringBoundary = qgsgeometry_cast< QgsMultiLineString * >( polygonBoundary.get() ) )
405 {
406 for ( int j = 0; j < multiLineStringBoundary->numGeometries(); ++j )
407 {
408 multiLine->addGeometry( multiLineStringBoundary->geometryN( j )->clone() );
409 }
410 }
411 }
412
413 if ( multiLine->numGeometries() == 0 )
414 {
415 return nullptr;
416 }
417 return multiLine.release();
418}
419
420QgsPolyhedralSurface *QgsPolyhedralSurface::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
421{
422 std::unique_ptr< QgsPolyhedralSurface > surface( createEmptyWithSameType() );
423
424 for ( QgsPolygon *patch : mPatches )
425 {
426 // exterior ring
427 std::unique_ptr<QgsCurve> exteriorRing( static_cast< QgsCurve *>( patch->exteriorRing()->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) );
428 if ( !exteriorRing )
429 {
430 return nullptr;
431 }
432
433 auto gridifiedPatch = std::make_unique<QgsPolygon>();
434 gridifiedPatch->setExteriorRing( exteriorRing.release() );
435
436 //interior rings
437 for ( int i = 0, n = patch->numInteriorRings(); i < n; ++i )
438 {
439 QgsCurve *interiorRing = patch->interiorRing( i );
440 if ( !interiorRing )
441 continue;
442
443 std::unique_ptr<QgsCurve> gridifiedInterior( static_cast< QgsCurve * >( interiorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) );
444 if ( gridifiedInterior )
445 gridifiedPatch->addInteriorRing( gridifiedInterior.release() );
446 }
447
448 surface->addPatch( gridifiedPatch.release() );
449 }
450
451 return surface.release();
452}
453
455{
456 if ( isEmpty() )
457 return nullptr;
458
459 auto simplifiedGeom = std::make_unique< QgsPolyhedralSurface >();
460 for ( QgsPolygon *polygon : mPatches )
461 {
462 std::unique_ptr<QgsCurvePolygon> polygonSimplified( polygon->simplifyByDistance( tolerance ) );
463 simplifiedGeom->addPatch( polygonSimplified->surfaceToPolygon() );
464 }
465 return simplifiedGeom.release();
466}
467
468bool QgsPolyhedralSurface::removeDuplicateNodes( double epsilon, bool useZValues )
469{
470 bool result = false;
471
472 for ( QgsPolygon *patch : std::as_const( mPatches ) )
473 {
474 if ( patch->removeDuplicateNodes( epsilon, useZValues ) )
475 {
476 result = true;
477 }
478 }
479 return result;
480}
481
483{
484 // if we already have the bounding box calculated, then this check is trivial!
485 if ( !mBoundingBox.isNull() )
486 {
487 return mBoundingBox.intersects( box3d );
488 }
489
490 // loop through each patch and test the bounding box intersection.
491 // This gives us a chance to use optimisations which may be present on the individual
492 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
493 // of each individual patch geometry which we would have to do anyway... (and these
494 // bounding boxes are cached, so would be reused without additional expense)
495 for ( const QgsPolygon *patch : mPatches )
496 {
497 if ( patch->boundingBoxIntersects( box3d ) )
498 return true;
499 }
500
501 // even if we don't intersect the bounding box of any rings, we may still intersect the
502 // bounding box of the overall polygon (we are considering worst case scenario here and
503 // the polygon is invalid, with rings outside the exterior ring!)
504 // so here we fall back to the non-optimised base class check which has to first calculate
505 // the overall bounding box of the polygon..
506 return QgsSurface::boundingBoxIntersects( box3d );
507}
508
509void QgsPolyhedralSurface::setPatches( const QVector<QgsPolygon *> &patches )
510{
511 qDeleteAll( mPatches );
512 mPatches.clear();
513
514 for ( QgsPolygon *patch : patches )
515 {
516 addPatch( patch );
517 }
518
519 clearCache();
520}
521
523{
524 if ( !patch )
525 return;
526
527 if ( mPatches.empty() )
528 {
530 }
531
532 // Ensure dimensionality of patch matches polyhedral surface
533 if ( !is3D() )
534 patch->dropZValue();
535 else if ( !patch->is3D() )
536 patch->addZValue();
537
538 if ( !isMeasure() )
539 patch->dropMValue();
540 else if ( !patch->isMeasure() )
541 patch->addMValue();
542
543 mPatches.append( patch );
544 clearCache();
545}
546
548{
549 if ( patchIndex < 0 || patchIndex >= mPatches.size() )
550 {
551 return false;
552 }
553
554 delete mPatches.takeAt( patchIndex );
555 clearCache();
556 return true;
557}
558
560{
561 QPainterPath painterPath;
562 for ( const QgsPolygon *patch : mPatches )
563 {
564 QPainterPath patchPath = patch->asQPainterPath();
565 patchPath.closeSubpath();
566 painterPath.addPath( patchPath );
567 }
568
569 return painterPath;
570}
571
572void QgsPolyhedralSurface::draw( QPainter &p ) const
573{
574 if ( mPatches.empty() )
575 return;
576
577 for ( const QgsPolygon *patch : mPatches )
578 {
579 patch->draw( p );
580 }
581}
582
584{
585 for ( QgsPolygon *patch : std::as_const( mPatches ) )
586 {
587 patch->transform( ct, d, transformZ );
588 }
589 clearCache();
590}
591
592void QgsPolyhedralSurface::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
593{
594 for ( QgsPolygon *patch : std::as_const( mPatches ) )
595 {
596 patch->transform( t, zTranslate, zScale, mTranslate, mScale );
597 }
598 clearCache();
599}
600
602{
603 QgsCoordinateSequence sequence;
604 for ( const QgsPolygon *polygon : std::as_const( mPatches ) )
605 {
606 QgsCoordinateSequence polyCoords = polygon->coordinateSequence();
607 QgsCoordinateSequence::const_iterator cIt = polyCoords.constBegin();
608 for ( ; cIt != polyCoords.constEnd(); ++cIt )
609 {
610 sequence.push_back( *cIt );
611 }
612 }
613
614 return sequence;
615}
616
618{
619 int count = 0;
620 for ( const QgsPolygon *patch : mPatches )
621 {
622 count += patch->nCoordinates();
623 }
624 return count;
625}
626
628{
629 if ( id.part < 0 || id.part >= partCount() )
630 return -1;
631
632 int number = 0;
633 for ( int i = 0; i < mPatches.count(); ++i )
634 {
635 if ( id.part == i )
636 {
637 int partNumber = mPatches.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
638 if ( partNumber == -1 )
639 {
640 return -1;
641 }
642
643 return number + partNumber;
644 }
645 else
646 {
647 number += mPatches.at( i )->nCoordinates();
648 }
649 }
650
651 return -1; // should not happen
652}
653
655{
656 return mPatches.isEmpty();
657}
658
659double QgsPolyhedralSurface::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
660{
661 QVector<QgsPolygon *> segmentList = mPatches;
662 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
663}
664
666{
667 if ( vId.part < 0 )
668 {
669 vId.part = 0;
670 vId.ring = -1;
671 vId.vertex = -1;
672 }
673
674 if ( isEmpty() || vId.part >= partCount() )
675 {
676 return false;
677 }
678
679 QgsPolygon *patch = mPatches[vId.part];
680 if ( patch->nextVertex( vId, vertex ) )
681 {
682 return true;
683 }
684
685 ++vId.part;
686 vId.ring = 0;
687 vId.vertex = -1;
688 if ( vId.part >= partCount() )
689 {
690 return false;
691 }
692 patch = mPatches[vId.part];
693 return patch->nextVertex( vId, vertex );
694}
695
697{
698 if ( vertex.part < 0 || vertex.part >= partCount() )
699 {
700 previousVertex = QgsVertexId();
702 return;
703 }
704
705 QgsPolygon *patch = mPatches[vertex.ring];
706 patch->adjacentVertices( QgsVertexId( 0, 0, vertex.vertex ), previousVertex, nextVertex );
707 return;
708}
709
711{
712 if ( vId.part < 0 || vId.part >= partCount() )
713 {
714 return false;
715 }
716
717 QgsPolygon *patch = mPatches.at( vId.part );
718 bool success = patch->insertVertex( QgsVertexId( 0, vId.ring, vId.vertex ), vertex );
719 if ( success )
720 {
721 clearCache();
722 }
723
724 return success;
725}
726
728{
729 if ( vId.part < 0 || vId.part >= partCount() )
730 {
731 return false;
732 }
733
734 QgsPolygon *patch = mPatches.at( vId.part );
735 bool success = patch->moveVertex( QgsVertexId( 0, vId.ring, vId.vertex ), newPos );
736 if ( success )
737 {
738 clearCache();
739 }
740
741 return success;
742}
743
745{
746 if ( vId.part < 0 || vId.part >= partCount() )
747 {
748 return false;
749 }
750
751 QgsPolygon *patch = mPatches.at( vId.part );
752 bool success = patch->deleteVertex( QgsVertexId( 0, vId.ring, vId.vertex ) );
753 if ( success )
754 {
755 // if the patch has lost its exterior ring, remove it
756 if ( !patch->exteriorRing() )
757 {
758 delete mPatches.takeAt( vId.part );
759 }
760 clearCache();
761 }
762
763 return success;
764}
765
767{
768 return false;
769}
770
772{
773 // This is only used by curves
774 Q_UNUSED( tolerance )
775 Q_UNUSED( toleranceType )
776 return clone();
777}
778
780{
781 if ( vertex.part < 0 || vertex.part >= partCount() )
782 {
783 return 0.0;
784 }
785
786 QgsPolygon *patch = mPatches[vertex.part];
787 return patch->vertexAngle( QgsVertexId( 0, vertex.ring, vertex.vertex ) );
788}
789
790int QgsPolyhedralSurface::vertexCount( int part, int ring ) const
791{
792 if ( part < 0 || part >= partCount() )
793 {
794 return 0;
795 }
796
797 QgsPolygon *patchPolygon = mPatches[part];
798 QgsCurve *ringCurve = ring == 0 ? patchPolygon->exteriorRing() : patchPolygon->interiorRing( ring - 1 );
799 if ( ringCurve )
800 {
801 return ringCurve->vertexCount();
802 }
803
804 return 0;
805}
806
808{
809 if ( part < 0 || part >= partCount() )
810 return 0;
811
812 return mPatches[part]->ringCount();
813}
814
816{
817 return mPatches.size();
818}
819
821{
822 if ( id.part < 0 || id.part >= partCount() )
823 return QgsPoint();
824
825 return mPatches[id.part]->vertexAt( id );
826}
827
829{
830 if ( startVertex.part < 0 || startVertex.part >= partCount() )
831 {
832 return 0.0;
833 }
834
835 const QgsPolygon *patch = mPatches[startVertex.part];
836 return patch->segmentLength( QgsVertexId( 0, startVertex.ring, startVertex.vertex ) );
837}
838
840{
842 {
843 return false;
844 }
845
847
848 for ( QgsPolygon *patch : std::as_const( mPatches ) )
849 {
850 patch->addZValue( zValue );
851 }
852 clearCache();
853 return true;
854}
855
857{
859 {
860 return false;
861 }
862
864
865 for ( QgsPolygon *patch : std::as_const( mPatches ) )
866 {
867 patch->addMValue( mValue );
868 }
869 clearCache();
870 return true;
871}
872
874{
875 if ( !is3D() )
876 {
877 return false;
878 }
879
881 for ( QgsPolygon *patch : std::as_const( mPatches ) )
882 {
883 patch->dropZValue();
884 }
885 clearCache();
886 return true;
887}
888
890{
891 if ( !isMeasure() )
892 {
893 return false;
894 }
895
897 for ( QgsPolygon *patch : std::as_const( mPatches ) )
898 {
899 patch->dropMValue();
900 }
901 clearCache();
902 return true;
903}
904
906{
907 for ( QgsPolygon *patch : std::as_const( mPatches ) )
908 {
909 patch->swapXy();
910 }
911 clearCache();
912}
913
915{
916 auto multiSurface = std::make_unique< QgsMultiSurface >();
917 multiSurface->reserve( mPatches.size() );
918 for ( const QgsPolygon *polygon : std::as_const( mPatches ) )
919 {
920 multiSurface->addGeometry( polygon->clone() );
921 }
922 return multiSurface.release();
923}
924
926{
927 if ( !transformer )
928 return false;
929
930 bool res = true;
931
932 for ( QgsPolygon *patch : std::as_const( mPatches ) )
933 {
934 res = patch->transform( transformer );
935 if ( feedback && feedback->isCanceled() )
936 res = false;
937
938 if ( !res )
939 break;
940 }
941
942 clearCache();
943 return res;
944}
945
947{
948 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
949 multiPolygon->reserve( mPatches.size() );
950 for ( const QgsPolygon *polygon : std::as_const( mPatches ) )
951 {
952 multiPolygon->addGeometry( polygon->clone() );
953 }
954 return multiPolygon.release();
955}
956
957void QgsPolyhedralSurface::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
958{
959 for ( QgsPolygon *patch : std::as_const( mPatches ) )
960 {
961 patch->filterVertices( filter );
962 }
963
964 clearCache();
965}
966
968{
969 for ( QgsPolygon *patch : std::as_const( mPatches ) )
970 {
971 patch->transformVertices( transform );
972 }
973
974 clearCache();
975}
976
978{
979 return mPatches.count();
980}
981
983{
984 return mPatches.at( index );
985}
986
988{
990 if ( !otherPolySurface )
991 return -1;
992
993 const int nPatches1 = mPatches.size();
994 const int nPatches2 = otherPolySurface->mPatches.size();
995 if ( nPatches1 < nPatches2 )
996 {
997 return -1;
998 }
999 if ( nPatches1 > nPatches2 )
1000 {
1001 return 1;
1002 }
1003
1004 for ( int i = 0; i < nPatches1; i++ )
1005 {
1006 const int polygonComp = mPatches.at( i )->compareTo( otherPolySurface->mPatches.at( i ) );
1007 if ( polygonComp != 0 )
1008 {
1009 return polygonComp;
1010 }
1011 }
1012
1013 return 0;
1014}
1015
1017{
1018 if ( flags == 0 && mHasCachedValidity )
1019 {
1020 // use cached validity results
1021 error = mValidityFailureReason;
1022 return error.isEmpty();
1023 }
1024
1025 if ( isEmpty() )
1026 return true;
1027
1028 error.clear();
1029
1030 // GEOS does not handle PolyhedralSurface, check the polygons one by one
1031 for ( int i = 0; i < mPatches.size(); ++i )
1032 {
1033 const QgsGeos geos( mPatches.at( i ), 0, Qgis::GeosCreationFlags() );
1034 const bool valid = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
1035 if ( !valid )
1036 {
1037 error = u"Polygon %1 is invalid: %2"_s.arg( QString::number( i ), error );
1038 break;
1039 }
1040 }
1041
1042 //TODO: Also check that the polyhedral surface is connected
1043 // For example, see SFCGAL implementation:
1044 // https://gitlab.com/sfcgal/SFCGAL/-/blob/19e3ff0c9057542a0e271edfee873d5f8b220871/src/algorithm/isValid.cpp#L469
1045
1046 const bool valid = error.isEmpty();
1047 if ( flags == 0 )
1048 {
1049 mValidityFailureReason = !valid ? error : QString();
1050 mHasCachedValidity = true;
1051 }
1052
1053 return valid;
1054}
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2130
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2133
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
Definition qgis.h:2216
@ Polygon
Polygons.
Definition qgis.h:368
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ Polygon
Polygon.
Definition qgis.h:284
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:295
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2729
An abstract base class for classes which transform geometries by transforming input points to output ...
virtual QgsAbstractGeometry * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const =0
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
QgsAbstractGeometry()=default
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition qgsbox3d.cpp:214
A const WKB pointer.
Definition qgswkbptr.h:139
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:60
Handles coordinate transforms between two coordinate systems.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate rotation angle for a vertex.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
bool dropMValue() override
Drops any measure values which exist in the geometry.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
void normalize() final
Reorganizes the geometry into a normalized form (or "canonical" form).
Definition qgscurve.cpp:212
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition qgscurve.cpp:181
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
bool isEmpty() const override
Returns true if the geometry is empty.
int numGeometries() const
Returns the number of geometries within the collection.
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:141
Line string geometry type, with support for z-dimension and m-values.
Multi line string geometry collection.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
Multi surface geometry collection.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
Polygon geometry type.
Definition qgspolygon.h:37
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
QgsPolyhedralSurface * clone() const override
Clones the geometry by performing a deep copy.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
QVector< QgsPolygon * > mPatches
double area() const override
Returns the planar, 2-dimensional area of the geometry.
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns a WKB representation of the geometry.
QgsAbstractGeometry * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
QString geometryType() const override
Returns a unique string representing the geometry type.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb().
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
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.
double area3D() const override
Returns the 3-dimensional surface area of the geometry.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
int dimension() const final
Returns the inherent dimension of the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
void normalize() override
Reorganizes the geometry into a normalized form (or "canonical" form).
bool removePatch(int patchIndex)
Removes a patch from the polyhedral surface.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
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.
bool dropMValue() override
Drops any measure values which exist in the geometry.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
int compareToSameClass(const QgsAbstractGeometry *other) const override
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
QgsPolyhedralSurface * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
QgsPolyhedralSurface * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
virtual void addPatch(QgsPolygon *patch)
Adds a patch to the geometry, transferring ownership to the polyhedral surface.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
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 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 hasCurvedSegments() const final
Returns true if the geometry contains curved segments.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate rotation angle for a vertex.
virtual void setPatches(const QVector< QgsPolygon * > &patches)
Sets all patches, transferring ownership to the polyhedral surface.
void clear() override
Clears the geometry, ie reset it to a null geometry.
bool isEmpty() const override
Returns true if the geometry is empty.
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.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QgsPolyhedralSurface * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
double perimeter() const override
Returns the planar, 2-dimensional perimeter of the geometry.
QgsMultiSurface * toCurveType() const override
Returns the geometry converted to the more generic curve type.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
int partCount() const override
Returns count of parts contained in the geometry.
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
void swapXy() override
Swaps the x and y coordinates from the geometry.
QgsPolyhedralSurface & operator=(const QgsPolyhedralSurface &p)
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.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
bool boundingBoxIntersects(const QgsBox3D &box3d) const override
Returns true if the bounding box of this geometry intersects with a box3d.
QgsMultiPolygon * toMultiPolygon() const
Converts a polyhedral surface to a multipolygon.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
Surface geometry type.
Definition qgssurface.h:34
QgsBox3D mBoundingBox
Definition qgssurface.h:101
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QString mValidityFailureReason
Definition qgssurface.h:103
bool mHasCachedValidity
Definition qgssurface.h:102
WKB pointer handler.
Definition qgswkbptr.h:45
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Contains geos related utilities and functions.
Definition qgsgeos.h:77
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsRingSequence > QgsCoordinateSequence
#define QgsDebugError(str)
Definition qgslogger.h:59
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:34
int vertex
Vertex number.
Definition qgsvertexid.h:98
int part
Part number.
Definition qgsvertexid.h:92
int ring
Ring number.
Definition qgsvertexid.h:95