QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
44
46{
47 if ( multiPolygon->isEmpty() )
48 {
49 return;
50 }
51
52 mPatches.reserve( multiPolygon->numGeometries() );
53 for ( int i = 0; i < multiPolygon->numGeometries(); ++i )
54 {
55 mPatches.append( multiPolygon->polygonN( i )->clone() );
56 }
57
59}
60
65
67{
68 auto result = std::make_unique< QgsPolyhedralSurface >();
69 result->mWkbType = mWkbType;
70 return result.release();
71}
72
74{
75 return QStringLiteral( "PolyhedralSurface" );
76}
77
79{
80 return 2;
81}
82
84 : QgsSurface( p )
85
86{
88 mPatches.reserve( p.mPatches.size() );
89
90 for ( const QgsPolygon *patch : p.mPatches )
91 {
92 mPatches.push_back( patch->clone() );
93 }
94
98}
99
100// cppcheck-suppress operatorEqVarError
102{
103 if ( &p != this )
104 {
106 mPatches.reserve( p.mPatches.size() );
107
108 for ( const QgsPolygon *patch : p.mPatches )
109 {
110 mPatches.push_back( patch->clone() );
111 }
112 }
113 return *this;
114}
115
120
122{
124 qDeleteAll( mPatches );
125 mPatches.clear();
126 clearCache();
127}
128
129
131{
132 clear();
133
134 if ( !wkbPtr )
135 {
136 return false;
137 }
138
139 Qgis::WkbType type = wkbPtr.readHeader();
141 {
142 return false;
143 }
144 mWkbType = type;
145
146 int nPatches;
147 wkbPtr >> nPatches;
148 std::unique_ptr< QgsPolygon > currentPatch;
149 for ( int i = 0; i < nPatches; ++i )
150 {
151 Qgis::WkbType polygonType = wkbPtr.readHeader();
152 wkbPtr -= 1 + sizeof( int );
153 Qgis::WkbType flatPolygonType = QgsWkbTypes::flatType( polygonType );
154 if ( flatPolygonType == Qgis::WkbType::Polygon )
155 {
156 currentPatch = std::make_unique<QgsPolygon>( );
157 }
158 else
159 {
160 return false;
161 }
162 currentPatch->fromWkb( wkbPtr ); // also updates wkbPtr
163 mPatches.append( currentPatch.release() );
164 }
165
166 return true;
167}
168
169bool QgsPolyhedralSurface::fromWkt( const QString &wkt )
170{
171 clear();
172
173 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
174
176 return false;
177
178 mWkbType = parts.first;
179
180 QString secondWithoutParentheses = parts.second;
181 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
182 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
183 secondWithoutParentheses.isEmpty() )
184 return true;
185
186 QString defaultChildWkbType = QStringLiteral( "Polygon%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
187
188 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
189 for ( const QString &childWkt : blocks )
190 {
191 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
192
193 if ( QgsWkbTypes::flatType( childParts.first ) == Qgis::WkbType::Polygon )
194 {
195 mPatches.append( new QgsPolygon() );
196 }
197 else
198 {
199 clear();
200 return false;
201 }
202
203 if ( !mPatches.back()->fromWkt( childWkt ) )
204 {
205 clear();
206 return false;
207 }
208 }
209
210 return true;
211}
212
214{
215 if ( mPatches.empty() )
216 {
217 return QgsBox3D();
218 }
219
220 QgsBox3D bbox = mPatches.at( 0 )->boundingBox3D();
221 for ( int i = 1; i < mPatches.size(); ++i )
222 {
223 QgsBox3D polygonBox = mPatches.at( i )->boundingBox3D();
224 bbox.combineWith( polygonBox );
225 }
226 return bbox;
227}
228
230{
231 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
232 for ( const QgsPolygon *patch : mPatches )
233 {
234 binarySize += patch->wkbSize( flags );
235 }
236 return binarySize;
237}
238
239QByteArray QgsPolyhedralSurface::asWkb( WkbFlags flags ) const
240{
241 QByteArray wkbArray;
242 wkbArray.resize( QgsPolyhedralSurface::wkbSize( flags ) );
243 QgsWkbPtr wkb( wkbArray );
244 wkb << static_cast<char>( QgsApplication::endian() );
245 wkb << static_cast<quint32>( wkbType() );
246 wkb << static_cast<quint32>( mPatches.size() );
247 for ( const QgsPolygon *patch : mPatches )
248 {
249 wkb << patch->asWkb( flags );
250 }
251 return wkbArray;
252}
253
254QString QgsPolyhedralSurface::asWkt( int precision ) const
255{
256 QString wkt = wktTypeStr();
257
258 if ( isEmpty() )
259 wkt += QLatin1String( " EMPTY" );
260 else
261 {
262 wkt += QLatin1String( " (" );
263 for ( const QgsPolygon *patch : mPatches )
264 {
265 QString childWkt = patch->asWkt( precision );
267 {
268 // Type names of linear geometries are omitted
269 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
270 }
271 wkt += childWkt + ',';
272 }
273 if ( wkt.endsWith( ',' ) )
274 {
275 wkt.chop( 1 ); // Remove last ','
276 }
277 wkt += ')';
278 }
279 return wkt;
280}
281
282QDomElement QgsPolyhedralSurface::asGml2( QDomDocument &, int, const QString &, const AxisOrder ) const
283{
284 QgsDebugError( QStringLiteral( "gml version 2 does not support PolyhedralSurface geometry" ) );
285 return QDomElement();
286}
287
288QDomElement QgsPolyhedralSurface::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
289{
290 QDomElement elemPolyhedralSurface = doc.createElementNS( ns, QStringLiteral( "PolyhedralSurface" ) );
291
292 if ( isEmpty() )
293 return elemPolyhedralSurface;
294
295 QDomElement elemPolygonPatches = doc.createElementNS( ns, QStringLiteral( "polygonPatches" ) );
296
297 for ( QgsPolygon *patch : mPatches )
298 {
299 QDomElement elemPolygonPatch = patch->asGml3( doc, precision, ns, axisOrder );
300 elemPolygonPatch.setTagName( "PolygonPatch" );
301
302 elemPolygonPatches.appendChild( elemPolygonPatch );
303 }
304 elemPolyhedralSurface.appendChild( elemPolygonPatches );
305
306 return elemPolyhedralSurface;
307}
308
309json QgsPolyhedralSurface::asJsonObject( int precision ) const
310{
311 // GeoJSON format does not support PolyhedralSurface geometry
312 // Return a multipolygon instead;
313 std::unique_ptr<QgsMultiPolygon> multiPolygon( toMultiPolygon() );
314 return multiPolygon->asJsonObject( precision );
315}
316
317QString QgsPolyhedralSurface::asKml( int ) const
318{
319 QgsDebugError( QStringLiteral( "kml format does not support PolyhedralSurface geometry" ) );
320 return QString( "" );
321}
322
324{
325 for ( QgsPolygon *patch : mPatches )
326 {
327 QgsCurve *exteriorRing = patch->exteriorRing();
328 if ( !exteriorRing )
329 continue;
330
331 exteriorRing->normalize();
332
333 if ( patch->numInteriorRings() > 0 )
334 {
335 QVector<QgsCurve *> interiorRings;
336 for ( int i = 0, n = patch->numInteriorRings(); i < n; ++i )
337 {
338 interiorRings.push_back( patch->interiorRing( i )->clone() );
339 }
340
341 // sort rings
342 std::sort( interiorRings.begin(), interiorRings.end(), []( const QgsCurve * a, const QgsCurve * b )
343 {
344 return a->compareTo( b ) > 0;
345 } );
346
347 patch->removeInteriorRings();
348 for ( QgsCurve *curve : interiorRings )
349 patch->addInteriorRing( curve );
350 }
351 }
352}
353
355{
356 // sum area of patches
357 double area = 0.0;
358 for ( const QgsPolygon *patch : mPatches )
359 {
360 area += patch->area();
361 }
362
363 return area;
364}
365
367{
368 // sum perimeter of patches
369 double perimeter = 0.0;
370 for ( const QgsPolygon *patch : mPatches )
371 {
372 perimeter += patch->perimeter();
373 }
374
375 return perimeter;
376}
377
379{
380 auto multiLine = std::make_unique<QgsMultiLineString>();
381 multiLine->reserve( mPatches.size() );
382 for ( QgsPolygon *polygon : mPatches )
383 {
384 std::unique_ptr<QgsAbstractGeometry> polygonBoundary( polygon->boundary() );
385 if ( QgsLineString *lineStringBoundary = qgsgeometry_cast< QgsLineString * >( polygonBoundary.get() ) )
386 {
387 multiLine->addGeometry( lineStringBoundary->clone() );
388 }
389 else if ( QgsMultiLineString *multiLineStringBoundary = qgsgeometry_cast< QgsMultiLineString * >( polygonBoundary.get() ) )
390 {
391 for ( int j = 0; j < multiLineStringBoundary->numGeometries(); ++j )
392 {
393 multiLine->addGeometry( multiLineStringBoundary->geometryN( j )->clone() );
394 }
395 }
396 }
397
398 if ( multiLine->numGeometries() == 0 )
399 {
400 return nullptr;
401 }
402 return multiLine.release();
403}
404
405QgsPolyhedralSurface *QgsPolyhedralSurface::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
406{
407 std::unique_ptr< QgsPolyhedralSurface > surface( createEmptyWithSameType() );
408
409 for ( QgsPolygon *patch : mPatches )
410 {
411 // exterior ring
412 std::unique_ptr<QgsCurve> exteriorRing( static_cast< QgsCurve *>( patch->exteriorRing()->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) );
413 if ( !exteriorRing )
414 {
415 return nullptr;
416 }
417
418 auto gridifiedPatch = std::make_unique<QgsPolygon>();
419 gridifiedPatch->setExteriorRing( exteriorRing.release() );
420
421 //interior rings
422 for ( int i = 0, n = patch->numInteriorRings(); i < n; ++i )
423 {
424 QgsCurve *interiorRing = patch->interiorRing( i );
425 if ( !interiorRing )
426 continue;
427
428 std::unique_ptr<QgsCurve> gridifiedInterior( static_cast< QgsCurve * >( interiorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) );
429 if ( gridifiedInterior )
430 gridifiedPatch->addInteriorRing( gridifiedInterior.release() );
431 }
432
433 surface->addPatch( gridifiedPatch.release() );
434 }
435
436 return surface.release();
437}
438
440{
441 if ( isEmpty() )
442 return nullptr;
443
444 auto simplifiedGeom = std::make_unique< QgsPolyhedralSurface >();
445 for ( QgsPolygon *polygon : mPatches )
446 {
447 std::unique_ptr<QgsCurvePolygon> polygonSimplified( polygon->simplifyByDistance( tolerance ) );
448 simplifiedGeom->addPatch( polygonSimplified->surfaceToPolygon() );
449 }
450 return simplifiedGeom.release();
451}
452
453bool QgsPolyhedralSurface::removeDuplicateNodes( double epsilon, bool useZValues )
454{
455 bool result = false;
456
457 for ( QgsPolygon *patch : std::as_const( mPatches ) )
458 {
459 if ( patch->removeDuplicateNodes( epsilon, useZValues ) )
460 {
461 result = true;
462 }
463 }
464 return result;
465}
466
468{
469 // if we already have the bounding box calculated, then this check is trivial!
470 if ( !mBoundingBox.isNull() )
471 {
472 return mBoundingBox.intersects( box3d );
473 }
474
475 // loop through each patch and test the bounding box intersection.
476 // This gives us a chance to use optimisations which may be present on the individual
477 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
478 // of each individual patch geometry which we would have to do anyway... (and these
479 // bounding boxes are cached, so would be reused without additional expense)
480 for ( const QgsPolygon *patch : mPatches )
481 {
482 if ( patch->boundingBoxIntersects( box3d ) )
483 return true;
484 }
485
486 // even if we don't intersect the bounding box of any rings, we may still intersect the
487 // bounding box of the overall polygon (we are considering worst case scenario here and
488 // the polygon is invalid, with rings outside the exterior ring!)
489 // so here we fall back to the non-optimised base class check which has to first calculate
490 // the overall bounding box of the polygon..
491 return QgsSurface::boundingBoxIntersects( box3d );
492}
493
494void QgsPolyhedralSurface::setPatches( const QVector<QgsPolygon *> &patches )
495{
496 qDeleteAll( mPatches );
497 mPatches.clear();
498
499 for ( QgsPolygon *patch : patches )
500 {
501 addPatch( patch );
502 }
503
504 clearCache();
505}
506
508{
509 if ( !patch )
510 return;
511
512 if ( mPatches.empty() )
513 {
515 }
516
517 // Ensure dimensionality of patch matches polyhedral surface
518 if ( !is3D() )
519 patch->dropZValue();
520 else if ( !patch->is3D() )
521 patch->addZValue();
522
523 if ( !isMeasure() )
524 patch->dropMValue();
525 else if ( !patch->isMeasure() )
526 patch->addMValue();
527
528 mPatches.append( patch );
529 clearCache();
530}
531
533{
534 if ( patchIndex < 0 || patchIndex >= mPatches.size() )
535 {
536 return false;
537 }
538
539 delete mPatches.takeAt( patchIndex );
540 clearCache();
541 return true;
542}
543
545{
546 QPainterPath painterPath;
547 for ( const QgsPolygon *patch : mPatches )
548 {
549 QPainterPath patchPath = patch->asQPainterPath();
550 patchPath.closeSubpath();
551 painterPath.addPath( patchPath );
552 }
553
554 return painterPath;
555}
556
557void QgsPolyhedralSurface::draw( QPainter &p ) const
558{
559 if ( mPatches.empty() )
560 return;
561
562 for ( const QgsPolygon *patch : mPatches )
563 {
564 patch->draw( p );
565 }
566}
567
569{
570 for ( QgsPolygon *patch : std::as_const( mPatches ) )
571 {
572 patch->transform( ct, d, transformZ );
573 }
574 clearCache();
575}
576
577void QgsPolyhedralSurface::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
578{
579 for ( QgsPolygon *patch : std::as_const( mPatches ) )
580 {
581 patch->transform( t, zTranslate, zScale, mTranslate, mScale );
582 }
583 clearCache();
584}
585
587{
588 QgsCoordinateSequence sequence;
589 for ( const QgsPolygon *polygon : std::as_const( mPatches ) )
590 {
591 QgsCoordinateSequence polyCoords = polygon->coordinateSequence();
592 QgsCoordinateSequence::const_iterator cIt = polyCoords.constBegin();
593 for ( ; cIt != polyCoords.constEnd(); ++cIt )
594 {
595 sequence.push_back( *cIt );
596 }
597 }
598
599 return sequence;
600}
601
603{
604 int count = 0;
605 for ( const QgsPolygon *patch : mPatches )
606 {
607 count += patch->nCoordinates();
608 }
609 return count;
610}
611
613{
614 if ( id.part < 0 || id.part >= partCount() )
615 return -1;
616
617 int number = 0;
618 for ( int i = 0; i < mPatches.count(); ++i )
619 {
620 if ( id.part == i )
621 {
622 int partNumber = mPatches.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
623 if ( partNumber == -1 )
624 {
625 return -1;
626 }
627
628 return number + partNumber;
629 }
630 else
631 {
632 number += mPatches.at( i )->nCoordinates();
633 }
634 }
635
636 return -1; // should not happen
637}
638
640{
641 return mPatches.isEmpty();
642}
643
644double QgsPolyhedralSurface::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
645{
646 QVector<QgsPolygon *> segmentList = mPatches;
647 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
648}
649
651{
652 if ( vId.part < 0 )
653 {
654 vId.part = 0;
655 vId.ring = -1;
656 vId.vertex = -1;
657 }
658
659 if ( isEmpty() || vId.part >= partCount() )
660 {
661 return false;
662 }
663
664 QgsPolygon *patch = mPatches[vId.part];
665 if ( patch->nextVertex( vId, vertex ) )
666 {
667 return true;
668 }
669
670 ++vId.part;
671 vId.ring = 0;
672 vId.vertex = -1;
673 if ( vId.part >= partCount() )
674 {
675 return false;
676 }
677 patch = mPatches[vId.part];
678 return patch->nextVertex( vId, vertex );
679}
680
682{
683 if ( vertex.part < 0 || vertex.part >= partCount() )
684 {
685 previousVertex = QgsVertexId();
687 return;
688 }
689
690 QgsPolygon *patch = mPatches[vertex.ring];
691 patch->adjacentVertices( QgsVertexId( 0, 0, vertex.vertex ), previousVertex, nextVertex );
692 return;
693}
694
696{
697 if ( vId.part < 0 || vId.part >= partCount() )
698 {
699 return false;
700 }
701
702 QgsPolygon *patch = mPatches.at( vId.part );
703 bool success = patch->insertVertex( QgsVertexId( 0, vId.ring, vId.vertex ), vertex );
704 if ( success )
705 {
706 clearCache();
707 }
708
709 return success;
710}
711
713{
714 if ( vId.part < 0 || vId.part >= partCount() )
715 {
716 return false;
717 }
718
719 QgsPolygon *patch = mPatches.at( vId.part );
720 bool success = patch->moveVertex( QgsVertexId( 0, vId.ring, vId.vertex ), newPos );
721 if ( success )
722 {
723 clearCache();
724 }
725
726 return success;
727}
728
730{
731 if ( vId.part < 0 || vId.part >= partCount() )
732 {
733 return false;
734 }
735
736 QgsPolygon *patch = mPatches.at( vId.part );
737 bool success = patch->deleteVertex( QgsVertexId( 0, vId.ring, vId.vertex ) );
738 if ( success )
739 {
740 // if the patch has lost its exterior ring, remove it
741 if ( !patch->exteriorRing() )
742 {
743 delete mPatches.takeAt( vId.part );
744 }
745 clearCache();
746 }
747
748 return success;
749}
750
752{
753 return false;
754}
755
757{
758 // This is only used by curves
759 Q_UNUSED( tolerance )
760 Q_UNUSED( toleranceType )
761 return clone();
762}
763
765{
766 if ( vertex.part < 0 || vertex.part >= partCount() )
767 {
768 return 0.0;
769 }
770
771 QgsPolygon *patch = mPatches[vertex.part];
772 return patch->vertexAngle( QgsVertexId( 0, vertex.ring, vertex.vertex ) );
773}
774
775int QgsPolyhedralSurface::vertexCount( int part, int ring ) const
776{
777 if ( part < 0 || part >= partCount() )
778 {
779 return 0;
780 }
781
782 QgsPolygon *patchPolygon = mPatches[part];
783 QgsCurve *ringCurve = ring == 0 ? patchPolygon->exteriorRing() : patchPolygon->interiorRing( ring - 1 );
784 if ( ringCurve )
785 {
786 return ringCurve->vertexCount();
787 }
788
789 return 0;
790}
791
793{
794 if ( part < 0 || part >= partCount() )
795 return 0;
796
797 return mPatches[part]->ringCount();
798}
799
801{
802 return mPatches.size();
803}
804
806{
807 if ( id.part < 0 || id.part >= partCount() )
808 return QgsPoint();
809
810 return mPatches[id.part]->vertexAt( id );
811}
812
814{
815 if ( startVertex.part < 0 || startVertex.part >= partCount() )
816 {
817 return 0.0;
818 }
819
820 const QgsPolygon *patch = mPatches[startVertex.part];
821 return patch->segmentLength( QgsVertexId( 0, startVertex.ring, startVertex.vertex ) );
822}
823
825{
827 {
828 return false;
829 }
830
832
833 for ( QgsPolygon *patch : std::as_const( mPatches ) )
834 {
835 patch->addZValue( zValue );
836 }
837 clearCache();
838 return true;
839}
840
842{
844 {
845 return false;
846 }
847
849
850 for ( QgsPolygon *patch : std::as_const( mPatches ) )
851 {
852 patch->addMValue( mValue );
853 }
854 clearCache();
855 return true;
856}
857
859{
860 if ( !is3D() )
861 {
862 return false;
863 }
864
866 for ( QgsPolygon *patch : std::as_const( mPatches ) )
867 {
868 patch->dropZValue();
869 }
870 clearCache();
871 return true;
872}
873
875{
876 if ( !isMeasure() )
877 {
878 return false;
879 }
880
882 for ( QgsPolygon *patch : std::as_const( mPatches ) )
883 {
884 patch->dropMValue();
885 }
886 clearCache();
887 return true;
888}
889
891{
892 for ( QgsPolygon *patch : std::as_const( mPatches ) )
893 {
894 patch->swapXy();
895 }
896 clearCache();
897}
898
900{
901 auto multiSurface = std::make_unique< QgsMultiSurface >();
902 multiSurface->reserve( mPatches.size() );
903 for ( const QgsPolygon *polygon : std::as_const( mPatches ) )
904 {
905 multiSurface->addGeometry( polygon->clone() );
906 }
907 return multiSurface.release();
908}
909
911{
912 if ( !transformer )
913 return false;
914
915 bool res = true;
916
917 for ( QgsPolygon *patch : std::as_const( mPatches ) )
918 {
919 res = patch->transform( transformer );
920 if ( feedback && feedback->isCanceled() )
921 res = false;
922
923 if ( !res )
924 break;
925 }
926
927 clearCache();
928 return res;
929}
930
932{
933 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
934 multiPolygon->reserve( mPatches.size() );
935 for ( const QgsPolygon *polygon : std::as_const( mPatches ) )
936 {
937 multiPolygon->addGeometry( polygon->clone() );
938 }
939 return multiPolygon.release();
940}
941
942void QgsPolyhedralSurface::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
943{
944 for ( QgsPolygon *patch : std::as_const( mPatches ) )
945 {
946 patch->filterVertices( filter );
947 }
948
949 clearCache();
950}
951
953{
954 for ( QgsPolygon *patch : std::as_const( mPatches ) )
955 {
956 patch->transformVertices( transform );
957 }
958
959 clearCache();
960}
961
963{
964 return mPatches.count();
965}
966
968{
969 return mPatches.at( index );
970}
971
973{
975 if ( !otherPolySurface )
976 return -1;
977
978 const int nPatches1 = mPatches.size();
979 const int nPatches2 = otherPolySurface->mPatches.size();
980 if ( nPatches1 < nPatches2 )
981 {
982 return -1;
983 }
984 if ( nPatches1 > nPatches2 )
985 {
986 return 1;
987 }
988
989 for ( int i = 0; i < nPatches1; i++ )
990 {
991 const int polygonComp = mPatches.at( i )->compareTo( otherPolySurface->mPatches.at( i ) );
992 if ( polygonComp != 0 )
993 {
994 return polygonComp;
995 }
996 }
997
998 return 0;
999}
1000
1002{
1003 if ( flags == 0 && mHasCachedValidity )
1004 {
1005 // use cached validity results
1006 error = mValidityFailureReason;
1007 return error.isEmpty();
1008 }
1009
1010 if ( isEmpty() )
1011 return true;
1012
1013 error.clear();
1014
1015 // GEOS does not handle PolyhedralSurface, check the polygons one by one
1016 for ( int i = 0; i < mPatches.size(); ++i )
1017 {
1018 const QgsGeos geos( mPatches.at( i ), 0, Qgis::GeosCreationFlags() );
1019 const bool valid = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
1020 if ( !valid )
1021 {
1022 error = QStringLiteral( "Polygon %1 is invalid: %2" ).arg( QString::number( i ), error );
1023 break;
1024 }
1025 }
1026
1027 //TODO: Also check that the polyhedral surface is connected
1028 // For example, see SFCGAL implementation:
1029 // https://gitlab.com/sfcgal/SFCGAL/-/blob/19e3ff0c9057542a0e271edfee873d5f8b220871/src/algorithm/isValid.cpp#L469
1030
1031 const bool valid = error.isEmpty();
1032 if ( flags == 0 )
1033 {
1034 mValidityFailureReason = !valid ? error : QString();
1035 mHasCachedValidity = true;
1036 }
1037
1038 return valid;
1039}
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2072
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2075
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
Definition qgis.h:2158
@ Polygon
Polygons.
Definition qgis.h:361
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
@ Polygon
Polygon.
Definition qgis.h:281
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:292
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2671
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:42
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition qgsbox3d.cpp:210
A const WKB pointer.
Definition qgswkbptr.h:139
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:56
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:53
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:49
Polygon geometry type.
Definition qgspolygon.h:33
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.
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:57
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30
int vertex
Vertex number.
Definition qgsvertexid.h:94
int part
Part number.
Definition qgsvertexid.h:88
int ring
Ring number.
Definition qgsvertexid.h:91