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