Python bindings for QgsGeometryCheck and co

Adds

 - python bindings
 - geometry check factory
 - geometry check registry
 - QgsFeedback for geometry checks (lots of potential still)
 - An IsValid geometry check
 - Splits classes into their own files
 - Decouples feature pools from the configuration context
This commit is contained in:
Matthias Kuhn 2018-09-06 17:30:56 +02:00
parent 1a61885254
commit bee5470e10
No known key found for this signature in database
GPG Key ID: 7A7F1A1C90C3E6A7
90 changed files with 3570 additions and 1424 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 *
************************************************************************/

View File

@ -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 *
************************************************************************/

View File

@ -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<QgsGeometryCheck::Flag> 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<QString, QMap<QgsFeatureId, QList<Change> > > Changes;
QgsGeometryCheck( CheckType checkType,
const QgsGeometryCheckContext *context,
const QVariantMap &configuration );
virtual ~QgsGeometryCheck();
virtual bool isCompatible( QgsVectorLayer *layer ) const;
virtual QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const = 0;
virtual QgsGeometryCheck::Flags flags() const;
virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &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 *
************************************************************************/

View File

@ -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 *
************************************************************************/

View File

@ -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 *
************************************************************************/

View File

@ -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 *
************************************************************************/

View File

@ -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<T>
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 *
************************************************************************/

View File

@ -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<QgsGeometryCheckFactory *> 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 *
************************************************************************/

View File

@ -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<QgsSingleGeometryCheckError *> 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 *
************************************************************************/

View File

@ -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<QString, QgsFeaturePool *> &featurePools,
QList<QgsGeometryCheckError *> &errors,
QStringList &messages,
QgsFeedback *feedback = 0,
const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const final;
virtual QList<QgsSingleGeometryCheckError *> 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 *
************************************************************************/

View File

@ -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

View File

@ -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<QgsGeometryCheckRegistry>() )
{
qRegisterMetaType< QList<std::shared_ptr<QgsGeometryCheckError> > >( "QList<std::shared_ptr<QgsGeometryCheckError>>" );
mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT<QgsGeometrySelfIntersectionCheck>() );
mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT<QgsGeometryGapCheck>() );
mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT<QgsGeometryOverlapCheck>() );
mGeometryCheckRegistry->registerGeometryCheck( new QgsGeometryCheckFactoryT<QgsGeometryMissingVertexCheck>() );
}
QgsAnalysis::~QgsAnalysis()
{
}

View File

@ -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 <memory>
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<QgsGeometryCheckRegistry> mGeometryCheckRegistry;
#ifdef SIP_RUN
QgsAnalysis( const QgsAnalysis &other );
#endif
};
#endif // QGSANALYSIS_H

View File

@ -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<QgsVectorLayer> layerPtr() const;
QPointer<QgsVectorLayer> 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<QgsFeatureId, QgsFeature> mFeatureCache;
QPointer<QgsVectorLayer> mLayer;

View File

@ -13,14 +13,17 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryanglecheck.h"
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryAngleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, context() );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &error
}
}
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryAngleCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
QgsFeature feature;
if ( !featurePool->getFeature( error->featureId(), feature ) )
{

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryAreaCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &errors
}
}
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
void QgsGeometryAreaCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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<QString, QgsFeaturePool *> &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;
}

View File

@ -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<double>( "areaThreshold" ) )
{}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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<QString, QgsFeaturePool *> &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

View File

