Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsgeometrygapcheck.cpp
3 : : ---------------------
4 : : begin : September 2015
5 : : copyright : (C) 2014 by Sandro Mani / Sourcepole AG
6 : : email : smani at sourcepole dot ch
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 "qgsgeometrycheckcontext.h"
17 : : #include "qgsgeometryengine.h"
18 : : #include "qgsgeometrygapcheck.h"
19 : : #include "qgsgeometrycollection.h"
20 : : #include "qgsfeaturepool.h"
21 : : #include "qgsvectorlayer.h"
22 : : #include "qgsvectorlayerutils.h"
23 : : #include "qgsfeedback.h"
24 : : #include "qgsapplication.h"
25 : : #include "qgsproject.h"
26 : : #include "qgsexpressioncontextutils.h"
27 : : #include "qgspolygon.h"
28 : : #include "qgscurve.h"
29 : : #include "qgssnappingutils.h"
30 : :
31 : : #include "geos_c.h"
32 : :
33 : 6 : QgsGeometryGapCheck::QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
34 : 3 : : QgsGeometryCheck( context, configuration )
35 : 6 : , mGapThresholdMapUnits( configuration.value( QStringLiteral( "gapThreshold" ) ).toDouble() )
36 : 6 : {
37 : 3 : }
38 : :
39 : 3 : void QgsGeometryGapCheck::prepare( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
40 : : {
41 : 6 : if ( configuration.value( QStringLiteral( "allowedGapsEnabled" ) ).toBool() )
42 : : {
43 : 3 : QgsVectorLayer *layer = context->project()->mapLayer<QgsVectorLayer *>( configuration.value( "allowedGapsLayer" ).toString() );
44 : 3 : if ( layer )
45 : : {
46 : 3 : mAllowedGapsLayer = layer;
47 : 3 : mAllowedGapsSource = std::make_unique<QgsVectorLayerFeatureSource>( layer );
48 : :
49 : 6 : mAllowedGapsBuffer = configuration.value( QStringLiteral( "allowedGapsBuffer" ) ).toDouble();
50 : 3 : }
51 : 3 : }
52 : : else
53 : : {
54 : 0 : mAllowedGapsSource.reset();
55 : : }
56 : 3 : }
57 : :
58 : 5 : void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
59 : : {
60 : 5 : if ( feedback )
61 : 5 : feedback->setProgress( feedback->progress() + 1.0 );
62 : :
63 : 5 : std::unique_ptr<QgsAbstractGeometry> allowedGapsGeom;
64 : 5 : std::unique_ptr<QgsGeometryEngine> allowedGapsGeomEngine;
65 : :
66 : 5 : if ( mAllowedGapsSource )
67 : : {
68 : 3 : QVector<QgsGeometry> allowedGaps;
69 : 3 : QgsFeatureRequest request;
70 : 3 : request.setSubsetOfAttributes( QgsAttributeList() );
71 : 3 : QgsFeatureIterator iterator = mAllowedGapsSource->getFeatures( request );
72 : 3 : QgsFeature feature;
73 : :
74 : 5 : while ( iterator.nextFeature( feature ) )
75 : : {
76 : 2 : QgsGeometry geom = feature.geometry();
77 : 2 : QgsGeometry gg = geom.buffer( mAllowedGapsBuffer, 20 );
78 : 2 : allowedGaps.append( gg );
79 : 2 : }
80 : :
81 : 3 : std::unique_ptr< QgsGeometryEngine > allowedGapsEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance );
82 : :
83 : : // Create union of allowed gaps
84 : 3 : QString errMsg;
85 : 3 : allowedGapsGeom.reset( allowedGapsEngine->combine( allowedGaps, &errMsg ) );
86 : 3 : allowedGapsGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( allowedGapsGeom.get(), mContext->tolerance );
87 : 3 : allowedGapsGeomEngine->prepareGeometry();
88 : 3 : }
89 : :
90 : 5 : QVector<QgsGeometry> geomList;
91 : 5 : QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
92 : 5 : const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
93 : 33 : for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
94 : : {
95 : 28 : geomList.append( layerFeature.geometry() );
96 : :
97 : 28 : if ( feedback && feedback->isCanceled() )
98 : : {
99 : 0 : geomList.clear();
100 : 0 : break;
101 : : }
102 : : }
103 : :
104 : 5 : if ( geomList.isEmpty() )
105 : : {
106 : 0 : return;
107 : : }
108 : :
109 : 5 : std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance );
110 : :
111 : : // Create union of geometry
112 : 5 : QString errMsg;
113 : 5 : std::unique_ptr<QgsAbstractGeometry> unionGeom( geomEngine->combine( geomList, &errMsg ) );
114 : 5 : if ( !unionGeom )
115 : : {
116 : 0 : messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
117 : 0 : return;
118 : : }
119 : :
120 : : // Get envelope of union
121 : 5 : geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom.get(), mContext->tolerance );
122 : 5 : geomEngine->prepareGeometry();
123 : 5 : std::unique_ptr<QgsAbstractGeometry> envelope( geomEngine->envelope( &errMsg ) );
124 : 5 : if ( !envelope )
125 : : {
126 : 0 : messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
127 : 0 : return;
128 : : }
129 : :
130 : : // Buffer envelope
131 : 5 : geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
132 : 5 : geomEngine->prepareGeometry();
133 : 5 : QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. ); //#spellok //#spellok
134 : 5 : envelope.reset( bufEnvelope );
135 : :
136 : : // Compute difference between envelope and union to obtain gap polygons
137 : 5 : geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
138 : 5 : geomEngine->prepareGeometry();
139 : 5 : std::unique_ptr<QgsAbstractGeometry> diffGeom( geomEngine->difference( unionGeom.get(), &errMsg ) );
140 : 5 : if ( !diffGeom )
141 : : {
142 : 0 : messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
143 : 0 : return;
144 : : }
145 : :
146 : : // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
147 : 5 : QgsGeometryPartIterator parts = diffGeom->parts();
148 : 35 : while ( parts.hasNext() )
149 : : {
150 : 33 : const QgsAbstractGeometry *gapGeom = parts.next();
151 : : // Skip the gap between features and boundingbox
152 : 30 : const double spacing = context()->tolerance;
153 : 30 : if ( gapGeom->boundingBox().snappedToGrid( spacing ) == envelope->boundingBox().snappedToGrid( spacing ) )
154 : : {
155 : 5 : continue;
156 : : }
157 : :
158 : : // Skip gaps above threshold
159 : 25 : if ( ( mGapThresholdMapUnits > 0 && gapGeom->area() > mGapThresholdMapUnits ) || gapGeom->area() < mContext->reducedTolerance )
160 : : {
161 : 4 : continue;
162 : : }
163 : :
164 : 21 : QgsRectangle gapAreaBBox = gapGeom->boundingBox();
165 : :
166 : : // Get neighboring polygons
167 : 21 : QMap<QString, QgsFeatureIds> neighboringIds;
168 : 21 : const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds.keys(), gapAreaBBox, compatibleGeometryTypes(), mContext );
169 : 21 : std::unique_ptr< QgsGeometryEngine > gapGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( gapGeom, mContext->tolerance );
170 : 21 : gapGeomEngine->prepareGeometry();
171 : 92 : for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
172 : : {
173 : 71 : const QgsGeometry geom = layerFeature.geometry();
174 : 71 : if ( gapGeomEngine->distance( geom.constGet() ) < mContext->tolerance )
175 : : {
176 : 63 : neighboringIds[layerFeature.layer()->id()].insert( layerFeature.feature().id() );
177 : 63 : gapAreaBBox.combineExtentWith( geom.boundingBox() );
178 : 63 : }
179 : 71 : }
180 : :
181 : 21 : if ( neighboringIds.isEmpty() )
182 : : {
183 : 0 : continue;
184 : : }
185 : :
186 : 21 : if ( allowedGapsGeomEngine && allowedGapsGeomEngine->contains( gapGeom ) )
187 : : {
188 : 2 : continue;
189 : : }
190 : :
191 : : // Add error
192 : 19 : double area = gapGeom->area();
193 : 19 : QgsRectangle gapBbox = gapGeom->boundingBox();
194 : 19 : errors.append( new QgsGeometryGapCheckError( this, QString(), QgsGeometry( gapGeom->clone() ), neighboringIds, area, gapBbox, gapAreaBBox ) );
195 : 21 : }
196 : 5 : }
197 : :
198 : 3 : void QgsGeometryGapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
199 : : {
200 : 3 : QMetaEnum metaEnum = QMetaEnum::fromType<QgsGeometryGapCheck::ResolutionMethod>();
201 : 3 : if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) )
202 : : {
203 : 0 : error->setFixFailed( tr( "Unknown method" ) );
204 : 0 : }
205 : : else
206 : : {
207 : 3 : ResolutionMethod methodValue = static_cast<ResolutionMethod>( method );
208 : 3 : switch ( methodValue )
209 : : {
210 : : case NoChange:
211 : 0 : error->setFixed( method );
212 : 0 : break;
213 : :
214 : : case MergeLongestEdge:
215 : : {
216 : 2 : QString errMsg;
217 : 2 : if ( mergeWithNeighbor( featurePools, static_cast<QgsGeometryGapCheckError *>( error ), changes, errMsg, LongestSharedEdge ) )
218 : : {
219 : 2 : error->setFixed( method );
220 : 2 : }
221 : : else
222 : : {
223 : 0 : error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
224 : : }
225 : : break;
226 : 2 : }
227 : :
228 : : case AddToAllowedGaps:
229 : : {
230 : 1 : QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mAllowedGapsLayer.data() );
231 : 1 : if ( layer )
232 : : {
233 : 1 : if ( !layer->isEditable() && !layer->startEditing() )
234 : : {
235 : 0 : error->setFixFailed( tr( "Could not start editing layer %1" ).arg( layer->name() ) );
236 : 0 : }
237 : : else
238 : : {
239 : 1 : QgsFeature feature = QgsVectorLayerUtils::createFeature( layer, error->geometry() );
240 : 1 : QgsFeatureList features = QgsVectorLayerUtils::makeFeatureCompatible( feature, layer );
241 : 1 : if ( !layer->addFeatures( features ) )
242 : : {
243 : 0 : error->setFixFailed( tr( "Could not add feature to layer %1" ).arg( layer->name() ) );
244 : 0 : }
245 : : else
246 : : {
247 : 1 : error->setFixed( method );
248 : : }
249 : 1 : }
250 : 1 : }
251 : : else
252 : : {
253 : 0 : error->setFixFailed( tr( "Allowed gaps layer could not be resolved" ) );
254 : : }
255 : 1 : break;
256 : : }
257 : :
258 : : case CreateNewFeature:
259 : : {
260 : 0 : QgsGeometryGapCheckError *gapCheckError = static_cast<QgsGeometryGapCheckError *>( error );
261 : 0 : QgsProject *project = QgsProject::instance();
262 : 0 : QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( project->mapLayer( gapCheckError->neighbors().keys().first() ) );
263 : 0 : if ( layer )
264 : : {
265 : 0 : const QgsGeometry geometry = error->geometry();
266 : 0 : QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
267 : 0 : QgsFeature feature = QgsVectorLayerUtils::createFeature( layer, geometry, QgsAttributeMap(), &context );
268 : 0 : if ( !layer->addFeature( feature ) )
269 : : {
270 : 0 : error->setFixFailed( tr( "Could not add feature" ) );
271 : 0 : }
272 : : else
273 : : {
274 : 0 : error->setFixed( method );
275 : : }
276 : 0 : }
277 : : else
278 : : {
279 : 0 : error->setFixFailed( tr( "Could not resolve target layer %1 to add feature" ).arg( error->layerId() ) );
280 : : }
281 : 0 : break;
282 : : }
283 : :
284 : : case MergeLargestArea:
285 : : {
286 : 0 : QString errMsg;
287 : 0 : if ( mergeWithNeighbor( featurePools, static_cast<QgsGeometryGapCheckError *>( error ), changes, errMsg, LargestArea ) )
288 : : {
289 : 0 : error->setFixed( method );
290 : 0 : }
291 : : else
292 : : {
293 : 0 : error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
294 : : }
295 : : break;
296 : 0 : }
297 : : }
298 : : }
299 : 3 : }
300 : :
301 : 2 : bool QgsGeometryGapCheck::mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg, Condition condition ) const
302 : : {
303 : 2 : double maxVal = 0.;
304 : 2 : QString mergeLayerId;
305 : 2 : QgsFeature mergeFeature;
306 : 2 : int mergePartIdx = -1;
307 : :
308 : 2 : const QgsGeometry geometry = err->geometry();
309 : 2 : const QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( geometry.constGet(), 0 );
310 : :
311 : 2 : const auto layerIds = err->neighbors().keys();
312 : 2 : QList<QgsFeature> neighbours;
313 : :
314 : : // Search for touching neighboring geometries
315 : 4 : for ( const QString &layerId : layerIds )
316 : : {
317 : 2 : QgsFeaturePool *featurePool = featurePools.value( layerId );
318 : 2 : std::unique_ptr<QgsAbstractGeometry> errLayerGeom( errGeometry->clone() );
319 : 2 : QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext );
320 : 2 : errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );
321 : :
322 : 2 : const auto featureIds = err->neighbors().value( layerId );
323 : :
324 : 8 : for ( QgsFeatureId testId : featureIds )
325 : : {
326 : 6 : QgsFeature feature;
327 : 6 : if ( !featurePool->getFeature( testId, feature ) )
328 : : {
329 : 0 : continue;
330 : : }
331 : :
332 : 6 : QgsGeometry transformedGeometry = feature.geometry();
333 : 6 : transformedGeometry.transform( ct );
334 : 6 : feature.setGeometry( transformedGeometry );
335 : 6 : neighbours.append( feature );
336 : 6 : }
337 : :
338 : 8 : for ( const QgsFeature &testFeature : neighbours )
339 : : {
340 : 6 : const QgsGeometry featureGeom = testFeature.geometry();
341 : 6 : const QgsAbstractGeometry *testGeom = featureGeom.constGet();
342 : 12 : for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
343 : : {
344 : 6 : double val = 0;
345 : 6 : switch ( condition )
346 : : {
347 : : case LongestSharedEdge:
348 : 6 : val = QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom.get(), QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance );
349 : 6 : break;
350 : :
351 : : case LargestArea:
352 : : // We might get a neighbour where we touch only a corner
353 : 0 : if ( QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom.get(), QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance ) > 0 )
354 : 0 : val = QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart )->area();
355 : 0 : break;
356 : : }
357 : :
358 : 6 : if ( val > maxVal )
359 : : {
360 : 3 : maxVal = val;
361 : 3 : mergeFeature = testFeature;
362 : 3 : mergePartIdx = iPart;
363 : 3 : mergeLayerId = layerId;
364 : 3 : }
365 : 6 : }
366 : 6 : }
367 : 2 : }
368 : :
369 : 2 : if ( maxVal == 0. )
370 : : {
371 : 0 : return false;
372 : : }
373 : :
374 : : // Create an index of all neighbouring vertices
375 : 2 : QgsSpatialIndex neighbourVerticesIndex( QgsSpatialIndex::Flag::FlagStoreFeatureGeometries );
376 : 2 : int id = 0;
377 : 8 : for ( const QgsFeature &neighbour : neighbours )
378 : : {
379 : 6 : QgsVertexIterator vit = neighbour.geometry().vertices();
380 : 43 : while ( vit.hasNext() )
381 : : {
382 : 37 : QgsPoint pt = vit.next();
383 : 37 : QgsFeature f;
384 : 37 : f.setId( id ); // required for SpatialIndex to return the correct result
385 : 37 : f.setGeometry( QgsGeometry( pt.clone() ) );
386 : 37 : neighbourVerticesIndex.addFeature( f );
387 : 37 : id++;
388 : 37 : }
389 : : }
390 : :
391 : : // Snap to the closest vertex
392 : 2 : QgsPolyline snappedRing;
393 : 2 : QgsVertexIterator iterator = errGeometry->vertices();
394 : 10 : while ( iterator.hasNext() )
395 : : {
396 : 8 : QgsPoint pt = iterator.next();
397 : 8 : QgsGeometry closestGeom = neighbourVerticesIndex.geometry( neighbourVerticesIndex.nearestNeighbor( QgsPointXY( pt ) ).first() );
398 : 8 : if ( !closestGeom.isEmpty() )
399 : : {
400 : 8 : snappedRing.append( QgsPoint( closestGeom.vertexAt( 0 ) ) );
401 : 8 : }
402 : 8 : }
403 : :
404 : 2 : std::unique_ptr<QgsPolygon> snappedErrGeom = std::make_unique<QgsPolygon>();
405 : 2 : snappedErrGeom->setExteriorRing( new QgsLineString( snappedRing ) );
406 : :
407 : : // Merge geometries
408 : 2 : QgsFeaturePool *featurePool = featurePools[ mergeLayerId ];
409 : 2 : std::unique_ptr<QgsAbstractGeometry> errLayerGeom( snappedErrGeom->clone() );
410 : 2 : QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext );
411 : 2 : errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );
412 : 2 : const QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
413 : 2 : const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet();
414 : 2 : std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom.get(), 0 );
415 : 2 : std::unique_ptr<QgsAbstractGeometry> combinedGeom( geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg ) );
416 : 2 : if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) )
417 : : {
418 : 0 : return false;
419 : : }
420 : :
421 : : // Add merged polygon to destination geometry
422 : 2 : replaceFeatureGeometryPart( featurePools, mergeLayerId, mergeFeature, mergePartIdx, combinedGeom.release(), changes );
423 : :
424 : 2 : return true;
425 : 2 : }
426 : :
427 : :
428 : 0 : QStringList QgsGeometryGapCheck::resolutionMethods() const
429 : : {
430 : 0 : QStringList methods = QStringList()
431 : 0 : << tr( "Add gap area to neighboring polygon with longest shared edge" )
432 : 0 : << tr( "No action" );
433 : 0 : if ( mAllowedGapsSource )
434 : 0 : methods << tr( "Add gap to allowed exceptions" );
435 : :
436 : 0 : return methods;
437 : 0 : }
438 : :
439 : 3 : QList<QgsGeometryCheckResolutionMethod> QgsGeometryGapCheck::availableResolutionMethods() const
440 : : {
441 : 12 : QList<QgsGeometryCheckResolutionMethod> fixes
442 : 12 : {
443 : 3 : QgsGeometryCheckResolutionMethod( MergeLongestEdge, tr( "Add to longest shared edge" ), tr( "Add the gap area to the neighbouring polygon with the longest shared edge." ), false ),
444 : 3 : QgsGeometryCheckResolutionMethod( CreateNewFeature, tr( "Create new feature" ), tr( "Create a new feature from the gap area." ), false ),
445 : 3 : QgsGeometryCheckResolutionMethod( MergeLargestArea, tr( "Add to largest neighbouring area" ), tr( "Add the gap area to the neighbouring polygon with the largest area." ), false )
446 : : };
447 : :
448 : 3 : if ( mAllowedGapsSource )
449 : 1 : fixes << QgsGeometryCheckResolutionMethod( AddToAllowedGaps, tr( "Add Gap to Allowed Exceptions" ), tr( "Create a new feature from the gap geometry on the allowed exceptions layer." ), true );
450 : :
451 : 3 : fixes << QgsGeometryCheckResolutionMethod( NoChange, tr( "No action" ), tr( "Do not perform any action and mark this error as fixed." ), false );
452 : :
453 : 3 : return fixes;
454 : 3 : }
455 : :
456 : 0 : QString QgsGeometryGapCheck::description() const
457 : : {
458 : 0 : return factoryDescription();
459 : : }
460 : :
461 : 0 : QString QgsGeometryGapCheck::id() const
462 : : {
463 : 0 : return factoryId();
464 : : }
465 : :
466 : 0 : QgsGeometryCheck::Flags QgsGeometryGapCheck::flags() const
467 : : {
468 : 0 : return factoryFlags();
469 : : }
470 : :
471 : : ///@cond private
472 : 0 : QString QgsGeometryGapCheck::factoryDescription()
473 : : {
474 : 0 : return tr( "Gap" );
475 : : }
476 : :
477 : 0 : QString QgsGeometryGapCheck::factoryId()
478 : : {
479 : 0 : return QStringLiteral( "QgsGeometryGapCheck" );
480 : : }
481 : :
482 : 0 : QgsGeometryCheck::Flags QgsGeometryGapCheck::factoryFlags()
483 : : {
484 : 0 : return QgsGeometryCheck::AvailableInValidation;
485 : : }
486 : :
487 : 26 : QList<QgsWkbTypes::GeometryType> QgsGeometryGapCheck::factoryCompatibleGeometryTypes()
488 : : {
489 : 26 : return {QgsWkbTypes::PolygonGeometry};
490 : : }
491 : :
492 : 0 : bool QgsGeometryGapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
493 : : {
494 : 0 : return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
495 : 0 : }
496 : :
497 : 0 : QgsGeometryCheck::CheckType QgsGeometryGapCheck::factoryCheckType()
498 : : {
499 : 0 : return QgsGeometryCheck::LayerCheck;
500 : : }
501 : : ///@endcond private
502 : :
503 : 2 : QgsRectangle QgsGeometryGapCheckError::contextBoundingBox() const
504 : : {
505 : 2 : return mContextBoundingBox;
506 : : }
507 : :
508 : 0 : bool QgsGeometryGapCheckError::isEqual( QgsGeometryCheckError *other ) const
509 : : {
510 : 0 : QgsGeometryGapCheckError *err = dynamic_cast<QgsGeometryGapCheckError *>( other );
511 : 0 : return err && QgsGeometryCheckerUtils::pointsFuzzyEqual( err->location(), location(), mCheck->context()->reducedTolerance ) && err->neighbors() == neighbors();
512 : : }
513 : :
514 : 0 : bool QgsGeometryGapCheckError::closeMatch( QgsGeometryCheckError *other ) const
515 : : {
516 : 0 : QgsGeometryGapCheckError *err = dynamic_cast<QgsGeometryGapCheckError *>( other );
517 : 0 : return err && err->layerId() == layerId() && err->neighbors() == neighbors();
518 : : }
519 : :
520 : 0 : void QgsGeometryGapCheckError::update( const QgsGeometryCheckError *other )
521 : : {
522 : 0 : QgsGeometryCheckError::update( other );
523 : : // Static cast since this should only get called if isEqual == true
524 : 0 : const QgsGeometryGapCheckError *err = static_cast<const QgsGeometryGapCheckError *>( other );
525 : 0 : mNeighbors = err->mNeighbors;
526 : 0 : mGapAreaBBox = err->mGapAreaBBox;
527 : 0 : }
528 : :
529 : 0 : bool QgsGeometryGapCheckError::handleChanges( const QgsGeometryCheck::Changes & )
530 : : {
531 : 0 : return true;
532 : : }
533 : :
534 : 2 : QgsRectangle QgsGeometryGapCheckError::affectedAreaBBox() const
535 : : {
536 : 2 : return mGapAreaBBox;
537 : : }
538 : :
539 : 0 : QMap<QString, QgsFeatureIds> QgsGeometryGapCheckError::involvedFeatures() const
540 : : {
541 : 0 : return mNeighbors;
542 : : }
543 : :
544 : 0 : QIcon QgsGeometryGapCheckError::icon() const
545 : : {
546 : :
547 : 0 : if ( status() == QgsGeometryCheckError::StatusFixed )
548 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) );
549 : : else
550 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/checks/SliverOrGap.svg" ) );
551 : 0 : }
|