Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgscurvepolygon.cpp
3 : : ---------------------
4 : : begin : September 2014
5 : : copyright : (C) 2014 by Marco Hugentobler
6 : : email : marco at sourcepole dot ch
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 : :
18 : : #include "qgscurvepolygon.h"
19 : : #include "qgsapplication.h"
20 : : #include "qgscircularstring.h"
21 : : #include "qgscompoundcurve.h"
22 : : #include "qgsgeometryutils.h"
23 : : #include "qgslinestring.h"
24 : : #include "qgspolygon.h"
25 : : #include "qgswkbptr.h"
26 : : #include "qgsmulticurve.h"
27 : : #include "qgsfeedback.h"
28 : :
29 : : #include <QJsonArray>
30 : : #include <QJsonObject>
31 : : #include <QPainter>
32 : : #include <QPainterPath>
33 : : #include <memory>
34 : : #include <nlohmann/json.hpp>
35 : :
36 : 3676 : QgsCurvePolygon::QgsCurvePolygon()
37 : 7352 : {
38 : 3676 : mWkbType = QgsWkbTypes::CurvePolygon;
39 : 3676 : }
40 : :
41 : 2515 : QgsCurvePolygon::~QgsCurvePolygon()
42 : 2515 : {
43 : 2224 : clear();
44 : 2515 : }
45 : :
46 : 1 : QgsCurvePolygon *QgsCurvePolygon::createEmptyWithSameType() const
47 : : {
48 : 1 : auto result = std::make_unique< QgsCurvePolygon >();
49 : 1 : result->mWkbType = mWkbType;
50 : 1 : return result.release();
51 : 1 : }
52 : :
53 : 416 : QString QgsCurvePolygon::geometryType() const
54 : : {
55 : 832 : return QStringLiteral( "CurvePolygon" );
56 : : }
57 : :
58 : 97 : int QgsCurvePolygon::dimension() const
59 : : {
60 : 97 : return 2;
61 : : }
62 : :
63 : 305 : QgsCurvePolygon::QgsCurvePolygon( const QgsCurvePolygon &p )
64 : 305 : : QgsSurface( p )
65 : :
66 : 610 : {
67 : 305 : mWkbType = p.mWkbType;
68 : 305 : if ( p.mExteriorRing )
69 : : {
70 : 258 : mExteriorRing.reset( p.mExteriorRing->clone() );
71 : 258 : }
72 : :
73 : 322 : for ( const QgsCurve *ring : p.mInteriorRings )
74 : : {
75 : 17 : mInteriorRings.push_back( ring->clone() );
76 : : }
77 : 305 : }
78 : :
79 : 11 : QgsCurvePolygon &QgsCurvePolygon::operator=( const QgsCurvePolygon &p )
80 : : {
81 : 11 : if ( &p != this )
82 : : {
83 : 11 : clearCache();
84 : 11 : QgsSurface::operator=( p );
85 : 11 : if ( p.mExteriorRing )
86 : : {
87 : 8 : mExteriorRing.reset( p.mExteriorRing->clone() );
88 : 8 : }
89 : :
90 : 13 : for ( const QgsCurve *ring : p.mInteriorRings )
91 : : {
92 : 2 : mInteriorRings.push_back( ring->clone() );
93 : : }
94 : 11 : }
95 : 11 : return *this;
96 : : }
97 : :
98 : 114 : bool QgsCurvePolygon::operator==( const QgsAbstractGeometry &other ) const
99 : : {
100 : 114 : const QgsCurvePolygon *otherPolygon = qgsgeometry_cast< const QgsCurvePolygon * >( &other );
101 : 114 : if ( !otherPolygon )
102 : 2 : return false;
103 : :
104 : : //run cheap checks first
105 : 112 : if ( mWkbType != otherPolygon->mWkbType )
106 : 4 : return false;
107 : :
108 : 108 : if ( ( !mExteriorRing && otherPolygon->mExteriorRing ) || ( mExteriorRing && !otherPolygon->mExteriorRing ) )
109 : 4 : return false;
110 : :
111 : 104 : if ( mInteriorRings.count() != otherPolygon->mInteriorRings.count() )
112 : 4 : return false;
113 : :
114 : : // compare rings
115 : 100 : if ( mExteriorRing && otherPolygon->mExteriorRing )
116 : : {
117 : 90 : if ( *mExteriorRing != *otherPolygon->mExteriorRing )
118 : 5 : return false;
119 : 85 : }
120 : :
121 : 121 : for ( int i = 0; i < mInteriorRings.count(); ++i )
122 : : {
123 : 60 : if ( ( !mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) ) ||
124 : 30 : ( mInteriorRings.at( i ) && !otherPolygon->mInteriorRings.at( i ) ) )
125 : 0 : return false;
126 : :
127 : 30 : if ( mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) &&
128 : 30 : *mInteriorRings.at( i ) != *otherPolygon->mInteriorRings.at( i ) )
129 : 4 : return false;
130 : 26 : }
131 : :
132 : 91 : return true;
133 : 114 : }
134 : :
135 : 19 : bool QgsCurvePolygon::operator!=( const QgsAbstractGeometry &other ) const
136 : : {
137 : 19 : return !operator==( other );
138 : : }
139 : :
140 : 57 : QgsCurvePolygon *QgsCurvePolygon::clone() const
141 : : {
142 : 57 : return new QgsCurvePolygon( *this );
143 : 0 : }
144 : :
145 : 3736 : void QgsCurvePolygon::clear()
146 : : {
147 : 3736 : mWkbType = QgsWkbTypes::CurvePolygon;
148 : 3736 : mExteriorRing.reset();
149 : 3736 : qDeleteAll( mInteriorRings );
150 : 3736 : mInteriorRings.clear();
151 : 3736 : clearCache();
152 : 3736 : }
153 : :
154 : :
155 : 15 : bool QgsCurvePolygon::fromWkb( QgsConstWkbPtr &wkbPtr )
156 : : {
157 : 15 : clear();
158 : 15 : if ( !wkbPtr )
159 : : {
160 : 1 : return false;
161 : : }
162 : :
163 : 14 : QgsWkbTypes::Type type = wkbPtr.readHeader();
164 : 14 : if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::CurvePolygon )
165 : : {
166 : 1 : return false;
167 : : }
168 : 13 : mWkbType = type;
169 : :
170 : : int nRings;
171 : 13 : wkbPtr >> nRings;
172 : 13 : std::unique_ptr< QgsCurve > currentCurve;
173 : 32 : for ( int i = 0; i < nRings; ++i )
174 : : {
175 : 19 : QgsWkbTypes::Type curveType = wkbPtr.readHeader();
176 : 19 : wkbPtr -= 1 + sizeof( int );
177 : 19 : QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( curveType );
178 : 19 : if ( flatCurveType == QgsWkbTypes::LineString )
179 : : {
180 : 0 : currentCurve.reset( new QgsLineString() );
181 : 0 : }
182 : 19 : else if ( flatCurveType == QgsWkbTypes::CircularString )
183 : : {
184 : 18 : currentCurve.reset( new QgsCircularString() );
185 : 18 : }
186 : 1 : else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
187 : : {
188 : 1 : currentCurve.reset( new QgsCompoundCurve() );
189 : 1 : }
190 : : else
191 : : {
192 : 0 : return false;
193 : : }
194 : 19 : currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
195 : 19 : if ( i == 0 )
196 : : {
197 : 13 : mExteriorRing = std::move( currentCurve );
198 : 13 : }
199 : : else
200 : : {
201 : 6 : mInteriorRings.append( currentCurve.release() );
202 : : }
203 : 19 : }
204 : :
205 : 13 : return true;
206 : 15 : }
207 : :
208 : 929 : bool QgsCurvePolygon::fromWkt( const QString &wkt )
209 : : {
210 : 929 : clear();
211 : :
212 : 929 : QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
213 : :
214 : 929 : if ( QgsWkbTypes::geometryType( parts.first ) != QgsWkbTypes::PolygonGeometry )
215 : 2 : return false;
216 : :
217 : 927 : mWkbType = parts.first;
218 : :
219 : 927 : QString secondWithoutParentheses = parts.second;
220 : 927 : secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
221 : 1053 : if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
222 : 126 : secondWithoutParentheses.isEmpty() )
223 : 802 : return true;
224 : :
225 : 290 : QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
226 : :
227 : 125 : const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
228 : 309 : for ( const QString &childWkt : blocks )
229 : : {
230 : 192 : QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
231 : :
232 : 192 : QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( childParts.first );
233 : 192 : if ( flatCurveType == QgsWkbTypes::LineString )
234 : 178 : mInteriorRings.append( new QgsLineString() );
235 : 14 : else if ( flatCurveType == QgsWkbTypes::CircularString )
236 : 7 : mInteriorRings.append( new QgsCircularString() );
237 : 7 : else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
238 : 2 : mInteriorRings.append( new QgsCompoundCurve() );
239 : : else
240 : : {
241 : 5 : clear();
242 : 5 : return false;
243 : : }
244 : 187 : if ( !mInteriorRings.back()->fromWkt( childWkt ) )
245 : : {
246 : 3 : clear();
247 : 3 : return false;
248 : : }
249 : 192 : }
250 : :
251 : 117 : if ( mInteriorRings.isEmpty() )
252 : : {
253 : 0 : clear();
254 : 0 : return false;
255 : : }
256 : :
257 : 117 : mExteriorRing.reset( mInteriorRings.takeFirst() );
258 : :
259 : : //scan through rings and check if dimensionality of rings is different to CurvePolygon.
260 : : //if so, update the type dimensionality of the CurvePolygon to match
261 : 117 : bool hasZ = false;
262 : 117 : bool hasM = false;
263 : 117 : if ( mExteriorRing )
264 : : {
265 : 117 : hasZ = hasZ || mExteriorRing->is3D();
266 : 117 : hasM = hasM || mExteriorRing->isMeasure();
267 : 117 : }
268 : 179 : for ( const QgsCurve *curve : std::as_const( mInteriorRings ) )
269 : : {
270 : 67 : hasZ = hasZ || curve->is3D();
271 : 67 : hasM = hasM || curve->isMeasure();
272 : 67 : if ( hasZ && hasM )
273 : 5 : break;
274 : : }
275 : 117 : if ( hasZ )
276 : 22 : addZValue( 0 );
277 : 117 : if ( hasM )
278 : 19 : addMValue( 0 );
279 : :
280 : 117 : return true;
281 : 929 : }
282 : :
283 : 948 : QgsRectangle QgsCurvePolygon::calculateBoundingBox() const
284 : : {
285 : 948 : if ( mExteriorRing )
286 : : {
287 : 947 : return mExteriorRing->boundingBox();
288 : : }
289 : 1 : return QgsRectangle();
290 : 948 : }
291 : :
292 : 23 : int QgsCurvePolygon::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
293 : : {
294 : 23 : int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
295 : 23 : if ( mExteriorRing )
296 : : {
297 : 23 : binarySize += mExteriorRing->wkbSize( flags );
298 : 23 : }
299 : 32 : for ( const QgsCurve *curve : mInteriorRings )
300 : : {
301 : 9 : binarySize += curve->wkbSize( flags );
302 : : }
303 : 23 : return binarySize;
304 : : }
305 : :
306 : 13 : QByteArray QgsCurvePolygon::asWkb( WkbFlags flags ) const
307 : : {
308 : 13 : QByteArray wkbArray;
309 : 13 : wkbArray.resize( QgsCurvePolygon::wkbSize( flags ) );
310 : 13 : QgsWkbPtr wkbPtr( wkbArray );
311 : 13 : wkbPtr << static_cast<char>( QgsApplication::endian() );
312 : 13 : wkbPtr << static_cast<quint32>( wkbType() );
313 : 13 : wkbPtr << static_cast<quint32>( ( mExteriorRing ? 1 : 0 ) + mInteriorRings.size() );
314 : 13 : if ( mExteriorRing )
315 : : {
316 : 13 : wkbPtr << mExteriorRing->asWkb( flags );
317 : 13 : }
318 : 19 : for ( const QgsCurve *curve : mInteriorRings )
319 : : {
320 : 6 : wkbPtr << curve->asWkb( flags );
321 : : }
322 : 13 : return wkbArray;
323 : 13 : }
324 : :
325 : 1312 : QString QgsCurvePolygon::asWkt( int precision ) const
326 : : {
327 : 1312 : QString wkt = wktTypeStr();
328 : :
329 : 1312 : if ( isEmpty() )
330 : 1207 : wkt += QLatin1String( " EMPTY" );
331 : : else
332 : : {
333 : 105 : wkt += QLatin1String( " (" );
334 : 105 : if ( mExteriorRing )
335 : : {
336 : 105 : QString childWkt = mExteriorRing->asWkt( precision );
337 : 105 : if ( qgsgeometry_cast<QgsLineString *>( mExteriorRing.get() ) )
338 : : {
339 : : // Type names of linear geometries are omitted
340 : 99 : childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
341 : 99 : }
342 : 105 : wkt += childWkt + ',';
343 : 105 : }
344 : 130 : for ( const QgsCurve *curve : mInteriorRings )
345 : : {
346 : 25 : QString childWkt = curve->asWkt( precision );
347 : 25 : if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
348 : : {
349 : : // Type names of linear geometries are omitted
350 : 24 : childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
351 : 24 : }
352 : 25 : wkt += childWkt + ',';
353 : 25 : }
354 : 105 : if ( wkt.endsWith( ',' ) )
355 : : {
356 : 105 : wkt.chop( 1 ); // Remove last ','
357 : 105 : }
358 : 105 : wkt += ')';
359 : : }
360 : 1312 : return wkt;
361 : 1312 : }
362 : :
363 : 18 : QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
364 : : {
365 : : // GML2 does not support curves
366 : 36 : QDomElement elemPolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
367 : :
368 : 18 : if ( isEmpty() )
369 : 3 : return elemPolygon;
370 : :
371 : 30 : QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, QStringLiteral( "outerBoundaryIs" ) );
372 : 15 : std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
373 : 15 : QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
374 : 30 : outerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
375 : 15 : elemOuterBoundaryIs.appendChild( outerRing );
376 : 15 : elemPolygon.appendChild( elemOuterBoundaryIs );
377 : 15 : std::unique_ptr< QgsLineString > interiorLineString;
378 : 19 : for ( int i = 0, n = numInteriorRings(); i < n; ++i )
379 : : {
380 : 8 : QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, QStringLiteral( "innerBoundaryIs" ) );
381 : 4 : interiorLineString.reset( interiorRing( i )->curveToLine() );
382 : 4 : QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
383 : 8 : innerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
384 : 4 : elemInnerBoundaryIs.appendChild( innerRing );
385 : 4 : elemPolygon.appendChild( elemInnerBoundaryIs );
386 : 4 : }
387 : 15 : return elemPolygon;
388 : 18 : }
389 : :
390 : 16 : QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
391 : : {
392 : 32 : QDomElement elemCurvePolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
393 : :
394 : 16 : if ( isEmpty() )
395 : 2 : return elemCurvePolygon;
396 : :
397 : 28 : QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
398 : 14 : QDomElement curveElem = exteriorRing()->asGml3( doc, precision, ns, axisOrder );
399 : 14 : if ( curveElem.tagName() == QLatin1String( "LineString" ) )
400 : : {
401 : 20 : curveElem.setTagName( QStringLiteral( "LinearRing" ) );
402 : 10 : }
403 : 14 : elemExterior.appendChild( curveElem );
404 : 14 : elemCurvePolygon.appendChild( elemExterior );
405 : :
406 : 18 : for ( int i = 0, n = numInteriorRings(); i < n; ++i )
407 : : {
408 : 8 : QDomElement elemInterior = doc.createElementNS( ns, QStringLiteral( "interior" ) );
409 : 4 : QDomElement innerRing = interiorRing( i )->asGml3( doc, precision, ns, axisOrder );
410 : 4 : if ( innerRing.tagName() == QLatin1String( "LineString" ) )
411 : : {
412 : 4 : innerRing.setTagName( QStringLiteral( "LinearRing" ) );
413 : 2 : }
414 : 4 : elemInterior.appendChild( innerRing );
415 : 4 : elemCurvePolygon.appendChild( elemInterior );
416 : 4 : }
417 : 14 : return elemCurvePolygon;
418 : 16 : }
419 : :
420 : 11 : json QgsCurvePolygon::asJsonObject( int precision ) const
421 : : {
422 : 11 : json coordinates( json::array( ) );
423 : 11 : if ( auto *lExteriorRing = exteriorRing() )
424 : : {
425 : 8 : std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
426 : 8 : QgsPointSequence exteriorPts;
427 : 8 : exteriorLineString->points( exteriorPts );
428 : 8 : coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
429 : :
430 : 8 : std::unique_ptr< QgsLineString > interiorLineString;
431 : 14 : for ( int i = 0, n = numInteriorRings(); i < n; ++i )
432 : : {
433 : 6 : interiorLineString.reset( interiorRing( i )->curveToLine() );
434 : 6 : QgsPointSequence interiorPts;
435 : 6 : interiorLineString->points( interiorPts );
436 : 6 : coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
437 : 6 : }
438 : 8 : }
439 : 44 : return
440 : 33 : {
441 : 11 : { "type", "Polygon" },
442 : 11 : { "coordinates", coordinates }
443 : : };
444 : 11 : }
445 : :
446 : 6 : QString QgsCurvePolygon::asKml( int precision ) const
447 : : {
448 : 6 : QString kml;
449 : 6 : kml.append( QLatin1String( "<Polygon>" ) );
450 : 6 : if ( mExteriorRing )
451 : : {
452 : 6 : kml.append( QLatin1String( "<outerBoundaryIs>" ) );
453 : 6 : kml.append( mExteriorRing->asKml( precision ) );
454 : 6 : kml.append( QLatin1String( "</outerBoundaryIs>" ) );
455 : 6 : }
456 : 6 : const QVector<QgsCurve *> &interiorRings = mInteriorRings;
457 : 9 : for ( const QgsCurve *ring : interiorRings )
458 : : {
459 : 3 : kml.append( QLatin1String( "<innerBoundaryIs>" ) );
460 : 3 : kml.append( ring->asKml( precision ) );
461 : 3 : kml.append( QLatin1String( "</innerBoundaryIs>" ) );
462 : : }
463 : 6 : kml.append( QLatin1String( "</Polygon>" ) );
464 : 6 : return kml;
465 : 6 : }
466 : :
467 : 161 : double QgsCurvePolygon::area() const
468 : : {
469 : 161 : if ( !mExteriorRing )
470 : : {
471 : 4 : return 0.0;
472 : : }
473 : :
474 : 157 : double totalArea = 0.0;
475 : :
476 : 157 : if ( mExteriorRing->isRing() )
477 : : {
478 : 155 : double area = 0.0;
479 : 155 : mExteriorRing->sumUpArea( area );
480 : 155 : totalArea += std::fabs( area );
481 : 155 : }
482 : :
483 : 159 : for ( const QgsCurve *ring : mInteriorRings )
484 : : {
485 : 2 : double area = 0.0;
486 : 2 : if ( ring->isRing() )
487 : : {
488 : 2 : ring->sumUpArea( area );
489 : 2 : totalArea -= std::fabs( area );
490 : 2 : }
491 : : }
492 : 157 : return totalArea;
493 : 161 : }
494 : :
495 : 76 : double QgsCurvePolygon::perimeter() const
496 : : {
497 : 76 : if ( !mExteriorRing )
498 : 4 : return 0.0;
499 : :
500 : : //sum perimeter of rings
501 : 72 : double perimeter = mExteriorRing->length();
502 : 72 : for ( const QgsCurve *ring : mInteriorRings )
503 : : {
504 : 0 : perimeter += ring->length();
505 : : }
506 : 72 : return perimeter;
507 : 76 : }
508 : :
509 : 12 : QgsPolygon *QgsCurvePolygon::surfaceToPolygon() const
510 : : {
511 : 12 : std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
512 : 12 : if ( !mExteriorRing )
513 : 1 : return polygon.release();
514 : :
515 : 11 : polygon->setExteriorRing( exteriorRing()->curveToLine() );
516 : 11 : QVector<QgsCurve *> interiors;
517 : 11 : int n = numInteriorRings();
518 : 11 : interiors.reserve( n );
519 : 13 : for ( int i = 0; i < n; ++i )
520 : : {
521 : 2 : interiors.append( interiorRing( i )->curveToLine() );
522 : 2 : }
523 : 11 : polygon->setInteriorRings( interiors );
524 : 11 : return polygon.release();
525 : 12 : }
526 : :
527 : 9 : QgsAbstractGeometry *QgsCurvePolygon::boundary() const
528 : : {
529 : 9 : if ( !mExteriorRing )
530 : 1 : return nullptr;
531 : :
532 : 8 : if ( mInteriorRings.isEmpty() )
533 : : {
534 : 7 : return mExteriorRing->clone();
535 : : }
536 : : else
537 : : {
538 : 1 : QgsMultiCurve *multiCurve = new QgsMultiCurve();
539 : 1 : int nInteriorRings = mInteriorRings.size();
540 : 1 : multiCurve->reserve( nInteriorRings + 1 );
541 : 1 : multiCurve->addGeometry( mExteriorRing->clone() );
542 : 3 : for ( int i = 0; i < nInteriorRings; ++i )
543 : : {
544 : 2 : multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
545 : 2 : }
546 : 1 : return multiCurve;
547 : : }
548 : 9 : }
549 : :
550 : 4 : QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
551 : : {
552 : 4 : if ( !mExteriorRing )
553 : 0 : return nullptr;
554 : :
555 : :
556 : 4 : std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
557 : :
558 : : // exterior ring
559 : 4 : auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
560 : :
561 : 4 : if ( !exterior )
562 : 2 : return nullptr;
563 : :
564 : 2 : polygon->mExteriorRing = std::move( exterior );
565 : :
566 : : //interior rings
567 : 5 : for ( auto interior : mInteriorRings )
568 : : {
569 : 3 : if ( !interior )
570 : 0 : continue;
571 : :
572 : 3 : QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
573 : :
574 : 3 : if ( !gridifiedInterior )
575 : 1 : continue;
576 : :
577 : 2 : polygon->mInteriorRings.append( gridifiedInterior );
578 : : }
579 : :
580 : 2 : return polygon.release();
581 : :
582 : 4 : }
583 : :
584 : 26 : bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
585 : : {
586 : 26 : bool result = false;
587 : 52 : auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
588 : : {
589 : 26 : if ( ring->numPoints() <= 4 )
590 : 8 : return false;
591 : :
592 : 18 : if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
593 : : {
594 : 4 : QgsPoint startPoint;
595 : : QgsVertexId::VertexType type;
596 : 4 : ring->pointAt( 0, startPoint, type );
597 : : // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
598 : 4 : ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
599 : 4 : return true;
600 : 4 : }
601 : :
602 : 14 : return false;
603 : 26 : };
604 : 26 : if ( mExteriorRing )
605 : : {
606 : 25 : result = cleanRing( mExteriorRing.get() );
607 : 25 : }
608 : 27 : for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
609 : : {
610 : 1 : if ( cleanRing( ring ) ) result = true;
611 : : }
612 : 26 : return result;
613 : : }
614 : :
615 : 12 : bool QgsCurvePolygon::boundingBoxIntersects( const QgsRectangle &rectangle ) const
616 : : {
617 : 12 : if ( !mExteriorRing && mInteriorRings.empty() )
618 : 1 : return false;
619 : :
620 : : // if we already have the bounding box calculated, then this check is trivial!
621 : 11 : if ( !mBoundingBox.isNull() )
622 : : {
623 : 3 : return mBoundingBox.intersects( rectangle );
624 : : }
625 : :
626 : : // loop through each ring and test the bounding box intersection.
627 : : // This gives us a chance to use optimisations which may be present on the individual
628 : : // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
629 : : // of each individual ring geometry which we would have to do anyway... (and these
630 : : // bounding boxes are cached, so would be reused without additional expense)
631 : 8 : if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( rectangle ) )
632 : 3 : return true;
633 : :
634 : 6 : for ( const QgsCurve *ring : mInteriorRings )
635 : : {
636 : 3 : if ( ring->boundingBoxIntersects( rectangle ) )
637 : 2 : return true;
638 : : }
639 : :
640 : : // even if we don't intersect the bounding box of any rings, we may still intersect the
641 : : // bounding box of the overall polygon (we are considering worst case scenario here and
642 : : // the polygon is invalid, with rings outside the exterior ring!)
643 : : // so here we fall back to the non-optimised base class check which has to first calculate
644 : : // the overall bounding box of the polygon..
645 : 3 : return QgsSurface::boundingBoxIntersects( rectangle );
646 : 12 : }
647 : :
648 : 8 : QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
649 : : {
650 : 8 : std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
651 : 8 : if ( !mExteriorRing )
652 : : {
653 : 1 : return poly.release();
654 : : }
655 : :
656 : 7 : poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
657 : :
658 : 7 : QVector<QgsCurve *> rings;
659 : 7 : rings.reserve( mInteriorRings.size() );
660 : 9 : for ( const QgsCurve *ring : mInteriorRings )
661 : : {
662 : 2 : rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
663 : : }
664 : 7 : poly->setInteriorRings( rings );
665 : 7 : return poly.release();
666 : 8 : }
667 : :
668 : 87 : void QgsCurvePolygon::setExteriorRing( QgsCurve *ring )
669 : : {
670 : 87 : if ( !ring )
671 : : {
672 : 1 : return;
673 : : }
674 : 86 : mExteriorRing.reset( ring );
675 : :
676 : : //set proper wkb type
677 : 86 : if ( QgsWkbTypes::flatType( wkbType() ) == QgsWkbTypes::Polygon )
678 : : {
679 : 0 : setZMTypeFromSubGeometry( ring, QgsWkbTypes::Polygon );
680 : 0 : }
681 : 86 : else if ( QgsWkbTypes::flatType( wkbType() ) == QgsWkbTypes::CurvePolygon )
682 : : {
683 : 86 : setZMTypeFromSubGeometry( ring, QgsWkbTypes::CurvePolygon );
684 : 86 : }
685 : :
686 : : //match dimensionality for rings
687 : 90 : for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
688 : : {
689 : 4 : if ( is3D() )
690 : 0 : ring->addZValue();
691 : : else
692 : 4 : ring->dropZValue();
693 : :
694 : 4 : if ( isMeasure() )
695 : 2 : ring->addMValue();
696 : : else
697 : 2 : ring->dropMValue();
698 : : }
699 : 86 : clearCache();
700 : 87 : }
701 : :
702 : 232 : void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
703 : : {
704 : 232 : qDeleteAll( mInteriorRings );
705 : 232 : mInteriorRings.clear();
706 : :
707 : : //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
708 : 285 : for ( QgsCurve *ring : rings )
709 : : {
710 : 53 : addInteriorRing( ring );
711 : : }
712 : 232 : clearCache();
713 : 232 : }
714 : :
715 : 188 : void QgsCurvePolygon::addInteriorRing( QgsCurve *ring )
716 : : {
717 : 188 : if ( !ring )
718 : 2 : return;
719 : :
720 : : //ensure dimensionality of ring matches curve polygon
721 : 186 : if ( !is3D() )
722 : 123 : ring->dropZValue();
723 : 63 : else if ( !ring->is3D() )
724 : 7 : ring->addZValue();
725 : :
726 : 186 : if ( !isMeasure() )
727 : 149 : ring->dropMValue();
728 : 37 : else if ( !ring->isMeasure() )
729 : 3 : ring->addMValue();
730 : :
731 : 186 : mInteriorRings.append( ring );
732 : 186 : clearCache();
733 : 188 : }
734 : :
735 : 18 : bool QgsCurvePolygon::removeInteriorRing( int nr )
736 : : {
737 : 18 : if ( nr < 0 || nr >= mInteriorRings.size() )
738 : : {
739 : 6 : return false;
740 : : }
741 : 12 : delete mInteriorRings.takeAt( nr );
742 : 12 : clearCache();
743 : 12 : return true;
744 : 18 : }
745 : :
746 : 8 : void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
747 : : {
748 : 14 : for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
749 : : {
750 : 6 : if ( minimumAllowedArea < 0 )
751 : 2 : delete mInteriorRings.takeAt( ringIndex );
752 : : else
753 : : {
754 : 4 : double area = 0.0;
755 : 4 : mInteriorRings.at( ringIndex )->sumUpArea( area );
756 : 4 : if ( area < minimumAllowedArea )
757 : 2 : delete mInteriorRings.takeAt( ringIndex );
758 : : }
759 : 6 : }
760 : :
761 : 8 : clearCache();
762 : 8 : }
763 : :
764 : 3 : void QgsCurvePolygon::removeInvalidRings()
765 : : {
766 : 3 : QVector<QgsCurve *> validRings;
767 : 3 : validRings.reserve( mInteriorRings.size() );
768 : 5 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
769 : : {
770 : 2 : if ( !curve->isRing() )
771 : : {
772 : : // remove invalid rings
773 : 1 : delete curve;
774 : 1 : }
775 : : else
776 : : {
777 : 1 : validRings << curve;
778 : : }
779 : : }
780 : 3 : mInteriorRings = validRings;
781 : 3 : }
782 : :
783 : 6 : void QgsCurvePolygon::forceRHR()
784 : : {
785 : 6 : if ( mExteriorRing && mExteriorRing->orientation() != QgsCurve::Clockwise )
786 : : {
787 : : // flip exterior ring orientation
788 : 2 : std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
789 : 2 : mExteriorRing = std::move( flipped );
790 : 2 : }
791 : :
792 : 6 : QVector<QgsCurve *> validRings;
793 : 9 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
794 : : {
795 : 3 : if ( curve && curve->orientation() != QgsCurve::CounterClockwise )
796 : : {
797 : : // flip interior ring orientation
798 : 1 : QgsCurve *flipped = curve->reversed();
799 : 1 : validRings << flipped;
800 : 1 : delete curve;
801 : 1 : }
802 : : else
803 : : {
804 : 2 : validRings << curve;
805 : : }
806 : : }
807 : 6 : mInteriorRings = validRings;
808 : 6 : }
809 : :
810 : 0 : QPainterPath QgsCurvePolygon::asQPainterPath() const
811 : : {
812 : 0 : QPainterPath p;
813 : 0 : if ( mExteriorRing )
814 : : {
815 : 0 : QPainterPath ring = mExteriorRing->asQPainterPath();
816 : 0 : ring.closeSubpath();
817 : 0 : p.addPath( ring );
818 : 0 : }
819 : :
820 : 0 : for ( const QgsCurve *ring : mInteriorRings )
821 : : {
822 : 0 : QPainterPath ringPath = ring->asQPainterPath();
823 : 0 : ringPath.closeSubpath();
824 : 0 : p.addPath( ringPath );
825 : 0 : }
826 : :
827 : 0 : return p;
828 : 0 : }
829 : :
830 : 1 : void QgsCurvePolygon::draw( QPainter &p ) const
831 : : {
832 : 1 : if ( !mExteriorRing )
833 : 1 : return;
834 : :
835 : 0 : if ( mInteriorRings.empty() )
836 : : {
837 : 0 : mExteriorRing->drawAsPolygon( p );
838 : 0 : }
839 : : else
840 : : {
841 : 0 : QPainterPath path;
842 : 0 : mExteriorRing->addToPainterPath( path );
843 : :
844 : 0 : for ( const QgsCurve *ring : mInteriorRings )
845 : : {
846 : 0 : ring->addToPainterPath( path );
847 : : }
848 : 0 : p.drawPath( path );
849 : 0 : }
850 : 1 : }
851 : :
852 : 68 : void QgsCurvePolygon::transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d, bool transformZ )
853 : : {
854 : 68 : if ( mExteriorRing )
855 : : {
856 : 68 : mExteriorRing->transform( ct, d, transformZ );
857 : 68 : }
858 : :
859 : 75 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
860 : : {
861 : 7 : curve->transform( ct, d, transformZ );
862 : : }
863 : 68 : clearCache();
864 : 68 : }
865 : :
866 : 46 : void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
867 : : {
868 : 46 : if ( mExteriorRing )
869 : : {
870 : 46 : mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
871 : 46 : }
872 : :
873 : 51 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
874 : : {
875 : 5 : curve->transform( t, zTranslate, zScale, mTranslate, mScale );
876 : : }
877 : 46 : clearCache();
878 : 46 : }
879 : :
880 : 2 : QgsCoordinateSequence QgsCurvePolygon::coordinateSequence() const
881 : : {
882 : 2 : QgsCoordinateSequence sequence;
883 : 2 : sequence.append( QgsRingSequence() );
884 : :
885 : 2 : if ( mExteriorRing )
886 : : {
887 : 2 : sequence.back().append( QgsPointSequence() );
888 : 2 : mExteriorRing->points( sequence.back().back() );
889 : 2 : }
890 : :
891 : 4 : for ( const QgsCurve *ring : mInteriorRings )
892 : : {
893 : 2 : sequence.back().append( QgsPointSequence() );
894 : 2 : ring->points( sequence.back().back() );
895 : : }
896 : :
897 : 2 : return sequence;
898 : 2 : }
899 : :
900 : 61 : int QgsCurvePolygon::nCoordinates() const
901 : : {
902 : 61 : int count = 0;
903 : :
904 : 61 : if ( mExteriorRing )
905 : : {
906 : 53 : count += mExteriorRing->nCoordinates();
907 : 53 : }
908 : :
909 : 71 : for ( const QgsCurve *ring : mInteriorRings )
910 : : {
911 : 10 : count += ring->nCoordinates();
912 : : }
913 : :
914 : 61 : return count;
915 : : }
916 : :
917 : 56 : int QgsCurvePolygon::vertexNumberFromVertexId( QgsVertexId id ) const
918 : : {
919 : 56 : if ( id.part != 0 )
920 : 4 : return -1;
921 : :
922 : 52 : if ( id.ring < 0 || id.ring >= ringCount() )
923 : 12 : return -1;
924 : :
925 : 40 : int number = 0;
926 : 40 : if ( id.ring == 0 && mExteriorRing )
927 : : {
928 : 29 : return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
929 : : }
930 : : else
931 : : {
932 : 11 : number += mExteriorRing->numPoints();
933 : : }
934 : :
935 : 11 : for ( int i = 0; i < mInteriorRings.count(); ++i )
936 : : {
937 : 11 : if ( id.ring == i + 1 )
938 : : {
939 : 11 : int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
940 : 11 : if ( partNumber == -1 )
941 : 2 : return -1;
942 : 9 : return number + partNumber;
943 : : }
944 : : else
945 : : {
946 : 0 : number += mInteriorRings.at( i )->numPoints();
947 : : }
948 : 0 : }
949 : 0 : return -1; // should not happen
950 : 56 : }
951 : :
952 : 2653 : bool QgsCurvePolygon::isEmpty() const
953 : : {
954 : 2653 : if ( !mExteriorRing )
955 : 1273 : return true;
956 : :
957 : 1380 : return mExteriorRing->isEmpty();
958 : 2653 : }
959 : :
960 : 46 : double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
961 : : {
962 : 46 : if ( !mExteriorRing )
963 : : {
964 : 2 : return -1;
965 : : }
966 : 44 : QVector<QgsCurve *> segmentList;
967 : 44 : segmentList.append( mExteriorRing.get() );
968 : 44 : segmentList.append( mInteriorRings );
969 : 44 : return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
970 : 46 : }
971 : :
972 : 536 : bool QgsCurvePolygon::nextVertex( QgsVertexId &vId, QgsPoint &vertex ) const
973 : : {
974 : 536 : if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
975 : : {
976 : 10 : return false;
977 : : }
978 : :
979 : 526 : if ( vId.ring < 0 )
980 : : {
981 : 71 : vId.ring = 0;
982 : 71 : vId.vertex = -1;
983 : 71 : if ( vId.part < 0 )
984 : : {
985 : 66 : vId.part = 0;
986 : 66 : }
987 : 71 : return mExteriorRing->nextVertex( vId, vertex );
988 : : }
989 : : else
990 : : {
991 : 455 : QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
992 : :
993 : 455 : if ( ring->nextVertex( vId, vertex ) )
994 : : {
995 : 394 : return true;
996 : : }
997 : 61 : ++vId.ring;
998 : 61 : vId.vertex = -1;
999 : 61 : if ( vId.ring >= 1 + mInteriorRings.size() )
1000 : : {
1001 : 58 : return false;
1002 : : }
1003 : 3 : ring = mInteriorRings[ vId.ring - 1 ];
1004 : 3 : return ring->nextVertex( vId, vertex );
1005 : : }
1006 : 536 : }
1007 : :
1008 : 16 : void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1009 : : {
1010 : 16 : int n = curve->numPoints();
1011 : 16 : if ( vertex.vertex < 0 || vertex.vertex >= n )
1012 : : {
1013 : 0 : previousVertex = QgsVertexId();
1014 : 0 : nextVertex = QgsVertexId();
1015 : 0 : return;
1016 : : }
1017 : :
1018 : 16 : if ( vertex.vertex == 0 && n < 3 )
1019 : : {
1020 : 0 : previousVertex = QgsVertexId();
1021 : 0 : }
1022 : 16 : else if ( vertex.vertex == 0 )
1023 : : {
1024 : 3 : previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1025 : 3 : }
1026 : : else
1027 : : {
1028 : 13 : previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1029 : : }
1030 : 16 : if ( vertex.vertex == n - 1 && n < 3 )
1031 : : {
1032 : 0 : nextVertex = QgsVertexId();
1033 : 0 : }
1034 : 16 : else if ( vertex.vertex == n - 1 )
1035 : : {
1036 : 3 : nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1037 : 3 : }
1038 : : else
1039 : : {
1040 : 13 : nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1041 : : }
1042 : 16 : }
1043 : :
1044 : 21 : void QgsCurvePolygon::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
1045 : : {
1046 : 21 : if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1047 : : {
1048 : 5 : previousVertex = QgsVertexId();
1049 : 5 : nextVertex = QgsVertexId();
1050 : 5 : return;
1051 : : }
1052 : :
1053 : 16 : if ( vertex.ring == 0 )
1054 : : {
1055 : 11 : ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1056 : 11 : }
1057 : : else
1058 : : {
1059 : 5 : ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1060 : : }
1061 : 21 : }
1062 : :
1063 : 39 : bool QgsCurvePolygon::insertVertex( QgsVertexId vId, const QgsPoint &vertex )
1064 : : {
1065 : 39 : if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1066 : : {
1067 : 12 : return false;
1068 : : }
1069 : :
1070 : 27 : QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1071 : 27 : int n = ring->numPoints();
1072 : 27 : bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1073 : 27 : if ( !success )
1074 : : {
1075 : 8 : return false;
1076 : : }
1077 : :
1078 : : // If first or last vertex is inserted, re-sync the last/first vertex
1079 : 19 : if ( vId.vertex == 0 )
1080 : 4 : ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1081 : 15 : else if ( vId.vertex == n )
1082 : 4 : ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1083 : :
1084 : 19 : clearCache();
1085 : :
1086 : 19 : return true;
1087 : 39 : }
1088 : :
1089 : 76 : bool QgsCurvePolygon::moveVertex( QgsVertexId vId, const QgsPoint &newPos )
1090 : : {
1091 : 76 : if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1092 : : {
1093 : 6 : return false;
1094 : : }
1095 : :
1096 : 70 : QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1097 : 70 : int n = ring->numPoints();
1098 : 70 : bool success = ring->moveVertex( vId, newPos );
1099 : 70 : if ( success )
1100 : : {
1101 : : // If first or last vertex is moved, also move the last/first vertex
1102 : 62 : if ( vId.vertex == 0 )
1103 : 20 : ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1104 : 42 : else if ( vId.vertex == n - 1 )
1105 : 0 : ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1106 : 62 : clearCache();
1107 : 62 : }
1108 : 70 : return success;
1109 : 76 : }
1110 : :
1111 : 47 : bool QgsCurvePolygon::deleteVertex( QgsVertexId vId )
1112 : : {
1113 : 47 : if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1114 : : {
1115 : 8 : return false;
1116 : : }
1117 : :
1118 : 39 : QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1119 : 39 : int n = ring->numPoints();
1120 : 39 : if ( n <= 4 )
1121 : : {
1122 : : //no points will be left in ring, so remove whole ring
1123 : 8 : if ( vId.ring == 0 )
1124 : : {
1125 : 6 : mExteriorRing.reset();
1126 : 6 : if ( !mInteriorRings.isEmpty() )
1127 : : {
1128 : 2 : mExteriorRing.reset( mInteriorRings.takeFirst() );
1129 : 2 : }
1130 : 6 : }
1131 : : else
1132 : : {
1133 : 2 : removeInteriorRing( vId.ring - 1 );
1134 : : }
1135 : 8 : clearCache();
1136 : 8 : return true;
1137 : : }
1138 : :
1139 : 31 : bool success = ring->deleteVertex( vId );
1140 : 31 : if ( success )
1141 : : {
1142 : : // If first or last vertex is removed, re-sync the last/first vertex
1143 : : // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1144 : : // may have been deleted (e.g. with CircularString)
1145 : 23 : if ( vId.vertex == 0 )
1146 : 10 : ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1147 : 13 : else if ( vId.vertex == n - 1 )
1148 : 4 : ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1149 : 23 : clearCache();
1150 : 23 : }
1151 : 31 : return success;
1152 : 47 : }
1153 : :
1154 : 15 : bool QgsCurvePolygon::hasCurvedSegments() const
1155 : : {
1156 : 15 : if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1157 : : {
1158 : 2 : return true;
1159 : : }
1160 : :
1161 : 13 : for ( const QgsCurve *ring : mInteriorRings )
1162 : : {
1163 : 1 : if ( ring->hasCurvedSegments() )
1164 : : {
1165 : 1 : return true;
1166 : : }
1167 : : }
1168 : 12 : return false;
1169 : 15 : }
1170 : :
1171 : 2 : QgsAbstractGeometry *QgsCurvePolygon::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
1172 : : {
1173 : 2 : return toPolygon( tolerance, toleranceType );
1174 : : }
1175 : :
1176 : 62 : double QgsCurvePolygon::vertexAngle( QgsVertexId vertex ) const
1177 : : {
1178 : 62 : if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1179 : : {
1180 : : //makes no sense - conversion of false to double!
1181 : 6 : return false;
1182 : : }
1183 : :
1184 : 56 : QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1185 : 56 : return ring->vertexAngle( vertex );
1186 : 62 : }
1187 : :
1188 : 482 : int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1189 : : {
1190 : 482 : return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1191 : : }
1192 : :
1193 : 692 : int QgsCurvePolygon::ringCount( int ) const
1194 : : {
1195 : 692 : return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1196 : : }
1197 : :
1198 : 219 : int QgsCurvePolygon::partCount() const
1199 : : {
1200 : 219 : return ringCount() > 0 ? 1 : 0;
1201 : : }
1202 : :
1203 : 5715 : QgsPoint QgsCurvePolygon::vertexAt( QgsVertexId id ) const
1204 : : {
1205 : 5715 : return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1206 : : }
1207 : :
1208 : 57 : double QgsCurvePolygon::segmentLength( QgsVertexId startVertex ) const
1209 : : {
1210 : 57 : if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1211 : : {
1212 : 12 : return 0.0;
1213 : : }
1214 : :
1215 : 45 : const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1216 : 45 : return ring->segmentLength( startVertex );
1217 : 57 : }
1218 : :
1219 : 31 : bool QgsCurvePolygon::addZValue( double zValue )
1220 : : {
1221 : 31 : if ( QgsWkbTypes::hasZ( mWkbType ) )
1222 : 23 : return false;
1223 : :
1224 : 8 : mWkbType = QgsWkbTypes::addZ( mWkbType );
1225 : :
1226 : 8 : if ( mExteriorRing )
1227 : 8 : mExteriorRing->addZValue( zValue );
1228 : 8 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1229 : : {
1230 : 0 : curve->addZValue( zValue );
1231 : : }
1232 : 8 : clearCache();
1233 : 8 : return true;
1234 : 31 : }
1235 : :
1236 : 28 : bool QgsCurvePolygon::addMValue( double mValue )
1237 : : {
1238 : 28 : if ( QgsWkbTypes::hasM( mWkbType ) )
1239 : 19 : return false;
1240 : :
1241 : 9 : mWkbType = QgsWkbTypes::addM( mWkbType );
1242 : :
1243 : 9 : if ( mExteriorRing )
1244 : 9 : mExteriorRing->addMValue( mValue );
1245 : 9 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1246 : : {
1247 : 0 : curve->addMValue( mValue );
1248 : : }
1249 : 9 : clearCache();
1250 : 9 : return true;
1251 : 28 : }
1252 : :
1253 : 13 : bool QgsCurvePolygon::dropZValue()
1254 : : {
1255 : 13 : if ( !is3D() )
1256 : 4 : return false;
1257 : :
1258 : 9 : mWkbType = QgsWkbTypes::dropZ( mWkbType );
1259 : 9 : if ( mExteriorRing )
1260 : 9 : mExteriorRing->dropZValue();
1261 : 13 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1262 : : {
1263 : 4 : curve->dropZValue();
1264 : : }
1265 : 9 : clearCache();
1266 : 9 : return true;
1267 : 13 : }
1268 : :
1269 : 13 : bool QgsCurvePolygon::dropMValue()
1270 : : {
1271 : 13 : if ( !isMeasure() )
1272 : 4 : return false;
1273 : :
1274 : 9 : mWkbType = QgsWkbTypes::dropM( mWkbType );
1275 : 9 : if ( mExteriorRing )
1276 : 9 : mExteriorRing->dropMValue();
1277 : 13 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1278 : : {
1279 : 4 : curve->dropMValue();
1280 : : }
1281 : 9 : clearCache();
1282 : 9 : return true;
1283 : 13 : }
1284 : :
1285 : 3 : void QgsCurvePolygon::swapXy()
1286 : : {
1287 : 3 : if ( mExteriorRing )
1288 : 2 : mExteriorRing->swapXy();
1289 : 4 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1290 : : {
1291 : 1 : curve->swapXy();
1292 : : }
1293 : 3 : clearCache();
1294 : 3 : }
1295 : :
1296 : 1 : QgsCurvePolygon *QgsCurvePolygon::toCurveType() const
1297 : : {
1298 : 1 : return clone();
1299 : : }
1300 : :
1301 : 4 : bool QgsCurvePolygon::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
1302 : : {
1303 : 4 : if ( !transformer )
1304 : 0 : return false;
1305 : :
1306 : 4 : bool res = true;
1307 : 4 : if ( mExteriorRing )
1308 : 3 : res = mExteriorRing->transform( transformer, feedback );
1309 : :
1310 : 4 : if ( !res || ( feedback && feedback->isCanceled() ) )
1311 : : {
1312 : 1 : clearCache();
1313 : 1 : return false;
1314 : : }
1315 : :
1316 : 5 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1317 : : {
1318 : 2 : res = curve->transform( transformer );
1319 : :
1320 : 2 : if ( feedback && feedback->isCanceled() )
1321 : 0 : res = false;
1322 : :
1323 : 2 : if ( !res )
1324 : 0 : break;
1325 : : }
1326 : 3 : clearCache();
1327 : 3 : return res;
1328 : 4 : }
1329 : :
1330 : 3 : void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1331 : : {
1332 : 3 : if ( mExteriorRing )
1333 : 2 : mExteriorRing->filterVertices( filter );
1334 : :
1335 : 5 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1336 : : {
1337 : 2 : curve->filterVertices( filter );
1338 : : }
1339 : 3 : clearCache();
1340 : 3 : }
1341 : :
1342 : 3 : void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1343 : : {
1344 : 3 : if ( mExteriorRing )
1345 : 2 : mExteriorRing->transformVertices( transform );
1346 : :
1347 : 5 : for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1348 : : {
1349 : 2 : curve->transformVertices( transform );
1350 : : }
1351 : 3 : clearCache();
1352 : 3 : }
1353 : :
1354 : 66 : int QgsCurvePolygon::childCount() const
1355 : : {
1356 : 66 : return 1 + mInteriorRings.count();
1357 : : }
1358 : :
1359 : 36 : QgsAbstractGeometry *QgsCurvePolygon::childGeometry( int index ) const
1360 : : {
1361 : 36 : if ( index == 0 )
1362 : 32 : return mExteriorRing.get();
1363 : : else
1364 : 4 : return mInteriorRings.at( index - 1 );
1365 : 36 : }
|