@ -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<QString, QgsFeaturePool *> &_featurePools, const QgsCoordinateTransformContext &transformContext )
: tolerance( std::pow( 10, -_precision ) )
, reducedTolerance( std::pow( 10, -_precision / 2 ) )
, mapCrs( _mapCrs )
, featurePools( _featurePools )
, transformContext( transformContext )
bool QgsGeometryCheck::isCompatible( QgsVectorLayer *layer ) const
{
return compatibleGeometryTypes().contains( layer->geometryType() );
}
const QgsCoordinateTransform &QgsGeometryCheckerContext::layerTransform( const QPointer<QgsVectorLayer> &layer )
void QgsGeometryCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, QgsGeometryCheck::Changes &changes ) const
{
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];
Q_UNUSED( featurePools )
Q_UNUSED( error )
Q_UNUSED( method )
Q_UNUSED( mergeAttributeIndices )
Q_UNUSED( changes )
}
double QgsGeometryCheckerContext::layerScaleFactor( const QPointer<QgsVectorLayer> &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
{
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;
}
QMap<QString, QgsFeatureIds> QgsGeometryCheck::allLayerFeatureIds() const
QMap<QString, QgsFeatureIds> QgsGeometryCheck::allLayerFeatureIds( const QMap<QString, QgsFeaturePool *> &featurePools ) const
{
QMap<QString, QgsFeatureIds> 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<QString, QgsFeaturePool *> &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<QString, QgsFeaturePool *> &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<QgsGeometryCollection *>( 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<QString, QgsFeaturePool *> &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<QgsCurvePolygon *>( 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<QgsVectorLayer> 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;
}

View File

@ -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<QgsFeatureId>::min()
struct ANALYSIS_EXPORT QgsGeometryCheckerContext
{
QgsGeometryCheckerContext( int _precision, const QgsCoordinateReferenceSystem &_mapCrs, const QMap<QString, QgsFeaturePool *> &_featurePools, const QgsCoordinateTransformContext &transformContext );
const double tolerance;
const double reducedTolerance;
const QgsCoordinateReferenceSystem mapCrs;
const QMap<QString, QgsFeaturePool *> featurePools;
const QgsCoordinateTransformContext transformContext;
const QgsCoordinateTransform &layerTransform( const QPointer<QgsVectorLayer> &layer );
double layerScaleFactor( const QPointer<QgsVectorLayer> &layer );
private:
QMap<QPointer<QgsVectorLayer>, QgsCoordinateTransform> mTransformCache;
QMap<QPointer<QgsVectorLayer>, 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<QString, QgsFeatureIds> &ids ) SIP_SKIP;
QMap<QString, QgsFeatureIds> ids SIP_SKIP;
#ifndef SIP_RUN
QMap<QString, QgsFeatureIds> 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<QString, QMap<QgsFeatureId, QList<Change>>> Changes;
typedef QMap<QString, QMap<QgsFeatureId, QList<Change> > > Changes;
QgsGeometryCheck( CheckType checkType, const QList<QgsWkbTypes::GeometryType> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const = 0;
virtual void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const = 0;
#ifndef SIP_RUN
template <class T>
T configurationValue( const QString &name, const QVariant &defaultValue = QVariant() )
{
return mConfiguration.value( name, QgsSettings().value( "/geometry_checker/" + id() + "/" + name, defaultValue ) ).value<T>();
}
#endif
virtual bool isCompatible( QgsVectorLayer *layer ) const;
virtual QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const = 0;
virtual QgsGeometryCheck::Flags flags() const {return nullptr;}
virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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<QString, QgsFeatureIds> 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<QString, QgsFeatureIds> allLayerFeatureIds( const QMap<QString, QgsFeaturePool *> &featurePools ) const SIP_SKIP;
void replaceFeatureGeometryPart( const QMap<QString, QgsFeaturePool *> &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const SIP_SKIP;
void deleteFeatureGeometryPart( const QMap<QString, QgsFeaturePool *> &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const SIP_SKIP;
void deleteFeatureGeometryRing( const QMap<QString, QgsFeaturePool *> &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const SIP_SKIP;
const CheckType mCheckType;
QList<QgsWkbTypes::GeometryType> mCompatibleGeometryTypes;
QgsGeometryCheckerContext *mContext;
const QgsGeometryCheckContext *mContext;
QVariantMap mConfiguration;
double scaleFactor( QPointer<QgsVectorLayer> layer ) const SIP_SKIP;
};
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;
};
Q_DECLARE_METATYPE( QgsGeometryCheckError * )
#endif // QGS_GEOMETRY_CHECK_H

View File

@ -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 )
{
}

View File

@ -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

View File

@ -14,23 +14,26 @@
* *
***************************************************************************/
#include "qgsgeometrychecker.h"
#include "qgsgeometrycheck.h"
#include "qgsfeaturepool.h"
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include <QtConcurrentMap>
#include <QFutureWatcher>
#include <QMutex>
#include <QTimer>
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrychecker.h"
#include "qgsgeometrycheck.h"
#include "qgsfeaturepool.h"
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include "qgsgeometrycheckerror.h"
QgsGeometryChecker::QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks, QgsGeometryCheckerContext *context )
QgsGeometryChecker::QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks, const QMap<QString, QgsFeaturePool *> &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<void> 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<void> 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<QString> 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<QgsFeatureId, QList<QgsGeometryCheck::Change>> &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<QString, QgsFeatureIds> 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<QString, QgsFeaturePool *> &featurePools, const QgsGeometryCheck *check )
{
// Run checks
QList<QgsGeometryCheckError *> 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 );
}

View File

@ -23,29 +23,33 @@
#include <QList>
#include <QMutex>
#include <QStringList>
#include "qgis_analysis.h"
#include "qgsfeedback.h"
#include "qgsfeatureid.h"
typedef qint64 QgsFeatureId;
typedef QSet<QgsFeatureId> 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<QgsGeometryCheck *> &checks, QgsGeometryCheckerContext *context );
QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks, const QMap<QString, QgsFeaturePool *> &featurePools );
~QgsGeometryChecker() override;
QFuture<void> execute( int *totalSteps = nullptr );
bool fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint = false );
const QList<QgsGeometryCheck *> getChecks() const { return mChecks; }
QStringList getMessages() const { return mMessages; }
void setMergeAttributeIndices( const QMap<QString, int> &mergeAttributeIndices ) { mMergeAttributeIndices = mergeAttributeIndices; }
QgsGeometryCheckerContext *getContext() const { return mContext; }
QgsGeometryCheckContext *getContext() const { return mContext; }
const QMap<QString, QgsFeaturePool *> featurePools() const {return mFeaturePools;}
signals:
void errorAdded( QgsGeometryCheckError *error );
@ -63,14 +67,15 @@ class ANALYSIS_EXPORT QgsGeometryChecker : public QObject
};
QList<QgsGeometryCheck *> mChecks;
QgsGeometryCheckerContext *mContext;
QgsGeometryCheckContext *mContext;
QList<QgsGeometryCheckError *> mCheckErrors;
QStringList mMessages;
QMutex mErrorListMutex;
QMap<QString, int> mMergeAttributeIndices;
QAtomicInt mProgressCounter;
QgsFeedback mFeedback;
QMap<QString, QgsFeaturePool *> mFeaturePools;
void runCheck( const QgsGeometryCheck *check );
void runCheck( const QMap<QString, QgsFeaturePool *> &featurePools, const QgsGeometryCheck *check );
private slots:
void emitProgressValue();

View File

@ -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<QString, QgsFeatureIds> &ids )
: ids( ids )
{
}

View File

@ -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

View File

