diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 9787ef21b6cc..cf0df62a2049 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -243,12 +243,15 @@ ENDIF (WITH_SERVER AND WITH_SERVER_PLUGINS) # additional analysis includes INCLUDE_DIRECTORIES(BEFORE - ../src/analysis/processing - ../src/analysis/vector - ../src/analysis/raster - ../src/analysis/network - ../src/analysis/interpolation - ../src/analysis/openstreetmap + ${CMAKE_SOURCE_DIR}/src/analysis + ${CMAKE_SOURCE_DIR}/src/analysis/processing + ${CMAKE_SOURCE_DIR}/src/analysis/vector + ${CMAKE_SOURCE_DIR}/src/analysis/vector/geometry_checker + ${CMAKE_SOURCE_DIR}/src/analysis/raster + ${CMAKE_SOURCE_DIR}/src/analysis/network + ${CMAKE_SOURCE_DIR}/src/analysis/interpolation + ${CMAKE_SOURCE_DIR}/src/analysis/openstreetmap + ${CMAKE_BINARY_DIR}/src/analysis/processing ${CMAKE_BINARY_DIR}/src/analysis/vector ${CMAKE_BINARY_DIR}/src/analysis/raster diff --git a/python/analysis/analysis_auto.sip b/python/analysis/analysis_auto.sip index 00ec4069e266..309b762b4060 100644 --- a/python/analysis/analysis_auto.sip +++ b/python/analysis/analysis_auto.sip @@ -1,4 +1,5 @@ // Include auto-generated SIP files +%Include auto_generated/qgsanalysis.sip %Include auto_generated/raster/qgsalignraster.sip %Include auto_generated/raster/qgsaspectfilter.sip %Include auto_generated/raster/qgsderivativefilter.sip @@ -15,6 +16,8 @@ %Include auto_generated/vector/qgsgeometrysnapper.sip %Include auto_generated/vector/qgsgeometrysnappersinglesource.sip %Include auto_generated/vector/qgszonalstatistics.sip +%Include auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip +%Include auto_generated/vector/geometry_checker/qgsfeaturepool.sip %Include auto_generated/interpolation/qgsinterpolator.sip %Include auto_generated/interpolation/qgsgridfilewriter.sip %Include auto_generated/interpolation/qgsidwinterpolator.sip @@ -27,5 +30,11 @@ %Include auto_generated/network/qgsnetworkdistancestrategy.sip %Include auto_generated/network/qgsgraphanalyzer.sip %Include auto_generated/network/qgsvectorlayerdirector.sip +%Include auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip +%Include auto_generated/vector/geometry_checker/qgsgeometrycheckcontext.sip +%Include auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip +%Include auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip +%Include auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip %Include auto_generated/processing/qgsnativealgorithms.sip %Include auto_generated/network/qgsgraphdirector.sip +%Include auto_generated/vector/geometry_checker/qgsgeometrycheck.sip diff --git a/python/analysis/auto_additions/qgsgeometrycheck.py b/python/analysis/auto_additions/qgsgeometrycheck.py new file mode 100644 index 000000000000..63d903c4192d --- /dev/null +++ b/python/analysis/auto_additions/qgsgeometrycheck.py @@ -0,0 +1,3 @@ +# The following has been generated automatically from src/analysis/vector/geometry_checker/qgsgeometrycheck.h +QgsGeometryCheck.Flags.baseClass = QgsGeometryCheck +Flags = QgsGeometryCheck # dirty hack since SIP seems to introduce the flags in module diff --git a/python/analysis/auto_generated/qgsanalysis.sip.in b/python/analysis/auto_generated/qgsanalysis.sip.in new file mode 100644 index 000000000000..e876d0ae6191 --- /dev/null +++ b/python/analysis/auto_generated/qgsanalysis.sip.in @@ -0,0 +1,52 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/qgsanalysis.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsAnalysis +{ +%Docstring +QgsAnalysis is a singleton class containing various registry and other global members +related to analysis classes. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgsanalysis.h" +%End + public: + + + + ~QgsAnalysis(); + + static QgsAnalysis *instance(); +%Docstring +Returns a pointer to the singleton instance. +%End + + static QgsGeometryCheckRegistry *geometryCheckRegistry(); +%Docstring +Returns the global geometry checker registry, used for managing all geometry check factories. +%End + + private: + QgsAnalysis( const QgsAnalysis &other ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/qgsanalysis.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in new file mode 100644 index 000000000000..bee154195dc2 --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in @@ -0,0 +1,102 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsfeaturepool.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsFeaturePool : QgsFeatureSink /Abstract/ +{ +%Docstring +A feature pool is based on a vector layer and caches features. +%End + +%TypeHeaderCode +#include "qgsfeaturepool.h" +%End + public: + QgsFeaturePool( QgsVectorLayer *layer ); + virtual ~QgsFeaturePool(); + + bool getFeature( QgsFeatureId id, QgsFeature &feature ); +%Docstring +Retrieve the feature with the specified ``id`` into ``feature``. +It will be retrieved from the cache or from the underlying layer if unavailable. +If the feature is neither available from the cache nor from the layer it will return false. +%End + + virtual void updateFeature( QgsFeature &feature ) = 0; +%Docstring +Updates a feature in this pool. +Implementations will update the feature on the layer or on the data provider. +%End + + virtual void deleteFeature( QgsFeatureId fid ) = 0; +%Docstring +Removes a feature from this pool. +Implementations will remove the feature from the layer or from the data provider. +%End + + + + QgsVectorLayer *layer() const; +%Docstring +Get a pointer to the underlying layer. +May return a ``None`` if the layer has been deleted. +This must only be called from the main thread. +%End + + + QString layerId() const; +%Docstring +The layer id of the layer. +%End + + QgsWkbTypes::GeometryType geometryType() const; +%Docstring +The geometry type of this layer. +%End + + QgsCoordinateReferenceSystem crs() const; +%Docstring +The coordinate reference system of this layer. +%End + + protected: + + void insertFeature( const QgsFeature &feature ); +%Docstring +Inserts a feature into the cache and the spatial index. +To be used by implementations of ``addFeature``. +%End + + void refreshCache( const QgsFeature &feature ); +%Docstring +Changes a feature in the cache and the spatial index. +To be used by implementations of ``updateFeature``. +%End + + void removeFeature( const QgsFeatureId featureId ); +%Docstring +Removes a feature from the cache and the spatial index. +To be used by implementations of ``deleteFeature``. +%End + + + private: + QgsFeaturePool( const QgsFeaturePool &other ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsfeaturepool.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in new file mode 100644 index 000000000000..0c93a580d245 --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in @@ -0,0 +1,119 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsGeometryCheck +{ +%Docstring +************************************************************************* +qgsgeometrycheck.h +--------------------- +begin : September 2014 +copyright : (C) 2014 by Sandro Mani / Sourcepole AG +email : smani at sourcepole dot ch +************************************************************************** + +This program is free software; you can redistribute it and/or modify * +it under the terms of the GNU General Public License as published by * +the Free Software Foundation; either version 2 of the License, or * +(at your option) any later version. * + +************************************************************************** +%End + +%TypeHeaderCode +#include "qgsgeometrycheck.h" +%End + public: + static const QMetaObject staticMetaObject; + + public: + + struct LayerFeatureIds + { + LayerFeatureIds(); + + + }; + + enum ChangeWhat + { + ChangeFeature, + ChangePart, + ChangeRing, + ChangeNode + }; + + enum ChangeType + { + ChangeAdded, + ChangeRemoved, + ChangeChanged + }; + + enum CheckType + { + FeatureNodeCheck, + FeatureCheck, + LayerCheck + }; + + enum Flag + { + SingleGeometryCheck, + SingleLayerTopologyCheck, + AvailableInValidation + }; + typedef QFlags Flags; + + + struct Change + { + Change(); + Change( ChangeWhat _what, ChangeType _type, QgsVertexId _vidx = QgsVertexId() ); + ChangeWhat what; + ChangeType type; + QgsVertexId vidx; + bool operator==( const Change &other ); + }; + + typedef QMap > > Changes; + + QgsGeometryCheck( CheckType checkType, + const QgsGeometryCheckContext *context, + const QVariantMap &configuration ); + virtual ~QgsGeometryCheck(); + + + virtual bool isCompatible( QgsVectorLayer *layer ) const; + virtual QList compatibleGeometryTypes() const = 0; + virtual QgsGeometryCheck::Flags flags() const; + + virtual void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = 0, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0; + virtual QStringList resolutionMethods() const = 0; + virtual QString description() const = 0; + virtual QString id() const = 0; + CheckType checkType() const; + const QgsGeometryCheckContext *context() const; + + protected: + + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckcontext.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckcontext.sip.in new file mode 100644 index 000000000000..9c5bc184e9ce --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckcontext.sip.in @@ -0,0 +1,31 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +struct QgsGeometryCheckContext +{ + QgsGeometryCheckContext( int precision, + const QgsCoordinateReferenceSystem &mapCrs, + const QgsCoordinateTransformContext &transformContext ); + const double tolerance; + const double reducedTolerance; + const QgsCoordinateReferenceSystem mapCrs; + const QgsCoordinateTransformContext transformContext; + + private: + QgsGeometryCheckContext( const QgsGeometryCheckContext &rh ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in new file mode 100644 index 000000000000..0c2d93a3d390 --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in @@ -0,0 +1,101 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + + +class QgsGeometryCheckError +{ +%Docstring +************************************************************************* + +This program is free software; you can redistribute it and/or modify * +it under the terms of the GNU General Public License as published by * +the Free Software Foundation; either version 2 of the License, or * +(at your option) any later version. * + +************************************************************************** +%End + +%TypeHeaderCode +#include "qgsgeometrycheckerror.h" +%End + public: + enum Status { StatusPending, StatusFixFailed, StatusFixed, StatusObsolete }; + enum ValueType { ValueLength, ValueArea, ValueOther }; + + QgsGeometryCheckError( const QgsGeometryCheck *check, + const QgsGeometryCheckerUtils::LayerFeature &layerFeature, + const QgsPointXY &errorLocation, + QgsVertexId vidx = QgsVertexId(), + const QVariant &value = QVariant(), + ValueType valueType = ValueOther ); + + virtual ~QgsGeometryCheckError(); + + + const QgsGeometryCheck *check() const; + const QString &layerId() const; + QgsFeatureId featureId() const; + const QgsAbstractGeometry *geometry() const; + virtual QgsRectangle affectedAreaBBox() const; + virtual QString description() const; + const QgsPointXY &location() const; + QVariant value() const; + ValueType valueType() const; + const QgsVertexId &vidx() const; + Status status() const; + QString resolutionMessage() const; + void setFixed( int method ); + void setFixFailed( const QString &reason ); + void setObsolete(); + + virtual bool isEqual( QgsGeometryCheckError *other ) const; +%Docstring +Check if this error is equal to ``other``. +Is reimplemented by subclasses with additional information, comparison +of base information is done in parent class. +%End + + virtual bool closeMatch( QgsGeometryCheckError * /*other*/ ) const; +%Docstring +Check if this error is almost equal to ``other``. +If this returns true, it can be used to update existing errors after re-checking. +%End + + virtual void update( const QgsGeometryCheckError *other ); +%Docstring +Update this error with the information from \other. +Will be used to update existing errors whenever they are re-checked. +%End + + + protected: + QgsGeometryCheckError( const QgsGeometryCheck *check, + const QString &layerId, + QgsFeatureId featureId, + const QgsGeometry &geometry, + const QgsPointXY &errorLocation, + QgsVertexId vidx = QgsVertexId(), + const QVariant &value = QVariant(), + ValueType valueType = ValueOther ); + + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in new file mode 100644 index 000000000000..3cf96b41b685 --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in @@ -0,0 +1,96 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsGeometryCheckerUtils +{ +%Docstring +************************************************************************* + +This program is free software; you can redistribute it and/or modify * +it under the terms of the GNU General Public License as published by * +the Free Software Foundation; either version 2 of the License, or * +(at your option) any later version. * + +************************************************************************** +%End + +%TypeHeaderCode +#include "qgsgeometrycheckerutils.h" +%End + public: + class LayerFeature +{ + +%TypeHeaderCode +#include "qgsgeometrycheckerutils.h" +%End + public: + + LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, const QgsGeometryCheckContext *context, bool useMapCrs ); +%Docstring +Create a new layer/feature combination. +The layer is defined by ``pool``, ``feature`` needs to be from this layer. +If ``useMapCrs`` is True, geometries will be reprojected to the mapCrs defined +in ``context``. +%End + + const QgsFeature &feature() const; +%Docstring +Returns the feature. +The geometry will not be reprojected regardless of useMapCrs. +%End + + + QString layerId() const; +%Docstring +The layer id. +%End + + const QgsGeometry &geometry() const; +%Docstring +Returns the geometry of this feature. +If useMapCrs was specified, it will already be reprojected into the +CRS specified in the context specified in the constructor. +%End + QString id() const; + bool operator==( const LayerFeature &other ) const; + bool operator!=( const LayerFeature &other ) const; + + bool useMapCrs() const; +%Docstring +Returns if the geometry is reprojected to the map CRS or not. +%End + + }; + + class LayerFeatures +{ + +%TypeHeaderCode +#include "qgsgeometrycheckerutils.h" +%End + public: + + private: + LayerFeatures(); + }; + + +}; // QgsGeometryCheckerUtils + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in new file mode 100644 index 000000000000..26eae23e292a --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in @@ -0,0 +1,63 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + + +class QgsGeometryCheckFactory /Abstract/ +{ + +%TypeHeaderCode +#include "qgsgeometrycheckfactory.h" +%End + public: + + virtual ~QgsGeometryCheckFactory(); + + virtual QgsGeometryCheck *createGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) const = 0 /Factory/; + + virtual QString id() const = 0; + + virtual QString description() const = 0; + + virtual bool isCompatible( QgsVectorLayer *layer ) const = 0; + + virtual QgsGeometryCheck::Flags flags() const = 0; +}; + +template +class QgsGeometryCheckFactoryT : QgsGeometryCheckFactory +{ + +%TypeHeaderCode +#include "qgsgeometrycheckfactory.h" +%End + public: + virtual QgsGeometryCheck *createGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) const; + + virtual QString description() const; + + virtual QString id() const; + + virtual bool isCompatible( QgsVectorLayer *layer ) const; + + virtual QgsGeometryCheck::Flags flags() const; + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in new file mode 100644 index 000000000000..f625bee4fa60 --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in @@ -0,0 +1,57 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsGeometryCheckRegistry +{ +%Docstring +This class manages all known geometry check factories. + +QgsGeometryCheckRegistry is not usually directly created, but rather accessed through +:py:func:`QgsAnalysis.geometryCheckRegistry()` +%End + +%TypeHeaderCode +#include "qgsgeometrycheckregistry.h" +%End + public: + + QgsGeometryCheckRegistry(); +%Docstring +Constructor for QgsGeometryCheckRegistry. QgsGeometryCheckRegistry is not usually directly created, but rather accessed through +:py:func:`QgsAnalysis.geometryCheckRegistry()` +%End + + void initialize(); + + ~QgsGeometryCheckRegistry(); + + QgsGeometryCheck *geometryCheck( const QString &checkId, QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfig ) /Transfer/; + + QList geometryCheckFactories( QgsVectorLayer *layer, QgsGeometryCheck::Flags flags = 0 ) const; +%Docstring +Get all geometry check factories that are compatible with ``layer`` and have all of the ``flags`` set. + +.. versionadded:: 3.4 +%End + + void registerGeometryCheck( QgsGeometryCheckFactory *checkFactory /Transfer/ ); + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsisvalidgeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsisvalidgeometrycheck.sip.in new file mode 100644 index 000000000000..2a6924a2e1d5 --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsisvalidgeometrycheck.sip.in @@ -0,0 +1,37 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsIsValidGeometryCheck : QgsSingleGeometryCheck +{ +%Docstring +Checks if geometries are valid. +%End + +%TypeHeaderCode +#include "qgsisvalidgeometrycheck.h" +%End + public: + explicit QgsIsValidGeometryCheck( QgsGeometryCheckerContext *context ); + virtual QList processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const; + + + virtual QString errorDescription() const; + + virtual QString errorName() const; + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in new file mode 100644 index 000000000000..cb436a99881f --- /dev/null +++ b/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in @@ -0,0 +1,142 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsSingleGeometryCheckError +{ +%Docstring + +An error from a QgsSingleGeometryCheck. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgssinglegeometrycheck.h" +%End + public: + QgsSingleGeometryCheckError( const QgsSingleGeometryCheck *check, const QgsGeometry &geometry, const QgsGeometry &errorLocation, const QgsVertexId &vertexId = QgsVertexId() ); + + virtual ~QgsSingleGeometryCheckError(); + + virtual void update( const QgsSingleGeometryCheckError *other ); +%Docstring +Update this error with the information from \other. +Will be used to update existing errors whenever they are re-checked. +%End + + virtual bool isEqual( const QgsSingleGeometryCheckError *other ) const; +%Docstring +Check if this error is equal to ``other``. +Is reimplemented by subclasses with additional information, comparison +of base information is done in parent class. +%End + + + virtual QString description() const; +%Docstring +A human readable description of this error. +%End + + const QgsSingleGeometryCheck *check() const; +%Docstring +The check that created this error. + +.. versionadded:: 3.4 +%End + + QgsGeometry errorLocation() const; +%Docstring +The exact location of the error. + +.. versionadded:: 3.4 +%End + + QgsVertexId vertexId() const; +%Docstring +The vertex id of the error. May be invalid depending on the check. + +.. versionadded:: 3.4 +%End + + protected: +}; + +class QgsGeometryCheckErrorSingle : QgsGeometryCheckError +{ +%Docstring + +Wraps a QgsSingleGeometryError into a standard :py:class:`QgsGeometryCheckError`. +The single error can be obtained via singleError. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgssinglegeometrycheck.h" +%End + public: + QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *singleError, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ); + + QgsSingleGeometryCheckError *singleError() const; +%Docstring +The underlying single error. +%End + + +}; + +class QgsSingleGeometryCheck : QgsGeometryCheck +{ +%Docstring + +Base class for geometry checks for a single geometry without any context of the layer or other layers in the project. +Classic examples are validity checks like self-intersection. + +Subclasses need to implement the processGeometry method. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgssinglegeometrycheck.h" +%End + public: + QgsSingleGeometryCheck( CheckType checkType, + const QgsGeometryCheckContext *context, + const QVariantMap &configuration ); + + void collectErrors( const QMap &featurePools, + QList &errors, + QStringList &messages, + QgsFeedback *feedback = 0, + const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const final; + + virtual QList processGeometry( const QgsGeometry &geometry ) const = 0; +%Docstring +Check the ``geometry`` for errors. It may make use of ``configuration`` options. + +Returns a list of QgsSingleGeometryCheckErrors, ownership is transferred to the caller. +An empty list is returned for geometries without errors. + +.. versionadded:: 3.4 +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index f5c683a14376..1239542c35a2 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -2,6 +2,8 @@ # sources SET(QGIS_ANALYSIS_SRCS + qgsanalysis.cpp + interpolation/qgsgridfilewriter.cpp interpolation/qgsidwinterpolator.cpp interpolation/qgsinterpolator.cpp @@ -134,22 +136,24 @@ SET(QGIS_ANALYSIS_SRCS network/qgsgraphanalyzer.cpp vector/geometry_checker/qgsfeaturepool.cpp - vector/geometry_checker/qgsvectordataproviderfeaturepool.cpp - vector/geometry_checker/qgsgeometrychecker.cpp vector/geometry_checker/qgsgeometryanglecheck.cpp vector/geometry_checker/qgsgeometryareacheck.cpp vector/geometry_checker/qgsgeometrycheck.cpp - vector/geometry_checker/qgssinglegeometrycheck.cpp + vector/geometry_checker/qgsgeometrycheckcontext.cpp vector/geometry_checker/qgsgeometrychecker.cpp + vector/geometry_checker/qgsgeometrycheckerror.cpp vector/geometry_checker/qgsgeometrycheckerutils.cpp + vector/geometry_checker/qgsgeometrycheckfactory.cpp + vector/geometry_checker/qgsgeometrycheckregistry.cpp vector/geometry_checker/qgsgeometrycontainedcheck.cpp vector/geometry_checker/qgsgeometrydanglecheck.cpp vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.cpp vector/geometry_checker/qgsgeometryduplicatecheck.cpp vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp - vector/geometry_checker/qgsgeometrygapcheck.cpp vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp + vector/geometry_checker/qgsgeometrygapcheck.cpp + vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp vector/geometry_checker/qgsgeometryholecheck.cpp vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp @@ -162,6 +166,11 @@ SET(QGIS_ANALYSIS_SRCS vector/geometry_checker/qgsgeometryselfintersectioncheck.cpp vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp vector/geometry_checker/qgsgeometrytypecheck.cpp + vector/geometry_checker/qgsisvalidgeometrycheck.cpp + vector/geometry_checker/qgssinglegeometrycheck.cpp + vector/geometry_checker/qgssinglegeometrycheck.cpp + vector/geometry_checker/qgsvectordataproviderfeaturepool.cpp + vector/geometry_checker/qgsvectorlayerfeaturepool.cpp ) SET(QGIS_ANALYSIS_MOC_HDRS @@ -175,6 +184,7 @@ SET(QGIS_ANALYSIS_MOC_HDRS processing/qgsalgorithmfiledownloader.h vector/geometry_checker/qgsgeometrychecker.h + vector/geometry_checker/qgsgeometrycheck.h ) INCLUDE_DIRECTORIES(SYSTEM ${SPATIALITE_INCLUDE_DIR}) @@ -215,6 +225,8 @@ QT5_WRAP_CPP(QGIS_ANALYSIS_MOC_SRCS ${QGIS_ANALYSIS_MOC_HDRS}) # install headers SET(QGIS_ANALYSIS_HDRS + qgsanalysis.h + processing/qgsalgorithmimportphotos.h processing/qgsreclassifyutils.h @@ -239,6 +251,7 @@ SET(QGIS_ANALYSIS_HDRS vector/geometry_checker/qgsgeometrycheckerutils.h vector/geometry_checker/qgsfeaturepool.h vector/geometry_checker/qgsvectordataproviderfeaturepool.h + vector/geometry_checker/qgsvectorlayerfeaturepool.h interpolation/qgsinterpolator.h interpolation/qgsgridfilewriter.h @@ -269,7 +282,8 @@ SET(QGIS_ANALYSIS_HDRS vector/geometry_checker/qgsgeometryanglecheck.h vector/geometry_checker/qgsgeometryareacheck.h vector/geometry_checker/qgsgeometrychecker.h - vector/geometry_checker/qgsgeometrycheck.h + vector/geometry_checker/qgsgeometrycheckerror.h + vector/geometry_checker/qgsgeometrycheckcontext.h vector/geometry_checker/qgssinglegeometrycheck.h vector/geometry_checker/qgsgeometrycontainedcheck.h vector/geometry_checker/qgsgeometrydanglecheck.h @@ -291,6 +305,10 @@ SET(QGIS_ANALYSIS_HDRS vector/geometry_checker/qgsgeometryselfintersectioncheck.h vector/geometry_checker/qgsgeometrysliverpolygoncheck.h vector/geometry_checker/qgsgeometrytypecheck.h + vector/geometry_checker/qgssinglegeometrycheck.h + vector/geometry_checker/qgsisvalidgeometrycheck.h + vector/geometry_checker/qgsgeometrycheckregistry.h + vector/geometry_checker/qgsgeometrycheckfactory.h ) INCLUDE_DIRECTORIES( @@ -302,6 +320,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/symbology ${CMAKE_SOURCE_DIR}/src/core/metadata ${CMAKE_SOURCE_DIR}/src/core/expression + ${CMAKE_SOURCE_DIR}/src/analysis/vector/geometry_checker + ${CMAKE_BINARY_DIR}/src/core ${CMAKE_BINARY_DIR}/src/analysis interpolation diff --git a/src/analysis/qgsanalysis.cpp b/src/analysis/qgsanalysis.cpp new file mode 100644 index 000000000000..0cf48d84e865 --- /dev/null +++ b/src/analysis/qgsanalysis.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + qgsanalysis.cpp + ---------- + begin : September 2018 + copyright : (C) 2018 by Matthias Kuhn + email : matthias@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsanalysis.h" +#include "qgsgeometrycheckregistry.h" +#include "qgsgeometrycheckfactory.h" +#include "qgis.h" + +#include "qgsgeometryselfintersectioncheck.h" +#include "qgsgeometrygapcheck.h" +#include "qgsgeometrymissingvertexcheck.h" +#include "qgsgeometryoverlapcheck.h" + +QgsAnalysis *QgsAnalysis::instance() +{ + static QgsAnalysis *sInstance( new QgsAnalysis() ); + return sInstance; +} + +QgsGeometryCheckRegistry *QgsAnalysis::geometryCheckRegistry() +{ + return instance()->mGeometryCheckRegistry.get(); +} + +QgsAnalysis::QgsAnalysis() + : mGeometryCheckRegistry( qgis::make_unique() ) +{ + qRegisterMetaType< QList > >( "QList>" ); + + mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT() ); + mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT() ); + mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT() ); + mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT() ); +} + +QgsAnalysis::~QgsAnalysis() +{ + +} diff --git a/src/analysis/qgsanalysis.h b/src/analysis/qgsanalysis.h new file mode 100644 index 000000000000..a87ad9ac7939 --- /dev/null +++ b/src/analysis/qgsanalysis.h @@ -0,0 +1,68 @@ +/*************************************************************************** + qgsanalysis.h + -------- + begin : September 2018 + copyright : (C) 2018 by Matthias Kuhn + email : matthias@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSANALYSIS_H +#define QGSANALYSIS_H + +#include "qgis_analysis.h" +#include "qgis_sip.h" + +#include + +class QgsGeometryCheckRegistry; + +/** + * \ingroup analysis + * QgsAnalysis is a singleton class containing various registry and other global members + * related to analysis classes. + * \since QGIS 3.4 + */ +class ANALYSIS_EXPORT QgsAnalysis +{ + public: + + //! QgsAnalysis cannot be copied + QgsAnalysis( const QgsAnalysis &other ) = delete; + + //! QgsAnalysis cannot be copied + QgsAnalysis &operator=( const QgsAnalysis &other ) = delete; + + ~QgsAnalysis(); + + /** + * Returns a pointer to the singleton instance. + */ + static QgsAnalysis *instance(); + + /** + * Returns the global geometry checker registry, used for managing all geometry check factories. + */ + static QgsGeometryCheckRegistry *geometryCheckRegistry(); + + private: + + QgsAnalysis(); + + std::unique_ptr mGeometryCheckRegistry; + +#ifdef SIP_RUN + QgsAnalysis( const QgsAnalysis &other ); +#endif + +}; + +#endif // QGSANALYSIS_H diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.h b/src/analysis/vector/geometry_checker/qgsfeaturepool.h index cb57e77b0b8a..060c8d707750 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.h +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.h @@ -14,8 +14,6 @@ * * ***************************************************************************/ -#define SIP_NO_FILE - #ifndef QGS_FEATUREPOOL_H #define QGS_FEATUREPOOL_H @@ -34,7 +32,7 @@ class QgsVectorLayer; * \ingroup analysis * A feature pool is based on a vector layer and caches features. */ -class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink +class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink SIP_ABSTRACT { public: @@ -64,13 +62,13 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink * Returns the complete set of feature ids in this pool. * Note that this concerns the features governed by this pool, which are not necessarily all cached. */ - QgsFeatureIds allFeatureIds() const; + QgsFeatureIds allFeatureIds() const SIP_SKIP; /** * Get all feature ids in the bounding box \a rect. It will use a spatial index to * determine the ids. */ - QgsFeatureIds getIntersects( const QgsRectangle &rect ) const; + QgsFeatureIds getIntersects( const QgsRectangle &rect ) const SIP_SKIP; /** * Get a pointer to the underlying layer. @@ -85,8 +83,10 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink * will need to be done on the main thread and * the pointer will need to be checked for validity * before usage. + * + * \note Not available in Python */ - QPointer layerPtr() const; + QPointer layerPtr() const SIP_SKIP; /** * The layer id of the layer. @@ -128,9 +128,14 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink * Should be called by subclasses constructor and whenever * they insert a new feature. */ - void setFeatureIds( const QgsFeatureIds &ids ); + void setFeatureIds( const QgsFeatureIds &ids ) SIP_SKIP; private: +#ifdef SIP_RUN + QgsFeaturePool( const QgsFeaturePool &other ) + {} +#endif + static const int CACHE_SIZE = 1000; QCache mFeatureCache; QPointer mLayer; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp index abd18fa0b1c6..79d90944cb8d 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp @@ -13,14 +13,17 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryanglecheck.h" #include "qgsgeometryutils.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryAngleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryAngleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, context() ); + Q_UNUSED( messages ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, context() ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -63,9 +66,9 @@ void QgsGeometryAngleCheck::collectErrors( QList &error } } -void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryAngleCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { diff --git a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h index 4812ccdc37d0..de05dbbdc82a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h @@ -23,15 +23,20 @@ class ANALYSIS_EXPORT QgsGeometryAngleCheck : public QgsGeometryCheck { public: - QgsGeometryAngleCheck( QgsGeometryCheckerContext *context, double minAngle ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) - , mMinAngle( minAngle ) + QgsGeometryAngleCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) + , mMinAngle( configuration.value( "minAngle", 0.0 ).toDouble() ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Minimal angle" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryAngleCheck" ); } + QString factoryDescription() const { return tr( "Minimal angle" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryAngleCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { DeleteNode, NoChange }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp index a54c6c1bb419..dc4450efbeef 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp @@ -13,19 +13,22 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryengine.h" #include "qgsgeometrycollection.h" #include "qgsgeometryareacheck.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryAreaCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryAreaCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + Q_UNUSED( messages ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); - double layerToMapUnits = mContext->layerScaleFactor( layerFeature.layer() ); + double layerToMapUnits = scaleFactor( layerFeature.layer() ); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { double value; @@ -38,9 +41,9 @@ void QgsGeometryAreaCheck::collectErrors( QList &errors } } -void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const +void QgsGeometryAreaCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { @@ -52,7 +55,7 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, c const QgsAbstractGeometry *geom = g.constGet(); QgsVertexId vidx = error->vidx(); - double layerToMapUnits = mContext->layerScaleFactor( featurePool->layer() ); + double layerToMapUnits = scaleFactor( featurePool->layer() ); // Check if polygon still exists if ( !vidx.isValid( geom ) ) @@ -76,13 +79,13 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, c } else if ( method == Delete ) { - deleteFeatureGeometryPart( error->layerId(), feature, vidx.part, changes ); + deleteFeatureGeometryPart( featurePools, error->layerId(), feature, vidx.part, changes ); error->setFixed( method ); } else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute ) { QString errMsg; - if ( mergeWithNeighbor( error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) ) + if ( mergeWithNeighbor( featurePools, error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) ) { error->setFixed( method ); } @@ -100,13 +103,15 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, c bool QgsGeometryAreaCheck::checkThreshold( double layerToMapUnits, const QgsAbstractGeometry *geom, double &value ) const { value = geom->area(); - double threshold = mThresholdMapUnits / ( layerToMapUnits * layerToMapUnits ); + double threshold = mAreaThreshold / ( layerToMapUnits * layerToMapUnits ); return value < threshold; } -bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const +bool QgsGeometryAreaCheck::mergeWithNeighbor( const QMap &featurePools, + const QString &layerId, QgsFeature &feature, + int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ layerId ]; + QgsFeaturePool *featurePool = featurePools[ layerId ]; double maxVal = 0.; QgsFeature mergeFeature; @@ -194,9 +199,9 @@ bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature { --mergePartIdx; } - replaceFeatureGeometryPart( layerId, mergeFeature, mergePartIdx, combinedGeom, changes ); + replaceFeatureGeometryPart( featurePools, layerId, mergeFeature, mergePartIdx, combinedGeom, changes ); // Remove polygon from source geometry - deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); + deleteFeatureGeometryPart( featurePools, layerId, feature, partIdx, changes ); return true; } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h index ffb6364d039c..5dc2a4002e33 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h @@ -25,24 +25,27 @@ class QgsSurface; class ANALYSIS_EXPORT QgsGeometryAreaCheck : public QgsGeometryCheck { public: - QgsGeometryAreaCheck( QgsGeometryCheckerContext *context, double thresholdMapUnits ) - : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, context ) - , mThresholdMapUnits( thresholdMapUnits ) + QgsGeometryAreaCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureCheck, context, configuration ) + , mAreaThreshold( configurationValue( "areaThreshold" ) ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Minimal area" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryAreaCheck" ); } + QString factoryDescription() const { return tr( "Minimal area" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryAreaCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { MergeLongestEdge, MergeLargestArea, MergeIdenticalAttribute, Delete, NoChange }; private: - virtual bool checkThreshold( double layerToMapUnits, const QgsAbstractGeometry *geom, double &value ) const; - bool mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const; + bool mergeWithNeighbor( const QMap &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const; - protected: - double mThresholdMapUnits; + const double mAreaThreshold; }; #endif // QGS_GEOMETRY_AREA_CHECK_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheck.cpp index f78651c0c966..2245dc65d9e9 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheck.cpp @@ -13,225 +13,48 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrycollection.h" #include "qgscurvepolygon.h" #include "qgsgeometrycheck.h" +#include "qgsgeometrycheckerror.h" #include "qgsfeaturepool.h" #include "qgsvectorlayer.h" #include "qgsreadwritelocker.h" #include "qgsthreadingutils.h" -QgsGeometryCheckerContext::QgsGeometryCheckerContext( int _precision, const QgsCoordinateReferenceSystem &_mapCrs, const QMap &_featurePools, const QgsCoordinateTransformContext &transformContext ) - : tolerance( std::pow( 10, -_precision ) ) - , reducedTolerance( std::pow( 10, -_precision / 2 ) ) - , mapCrs( _mapCrs ) - , featurePools( _featurePools ) - , transformContext( transformContext ) -{ -} - -const QgsCoordinateTransform &QgsGeometryCheckerContext::layerTransform( const QPointer &layer ) -{ - QgsReadWriteLocker locker( mCacheLock, QgsReadWriteLocker::Read ); - if ( !mTransformCache.contains( layer ) ) - { - QgsCoordinateTransform transform; - QgsThreadingUtils::runOnMainThread( [this, &transform, layer]() - { - QgsVectorLayer *lyr = layer.data(); - if ( lyr ) - transform = QgsCoordinateTransform( lyr->crs(), mapCrs, transformContext ); - } ); - locker.changeMode( QgsReadWriteLocker::Write ); - mTransformCache[layer] = transform; - locker.changeMode( QgsReadWriteLocker::Read ); - } - - return mTransformCache[layer]; -} - -double QgsGeometryCheckerContext::layerScaleFactor( const QPointer &layer ) -{ - QgsReadWriteLocker locker( mCacheLock, QgsReadWriteLocker::Read ); - if ( !mScaleFactorCache.contains( layer ) ) - { - double scaleFactor = 1.0; - QgsThreadingUtils::runOnMainThread( [this, layer, &scaleFactor]() - { - QgsVectorLayer *lyr = layer.data(); - if ( lyr ) - scaleFactor = layerTransform( layer ).scaleFactor( lyr->extent() ); - } ); - - locker.changeMode( QgsReadWriteLocker::Write ); - mScaleFactorCache[layer] = scaleFactor; - locker.changeMode( QgsReadWriteLocker::Read ); - } - return mScaleFactorCache.value( layer ); -} -QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, const QString &layerId, - QgsFeatureId featureId, const QgsGeometry &geometry, - const QgsPointXY &errorLocation, - QgsVertexId vidx, - const QVariant &value, ValueType valueType ) - : mCheck( check ) - , mLayerId( layerId ) - , mFeatureId( featureId ) - , mGeometry( geometry ) - , mErrorLocation( errorLocation ) - , mVidx( vidx ) - , mValue( value ) - , mValueType( valueType ) - , mStatus( StatusPending ) -{ -} - -QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, - const QgsGeometryCheckerUtils::LayerFeature &layerFeature, - const QgsPointXY &errorLocation, QgsVertexId vidx, - const QVariant &value, ValueType valueType ) - : mCheck( check ) - , mLayerId( layerFeature.layerId() ) - , mFeatureId( layerFeature.feature().id() ) - , mErrorLocation( errorLocation ) - , mVidx( vidx ) - , mValue( value ) - , mValueType( valueType ) - , mStatus( StatusPending ) -{ - if ( vidx.part != -1 ) - { - mGeometry = QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( layerFeature.geometry().constGet(), vidx.part )->clone() ); - } - else - { - mGeometry = layerFeature.geometry(); - } - if ( !layerFeature.useMapCrs() ) - { - const QgsCoordinateTransform &transform = check->context()->layerTransform( layerFeature.layer() ); - mGeometry.transform( transform ); - mErrorLocation = transform.transform( mErrorLocation ); - } -} - -const QgsAbstractGeometry *QgsGeometryCheckError::geometry() const -{ - return mGeometry.constGet(); -} -QgsRectangle QgsGeometryCheckError::affectedAreaBBox() const +bool QgsGeometryCheck::isCompatible( QgsVectorLayer *layer ) const { - return mGeometry.boundingBox(); + return compatibleGeometryTypes().contains( layer->geometryType() ); } -void QgsGeometryCheckError::setFixed( int method ) +void QgsGeometryCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, QgsGeometryCheck::Changes &changes ) const { - mStatus = StatusFixed; - const QStringList methods = mCheck->resolutionMethods(); - mResolutionMessage = methods[method]; + Q_UNUSED( featurePools ) + Q_UNUSED( error ) + Q_UNUSED( method ) + Q_UNUSED( mergeAttributeIndices ) + Q_UNUSED( changes ) } -void QgsGeometryCheckError::setFixFailed( const QString &reason ) -{ - mStatus = StatusFixFailed; - mResolutionMessage = reason; -} - -bool QgsGeometryCheckError::isEqual( QgsGeometryCheckError *other ) const -{ - return other->check() == check() && - other->layerId() == layerId() && - other->featureId() == featureId() && - other->vidx() == vidx(); -} - -bool QgsGeometryCheckError::closeMatch( QgsGeometryCheckError * ) const -{ - return false; -} - -bool QgsGeometryCheckError::handleChanges( const QgsGeometryCheck::Changes &changes ) -{ - if ( status() == StatusObsolete ) - { - return false; - } - - for ( const QgsGeometryCheck::Change &change : changes.value( layerId() ).value( featureId() ) ) - { - if ( change.what == QgsGeometryCheck::ChangeFeature ) - { - if ( change.type == QgsGeometryCheck::ChangeRemoved ) - { - return false; - } - else if ( change.type == QgsGeometryCheck::ChangeChanged ) - { - // If the check is checking the feature at geometry nodes level, the - // error almost certainly invalid after a geometry change. In the other - // cases, it might likely still be valid. - return mCheck->checkType() != QgsGeometryCheck::FeatureNodeCheck; - } - } - else if ( change.what == QgsGeometryCheck::ChangePart ) - { - if ( mVidx.part == change.vidx.part ) - { - return false; - } - else if ( mVidx.part > change.vidx.part ) - { - mVidx.part += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1; - } - } - else if ( change.what == QgsGeometryCheck::ChangeRing ) - { - if ( mVidx.partEqual( change.vidx ) ) - { - if ( mVidx.ring == change.vidx.ring ) - { - return false; - } - else if ( mVidx.ring > change.vidx.ring ) - { - mVidx.ring += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1; - } - } - } - else if ( change.what == QgsGeometryCheck::ChangeNode ) - { - if ( mVidx.ringEqual( change.vidx ) ) - { - if ( mVidx.vertex == change.vidx.vertex ) - { - return false; - } - else if ( mVidx.vertex > change.vidx.vertex ) - { - mVidx.vertex += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1; - } - } - } - } - return true; -} - -QMap QgsGeometryCheck::allLayerFeatureIds() const +QMap QgsGeometryCheck::allLayerFeatureIds( const QMap &featurePools ) const { QMap featureIds; - for ( QgsFeaturePool *pool : mContext->featurePools ) + for ( QgsFeaturePool *pool : featurePools ) { featureIds.insert( pool->layerId(), pool->allFeatureIds() ); } return featureIds; } -void QgsGeometryCheck::replaceFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const +void QgsGeometryCheck::replaceFeatureGeometryPart( const QMap &featurePools, + const QString &layerId, QgsFeature &feature, + int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[layerId]; + QgsFeaturePool *featurePool = featurePools[layerId]; QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *geom = featureGeom.get(); if ( QgsGeometryCollection *geomCollection = dynamic_cast< QgsGeometryCollection *>( geom ) ) @@ -250,9 +73,9 @@ void QgsGeometryCheck::replaceFeatureGeometryPart( const QString &layerId, QgsFe featurePool->updateFeature( feature ); } -void QgsGeometryCheck::deleteFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const +void QgsGeometryCheck::deleteFeatureGeometryPart( const QMap &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[layerId]; + QgsFeaturePool *featurePool = featurePools[layerId]; QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *geom = featureGeom.get(); if ( dynamic_cast( geom ) ) @@ -277,9 +100,11 @@ void QgsGeometryCheck::deleteFeatureGeometryPart( const QString &layerId, QgsFea } } -void QgsGeometryCheck::deleteFeatureGeometryRing( const QString &layerId, QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const +void QgsGeometryCheck::deleteFeatureGeometryRing( const QMap &featurePools, + const QString &layerId, QgsFeature &feature, + int partIdx, int ringIdx, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[layerId]; + QgsFeaturePool *featurePool = featurePools[layerId]; QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *partGeom = QgsGeometryCheckerUtils::getGeomPart( featureGeom.get(), partIdx ); if ( dynamic_cast( partGeom ) ) @@ -287,7 +112,7 @@ void QgsGeometryCheck::deleteFeatureGeometryRing( const QString &layerId, QgsFea // If we delete the exterior ring of a polygon, it makes no sense to keep the interiors if ( ringIdx == 0 ) { - deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); + deleteFeatureGeometryPart( featurePools, layerId, feature, partIdx, changes ); } else { @@ -300,17 +125,21 @@ void QgsGeometryCheck::deleteFeatureGeometryRing( const QString &layerId, QgsFea // Other geometry types do not have rings, remove the entire part else { - deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); + deleteFeatureGeometryPart( featurePools, layerId, feature, partIdx, changes ); } } -void QgsGeometryCheckError::update( const QgsGeometryCheckError *other ) +double QgsGeometryCheck::scaleFactor( QPointer layer ) const { - Q_ASSERT( mCheck == other->mCheck ); - Q_ASSERT( mLayerId == other->mLayerId ); - Q_ASSERT( mFeatureId == other->mFeatureId ); - mErrorLocation = other->mErrorLocation; - mVidx = other->mVidx; - mValue = other->mValue; - mGeometry = other->mGeometry; + double scaleFactor = 1.0; + + QgsVectorLayer *lyr = layer.data(); + if ( lyr ) + { + QgsCoordinateTransform ct( lyr->crs(), mContext->mapCrs, mContext->transformContext ); + scaleFactor = ct.scaleFactor( lyr->extent() ); + } + return scaleFactor; } + + diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h index 1ae27100819d..f2dfec743815 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h @@ -13,8 +13,6 @@ * * ***************************************************************************/ -#define SIP_NO_FILE - #ifndef QGS_GEOMETRY_CHECK_H #define QGS_GEOMETRY_CHECK_H @@ -28,34 +26,47 @@ #include "qgsvectorlayer.h" #include "geometry/qgsgeometry.h" #include "qgsgeometrycheckerutils.h" +#include "qgssettings.h" class QgsGeometryCheckError; class QgsFeaturePool; #define FEATUREID_NULL std::numeric_limits::min() -struct ANALYSIS_EXPORT QgsGeometryCheckerContext -{ - QgsGeometryCheckerContext( int _precision, const QgsCoordinateReferenceSystem &_mapCrs, const QMap &_featurePools, const QgsCoordinateTransformContext &transformContext ); - const double tolerance; - const double reducedTolerance; - const QgsCoordinateReferenceSystem mapCrs; - const QMap featurePools; - const QgsCoordinateTransformContext transformContext; - const QgsCoordinateTransform &layerTransform( const QPointer &layer ); - double layerScaleFactor( const QPointer &layer ); - - private: - QMap, QgsCoordinateTransform> mTransformCache; - QMap, double> mScaleFactorCache; - QReadWriteLock mCacheLock; -}; - class ANALYSIS_EXPORT QgsGeometryCheck { + Q_GADGET Q_DECLARE_TR_FUNCTIONS( QgsGeometryCheck ) public: + + /** + * A list of layers and feature ids for each of these layers. + * In C++, the member `ids` can be accessed directly. + * In Python some accessor methods will need to be written. + * + * \since QGIS 3.4 + */ + struct LayerFeatureIds + { + LayerFeatureIds() = default; + LayerFeatureIds( const QMap &ids ) SIP_SKIP; + + QMap ids SIP_SKIP; + +#ifndef SIP_RUN + QMap toMap() const + { + return ids; + } + + bool isEmpty() const + { + return ids.isEmpty(); + } +#endif + }; + enum ChangeWhat { ChangeFeature, @@ -78,6 +89,15 @@ class ANALYSIS_EXPORT QgsGeometryCheck LayerCheck }; + enum Flag + { + SingleGeometryCheck = 1 << 1, + SingleLayerTopologyCheck = 1 << 2, + AvailableInValidation = 1 << 3 + }; + Q_DECLARE_FLAGS( Flags, Flag ) + Q_FLAG( Flags ) + struct Change { Change() = default; @@ -95,120 +115,49 @@ class ANALYSIS_EXPORT QgsGeometryCheck } }; - typedef QMap>> Changes; + typedef QMap > > Changes; - QgsGeometryCheck( CheckType checkType, const QList &compatibleGeometryTypes, QgsGeometryCheckerContext *context ) + QgsGeometryCheck( CheckType checkType, + const QgsGeometryCheckContext *context, + const QVariantMap &configuration ) : mCheckType( checkType ) - , mCompatibleGeometryTypes( compatibleGeometryTypes ) , mContext( context ) + , mConfiguration( configuration ) {} virtual ~QgsGeometryCheck() = default; - virtual void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const = 0; - virtual void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const = 0; + +#ifndef SIP_RUN + template + T configurationValue( const QString &name, const QVariant &defaultValue = QVariant() ) + { + return mConfiguration.value( name, QgsSettings().value( "/geometry_checker/" + id() + "/" + name, defaultValue ) ).value(); + } +#endif + + virtual bool isCompatible( QgsVectorLayer *layer ) const; + virtual QList compatibleGeometryTypes() const = 0; + virtual QgsGeometryCheck::Flags flags() const {return nullptr;} + + virtual void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0; + //! Fix the error \a error with the specified \a method. + virtual void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes SIP_INOUT ) const SIP_SKIP; virtual QStringList resolutionMethods() const = 0; - virtual QString errorDescription() const = 0; - virtual QString errorName() const = 0; + virtual QString description() const = 0; + virtual QString id() const = 0; CheckType checkType() const { return mCheckType; } - bool isCompatible( QgsWkbTypes::GeometryType type ) const { return mCompatibleGeometryTypes.contains( type ); } - QgsGeometryCheckerContext *context() const { return mContext; } + const QgsGeometryCheckContext *context() const { return mContext; } protected: - QMap allLayerFeatureIds() const; - void replaceFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const; - void deleteFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const; - void deleteFeatureGeometryRing( const QString &layerId, QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const; + QMap allLayerFeatureIds( const QMap &featurePools ) const SIP_SKIP; + void replaceFeatureGeometryPart( const QMap &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const SIP_SKIP; + void deleteFeatureGeometryPart( const QMap &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const SIP_SKIP; + void deleteFeatureGeometryRing( const QMap &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const SIP_SKIP; const CheckType mCheckType; - QList mCompatibleGeometryTypes; - QgsGeometryCheckerContext *mContext; -}; - - -class ANALYSIS_EXPORT QgsGeometryCheckError -{ - public: - enum Status { StatusPending, StatusFixFailed, StatusFixed, StatusObsolete }; - enum ValueType { ValueLength, ValueArea, ValueOther }; - - QgsGeometryCheckError( const QgsGeometryCheck *check, - const QgsGeometryCheckerUtils::LayerFeature &layerFeature, - const QgsPointXY &errorLocation, - QgsVertexId vidx = QgsVertexId(), - const QVariant &value = QVariant(), - ValueType valueType = ValueOther ); - - virtual ~QgsGeometryCheckError() = default; - - const QgsGeometryCheckError &operator=( const QgsGeometryCheckError & ) = delete; - - const QgsGeometryCheck *check() const { return mCheck; } - const QString &layerId() const { return mLayerId; } - QgsFeatureId featureId() const { return mFeatureId; } - // In map units - const QgsAbstractGeometry *geometry() const; - // In map units - virtual QgsRectangle affectedAreaBBox() const; - virtual QString description() const { return mCheck->errorDescription(); } - // In map units - const QgsPointXY &location() const { return mErrorLocation; } - // Lengths, areas in map units - QVariant value() const { return mValue; } - ValueType valueType() const { return mValueType; } - const QgsVertexId &vidx() const { return mVidx; } - Status status() const { return mStatus; } - QString resolutionMessage() const { return mResolutionMessage; } - void setFixed( int method ); - void setFixFailed( const QString &reason ); - void setObsolete() { mStatus = StatusObsolete; } - - /** - * Check if this error is equal to \a other. - * Is reimplemented by subclasses with additional information, comparison - * of base information is done in parent class. - */ - virtual bool isEqual( QgsGeometryCheckError *other ) const; - - /** - * Check if this error is almost equal to \a other. - * If this returns true, it can be used to update existing errors after re-checking. - */ - virtual bool closeMatch( QgsGeometryCheckError * /*other*/ ) const; - - /** - * Update this error with the information from \other. - * Will be used to update existing errors whenever they are re-checked. - */ - virtual void update( const QgsGeometryCheckError *other ); - - /** - * Apply a list of \a changes. - */ - virtual bool handleChanges( const QgsGeometryCheck::Changes &changes ); - - protected: - // Users of this constructor must ensure geometry and errorLocation are in map coordinates - QgsGeometryCheckError( const QgsGeometryCheck *check, - const QString &layerId, - QgsFeatureId featureId, - const QgsGeometry &geometry, - const QgsPointXY &errorLocation, - QgsVertexId vidx = QgsVertexId(), - const QVariant &value = QVariant(), - ValueType valueType = ValueOther ); - - const QgsGeometryCheck *mCheck = nullptr; - QString mLayerId; - QgsFeatureId mFeatureId; - QgsGeometry mGeometry; - QgsPointXY mErrorLocation; - QgsVertexId mVidx; - QVariant mValue; - ValueType mValueType; - Status mStatus; - QString mResolutionMessage; + const QgsGeometryCheckContext *mContext; + QVariantMap mConfiguration; + double scaleFactor( QPointer layer ) const SIP_SKIP; }; -Q_DECLARE_METATYPE( QgsGeometryCheckError * ) - #endif // QGS_GEOMETRY_CHECK_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.cpp new file mode 100644 index 000000000000..46080bd5885a --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.cpp @@ -0,0 +1,27 @@ +/*************************************************************************** + qgsgeometrycheckcontext.h + --------------------- + begin : September 2018 + copyright : (C) 2018 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsgeometrycheckcontext.h" +#include "qgsreadwritelocker.h" +#include "qgsthreadingutils.h" +#include "qgsvectorlayer.h" + +QgsGeometryCheckContext::QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext ) + : tolerance( std::pow( 10, -precision ) ) + , reducedTolerance( std::pow( 10, -precision / 2 ) ) + , mapCrs( mapCrs ) + , transformContext( transformContext ) +{ +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h new file mode 100644 index 000000000000..f20a28bf17d1 --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h @@ -0,0 +1,41 @@ +/*************************************************************************** + qgsgeometrycheckcontext.h + --------------------- + begin : September 2018 + copyright : (C) 2018 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYCHECKCONTEXT_H +#define QGSGEOMETRYCHECKCONTEXT_H + +#include "qgis_analysis.h" +#include "qgscoordinatereferencesystem.h" +#include "qgscoordinatetransformcontext.h" +#include "qgsfeaturepool.h" + +struct ANALYSIS_EXPORT QgsGeometryCheckContext +{ + QgsGeometryCheckContext( int precision, + const QgsCoordinateReferenceSystem &mapCrs, + const QgsCoordinateTransformContext &transformContext ); + const double tolerance; + const double reducedTolerance; + const QgsCoordinateReferenceSystem mapCrs; + const QgsCoordinateTransformContext transformContext; + + private: +#ifdef SIP_RUN + QgsGeometryCheckContext( const QgsGeometryCheckContext &rh ) + {} +#endif +}; + +#endif // QGSGEOMETRYCHECKCONTEXT_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp b/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp index 337931c4f173..f4166aab509c 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp @@ -14,23 +14,26 @@ * * ***************************************************************************/ +#include +#include +#include +#include + +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrychecker.h" #include "qgsgeometrycheck.h" #include "qgsfeaturepool.h" #include "qgsproject.h" #include "qgsvectorlayer.h" +#include "qgsgeometrycheckerror.h" -#include -#include -#include -#include -QgsGeometryChecker::QgsGeometryChecker( const QList &checks, QgsGeometryCheckerContext *context ) +QgsGeometryChecker::QgsGeometryChecker( const QList &checks, const QMap &featurePools ) : mChecks( checks ) - , mContext( context ) + , mFeaturePools( featurePools ) { - for ( auto it = mContext->featurePools.constBegin(); it != mContext->featurePools.constEnd(); ++it ) + for ( auto it = featurePools.constBegin(); it != mFeaturePools.constEnd(); ++it ) { if ( it.value()->layer() ) { @@ -45,7 +48,7 @@ QgsGeometryChecker::~QgsGeometryChecker() { qDeleteAll( mCheckErrors ); qDeleteAll( mChecks ); - for ( auto it = mContext->featurePools.constBegin(); it != mContext->featurePools.constEnd(); ++it ) + for ( auto it = mFeaturePools.constBegin(); it != mFeaturePools.constEnd(); ++it ) { if ( it.value()->layer() ) { @@ -64,11 +67,11 @@ QFuture QgsGeometryChecker::execute( int *totalSteps ) *totalSteps = 0; for ( QgsGeometryCheck *check : qgis::as_const( mChecks ) ) { - for ( auto it = mContext->featurePools.constBegin(); it != mContext->featurePools.constEnd(); ++it ) + for ( auto it = mFeaturePools.constBegin(); it != mFeaturePools.constEnd(); ++it ) { if ( check->checkType() <= QgsGeometryCheck::FeatureCheck ) { - *totalSteps += check->isCompatible( it.value()->layer()->geometryType() ) ? it.value()->allFeatureIds().size() : 0; + *totalSteps += check->isCompatible( it.value()->layer() ) ? it.value()->allFeatureIds().size() : 0; } else { @@ -93,7 +96,7 @@ QFuture QgsGeometryChecker::execute( int *totalSteps ) void QgsGeometryChecker::emitProgressValue() { - emit progressValue( mProgressCounter ); + emit progressValue( mFeedback.progress() ); } bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint ) @@ -110,7 +113,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo QgsGeometryCheck::Changes changes; QgsRectangle recheckArea = error->affectedAreaBBox(); - error->check()->fixError( error, method, mMergeAttributeIndices, changes ); + error->check()->fixError( mFeaturePools, error, method, mMergeAttributeIndices, changes ); #if 0 QTextStream( stdout ) << " * Status: " << error->resolutionMessage() << endl; static QVector strChangeWhat = { "ChangeFeature", "ChangePart", "ChangeRing", "ChangeNode" }; @@ -144,7 +147,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo for ( auto it = changes.constBegin(); it != changes.constEnd(); ++it ) { const QMap> &layerChanges = it.value(); - QgsFeaturePool *featurePool = mContext->featurePools[it.key()]; + QgsFeaturePool *featurePool = mFeaturePools[it.key()]; QgsCoordinateTransform t( featurePool->layer()->crs(), mContext->mapCrs, QgsProject::instance() ); for ( auto layerChangeIt = layerChanges.constBegin(); layerChangeIt != layerChanges.constEnd(); ++layerChangeIt ) { @@ -181,9 +184,9 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo } recheckArea.grow( 10 * mContext->tolerance ); QMap recheckAreaFeatures; - for ( const QString &layerId : mContext->featurePools.keys() ) + for ( const QString &layerId : mFeaturePools.keys() ) { - QgsFeaturePool *featurePool = mContext->featurePools[layerId]; + QgsFeaturePool *featurePool = mFeaturePools[layerId]; QgsCoordinateTransform t( mContext->mapCrs, featurePool->layer()->crs(), QgsProject::instance() ); recheckAreaFeatures[layerId] = featurePool->getIntersects( t.transform( recheckArea ) ); } @@ -196,14 +199,14 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo { if ( !recheckAreaFeatures.isEmpty() ) { - check->collectErrors( recheckErrors, mMessages, nullptr, recheckAreaFeatures ); + check->collectErrors( mFeaturePools, recheckErrors, mMessages, nullptr, recheckAreaFeatures ); } } else { if ( !recheckFeatures.isEmpty() ) { - check->collectErrors( recheckErrors, mMessages, nullptr, recheckFeatures ); + check->collectErrors( mFeaturePools, recheckErrors, mMessages, nullptr, recheckFeatures ); } } } @@ -269,19 +272,19 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo { for ( const QString &layerId : changes.keys() ) { - mContext->featurePools[layerId]->layer()->triggerRepaint(); + mFeaturePools[layerId]->layer()->triggerRepaint(); } } return true; } -void QgsGeometryChecker::runCheck( const QgsGeometryCheck *check ) +void QgsGeometryChecker::runCheck( const QMap &featurePools, const QgsGeometryCheck *check ) { // Run checks QList errors; QStringList messages; - check->collectErrors( errors, messages, &mProgressCounter ); + check->collectErrors( featurePools, errors, messages, &mFeedback ); mErrorListMutex.lock(); mCheckErrors.append( errors ); mMessages.append( messages ); @@ -299,5 +302,5 @@ QgsGeometryChecker::RunCheckWrapper::RunCheckWrapper( QgsGeometryChecker *instan void QgsGeometryChecker::RunCheckWrapper::operator()( const QgsGeometryCheck *check ) { - mInstance->runCheck( check ); + mInstance->runCheck( mInstance->mFeaturePools, check ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrychecker.h b/src/analysis/vector/geometry_checker/qgsgeometrychecker.h index 8ff9eef2b400..cdc6cbfde15d 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrychecker.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrychecker.h @@ -23,29 +23,33 @@ #include #include #include + #include "qgis_analysis.h" +#include "qgsfeedback.h" +#include "qgsfeatureid.h" typedef qint64 QgsFeatureId; -typedef QSet QgsFeatureIds; -struct QgsGeometryCheckerContext; +struct QgsGeometryCheckContext; class QgsGeometryCheck; class QgsGeometryCheckError; class QgsMapLayer; class QgsVectorLayer; +class QgsFeaturePool; class QMutex; class ANALYSIS_EXPORT QgsGeometryChecker : public QObject { Q_OBJECT public: - QgsGeometryChecker( const QList &checks, QgsGeometryCheckerContext *context ); + QgsGeometryChecker( const QList &checks, const QMap &featurePools ); ~QgsGeometryChecker() override; QFuture execute( int *totalSteps = nullptr ); bool fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint = false ); const QList getChecks() const { return mChecks; } QStringList getMessages() const { return mMessages; } void setMergeAttributeIndices( const QMap &mergeAttributeIndices ) { mMergeAttributeIndices = mergeAttributeIndices; } - QgsGeometryCheckerContext *getContext() const { return mContext; } + QgsGeometryCheckContext *getContext() const { return mContext; } + const QMap featurePools() const {return mFeaturePools;} signals: void errorAdded( QgsGeometryCheckError *error ); @@ -63,14 +67,15 @@ class ANALYSIS_EXPORT QgsGeometryChecker : public QObject }; QList mChecks; - QgsGeometryCheckerContext *mContext; + QgsGeometryCheckContext *mContext; QList mCheckErrors; QStringList mMessages; QMutex mErrorListMutex; QMap mMergeAttributeIndices; - QAtomicInt mProgressCounter; + QgsFeedback mFeedback; + QMap mFeaturePools; - void runCheck( const QgsGeometryCheck *check ); + void runCheck( const QMap &featurePools, const QgsGeometryCheck *check ); private slots: void emitProgressValue(); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.cpp new file mode 100644 index 000000000000..2fb7b33ba69f --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.cpp @@ -0,0 +1,190 @@ +/*************************************************************************** + qgsgeometrycheckerror.cpp + -------- + begin : September 2018 + copyright : (C) 2018 by Denis Rouzaud + email : denis@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsgeometrycheckerror.h" + +QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, + const QString &layerId, + QgsFeatureId featureId, + const QgsGeometry &geometry, + const QgsPointXY &errorLocation, + QgsVertexId vidx, + const QVariant &value, ValueType valueType ) + : mCheck( check ) + , mLayerId( layerId ) + , mFeatureId( featureId ) + , mGeometry( geometry ) + , mErrorLocation( errorLocation ) + , mVidx( vidx ) + , mValue( value ) + , mValueType( valueType ) + , mStatus( StatusPending ) +{ +} + +QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, + const QgsGeometryCheckerUtils::LayerFeature &layerFeature, + const QgsPointXY &errorLocation, + QgsVertexId vidx, + const QVariant &value, + ValueType valueType ) + : mCheck( check ) + , mLayerId( layerFeature.layerId() ) + , mFeatureId( layerFeature.feature().id() ) + , mErrorLocation( errorLocation ) + , mVidx( vidx ) + , mValue( value ) + , mValueType( valueType ) + , mStatus( StatusPending ) +{ + if ( vidx.part != -1 ) + { + mGeometry = QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( layerFeature.geometry().constGet(), vidx.part )->clone() ); + } + else + { + mGeometry = layerFeature.geometry(); + } + if ( !layerFeature.useMapCrs() ) + { + QgsVectorLayer *vl = layerFeature.layer().data(); + if ( vl ) + { + QgsCoordinateTransform ct( vl->crs(), check->context()->mapCrs, check->context()->transformContext ); + mGeometry.transform( ct ); + mErrorLocation = ct.transform( mErrorLocation ); + } + } +} + +const QgsAbstractGeometry *QgsGeometryCheckError::geometry() const +{ + return mGeometry.constGet(); +} + +QgsRectangle QgsGeometryCheckError::affectedAreaBBox() const +{ + return mGeometry.boundingBox(); +} + +void QgsGeometryCheckError::setFixed( int method ) +{ + mStatus = StatusFixed; + const QStringList methods = mCheck->resolutionMethods(); + mResolutionMessage = methods[method]; +} + +void QgsGeometryCheckError::setFixFailed( const QString &reason ) +{ + mStatus = StatusFixFailed; + mResolutionMessage = reason; +} + +bool QgsGeometryCheckError::isEqual( QgsGeometryCheckError *other ) const +{ + return other->check() == check() && + other->layerId() == layerId() && + other->featureId() == featureId() && + other->vidx() == vidx(); +} + +bool QgsGeometryCheckError::closeMatch( QgsGeometryCheckError * ) const +{ + return false; +} + +bool QgsGeometryCheckError::handleChanges( const QgsGeometryCheck::Changes &changes ) +{ + if ( status() == StatusObsolete ) + { + return false; + } + + for ( const QgsGeometryCheck::Change &change : changes.value( layerId() ).value( featureId() ) ) + { + if ( change.what == QgsGeometryCheck::ChangeFeature ) + { + if ( change.type == QgsGeometryCheck::ChangeRemoved ) + { + return false; + } + else if ( change.type == QgsGeometryCheck::ChangeChanged ) + { + // If the check is checking the feature at geometry nodes level, the + // error almost certainly invalid after a geometry change. In the other + // cases, it might likely still be valid. + return mCheck->checkType() != QgsGeometryCheck::FeatureNodeCheck; + } + } + else if ( change.what == QgsGeometryCheck::ChangePart ) + { + if ( mVidx.part == change.vidx.part ) + { + return false; + } + else if ( mVidx.part > change.vidx.part ) + { + mVidx.part += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1; + } + } + else if ( change.what == QgsGeometryCheck::ChangeRing ) + { + if ( mVidx.partEqual( change.vidx ) ) + { + if ( mVidx.ring == change.vidx.ring ) + { + return false; + } + else if ( mVidx.ring > change.vidx.ring ) + { + mVidx.ring += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1; + } + } + } + else if ( change.what == QgsGeometryCheck::ChangeNode ) + { + if ( mVidx.ringEqual( change.vidx ) ) + { + if ( mVidx.vertex == change.vidx.vertex ) + { + return false; + } + else if ( mVidx.vertex > change.vidx.vertex ) + { + mVidx.vertex += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1; + } + } + } + } + return true; +} + +void QgsGeometryCheckError::update( const QgsGeometryCheckError *other ) +{ + Q_ASSERT( mCheck == other->mCheck ); + Q_ASSERT( mLayerId == other->mLayerId ); + Q_ASSERT( mFeatureId == other->mFeatureId ); + mErrorLocation = other->mErrorLocation; + mVidx = other->mVidx; + mValue = other->mValue; + mGeometry = other->mGeometry; +} + +QgsGeometryCheck::LayerFeatureIds::LayerFeatureIds( const QMap &ids ) + : ids( ids ) +{ +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h new file mode 100644 index 000000000000..db2e2b133f94 --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h @@ -0,0 +1,116 @@ +/*************************************************************************** + qgsgeometrycheckerror.h + -------- + begin : September 2018 + copyright : (C) 2018 by Denis Rouzaud + email : denis@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYCHECKERROR_H +#define QGSGEOMETRYCHECKERROR_H + +#include "qgis_analysis.h" + +#include "qgsgeometrycheck.h" +#include "qgsgeometrycheckerutils.h" + +class QgsPointXY; + + +class ANALYSIS_EXPORT QgsGeometryCheckError +{ + public: + enum Status { StatusPending, StatusFixFailed, StatusFixed, StatusObsolete }; + enum ValueType { ValueLength, ValueArea, ValueOther }; + + QgsGeometryCheckError( const QgsGeometryCheck *check, + const QgsGeometryCheckerUtils::LayerFeature &layerFeature, + const QgsPointXY &errorLocation, + QgsVertexId vidx = QgsVertexId(), + const QVariant &value = QVariant(), + ValueType valueType = ValueOther ); + + virtual ~QgsGeometryCheckError() = default; + + const QgsGeometryCheckError &operator=( const QgsGeometryCheckError & ) = delete; + + const QgsGeometryCheck *check() const { return mCheck; } + const QString &layerId() const { return mLayerId; } + QgsFeatureId featureId() const { return mFeatureId; } + // In map units + const QgsAbstractGeometry *geometry() const; + // In map units + virtual QgsRectangle affectedAreaBBox() const; + virtual QString description() const { return mCheck->description(); } + // In map units + const QgsPointXY &location() const { return mErrorLocation; } + // Lengths, areas in map units + QVariant value() const { return mValue; } + ValueType valueType() const { return mValueType; } + const QgsVertexId &vidx() const { return mVidx; } + Status status() const { return mStatus; } + QString resolutionMessage() const { return mResolutionMessage; } + void setFixed( int method ); + void setFixFailed( const QString &reason ); + void setObsolete() { mStatus = StatusObsolete; } + + /** + * Check if this error is equal to \a other. + * Is reimplemented by subclasses with additional information, comparison + * of base information is done in parent class. + */ + virtual bool isEqual( QgsGeometryCheckError *other ) const; + + /** + * Check if this error is almost equal to \a other. + * If this returns true, it can be used to update existing errors after re-checking. + */ + virtual bool closeMatch( QgsGeometryCheckError * /*other*/ ) const; + + /** + * Update this error with the information from \other. + * Will be used to update existing errors whenever they are re-checked. + */ + virtual void update( const QgsGeometryCheckError *other ); + + /** + * Apply a list of \a changes. + */ + virtual bool handleChanges( const QgsGeometryCheck::Changes &changes ) SIP_SKIP; + + protected: + // Users of this constructor must ensure geometry and errorLocation are in map coordinates + QgsGeometryCheckError( const QgsGeometryCheck *check, + const QString &layerId, + QgsFeatureId featureId, + const QgsGeometry &geometry, + const QgsPointXY &errorLocation, + QgsVertexId vidx = QgsVertexId(), + const QVariant &value = QVariant(), + ValueType valueType = ValueOther ); + + const QgsGeometryCheck *mCheck = nullptr; + QString mLayerId; + QgsFeatureId mFeatureId; + QgsGeometry mGeometry; + QgsPointXY mErrorLocation; + QgsVertexId mVidx; + QVariant mValue; + ValueType mValueType; + Status mStatus; + QString mResolutionMessage; + +}; + +Q_DECLARE_METATYPE( QgsGeometryCheckError * ) + +#endif // QGSGEOMETRYCHECKERROR_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp index 97fadc25f1b3..0cd1548f697b 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp @@ -14,6 +14,7 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrycheckerutils.h" #include "qgsgeometry.h" #include "qgsgeometryutils.h" @@ -24,355 +25,356 @@ #include "qgssurface.h" #include "qgsvectorlayer.h" #include "qgsgeometrycheck.h" +#include "qgsfeedback.h" #include -namespace QgsGeometryCheckerUtils +QgsGeometryCheckerUtils::LayerFeature::LayerFeature( const QgsFeaturePool *pool, + const QgsFeature &feature, + const QgsGeometryCheckContext *context, + bool useMapCrs ) + : mFeaturePool( pool ) + , mFeature( feature ) + , mMapCrs( useMapCrs ) { - LayerFeature::LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, QgsGeometryCheckerContext *context, bool useMapCrs ) - : mFeaturePool( pool ) - , mFeature( feature ) - , mMapCrs( useMapCrs ) + mGeometry = feature.geometry(); + const QgsCoordinateTransform transform( pool->crs(), context->mapCrs, context->transformContext ); + if ( useMapCrs && context->mapCrs.isValid() && !transform.isShortCircuited() ) { - mGeometry = feature.geometry(); - const QgsCoordinateTransform &transform = context->layerTransform( mFeaturePool->layerPtr() ); - if ( useMapCrs && context->mapCrs.isValid() && !transform.isShortCircuited() ) - { - mGeometry.transform( transform ); - } + mGeometry.transform( transform ); } +} - const QgsFeature &LayerFeature::feature() const - { - return mFeature; - } +const QgsFeature &QgsGeometryCheckerUtils::LayerFeature::feature() const +{ + return mFeature; +} - QPointer LayerFeature::layer() const - { - return mFeaturePool->layerPtr(); - } +QPointer QgsGeometryCheckerUtils::LayerFeature::layer() const +{ + return mFeaturePool->layerPtr(); +} - QString LayerFeature::layerId() const - { - return mFeaturePool->layerId(); - } +QString QgsGeometryCheckerUtils::LayerFeature::layerId() const +{ + return mFeaturePool->layerId(); +} - const QgsGeometry &LayerFeature::geometry() const - { - return mGeometry; - } +const QgsGeometry &QgsGeometryCheckerUtils::LayerFeature::geometry() const +{ + return mGeometry; +} - QString LayerFeature::id() const - { - return QString( "%1:%2" ).arg( layer()->name() ).arg( mFeature.id() ); - } +QString QgsGeometryCheckerUtils::LayerFeature::id() const +{ + return QString( "%1:%2" ).arg( layer()->name() ).arg( mFeature.id() ); +} - bool LayerFeature::operator==( const LayerFeature &other ) const - { - return layer()->id() == other.layer()->id() && feature().id() == other.feature().id(); - } +bool QgsGeometryCheckerUtils::LayerFeature::operator==( const LayerFeature &other ) const +{ + return layer()->id() == other.layer()->id() && feature().id() == other.feature().id(); +} - bool LayerFeature::operator!=( const LayerFeature &other ) const - { - return layer()->id() != other.layer()->id() || feature().id() != other.feature().id(); - } +bool QgsGeometryCheckerUtils::LayerFeature::operator!=( const LayerFeature &other ) const +{ + return layer()->id() != other.layer()->id() || feature().id() != other.feature().id(); +} - ///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// - LayerFeatures::iterator::iterator( const QStringList::const_iterator &layerIt, const LayerFeatures *parent ) - : mLayerIt( layerIt ) - , mFeatureIt( QgsFeatureIds::const_iterator() ) - , mParent( parent ) - { - nextLayerFeature( true ); - } +QgsGeometryCheckerUtils::LayerFeatures::iterator::iterator( const QStringList::const_iterator &layerIt, const LayerFeatures *parent ) + : mLayerIt( layerIt ) + , mFeatureIt( QgsFeatureIds::const_iterator() ) + , mParent( parent ) +{ + nextLayerFeature( true ); +} - bool LayerFeature::useMapCrs() const - { - return mMapCrs; - } - LayerFeatures::iterator::~iterator() - { - delete mCurrentFeature; - } +bool QgsGeometryCheckerUtils::LayerFeature::useMapCrs() const +{ + return mMapCrs; +} +QgsGeometryCheckerUtils::LayerFeatures::iterator::~iterator() +{ + delete mCurrentFeature; +} - const LayerFeatures::iterator &LayerFeatures::iterator::operator++() +const QgsGeometryCheckerUtils::LayerFeatures::iterator &QgsGeometryCheckerUtils::LayerFeatures::iterator::operator++() +{ + nextLayerFeature( false ); + return *this; +} +bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayerFeature( bool begin ) +{ + if ( !begin && nextFeature( false ) ) { - nextLayerFeature( false ); - return *this; + return true; } - bool LayerFeatures::iterator::nextLayerFeature( bool begin ) + while ( nextLayer( begin ) ) { - if ( !begin && nextFeature( false ) ) + begin = false; + if ( nextFeature( true ) ) { return true; } - while ( nextLayer( begin ) ) - { - begin = false; - if ( nextFeature( true ) ) - { - return true; - } - } - // End - mFeatureIt = QgsFeatureIds::const_iterator(); - delete mCurrentFeature; - mCurrentFeature = nullptr; - return false; } - - bool LayerFeatures::iterator::nextLayer( bool begin ) + // End + mFeatureIt = QgsFeatureIds::const_iterator(); + delete mCurrentFeature; + mCurrentFeature = nullptr; + return false; +} + +bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayer( bool begin ) +{ + if ( !begin ) + { + ++mLayerIt; + } + while ( true ) { - if ( !begin ) + if ( mLayerIt == mParent->mLayerIds.end() ) { - ++mLayerIt; + break; } - while ( true ) + if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) ) { - if ( mLayerIt == mParent->mLayerIds.end() ) - { - break; - } - if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) ) - { - mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin(); - return true; - } - ++mLayerIt; + mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin(); + return true; } - return false; + ++mLayerIt; } + return false; +} - bool LayerFeatures::iterator::nextFeature( bool begin ) +bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextFeature( bool begin ) +{ + QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt]; + const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt]; + if ( !begin ) + { + ++mFeatureIt; + } + while ( true ) { - QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt]; - const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt]; - if ( !begin ) + if ( mFeatureIt == featureIds.end() ) { - ++mFeatureIt; + break; } - while ( true ) + if ( mParent->mFeedback ) + mParent->mFeedback->setProgress( mParent->mFeedback->progress() + 1.0 ); + QgsFeature feature; + if ( featurePool->getFeature( *mFeatureIt, feature ) && feature.geometry() && feature.geometry().constGet() ) { - if ( mFeatureIt == featureIds.end() ) - { - break; - } - if ( mParent->mProgressCounter ) - mParent->mProgressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( featurePool->getFeature( *mFeatureIt, feature ) && feature.geometry() && feature.geometry().constGet() ) - { - delete mCurrentFeature; - mCurrentFeature = new LayerFeature( mParent->mFeaturePools[*mLayerIt], feature, mParent->mContext, mParent->mUseMapCrs ); - return true; - } - ++mFeatureIt; + delete mCurrentFeature; + mCurrentFeature = new LayerFeature( mParent->mFeaturePools[*mLayerIt], feature, mParent->mContext, mParent->mUseMapCrs ); + return true; } - return false; + ++mFeatureIt; } + return false; +} ///////////////////////////////////////////////////////////////////////////// - LayerFeatures::LayerFeatures( const QMap &featurePools, - const QMap &featureIds, - const QList &geometryTypes, - QAtomicInt *progressCounter, - QgsGeometryCheckerContext *context, - bool useMapCrs ) - : mFeaturePools( featurePools ) - , mFeatureIds( featureIds ) - , mLayerIds( featurePools.keys() ) - , mGeometryTypes( geometryTypes ) - , mProgressCounter( progressCounter ) - , mContext( context ) - , mUseMapCrs( useMapCrs ) - {} - - LayerFeatures::LayerFeatures( const QMap &featurePools, - const QList &layerIds, const QgsRectangle &extent, - const QList &geometryTypes, - QgsGeometryCheckerContext *context ) - : mFeaturePools( featurePools ) - , mLayerIds( layerIds ) - , mExtent( extent ) - , mGeometryTypes( geometryTypes ) - , mContext( context ) - , mUseMapCrs( true ) +QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap &featurePools, + const QMap &featureIds, + const QList &geometryTypes, + QgsFeedback *feedback, + const QgsGeometryCheckContext *context, + bool useMapCrs ) + : mFeaturePools( featurePools ) + , mFeatureIds( featureIds ) + , mLayerIds( featurePools.keys() ) + , mGeometryTypes( geometryTypes ) + , mFeedback( feedback ) + , mContext( context ) + , mUseMapCrs( useMapCrs ) +{} + +QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap &featurePools, + const QList &layerIds, const QgsRectangle &extent, + const QList &geometryTypes, + const QgsGeometryCheckContext *context ) + : mFeaturePools( featurePools ) + , mLayerIds( layerIds ) + , mExtent( extent ) + , mGeometryTypes( geometryTypes ) + , mContext( context ) + , mUseMapCrs( true ) +{ + for ( const QString &layerId : layerIds ) { - for ( const QString &layerId : layerIds ) + const QgsFeaturePool *featurePool = featurePools[layerId]; + if ( geometryTypes.contains( featurePool->geometryType() ) ) { - const QgsFeaturePool *featurePool = featurePools[layerId]; - if ( geometryTypes.contains( featurePool->geometryType() ) ) - { - mFeatureIds.insert( layerId, featurePool->getIntersects( context->layerTransform( featurePool->layer() ).transform( extent, QgsCoordinateTransform::ReverseTransform ) ) ); - } - else - { - mFeatureIds.insert( layerId, QgsFeatureIds() ); - } + QgsCoordinateTransform ct( featurePool->crs(), context->mapCrs, context->transformContext ); + mFeatureIds.insert( layerId, featurePool->getIntersects( ct.transform( extent, QgsCoordinateTransform::ReverseTransform ) ) ); + } + else + { + mFeatureIds.insert( layerId, QgsFeatureIds() ); } } +} - ///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +std::unique_ptr QgsGeometryCheckerUtils::createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance ) +{ + return qgis::make_unique( geometry, tolerance ); +} - std::unique_ptr createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance ) +QgsAbstractGeometry *QgsGeometryCheckerUtils::getGeomPart( QgsAbstractGeometry *geom, int partIdx ) +{ + if ( dynamic_cast( geom ) ) { - return qgis::make_unique( geometry, tolerance ); + return static_cast( geom )->geometryN( partIdx ); } + return geom; +} - QgsAbstractGeometry *getGeomPart( QgsAbstractGeometry *geom, int partIdx ) +const QgsAbstractGeometry *QgsGeometryCheckerUtils::getGeomPart( const QgsAbstractGeometry *geom, int partIdx ) +{ + if ( dynamic_cast( geom ) ) { - if ( dynamic_cast( geom ) ) - { - return static_cast( geom )->geometryN( partIdx ); - } - return geom; + return static_cast( geom )->geometryN( partIdx ); } + return geom; +} - const QgsAbstractGeometry *getGeomPart( const QgsAbstractGeometry *geom, int partIdx ) +QList QgsGeometryCheckerUtils::polygonRings( const QgsPolygon *polygon ) +{ + QList rings; + if ( const QgsLineString *exterior = dynamic_cast( polygon->exteriorRing() ) ) { - if ( dynamic_cast( geom ) ) - { - return static_cast( geom )->geometryN( partIdx ); - } - return geom; + rings.append( exterior ); } - - QList polygonRings( const QgsPolygon *polygon ) + for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt ) { - QList rings; - if ( const QgsLineString *exterior = dynamic_cast( polygon->exteriorRing() ) ) - { - rings.append( exterior ); - } - for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt ) + if ( const QgsLineString *interior = dynamic_cast( polygon->interiorRing( iInt ) ) ) { - if ( const QgsLineString *interior = dynamic_cast( polygon->interiorRing( iInt ) ) ) - { - rings.append( interior ); - } + rings.append( interior ); } - return rings; } + return rings; +} - void filter1DTypes( QgsAbstractGeometry *geom ) +void QgsGeometryCheckerUtils::filter1DTypes( QgsAbstractGeometry *geom ) +{ + if ( dynamic_cast( geom ) ) { - if ( dynamic_cast( geom ) ) + QgsGeometryCollection *geomCollection = static_cast( geom ); + for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart ) { - QgsGeometryCollection *geomCollection = static_cast( geom ); - for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart ) + if ( !dynamic_cast( geomCollection->geometryN( iPart ) ) ) { - if ( !dynamic_cast( geomCollection->geometryN( iPart ) ) ) - { - geomCollection->removeGeometry( iPart ); - } + geomCollection->removeGeometry( iPart ); } } } +} - static inline double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q ) - { - double nom = std::fabs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() ); - double dx = p2.x() - p1.x(); - double dy = p2.y() - p1.y(); - return nom / std::sqrt( dx * dx + dy * dy ); - } +double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q ) +{ + double nom = std::fabs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() ); + double dx = p2.x() - p1.x(); + double dy = p2.y() - p1.y(); + return nom / std::sqrt( dx * dx + dy * dy ); +} - bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities ) +bool QgsGeometryCheckerUtils::pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities ) +{ + int nVerts = line->vertexCount(); + for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i ) { - int nVerts = line->vertexCount(); - for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i ) + QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) ); + QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) ); + double dist = pointLineDist( p1, p2, p ); + if ( dist < tol ) { - QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) ); - QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) ); - double dist = pointLineDist( p1, p2, p ); - if ( dist < tol ) - { - return true; - } + return true; } - return false; } + return false; +} - QList lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol ) +QList QgsGeometryCheckerUtils::lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol ) +{ + QList intersections; + QgsPoint inter; + bool intersection = false; + for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i ) { - QList intersections; - QgsPoint inter; - bool intersection = false; - for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i ) + for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j ) { - for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j ) + QgsPoint p1 = line1->vertexAt( QgsVertexId( 0, 0, i ) ); + QgsPoint p2 = line1->vertexAt( QgsVertexId( 0, 0, i + 1 ) ); + QgsPoint q1 = line2->vertexAt( QgsVertexId( 0, 0, j ) ); + QgsPoint q2 = line2->vertexAt( QgsVertexId( 0, 0, j + 1 ) ); + if ( QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, inter, intersection, tol ) ) { - QgsPoint p1 = line1->vertexAt( QgsVertexId( 0, 0, i ) ); - QgsPoint p2 = line1->vertexAt( QgsVertexId( 0, 0, i + 1 ) ); - QgsPoint q1 = line2->vertexAt( QgsVertexId( 0, 0, j ) ); - QgsPoint q2 = line2->vertexAt( QgsVertexId( 0, 0, j + 1 ) ); - if ( QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, inter, intersection, tol ) ) - { - intersections.append( inter ); - } + intersections.append( inter ); } } - return intersections; } + return intersections; +} - double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ) - { - double len = 0; +double QgsGeometryCheckerUtils::sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ) +{ + double len = 0; - // Test every pair of segments for shared edges - for ( int iPart1 = 0, nParts1 = geom1->partCount(); iPart1 < nParts1; ++iPart1 ) + // Test every pair of segments for shared edges + for ( int iPart1 = 0, nParts1 = geom1->partCount(); iPart1 < nParts1; ++iPart1 ) + { + for ( int iRing1 = 0, nRings1 = geom1->ringCount( iPart1 ); iRing1 < nRings1; ++iRing1 ) { - for ( int iRing1 = 0, nRings1 = geom1->ringCount( iPart1 ); iRing1 < nRings1; ++iRing1 ) + for ( int iVert1 = 0, jVert1 = 1, nVerts1 = geom1->vertexCount( iPart1, iRing1 ); jVert1 < nVerts1; iVert1 = jVert1++ ) { - for ( int iVert1 = 0, jVert1 = 1, nVerts1 = geom1->vertexCount( iPart1, iRing1 ); jVert1 < nVerts1; iVert1 = jVert1++ ) + QgsPoint p1 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, iVert1 ) ); + QgsPoint p2 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, jVert1 ) ); + double lambdap1 = 0.; + double lambdap2 = std::sqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) ); + QgsVector d; + try { - QgsPoint p1 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, iVert1 ) ); - QgsPoint p2 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, jVert1 ) ); - double lambdap1 = 0.; - double lambdap2 = std::sqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) ); - QgsVector d; - try - { - d = QgsVector( p2.x() - p1.x(), p2.y() - p1.y() ).normalized(); - } - catch ( const QgsException & ) - { - // Edge has zero length, skip - continue; - } + d = QgsVector( p2.x() - p1.x(), p2.y() - p1.y() ).normalized(); + } + catch ( const QgsException & ) + { + // Edge has zero length, skip + continue; + } - for ( int iPart2 = 0, nParts2 = geom2->partCount(); iPart2 < nParts2; ++iPart2 ) + for ( int iPart2 = 0, nParts2 = geom2->partCount(); iPart2 < nParts2; ++iPart2 ) + { + for ( int iRing2 = 0, nRings2 = geom2->ringCount( iPart2 ); iRing2 < nRings2; ++iRing2 ) { - for ( int iRing2 = 0, nRings2 = geom2->ringCount( iPart2 ); iRing2 < nRings2; ++iRing2 ) + for ( int iVert2 = 0, jVert2 = 1, nVerts2 = geom2->vertexCount( iPart2, iRing2 ); jVert2 < nVerts2; iVert2 = jVert2++ ) { - for ( int iVert2 = 0, jVert2 = 1, nVerts2 = geom2->vertexCount( iPart2, iRing2 ); jVert2 < nVerts2; iVert2 = jVert2++ ) - { - QgsPoint q1 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, iVert2 ) ); - QgsPoint q2 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, jVert2 ) ); + QgsPoint q1 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, iVert2 ) ); + QgsPoint q2 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, jVert2 ) ); - // Check whether q1 and q2 are on the line p1, p - if ( pointLineDist( p1, p2, q1 ) <= tol && pointLineDist( p1, p2, q2 ) <= tol ) + // Check whether q1 and q2 are on the line p1, p + if ( pointLineDist( p1, p2, q1 ) <= tol && pointLineDist( p1, p2, q2 ) <= tol ) + { + // Get length common edge + double lambdaq1 = QgsVector( q1.x() - p1.x(), q1.y() - p1.y() ) * d; + double lambdaq2 = QgsVector( q2.x() - p1.x(), q2.y() - p1.y() ) * d; + if ( lambdaq1 > lambdaq2 ) { - // Get length common edge - double lambdaq1 = QgsVector( q1.x() - p1.x(), q1.y() - p1.y() ) * d; - double lambdaq2 = QgsVector( q2.x() - p1.x(), q2.y() - p1.y() ) * d; - if ( lambdaq1 > lambdaq2 ) - { - std::swap( lambdaq1, lambdaq2 ); - } - double lambda1 = std::max( lambdaq1, lambdap1 ); - double lambda2 = std::min( lambdaq2, lambdap2 ); - len += std::max( 0., lambda2 - lambda1 ); + std::swap( lambdaq1, lambdaq2 ); } + double lambda1 = std::max( lambdaq1, lambdap1 ); + double lambda2 = std::min( lambdaq2, lambdap2 ); + len += std::max( 0., lambda2 - lambda1 ); } } } } } } - return len; } - -} // QgsGeometryCheckerUtils + return len; +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h index 86ed7fc49869..ab03b416f997 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h @@ -14,181 +14,194 @@ * * ***************************************************************************/ -#define SIP_NO_FILE - #ifndef QGS_GEOMETRYCHECKERUTILS_H #define QGS_GEOMETRYCHECKERUTILS_H +#include "qgis_analysis.h" #include "qgsfeature.h" #include "geometry/qgsabstractgeometry.h" #include "geometry/qgspoint.h" +#include "qgsgeometrycheckcontext.h" #include class QgsGeometryEngine; class QgsFeaturePool; -struct QgsGeometryCheckerContext; +class QgsFeedback; -namespace QgsGeometryCheckerUtils +class ANALYSIS_EXPORT QgsGeometryCheckerUtils { - class LayerFeature - { - public: - - /** - * Create a new layer/feature combination. - * The layer is defined by \a pool, \a feature needs to be from this layer. - * If \a useMapCrs is True, geometries will be reprojected to the mapCrs defined - * in \a context. - */ - LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, QgsGeometryCheckerContext *context, bool useMapCrs ); + public: + class ANALYSIS_EXPORT LayerFeature + { + public: + + /** + * Create a new layer/feature combination. + * The layer is defined by \a pool, \a feature needs to be from this layer. + * If \a useMapCrs is True, geometries will be reprojected to the mapCrs defined + * in \a context. + */ + LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, const QgsGeometryCheckContext *context, bool useMapCrs ); + + /** + * Returns the feature. + * The geometry will not be reprojected regardless of useMapCrs. + */ + const QgsFeature &feature() const; + + /** + * The layer. + */ + QPointer layer() const SIP_SKIP; + + /** + * The layer id. + */ + QString layerId() const; + + /** + * Returns the geometry of this feature. + * If useMapCrs was specified, it will already be reprojected into the + * CRS specified in the context specified in the constructor. + */ + const QgsGeometry &geometry() const; + QString id() const; + bool operator==( const LayerFeature &other ) const; + bool operator!=( const LayerFeature &other ) const; + + /** + * Returns if the geometry is reprojected to the map CRS or not. + */ + bool useMapCrs() const; + + private: + const QgsFeaturePool *mFeaturePool; + QgsFeature mFeature; + QgsGeometry mGeometry; + bool mMapCrs; + }; + + class ANALYSIS_EXPORT LayerFeatures + { + public: +#ifndef SIP_RUN + LayerFeatures( const QMap &featurePools, + const QMap &featureIds, + const QList &geometryTypes, + QgsFeedback *feedback, + const QgsGeometryCheckContext *context, + bool useMapCrs = false ); + + LayerFeatures( const QMap &featurePools, + const QList &layerIds, const QgsRectangle &extent, + const QList &geometryTypes, + const QgsGeometryCheckContext *context ); + + class iterator + { + public: + iterator( const QList::const_iterator &layerIt, const LayerFeatures *parent ); + ~iterator(); + const iterator &operator++(); + iterator operator++( int ) { iterator tmp( *this ); ++*this; return tmp; } + const LayerFeature &operator*() const { Q_ASSERT( mCurrentFeature ); return *mCurrentFeature; } + bool operator!=( const iterator &other ) { return mLayerIt != other.mLayerIt || mFeatureIt != other.mFeatureIt; } + + private: + bool nextLayerFeature( bool begin ); + bool nextLayer( bool begin ); + bool nextFeature( bool begin ); + QList::const_iterator mLayerIt; + QgsFeatureIds::const_iterator mFeatureIt; + const LayerFeatures *mParent; + const LayerFeature *mCurrentFeature = nullptr; + }; + + iterator begin() const { return iterator( mLayerIds.constBegin(), this ); } + iterator end() const { return iterator( mLayerIds.end(), this ); } + +#endif + + private: +#ifdef SIP_RUN + LayerFeatures(); +#endif + QMap mFeaturePools; + QMap mFeatureIds; + QList mLayerIds; + QgsRectangle mExtent; + QList mGeometryTypes; + QgsFeedback *mFeedback = nullptr; + const QgsGeometryCheckContext *mContext = nullptr; + bool mUseMapCrs = true; + }; + +#ifndef SIP_RUN + + static std::unique_ptr createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance ); + + static QgsAbstractGeometry *getGeomPart( QgsAbstractGeometry *geom, int partIdx ); + static const QgsAbstractGeometry *getGeomPart( const QgsAbstractGeometry *geom, int partIdx ); + + static QList polygonRings( const QgsPolygon *polygon ); + + static void filter1DTypes( QgsAbstractGeometry *geom ); + + /** + * Returns the number of points in a polyline, accounting for duplicate start and end point if the polyline is closed + * \param polyLine The polyline + * \returns The number of distinct points of the polyline + */ + static inline int polyLineSize( const QgsAbstractGeometry *geom, int iPart, int iRing, bool *isClosed = nullptr ) + { + if ( !geom->isEmpty() ) + { + int nVerts = geom->vertexCount( iPart, iRing ); + QgsPoint front = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ); + QgsPoint back = geom->vertexAt( QgsVertexId( iPart, iRing, nVerts - 1 ) ); + bool closed = back == front; + if ( isClosed ) + *isClosed = closed; + return closed ? nVerts - 1 : nVerts; + } + else + { + if ( isClosed ) + *isClosed = true; + return 0; + } + } - /** - * Returns the feature. - * The geometry will not be reprojected regardless of useMapCrs. - */ - const QgsFeature &feature() const; + static bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities = false ); - /** - * The layer. - */ - QPointer layer() const; + static QList lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol ); - /** - * The layer id. - */ - QString layerId() const; + static double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ); - /** - * Returns the geometry of this feature. - * If useMapCrs was specified, it will already be reprojected into the - * CRS specified in the context specified in the constructor. + /** + * \brief Determine whether two points are equal up to the specified tolerance + * \param p1 The first point + * \param p2 The second point + * \param tol The tolerance + * \returns Whether the points are equal */ - const QgsGeometry &geometry() const; - QString id() const; - bool operator==( const LayerFeature &other ) const; - bool operator!=( const LayerFeature &other ) const; + static inline bool pointsFuzzyEqual( const QgsPointXY &p1, const QgsPointXY &p2, double tol ) + { + double dx = p1.x() - p2.x(), dy = p1.y() - p2.y(); + return ( dx * dx + dy * dy ) < tol * tol; + } - /** - * Returns if the geometry is reprojected to the map CRS or not. - */ - bool useMapCrs() const; - - private: - const QgsFeaturePool *mFeaturePool; - QgsFeature mFeature; - QgsGeometry mGeometry; - bool mMapCrs; - }; - - class LayerFeatures - { - public: - LayerFeatures( const QMap &featurePools, - const QMap &featureIds, - const QList &geometryTypes, - QAtomicInt *progressCounter, - QgsGeometryCheckerContext *context, - bool useMapCrs = false ); - - LayerFeatures( const QMap &featurePools, - const QList &layerIds, const QgsRectangle &extent, - const QList &geometryTypes, - QgsGeometryCheckerContext *context ); - - class iterator - { - public: - iterator( const QList::const_iterator &layerIt, const LayerFeatures *parent ); - ~iterator(); - const iterator &operator++(); - iterator operator++( int ) { iterator tmp( *this ); ++*this; return tmp; } - const LayerFeature &operator*() const { Q_ASSERT( mCurrentFeature ); return *mCurrentFeature; } - bool operator!=( const iterator &other ) { return mLayerIt != other.mLayerIt || mFeatureIt != other.mFeatureIt; } - - private: - bool nextLayerFeature( bool begin ); - bool nextLayer( bool begin ); - bool nextFeature( bool begin ); - QList::const_iterator mLayerIt; - QgsFeatureIds::const_iterator mFeatureIt; - const LayerFeatures *mParent; - const LayerFeature *mCurrentFeature = nullptr; - }; - - iterator begin() const { return iterator( mLayerIds.constBegin(), this ); } - iterator end() const { return iterator( mLayerIds.end(), this ); } - private: - QMap mFeaturePools; - QMap mFeatureIds; - QList mLayerIds; - QgsRectangle mExtent; - QList mGeometryTypes; - QAtomicInt *mProgressCounter = nullptr; - QgsGeometryCheckerContext *mContext = nullptr; - bool mUseMapCrs = true; - }; - - std::unique_ptr createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance ); - - QgsAbstractGeometry *getGeomPart( QgsAbstractGeometry *geom, int partIdx ); - const QgsAbstractGeometry *getGeomPart( const QgsAbstractGeometry *geom, int partIdx ); - - QList polygonRings( const QgsPolygon *polygon ); - - void filter1DTypes( QgsAbstractGeometry *geom ); - - /** - * Returns the number of points in a polyline, accounting for duplicate start and end point if the polyline is closed - * \param polyLine The polyline - * \returns The number of distinct points of the polyline - */ - inline int polyLineSize( const QgsAbstractGeometry *geom, int iPart, int iRing, bool *isClosed = nullptr ) - { - if ( !geom->isEmpty() ) + static inline bool canDeleteVertex( const QgsAbstractGeometry *geom, int iPart, int iRing ) { int nVerts = geom->vertexCount( iPart, iRing ); QgsPoint front = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ); QgsPoint back = geom->vertexAt( QgsVertexId( iPart, iRing, nVerts - 1 ) ); bool closed = back == front; - if ( isClosed ) - *isClosed = closed; - return closed ? nVerts - 1 : nVerts; + return closed ? nVerts > 4 : nVerts > 2; } - else - { - if ( isClosed ) - *isClosed = true; - return 0; - } - } - - bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities = false ); - QList lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol ); +#endif - double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol ); - - /** - * \brief Determine whether two points are equal up to the specified tolerance - * \param p1 The first point - * \param p2 The second point - * \param tol The tolerance - * \returns Whether the points are equal - */ - inline bool pointsFuzzyEqual( const QgsPointXY &p1, const QgsPointXY &p2, double tol ) - { - double dx = p1.x() - p2.x(), dy = p1.y() - p2.y(); - return ( dx * dx + dy * dy ) < tol * tol; - } - - inline bool canDeleteVertex( const QgsAbstractGeometry *geom, int iPart, int iRing ) - { - int nVerts = geom->vertexCount( iPart, iRing ); - QgsPoint front = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ); - QgsPoint back = geom->vertexAt( QgsVertexId( iPart, iRing, nVerts - 1 ) ); - bool closed = back == front; - return closed ? nVerts > 4 : nVerts > 2; - } -} // QgsGeometryCheckerUtils +}; // QgsGeometryCheckerUtils #endif // QGS_GEOMETRYCHECKERUTILS_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h new file mode 100644 index 000000000000..cec0f2cb8633 --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h @@ -0,0 +1,92 @@ +/*************************************************************************** + qgsgeometrycheckfactory.h + -------------------------------------- + Date : September 2018 + Copyright : (C) 2018 Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYCHECKFACTORY_H +#define QGSGEOMETRYCHECKFACTORY_H + +#include +#include +#include + +#include "qgsgeometrycheck.h" +#include "qgis_sip.h" +#include "qgis_analysis.h" + +#include "qgsgeometryselfintersectioncheck.h" +#include "qgssinglegeometrycheck.h" + +class QgsGeometryCheck; +class QgsSingleGeometryCheck; + +struct QgsGeometryCheckContext; + +/** + * \ingroup analysis + */ +class ANALYSIS_EXPORT QgsGeometryCheckFactory SIP_ABSTRACT +{ + public: + + /** + * Destructor + * + * Deletes all the registered checks + */ + virtual ~QgsGeometryCheckFactory() = default; + + virtual QgsGeometryCheck *createGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) const = 0 SIP_FACTORY; + + virtual QString id() const = 0; + + virtual QString description() const = 0; + + virtual bool isCompatible( QgsVectorLayer *layer ) const = 0; + + virtual QgsGeometryCheck::Flags flags() const = 0; +}; + +template +class QgsGeometryCheckFactoryT : public QgsGeometryCheckFactory +{ + public: + QgsGeometryCheck *createGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) const override + { + return new T( context, configuration ); + } + + QString description() const override + { + return T::factoryDescription(); + } + + QString id() const override + { + return T::factoryId(); + } + + bool isCompatible( QgsVectorLayer *layer ) const override + { + return T::factoryIsCompatible( layer ); + } + + QgsGeometryCheck::Flags flags() const override + { + return T::factoryFlags(); + } + +}; + + +#endif // QGSGEOMETRYCHECKFACTORY_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.cpp new file mode 100644 index 000000000000..7dfdd7ef337d --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + qgsgeometrycheckregistry.cpp + -------------------------------------- + Date : September 2018 + Copyright : (C) 2018 Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsgeometrycheckregistry.h" +#include "qgsgeometrycheckfactory.h" +#include "qgis.h" + + +QgsGeometryCheckRegistry::QgsGeometryCheckRegistry() +{ + +} + +void QgsGeometryCheckRegistry::initialize() +{ + +} + +QgsGeometryCheckRegistry::~QgsGeometryCheckRegistry() +{ + qDeleteAll( mGeometryCheckFactories.values() ); +} + +QgsGeometryCheck *QgsGeometryCheckRegistry::geometryCheck( const QString &checkId, QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfig ) +{ + QgsGeometryCheckFactory *factory = mGeometryCheckFactories.value( checkId ); + if ( factory ) + return factory->createGeometryCheck( context, geometryCheckConfig ); + else + return nullptr; +} + +QList QgsGeometryCheckRegistry::geometryCheckFactories( QgsVectorLayer *layer, QgsGeometryCheck::Flags flags ) const +{ + QList factories; + for ( QgsGeometryCheckFactory *factory : mGeometryCheckFactories ) + { + if ( ( factory->flags() & flags ) == flags && factory->isCompatible( layer ) ) + factories << factory; + } + return factories; +} + +void QgsGeometryCheckRegistry::registerGeometryCheck( QgsGeometryCheckFactory *checkFactory ) +{ + mGeometryCheckFactories.insert( checkFactory->id(), checkFactory ); +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h new file mode 100644 index 000000000000..4adbecd4a473 --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h @@ -0,0 +1,72 @@ +/*************************************************************************** + qgsgeometrycheckregistry.h + -------------------------------------- + Date : September 2018 + Copyright : (C) 2018 Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYCHECKREGISTRY_H +#define QGSGEOMETRYCHECKREGISTRY_H + +#include +#include +#include + +#include "qgis_sip.h" +#include "qgis_analysis.h" +#include "qgsgeometrycheck.h" + +class QgsGeometryCheckFactory; +struct QgsGeometryCheckContext; + + +/** + * \ingroup analysis + * This class manages all known geometry check factories. + * + * QgsGeometryCheckRegistry is not usually directly created, but rather accessed through + * QgsAnalysis::geometryCheckRegistry(). + */ +class ANALYSIS_EXPORT QgsGeometryCheckRegistry +{ + public: + + /** + * Constructor for QgsGeometryCheckRegistry. QgsGeometryCheckRegistry is not usually directly created, but rather accessed through + * QgsAnalysis::geometryCheckRegistry(). + */ + QgsGeometryCheckRegistry(); + + void initialize(); + + /** + * Destructor + * + * Deletes all the registered checks + */ + ~QgsGeometryCheckRegistry(); + + QgsGeometryCheck *geometryCheck( const QString &checkId, QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfig ) SIP_TRANSFER; + + /** + * Get all geometry check factories that are compatible with \a layer and have all of the \a flags set. + * + * \since QGIS 3.4 + */ + QList geometryCheckFactories( QgsVectorLayer *layer, QgsGeometryCheck::Flags flags = nullptr ) const; + + void registerGeometryCheck( QgsGeometryCheckFactory *checkFactory SIP_TRANSFER ); + + private: + QMap mGeometryCheckFactories; +}; + +#endif // QGSGEOMETRYCHECKREGISTRY_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp index 032a61739654..651449ac42a7 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp @@ -13,16 +13,17 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryengine.h" #include "qgsgeometrycontainedcheck.h" #include "qgsfeaturepool.h" #include "qgsvectorlayer.h" -void QgsGeometryContainedCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryContainedCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA ) { QgsRectangle bboxA = layerFeatureA.geometry().boundingBox(); @@ -32,7 +33,7 @@ void QgsGeometryContainedCheck::collectErrors( QList &e messages.append( tr( "Contained check failed for (%1): the geometry is invalid" ).arg( layerFeatureA.id() ) ); continue; } - QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( mContext->featurePools, featureIds.keys(), bboxA, mCompatibleGeometryTypes, mContext ); + QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, featureIds.keys(), bboxA, compatibleGeometryTypes(), mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB ) { if ( layerFeatureA == layerFeatureB ) @@ -59,11 +60,11 @@ void QgsGeometryContainedCheck::collectErrors( QList &e } } -void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryContainedCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsGeometryContainedCheckError *containerError = static_cast( error ); - QgsFeaturePool *featurePoolA = mContext->featurePools[ error->layerId() ]; - QgsFeaturePool *featurePoolB = mContext->featurePools[ containerError->containingFeature().first ]; + QgsFeaturePool *featurePoolA = featurePools[ error->layerId() ]; + QgsFeaturePool *featurePoolB = featurePools[ containerError->containingFeature().first ]; QgsFeature featureA; QgsFeature featureB; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h index d19a1ac6f98a..889abf4ef6ba 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h @@ -20,6 +20,7 @@ #include "qgsgeometrycheck.h" #include "qgsvectorlayer.h" +#include "qgsgeometrycheckerror.h" class ANALYSIS_EXPORT QgsGeometryContainedCheckError : public QgsGeometryCheckError { @@ -41,7 +42,8 @@ class ANALYSIS_EXPORT QgsGeometryContainedCheckError : public QgsGeometryCheckEr static_cast( other )->containingFeature() == containingFeature(); } - QString description() const override { return QApplication::translate( "QgsGeometryContainedCheckError", "Within feature" ); } + QString factoryDescription() const { return QApplication::translate( "QgsGeometryContainedCheckError", "Within feature" ); } + QString description() const override { return factoryDescription(); } private: QPair mContainingFeature; @@ -50,13 +52,18 @@ class ANALYSIS_EXPORT QgsGeometryContainedCheckError : public QgsGeometryCheckEr class ANALYSIS_EXPORT QgsGeometryContainedCheck : public QgsGeometryCheck { public: - explicit QgsGeometryContainedCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + explicit QgsGeometryContainedCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureCheck, context, configuration ) {} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Within" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryContainedCheck" ); } + QString factoryDescription() const { return tr( "Within" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryContainedCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { Delete, NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp index ff4269ce019d..b0a094d26ad5 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp @@ -13,14 +13,17 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrydanglecheck.h" #include "qgslinestring.h" #include "qgsvectorlayer.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryDangleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryDangleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + Q_UNUSED( messages ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -47,7 +50,7 @@ void QgsGeometryDangleCheck::collectErrors( QList &erro } // Check whether endpoints line on another line in the layer - QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, QList() << layerFeature.layer()->id(), line->boundingBox(), {QgsWkbTypes::LineGeometry}, mContext ); + QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, QList() << layerFeature.layer()->id(), line->boundingBox(), {QgsWkbTypes::LineGeometry}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) { const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet(); @@ -91,8 +94,10 @@ void QgsGeometryDangleCheck::collectErrors( QList &erro } } -void QgsGeometryDangleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +void QgsGeometryDangleCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { + Q_UNUSED( featurePools ) + if ( method == NoChange ) { error->setFixed( method ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h index dcc55246817b..63cdc28e0464 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h @@ -23,14 +23,19 @@ class ANALYSIS_EXPORT QgsGeometryDangleCheck : public QgsGeometryCheck { public: - QgsGeometryDangleCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry}, context ) + QgsGeometryDangleCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Dangle" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryDangleCheck" ); } + QString factoryDescription() const { return tr( "Dangle" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryDangleCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.cpp index cc44aaeb5ffc..00258722fbd6 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.cpp @@ -13,13 +13,18 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrydegeneratepolygoncheck.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryDegeneratePolygonCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const + +void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + Q_UNUSED( messages ) + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -37,9 +42,9 @@ void QgsGeometryDegeneratePolygonCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryDegeneratePolygonCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { @@ -71,7 +76,7 @@ void QgsGeometryDegeneratePolygonCheck::fixError( QgsGeometryCheckError *error, } else if ( method == DeleteRing ) { - deleteFeatureGeometryRing( error->layerId(), feature, vidx.part, vidx.ring, changes ); + deleteFeatureGeometryRing( featurePools, error->layerId(), feature, vidx.part, vidx.ring, changes ); error->setFixed( method ); } else diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h index 8185f6b9bef3..8641aeb71f6f 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h @@ -23,13 +23,18 @@ class ANALYSIS_EXPORT QgsGeometryDegeneratePolygonCheck : public QgsGeometryCheck { public: - explicit QgsGeometryDegeneratePolygonCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PolygonGeometry}, context ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + explicit QgsGeometryDegeneratePolygonCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Polygon with less than three nodes" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryDegeneratePolygonCheck" ); } + QString factoryDescription() const { return tr( "Polygon with less than three nodes" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryDegeneratePolygonCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { DeleteRing, NoChange }; private: diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp index 2bd37b686843..9dd21de9a529 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp @@ -13,6 +13,7 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryengine.h" #include "qgsgeometryduplicatecheck.h" #include "qgsspatialindex.h" @@ -37,10 +38,10 @@ QString QgsGeometryDuplicateCheckError::duplicatesString( const QMap &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryDuplicateCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true ); + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); QList layerIds = featureIds.keys(); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA ) { @@ -57,7 +58,7 @@ void QgsGeometryDuplicateCheck::collectErrors( QList &e QMap> duplicates; QgsWkbTypes::GeometryType geomType = layerFeatureA.feature().geometry().type(); - QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( mContext->featurePools, QList() << layerFeatureA.layer()->id() << layerIds, bboxA, {geomType}, mContext ); + QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList() << layerFeatureA.layer()->id() << layerIds, bboxA, {geomType}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB ) { // > : only report overlaps within same layer once @@ -79,14 +80,14 @@ void QgsGeometryDuplicateCheck::collectErrors( QList &e } if ( !duplicates.isEmpty() ) { - errors.append( new QgsGeometryDuplicateCheckError( this, layerFeatureA, layerFeatureA.geometry().constGet()->centroid(), duplicates ) ); + errors.append( new QgsGeometryDuplicateCheckError( this, layerFeatureA, layerFeatureA.geometry().constGet()->centroid(), featurePools, duplicates ) ); } } } -void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryDuplicateCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePoolA = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePoolA = featurePools[ error->layerId() ]; QgsFeature featureA; if ( !featurePoolA->getFeature( error->featureId(), featureA ) ) { @@ -106,7 +107,7 @@ void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError *error, int meth QgsGeometryDuplicateCheckError *duplicateError = static_cast( error ); for ( const QString &layerIdB : duplicateError->duplicates().keys() ) { - QgsFeaturePool *featurePoolB = mContext->featurePools[ layerIdB ]; + QgsFeaturePool *featurePoolB = featurePools[ layerIdB ]; for ( QgsFeatureId idB : duplicateError->duplicates()[layerIdB] ) { QgsFeature featureB; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h index cb22660303bb..b9fca61a8ee9 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h @@ -18,7 +18,9 @@ #ifndef QGS_GEOMETRY_DUPLICATE_CHECK_H #define QGS_GEOMETRY_DUPLICATE_CHECK_H +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrycheck.h" +#include "qgsgeometrycheckerror.h" class ANALYSIS_EXPORT QgsGeometryDuplicateCheckError : public QgsGeometryCheckError { @@ -26,8 +28,9 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateCheckError : public QgsGeometryCheckEr QgsGeometryDuplicateCheckError( const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, + const QMap &featurePools, const QMap> &duplicates ) - : QgsGeometryCheckError( check, layerFeature, errorLocation, QgsVertexId(), duplicatesString( check->context()->featurePools, duplicates ) ) + : QgsGeometryCheckError( check, layerFeature, errorLocation, QgsVertexId(), duplicatesString( featurePools, duplicates ) ) , mDuplicates( duplicates ) { } QMap> duplicates() const { return mDuplicates; } @@ -50,13 +53,18 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateCheckError : public QgsGeometryCheckEr class ANALYSIS_EXPORT QgsGeometryDuplicateCheck : public QgsGeometryCheck { public: - explicit QgsGeometryDuplicateCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + explicit QgsGeometryDuplicateCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureCheck, context, configuration ) {} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Duplicate" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryDuplicateCheck" ); } + QString factoryDescription() const { return tr( "Duplicate" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryDuplicateCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange, RemoveDuplicates }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp index e94de825a79e..80c63196c1a4 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp @@ -13,14 +13,18 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryduplicatenodescheck.h" #include "qgsgeometryutils.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryDuplicateNodesCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + Q_UNUSED( messages ) + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -45,9 +49,9 @@ void QgsGeometryDuplicateNodesCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryDuplicateNodesCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h index 0f94a9f4d30b..b140e4cb4407 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h @@ -23,13 +23,18 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateNodesCheck : public QgsGeometryCheck { public: - explicit QgsGeometryDuplicateNodesCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + explicit QgsGeometryDuplicateNodesCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Duplicate node" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryDuplicateNodesCheck" ); } + QString factoryDescription() const { return tr( "Duplicate node" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryDuplicateNodesCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { RemoveDuplicates, NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp index 33f67b227de7..1718bfd16715 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp @@ -13,14 +13,16 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryfollowboundariescheck.h" #include "qgsgeometryengine.h" #include "qgsproject.h" #include "qgsspatialindex.h" #include "qgsvectorlayer.h" +#include "qgsgeometrycheckerror.h" -QgsGeometryFollowBoundariesCheck::QgsGeometryFollowBoundariesCheck( QgsGeometryCheckerContext *context, QgsVectorLayer *checkLayer ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PolygonGeometry}, context ) +QgsGeometryFollowBoundariesCheck::QgsGeometryFollowBoundariesCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration, QgsVectorLayer *checkLayer ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) { mCheckLayer = checkLayer; if ( mCheckLayer ) @@ -34,16 +36,18 @@ QgsGeometryFollowBoundariesCheck::~QgsGeometryFollowBoundariesCheck() delete mIndex; } -void QgsGeometryFollowBoundariesCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { + Q_UNUSED( messages ) + if ( !mIndex || !mCheckLayer ) { return; } - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); featureIds.remove( mCheckLayer->id() ); // Don't check layer against itself - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -87,8 +91,10 @@ void QgsGeometryFollowBoundariesCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +void QgsGeometryFollowBoundariesCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { + Q_UNUSED( featurePools ) + if ( method == NoChange ) { error->setFixed( method ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h index d7ffa6b936e7..f64fbe2bbb33 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h @@ -26,13 +26,18 @@ class QgsSpatialIndex; class ANALYSIS_EXPORT QgsGeometryFollowBoundariesCheck : public QgsGeometryCheck { public: - QgsGeometryFollowBoundariesCheck( QgsGeometryCheckerContext *context, QgsVectorLayer *checkLayer ); + QgsGeometryFollowBoundariesCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration, QgsVectorLayer *checkLayer ); ~QgsGeometryFollowBoundariesCheck() override; - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Polygon does not follow boundaries" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryFollowBoundariesCheck" ); } + QString factoryDescription() const { return tr( "Polygon does not follow boundaries" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryFollowBoundariesCheck" ); } + QString id() const override { return factoryId(); } private: enum ResolutionMethod { NoChange }; QgsVectorLayer *mCheckLayer; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp index a94cb33e6a5b..4d9e6616cd0d 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp @@ -13,23 +13,34 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryengine.h" #include "qgsgeometrygapcheck.h" #include "qgsgeometrycollection.h" #include "qgsfeaturepool.h" #include "qgsvectorlayer.h" +#include "qgsfeedback.h" #include "geos_c.h" -void QgsGeometryGapCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +QgsGeometryGapCheck::QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( LayerCheck, context, configuration ) + , mGapThresholdMapUnits( configuration.value( "gapThreshold" ).toDouble() ) + +{ + +} + +void QgsGeometryGapCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - if ( progressCounter ) - progressCounter->fetchAndAddRelaxed( 1 ); + if ( feedback ) + feedback->setProgress( feedback->progress() + 1.0 ); QVector geomList; - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, nullptr, mContext, true ); + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { geomList.append( layerFeature.geometry().constGet()->clone() ); @@ -86,7 +97,7 @@ void QgsGeometryGapCheck::collectErrors( QList &errors, } // Skip gaps above threshold - if ( gapGeom->area() > mThresholdMapUnits || gapGeom->area() < mContext->reducedTolerance ) + if ( gapGeom->area() > mGapThresholdMapUnits || gapGeom->area() < mContext->reducedTolerance ) { continue; } @@ -95,7 +106,7 @@ void QgsGeometryGapCheck::collectErrors( QList &errors, // Get neighboring polygons QMap neighboringIds; - const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds.keys(), gapAreaBBox, mCompatibleGeometryTypes, mContext ); + const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds.keys(), gapAreaBBox, compatibleGeometryTypes(), mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { if ( QgsGeometryCheckerUtils::sharedEdgeLength( gapGeom.get(), layerFeature.geometry().constGet(), mContext->reducedTolerance ) > 0 ) @@ -117,7 +128,7 @@ void QgsGeometryGapCheck::collectErrors( QList &errors, } } -void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryGapCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { if ( method == NoChange ) { @@ -126,7 +137,7 @@ void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, co else if ( method == MergeLongestEdge ) { QString errMsg; - if ( mergeWithNeighbor( static_cast( error ), changes, errMsg ) ) + if ( mergeWithNeighbor( featurePools, static_cast( error ), changes, errMsg ) ) { error->setFixed( method ); } @@ -141,7 +152,9 @@ void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, co } } -bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const +bool QgsGeometryGapCheck::mergeWithNeighbor( const QMap &featurePools, + QgsGeometryGapCheckError *err, + Changes &changes, QString &errMsg ) const { double maxVal = 0.; QString mergeLayerId; @@ -154,9 +167,10 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan // Search for touching neighboring geometries for ( const QString &layerId : layerIds ) { - QgsFeaturePool *featurePool = mContext->featurePools.value( layerId ); + QgsFeaturePool *featurePool = featurePools.value( layerId ); std::unique_ptr errLayerGeom( errGeometry->clone() ); - errLayerGeom->transform( mContext->layerTransform( featurePool->layer() ), QgsCoordinateTransform::ReverseTransform ); + QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext ); + errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform ); const auto featureIds = err->neighbors().value( layerId ); @@ -189,9 +203,10 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan } // Merge geometries - QgsFeaturePool *featurePool = mContext->featurePools[ mergeLayerId ]; + QgsFeaturePool *featurePool = featurePools[ mergeLayerId ]; std::unique_ptr errLayerGeom( errGeometry->clone() ); - errLayerGeom->transform( mContext->layerTransform( featurePool->layer() ), QgsCoordinateTransform::ReverseTransform ); + QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext ); + errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform ); QgsGeometry mergeFeatureGeom = mergeFeature.geometry(); const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet(); std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom.get(), mContext->reducedTolerance ); @@ -202,7 +217,7 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan } // Add merged polygon to destination geometry - replaceFeatureGeometryPart( mergeLayerId, mergeFeature, mergePartIdx, combinedGeom.release(), changes ); + replaceFeatureGeometryPart( featurePools, mergeLayerId, mergeFeature, mergePartIdx, combinedGeom.release(), changes ); return true; } @@ -213,3 +228,43 @@ QStringList QgsGeometryGapCheck::resolutionMethods() const static QStringList methods = QStringList() << tr( "Add gap area to neighboring polygon with longest shared edge" ) << tr( "No action" ); return methods; } + +QString QgsGeometryGapCheck::description() const +{ + return factoryDescription(); +} + +QString QgsGeometryGapCheck::id() const +{ + return factoryId(); +} + +QgsGeometryCheck::Flags QgsGeometryGapCheck::flags() const +{ + return factoryFlags(); +} + +QString QgsGeometryGapCheck::factoryDescription() +{ + return tr( "Gap" ); +} + +QString QgsGeometryGapCheck::factoryId() +{ + return QStringLiteral( "QgsGeometryGapCheck" ); +} + +QgsGeometryCheck::Flags QgsGeometryGapCheck::factoryFlags() +{ + return QgsGeometryCheck::SingleLayerTopologyCheck; +} + +QList QgsGeometryGapCheck::factoryCompatibleGeometryTypes() +{ + return {QgsWkbTypes::PolygonGeometry}; +} + +bool QgsGeometryGapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP +{ + return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h index 1c4abd366c48..f3e43d78d0ef 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h @@ -19,6 +19,7 @@ #define QGS_GEOMETRY_GAP_CHECK_H #include "qgsgeometrycheck.h" +#include "qgsgeometrycheckerror.h" class ANALYSIS_EXPORT QgsGeometryGapCheckError : public QgsGeometryCheckError { @@ -75,23 +76,32 @@ class ANALYSIS_EXPORT QgsGeometryGapCheckError : public QgsGeometryCheckError class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck { public: - QgsGeometryGapCheck( QgsGeometryCheckerContext *context, double thresholdMapUnits ) - : QgsGeometryCheck( LayerCheck, {QgsWkbTypes::PolygonGeometry}, context ) - , mThresholdMapUnits( thresholdMapUnits ) - {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + explicit QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); + + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Gap" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryGapCheck" ); } + + QString description() const override; + QString id() const override; + QgsGeometryCheck::Flags flags() const override; + +///@cond private + static QString factoryDescription() SIP_SKIP; + static QString factoryId() SIP_SKIP; + static QgsGeometryCheck::Flags factoryFlags() SIP_SKIP; + static QList factoryCompatibleGeometryTypes() SIP_SKIP; + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP; +///@endcond private enum ResolutionMethod { MergeLongestEdge, NoChange }; private: + bool mergeWithNeighbor( const QMap &featurePools, + QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const; - double mThresholdMapUnits; - - bool mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const; + const double mGapThresholdMapUnits; }; #endif // QGS_GEOMETRY_GAP_CHECK_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp index 54dbd82c7d21..bccf84db0f6a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp @@ -13,15 +13,19 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryholecheck.h" #include "qgscurve.h" #include "qgscurvepolygon.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryHoleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryHoleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + Q_UNUSED( messages ) + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -43,9 +47,9 @@ void QgsGeometryHoleCheck::collectErrors( QList &errors } } -void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryHoleCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { @@ -70,7 +74,7 @@ void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError *error, int method, c } else if ( method == RemoveHoles ) { - deleteFeatureGeometryRing( error->layerId(), feature, vidx.part, vidx.ring, changes ); + deleteFeatureGeometryRing( featurePools, error->layerId(), feature, vidx.part, vidx.ring, changes ); error->setFixed( method ); } else diff --git a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h index 1bc0b16dd919..8f267f685c0a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h @@ -23,13 +23,18 @@ class ANALYSIS_EXPORT QgsGeometryHoleCheck : public QgsGeometryCheck { public: - explicit QgsGeometryHoleCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, context ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + explicit QgsGeometryHoleCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureCheck, context, configuration ) {} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Polygon with hole" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryHoleCheck" ); } + QString factoryDescription() const { return tr( "Polygon with hole" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryHoleCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { RemoveHoles, NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp index 01a6b44ab25c..8805cd4b27e6 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp @@ -13,14 +13,18 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrylineintersectioncheck.h" #include "qgslinestring.h" #include "qgsvectorlayer.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryLineIntersectionCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryLineIntersectionCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true ); + Q_UNUSED( messages ) + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); QList layerIds = featureIds.keys(); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA ) { @@ -38,7 +42,7 @@ void QgsGeometryLineIntersectionCheck::collectErrors( QListfeaturePools, QList() << layerFeatureA.layer()->id() << layerIds, line->boundingBox(), {QgsWkbTypes::LineGeometry}, mContext ); + QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList() << layerFeatureA.layer()->id() << layerIds, line->boundingBox(), {QgsWkbTypes::LineGeometry}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB ) { // > : only report intersections within same layer once @@ -71,8 +75,10 @@ void QgsGeometryLineIntersectionCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +void QgsGeometryLineIntersectionCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { + Q_UNUSED( featurePools ) + if ( method == NoChange ) { error->setFixed( method ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h index a846373a91c2..38bc31e5092d 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h @@ -23,14 +23,19 @@ class ANALYSIS_EXPORT QgsGeometryLineIntersectionCheck : public QgsGeometryCheck { public: - QgsGeometryLineIntersectionCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry}, context ) + QgsGeometryLineIntersectionCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Intersection" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryLineIntersectionCheck" ); } + QString factoryDescription() const { return tr( "Intersection" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryLineIntersectionCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp index fb3c852ba2b1..54fe8a839cc6 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp @@ -13,16 +13,20 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrylinelayerintersectioncheck.h" #include "qgspolygon.h" #include "qgslinestring.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryLineLayerIntersectionCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + Q_UNUSED( messages ) + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); featureIds.remove( mCheckLayer ); // Don't check layer against itself - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true ); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -36,7 +40,7 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( QListfeaturePools, QStringList() << mCheckLayer, line->boundingBox(), {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, mContext ); + QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, QStringList() << mCheckLayer, line->boundingBox(), {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) { const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet(); @@ -69,8 +73,10 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +void QgsGeometryLineLayerIntersectionCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { + Q_UNUSED( featurePools ) + if ( method == NoChange ) { error->setFixed( method ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h index 94d29447f4e8..0d97eac33f77 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h @@ -23,14 +23,20 @@ class ANALYSIS_EXPORT QgsGeometryLineLayerIntersectionCheck : public QgsGeometryCheck { public: - QgsGeometryLineLayerIntersectionCheck( QgsGeometryCheckerContext *context, const QString &checkLayer ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry}, context ), mCheckLayer( checkLayer ) + QgsGeometryLineLayerIntersectionCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) + , mCheckLayer( configurationValue( "checkLayer" ) ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Intersection" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryLineLayerIntersectionCheck" ); } + QString factoryDescription() const { return tr( "Intersection" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryLineLayerIntersectionCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp index 4aea64439afb..24e6bf3fab64 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp @@ -15,7 +15,7 @@ #include "qgsgeometrymissingvertexcheck.h" -#include "qgsfeaturepool.h" +#include "qgsfeedback.h" #include "qgsgeometrycollection.h" #include "qgsmultipolygon.h" #include "qgscurvepolygon.h" @@ -24,17 +24,24 @@ #include "qgsgeometryengine.h" #include "qgsgeometryutils.h" -void QgsGeometryMissingVertexCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration ) + : QgsGeometryCheck( LayerCheck, context, geometryCheckConfiguration ) + +{ + +} + +void QgsGeometryMissingVertexCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - Q_UNUSED( messages ); - if ( progressCounter ) - progressCounter->fetchAndAddRelaxed( 1 ); + Q_UNUSED( messages ) + if ( feedback ) + feedback->setProgress( feedback->progress() + 1.0 ); QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsFeaturePool *featurePool = mContext->featurePools.value( featureIds.firstKey() ); - const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, nullptr, mContext, true ); + const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { @@ -58,10 +65,10 @@ void QgsGeometryMissingVertexCheck::collectErrors( QList &mergeAttributeIndices, Changes &changes ) const +void QgsGeometryMissingVertexCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - Q_UNUSED( mergeAttributeIndices ); - Q_UNUSED( changes ); + Q_UNUSED( featurePools ) + Q_UNUSED( changes ) if ( method == NoChange ) { error->setFixed( method ); @@ -74,6 +81,11 @@ QStringList QgsGeometryMissingVertexCheck::resolutionMethods() const return methods; } +QString QgsGeometryMissingVertexCheck::description() const +{ + return factoryDescription(); +} + void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const { const QgsFeature ¤tFeature = layerFeature.feature(); @@ -131,3 +143,43 @@ void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polyg } } } + +QString QgsGeometryMissingVertexCheck::id() const +{ + return factoryId(); +} + +QList QgsGeometryMissingVertexCheck::compatibleGeometryTypes() const +{ + return factoryCompatibleGeometryTypes(); +} + +QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::flags() const +{ + return factoryFlags(); +} + +QList QgsGeometryMissingVertexCheck::factoryCompatibleGeometryTypes() +{ + return {QgsWkbTypes::PolygonGeometry}; +} + +bool QgsGeometryMissingVertexCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP +{ + return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); +} + +QString QgsGeometryMissingVertexCheck::factoryDescription() +{ + return tr( "Missing Vertex" ); +} + +QString QgsGeometryMissingVertexCheck::factoryId() +{ + return QStringLiteral( "QgsGeometryMissingVertexCheck" ); +} + +QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::factoryFlags() +{ + return QgsGeometryCheck::SingleLayerTopologyCheck; +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h index 20197e4ef409..a40e561567e3 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h @@ -19,6 +19,7 @@ #define QGSGEOMETRYMISSINGVERTEXCHECK_H #include "qgsgeometrycheck.h" +#include "qgsgeometrycheckerror.h" class QgsCurvePolygon; @@ -34,19 +35,33 @@ class QgsCurvePolygon; class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck { public: - explicit QgsGeometryMissingVertexCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( LayerCheck, {QgsWkbTypes::PolygonGeometry}, context ) - {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + enum ResolutionMethod + { + NoChange + }; + + explicit QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration ); + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Missing Vertex" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryMissingVertexCheck" ); } - enum ResolutionMethod { NoChange }; + QString description() const override; + QString id() const override; + QList compatibleGeometryTypes() const override; + QgsGeometryCheck::Flags flags() const override; + +///@cond private + static QList factoryCompatibleGeometryTypes() SIP_SKIP; + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP; + static QString factoryDescription() SIP_SKIP; + static QString factoryId() SIP_SKIP; + static QgsGeometryCheck::Flags factoryFlags() SIP_SKIP; +///@endcond private: void processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const; }; -#endif // QGSGEOMETRYMISSINGVERTEXCHECK_ + + +#endif // QGSGEOMETRYMISSINGVERTEXCHECK_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.cpp index a69a2cab334d..a195a0c8c37e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.cpp @@ -13,12 +13,12 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrymultipartcheck.h" #include "qgsfeaturepool.h" -QList QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const +QList QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry ) const { - Q_UNUSED( configuration ) QList errors; const QgsAbstractGeometry *geom = geometry.constGet(); @@ -30,9 +30,9 @@ QList QgsGeometryMultipartCheck::processGeometry( return errors; } -void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryMultipartCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.h index 085a25ef4cc6..b5fd21638fd2 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrymultipartcheck.h @@ -23,13 +23,20 @@ class ANALYSIS_EXPORT QgsGeometryMultipartCheck : public QgsSingleGeometryCheck { public: - explicit QgsGeometryMultipartCheck( QgsGeometryCheckerContext *context ) - : QgsSingleGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {} - QList processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + explicit QgsGeometryMultipartCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsSingleGeometryCheck( FeatureCheck, + context, + configuration ) {} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + QList processGeometry( const QgsGeometry &geometry ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Multipart object with only one feature" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryMultipartCheck" ); } + QString factoryDescription() const { return tr( "Multipart object with only one feature" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryMultipartCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { ConvertToSingle, RemoveObject, NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp index cc1328dbedc1..b7298808bc38 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp @@ -13,16 +13,25 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryengine.h" #include "qgsgeometryoverlapcheck.h" #include "qgsfeaturepool.h" +#include "qgsvectorlayer.h" -void QgsGeometryOverlapCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +QgsGeometryOverlapCheck::QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( LayerCheck, context, configuration ) + , mOverlapThresholdMapUnits( configurationValue( "maxOverlapArea" ) ) + +{ + +} + +void QgsGeometryOverlapCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - double overlapThreshold = mThresholdMapUnits; - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true ); + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); QList layerIds = featureIds.keys(); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA ) { @@ -37,7 +46,7 @@ void QgsGeometryOverlapCheck::collectErrors( QList &err continue; } - const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( mContext->featurePools, QList() << layerFeatureA.layer()->id() << layerIds, bboxA, mCompatibleGeometryTypes, mContext ); + const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList() << layerFeatureA.layer()->id() << layerIds, bboxA, compatibleGeometryTypes(), mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB ) { // > : only report overlaps within same layer once @@ -56,7 +65,7 @@ void QgsGeometryOverlapCheck::collectErrors( QList &err { QgsAbstractGeometry *interPart = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart ); double area = interPart->area(); - if ( area > mContext->reducedTolerance && area < overlapThreshold ) + if ( area > mContext->reducedTolerance && area < mOverlapThresholdMapUnits ) { errors.append( new QgsGeometryOverlapCheckError( this, layerFeatureA, QgsGeometry( interPart->clone() ), interPart->centroid(), area, layerFeatureB ) ); } @@ -72,13 +81,13 @@ void QgsGeometryOverlapCheck::collectErrors( QList &err } } -void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryOverlapCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QString errMsg; QgsGeometryOverlapCheckError *overlapError = static_cast( error ); - QgsFeaturePool *featurePoolA = mContext->featurePools[ overlapError->layerId() ]; - QgsFeaturePool *featurePoolB = mContext->featurePools[ overlapError->overlappedFeature().first ]; + QgsFeaturePool *featurePoolA = featurePools[ overlapError->layerId() ]; + QgsFeaturePool *featurePoolB = featurePools[ overlapError->overlappedFeature().first ]; QgsFeature featureA; QgsFeature featureB; if ( !featurePoolA->getFeature( overlapError->featureId(), featureA ) || @@ -159,7 +168,8 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method { if ( shared1 < shared2 ) { - diff1->transform( mContext->layerTransform( featurePoolA->layer() ), QgsCoordinateTransform::ReverseTransform ); + QgsCoordinateTransform ct( featurePoolA->crs(), mContext->mapCrs, mContext->transformContext ); + diff1->transform( ct, QgsCoordinateTransform::ReverseTransform ); featureA.setGeometry( QgsGeometry( std::move( diff1 ) ) ); changes[error->layerId()][featureA.id()].append( Change( ChangeFeature, ChangeChanged ) ); @@ -167,7 +177,8 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method } else { - diff2->transform( mContext->layerTransform( featurePoolB->layer() ), QgsCoordinateTransform::ReverseTransform ); + QgsCoordinateTransform ct( featurePoolB->crs(), mContext->mapCrs, mContext->transformContext ); + diff2->transform( ct, QgsCoordinateTransform::ReverseTransform ); featureB.setGeometry( QgsGeometry( std::move( diff2 ) ) ); changes[overlapError->overlappedFeature().first][featureB.id()].append( Change( ChangeFeature, ChangeChanged ) ); @@ -190,3 +201,51 @@ QStringList QgsGeometryOverlapCheck::resolutionMethods() const << tr( "No action" ); return methods; } + +QString QgsGeometryOverlapCheck::description() const +{ + return factoryDescription(); +} + +QString QgsGeometryOverlapCheck::id() const +{ + return factoryId(); +} + +QgsGeometryCheck::Flags QgsGeometryOverlapCheck::flags() const +{ + return factoryFlags(); +} + +QString QgsGeometryOverlapCheck::factoryDescription() +{ + return tr( "Overlap" ); +} + +QString QgsGeometryOverlapCheck::factoryId() +{ + return QStringLiteral( "QgsGeometryOverlapCheck" ); +} + +QgsGeometryCheck::Flags QgsGeometryOverlapCheck::factoryFlags() +{ + return QgsGeometryCheck::SingleLayerTopologyCheck; +} + +QList QgsGeometryOverlapCheck::factoryCompatibleGeometryTypes() +{ + return {QgsWkbTypes::PolygonGeometry}; +} + +bool QgsGeometryOverlapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP +{ + return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); +} + +QgsGeometryOverlapCheckError::QgsGeometryOverlapCheckError( const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsGeometry &geometry, const QgsPointXY &errorLocation, const QVariant &value, const QgsGeometryCheckerUtils::LayerFeature &overlappedFeature ) + : QgsGeometryCheckError( check, layerFeature.layer()->id(), layerFeature.feature().id(), geometry, errorLocation, QgsVertexId(), value, ValueArea ) + , mOverlappedFeature( qMakePair( overlappedFeature.layer()->id(), overlappedFeature.feature().id() ) ) + +{ + +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index 1ea81e04d908..24ad045ffd65 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -19,7 +19,8 @@ #define QGS_GEOMETRY_OVERLAP_CHECK_H #include "qgsgeometrycheck.h" -#include "qgsvectorlayer.h" +#include "qgsgeometrycheckerror.h" + class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckError { @@ -29,10 +30,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro const QgsGeometry &geometry, const QgsPointXY &errorLocation, const QVariant &value, - const QgsGeometryCheckerUtils::LayerFeature &overlappedFeature ) - : QgsGeometryCheckError( check, layerFeature.layer()->id(), layerFeature.feature().id(), geometry, errorLocation, QgsVertexId(), value, ValueArea ) - , mOverlappedFeature( qMakePair( overlappedFeature.layer()->id(), overlappedFeature.feature().id() ) ) - { } + const QgsGeometryCheckerUtils::LayerFeature &overlappedFeature ); const QPair &overlappedFeature() const { return mOverlappedFeature; } bool isEqual( QgsGeometryCheckError *other ) const override @@ -65,7 +63,8 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro return true; } - QString description() const override { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1:%2" ).arg( mOverlappedFeature.first ).arg( mOverlappedFeature.second ); } + QString factoryDescription() const { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1:%2" ).arg( mOverlappedFeature.first ).arg( mOverlappedFeature.second ); } + QString description() const override { return factoryDescription(); } private: QPair mOverlappedFeature; @@ -74,20 +73,29 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck { public: - QgsGeometryOverlapCheck( QgsGeometryCheckerContext *context, double thresholdMapUnits ) - : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, context ) - , mThresholdMapUnits( thresholdMapUnits ) - {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Overlap" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryOverlapCheck" ); } + + QString description() const override; + QString id() const override; + QgsGeometryCheck::Flags flags() const override; + +///@cond private + static QString factoryDescription() SIP_SKIP; + static QString factoryId() SIP_SKIP; + static QgsGeometryCheck::Flags factoryFlags() SIP_SKIP; + static QList factoryCompatibleGeometryTypes() SIP_SKIP; + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP; +///@endcond private enum ResolutionMethod { Subtract, NoChange }; private: - double mThresholdMapUnits; + const double mOverlapThresholdMapUnits; + }; #endif // QGS_GEOMETRY_OVERLAP_CHECK_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp index 2856ca782c74..bfe2f5bdb181 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp @@ -13,14 +13,18 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrypointcoveredbylinecheck.h" #include "qgslinestring.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryPointCoveredByLineCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true ); + Q_UNUSED( messages ) + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -36,7 +40,7 @@ void QgsGeometryPointCoveredByLineCheck::collectErrors( QListx() - mContext->tolerance, point->y() - mContext->tolerance, point->x() + mContext->tolerance, point->y() + mContext->tolerance ); - QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, featureIds.keys(), rect, {QgsWkbTypes::LineGeometry}, mContext ); + QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, featureIds.keys(), rect, {QgsWkbTypes::LineGeometry}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) { const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet(); @@ -67,8 +71,10 @@ void QgsGeometryPointCoveredByLineCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +void QgsGeometryPointCoveredByLineCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { + Q_UNUSED( featurePools ) + if ( method == NoChange ) { error->setFixed( method ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h index 1975abcddeb4..bfe2457c013c 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h @@ -23,14 +23,19 @@ class ANALYSIS_EXPORT QgsGeometryPointCoveredByLineCheck : public QgsGeometryCheck { public: - QgsGeometryPointCoveredByLineCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PointGeometry}, context ) + QgsGeometryPointCoveredByLineCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Point not covered by line" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryPointCoveredByLineCheck" ); } + QString factoryDescription() const { return tr( "Point not covered by line" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryPointCoveredByLineCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.cpp index 145a60be216d..791dcb5b2d2a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.cpp @@ -13,14 +13,16 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrypointinpolygoncheck.h" #include "qgspolygon.h" #include "qgsgeometryengine.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometryPointInPolygonCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometryPointInPolygonCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true ); + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -38,7 +40,7 @@ void QgsGeometryPointInPolygonCheck::collectErrors( QListx() - mContext->tolerance, point->y() - mContext->tolerance, point->x() + mContext->tolerance, point->y() + mContext->tolerance ); - QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, featureIds.keys(), rect, {QgsWkbTypes::PolygonGeometry}, mContext ); + QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, featureIds.keys(), rect, {QgsWkbTypes::PolygonGeometry}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) { ++nTested; @@ -62,8 +64,10 @@ void QgsGeometryPointInPolygonCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +void QgsGeometryPointInPolygonCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { + Q_UNUSED( featurePools ) + if ( method == NoChange ) { error->setFixed( method ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h index 9f05bef5bb67..54f8e260d5e2 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h @@ -23,14 +23,19 @@ class ANALYSIS_EXPORT QgsGeometryPointInPolygonCheck : public QgsGeometryCheck { public: - QgsGeometryPointInPolygonCheck( QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PointGeometry}, context ) + QgsGeometryPointInPolygonCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Point not in polygon" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryPointInPolygonCheck" ); } + QString factoryDescription() const { return tr( "Point not in polygon" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometryPointInPolygonCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.cpp index 531b6b262511..b32df828a5a6 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.cpp @@ -13,17 +13,21 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrysegmentlengthcheck.h" #include "qgsgeometryutils.h" #include "qgsfeaturepool.h" +#include "qgsgeometrycheckerror.h" -void QgsGeometrySegmentLengthCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsGeometrySegmentLengthCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + Q_UNUSED( messages ) + + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { - double layerToMapUnits = mContext->layerScaleFactor( layerFeature.layer() ); + double layerToMapUnits = scaleFactor( layerFeature.layer() ); double minLength = mMinLengthMapUnits / layerToMapUnits; const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); @@ -54,9 +58,9 @@ void QgsGeometrySegmentLengthCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes &/*changes*/ ) const +void QgsGeometrySegmentLengthCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &/*changes*/ ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { @@ -86,7 +90,7 @@ void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError *error, int QgsPoint pi = geom->vertexAt( error->vidx() ); QgsPoint pj = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + nVerts ) % nVerts ) ); double dist = pi.distance( pj ); - double layerToMapUnits = mContext->layerScaleFactor( featurePool->layer() ); + double layerToMapUnits = scaleFactor( featurePool->layer() ); double minLength = mMinLengthMapUnits / layerToMapUnits; if ( dist >= minLength ) { diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h index 4bebe5ccb9cb..31b5391eac26 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h @@ -23,15 +23,20 @@ class ANALYSIS_EXPORT QgsGeometrySegmentLengthCheck : public QgsGeometryCheck { public: - QgsGeometrySegmentLengthCheck( QgsGeometryCheckerContext *context, double minLengthMapUnits ) - : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) - , mMinLengthMapUnits( minLengthMapUnits ) + QgsGeometrySegmentLengthCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryCheck( FeatureNodeCheck, context, configuration ) + , mMinLengthMapUnits( configuration.value( "minSegmentLength" ).toDouble() ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Minimal segment length" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometrySegmentLengthCheck" ); } + QString factoryDescription() const { return tr( "Minimal segment length" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometrySegmentLengthCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.cpp index 1531193c32d1..400163c1046b 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.cpp @@ -13,13 +13,13 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryselfcontactcheck.h" #include "qgsgeometryutils.h" #include "qgsfeaturepool.h" -QList QgsGeometrySelfContactCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const +QList QgsGeometrySelfContactCheck::processGeometry( const QgsGeometry &geometry ) const { - Q_UNUSED( configuration ) QList errors; const QgsAbstractGeometry *geom = geometry.constGet(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) @@ -81,8 +81,9 @@ QList QgsGeometrySelfContactCheck::processGeometr return errors; } -void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const +void QgsGeometrySelfContactCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { + Q_UNUSED( featurePools ) if ( method == NoChange ) { error->setFixed( method ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.h index 9545f200af89..a7586f24d946 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryselfcontactcheck.h @@ -23,13 +23,18 @@ class ANALYSIS_EXPORT QgsGeometrySelfContactCheck : public QgsSingleGeometryCheck { public: - QgsGeometrySelfContactCheck( QgsGeometryCheckerContext *context ) - : QgsSingleGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {} - QList processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes & ) const override; + QgsGeometrySelfContactCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsSingleGeometryCheck( FeatureNodeCheck, context, configuration ) {} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + QList processGeometry( const QgsGeometry &geometry ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes & ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Self contact" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometrySelfContactCheck" ); } + QString factoryDescription() const { return tr( "Self contact" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometrySelfContactCheck" ); } + QString id() const override { return factoryId(); } enum ResolutionMethod { NoChange }; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.cpp index 62ed66fd21bf..129c9bbb686e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.cpp @@ -13,6 +13,7 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometryselfintersectioncheck.h" #include "qgspolygon.h" #include "qgslinestring.h" @@ -66,9 +67,9 @@ void QgsGeometrySelfIntersectionCheckError::update( const QgsSingleGeometryCheck mIntersection.point = err->mIntersection.point; } -void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometrySelfIntersectionCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { @@ -315,10 +316,8 @@ QStringList QgsGeometrySelfIntersectionCheck::resolutionMethods() const return methods; } -QList QgsGeometrySelfIntersectionCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const +QList QgsGeometrySelfIntersectionCheck::processGeometry( const QgsGeometry &geometry ) const { - Q_UNUSED( configuration ) - QList errors; const QgsAbstractGeometry *geom = geometry.constGet(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) @@ -333,3 +332,28 @@ QList QgsGeometrySelfIntersectionCheck::processGe } return errors; } + +QList QgsGeometrySelfIntersectionCheck::factoryCompatibleGeometryTypes() +{ + return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; +} + +bool QgsGeometrySelfIntersectionCheck::factoryIsCompatible( QgsVectorLayer *layer ) +{ + return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); +} + +QString QgsGeometrySelfIntersectionCheck::factoryDescription() +{ + return tr( "Self intersection" ); +} + +QgsGeometryCheck::Flags QgsGeometrySelfIntersectionCheck::factoryFlags() +{ + return QgsGeometryCheck::SingleGeometryCheck; +} + +QString QgsGeometrySelfIntersectionCheck::factoryId() +{ + return QStringLiteral( "QgsGeometrySelfIntersectionCheck" ); +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.h b/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.h index c6fc33b8416d..25e19066ad94 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryselfintersectioncheck.h @@ -45,15 +45,32 @@ class ANALYSIS_EXPORT QgsGeometrySelfIntersectionCheckError : public QgsSingleGe class ANALYSIS_EXPORT QgsGeometrySelfIntersectionCheck : public QgsSingleGeometryCheck { public: - explicit QgsGeometrySelfIntersectionCheck( QgsGeometryCheckerContext *context ) - : QgsSingleGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {} - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + enum ResolutionMethod + { + ToMultiObject, + ToSingleObjects, + NoChange + }; + + explicit QgsGeometrySelfIntersectionCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration = QVariantMap() ) + : QgsSingleGeometryCheck( FeatureNodeCheck, + context, + configuration ) {} + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Self intersection" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometrySelfIntersectionCheck" ); } - QList processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override; + QString description() const override { return factoryDescription(); } + QString id() const override { return factoryId(); } + QgsGeometryCheck::Flags flags() const override {return factoryFlags(); } + QList processGeometry( const QgsGeometry &geometry ) const override; +///@cond private + static QList factoryCompatibleGeometryTypes() SIP_SKIP; + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP; + static QString factoryDescription() SIP_SKIP; + static QgsGeometryCheck::Flags factoryFlags() SIP_SKIP; + static QString factoryId() SIP_SKIP; +///@endcond private - enum ResolutionMethod { ToMultiObject, ToSingleObjects, NoChange }; }; #endif // QGS_GEOMETRY_SELFINTERSECTION_CHECK_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp index 4e8e314c700c..8f7a83117978 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp @@ -18,7 +18,7 @@ bool QgsGeometrySliverPolygonCheck::checkThreshold( double layerToMapUnits, const QgsAbstractGeometry *geom, double &value ) const { - double maxArea = mMaxAreaMapUnits / ( layerToMapUnits * layerToMapUnits ); + double maxArea = mMaxArea / ( layerToMapUnits * layerToMapUnits ); QgsRectangle bb = geom->boundingBox(); double maxDim = std::max( bb.width(), bb.height() ); double area = geom->area(); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.h index af74f231a65f..c3018559efe7 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.h @@ -23,17 +23,23 @@ class ANALYSIS_EXPORT QgsGeometrySliverPolygonCheck : public QgsGeometryAreaCheck { public: - QgsGeometrySliverPolygonCheck( QgsGeometryCheckerContext *context, double threshold, double maxAreaMapUnits ) - : QgsGeometryAreaCheck( context, threshold ) - , mMaxAreaMapUnits( maxAreaMapUnits ) - {} - QString errorDescription() const override { return tr( "Sliver polygon" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometrySliverPolygonCheck" ); } + QgsGeometrySliverPolygonCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsGeometryAreaCheck( context, configuration ) + { + mThresholdMapUnits = configurationValue( "threshold" ); + mMaxArea = configurationValue( "maxArea" ); + } + QString factoryDescription() const { return tr( "Sliver polygon" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const { return QStringLiteral( "QgsGeometrySliverPolygonCheck" ); } + QString id() const override { return factoryId(); } private: - double mMaxAreaMapUnits; - bool checkThreshold( double layerToMapUnits, const QgsAbstractGeometry *geom, double &value ) const override; + + double mThresholdMapUnits; + double mMaxArea; + }; #endif // QGS_GEOMETRY_SLIVERPOLYGON_CHECK_H diff --git a/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.cpp index 4c8d681cc461..8c05dbc24c32 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.cpp @@ -13,6 +13,7 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrytypecheck.h" #include "qgsgeometrycollection.h" #include "qgsmulticurve.h" @@ -23,9 +24,9 @@ #include "qgsfeaturepool.h" -QList QgsGeometryTypeCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const + +QList QgsGeometryTypeCheck::processGeometry( const QgsGeometry &geometry ) const { - Q_UNUSED( configuration ) QList errors; const QgsAbstractGeometry *geom = geometry.constGet(); QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() ); @@ -36,9 +37,9 @@ QList QgsGeometryTypeCheck::processGeometry( cons return errors; } -void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const +void QgsGeometryTypeCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { @@ -156,6 +157,26 @@ QStringList QgsGeometryTypeCheck::resolutionMethods() const return methods; } +QString QgsGeometryTypeCheck::factoryDescription() const +{ + return tr( "Geometry type" ); +} + +QString QgsGeometryTypeCheck::description() const +{ + return factoryDescription(); +} + +QString QgsGeometryTypeCheck::factoryId() const +{ + return QStringLiteral( "QgsGeometryTypeCheck" ); +} + +QString QgsGeometryTypeCheck::id() const +{ + return factoryId(); +} + bool QgsGeometryTypeCheckError::isEqual( const QgsSingleGeometryCheckError *other ) const { return QgsSingleGeometryCheckError::isEqual( other ) && @@ -164,5 +185,5 @@ bool QgsGeometryTypeCheckError::isEqual( const QgsSingleGeometryCheckError *othe QString QgsGeometryTypeCheckError::description() const { - return QStringLiteral( "%1 (%2)" ).arg( mCheck->errorDescription(), QgsWkbTypes::displayString( mFlatType ) ); + return QStringLiteral( "%1 (%2)" ).arg( mCheck->description(), QgsWkbTypes::displayString( mFlatType ) ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.h b/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.h index a2d543614ae8..be4972b133f3 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrytypecheck.h @@ -43,15 +43,22 @@ class ANALYSIS_EXPORT QgsGeometryTypeCheckError : public QgsSingleGeometryCheckE class ANALYSIS_EXPORT QgsGeometryTypeCheck : public QgsSingleGeometryCheck { public: - QgsGeometryTypeCheck( QgsGeometryCheckerContext *context, int allowedTypes ) - : QgsSingleGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) - , mAllowedTypes( allowedTypes ) + QgsGeometryTypeCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration, int allowedTypes ) + : QgsSingleGeometryCheck( FeatureCheck, context, configuration ) + , mAllowedTypes( allowedTypes ) {} - QList processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override; - void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + QList processGeometry( const QgsGeometry &geometry ) const override; + void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; - QString errorDescription() const override { return tr( "Geometry type" ); } - QString errorName() const override { return QStringLiteral( "QgsGeometryTypeCheck" ); } + QString description() const override; + QString id() const override; + + static QList factoryCompatibleGeometryTypes() SIP_SKIP {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QString factoryDescription() const SIP_SKIP; + QString factoryId() const SIP_SKIP; + private: enum ResolutionMethod { Convert, Delete, NoChange }; int mAllowedTypes; diff --git a/src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.cpp new file mode 100644 index 000000000000..3bf6436ce6bf --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + qgssinglegeometrycheck.cpp + -------------------------------------- +Date : 7.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsisvalidgeometrycheck.h" +#include "qgsfeature.h" +#include "qgssettings.h" +#include "qgsgeos.h" +#include "qgsgeometryvalidator.h" + +QList QgsIsValidGeometryCheck::processGeometry( const QgsGeometry &geometry ) const +{ + QVector errors; + + QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal; + if ( QgsSettings().value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 ) + method = QgsGeometry::ValidatorGeos; + + QgsGeometryValidator validator( geometry, &errors, method ); + + QObject::connect( &validator, &QgsGeometryValidator::errorFound, &validator, [ &errors ]( const QgsGeometry::Error & error ) + { + errors.append( error ); + } ); + + // We are already on a thread here normally, no reason to start yet another one. Run synchroneously. + validator.run(); + + QList result; + for ( const auto &error : qgis::as_const( errors ) ) + { + result << new QgsSingleGeometryCheckError( this, geometry, QgsGeometry( qgis::make_unique( error.where() ) ) ); + } + return result; +} + +QStringList QgsIsValidGeometryCheck::resolutionMethods() const +{ + return QStringList(); +} diff --git a/src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.h b/src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.h new file mode 100644 index 000000000000..71676527e2bc --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsisvalidgeometrycheck.h @@ -0,0 +1,48 @@ +/*************************************************************************** + qgsisvalidgeometrycheck.h + -------------------------------------- +Date : 7.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSISVALIDGEOMETRYCHECK_H +#define QGSISVALIDGEOMETRYCHECK_H + +#define SIP_NO_FILE + +#include "qgssinglegeometrycheck.h" + +/** + * Checks if geometries are valid. + */ +class ANALYSIS_EXPORT QgsIsValidGeometryCheck : public QgsSingleGeometryCheck +{ + public: + explicit QgsIsValidGeometryCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) + : QgsSingleGeometryCheck( FeatureNodeCheck, context, configuration ) {} + + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } + QList processGeometry( const QgsGeometry &geometry ) const override; + + QStringList resolutionMethods() const override; + QString factoryDescription() const { return tr( "Is Valid" ); } + QString description() const override { return factoryDescription(); } + QString factoryId() const + { + return QStringLiteral( "QgsIsValidCheck" ); + } + + QString id() const override { return factoryId(); } +}; + +#endif // QGSISVALIDGEOMETRYCHECK_H diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp index 311624554368..6f511d893f66 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp @@ -14,22 +14,21 @@ email : matthias@opengis.ch ***************************************************************************/ #include "qgssinglegeometrycheck.h" +#include "qgsgeometrycheckcontext.h" #include "qgspoint.h" -QgsSingleGeometryCheck::QgsSingleGeometryCheck( CheckType checkType, const QList &compatibleGeometryTypes, QgsGeometryCheckerContext *context ) - : QgsGeometryCheck( checkType, compatibleGeometryTypes, context ) -{ -} -void QgsSingleGeometryCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +void QgsSingleGeometryCheck::collectErrors( const QMap &featurePools, + QList &errors, + QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { - const auto singleErrors = processGeometry( layerFeature.geometry(), QVariantMap() ); + const auto singleErrors = processGeometry( layerFeature.geometry() ); for ( const auto error : singleErrors ) errors.append( convertToGeometryCheckError( error, layerFeature ) ); } @@ -64,7 +63,7 @@ bool QgsSingleGeometryCheckError::handleChanges( const QListerrorDescription(); + return mCheck->description(); } const QgsSingleGeometryCheck *QgsSingleGeometryCheckError::check() const diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h index 7cab846cc7d4..adce013afa2d 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h @@ -16,13 +16,12 @@ email : matthias@opengis.ch #ifndef QGSSINGLEGEOMETRYCHECK_H #define QGSSINGLEGEOMETRYCHECK_H -#define SIP_NO_FILE - #include #include #include "qgsgeometry.h" #include "qgsgeometrycheck.h" +#include "qgsgeometrycheckerror.h" #include "qgis_analysis.h" @@ -64,7 +63,7 @@ class ANALYSIS_EXPORT QgsSingleGeometryCheckError /** * Apply a list of \a changes. */ - virtual bool handleChanges( const QList &changes ); + virtual bool handleChanges( const QList &changes ) SIP_SKIP; /** * A human readable description of this error. @@ -117,7 +116,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckErrorSingle : public QgsGeometryCheckError */ QgsSingleGeometryCheckError *singleError() const; - bool handleChanges( const QgsGeometryCheck::Changes &changes ) override; + bool handleChanges( const QgsGeometryCheck::Changes &changes ) override SIP_SKIP; private: QgsSingleGeometryCheckError *mError = nullptr; @@ -136,9 +135,17 @@ class ANALYSIS_EXPORT QgsGeometryCheckErrorSingle : public QgsGeometryCheckError class ANALYSIS_EXPORT QgsSingleGeometryCheck : public QgsGeometryCheck { public: - QgsSingleGeometryCheck( CheckType checkType, const QList &compatibleGeometryTypes, QgsGeometryCheckerContext *context ); + QgsSingleGeometryCheck( CheckType checkType, + const QgsGeometryCheckContext *context, + const QVariantMap &configuration ) + : QgsGeometryCheck( checkType, context, configuration ) + {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const final; + void collectErrors( const QMap &featurePools, + QList &errors, + QStringList &messages, + QgsFeedback *feedback = nullptr, + const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const final; /** * Check the \a geometry for errors. It may make use of \a configuration options. @@ -148,7 +155,7 @@ class ANALYSIS_EXPORT QgsSingleGeometryCheck : public QgsGeometryCheck * * \since QGIS 3.4 */ - virtual QList processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const = 0; + virtual QList processGeometry( const QgsGeometry &geometry ) const = 0; private: diff --git a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp new file mode 100644 index 000000000000..0c7a8a25bab4 --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** + qgsvectorlayerfeaturepool.h + -------------------------------------- +Date : 18.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsvectorlayerfeaturepool.h" +#include "qgsthreadingutils.h" + +#include "qgsfeaturerequest.h" + +QgsVectorLayerFeaturePool::QgsVectorLayerFeaturePool( QgsVectorLayer *layer ) + : QgsFeaturePool( layer ) +{ + // Build spatial index + QgsFeature feature; + QgsFeatureRequest req; + QgsFeatureIds featureIds; + + QgsFeatureIterator it = layer->getFeatures( req ); + while ( it.nextFeature( feature ) ) + { + if ( feature.geometry() ) + { + insertFeature( feature ); + featureIds.insert( feature.id() ); + } + else + { + featureIds.remove( feature.id() ); + } + } + setFeatureIds( featureIds ); +} + +bool QgsVectorLayerFeaturePool::addFeature( QgsFeature &feature, Flags flags ) +{ + Q_UNUSED( flags ); + + bool res = false; + + auto addFeatureSynchronized = [ this, &feature, &res ]() + { + QgsVectorLayer *lyr = layer(); + if ( lyr ) + res = lyr->addFeature( feature ); + }; + + QgsThreadingUtils::runOnMainThread( addFeatureSynchronized ); + + if ( !res ) + return false; + +#if 0 + if ( mSelectedOnly ) + { + QgsThreadingUtils::runOnMainThread( [ this, feature ]() + { + QgsVectorLayer *lyr = layer(); + if ( lyr ) + { + QgsFeatureIds selectedFeatureIds = lyr->selectedFeatureIds(); + selectedFeatureIds.insert( feature.id() ); + lyr->selectByIds( selectedFeatureIds ); + } + } ); + } +#endif + insertFeature( feature ); + + return res; +} + +bool QgsVectorLayerFeaturePool::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags ) +{ + Q_UNUSED( flags ); + + bool res = false; + + auto addFeatureSynchronized = [ this, &features, &res ]() + { + QgsVectorLayer *lyr = layer(); + if ( lyr ) + res = lyr->addFeatures( features ); + }; + + QgsThreadingUtils::runOnMainThread( addFeatureSynchronized ); + + if ( !res ) + return false; + +#if 0 + if ( mSelectedOnly ) + { + QgsThreadingUtils::runOnMainThread( [ this, features ]() + { + QgsVectorLayer *lyr = layer(); + if ( lyr ) + { + QgsFeatureIds selectedFeatureIds = lyr->selectedFeatureIds(); + for ( const QgsFeature &feature : qgis::as_const( features ) ) + selectedFeatureIds.insert( feature.id() ); + lyr->selectByIds( selectedFeatureIds ); + } + } ); + } +#endif + + for ( const QgsFeature &feature : qgis::as_const( features ) ) + insertFeature( feature ); + + return res; +} + +void QgsVectorLayerFeaturePool::updateFeature( QgsFeature &feature ) +{ + QgsThreadingUtils::runOnMainThread( [this, &feature]() + { + QgsVectorLayer *lyr = layer(); + if ( lyr ) + { + lyr->updateFeature( feature ); + } + } ); + + refreshCache( feature ); +} + +void QgsVectorLayerFeaturePool::deleteFeature( QgsFeatureId fid ) +{ + removeFeature( fid ); + QgsThreadingUtils::runOnMainThread( [this, fid]() + { + QgsVectorLayer *lyr = layer(); + if ( lyr ) + { + lyr->deleteFeatures( QgsFeatureIds() << fid ); + } + } ); +} diff --git a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.h b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.h new file mode 100644 index 000000000000..8444a3c4c8ec --- /dev/null +++ b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.h @@ -0,0 +1,41 @@ +/*************************************************************************** + qgsvectorlayerfeaturepool.h + -------------------------------------- +Date : 18.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSVECTORLAYERFEATUREPOOL_H +#define QGSVECTORLAYERFEATUREPOOL_H + +#include "qgsfeaturepool.h" +#include "qgsvectorlayer.h" + +#define SIP_NO_FILE + +/** + * \ingroup analysis + * A feature pool based on a vector data provider. + * + * \since QGIS 3.4 + */ +class ANALYSIS_EXPORT QgsVectorLayerFeaturePool : public QgsFeaturePool +{ + public: + QgsVectorLayerFeaturePool( QgsVectorLayer *layer ); + + bool addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags = nullptr ) override; + bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = nullptr ) override; + void updateFeature( QgsFeature &feature ) override; + void deleteFeature( QgsFeatureId fid ) override; +}; + +#endif // QGSVECTORLAYERFEATUREPOOL_H diff --git a/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp b/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp index 7aeaba6a73f6..f6aadf670d70 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp @@ -14,6 +14,7 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrycheckerfixsummarydialog.h" #include "qgsgeometrychecker.h" #include "qgsgeometrycheck.h" @@ -21,8 +22,11 @@ #include "qgisinterface.h" #include "qgsmapcanvas.h" #include "qgsvectorlayer.h" +#include "qgsgeometrycheckerror.h" -QgsGeometryCheckerFixSummaryDialog::QgsGeometryCheckerFixSummaryDialog( const Statistics &stats, QgsGeometryChecker *checker, QWidget *parent ) +QgsGeometryCheckerFixSummaryDialog::QgsGeometryCheckerFixSummaryDialog( const Statistics &stats, + QgsGeometryChecker *checker, + QWidget *parent ) : QDialog( parent ) , mChecker( checker ) { @@ -75,7 +79,7 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome int row = table->rowCount(); table->insertRow( row ); - table->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->layer()->name() : "" ) ); + table->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->featurePools()[error->layerId()]->layer()->name() : "" ) ); QTableWidgetItem *idItem = new QTableWidgetItem(); idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() ); table->setItem( row, 1, idItem ); diff --git a/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.h b/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.h index bb1de59ffb0e..735ce9be74c6 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.h +++ b/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.h @@ -24,6 +24,7 @@ class QgisInterface; class QgsGeometryCheckError; class QgsGeometryChecker; +class QgsFeaturePool; class QgsGeometryCheckerFixSummaryDialog : public QDialog { diff --git a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp index 984de8977af0..008f3fd2e716 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp @@ -21,6 +21,7 @@ #include #include +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrycheckerresulttab.h" #include "qgsgeometrycheckfixdialog.h" @@ -41,6 +42,7 @@ #include "qgsvscrollarea.h" #include "qgssettings.h" #include "qgsscrollarea.h" +#include "qgsgeometrycheckerror.h" QString QgsGeometryCheckerResultTab::sSettingsGroup = QStringLiteral( "/geometry_checker/default_fix_methods/" ); @@ -55,9 +57,9 @@ QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface, mFixedCount = 0; mCloseable = true; - for ( const QString &layerId : mChecker->getContext()->featurePools.keys() ) + for ( const QString &layerId : mChecker->featurePools().keys() ) { - QgsVectorLayer *layer = mChecker->getContext()->featurePools[layerId]->layer(); + QgsVectorLayer *layer = mChecker->featurePools()[layerId]->layer(); QTreeWidgetItem *item = new QTreeWidgetItem( ui.treeWidgetMergeAttribute, QStringList() << layer->name() << "" ); QComboBox *attribCombo = new QComboBox(); for ( int i = 0, n = layer->fields().count(); i < n; ++i ) @@ -83,7 +85,7 @@ QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface, connect( ui.pushButtonExport, &QAbstractButton::clicked, this, &QgsGeometryCheckerResultTab::exportErrors ); bool allLayersEditable = true; - for ( const QgsFeaturePool *featurePool : mChecker->getContext()->featurePools.values() ) + for ( const QgsFeaturePool *featurePool : mChecker->featurePools().values() ) { if ( ( featurePool->layer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 ) { @@ -148,7 +150,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error ) ui.tableWidgetErrors->insertRow( row ); QTableWidgetItem *idItem = new QTableWidgetItem(); idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() ); - ui.tableWidgetErrors->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->layer()->name() : "" ) ); + ui.tableWidgetErrors->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->featurePools()[error->layerId()]->layer()->name() : "" ) ); ui.tableWidgetErrors->setItem( row, 1, idItem ); ui.tableWidgetErrors->setItem( row, 2, new QTableWidgetItem( error->description() ) ); ui.tableWidgetErrors->setItem( row, 3, new QTableWidgetItem( posStr ) ); @@ -221,7 +223,7 @@ void QgsGeometryCheckerResultTab::updateError( QgsGeometryCheckError *error, boo void QgsGeometryCheckerResultTab::exportErrors() { QString initialdir; - QDir dir = QFileInfo( mChecker->getContext()->featurePools.first()->layer()->dataProvider()->dataSourceUri() ).dir(); + QDir dir = QFileInfo( mChecker->featurePools().first()->layer()->dataProvider()->dataSourceUri() ).dir(); if ( dir.exists() ) { initialdir = dir.absolutePath(); @@ -280,7 +282,7 @@ bool QgsGeometryCheckerResultTab::exportErrorsDo( const QString &file ) for ( int row = 0, nRows = ui.tableWidgetErrors->rowCount(); row < nRows; ++row ) { QgsGeometryCheckError *error = ui.tableWidgetErrors->item( row, 0 )->data( Qt::UserRole ).value(); - QgsVectorLayer *srcLayer = mChecker->getContext()->featurePools[error->layerId()]->layer(); + QgsVectorLayer *srcLayer = mChecker->featurePools()[error->layerId()]->layer(); QgsFeature f( layer->fields() ); f.setAttribute( fieldLayer, srcLayer->name() ); f.setAttribute( fieldFeatureId, error->featureId() ); @@ -454,7 +456,7 @@ void QgsGeometryCheckerResultTab::openAttributeTable() { mAttribTableDialogs[layerId]->close(); } - mAttribTableDialogs[layerId] = mIface->showAttributeTable( mChecker->getContext()->featurePools[layerId]->layer(), expr.join( QStringLiteral( " or " ) ) ); + mAttribTableDialogs[layerId] = mIface->showAttributeTable( mChecker->featurePools()[layerId]->layer(), expr.join( QStringLiteral( " or " ) ) ); } } @@ -517,7 +519,7 @@ void QgsGeometryCheckerResultTab::fixErrors( bool prompt ) for ( QgsGeometryCheckError *error : qgis::as_const( errors ) ) { - int fixMethod = QgsSettings().value( sSettingsGroup + error->check()->errorName(), QVariant::fromValue( 0 ) ).toInt(); + int fixMethod = QgsSettings().value( sSettingsGroup + error->check()->id(), QVariant::fromValue( 0 ) ).toInt(); mChecker->fixError( error, fixMethod ); ui.progressBarFixErrors->setValue( ui.progressBarFixErrors->value() + 1 ); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); @@ -526,9 +528,9 @@ void QgsGeometryCheckerResultTab::fixErrors( bool prompt ) ui.progressBarFixErrors->setVisible( false ); unsetCursor(); } - for ( const QString &layerId : mChecker->getContext()->featurePools.keys() ) + for ( const QString &layerId : mChecker->featurePools().keys() ) { - mChecker->getContext()->featurePools[layerId]->layer()->triggerRepaint(); + mChecker->featurePools()[layerId]->layer()->triggerRepaint(); } if ( mStatistics.itemCount() > 0 ) @@ -576,15 +578,15 @@ void QgsGeometryCheckerResultTab::setDefaultResolutionMethods() for ( const QgsGeometryCheck *check : mChecker->getChecks() ) { QGroupBox *groupBox = new QGroupBox( scrollAreaContents ); - groupBox->setTitle( check->errorDescription() ); + groupBox->setTitle( check->description() ); groupBox->setFlat( true ); QVBoxLayout *groupBoxLayout = new QVBoxLayout( groupBox ); groupBoxLayout->setContentsMargins( 2, 0, 2, 2 ); QButtonGroup *radioGroup = new QButtonGroup( groupBox ); - radioGroup->setProperty( "errorType", check->errorName() ); + radioGroup->setProperty( "errorType", check->id() ); int id = 0; - int checkedId = QgsSettings().value( sSettingsGroup + check->errorName(), QVariant::fromValue( 0 ) ).toInt(); + int checkedId = QgsSettings().value( sSettingsGroup + check->id(), QVariant::fromValue( 0 ) ).toInt(); for ( const QString &method : check->resolutionMethods() ) { QRadioButton *radio = new QRadioButton( method, groupBox ); @@ -614,7 +616,7 @@ void QgsGeometryCheckerResultTab::storeDefaultResolutionMethod( int id ) const void QgsGeometryCheckerResultTab::checkRemovedLayer( const QStringList &ids ) { bool requiredLayersRemoved = false; - for ( const QString &layerId : mChecker->getContext()->featurePools.keys() ) + for ( const QString &layerId : mChecker->featurePools().keys() ) { if ( ids.contains( layerId ) ) { diff --git a/src/plugins/geometry_checker/qgsgeometrycheckersetuptab.cpp b/src/plugins/geometry_checker/qgsgeometrycheckersetuptab.cpp index bf7f1983dd1e..bdd23d718384 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckersetuptab.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckersetuptab.cpp @@ -15,6 +15,7 @@ * * ***************************************************************************/ +#include "qgsgeometrycheckcontext.h" #include "qgsgeometrycheckersetuptab.h" #include "qgsgeometrycheckerresulttab.h" #include "qgsgeometrychecker.h" @@ -425,7 +426,7 @@ void QgsGeometryCheckerSetupTab::runChecks() featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, selectedOnly ) ); } - QgsGeometryCheckerContext *context = new QgsGeometryCheckerContext( ui.spinBoxTolerance->value(), QgsProject::instance()->crs(), featurePools, QgsProject::instance()->transformContext() ); + QgsGeometryCheckContext *context = new QgsGeometryCheckContext( ui.spinBoxTolerance->value(), QgsProject::instance()->crs(), QgsProject::instance()->transformContext() ); QList checks; for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() ) @@ -436,7 +437,7 @@ void QgsGeometryCheckerSetupTab::runChecks() checks.append( check ); } } - QgsGeometryChecker *checker = new QgsGeometryChecker( checks, context ); + QgsGeometryChecker *checker = new QgsGeometryChecker( checks, featurePools ); emit checkerStarted( checker ); diff --git a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp index a9abc9fb31bf..dc7538850218 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp @@ -58,13 +58,15 @@ template<> bool QgsGeometryCheckFactoryT::checkApplicabil return ui.checkBoxAngle->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkAngle", ui.checkBoxAngle->isChecked() ); QgsSettings().setValue( sSettingsGroup + "minimalAngle", ui.doubleSpinBoxAngle->value() ); + QVariantMap configuration; + configuration.insert( "minAngle", ui.doubleSpinBoxAngle->value() ); if ( ui.checkBoxAngle->isEnabled() && ui.checkBoxAngle->isChecked() ) { - return new QgsGeometryAngleCheck( context, ui.doubleSpinBoxAngle->value() ); + return new QgsGeometryAngleCheck( context, configuration ); } else { @@ -89,13 +91,15 @@ template<> bool QgsGeometryCheckFactoryT::checkApplicabili return ui.checkBoxArea->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkArea", ui.checkBoxArea->isChecked() ); QgsSettings().setValue( sSettingsGroup + "minimalArea", ui.doubleSpinBoxArea->value() ); + QVariantMap configuration; + configuration.insert( "areaThreshold", ui.doubleSpinBoxAngle->value() ); if ( ui.checkBoxArea->isEnabled() && ui.checkBoxArea->isChecked() ) { - return new QgsGeometryAreaCheck( context, ui.doubleSpinBoxArea->value() ); + return new QgsGeometryAreaCheck( context, configuration ); } else { @@ -118,12 +122,12 @@ template<> bool QgsGeometryCheckFactoryT::checkApplic return ui.checkBoxCovered->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkCovers", ui.checkBoxCovered->isChecked() ); if ( ui.checkBoxCovered->isEnabled() && ui.checkBoxCovered->isChecked() ) { - return new QgsGeometryContainedCheck( context ); + return new QgsGeometryContainedCheck( context, QVariantMap() ); } else { @@ -146,12 +150,12 @@ template<> bool QgsGeometryCheckFactoryT::checkApplicabi return ui.checkBoxDangle->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkDangle", ui.checkBoxDangle->isChecked() ); if ( ui.checkBoxDangle->isEnabled() && ui.checkBoxDangle->isChecked() ) { - return new QgsGeometryDangleCheck( context ); + return new QgsGeometryDangleCheck( context, QVariantMap() ); } else { @@ -174,12 +178,12 @@ template<> bool QgsGeometryCheckFactoryT::che return ui.checkBoxDegeneratePolygon->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkDegeneratePolygon", ui.checkBoxDegeneratePolygon->isChecked() ); if ( ui.checkBoxDegeneratePolygon->isEnabled() && ui.checkBoxDegeneratePolygon->isChecked() ) { - return new QgsGeometryDegeneratePolygonCheck( context ); + return new QgsGeometryDegeneratePolygonCheck( context, QVariantMap() ); } else { @@ -202,12 +206,12 @@ template<> bool QgsGeometryCheckFactoryT::checkApplic return ui.checkBoxDuplicates->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkDuplicates", ui.checkBoxDuplicates->isChecked() ); if ( ui.checkBoxDuplicates->isEnabled() && ui.checkBoxDuplicates->isChecked() ) { - return new QgsGeometryDuplicateCheck( context ); + return new QgsGeometryDuplicateCheck( context, QVariantMap() ); } else { @@ -230,12 +234,12 @@ template<> bool QgsGeometryCheckFactoryT::checkA return ui.checkBoxDuplicateNodes->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkDuplicateNodes", ui.checkBoxDuplicateNodes->isChecked() ); if ( ui.checkBoxDuplicateNodes->isEnabled() && ui.checkBoxDuplicateNodes->isChecked() ) { - return new QgsGeometryDuplicateNodesCheck( context ); + return new QgsGeometryDuplicateNodesCheck( context, QVariantMap() ); } else { @@ -262,13 +266,13 @@ template<> bool QgsGeometryCheckFactoryT::chec return enabled; } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkFollowBoundaries", ui.checkBoxFollowBoundaries->isChecked() ); if ( ui.checkBoxFollowBoundaries->isEnabled() && ui.checkBoxFollowBoundaries->isChecked() ) { QgsVectorLayer *checkLayer = qobject_cast( QgsProject::instance()->mapLayer( ui.comboBoxFollowBoundaries->currentData().toString() ) ); - return new QgsGeometryFollowBoundariesCheck( context, checkLayer ); + return new QgsGeometryFollowBoundariesCheck( context, QVariantMap(), checkLayer ); } else { @@ -293,13 +297,16 @@ template<> bool QgsGeometryCheckFactoryT::checkApplicabilit return ui.checkBoxGaps->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkGaps", ui.checkBoxGaps->isChecked() ); QgsSettings().setValue( sSettingsGroup + "maxGapArea", ui.doubleSpinBoxGapArea->value() ); + QVariantMap configuration; + configuration.insert( "gapThreshold", ui.doubleSpinBoxGapArea->value() ); + if ( ui.checkBoxGaps->isEnabled() && ui.checkBoxGaps->isChecked() ) { - return new QgsGeometryGapCheck( context, ui.doubleSpinBoxGapArea->value() ); + return new QgsGeometryGapCheck( context, configuration ); } else { @@ -322,12 +329,12 @@ template<> bool QgsGeometryCheckFactoryT::checkApplicabili return ui.checkBoxNoHoles->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkHoles", ui.checkBoxNoHoles->isChecked() ); if ( ui.checkBoxNoHoles->isEnabled() && ui.checkBoxNoHoles->isChecked() ) { - return new QgsGeometryHoleCheck( context ); + return new QgsGeometryHoleCheck( context, QVariantMap() ); } else { @@ -350,12 +357,12 @@ template<> bool QgsGeometryCheckFactoryT::chec return ui.checkLineIntersection->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkLineIntersection", ui.checkLineIntersection->isChecked() ); if ( ui.checkLineIntersection->isEnabled() && ui.checkLineIntersection->isChecked() ) { - return new QgsGeometryLineIntersectionCheck( context ); + return new QgsGeometryLineIntersectionCheck( context, QVariantMap() ); } else { @@ -382,12 +389,14 @@ template<> bool QgsGeometryCheckFactoryT: return enabled; } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkLineLayerIntersection", ui.checkLineLayerIntersection->isChecked() ); + QVariantMap configuration; + configuration.insert( "checkLayer", ui.comboLineLayerIntersection->currentData().toString() ); if ( ui.checkLineLayerIntersection->isEnabled() && ui.checkLineLayerIntersection->isChecked() ) { - return new QgsGeometryLineLayerIntersectionCheck( context, ui.comboLineLayerIntersection->currentData().toString() ); + return new QgsGeometryLineLayerIntersectionCheck( context, configuration ); } else { @@ -410,12 +419,12 @@ template<> bool QgsGeometryCheckFactoryT::checkApplic return ui.checkBoxMultipart->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkMultipart", ui.checkBoxMultipart->isChecked() ); if ( ui.checkBoxMultipart->isEnabled() && ui.checkBoxMultipart->isChecked() ) { - return new QgsGeometryMultipartCheck( context ); + return new QgsGeometryMultipartCheck( context, QVariantMap() ); } else { @@ -440,13 +449,15 @@ template<> bool QgsGeometryCheckFactoryT::checkApplicab return ui.checkBoxOverlaps->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkOverlaps", ui.checkBoxOverlaps->isChecked() ); QgsSettings().setValue( sSettingsGroup + "maxOverlapArea", ui.doubleSpinBoxOverlapArea->value() ); + QVariantMap configuration; + configuration.insert( "maxOverlapArea", ui.doubleSpinBoxOverlapArea->value() ); if ( ui.checkBoxOverlaps->isEnabled() && ui.checkBoxOverlaps->isChecked() ) { - return new QgsGeometryOverlapCheck( context, ui.doubleSpinBoxOverlapArea->value() ); + return new QgsGeometryOverlapCheck( context, configuration ); } else { @@ -469,12 +480,12 @@ template<> bool QgsGeometryCheckFactoryT::ch return ui.checkPointCoveredByLine->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkPointCoveredByLine", ui.checkPointCoveredByLine->isChecked() ); if ( ui.checkPointCoveredByLine->isEnabled() && ui.checkPointCoveredByLine->isChecked() ) { - return new QgsGeometryPointCoveredByLineCheck( context ); + return new QgsGeometryPointCoveredByLineCheck( context, QVariantMap() ); } else { @@ -497,12 +508,12 @@ template<> bool QgsGeometryCheckFactoryT::checkA return ui.checkPointInPolygon->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkPointInPolygon", ui.checkPointInPolygon->isChecked() ); if ( ui.checkPointInPolygon->isEnabled() && ui.checkPointInPolygon->isChecked() ) { - return new QgsGeometryPointInPolygonCheck( context ); + return new QgsGeometryPointInPolygonCheck( context, QVariantMap() ); } else { @@ -527,13 +538,15 @@ template<> bool QgsGeometryCheckFactoryT::checkAp return ui.checkBoxSegmentLength->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkSegmentLength", ui.checkBoxSegmentLength->isChecked() ); QgsSettings().setValue( sSettingsGroup + "minSegmentLength", ui.doubleSpinBoxSegmentLength->value() ); + QVariantMap configuration; + configuration.insert( "minSegmentLength", ui.doubleSpinBoxSegmentLength->value() ); if ( ui.checkBoxSegmentLength->isEnabled() && ui.checkBoxSegmentLength->isChecked() ) { - return new QgsGeometrySegmentLengthCheck( context, ui.doubleSpinBoxSegmentLength->value() ); + return new QgsGeometrySegmentLengthCheck( context, configuration ); } else { @@ -556,12 +569,12 @@ template<> bool QgsGeometryCheckFactoryT::checkAppl return ui.checkBoxSelfContacts->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkSelfContacts", ui.checkBoxSelfContacts->isChecked() ); if ( ui.checkBoxSelfContacts->isEnabled() && ui.checkBoxSelfContacts->isChecked() ) { - return new QgsGeometrySelfContactCheck( context ); + return new QgsGeometrySelfContactCheck( context, QVariantMap() ); } else { @@ -584,12 +597,12 @@ template<> bool QgsGeometryCheckFactoryT::chec return ui.checkBoxSelfIntersections->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkSelfIntersections", ui.checkBoxSelfIntersections->isChecked() ); if ( ui.checkBoxSelfIntersections->isEnabled() && ui.checkBoxSelfIntersections->isChecked() ) { - return new QgsGeometrySelfIntersectionCheck( context ); + return new QgsGeometrySelfIntersectionCheck( context, QVariantMap() ); } else { @@ -616,7 +629,7 @@ template<> bool QgsGeometryCheckFactoryT::checkAp return ui.checkBoxSliverPolygons->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { double threshold = ui.doubleSpinBoxSliverThinness->value(); double maxArea = ui.checkBoxSliverArea->isChecked() ? ui.doubleSpinBoxSliverArea->value() : 0.; @@ -624,9 +637,13 @@ template<> QgsGeometryCheck *QgsGeometryCheckFactoryTvalue() ); QgsSettings().setValue( sSettingsGroup + "sliverPolygonsThinnessThreshold", ui.doubleSpinBoxSliverThinness->value() ); QgsSettings().setValue( sSettingsGroup + "checkSliverPolygons", ui.checkBoxSliverPolygons->isChecked() ); + QVariantMap configuration; + configuration.insert( "threshold", threshold ); + configuration.insert( "maxArea", maxArea ); + if ( ui.checkBoxSliverPolygons->isEnabled() && ui.checkBoxSliverPolygons->isChecked() ) { - return new QgsGeometrySliverPolygonCheck( context, threshold, maxArea ); + return new QgsGeometrySliverPolygonCheck( context, configuration ); } else { @@ -659,7 +676,7 @@ template<> bool QgsGeometryCheckFactoryT::checkApplicabili return nPoint + nLineString + nPolygon > 0; } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkTypePoint", ui.checkBoxPoint->isChecked() ); QgsSettings().setValue( sSettingsGroup + "checkTypeMultipoint", ui.checkBoxMultipoint->isChecked() ); @@ -695,7 +712,7 @@ template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::cre } if ( allowedTypes != 0 ) { - return new QgsGeometryTypeCheck( context, allowedTypes ); + return new QgsGeometryTypeCheck( context, QVariantMap(), allowedTypes ); } else { diff --git a/src/plugins/geometry_checker/qgsgeometrycheckfactory.h b/src/plugins/geometry_checker/qgsgeometrycheckfactory.h index 6f0aee325f36..690d8e93efe5 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckfactory.h +++ b/src/plugins/geometry_checker/qgsgeometrycheckfactory.h @@ -18,7 +18,7 @@ #include "qgis.h" #include "ui_qgsgeometrycheckersetuptab.h" -struct QgsGeometryCheckerContext; +struct QgsGeometryCheckContext; class QgsGeometryCheck; class QgsGeometryCheckFactory @@ -27,7 +27,7 @@ class QgsGeometryCheckFactory virtual ~QgsGeometryCheckFactory() = default; virtual void restorePrevious( Ui::QgsGeometryCheckerSetupTab & /*ui*/ ) const = 0; virtual bool checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, int /*nPoint*/, int /*nLineString*/, int /*nPolygon*/ ) const = 0; - virtual QgsGeometryCheck *createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const = 0; + virtual QgsGeometryCheck *createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const = 0; protected: static QString sSettingsGroup; @@ -38,7 +38,7 @@ class QgsGeometryCheckFactoryT : public QgsGeometryCheckFactory { void restorePrevious( Ui::QgsGeometryCheckerSetupTab & /*ui*/ ) const override; bool checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int nPoint, int nLineString, int nPolygon ) const override; - QgsGeometryCheck *createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const override; + QgsGeometryCheck *createInstance( QgsGeometryCheckContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const override; }; class QgsGeometryCheckFactoryRegistry diff --git a/src/plugins/geometry_checker/qgsgeometrycheckfixdialog.cpp b/src/plugins/geometry_checker/qgsgeometrycheckfixdialog.cpp index ccf6be2c380e..f5f2750f3ec3 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckfixdialog.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckfixdialog.cpp @@ -14,15 +14,6 @@ * * ***************************************************************************/ -#include "qgsgeometrycheckfixdialog.h" -#include "qgsgeometrycheckerresulttab.h" -#include "qgisinterface.h" -#include "qgsmapcanvas.h" -#include "qgssettings.h" - -#include "qgsgeometrychecker.h" -#include "qgsgeometrycheck.h" - #include #include #include @@ -32,6 +23,15 @@ #include #include +#include "qgsgeometrycheckfixdialog.h" +#include "qgsgeometrycheckerresulttab.h" +#include "qgisinterface.h" +#include "qgsmapcanvas.h" +#include "qgssettings.h" +#include "qgsgeometrycheckerror.h" +#include "qgsgeometrychecker.h" +#include "qgsgeometrycheck.h" + QgsGeometryCheckerFixDialog::QgsGeometryCheckerFixDialog( QgsGeometryChecker *checker, const QList &errors, QWidget *parent ) : QDialog( parent ) @@ -105,7 +105,7 @@ void QgsGeometryCheckerFixDialog::setupNextError() mResolutionsBox->layout()->setContentsMargins( 0, 0, 0, 4 ); int id = 0; - int checkedid = QgsSettings().value( QgsGeometryCheckerResultTab::sSettingsGroup + error->check()->errorName(), QVariant::fromValue( 0 ) ).toInt(); + int checkedid = QgsSettings().value( QgsGeometryCheckerResultTab::sSettingsGroup + error->check()->id(), QVariant::fromValue( 0 ) ).toInt(); for ( const QString &method : error->check()->resolutionMethods() ) { QRadioButton *radio = new QRadioButton( method ); diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index c1cc06101ae1..f26d5f6ab288 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -23,8 +23,8 @@ :/images/icons/qgis-icon-16x16.png:/images/icons/qgis-icon-16x16.png - - + + Qt::Horizontal @@ -357,7 +357,7 @@ - 1 + 18 @@ -433,7 +433,7 @@ 0 0 - 289 + 325 389 @@ -2358,56 +2358,86 @@ border-radius: 2px; 0 - - - Automatic Fixes + + + true - - - - - <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> - - - Geometry precision - - - - - - - Remove duplicate nodes - - - - - - - - - <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> - - - [Disabled] - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + 0 + 0 + 651 + 804 + + + + + + + Automatic Fixes + + + + + + <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> + + + Geometry precision + + + + + + + Remove duplicate nodes + + + + + + + + + [Units] + + + + + + + Qt::ImhFormattedNumbersOnly + + + + + + [No precision restriction] + + + + + + + + + + + + Geometry checks + + + + + + + Topology checks + + + + + @@ -2418,7 +2448,7 @@ border-radius: 2px; - + @@ -2461,18 +2491,18 @@ border-radius: 2px; - - QgsScrollArea - QScrollArea -
qgsscrollarea.h
- 1 -
QgsCollapsibleGroupBox QGroupBox
qgscollapsiblegroupbox.h
1
+ + QgsScrollArea + QScrollArea +
qgsscrollarea.h
+ 1 +
QgsProjectionSelectionWidget QWidget @@ -2529,11 +2559,6 @@ border-radius: 2px;
qgsvectorlayerlegendwidget.h
1
- - QgsDoubleSpinBox - QDoubleSpinBox -
qgsdoublespinbox.h
-
mSearchLineEdit @@ -2596,7 +2621,6 @@ border-radius: 2px; mLayerLegendUrlLineEdit mLayerLegendUrlFormatComboBox mRemoveDuplicateNodesCheckbox - mGeometryPrecisionSpinBox diff --git a/tests/src/geometry_checker/testqgsgeometrychecks.cpp b/tests/src/geometry_checker/testqgsgeometrychecks.cpp index 38d6bdbe91c5..59d5e7177dd2 100644 --- a/tests/src/geometry_checker/testqgsgeometrychecks.cpp +++ b/tests/src/geometry_checker/testqgsgeometrychecks.cpp @@ -59,11 +59,11 @@ class TestQgsGeometryChecks: public QObject }; double layerToMapUnits( const QgsMapLayer *layer, const QgsCoordinateReferenceSystem &mapCrs ) const; QgsFeaturePool *createFeaturePool( QgsVectorLayer *layer, bool selectedOnly = false ) const; - QgsGeometryCheckerContext *createTestContext( QTemporaryDir &tempDir, QMap &layers, const QgsCoordinateReferenceSystem &mapCrs = QgsCoordinateReferenceSystem( "EPSG:4326" ), double prec = 8 ) const; - void cleanupTestContext( QgsGeometryCheckerContext *ctx ) const; + QPair > createTestContext( QTemporaryDir &tempDir, QMap &layers, const QgsCoordinateReferenceSystem &mapCrs = QgsCoordinateReferenceSystem( "EPSG:4326" ), double prec = 8 ) const; + void cleanupTestContext( QPair > ctx ) const; void listErrors( const QList &checkErrors, const QStringList &messages ) const; QList searchCheckErrors( const QList &checkErrors, const QString &layerId, const QgsFeatureId &featureId = -1, const QgsPointXY &pos = QgsPointXY(), const QgsVertexId &vid = QgsVertexId(), const QVariant &value = QVariant(), double tol = 1E-4 ) const; - bool fixCheckError( QgsGeometryCheckError *error, int method, const QgsGeometryCheckError::Status &expectedStatus, const QVector &expectedChanges, const QMap &mergeAttr = QMap() ); + bool fixCheckError( QMap featurePools, QgsGeometryCheckError *error, int method, const QgsGeometryCheckError::Status &expectedStatus, const QVector &expectedChanges, const QMap &mergeAttr = QMap() ); QgsGeometryCheck::Changes change2changes( const Change &change ) const; private slots: @@ -115,14 +115,17 @@ void TestQgsGeometryChecks::testAngleCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryAngleCheck check( context, 15 ); - check.collectErrors( checkErrors, messages ); + QVariantMap configuration; + configuration.insert( "minAngle", 15 ); + + QgsGeometryAngleCheck check( testContext.first, configuration ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -143,21 +146,21 @@ void TestQgsGeometryChecks::testAngleCheck() QgsFeature f; int n1, n2; - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); n1 = f.geometry().constGet()->vertexCount( errs1[0]->vidx().part, errs1[0]->vidx().ring ); - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryAngleCheck::DeleteNode, QgsGeometryCheckError::StatusFixed, {{errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeRemoved, errs1[0]->vidx()}} ) ); - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); n2 = f.geometry().constGet()->vertexCount( errs1[0]->vidx().part, errs1[0]->vidx().ring ); QCOMPARE( n1, n2 + 1 ); - context->featurePools[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); + testContext.second[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); n1 = f.geometry().constGet()->vertexCount( errs2[0]->vidx().part, errs2[0]->vidx().ring ); - QVERIFY( fixCheckError( errs2[0], + QVERIFY( fixCheckError( testContext.second, errs2[0], QgsGeometryAngleCheck::DeleteNode, QgsGeometryCheckError::StatusFixed, {{errs2[0]->layerId(), errs2[0]->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeRemoved, errs2[0]->vidx()}} ) ); - context->featurePools[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); + testContext.second[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); n2 = f.geometry().constGet()->vertexCount( errs2[0]->vidx().part, errs2[0]->vidx().ring ); QCOMPARE( n1, n2 + 1 ); @@ -180,7 +183,7 @@ void TestQgsGeometryChecks::testAngleCheck() QVERIFY( errs2[0]->handleChanges( change2changes( {errs2[0]->layerId(), errs2[0]->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeRemoved, QgsVertexId( errs2[0]->vidx().part, errs2[0]->vidx().ring, errs2[0]->vidx().vertex - 1 )} ) ) ); QVERIFY( errs2[0]->vidx().vertex == oldVidx.vertex - 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testAreaCheck() @@ -190,14 +193,17 @@ void TestQgsGeometryChecks::testAreaCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryAreaCheck check( context, 0.04 ); - check.collectErrors( checkErrors, messages ); + QVariantMap configuration; + configuration.insert( "areaThreshold", 0.04 ); + + QgsGeometryAreaCheck check( testContext.first, configuration ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -221,58 +227,58 @@ void TestQgsGeometryChecks::testAreaCheck() QgsFeature f; bool valid; - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryAreaCheck::Delete, QgsGeometryCheckError::StatusFixed, {{errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId()}} ) ); - valid = context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + valid = testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); QVERIFY( !valid ); // Try merging a small geometry by longest edge, largest area and common value - context->featurePools[layers["polygon_layer.shp"]]->getFeature( 15, f ); + testContext.second[layers["polygon_layer.shp"]]->getFeature( 15, f ); double area15 = f.geometry().area(); - QVERIFY( fixCheckError( errs2[0], + QVERIFY( fixCheckError( testContext.second, errs2[0], QgsGeometryAreaCheck::MergeLargestArea, QgsGeometryCheckError::StatusFixed, { {errs2[0]->layerId(), errs2[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId()}, {layers["polygon_layer.shp"], 15, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0 )}, {layers["polygon_layer.shp"], 15, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 0 )} } ) ); - context->featurePools[layers["polygon_layer.shp"]]->getFeature( 15, f ); + testContext.second[layers["polygon_layer.shp"]]->getFeature( 15, f ); QVERIFY( f.geometry().area() > area15 ); - valid = context->featurePools[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); + valid = testContext.second[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); QVERIFY( !valid ); - context->featurePools[layers["polygon_layer.shp"]]->getFeature( 18, f ); + testContext.second[layers["polygon_layer.shp"]]->getFeature( 18, f ); double area18 = f.geometry().area(); - QVERIFY( fixCheckError( errs3[0], + QVERIFY( fixCheckError( testContext.second, errs3[0], QgsGeometryAreaCheck::MergeLongestEdge, QgsGeometryCheckError::StatusFixed, { {errs3[0]->layerId(), errs3[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId()}, {layers["polygon_layer.shp"], 18, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0 )}, {layers["polygon_layer.shp"], 18, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 0 )} } ) ); - context->featurePools[layers["polygon_layer.shp"]]->getFeature( 18, f ); + testContext.second[layers["polygon_layer.shp"]]->getFeature( 18, f ); QVERIFY( f.geometry().area() > area18 ); - valid = context->featurePools[errs3[0]->layerId()]->getFeature( errs3[0]->featureId(), f ); + valid = testContext.second[errs3[0]->layerId()]->getFeature( errs3[0]->featureId(), f ); QVERIFY( !valid ); - context->featurePools[layers["polygon_layer.shp"]]->getFeature( 21, f ); + testContext.second[layers["polygon_layer.shp"]]->getFeature( 21, f ); double area21 = f.geometry().area(); QMap mergeIdx; mergeIdx.insert( layers["polygon_layer.shp"], 1 ); // 1: attribute "attr" - QVERIFY( fixCheckError( errs4[0], + QVERIFY( fixCheckError( testContext.second, errs4[0], QgsGeometryAreaCheck::MergeIdenticalAttribute, QgsGeometryCheckError::StatusFixed, { {errs4[0]->layerId(), errs4[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId()}, {layers["polygon_layer.shp"], 21, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0 )}, {layers["polygon_layer.shp"], 21, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 0 )} }, mergeIdx ) ); - context->featurePools[layers["polygon_layer.shp"]]->getFeature( 21, f ); + testContext.second[layers["polygon_layer.shp"]]->getFeature( 21, f ); QVERIFY( f.geometry().area() > area21 ); - valid = context->featurePools[errs4[0]->layerId()]->getFeature( errs4[0]->featureId(), f ); + valid = testContext.second[errs4[0]->layerId()]->getFeature( errs4[0]->featureId(), f ); QVERIFY( !valid ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testContainedCheck() @@ -282,14 +288,14 @@ void TestQgsGeometryChecks::testContainedCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryContainedCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryContainedCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -302,14 +308,14 @@ void TestQgsGeometryChecks::testContainedCheck() QVERIFY( messages.contains( "Contained check failed for (polygon_layer.shp:1): the geometry is invalid" ) ); // Test fixes - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryContainedCheck::Delete, QgsGeometryCheckError::StatusFixed, {{errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId()}} ) ); QgsFeature f; - bool valid = context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + bool valid = testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); QVERIFY( !valid ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testDangleCheck() @@ -319,14 +325,14 @@ void TestQgsGeometryChecks::testDangleCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryDangleCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryDangleCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -346,7 +352,7 @@ void TestQgsGeometryChecks::testDangleCheck() QVERIFY( errs1[0]->handleChanges( change2changes( {errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0 )} ) ) ); QVERIFY( errs1[0]->vidx().part == oldVidx.part - 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testDegeneratePolygonCheck() @@ -356,14 +362,14 @@ void TestQgsGeometryChecks::testDegeneratePolygonCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryDegeneratePolygonCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryDegeneratePolygonCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -374,14 +380,14 @@ void TestQgsGeometryChecks::testDegeneratePolygonCheck() QVERIFY( ( errs1 = searchCheckErrors( checkErrors, layers["polygon_layer.shp"], 6, QgsPointXY(), QgsVertexId( 0, 0 ) ) ).size() == 1 ); // Test fixes - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryDegeneratePolygonCheck::DeleteRing, QgsGeometryCheckError::StatusFixed, {{errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId()}} ) ); QgsFeature f; - bool valid = context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + bool valid = testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); QVERIFY( !valid ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testDuplicateCheck() @@ -391,14 +397,14 @@ void TestQgsGeometryChecks::testDuplicateCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryDuplicateCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryDuplicateCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -418,14 +424,14 @@ void TestQgsGeometryChecks::testDuplicateCheck() QgsGeometryDuplicateCheckError *dupErr = static_cast( errs1[0] ); QString dup1LayerId = dupErr->duplicates().firstKey(); QgsFeatureId dup1Fid = dupErr->duplicates()[dup1LayerId][0]; - QVERIFY( fixCheckError( dupErr, + QVERIFY( fixCheckError( testContext.second, dupErr, QgsGeometryDuplicateCheck::RemoveDuplicates, QgsGeometryCheckError::StatusFixed, {{dup1LayerId, dup1Fid, QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId()}} ) ); QgsFeature f; - bool valid = context->featurePools[dup1LayerId]->getFeature( dup1Fid, f ); + bool valid = testContext.second[dup1LayerId]->getFeature( dup1Fid, f ); QVERIFY( !valid ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testDuplicateNodesCheck() @@ -435,14 +441,14 @@ void TestQgsGeometryChecks::testDuplicateNodesCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryDuplicateNodesCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryDuplicateNodesCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -457,16 +463,16 @@ void TestQgsGeometryChecks::testDuplicateNodesCheck() // Test fixes QgsFeature f; - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); int n1 = f.geometry().constGet()->vertexCount( errs1[0]->vidx().part, errs1[0]->vidx().ring ); - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryDuplicateNodesCheck::RemoveDuplicates, QgsGeometryCheckError::StatusFixed, {{errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeNode, QgsGeometryCheck::ChangeRemoved, errs1[0]->vidx()}} ) ); - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); int n2 = f.geometry().constGet()->vertexCount( errs1[0]->vidx().part, errs1[0]->vidx().ring ); QCOMPARE( n1, n2 + 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testFollowBoundariesCheck() @@ -475,20 +481,20 @@ void TestQgsGeometryChecks::testFollowBoundariesCheck() QMap layers; layers.insert( "follow_ref.shp", "" ); layers.insert( "follow_subj.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryFollowBoundariesCheck( context, context->featurePools[layers["follow_ref.shp"]]->layer() ).collectErrors( checkErrors, messages ); + QgsGeometryFollowBoundariesCheck( testContext.first, QVariantMap(), testContext.second[layers["follow_ref.shp"]]->layer() ).collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 2 ); QVERIFY( searchCheckErrors( checkErrors, layers["follow_subj.shp"], 1 ).size() == 1 ); QVERIFY( searchCheckErrors( checkErrors, layers["follow_subj.shp"], 3 ).size() == 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testGapCheck() @@ -496,14 +502,17 @@ void TestQgsGeometryChecks::testGapCheck() QTemporaryDir dir; QMap layers; layers.insert( "gap_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryGapCheck check( context, 0.01 ); - check.collectErrors( checkErrors, messages ); + QVariantMap configuration; + configuration.insert( "gapThreshold", 0.01 ); + + QgsGeometryGapCheck check( testContext.first, configuration ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -518,18 +527,18 @@ void TestQgsGeometryChecks::testGapCheck() // Test fixes QgsFeature f; - context->featurePools[layers["gap_layer.shp"]]->getFeature( 0, f ); + testContext.second[layers["gap_layer.shp"]]->getFeature( 0, f ); double areaOld = f.geometry().area(); - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryGapCheck::MergeLongestEdge, QgsGeometryCheckError::StatusFixed, { {layers["gap_layer.shp"], 0, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0 )}, {layers["gap_layer.shp"], 0, QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 0 )} } ) ); - context->featurePools[layers["gap_layer.shp"]]->getFeature( 0, f ); + testContext.second[layers["gap_layer.shp"]]->getFeature( 0, f ); QVERIFY( f.geometry().area() > areaOld ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testMissingVertexCheck() @@ -537,17 +546,17 @@ void TestQgsGeometryChecks::testMissingVertexCheck() QTemporaryDir dir; QMap layers; layers.insert( QStringLiteral( "missing_vertex.gpkg" ), QString() ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryMissingVertexCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryMissingVertexCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); - const QString layerId = layers.values().first(); + const QString layerId = testContext.second.first()->layerId(); QVERIFY( searchCheckErrors( checkErrors, layerId, 0, QgsPointXY( 0.251153, -0.460895 ), QgsVertexId() ).size() == 1 ); QVERIFY( searchCheckErrors( checkErrors, layerId, 3, QgsPointXY( 0.257985, -0.932886 ), QgsVertexId() ).size() == 1 ); QVERIFY( searchCheckErrors( checkErrors, layerId, 5, QgsPointXY( 0.59781, -0.480033 ), QgsVertexId() ).size() == 1 ); @@ -556,7 +565,7 @@ void TestQgsGeometryChecks::testMissingVertexCheck() QCOMPARE( checkErrors.size(), 5 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testHoleCheck() @@ -566,14 +575,14 @@ void TestQgsGeometryChecks::testHoleCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryHoleCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryHoleCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -586,15 +595,15 @@ void TestQgsGeometryChecks::testHoleCheck() // Test fixes QgsFeature f; - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryHoleCheck::RemoveHoles, QgsGeometryCheckError::StatusFixed, { {errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeRing, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0, 1 )} } ) ); - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); QVERIFY( f.geometry().constGet()->ringCount( 0 ) == 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testLineIntersectionCheck() @@ -604,14 +613,14 @@ void TestQgsGeometryChecks::testLineIntersectionCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryLineIntersectionCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryLineIntersectionCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 1 ); @@ -619,7 +628,7 @@ void TestQgsGeometryChecks::testLineIntersectionCheck() QVERIFY( searchCheckErrors( checkErrors, layers["polygon_layer.shp"] ).isEmpty() ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"], 1, QgsPointXY( -0.5594, 0.4098 ), QgsVertexId( 0 ), QVariant( "line_layer.shp:0" ) ).size() == 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testLineLayerIntersectionCheck() @@ -629,14 +638,17 @@ void TestQgsGeometryChecks::testLineLayerIntersectionCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryLineLayerIntersectionCheck check( context, layers["polygon_layer.shp"] ); - check.collectErrors( checkErrors, messages ); + QVariantMap configuration; + configuration.insert( "checkLayer", layers["polygon_layer.shp"] ); + + QgsGeometryLineLayerIntersectionCheck check( testContext.first, configuration ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 5 ); @@ -648,7 +660,7 @@ void TestQgsGeometryChecks::testLineLayerIntersectionCheck() QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"], 7, QgsPointXY( 0.9906, 1.1169 ), QgsVertexId( 0 ), QVariant( "polygon_layer.shp:2" ) ).size() == 1 ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"], 7, QgsPointXY( 1.0133, 1.0772 ), QgsVertexId( 0 ), QVariant( "polygon_layer.shp:2" ) ).size() == 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testMultipartCheck() @@ -658,21 +670,21 @@ void TestQgsGeometryChecks::testMultipartCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryMultipartCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryMultipartCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QVERIFY( searchCheckErrors( checkErrors, layers["point_layer.shp"] ).isEmpty() ); // Easier to ensure that multipart features don't appear as errors than verifying each single-part multi-type feature - QVERIFY( QgsWkbTypes::isSingleType( context->featurePools[layers["point_layer.shp"]]->layer()->wkbType() ) ); - QVERIFY( QgsWkbTypes::isMultiType( context->featurePools[layers["line_layer.shp"]]->layer()->wkbType() ) ); - QVERIFY( QgsWkbTypes::isMultiType( context->featurePools[layers["polygon_layer.shp"]]->layer()->wkbType() ) ); + QVERIFY( QgsWkbTypes::isSingleType( testContext.second[layers["point_layer.shp"]]->layer()->wkbType() ) ); + QVERIFY( QgsWkbTypes::isMultiType( testContext.second[layers["line_layer.shp"]]->layer()->wkbType() ) ); + QVERIFY( QgsWkbTypes::isMultiType( testContext.second[layers["polygon_layer.shp"]]->layer()->wkbType() ) ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"] ).size() > 0 ); QVERIFY( searchCheckErrors( checkErrors, layers["polygon_layer.shp"] ).size() > 0 ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"], 0 ).isEmpty() ); @@ -689,24 +701,24 @@ void TestQgsGeometryChecks::testMultipartCheck() QgsFeature f; #if 0 // The ogr provider apparently automatically re-converts the geometry type to a multitype... - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryMultipartCheck::ConvertToSingle, QgsGeometryCheckError::StatusFixed, { {errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeChanged, QgsVertexId( )} } ) ); - context->featurePools[errs1[0]->layerId()]->get( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->get( errs1[0]->featureId(), f ); QVERIFY( QgsWkbTypes::isSingleType( f.geometry().geometry()->wkbType() ) ); #endif - QVERIFY( fixCheckError( errs2[0], + QVERIFY( fixCheckError( testContext.second, errs2[0], QgsGeometryMultipartCheck::RemoveObject, QgsGeometryCheckError::StatusFixed, { {errs2[0]->layerId(), errs2[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeRemoved, QgsVertexId( )} } ) ); - bool valid = context->featurePools[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); + bool valid = testContext.second[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); QVERIFY( !valid ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testOverlapCheck() @@ -716,14 +728,17 @@ void TestQgsGeometryChecks::testOverlapCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryOverlapCheck check( context, 0.01 ); - check.collectErrors( checkErrors, messages ); + QVariantMap configuration; + configuration.insert( "maxOverlapArea", 0.01 ); + + QgsGeometryOverlapCheck check( testContext.first, configuration ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -737,17 +752,17 @@ void TestQgsGeometryChecks::testOverlapCheck() // Test fixes QgsFeature f; - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); double areaOld = f.geometry().area(); - QVERIFY( fixCheckError( errs1[0], + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometryOverlapCheck::Subtract, QgsGeometryCheckError::StatusFixed, { {errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeChanged, QgsVertexId( )} } ) ); - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); QVERIFY( f.geometry().area() < areaOld ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testPointCoveredByLineCheck() @@ -757,14 +772,14 @@ void TestQgsGeometryChecks::testPointCoveredByLineCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryPointCoveredByLineCheck errs( context ); - errs.collectErrors( checkErrors, messages ); + QgsGeometryPointCoveredByLineCheck errs( testContext.first, QVariantMap() ); + errs.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"] ).isEmpty() ); @@ -773,7 +788,7 @@ void TestQgsGeometryChecks::testPointCoveredByLineCheck() QVERIFY( searchCheckErrors( checkErrors, layers["point_layer.shp"], 0 ).isEmpty() ); QVERIFY( searchCheckErrors( checkErrors, layers["point_layer.shp"], 1 ).isEmpty() ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testPointInPolygonCheck() @@ -783,14 +798,14 @@ void TestQgsGeometryChecks::testPointInPolygonCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometryPointInPolygonCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometryPointInPolygonCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"] ).isEmpty() ); @@ -799,7 +814,7 @@ void TestQgsGeometryChecks::testPointInPolygonCheck() QVERIFY( searchCheckErrors( checkErrors, layers["point_layer.shp"], 5 ).isEmpty() ); QVERIFY( messages.contains( "Point in polygon check failed for (polygon_layer.shp:1): the geometry is invalid" ) ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testSegmentLengthCheck() @@ -809,14 +824,17 @@ void TestQgsGeometryChecks::testSegmentLengthCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometrySegmentLengthCheck check( context, 0.03 ); - check.collectErrors( checkErrors, messages ); + QVariantMap configuration; + configuration.insert( "minSegmentLength", 0.03 ); + + QgsGeometrySegmentLengthCheck check( testContext.first, configuration ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 4 ); @@ -826,7 +844,7 @@ void TestQgsGeometryChecks::testSegmentLengthCheck() QVERIFY( searchCheckErrors( checkErrors, layers["polygon_layer.shp"], 10, QgsPointXY( -0.2819, 1.3553 ), QgsVertexId( 0, 0, 2 ), 0.0281 ).size() == 1 ); QVERIFY( searchCheckErrors( checkErrors, layers["polygon_layer.shp"], 11, QgsPointXY( -0.2819, 1.3553 ), QgsVertexId( 0, 0, 0 ), 0.0281 ).size() == 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testSelfContactCheck() @@ -836,14 +854,14 @@ void TestQgsGeometryChecks::testSelfContactCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometrySelfContactCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometrySelfContactCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 3 ); @@ -852,7 +870,7 @@ void TestQgsGeometryChecks::testSelfContactCheck() QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"], 5, QgsPointXY( -1.2399, -1.0502 ), QgsVertexId( 0, 0, 6 ) ).size() == 1 ); QVERIFY( searchCheckErrors( checkErrors, layers["polygon_layer.shp"], 9, QgsPointXY( -0.2080, 1.9830 ), QgsVertexId( 0, 0, 3 ) ).size() == 1 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testSelfIntersectionCheck() @@ -862,14 +880,14 @@ void TestQgsGeometryChecks::testSelfIntersectionCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometrySelfIntersectionCheck check( context ); - check.collectErrors( checkErrors, messages ); + QgsGeometrySelfIntersectionCheck check( testContext.first, QVariantMap() ); + check.collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QList errs1; @@ -888,55 +906,55 @@ void TestQgsGeometryChecks::testSelfIntersectionCheck() // Test fixes QgsFeature f; - int nextId = context->featurePools[errs1[0]->layerId()]->layer()->featureCount(); - QVERIFY( fixCheckError( errs1[0], + int nextId = testContext.second[errs1[0]->layerId()]->layer()->featureCount(); + QVERIFY( fixCheckError( testContext.second, errs1[0], QgsGeometrySelfIntersectionCheck::ToSingleObjects, QgsGeometryCheckError::StatusFixed, { {errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0 )}, {errs1[0]->layerId(), errs1[0]->featureId(), QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 0 )}, {errs1[0]->layerId(), nextId, QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeAdded, QgsVertexId()} } ) ); - context->featurePools[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); + testContext.second[errs1[0]->layerId()]->getFeature( errs1[0]->featureId(), f ); QCOMPARE( f.geometry().constGet()->partCount(), 1 ); QCOMPARE( f.geometry().constGet()->vertexCount(), 4 ); - context->featurePools[errs1[0]->layerId()]->getFeature( nextId, f ); + testContext.second[errs1[0]->layerId()]->getFeature( nextId, f ); QCOMPARE( f.geometry().constGet()->partCount(), 1 ); QCOMPARE( f.geometry().constGet()->vertexCount(), 6 ); - QVERIFY( fixCheckError( errs2[0], + QVERIFY( fixCheckError( testContext.second, errs2[0], QgsGeometrySelfIntersectionCheck::ToMultiObject, QgsGeometryCheckError::StatusFixed, { {errs2[0]->layerId(), errs2[0]->featureId(), QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0 )}, {errs2[0]->layerId(), errs2[0]->featureId(), QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 0 )}, {errs2[0]->layerId(), errs2[0]->featureId(), QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 1 )} } ) ); - context->featurePools[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); + testContext.second[errs2[0]->layerId()]->getFeature( errs2[0]->featureId(), f ); QCOMPARE( f.geometry().constGet()->partCount(), 2 ); QCOMPARE( f.geometry().constGet()->vertexCount( 0 ), 4 ); QCOMPARE( f.geometry().constGet()->vertexCount( 1 ), 5 ); - nextId = context->featurePools[errs3[0]->layerId()]->layer()->featureCount(); - QVERIFY( fixCheckError( errs3[0], + nextId = testContext.second[errs3[0]->layerId()]->layer()->featureCount(); + QVERIFY( fixCheckError( testContext.second, errs3[0], QgsGeometrySelfIntersectionCheck::ToSingleObjects, QgsGeometryCheckError::StatusFixed, { {errs3[0]->layerId(), errs3[0]->featureId(), QgsGeometryCheck::ChangeRing, QgsGeometryCheck::ChangeChanged, QgsVertexId( 0, 0 )}, {errs3[0]->layerId(), nextId, QgsGeometryCheck::ChangeFeature, QgsGeometryCheck::ChangeAdded, QgsVertexId()} } ) ); - context->featurePools[errs3[0]->layerId()]->getFeature( errs3[0]->featureId(), f ); + testContext.second[errs3[0]->layerId()]->getFeature( errs3[0]->featureId(), f ); QCOMPARE( f.geometry().constGet()->partCount(), 1 ); QCOMPARE( f.geometry().constGet()->vertexCount(), 6 ); - context->featurePools[errs3[0]->layerId()]->getFeature( nextId, f ); + testContext.second[errs3[0]->layerId()]->getFeature( nextId, f ); QCOMPARE( f.geometry().constGet()->partCount(), 1 ); QCOMPARE( f.geometry().constGet()->vertexCount(), 4 ); - QVERIFY( fixCheckError( errs4[0], + QVERIFY( fixCheckError( testContext.second, errs4[0], QgsGeometrySelfIntersectionCheck::ToMultiObject, QgsGeometryCheckError::StatusFixed, { {errs4[0]->layerId(), errs4[0]->featureId(), QgsGeometryCheck::ChangeRing, QgsGeometryCheck::ChangeChanged, QgsVertexId( 0, 0 )}, {errs4[0]->layerId(), errs4[0]->featureId(), QgsGeometryCheck::ChangeRing, QgsGeometryCheck::ChangeRemoved, QgsVertexId( 0, 1 )}, {errs4[0]->layerId(), errs4[0]->featureId(), QgsGeometryCheck::ChangePart, QgsGeometryCheck::ChangeAdded, QgsVertexId( 1 )} } ) ); - context->featurePools[errs4[0]->layerId()]->getFeature( errs4[0]->featureId(), f ); + testContext.second[errs4[0]->layerId()]->getFeature( errs4[0]->featureId(), f ); QCOMPARE( f.geometry().constGet()->partCount(), 2 ); QCOMPARE( f.geometry().constGet()->ringCount( 0 ), 1 ); QCOMPARE( f.geometry().constGet()->vertexCount( 0, 0 ), 5 ); @@ -956,7 +974,7 @@ void TestQgsGeometryChecks::testSelfIntersectionCheck() QVERIFY( oldInter.segment1 == newInter.segment1 ); QVERIFY( oldInter.segment2 == newInter.segment2 ); - cleanupTestContext( context ); + cleanupTestContext( testContext ); } void TestQgsGeometryChecks::testSliverPolygonCheck() @@ -966,13 +984,17 @@ void TestQgsGeometryChecks::testSliverPolygonCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); - QgsGeometryCheckerContext *context = createTestContext( dir, layers ); + auto testContext = createTestContext( dir, layers ); // Test detection QList checkErrors; QStringList messages; - QgsGeometrySliverPolygonCheck( context, 20, 0.04 ).collectErrors( checkErrors, messages ); + QVariantMap configuration; + configuration.insert( "threshold", 20 ); + configuration.insert( "maxArea", 0.04 ); + + QgsGeometrySliverPolygonCheck( testContext.first, configuration ).collectErrors( testContext.second, checkErrors, messages ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 2 ); @@ -983,7 +1005,7 @@ void TestQgsGeometryChecks::testSliverPolygonCheck() // The fix methods are exactely the same as in QgsGeometryAreaCheck, no point repeating... - cleanupTestContext( context ); + cleanupTestContext( testContext ); } /////////////////////////////////////////////////////////////////////////////// @@ -1006,7 +1028,7 @@ QgsFeaturePool *TestQgsGeometryChecks::createFeaturePool( QgsVectorLayer *layer, return new QgsVectorDataProviderFeaturePool( layer, selectedOnly ); } -QgsGeometryCheckerContext *TestQgsGeometryChecks::createTestContext( QTemporaryDir &tempDir, QMap &layers, const QgsCoordinateReferenceSystem &mapCrs, double prec ) const +QPair > TestQgsGeometryChecks::createTestContext( QTemporaryDir &tempDir, QMap &layers, const QgsCoordinateReferenceSystem &mapCrs, double prec ) const { QDir testDataDir( QDir( TEST_DATA_DIR ).absoluteFilePath( "geometry_checker" ) ); QDir tmpDir( tempDir.path() ); @@ -1029,18 +1051,18 @@ QgsGeometryCheckerContext *TestQgsGeometryChecks::createTestContext( QTemporaryD layer->dataProvider()->enterUpdateMode(); featurePools.insert( layer->id(), createFeaturePool( layer ) ); } - return new QgsGeometryCheckerContext( prec, mapCrs, featurePools, QgsProject::instance()->transformContext() ); + return qMakePair( new QgsGeometryCheckContext( prec, mapCrs, QgsProject::instance()->transformContext() ), featurePools ); } -void TestQgsGeometryChecks::cleanupTestContext( QgsGeometryCheckerContext *ctx ) const +void TestQgsGeometryChecks::cleanupTestContext( QPair > ctx ) const { - for ( const QgsFeaturePool *pool : ctx->featurePools ) + for ( const QgsFeaturePool *pool : ctx.second ) { pool->layer()->dataProvider()->leaveUpdateMode(); delete pool->layer(); } - qDeleteAll( ctx->featurePools ); - delete ctx; + qDeleteAll( ctx.second ); + delete ctx.first; } void TestQgsGeometryChecks::listErrors( const QList &checkErrors, const QStringList &messages ) const @@ -1096,11 +1118,11 @@ QList TestQgsGeometryChecks::searchCheckErrors( const Q return matching; } -bool TestQgsGeometryChecks::fixCheckError( QgsGeometryCheckError *error, int method, const QgsGeometryCheckError::Status &expectedStatus, const QVector &expectedChanges, const QMap &mergeAttrs ) +bool TestQgsGeometryChecks::fixCheckError( QMap featurePools, QgsGeometryCheckError *error, int method, const QgsGeometryCheckError::Status &expectedStatus, const QVector &expectedChanges, const QMap &mergeAttrs ) { QTextStream( stdout ) << " - Fixing " << error->layerId() << ":" << error->featureId() << " @[" << error->vidx().part << ", " << error->vidx().ring << ", " << error->vidx().vertex << "](" << error->location().x() << ", " << error->location().y() << ") = " << error->value().toString() << endl; QgsGeometryCheck::Changes changes; - error->check()->fixError( error, method, mergeAttrs, changes ); + error->check()->fixError( featurePools, error, method, mergeAttrs, changes ); QTextStream( stdout ) << " * Fix status: " << error->status() << endl; if ( error->status() != expectedStatus ) {