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