@ -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 <qmath.h>
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<QgsVectorLayer> LayerFeature::layer() const
{
return mFeaturePool->layerPtr();
}
QPointer<QgsVectorLayer> 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();
}
/////////////////////////////////////////////////////////////////////////////
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;
}
const LayerFeatures::iterator &LayerFeatures::iterator::operator++()
{
nextLayerFeature( false );
return *this;
}
bool LayerFeatures::iterator::nextLayerFeature( bool begin )
{
if ( !begin && nextFeature( false ) )
{
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 )
{
if ( !begin )
{
++mLayerIt;
}
while ( true )
{
if ( mLayerIt == mParent->mLayerIds.end() )
{
break;
}
if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) )
{
mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin();
return true;
}
++mLayerIt;
}
return false;
}
bool LayerFeatures::iterator::nextFeature( bool begin )
{
QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt];
const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt];
if ( !begin )
{
++mFeatureIt;
}
while ( true )
{
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;
}
return false;
}
bool QgsGeometryCheckerUtils::LayerFeature::operator!=( const LayerFeature &other ) const
{
return layer()->id() != other.layer()->id() || feature().id() != other.feature().id();
}
/////////////////////////////////////////////////////////////////////////////
LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
const QMap<QString, QgsFeatureIds> &featureIds,
const QList<QgsWkbTypes::GeometryType> &geometryTypes,
QAtomicInt *progressCounter,
QgsGeometryCheckerContext *context,
bool useMapCrs )
: mFeaturePools( featurePools )
, mFeatureIds( featureIds )
, mLayerIds( featurePools.keys() )
, mGeometryTypes( geometryTypes )
, mProgressCounter( progressCounter )
, mContext( context )
, mUseMapCrs( useMapCrs )
{}
QgsGeometryCheckerUtils::LayerFeatures::iterator::iterator( const QStringList::const_iterator &layerIt, const LayerFeatures *parent )
: mLayerIt( layerIt )
, mFeatureIt( QgsFeatureIds::const_iterator() )
, mParent( parent )
{
nextLayerFeature( true );
}
LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
const QList<QString> &layerIds, const QgsRectangle &extent,
const QList<QgsWkbTypes::GeometryType> &geometryTypes,
QgsGeometryCheckerContext *context )
: mFeaturePools( featurePools )
, mLayerIds( layerIds )
, mExtent( extent )
, mGeometryTypes( geometryTypes )
, mContext( context )
, mUseMapCrs( true )
bool QgsGeometryCheckerUtils::LayerFeature::useMapCrs() const
{
return mMapCrs;
}
QgsGeometryCheckerUtils::LayerFeatures::iterator::~iterator()
{
delete mCurrentFeature;
}
const QgsGeometryCheckerUtils::LayerFeatures::iterator &QgsGeometryCheckerUtils::LayerFeatures::iterator::operator++()
{
nextLayerFeature( false );
return *this;
}
bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayerFeature( bool begin )
{
if ( !begin && nextFeature( false ) )
{
for ( const QString &layerId : layerIds )
return true;
}
while ( nextLayer( begin ) )
{
begin = false;
if ( nextFeature( true ) )
{
const QgsFeaturePool *featurePool = featurePools[layerId];
if ( geometryTypes.contains( featurePool->geometryType() ) )
return true;
}
}
// End
mFeatureIt = QgsFeatureIds::const_iterator();
delete mCurrentFeature;
mCurrentFeature = nullptr;
return false;
}
bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayer( bool begin )
{
if ( !begin )
{
++mLayerIt;
}
while ( true )
{
if ( mLayerIt == mParent->mLayerIds.end() )
{
break;
}
if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) )
{
mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin();
return true;
}
++mLayerIt;
}
return false;
}
bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextFeature( bool begin )
{
QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt];
const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt];
if ( !begin )
{
++mFeatureIt;
}
while ( true )
{
if ( mFeatureIt == featureIds.end() )
{
break;
}
if ( mParent->mFeedback )
mParent->mFeedback->setProgress( mParent->mFeedback->progress() + 1.0 );
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;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////
QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
const QMap<QString, QgsFeatureIds> &featureIds,
const QList<QgsWkbTypes::GeometryType> &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<QString, QgsFeaturePool *> &featurePools,
const QList<QString> &layerIds, const QgsRectangle &extent,
const QList<QgsWkbTypes::GeometryType> &geometryTypes,
const QgsGeometryCheckContext *context )
: mFeaturePools( featurePools )
, mLayerIds( layerIds )
, mExtent( extent )
, mGeometryTypes( geometryTypes )
, mContext( context )
, mUseMapCrs( true )
{
for ( const QString &layerId : layerIds )
{
const QgsFeaturePool *featurePool = featurePools[layerId];
if ( geometryTypes.contains( featurePool->geometryType() ) )
{
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<QgsGeometryEngine> QgsGeometryCheckerUtils::createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance )
{
return qgis::make_unique<QgsGeos>( geometry, tolerance );
}
QgsAbstractGeometry *QgsGeometryCheckerUtils::getGeomPart( QgsAbstractGeometry *geom, int partIdx )
{
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
{
return static_cast<QgsGeometryCollection *>( geom )->geometryN( partIdx );
}
return geom;
}
const QgsAbstractGeometry *QgsGeometryCheckerUtils::getGeomPart( const QgsAbstractGeometry *geom, int partIdx )
{
if ( dynamic_cast<const QgsGeometryCollection *>( geom ) )
{
return static_cast<const QgsGeometryCollection *>( geom )->geometryN( partIdx );
}
return geom;
}
QList<const QgsLineString *> QgsGeometryCheckerUtils::polygonRings( const QgsPolygon *polygon )
{
QList<const QgsLineString *> rings;
if ( const QgsLineString *exterior = dynamic_cast<const QgsLineString *>( polygon->exteriorRing() ) )
{
rings.append( exterior );
}
for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt )
{
if ( const QgsLineString *interior = dynamic_cast<const QgsLineString *>( polygon->interiorRing( iInt ) ) )
{
rings.append( interior );
}
}
return rings;
}
void QgsGeometryCheckerUtils::filter1DTypes( QgsAbstractGeometry *geom )
{
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
{
QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom );
for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart )
{
if ( !dynamic_cast<QgsSurface *>( geomCollection->geometryN( iPart ) ) )
{
mFeatureIds.insert( layerId, featurePool->getIntersects( context->layerTransform( featurePool->layer() ).transform( extent, QgsCoordinateTransform::ReverseTransform ) ) );
}
else
{
mFeatureIds.insert( layerId, QgsFeatureIds() );
geomCollection->removeGeometry( iPart );
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
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 );
}
std::unique_ptr<QgsGeometryEngine> createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance )
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 )
{
return qgis::make_unique<QgsGeos>( geometry, tolerance );
}
QgsAbstractGeometry *getGeomPart( QgsAbstractGeometry *geom, int partIdx )
{
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
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 static_cast<QgsGeometryCollection *>( geom )->geometryN( partIdx );
return true;
}
return geom;
}
return false;
}
const QgsAbstractGeometry *getGeomPart( const QgsAbstractGeometry *geom, int partIdx )
QList<QgsPoint> QgsGeometryCheckerUtils::lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol )
{
QList<QgsPoint> intersections;
QgsPoint inter;
bool intersection = false;
for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i )
{
if ( dynamic_cast<const QgsGeometryCollection *>( geom ) )
for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j )
{
return static_cast<const QgsGeometryCollection *>( geom )->geometryN( partIdx );
}
return geom;
}
QList<const QgsLineString *> polygonRings( const QgsPolygon *polygon )
{
QList<const QgsLineString *> rings;
if ( const QgsLineString *exterior = dynamic_cast<const QgsLineString *>( polygon->exteriorRing() ) )
{
rings.append( exterior );
}
for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt )
{
if ( const QgsLineString *interior = dynamic_cast<const QgsLineString *>( polygon->interiorRing( iInt ) ) )
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 ) )
{
rings.append( interior );
intersections.append( inter );
}
}
return rings;
}
return intersections;
}
void filter1DTypes( QgsAbstractGeometry *geom )
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 )
{
if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
for ( int iRing1 = 0, nRings1 = geom1->ringCount( iPart1 ); iRing1 < nRings1; ++iRing1 )
{
QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom );
for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart )
for ( int iVert1 = 0, jVert1 = 1, nVerts1 = geom1->vertexCount( iPart1, iRing1 ); jVert1 < nVerts1; iVert1 = jVert1++ )
{
if ( !dynamic_cast<QgsSurface *>( geomCollection->geometryN( iPart ) ) )
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
{
geomCollection->removeGeometry( iPart );
d = QgsVector( p2.x() - p1.x(), p2.y() - p1.y() ).normalized();
}
}
}
}
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 );
}
bool 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 )
{
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 false;
}
QList<QgsPoint> lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol )
{
QList<QgsPoint> 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 )
{
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 ) )
catch ( const QgsException & )
{
intersections.append( inter );
// Edge has zero length, skip
continue;
}
}
}
return intersections;
}
double 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 )
{
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 iPart2 = 0, nParts2 = geom2->partCount(); iPart2 < nParts2; ++iPart2 )
{
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
for ( int iRing2 = 0, nRings2 = geom2->ringCount( iPart2 ); iRing2 < nRings2; ++iRing2 )
{
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 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;
}

View File

@ -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 <qmath.h>
class QgsGeometryEngine;
class QgsFeaturePool;
struct QgsGeometryCheckerContext;
class QgsFeedback;
namespace QgsGeometryCheckerUtils
class ANALYSIS_EXPORT QgsGeometryCheckerUtils
{
class LayerFeature
{
public:
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, QgsGeometryCheckerContext *context, bool useMapCrs );
/**
* 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;
/**
* Returns the feature.
* The geometry will not be reprojected regardless of useMapCrs.
*/
const QgsFeature &feature() const;
/**
* The layer.
*/
QPointer<QgsVectorLayer> layer() const;
/**
* The layer.
*/
QPointer<QgsVectorLayer> layer() const SIP_SKIP;
/**
* The layer id.
*/
QString layerId() const;
/**
* 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 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;
/**
* 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;
};
private:
const QgsFeaturePool *mFeaturePool;
QgsFeature mFeature;
QgsGeometry mGeometry;
bool mMapCrs;
};
class LayerFeatures
{
public:
LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
const QMap<QString, QgsFeatureIds> &featureIds,
const QList<QgsWkbTypes::GeometryType> &geometryTypes,
QAtomicInt *progressCounter,
QgsGeometryCheckerContext *context,
bool useMapCrs = false );
class ANALYSIS_EXPORT LayerFeatures
{
public:
#ifndef SIP_RUN
LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
const QMap<QString, QgsFeatureIds> &featureIds,
const QList<QgsWkbTypes::GeometryType> &geometryTypes,
QgsFeedback *feedback,
const QgsGeometryCheckContext *context,
bool useMapCrs = false );
LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
const QList<QString> &layerIds, const QgsRectangle &extent,
const QList<QgsWkbTypes::GeometryType> &geometryTypes,
QgsGeometryCheckerContext *context );
LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
const QList<QString> &layerIds, const QgsRectangle &extent,
const QList<QgsWkbTypes::GeometryType> &geometryTypes,
const QgsGeometryCheckContext *context );
class iterator
class iterator
{
public:
iterator( const QList<QString>::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<QString>::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<QString, QgsFeaturePool *> mFeaturePools;
QMap<QString, QgsFeatureIds> mFeatureIds;
QList<QString> mLayerIds;
QgsRectangle mExtent;
QList<QgsWkbTypes::GeometryType> mGeometryTypes;
QgsFeedback *mFeedback = nullptr;
const QgsGeometryCheckContext *mContext = nullptr;
bool mUseMapCrs = true;
};
#ifndef SIP_RUN
static std::unique_ptr<QgsGeometryEngine> createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance );
static QgsAbstractGeometry *getGeomPart( QgsAbstractGeometry *geom, int partIdx );
static const QgsAbstractGeometry *getGeomPart( const QgsAbstractGeometry *geom, int partIdx );
static QList <const QgsLineString *> 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() )
{
public:
iterator( const QList<QString>::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; }
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;
}
}
private:
bool nextLayerFeature( bool begin );
bool nextLayer( bool begin );
bool nextFeature( bool begin );
QList<QString>::const_iterator mLayerIt;
QgsFeatureIds::const_iterator mFeatureIt;
const LayerFeatures *mParent;
const LayerFeature *mCurrentFeature = nullptr;
};
static bool pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities = false );
iterator begin() const { return iterator( mLayerIds.constBegin(), this ); }
iterator end() const { return iterator( mLayerIds.end(), this ); }
private:
QMap<QString, QgsFeaturePool *> mFeaturePools;
QMap<QString, QgsFeatureIds> mFeatureIds;
QList<QString> mLayerIds;
QgsRectangle mExtent;
QList<QgsWkbTypes::GeometryType> mGeometryTypes;
QAtomicInt *mProgressCounter = nullptr;
QgsGeometryCheckerContext *mContext = nullptr;
bool mUseMapCrs = true;
};
static QList<QgsPoint> lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol );
std::unique_ptr<QgsGeometryEngine> createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance );
static double sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol );
QgsAbstractGeometry *getGeomPart( QgsAbstractGeometry *geom, int partIdx );
const QgsAbstractGeometry *getGeomPart( const QgsAbstractGeometry *geom, int partIdx );
/**
* \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
*/
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;
}
QList<const QgsLineString *> 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 );
#endif
QList<QgsPoint> lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol );
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

