Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsgeometrycollection.cpp
3 : : -------------------------------------------------------------------
4 : : Date : 28 Oct 2014
5 : : Copyright : (C) 2014 by Marco Hugentobler
6 : : email : marco.hugentobler at sourcepole dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : :
16 : : #include "qgsgeometrycollection.h"
17 : : #include "qgsapplication.h"
18 : : #include "qgsgeometryfactory.h"
19 : : #include "qgsgeometryutils.h"
20 : : #include "qgscircularstring.h"
21 : : #include "qgscompoundcurve.h"
22 : : #include "qgslinestring.h"
23 : : #include "qgsmultilinestring.h"
24 : : #include "qgspoint.h"
25 : : #include "qgsmultipoint.h"
26 : : #include "qgspolygon.h"
27 : : #include "qgsmultipolygon.h"
28 : : #include "qgswkbptr.h"
29 : : #include "qgsgeos.h"
30 : : #include "qgsfeedback.h"
31 : :
32 : : #include <nlohmann/json.hpp>
33 : : #include <memory>
34 : :
35 : 4814 : QgsGeometryCollection::QgsGeometryCollection()
36 : 9628 : {
37 : 4814 : mWkbType = QgsWkbTypes::GeometryCollection;
38 : 4814 : }
39 : :
40 : 94 : QgsGeometryCollection::QgsGeometryCollection( const QgsGeometryCollection &c ):
41 : 94 : QgsAbstractGeometry( c ),
42 : 94 : mBoundingBox( c.mBoundingBox ),
43 : 94 : mHasCachedValidity( c.mHasCachedValidity ),
44 : 94 : mValidityFailureReason( c.mValidityFailureReason )
45 : 188 : {
46 : 94 : int nGeoms = c.mGeometries.size();
47 : 94 : mGeometries.resize( nGeoms );
48 : 191 : for ( int i = 0; i < nGeoms; ++i )
49 : : {
50 : 97 : mGeometries[i] = c.mGeometries.at( i )->clone();
51 : 97 : }
52 : 94 : }
53 : :
54 : 12 : QgsGeometryCollection &QgsGeometryCollection::operator=( const QgsGeometryCollection &c )
55 : : {
56 : 12 : if ( &c != this )
57 : : {
58 : 12 : clearCache();
59 : 12 : QgsAbstractGeometry::operator=( c );
60 : 12 : int nGeoms = c.mGeometries.size();
61 : 12 : mGeometries.resize( nGeoms );
62 : 24 : for ( int i = 0; i < nGeoms; ++i )
63 : : {
64 : 12 : mGeometries[i] = c.mGeometries.at( i )->clone();
65 : 12 : }
66 : 12 : }
67 : 12 : return *this;
68 : : }
69 : :
70 : 2610 : QgsGeometryCollection::~QgsGeometryCollection()
71 : 2610 : {
72 : 2381 : clear();
73 : 2610 : }
74 : :
75 : 8 : bool QgsGeometryCollection::operator==( const QgsAbstractGeometry &other ) const
76 : : {
77 : 8 : const QgsGeometryCollection *otherCollection = qgsgeometry_cast< const QgsGeometryCollection * >( &other );
78 : 8 : if ( !otherCollection )
79 : 2 : return false;
80 : :
81 : 6 : if ( mWkbType != otherCollection->mWkbType )
82 : 2 : return false;
83 : :
84 : 4 : if ( mGeometries.count() != otherCollection->mGeometries.count() )
85 : 2 : return false;
86 : :
87 : 3 : for ( int i = 0; i < mGeometries.count(); ++i )
88 : : {
89 : 2 : QgsAbstractGeometry *g1 = mGeometries.at( i );
90 : 2 : QgsAbstractGeometry *g2 = otherCollection->mGeometries.at( i );
91 : :
92 : : // Quick check if the geometries are exactly the same
93 : 2 : if ( g1 != g2 )
94 : : {
95 : 2 : if ( !g1 || !g2 )
96 : 0 : return false;
97 : :
98 : : // Slower check, compare the contents of the geometries
99 : 2 : if ( *g1 != *g2 )
100 : 1 : return false;
101 : 1 : }
102 : 1 : }
103 : :
104 : 1 : return true;
105 : 8 : }
106 : :
107 : 5 : bool QgsGeometryCollection::operator!=( const QgsAbstractGeometry &other ) const
108 : : {
109 : 5 : return !operator==( other );
110 : : }
111 : :
112 : 1 : QgsGeometryCollection *QgsGeometryCollection::createEmptyWithSameType() const
113 : : {
114 : 1 : auto result = std::make_unique< QgsGeometryCollection >();
115 : 1 : result->mWkbType = mWkbType;
116 : 1 : return result.release();
117 : 1 : }
118 : :
119 : 3 : QgsGeometryCollection *QgsGeometryCollection::clone() const
120 : : {
121 : 3 : return new QgsGeometryCollection( *this );
122 : 0 : }
123 : :
124 : 4590 : void QgsGeometryCollection::clear()
125 : : {
126 : 4590 : qDeleteAll( mGeometries );
127 : 4590 : mGeometries.clear();
128 : 4590 : clearCache(); //set bounding box invalid
129 : 4590 : }
130 : :
131 : 1 : QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
132 : : {
133 : 1 : std::unique_ptr<QgsGeometryCollection> result;
134 : :
135 : 5 : for ( auto geom : mGeometries )
136 : : {
137 : 4 : std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) };
138 : 4 : if ( gridified )
139 : : {
140 : 2 : if ( !result )
141 : 1 : result = std::unique_ptr<QgsGeometryCollection> { createEmptyWithSameType() };
142 : :
143 : 2 : result->mGeometries.append( gridified.release() );
144 : 2 : }
145 : 4 : }
146 : :
147 : 1 : return result.release();
148 : 1 : }
149 : :
150 : 9 : bool QgsGeometryCollection::removeDuplicateNodes( double epsilon, bool useZValues )
151 : : {
152 : 9 : bool result = false;
153 : 21 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
154 : : {
155 : 12 : if ( geom->removeDuplicateNodes( epsilon, useZValues ) ) result = true;
156 : : }
157 : 9 : return result;
158 : : }
159 : :
160 : 2 : QgsAbstractGeometry *QgsGeometryCollection::boundary() const
161 : : {
162 : 2 : return nullptr;
163 : : }
164 : :
165 : 10 : void QgsGeometryCollection::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
166 : : {
167 : 10 : if ( vertex.part < 0 || vertex.part >= mGeometries.count() )
168 : : {
169 : 5 : previousVertex = QgsVertexId();
170 : 5 : nextVertex = QgsVertexId();
171 : 5 : return;
172 : : }
173 : :
174 : 5 : mGeometries.at( vertex.part )->adjacentVertices( vertex, previousVertex, nextVertex );
175 : 10 : }
176 : :
177 : 58 : int QgsGeometryCollection::vertexNumberFromVertexId( QgsVertexId id ) const
178 : : {
179 : 58 : if ( id.part < 0 || id.part >= mGeometries.count() )
180 : 9 : return -1;
181 : :
182 : 49 : int number = 0;
183 : 49 : int part = 0;
184 : 122 : for ( QgsAbstractGeometry *geometry : mGeometries )
185 : : {
186 : 122 : if ( part == id.part )
187 : : {
188 : 49 : int partNumber = geometry->vertexNumberFromVertexId( QgsVertexId( 0, id.ring, id.vertex ) );
189 : 49 : if ( partNumber == -1 )
190 : 18 : return -1;
191 : 31 : return number + partNumber;
192 : : }
193 : : else
194 : : {
195 : 73 : number += geometry->nCoordinates();
196 : : }
197 : :
198 : 73 : part++;
199 : : }
200 : 0 : return -1; // should not happen
201 : 58 : }
202 : :
203 : 0 : bool QgsGeometryCollection::boundingBoxIntersects( const QgsRectangle &rectangle ) const
204 : : {
205 : 0 : if ( mGeometries.empty() )
206 : 0 : return false;
207 : :
208 : : // if we already have the bounding box calculated, then this check is trivial!
209 : 0 : if ( !mBoundingBox.isNull() )
210 : : {
211 : 0 : return mBoundingBox.intersects( rectangle );
212 : : }
213 : :
214 : : // otherwise loop through each member geometry and test the bounding box intersection.
215 : : // This gives us a chance to use optimisations which may be present on the individual
216 : : // geometry subclasses, and at worst it will cause a calculation of the bounding box
217 : : // of each individual member geometry which we would have to do anyway... (and these
218 : : // bounding boxes are cached, so would be reused without additional expense)
219 : 0 : for ( const QgsAbstractGeometry *geometry : mGeometries )
220 : : {
221 : 0 : if ( geometry->boundingBoxIntersects( rectangle ) )
222 : 0 : return true;
223 : : }
224 : :
225 : : // even if we don't intersect the bounding box of any member geometries, we may still intersect the
226 : : // bounding box of the overall collection.
227 : : // so here we fall back to the non-optimised base class check which has to first calculate
228 : : // the overall bounding box of the collection..
229 : 0 : return QgsAbstractGeometry::boundingBoxIntersects( rectangle );
230 : 0 : }
231 : :
232 : 123 : void QgsGeometryCollection::reserve( int size )
233 : : {
234 : 123 : mGeometries.reserve( size );
235 : 123 : }
236 : :
237 : 689 : QgsAbstractGeometry *QgsGeometryCollection::geometryN( int n )
238 : : {
239 : 689 : clearCache();
240 : 689 : return mGeometries.value( n );
241 : : }
242 : :
243 : 2370 : bool QgsGeometryCollection::isEmpty() const
244 : : {
245 : 2370 : if ( mGeometries.isEmpty() )
246 : 2099 : return true;
247 : :
248 : 271 : for ( QgsAbstractGeometry *geometry : mGeometries )
249 : : {
250 : 271 : if ( !geometry->isEmpty() )
251 : 271 : return false;
252 : : }
253 : 0 : return true;
254 : 2370 : }
255 : :
256 : 1607 : bool QgsGeometryCollection::addGeometry( QgsAbstractGeometry *g )
257 : : {
258 : 1607 : if ( !g )
259 : : {
260 : 1 : return false;
261 : : }
262 : :
263 : 1606 : mGeometries.append( g );
264 : 1606 : clearCache(); //set bounding box invalid
265 : 1606 : return true;
266 : 1607 : }
267 : :
268 : 11 : bool QgsGeometryCollection::insertGeometry( QgsAbstractGeometry *g, int index )
269 : : {
270 : 11 : if ( !g )
271 : : {
272 : 3 : return false;
273 : : }
274 : :
275 : 8 : index = std::min( static_cast<int>( mGeometries.count() ), index );
276 : :
277 : 8 : mGeometries.insert( index, g );
278 : 8 : clearCache(); //set bounding box invalid
279 : 8 : return true;
280 : 11 : }
281 : :
282 : 25 : bool QgsGeometryCollection::removeGeometry( int nr )
283 : : {
284 : 25 : if ( nr >= mGeometries.size() || nr < 0 )
285 : : {
286 : 5 : return false;
287 : : }
288 : 20 : delete mGeometries.at( nr );
289 : 20 : mGeometries.remove( nr );
290 : 20 : clearCache(); //set bounding box invalid
291 : 20 : return true;
292 : 25 : }
293 : :
294 : 12 : int QgsGeometryCollection::dimension() const
295 : : {
296 : 12 : int maxDim = 0;
297 : 12 : QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
298 : 18 : for ( ; it != mGeometries.constEnd(); ++it )
299 : : {
300 : 6 : int dim = ( *it )->dimension();
301 : 6 : if ( dim > maxDim )
302 : : {
303 : 5 : maxDim = dim;
304 : 5 : }
305 : 6 : }
306 : 12 : return maxDim;
307 : : }
308 : :
309 : 426 : QString QgsGeometryCollection::geometryType() const
310 : : {
311 : 852 : return QStringLiteral( "GeometryCollection" );
312 : : }
313 : :
314 : 47 : void QgsGeometryCollection::transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d, bool transformZ )
315 : : {
316 : 97 : for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
317 : : {
318 : 50 : g->transform( ct, d, transformZ );
319 : : }
320 : 47 : clearCache(); //set bounding box invalid
321 : 47 : }
322 : :
323 : 1 : void QgsGeometryCollection::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
324 : : {
325 : 3 : for ( QgsAbstractGeometry *g : std::as_const( mGeometries ) )
326 : : {
327 : 2 : g->transform( t, zTranslate, zScale, mTranslate, mScale );
328 : : }
329 : 1 : clearCache(); //set bounding box invalid
330 : 1 : }
331 : :
332 : 0 : void QgsGeometryCollection::draw( QPainter &p ) const
333 : : {
334 : 0 : QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
335 : 0 : for ( ; it != mGeometries.constEnd(); ++it )
336 : : {
337 : 0 : ( *it )->draw( p );
338 : 0 : }
339 : 0 : }
340 : :
341 : 0 : QPainterPath QgsGeometryCollection::asQPainterPath() const
342 : : {
343 : 0 : QPainterPath p;
344 : 0 : for ( const QgsAbstractGeometry *geom : mGeometries )
345 : : {
346 : 0 : QPainterPath partPath = geom->asQPainterPath();
347 : 0 : if ( !partPath.isEmpty() )
348 : 0 : p.addPath( partPath );
349 : 4814 : }
350 : 0 : return p;
351 : 0 : }
352 : :
353 : 36 : bool QgsGeometryCollection::fromWkb( QgsConstWkbPtr &wkbPtr )
354 : : {
355 : 36 : if ( !wkbPtr )
356 : : {
357 : 6 : return false;
358 : : }
359 : :
360 : 30 : QgsWkbTypes::Type wkbType = wkbPtr.readHeader();
361 : 30 : if ( QgsWkbTypes::flatType( wkbType ) != QgsWkbTypes::flatType( mWkbType ) )
362 : 6 : return false;
363 : :
364 : 24 : mWkbType = wkbType;
365 : :
366 : 24 : int nGeometries = 0;
367 : 24 : wkbPtr >> nGeometries;
368 : :
369 : 24 : QVector<QgsAbstractGeometry *> geometryListBackup = mGeometries;
370 : 24 : mGeometries.clear();
371 : 24 : mGeometries.reserve( nGeometries );
372 : 72 : for ( int i = 0; i < nGeometries; ++i )
373 : : {
374 : 48 : std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkb( wkbPtr ) ); // also updates wkbPtr
375 : 48 : if ( geom )
376 : : {
377 : 48 : if ( !addGeometry( geom.release() ) )
378 : : {
379 : 0 : qDeleteAll( mGeometries );
380 : 0 : mGeometries = geometryListBackup;
381 : 0 : return false;
382 : : }
383 : 48 : }
384 : 48 : }
385 : 24 : qDeleteAll( geometryListBackup );
386 : :
387 : 24 : clearCache(); //set bounding box invalid
388 : :
389 : 24 : return true;
390 : 36 : }
391 : :
392 : 416 : bool QgsGeometryCollection::fromWkt( const QString &wkt )
393 : : {
394 : 832 : return fromCollectionWkt( wkt, QVector<QgsAbstractGeometry *>() << new QgsPoint << new QgsLineString << new QgsPolygon
395 : 416 : << new QgsCircularString << new QgsCompoundCurve
396 : 416 : << new QgsCurvePolygon
397 : 416 : << new QgsMultiPoint << new QgsMultiLineString
398 : 416 : << new QgsMultiPolygon << new QgsGeometryCollection
399 : 832 : << new QgsMultiCurve << new QgsMultiSurface, QStringLiteral( "GeometryCollection" ) );
400 : 0 : }
401 : :
402 : 39 : int QgsGeometryCollection::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
403 : : {
404 : 39 : int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
405 : 108 : for ( const QgsAbstractGeometry *geom : mGeometries )
406 : : {
407 : 69 : if ( geom )
408 : : {
409 : 69 : binarySize += geom->wkbSize( flags );
410 : 69 : }
411 : : }
412 : :
413 : 39 : return binarySize;
414 : : }
415 : :
416 : 37 : QByteArray QgsGeometryCollection::asWkb( WkbFlags flags ) const
417 : : {
418 : 37 : int countNonNull = 0;
419 : 102 : for ( const QgsAbstractGeometry *geom : mGeometries )
420 : : {
421 : 65 : if ( geom )
422 : : {
423 : 65 : countNonNull ++;
424 : 65 : }
425 : : }
426 : :
427 : 37 : QByteArray wkbArray;
428 : 37 : wkbArray.resize( QgsGeometryCollection::wkbSize( flags ) );
429 : 37 : QgsWkbPtr wkb( wkbArray );
430 : 37 : wkb << static_cast<char>( QgsApplication::endian() );
431 : 37 : wkb << static_cast<quint32>( wkbType() );
432 : 37 : wkb << static_cast<quint32>( countNonNull );
433 : 102 : for ( const QgsAbstractGeometry *geom : mGeometries )
434 : : {
435 : 65 : if ( geom )
436 : : {
437 : 65 : wkb << geom->asWkb( flags );
438 : 65 : }
439 : : }
440 : 37 : return wkbArray;
441 : 37 : }
442 : :
443 : 2059 : QString QgsGeometryCollection::asWkt( int precision ) const
444 : : {
445 : 2059 : QString wkt = wktTypeStr();
446 : :
447 : 2059 : if ( isEmpty() )
448 : 2014 : wkt += QLatin1String( " EMPTY" );
449 : : else
450 : : {
451 : 45 : wkt += QLatin1String( " (" );
452 : 120 : for ( const QgsAbstractGeometry *geom : mGeometries )
453 : : {
454 : 75 : QString childWkt = geom->asWkt( precision );
455 : 75 : if ( wktOmitChildType() )
456 : : {
457 : 40 : childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
458 : 40 : }
459 : 75 : wkt += childWkt + ',';
460 : 75 : }
461 : 45 : if ( wkt.endsWith( ',' ) )
462 : : {
463 : 45 : wkt.chop( 1 ); // Remove last ','
464 : 45 : }
465 : 45 : wkt += ')';
466 : : }
467 : 2059 : return wkt;
468 : 2059 : }
469 : :
470 : 4 : QDomElement QgsGeometryCollection::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
471 : : {
472 : 8 : QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
473 : 9 : for ( const QgsAbstractGeometry *geom : mGeometries )
474 : : {
475 : 10 : QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
476 : 5 : elemGeometryMember.appendChild( geom->asGml2( doc, precision, ns, axisOrder ) );
477 : 5 : elemMultiGeometry.appendChild( elemGeometryMember );
478 : 5 : }
479 : 4 : return elemMultiGeometry;
480 : 4 : }
481 : :
482 : 4 : QDomElement QgsGeometryCollection::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
483 : : {
484 : 8 : QDomElement elemMultiGeometry = doc.createElementNS( ns, QStringLiteral( "MultiGeometry" ) );
485 : 9 : for ( const QgsAbstractGeometry *geom : mGeometries )
486 : : {
487 : 10 : QDomElement elemGeometryMember = doc.createElementNS( ns, QStringLiteral( "geometryMember" ) );
488 : 5 : elemGeometryMember.appendChild( geom->asGml3( doc, precision, ns, axisOrder ) );
489 : 5 : elemMultiGeometry.appendChild( elemGeometryMember );
490 : 5 : }
491 : 4 : return elemMultiGeometry;
492 : 4 : }
493 : :
494 : 4 : json QgsGeometryCollection::asJsonObject( int precision ) const
495 : : {
496 : 4 : json coordinates( json::array( ) );
497 : 9 : for ( const QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
498 : : {
499 : 5 : coordinates.push_back( geom->asJsonObject( precision ) );
500 : : }
501 : 16 : return
502 : 12 : {
503 : 4 : { "type", "GeometryCollection" },
504 : 4 : { "geometries", coordinates }
505 : : };
506 : 4 : }
507 : :
508 : 10 : QString QgsGeometryCollection::asKml( int precision ) const
509 : : {
510 : 10 : QString kml;
511 : 10 : kml.append( QLatin1String( "<MultiGeometry>" ) );
512 : 10 : const QVector< QgsAbstractGeometry * > &geometries = mGeometries;
513 : 29 : for ( const QgsAbstractGeometry *geometry : geometries )
514 : : {
515 : 19 : kml.append( geometry->asKml( precision ) );
516 : : }
517 : 10 : kml.append( QLatin1String( "</MultiGeometry>" ) );
518 : 10 : return kml;
519 : 10 : }
520 : :
521 : 1194 : QgsRectangle QgsGeometryCollection::boundingBox() const
522 : : {
523 : 1194 : if ( mBoundingBox.isNull() )
524 : : {
525 : 995 : mBoundingBox = calculateBoundingBox();
526 : 995 : }
527 : 1194 : return mBoundingBox;
528 : : }
529 : :
530 : 995 : QgsRectangle QgsGeometryCollection::calculateBoundingBox() const
531 : : {
532 : 995 : if ( mGeometries.empty() )
533 : : {
534 : 1 : return QgsRectangle();
535 : : }
536 : :
537 : 994 : QgsRectangle bbox = mGeometries.at( 0 )->boundingBox();
538 : 1055 : for ( int i = 1; i < mGeometries.size(); ++i )
539 : : {
540 : 61 : if ( mGeometries.at( i )->isEmpty() )
541 : 0 : continue;
542 : :
543 : 61 : QgsRectangle geomBox = mGeometries.at( i )->boundingBox();
544 : 61 : if ( bbox.isNull() )
545 : : {
546 : : // workaround treatment of a QgsRectangle(0,0,0,0) as a "null"/invalid rectangle
547 : : // if bbox is null, then the first geometry must have returned a bounding box of (0,0,0,0)
548 : : // so just manually include that as a point... ew.
549 : 3 : geomBox.combineExtentWith( QPointF( 0, 0 ) );
550 : 3 : bbox = geomBox;
551 : 3 : }
552 : 58 : else if ( geomBox.isNull() )
553 : : {
554 : : // ...as above... this part must have a bounding box of (0,0,0,0).
555 : : // if we try to combine the extent with this "null" box it will just be ignored.
556 : 1 : bbox.combineExtentWith( QPointF( 0, 0 ) );
557 : 1 : }
558 : : else
559 : : {
560 : 57 : bbox.combineExtentWith( geomBox );
561 : : }
562 : 61 : }
563 : 994 : return bbox;
564 : 995 : }
565 : :
566 : 7064 : void QgsGeometryCollection::clearCache() const
567 : : {
568 : 7064 : mBoundingBox = QgsRectangle();
569 : 7064 : mHasCachedValidity = false;
570 : 7064 : mValidityFailureReason.clear();
571 : 7064 : QgsAbstractGeometry::clearCache();
572 : 7064 : }
573 : :
574 : 2 : QgsCoordinateSequence QgsGeometryCollection::coordinateSequence() const
575 : : {
576 : 2 : QgsCoordinateSequence sequence;
577 : 2 : QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
578 : 6 : for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
579 : : {
580 : 4 : QgsCoordinateSequence geomCoords = ( *geomIt )->coordinateSequence();
581 : :
582 : 4 : QgsCoordinateSequence::const_iterator cIt = geomCoords.constBegin();
583 : 8 : for ( ; cIt != geomCoords.constEnd(); ++cIt )
584 : : {
585 : 4 : sequence.push_back( *cIt );
586 : 4 : }
587 : 4 : }
588 : :
589 : 2 : return sequence;
590 : 2 : }
591 : :
592 : 40 : int QgsGeometryCollection::nCoordinates() const
593 : : {
594 : 40 : int count = 0;
595 : :
596 : 40 : QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
597 : 78 : for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
598 : : {
599 : 38 : count += ( *geomIt )->nCoordinates();
600 : 38 : }
601 : :
602 : 40 : return count;
603 : : }
604 : :
605 : 24 : double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
606 : : {
607 : 24 : return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
608 : : }
609 : :
610 : 39 : bool QgsGeometryCollection::nextVertex( QgsVertexId &id, QgsPoint &vertex ) const
611 : : {
612 : 39 : if ( id.part < 0 )
613 : : {
614 : 3 : id.part = 0;
615 : 3 : id.ring = -1;
616 : 3 : id.vertex = -1;
617 : 3 : }
618 : 39 : if ( mGeometries.isEmpty() )
619 : : {
620 : 3 : return false;
621 : : }
622 : :
623 : 36 : if ( id.part >= mGeometries.count() )
624 : 2 : return false;
625 : :
626 : 34 : QgsAbstractGeometry *geom = mGeometries.at( id.part );
627 : 34 : if ( geom->nextVertex( id, vertex ) )
628 : : {
629 : 30 : return true;
630 : : }
631 : 4 : if ( ( id.part + 1 ) >= numGeometries() )
632 : : {
633 : 2 : return false;
634 : : }
635 : 2 : ++id.part;
636 : 2 : id.ring = -1;
637 : 2 : id.vertex = -1;
638 : 2 : return mGeometries.at( id.part )->nextVertex( id, vertex );
639 : 39 : }
640 : :
641 : 16 : bool QgsGeometryCollection::insertVertex( QgsVertexId position, const QgsPoint &vertex )
642 : : {
643 : 16 : if ( position.part >= mGeometries.size() )
644 : : {
645 : 6 : return false;
646 : : }
647 : :
648 : 10 : bool success = mGeometries.at( position.part )->insertVertex( position, vertex );
649 : 10 : if ( success )
650 : : {
651 : 6 : clearCache(); //set bounding box invalid
652 : 6 : }
653 : 10 : return success;
654 : 16 : }
655 : :
656 : 18 : bool QgsGeometryCollection::moveVertex( QgsVertexId position, const QgsPoint &newPos )
657 : : {
658 : 18 : if ( position.part < 0 || position.part >= mGeometries.size() )
659 : : {
660 : 5 : return false;
661 : : }
662 : :
663 : 13 : bool success = mGeometries.at( position.part )->moveVertex( position, newPos );
664 : 13 : if ( success )
665 : : {
666 : 9 : clearCache(); //set bounding box invalid
667 : 9 : }
668 : 13 : return success;
669 : 18 : }
670 : :
671 : 31 : bool QgsGeometryCollection::deleteVertex( QgsVertexId position )
672 : : {
673 : 31 : if ( position.part < 0 || position.part >= mGeometries.size() )
674 : : {
675 : 6 : return false;
676 : : }
677 : :
678 : 25 : QgsAbstractGeometry *geom = mGeometries.at( position.part );
679 : 25 : if ( !geom )
680 : : {
681 : 0 : return false;
682 : : }
683 : :
684 : 25 : bool success = geom->deleteVertex( position );
685 : :
686 : : //remove geometry if no vertices left
687 : 25 : if ( geom->isEmpty() )
688 : : {
689 : 3 : removeGeometry( position.part );
690 : 3 : }
691 : :
692 : 25 : if ( success )
693 : : {
694 : 21 : clearCache(); //set bounding box invalid
695 : 21 : }
696 : 25 : return success;
697 : 31 : }
698 : :
699 : 0 : double QgsGeometryCollection::length() const
700 : : {
701 : 0 : double length = 0.0;
702 : 0 : QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
703 : 0 : for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
704 : : {
705 : 0 : length += ( *geomIt )->length();
706 : 0 : }
707 : 0 : return length;
708 : : }
709 : :
710 : 10 : double QgsGeometryCollection::area() const
711 : : {
712 : 10 : double area = 0.0;
713 : 10 : QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
714 : 14 : for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
715 : : {
716 : 4 : area += ( *geomIt )->area();
717 : 4 : }
718 : 10 : return area;
719 : : }
720 : :
721 : 10 : double QgsGeometryCollection::perimeter() const
722 : : {
723 : 10 : double perimeter = 0.0;
724 : 10 : QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
725 : 14 : for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
726 : : {
727 : 4 : perimeter += ( *geomIt )->perimeter();
728 : 4 : }
729 : 10 : return perimeter;
730 : : }
731 : :
732 : 2109 : bool QgsGeometryCollection::fromCollectionWkt( const QString &wkt, const QVector<QgsAbstractGeometry *> &subtypes, const QString &defaultChildWkbType )
733 : : {
734 : 2109 : clear();
735 : :
736 : 2109 : QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
737 : :
738 : 2109 : if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::flatType( wkbType() ) )
739 : : {
740 : 6 : qDeleteAll( subtypes );
741 : 6 : return false;
742 : : }
743 : 2103 : mWkbType = parts.first;
744 : :
745 : 2103 : QString secondWithoutParentheses = parts.second;
746 : 2103 : secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
747 : 2199 : if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
748 : 96 : secondWithoutParentheses.isEmpty() )
749 : 2026 : return true;
750 : :
751 : 173 : QString defChildWkbType = QStringLiteral( "%1%2%3 " ).arg( defaultChildWkbType, is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
752 : :
753 : 77 : const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defChildWkbType );
754 : 210 : for ( const QString &childWkt : blocks )
755 : : {
756 : 141 : QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
757 : :
758 : 141 : bool success = false;
759 : 207 : for ( const QgsAbstractGeometry *geom : subtypes )
760 : : {
761 : 199 : if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::flatType( geom->wkbType() ) )
762 : : {
763 : 141 : mGeometries.append( geom->clone() );
764 : 141 : if ( mGeometries.back()->fromWkt( childWkt ) )
765 : : {
766 : 133 : success = true;
767 : 133 : break;
768 : : }
769 : 8 : }
770 : : }
771 : 141 : if ( !success )
772 : : {
773 : 8 : clear();
774 : 8 : qDeleteAll( subtypes );
775 : 8 : return false;
776 : : }
777 : 141 : }
778 : 69 : qDeleteAll( subtypes );
779 : :
780 : : //scan through geometries and check if dimensionality of geometries is different to collection.
781 : : //if so, update the type dimensionality of the collection to match
782 : 69 : bool hasZ = false;
783 : 69 : bool hasM = false;
784 : 188 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
785 : : {
786 : 127 : hasZ = hasZ || geom->is3D();
787 : 127 : hasM = hasM || geom->isMeasure();
788 : 127 : if ( hasZ && hasM )
789 : 8 : break;
790 : : }
791 : 69 : if ( hasZ )
792 : 11 : addZValue( 0 );
793 : 69 : if ( hasM )
794 : 10 : addMValue( 0 );
795 : :
796 : 69 : return true;
797 : 2109 : }
798 : :
799 : 15 : bool QgsGeometryCollection::hasCurvedSegments() const
800 : : {
801 : 15 : QVector< QgsAbstractGeometry * >::const_iterator it = mGeometries.constBegin();
802 : 21 : for ( ; it != mGeometries.constEnd(); ++it )
803 : : {
804 : 9 : if ( ( *it )->hasCurvedSegments() )
805 : : {
806 : 3 : return true;
807 : : }
808 : 6 : }
809 : 12 : return false;
810 : 15 : }
811 : :
812 : 2 : QgsAbstractGeometry *QgsGeometryCollection::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
813 : : {
814 : 2 : std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::linearType( mWkbType ) ) );
815 : 2 : QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
816 : 2 : if ( !geomCollection )
817 : : {
818 : 0 : return clone();
819 : : }
820 : :
821 : 2 : geomCollection->reserve( mGeometries.size() );
822 : 2 : QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin();
823 : 4 : for ( ; geomIt != mGeometries.constEnd(); ++geomIt )
824 : : {
825 : 2 : geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) );
826 : 2 : }
827 : 2 : return geom.release();
828 : 2 : }
829 : :
830 : 33 : double QgsGeometryCollection::vertexAngle( QgsVertexId vertex ) const
831 : : {
832 : 33 : if ( vertex.part < 0 || vertex.part >= mGeometries.size() )
833 : : {
834 : 5 : return 0.0;
835 : : }
836 : :
837 : 28 : QgsAbstractGeometry *geom = mGeometries[vertex.part];
838 : 28 : if ( !geom )
839 : : {
840 : 0 : return 0.0;
841 : : }
842 : :
843 : 28 : return geom->vertexAngle( vertex );
844 : 33 : }
845 : :
846 : 37 : double QgsGeometryCollection::segmentLength( QgsVertexId startVertex ) const
847 : : {
848 : 37 : if ( startVertex.part < 0 || startVertex.part >= mGeometries.size() )
849 : : {
850 : 15 : return 0.0;
851 : : }
852 : :
853 : 22 : const QgsAbstractGeometry *geom = mGeometries[startVertex.part];
854 : 22 : if ( !geom )
855 : : {
856 : 0 : return 0.0;
857 : : }
858 : :
859 : 22 : return geom->segmentLength( startVertex );
860 : 37 : }
861 : :
862 : 374 : int QgsGeometryCollection::vertexCount( int part, int ring ) const
863 : : {
864 : 374 : if ( part < 0 || part >= mGeometries.size() )
865 : : {
866 : 34 : return 0;
867 : : }
868 : :
869 : 340 : return mGeometries[part]->vertexCount( 0, ring );
870 : 374 : }
871 : :
872 : 270 : int QgsGeometryCollection::ringCount( int part ) const
873 : : {
874 : 270 : if ( part < 0 || part >= mGeometries.size() )
875 : : {
876 : 23 : return 0;
877 : : }
878 : :
879 : 247 : return mGeometries[part]->ringCount();
880 : 270 : }
881 : :
882 : 537 : int QgsGeometryCollection::partCount() const
883 : : {
884 : 537 : return mGeometries.size();
885 : : }
886 : :
887 : 3328 : QgsPoint QgsGeometryCollection::vertexAt( QgsVertexId id ) const
888 : : {
889 : 3328 : return mGeometries[id.part]->vertexAt( id );
890 : : }
891 : :
892 : 3 : bool QgsGeometryCollection::isValid( QString &error, int flags ) const
893 : : {
894 : 3 : if ( flags == 0 && mHasCachedValidity )
895 : : {
896 : : // use cached validity results
897 : 0 : error = mValidityFailureReason;
898 : 0 : return error.isEmpty();
899 : : }
900 : :
901 : 3 : QgsGeos geos( this );
902 : 3 : bool res = geos.isValid( &error, flags & QgsGeometry::FlagAllowSelfTouchingHoles, nullptr );
903 : 3 : if ( flags == 0 )
904 : : {
905 : 3 : mValidityFailureReason = !res ? error : QString();
906 : 3 : mHasCachedValidity = true;
907 : 3 : }
908 : 3 : return res;
909 : 3 : }
910 : :
911 : 15 : bool QgsGeometryCollection::addZValue( double zValue )
912 : : {
913 : 15 : if ( QgsWkbTypes::hasZ( mWkbType ) )
914 : 10 : return false;
915 : :
916 : 5 : mWkbType = QgsWkbTypes::addZ( mWkbType );
917 : :
918 : 7 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
919 : : {
920 : 2 : geom->addZValue( zValue );
921 : : }
922 : 5 : clearCache();
923 : 5 : return true;
924 : 15 : }
925 : :
926 : 14 : bool QgsGeometryCollection::addMValue( double mValue )
927 : : {
928 : 14 : if ( QgsWkbTypes::hasM( mWkbType ) )
929 : 9 : return false;
930 : :
931 : 5 : mWkbType = QgsWkbTypes::addM( mWkbType );
932 : :
933 : 7 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
934 : : {
935 : 2 : geom->addMValue( mValue );
936 : : }
937 : 5 : clearCache();
938 : 5 : return true;
939 : 14 : }
940 : :
941 : :
942 : 4 : bool QgsGeometryCollection::dropZValue()
943 : : {
944 : 4 : if ( mWkbType != QgsWkbTypes::GeometryCollection && !is3D() )
945 : 0 : return false;
946 : :
947 : 4 : mWkbType = QgsWkbTypes::dropZ( mWkbType );
948 : 10 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
949 : : {
950 : 6 : geom->dropZValue();
951 : : }
952 : 4 : clearCache();
953 : 4 : return true;
954 : 4 : }
955 : :
956 : 4 : bool QgsGeometryCollection::dropMValue()
957 : : {
958 : 4 : if ( mWkbType != QgsWkbTypes::GeometryCollection && !isMeasure() )
959 : 0 : return false;
960 : :
961 : 4 : mWkbType = QgsWkbTypes::dropM( mWkbType );
962 : 10 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
963 : : {
964 : 6 : geom->dropMValue();
965 : : }
966 : 4 : clearCache();
967 : 4 : return true;
968 : 4 : }
969 : :
970 : 3 : void QgsGeometryCollection::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
971 : : {
972 : 6 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
973 : : {
974 : 3 : if ( geom )
975 : 3 : geom->filterVertices( filter );
976 : : }
977 : 3 : clearCache();
978 : 3 : }
979 : :
980 : 3 : void QgsGeometryCollection::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
981 : : {
982 : 6 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
983 : : {
984 : 3 : if ( geom )
985 : 3 : geom->transformVertices( transform );
986 : : }
987 : 3 : clearCache();
988 : 3 : }
989 : :
990 : 3 : void QgsGeometryCollection::swapXy()
991 : : {
992 : 6 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
993 : : {
994 : 3 : if ( geom )
995 : 3 : geom->swapXy();
996 : : }
997 : 3 : clearCache();
998 : 3 : }
999 : :
1000 : 1 : QgsGeometryCollection *QgsGeometryCollection::toCurveType() const
1001 : : {
1002 : 1 : std::unique_ptr< QgsGeometryCollection > newCollection( new QgsGeometryCollection() );
1003 : 1 : newCollection->reserve( mGeometries.size() );
1004 : 3 : for ( QgsAbstractGeometry *geom : mGeometries )
1005 : : {
1006 : 2 : newCollection->addGeometry( geom->toCurveType() );
1007 : : }
1008 : 1 : return newCollection.release();
1009 : 1 : }
1010 : :
1011 : 4 : bool QgsGeometryCollection::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
1012 : : {
1013 : 4 : if ( !transformer )
1014 : 0 : return false;
1015 : :
1016 : 4 : bool res = true;
1017 : 7 : for ( QgsAbstractGeometry *geom : std::as_const( mGeometries ) )
1018 : : {
1019 : 4 : if ( geom )
1020 : 4 : res = geom->transform( transformer, feedback );
1021 : :
1022 : 4 : if ( feedback && feedback->isCanceled() )
1023 : 0 : res = false;
1024 : :
1025 : 4 : if ( !res )
1026 : 1 : break;
1027 : : }
1028 : 4 : clearCache();
1029 : 4 : return res;
1030 : 4 : }
1031 : :
1032 : 35 : bool QgsGeometryCollection::wktOmitChildType() const
1033 : : {
1034 : 35 : return false;
1035 : : }
1036 : :
1037 : 578 : int QgsGeometryCollection::childCount() const
1038 : : {
1039 : 578 : return mGeometries.count();
1040 : : }
1041 : :
1042 : 38 : QgsAbstractGeometry *QgsGeometryCollection::childGeometry( int index ) const
1043 : : {
1044 : 38 : if ( index < 0 || index > mGeometries.count() )
1045 : 0 : return nullptr;
1046 : 38 : return mGeometries.at( index );
1047 : 38 : }
|