View File

@ -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 <QString>
#include <QMap>
#include <QVariantMap>
#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 T>
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

View File

@ -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<QgsGeometryCheckFactory *> QgsGeometryCheckRegistry::geometryCheckFactories( QgsVectorLayer *layer, QgsGeometryCheck::Flags flags ) const
{
QList<QgsGeometryCheckFactory *> 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 );
}

View File

@ -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 <QString>
#include <QMap>
#include <QVariant>
#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<QgsGeometryCheckFactory *> geometryCheckFactories( QgsVectorLayer *layer, QgsGeometryCheck::Flags flags = nullptr ) const;
void registerGeometryCheck( QgsGeometryCheckFactory *checkFactory SIP_TRANSFER );
private:
QMap<QString, QgsGeometryCheckFactory *> mGeometryCheckFactories;
};
#endif // QGSGEOMETRYCHECKREGISTRY_H

View File

@ -13,16 +13,17 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryengine.h"
#include "qgsgeometrycontainedcheck.h"
#include "qgsfeaturepool.h"
#include "qgsvectorlayer.h"
void QgsGeometryContainedCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryContainedCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &e
}
}
void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryContainedCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsGeometryContainedCheckError *containerError = static_cast<QgsGeometryContainedCheckError *>( 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;

View File

@ -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<QgsGeometryContainedCheckError *>( 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<QString, QgsFeatureId> 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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
explicit QgsGeometryContainedCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( FeatureCheck, context, configuration ) {}
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -13,14 +13,17 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrydanglecheck.h"
#include "qgslinestring.h"
#include "qgsvectorlayer.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryDangleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryDangleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &erro
}
// Check whether endpoints line on another line in the layer
QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, QList<QString>() << layerFeature.layer()->id(), line->boundingBox(), {QgsWkbTypes::LineGeometry}, mContext );
QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, QList<QString>() << 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<QgsGeometryCheckError *> &erro
}
}
void QgsGeometryDangleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
void QgsGeometryDangleCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
Q_UNUSED( featurePools )
if ( method == NoChange )
{
error->setFixed( method );

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -13,13 +13,18 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrydegeneratepolygoncheck.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryDegeneratePolygonCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckErr
}
}
void QgsGeometryDegeneratePolygonCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryDegeneratePolygonCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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

View File

@ -23,13 +23,18 @@
class ANALYSIS_EXPORT QgsGeometryDegeneratePolygonCheck : public QgsGeometryCheck
{
public:
explicit QgsGeometryDegeneratePolygonCheck( QgsGeometryCheckerContext *context )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PolygonGeometry}, context ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
explicit QgsGeometryDegeneratePolygonCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {}
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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:

View File

@ -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<QString, Qg
}
void QgsGeometryDuplicateCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryDuplicateCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true );
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true );
QList<QString> layerIds = featureIds.keys();
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA )
{
@ -57,7 +58,7 @@ void QgsGeometryDuplicateCheck::collectErrors( QList<QgsGeometryCheckError *> &e
QMap<QString, QList<QgsFeatureId>> duplicates;
QgsWkbTypes::GeometryType geomType = layerFeatureA.feature().geometry().type();
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( mContext->featurePools, QList<QString>() << layerFeatureA.layer()->id() << layerIds, bboxA, {geomType}, mContext );
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList<QString>() << 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<QgsGeometryCheckError *> &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<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryDuplicateCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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<QgsGeometryDuplicateCheckError *>( 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;

View File

@ -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<QString, QgsFeaturePool *> &featurePools,
const QMap<QString, QList<QgsFeatureId>> &duplicates )
: QgsGeometryCheckError( check, layerFeature, errorLocation, QgsVertexId(), duplicatesString( check->context()->featurePools, duplicates ) )
: QgsGeometryCheckError( check, layerFeature, errorLocation, QgsVertexId(), duplicatesString( featurePools, duplicates ) )
, mDuplicates( duplicates )
{ }
QMap<QString, QList<QgsFeatureId>> 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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
explicit QgsGeometryDuplicateCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( FeatureCheck, context, configuration ) {}
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -13,14 +13,18 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryduplicatenodescheck.h"
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryDuplicateNodesCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError
}
}
void QgsGeometryDuplicateNodesCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryDuplicateNodesCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
QgsFeature feature;
if ( !featurePool->getFeature( error->featureId(), feature ) )
{

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
explicit QgsGeometryDuplicateNodesCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( FeatureNodeCheck, context, configuration ) {}
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
if ( !mIndex || !mCheckLayer )
{
return;
}
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckErro
}
}
void QgsGeometryFollowBoundariesCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
void QgsGeometryFollowBoundariesCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
Q_UNUSED( featurePools )
if ( method == NoChange )
{
error->setFixed( method );

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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;

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
QgsGeometryGapCheck::QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( LayerCheck, context, configuration )
, mGapThresholdMapUnits( configuration.value( "gapThreshold" ).toDouble() )
{
if ( progressCounter )
progressCounter->fetchAndAddRelaxed( 1 );
}
void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
if ( feedback )
feedback->setProgress( feedback->progress() + 1.0 );
QVector<QgsAbstractGeometry *> geomList;
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, nullptr, mContext, true );
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors,
// Get neighboring polygons
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &errors,
}
}
void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryGapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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<QgsGeometryGapCheckError *>( error ), changes, errMsg ) )
if ( mergeWithNeighbor( featurePools, static_cast<QgsGeometryGapCheckError *>( 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<QString, QgsFeaturePool *> &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<QgsAbstractGeometry> 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<QgsAbstractGeometry> 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<QgsWkbTypes::GeometryType> QgsGeometryGapCheck::factoryCompatibleGeometryTypes()
{
return {QgsWkbTypes::PolygonGeometry};
}
bool QgsGeometryGapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
{
return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
}

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
explicit QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration );
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() SIP_SKIP;
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP;
///@endcond private
enum ResolutionMethod { MergeLongestEdge, NoChange };
private:
bool mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &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

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryHoleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &errors
}
}
void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryHoleCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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

View File

@ -23,13 +23,18 @@
class ANALYSIS_EXPORT QgsGeometryHoleCheck : public QgsGeometryCheck
{
public:
explicit QgsGeometryHoleCheck( QgsGeometryCheckerContext *context )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, context ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
explicit QgsGeometryHoleCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( FeatureCheck, context, configuration ) {}
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -13,14 +13,18 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrylineintersectioncheck.h"
#include "qgslinestring.h"
#include "qgsvectorlayer.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryLineIntersectionCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryLineIntersectionCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true );
QList<QString> layerIds = featureIds.keys();
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA )
{
@ -38,7 +42,7 @@ void QgsGeometryLineIntersectionCheck::collectErrors( QList<QgsGeometryCheckErro
}
// Check whether the line intersects with any other lines
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( mContext->featurePools, QList<QString>() << layerFeatureA.layer()->id() << layerIds, line->boundingBox(), {QgsWkbTypes::LineGeometry}, mContext );
QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList<QString>() << 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<QgsGeometryCheckErro
}
}
void QgsGeometryLineIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
void QgsGeometryLineIntersectionCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
Q_UNUSED( featurePools )
if ( method == NoChange )
{
error->setFixed( method );

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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( QList<QgsGeometryChec
}
// Check whether the line intersects with any other features of the specified layer
QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, 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<QgsGeometryChec
}
}
void QgsGeometryLineLayerIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
void QgsGeometryLineLayerIntersectionCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
Q_UNUSED( featurePools )
if ( method == NoChange )
{
error->setFixed( method );

View File

@ -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<QString>( "checkLayer" ) )
{}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration )
: QgsGeometryCheck( LayerCheck, context, geometryCheckConfiguration )
{
Q_UNUSED( messages );
if ( progressCounter )
progressCounter->fetchAndAddRelaxed( 1 );
}
void QgsGeometryMissingVertexCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
if ( feedback )
feedback->setProgress( feedback->progress() + 1.0 );
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *
}
}
void QgsGeometryMissingVertexCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
void QgsGeometryMissingVertexCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const
{
const QgsFeature &currentFeature = layerFeature.feature();
@ -131,3 +143,43 @@ void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polyg
}
}
}
QString QgsGeometryMissingVertexCheck::id() const
{
return factoryId();
}
QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::compatibleGeometryTypes() const
{
return factoryCompatibleGeometryTypes();
}
QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::flags() const
{
return factoryFlags();
}
QList<QgsWkbTypes::GeometryType> 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;
}

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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
};
enum ResolutionMethod { NoChange };
explicit QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration );
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList resolutionMethods() const override;
QString description() const override;
QString id() const override;
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override;
QgsGeometryCheck::Flags flags() const override;
///@cond private
static QList<QgsWkbTypes::GeometryType> 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<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const;
};
#endif // QGSGEOMETRYMISSINGVERTEXCHECK_
#endif // QGSGEOMETRYMISSINGVERTEXCHECK_H

View File

@ -13,12 +13,12 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrymultipartcheck.h"
#include "qgsfeaturepool.h"
QList<QgsSingleGeometryCheckError *> QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
QList<QgsSingleGeometryCheckError *> QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry ) const
{
Q_UNUSED( configuration )
QList<QgsSingleGeometryCheckError *> errors;
const QgsAbstractGeometry *geom = geometry.constGet();
@ -30,9 +30,9 @@ QList<QgsSingleGeometryCheckError *> QgsGeometryMultipartCheck::processGeometry(
return errors;
}
void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryMultipartCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ];
QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
QgsFeature feature;
if ( !featurePool->getFeature( error->featureId(), feature ) )
{

View File

@ -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<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
explicit QgsGeometryMultipartCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsSingleGeometryCheck( FeatureCheck,
context,
configuration ) {}
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -13,16 +13,25 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryengine.h"
#include "qgsgeometryoverlapcheck.h"
#include "qgsfeaturepool.h"
#include "qgsvectorlayer.h"
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
QgsGeometryOverlapCheck::QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( LayerCheck, context, configuration )
, mOverlapThresholdMapUnits( configurationValue<double>( "maxOverlapArea" ) )
{
double overlapThreshold = mThresholdMapUnits;
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true );
}
void QgsGeometryOverlapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true );
QList<QString> layerIds = featureIds.keys();
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA )
{
@ -37,7 +46,7 @@ void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &err
continue;
}
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( mContext->featurePools, QList<QString>() << layerFeatureA.layer()->id() << layerIds, bboxA, mCompatibleGeometryTypes, mContext );
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList<QString>() << 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<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &err
}
}
void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryOverlapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QString errMsg;
QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( 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<QgsWkbTypes::GeometryType> 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() ) )
{
}

View File

@ -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<QString, QgsFeatureId> &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<QString, QgsFeatureId> 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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration );
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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<QgsWkbTypes::GeometryType> 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

View File

@ -13,14 +13,18 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrypointcoveredbylinecheck.h"
#include "qgslinestring.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryPointCoveredByLineCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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( QList<QgsGeometryCheckEr
bool touches = false;
QgsRectangle rect( point->x() - 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<QgsGeometryCheckEr
}
}
void QgsGeometryPointCoveredByLineCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
void QgsGeometryPointCoveredByLineCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
Q_UNUSED( featurePools )
if ( method == NoChange )
{
error->setFixed( method );

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -13,14 +13,16 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrypointinpolygoncheck.h"
#include "qgspolygon.h"
#include "qgsgeometryengine.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryPointInPolygonCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometryPointInPolygonCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true );
QMap<QString, QgsFeatureIds> 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( QList<QgsGeometryCheckError
// Check whether point is contained by a fully contained by a polygon
QgsRectangle rect( point->x() - 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<QgsGeometryCheckError
}
}
void QgsGeometryPointInPolygonCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
void QgsGeometryPointInPolygonCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
Q_UNUSED( featurePools )
if ( method == NoChange )
{
error->setFixed( method );

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -13,17 +13,21 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrysegmentlengthcheck.h"
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsGeometrySegmentLengthCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *
}
}
void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &/*changes*/ ) const
void QgsGeometrySegmentLengthCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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 )
{

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };

View File

@ -13,13 +13,13 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryselfcontactcheck.h"
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"
QList<QgsSingleGeometryCheckError *> QgsGeometrySelfContactCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
QList<QgsSingleGeometryCheckError *> QgsGeometrySelfContactCheck::processGeometry( const QgsGeometry &geometry ) const
{
Q_UNUSED( configuration )
QList<QgsSingleGeometryCheckError *> errors;
const QgsAbstractGeometry *geom = geometry.constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
@ -81,8 +81,9 @@ QList<QgsSingleGeometryCheckError *> QgsGeometrySelfContactCheck::processGeometr
return errors;
}
void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
void QgsGeometrySelfContactCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
Q_UNUSED( featurePools )
if ( method == NoChange )
{
error->setFixed( method );

View File

@ -23,13 +23,18 @@
class ANALYSIS_EXPORT QgsGeometrySelfContactCheck : public QgsSingleGeometryCheck
{
public:
QgsGeometrySelfContactCheck( QgsGeometryCheckerContext *context )
: QgsSingleGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, context ) {}
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes & ) const override;
QgsGeometrySelfContactCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsSingleGeometryCheck( FeatureNodeCheck, context, configuration ) {}
static QList<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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 };
};

View File

@ -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<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometrySelfIntersectionCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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<QgsSingleGeometryCheckError *> QgsGeometrySelfIntersectionCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
QList<QgsSingleGeometryCheckError *> QgsGeometrySelfIntersectionCheck::processGeometry( const QgsGeometry &geometry ) const
{
Q_UNUSED( configuration )
QList<QgsSingleGeometryCheckError *> errors;
const QgsAbstractGeometry *geom = geometry.constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
@ -333,3 +332,28 @@ QList<QgsSingleGeometryCheckError *> QgsGeometrySelfIntersectionCheck::processGe
}
return errors;
}
QList<QgsWkbTypes::GeometryType> 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" );
}

View File

@ -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<QString, int> &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<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
enum ResolutionMethod
{
ToMultiObject,
ToSingleObjects,
NoChange
};
explicit QgsGeometrySelfIntersectionCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration = QVariantMap() )
: QgsSingleGeometryCheck( FeatureNodeCheck,
context,
configuration ) {}
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList resolutionMethods() const override;
QString description() const override { return factoryDescription(); }
QString id() const override { return factoryId(); }
QgsGeometryCheck::Flags flags() const override {return factoryFlags(); }
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry ) const override;
///@cond private
static QList<QgsWkbTypes::GeometryType> 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

View File

@ -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();

View File

@ -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<double>( "threshold" );
mMaxArea = configurationValue<double>( "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

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrytypecheck.h"
#include "qgsgeometrycollection.h"
#include "qgsmulticurve.h"
@ -23,9 +24,9 @@
#include "qgsfeaturepool.h"
QList<QgsSingleGeometryCheckError *> QgsGeometryTypeCheck::processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const
QList<QgsSingleGeometryCheckError *> QgsGeometryTypeCheck::processGeometry( const QgsGeometry &geometry ) const
{
Q_UNUSED( configuration )
QList<QgsSingleGeometryCheckError *> errors;
const QgsAbstractGeometry *geom = geometry.constGet();
QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() );
@ -36,9 +37,9 @@ QList<QgsSingleGeometryCheckError *> QgsGeometryTypeCheck::processGeometry( cons
return errors;
}
void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
void QgsGeometryTypeCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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 ) );
}

View File

@ -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<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry ) const override;
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &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<QgsWkbTypes::GeometryType> 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;

View File

@ -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<QgsSingleGeometryCheckError *> QgsIsValidGeometryCheck::processGeometry( const QgsGeometry &geometry ) const
{
QVector<QgsGeometry::Error> 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<QgsSingleGeometryCheckError *> result;
for ( const auto &error : qgis::as_const( errors ) )
{
result << new QgsSingleGeometryCheckError( this, geometry, QgsGeometry( qgis::make_unique<QgsPoint>( error.where() ) ) );
}
return result;
}
QStringList QgsIsValidGeometryCheck::resolutionMethods() const
{
return QStringList();
}

View File

@ -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<QgsWkbTypes::GeometryType> factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
QList<QgsSingleGeometryCheckError *> 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

View File

@ -14,22 +14,21 @@ email : matthias@opengis.ch
***************************************************************************/
#include "qgssinglegeometrycheck.h"
#include "qgsgeometrycheckcontext.h"
#include "qgspoint.h"
QgsSingleGeometryCheck::QgsSingleGeometryCheck( CheckType checkType, const QList<QgsWkbTypes::GeometryType> &compatibleGeometryTypes, QgsGeometryCheckerContext *context )
: QgsGeometryCheck( checkType, compatibleGeometryTypes, context )
{
}
void QgsSingleGeometryCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
void QgsSingleGeometryCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools,
QList<QgsGeometryCheckError *> &errors,
QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext );
QMap<QString, QgsFeatureIds> 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 QList<QgsGeometryCheck::C
QString QgsSingleGeometryCheckError::description() const
{
return mCheck->errorDescription();
return mCheck->description();
}
const QgsSingleGeometryCheck *QgsSingleGeometryCheckError::check() const

View File

@ -16,13 +16,12 @@ email : matthias@opengis.ch
#ifndef QGSSINGLEGEOMETRYCHECK_H
#define QGSSINGLEGEOMETRYCHECK_H
#define SIP_NO_FILE
#include <QList>
#include <QCoreApplication>
#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<QgsGeometryCheck::Change> &changes );
virtual bool handleChanges( const QList<QgsGeometryCheck::Change> &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<QgsWkbTypes::GeometryType> &compatibleGeometryTypes, QgsGeometryCheckerContext *context );
QgsSingleGeometryCheck( CheckType checkType,
const QgsGeometryCheckContext *context,
const QVariantMap &configuration )
: QgsGeometryCheck( checkType, context, configuration )
{}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const final;
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools,
QList<QgsGeometryCheckError *> &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<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry, const QVariantMap &configuration ) const = 0;
virtual QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry ) const = 0;
private:

View File

@ -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 );
}
} );
}

View File

@ -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

View File

@ -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 );

View File

@ -24,6 +24,7 @@
class QgisInterface;
class QgsGeometryCheckError;
class QgsGeometryChecker;
class QgsFeaturePool;
class QgsGeometryCheckerFixSummaryDialog : public QDialog
{

View File

@ -21,6 +21,7 @@
#include <QDialogButtonBox>
#include <QPlainTextEdit>
#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<QgsGeometryCheckError *>();
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<int>( 0 ) ).toInt();
int fixMethod = QgsSettings().value( sSettingsGroup + error->check()->id(), QVariant::fromValue<int>( 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<int>( 0 ) ).toInt();
int checkedId = QgsSettings().value( sSettingsGroup + check->id(), QVariant::fromValue<int>( 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 ) )
{

View File

@ -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<QgsGeometryCheck *> 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 );

View File

@ -58,13 +58,15 @@ template<> bool QgsGeometryCheckFactoryT<QgsGeometryAngleCheck>::checkApplicabil
return ui.checkBoxAngle->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryAngleCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryAngleCheck>::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<QgsGeometryAreaCheck>::checkApplicabili
return ui.checkBoxArea->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryAreaCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryAreaCheck>::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<QgsGeometryContainedCheck>::checkApplic
return ui.checkBoxCovered->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryContainedCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryContainedCheck>::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<QgsGeometryDangleCheck>::checkApplicabi
return ui.checkBoxDangle->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDangleCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDangleCheck>::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<QgsGeometryDegeneratePolygonCheck>::che
return ui.checkBoxDegeneratePolygon->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDegeneratePolygonCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDegeneratePolygonCheck>::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<QgsGeometryDuplicateCheck>::checkApplic
return ui.checkBoxDuplicates->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDuplicateCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDuplicateCheck>::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<QgsGeometryDuplicateNodesCheck>::checkA
return ui.checkBoxDuplicateNodes->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDuplicateNodesCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDuplicateNodesCheck>::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<QgsGeometryFollowBoundariesCheck>::chec
return enabled;
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryFollowBoundariesCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryFollowBoundariesCheck>::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<QgsVectorLayer *>( 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<QgsGeometryGapCheck>::checkApplicabilit
return ui.checkBoxGaps->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryGapCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryGapCheck>::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<QgsGeometryHoleCheck>::checkApplicabili
return ui.checkBoxNoHoles->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryHoleCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryHoleCheck>::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<QgsGeometryLineIntersectionCheck>::chec
return ui.checkLineIntersection->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryLineIntersectionCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryLineIntersectionCheck>::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<QgsGeometryLineLayerIntersectionCheck>:
return enabled;
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryLineLayerIntersectionCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryLineLayerIntersectionCheck>::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<QgsGeometryMultipartCheck>::checkApplic
return ui.checkBoxMultipart->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryMultipartCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryMultipartCheck>::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<QgsGeometryOverlapCheck>::checkApplicab
return ui.checkBoxOverlaps->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryOverlapCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryOverlapCheck>::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<QgsGeometryPointCoveredByLineCheck>::ch
return ui.checkPointCoveredByLine->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryPointCoveredByLineCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryPointCoveredByLineCheck>::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<QgsGeometryPointInPolygonCheck>::checkA
return ui.checkPointInPolygon->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryPointInPolygonCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryPointInPolygonCheck>::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<QgsGeometrySegmentLengthCheck>::checkAp
return ui.checkBoxSegmentLength->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySegmentLengthCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySegmentLengthCheck>::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<QgsGeometrySelfContactCheck>::checkAppl
return ui.checkBoxSelfContacts->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::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<QgsGeometrySelfIntersectionCheck>::chec
return ui.checkBoxSelfIntersections->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySelfIntersectionCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySelfIntersectionCheck>::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<QgsGeometrySliverPolygonCheck>::checkAp
return ui.checkBoxSliverPolygons->isEnabled();
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySliverPolygonCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySliverPolygonCheck>::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 *QgsGeometryCheckFactoryT<QgsGeometrySliverPolygonCh
QgsSettings().setValue( sSettingsGroup + "sliverPolygonsAreaThreshold", ui.doubleSpinBoxSliverArea->value() );
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<QgsGeometryTypeCheck>::checkApplicabili
return nPoint + nLineString + nPolygon > 0;
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryTypeCheck>::createInstance( QgsGeometryCheckerContext *context, const Ui::QgsGeometryCheckerSetupTab &ui ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryTypeCheck>::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<QgsGeometryTypeCheck>::cre
}
if ( allowedTypes != 0 )
{
return new QgsGeometryTypeCheck( context, allowedTypes );
return new QgsGeometryTypeCheck( context, QVariantMap(), allowedTypes );
}
else
{

View File

@ -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

View File

@ -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 <QButtonGroup>
#include <QDialogButtonBox>
#include <QGroupBox>
@ -32,6 +23,15 @@
#include <QRadioButton>
#include <QGridLayout>
#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<QgsGeometryCheckError *> &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<int>( 0 ) ).toInt();
int checkedid = QgsSettings().value( QgsGeometryCheckerResultTab::sSettingsGroup + error->check()->id(), QVariant::fromValue<int>( 0 ) ).toInt();
for ( const QString &method : error->check()->resolutionMethods() )
{
QRadioButton *radio = new QRadioButton( method );

View File

@ -23,8 +23,8 @@
<iconset resource="../../images/images.qrc">
<normaloff>:/images/icons/qgis-icon-16x16.png</normaloff>:/images/icons/qgis-icon-16x16.png</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_26">
<item>
<widget class="QSplitter" name="mOptionsSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -357,7 +357,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
<number>18</number>
</property>
<widget class="QWidget" name="mOptsPage_Information">
<layout class="QVBoxLayout" name="verticalLayout_5">
@ -433,7 +433,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>289</width>
<width>325</width>
<height>389</height>
</rect>
</property>
@ -2358,56 +2358,86 @@ border-radius: 2px;</string>
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QGroupBox" name="mGeometryAutoFixesGroupBox">
<property name="title">
<string>Automatic Fixes</string>
<widget class="QScrollArea" name="scrollArea_2">
<property name="widgetResizable">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Geometry precision</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="mRemoveDuplicateNodesCheckbox">
<property name="text">
<string>Remove duplicate nodes</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_11" stretch="1,2">
<item>
<widget class="QgsDoubleSpinBox" name="mGeometryPrecisionSpinBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="specialValueText">
<string>[Disabled]</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>651</width>
<height>804</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_20">
<item>
<widget class="QGroupBox" name="mGeometryAutoFixesGroupBox">
<property name="title">
<string>Automatic Fixes</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Geometry precision</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="mRemoveDuplicateNodesCheckbox">
<property name="text">
<string>Remove duplicate nodes</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_11" stretch="0,0">
<item>
<widget class="QLabel" name="mPrecisionUnitsLabel">
<property name="text">
<string>[Units]</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mGeometryPrecisionLineEdit">
<property name="inputMethodHints">
<set>Qt::ImhFormattedNumbersOnly</set>
</property>
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>[No precision restriction]</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="mGeometryValidationGroupBox">
<property name="title">
<string>Geometry checks</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="mTopologyChecksGroupBox">
<property name="title">
<string>Topology checks</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -2418,7 +2448,7 @@ border-radius: 2px;</string>
</widget>
</widget>
</item>
<item row="1" column="0">
<item>
<widget class="QFrame" name="mButtonBoxFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@ -2461,18 +2491,18 @@ border-radius: 2px;</string>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsScrollArea</class>
<extends>QScrollArea</extends>
<header>qgsscrollarea.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsCollapsibleGroupBox</class>
<extends>QGroupBox</extends>
<header>qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsScrollArea</class>
<extends>QScrollArea</extends>
<header>qgsscrollarea.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsProjectionSelectionWidget</class>
<extends>QWidget</extends>
@ -2529,11 +2559,6 @@ border-radius: 2px;</string>
<header>qgsvectorlayerlegendwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mSearchLineEdit</tabstop>
@ -2596,7 +2621,6 @@ border-radius: 2px;</string>
<tabstop>mLayerLegendUrlLineEdit</tabstop>
<tabstop>mLayerLegendUrlFormatComboBox</tabstop>
<tabstop>mRemoveDuplicateNodesCheckbox</tabstop>
<tabstop>mGeometryPrecisionSpinBox</tabstop>
</tabstops>
<resources>
<include location="../../images/images.qrc"/>

View File

@ -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<QString, QString> &layers, const QgsCoordinateReferenceSystem &mapCrs = QgsCoordinateReferenceSystem( "EPSG:4326" ), double prec = 8 ) const;
void cleanupTestContext( QgsGeometryCheckerContext *ctx ) const;
QPair<QgsGeometryCheckContext *, QMap<QString, QgsFeaturePool *> > createTestContext( QTemporaryDir &tempDir, QMap<QString, QString> &layers, const QgsCoordinateReferenceSystem &mapCrs = QgsCoordinateReferenceSystem( "EPSG:4326" ), double prec = 8 ) const;
void cleanupTestContext( QPair<QgsGeometryCheckContext *, QMap<QString, QgsFeaturePool *> > ctx ) const;
void listErrors( const QList<QgsGeometryCheckError *> &checkErrors, const QStringList &messages ) const;
QList<QgsGeometryCheckError *> searchCheckErrors( const QList<QgsGeometryCheckError *> &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<Change> &expectedChanges, const QMap<QString, int> &mergeAttr = QMap<QString, int>() );
bool fixCheckError( QMap<QString, QgsFeaturePool *> featurePools, QgsGeometryCheckError *error, int method, const QgsGeometryCheckError::Status &expectedStatus, const QVector<Change> &expectedChanges, const QMap<QString, int> &mergeAttr = QMap<QString, int>() );
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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QString, int> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> errs1;
@ -418,14 +424,14 @@ void TestQgsGeometryChecks::testDuplicateCheck()
QgsGeometryDuplicateCheckError *dupErr = static_cast<QgsGeometryDuplicateCheckError *>( 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QString, QString> layers;
layers.insert( "follow_ref.shp", "" );
layers.insert( "follow_subj.shp", "" );
QgsGeometryCheckerContext *context = createTestContext( dir, layers );
auto testContext = createTestContext( dir, layers );
// Test detection
QList<QgsGeometryCheckError *> 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<QString, QString> layers;
layers.insert( "gap_layer.shp", "" );
QgsGeometryCheckerContext *context = createTestContext( dir, layers );
auto testContext = createTestContext( dir, layers );
// Test detection
QList<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QString, QString> layers;
layers.insert( QStringLiteral( "missing_vertex.gpkg" ), QString() );
QgsGeometryCheckerContext *context = createTestContext( dir, layers );
auto testContext = createTestContext( dir, layers );
// Test detection
QList<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QgsGeometryCheckError *> 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<QString, QString> &layers, const QgsCoordinateReferenceSystem &mapCrs, double prec ) const
QPair<QgsGeometryCheckContext *, QMap<QString, QgsFeaturePool *> > TestQgsGeometryChecks::createTestContext( QTemporaryDir &tempDir, QMap<QString, QString> &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<QgsGeometryCheckContext *, QMap<QString, QgsFeaturePool *> > 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<QgsGeometryCheckError *> &checkErrors, const QStringList &messages ) const
@ -1096,11 +1118,11 @@ QList<QgsGeometryCheckError *> TestQgsGeometryChecks::searchCheckErrors( const Q
return matching;
}
bool TestQgsGeometryChecks::fixCheckError( QgsGeometryCheckError *error, int method, const QgsGeometryCheckError::Status &expectedStatus, const QVector<Change> &expectedChanges, const QMap<QString, int> &mergeAttrs )
bool TestQgsGeometryChecks::fixCheckError( QMap<QString, QgsFeaturePool *> featurePools, QgsGeometryCheckError *error, int method, const QgsGeometryCheckError::Status &expectedStatus, const QVector<Change> &expectedChanges, const QMap<QString, int> &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 )
{