Compare commits

...

24 Commits

Author SHA1 Message Date
Alexander Bruy
4d0daba084
Merge 4256f9d93a9db1ecb0b3078a3383eeb64d7ed9d4 into 8779a4ea7161a303bfd2ebb556c651f4f36cb807 2025-10-02 14:51:38 +02:00
github-actions[bot]
8779a4ea71 auto-fix pre-commit issues 2025-10-02 11:25:13 +00:00
Martin Dobias
5912a6a69d Review from Benoit 2025-10-02 13:24:05 +02:00
Martin Dobias
bbbe278b22 fix windows build 2025-10-02 13:24:05 +02:00
Martin Dobias
412045d752 get rid of duplicate code 2025-10-02 13:24:05 +02:00
Martin Dobias
cb744707bd fixes 2025-10-02 13:24:05 +02:00
Martin Dobias
6edfbdd66c review from Stefanos 2025-10-02 13:24:05 +02:00
Martin Dobias
f6b55063c4 Add tests for I3S data provider and its index implementation 2025-10-02 13:24:05 +02:00
Martin Dobias
8c0d221f22 Make I3S parsing a bit more robust 2025-10-02 13:24:05 +02:00
Martin Dobias
4f33b6703f fix provider registry test 2025-10-02 13:24:05 +02:00
Martin Dobias
4b6afa446d sip, doc, unity, indentation fixes 2025-10-02 13:24:05 +02:00
Martin Dobias
cbe1a74224 Add ESRI I3S data provider + 2D/3D rendering of it 2025-10-02 13:24:05 +02:00
Alexander Bruy
4256f9d93a add documentation 2025-10-02 08:43:06 +01:00
Alexander Bruy
109f584398 address review 2025-10-02 08:33:07 +01:00
Alexander Bruy
dd35b1b7df remove comment 2025-10-02 08:29:20 +01:00
Alessandro Pasotti
24862b27af [mssql] Fix curvepolygon hidden in browser
Fix #63365
2025-10-02 09:54:09 +10:00
Alexander Bruy
19a8599ca6 make small gaps check/fix work without QgsProject::instance() 2025-10-01 14:51:08 +01:00
Alexander Bruy
75f5f57c10 do not use QgsProject::instance() in the majority of the geometry
checker algorithms

The small gaps check still rely on QgsProject::instance() as well
as corresponding fixer.
2025-10-01 14:51:01 +01:00
Alexander Bruy
adfcf666ed mark geometry checker algorithms as requiring project 2025-10-01 12:02:14 +01:00
Alexander Bruy
0ba6216859 add cancellation support in more places 2025-10-01 10:30:30 +01:00
Alexander Bruy
26c7fa0930 more const 2025-10-01 10:19:20 +01:00
Alexander Bruy
22d17abd58 add cancellation support for collecting errors in geometry checks 2025-10-01 10:19:20 +01:00
Alexander Bruy
c10951d1d3 add tests 2025-10-01 10:19:20 +01:00
Alexander Bruy
6038a4a84c optionally check for duplicated unique IDs when performing geometry
checks

If the uniqueIdFieldIndex in QgsGeometryCheckerContext is a valid field
index, check that there are no duplicated values in that field and fail
if this is the case.
2025-10-01 10:19:20 +01:00
97 changed files with 3467 additions and 302 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -9,6 +9,23 @@ QgsGeometryCheck.ChangeChanged = QgsGeometryCheck.ChangeType.ChangeChanged
QgsGeometryCheck.FeatureNodeCheck = QgsGeometryCheck.CheckType.FeatureNodeCheck
QgsGeometryCheck.FeatureCheck = QgsGeometryCheck.CheckType.FeatureCheck
QgsGeometryCheck.LayerCheck = QgsGeometryCheck.CheckType.LayerCheck
# monkey patching scoped based enum
QgsGeometryCheck.Result.Success.__doc__ = "Operation completed successfully"
QgsGeometryCheck.Result.Canceled.__doc__ = "User canceled calculation"
QgsGeometryCheck.Result.DuplicatedUniqueId.__doc__ = "Found duplicated unique ID value"
QgsGeometryCheck.Result.InvalidReferenceLayer.__doc__ = "Missed or invalid reference layer"
QgsGeometryCheck.Result.GeometryOverlayError.__doc__ = "Error performing geometry overlay operation"
QgsGeometryCheck.Result.__doc__ = """
.. versionadded:: 4.0
* ``Success``: Operation completed successfully
* ``Canceled``: User canceled calculation
* ``DuplicatedUniqueId``: Found duplicated unique ID value
* ``InvalidReferenceLayer``: Missed or invalid reference layer
* ``GeometryOverlayError``: Error performing geometry overlay operation
"""
# --
QgsGeometryCheck.AvailableInValidation = QgsGeometryCheck.Flag.AvailableInValidation
QgsGeometryCheck.Flags = lambda flags=0: QgsGeometryCheck.Flag(flags)
QgsGeometryCheck.Flags.baseClass = QgsGeometryCheck
@ -23,8 +40,8 @@ try:
except (NameError, AttributeError):
pass
try:
QgsGeometryCheck.__virtual_methods__ = ['prepare', 'isCompatible', 'flags', 'availableResolutionMethods', 'resolutionMethods']
QgsGeometryCheck.__abstract_methods__ = ['compatibleGeometryTypes', 'collectErrors', 'description', 'id', 'checkType']
QgsGeometryCheck.__virtual_methods__ = ['prepare', 'isCompatible', 'flags', 'collectErrors', 'availableResolutionMethods', 'resolutionMethods']
QgsGeometryCheck.__abstract_methods__ = ['compatibleGeometryTypes', 'description', 'id', 'checkType']
QgsGeometryCheck.__group__ = ['vector', 'geometry_checker']
except (NameError, AttributeError):
pass

View File

@ -127,6 +127,15 @@ the system is aware of the available geometry checks.
LayerCheck
};
enum class Result /BaseType=IntEnum/
{
Success,
Canceled,
DuplicatedUniqueId,
InvalidReferenceLayer,
GeometryOverlayError
};
enum Flag /BaseType=IntEnum/
{
AvailableInValidation
@ -193,7 +202,7 @@ A list of geometry types for which this check can be performed.
Flags for this geometry check.
%End
virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors /In,Out/, QStringList &messages /In,Out/, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0;
virtual Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors /In,Out/, QStringList &messages /In,Out/, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const;
%Docstring
The main worker method. Check all features available from
``featurePools`` and write errors found to ``errors``. Other status
@ -201,6 +210,9 @@ messages can be written to ``messages``. Progress should be reported to
``feedback``. Only features and layers listed in ``ids`` should be
checked.
:return: QgsGeometryCheck.Result.Success in case of success or error
value on failure.
.. versionadded:: 3.4
%End
@ -257,6 +269,7 @@ Returns the context
};
/************************************************************************

View File

@ -24,9 +24,20 @@ Base configuration for geometry checks.
#include "qgsgeometrycheckcontext.h"
%End
public:
QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *mProject );
QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *mProject = 0, const int uniqueIdFieldIndex = -1 );
%Docstring
Creates a new QgsGeometryCheckContext.
:param precision: The precision used to define gemetry check tolerance.
Tolerance is calculated as pow(10, -precision)
:param mapCrs: The coordinate system in which calculations should be
done
:param transformContext: The coordinate transform context
:param mProject: The project used to resolve additional layers
:param uniqueIdFieldIndex: The index of the unique ID field used to
identify features. If set to valid field
index, geometry checker will fail if this
field is not unique (since QGIS 4.0)
%End
const double tolerance;
@ -37,6 +48,8 @@ Creates a new QgsGeometryCheckContext.
const QgsCoordinateTransformContext transformContext;
const int uniqueIdFieldIndex;
const QgsProject *project() const;
%Docstring
The project can be used to resolve additional layers.

View File

@ -132,7 +132,7 @@ Subclasses need to implement the processGeometry method.
Creates a new single geometry check.
%End
virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = 0, const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const ${SIP_FINAL};
virtual QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = 0, const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const ${SIP_FINAL};
virtual QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry ) const = 0;

View File

@ -1,4 +1,21 @@
# The following has been generated automatically from src/analysis/vector/geometry_checker/qgsgeometrycheck.h
# monkey patching scoped based enum
QgsGeometryCheck.Result.Success.__doc__ = "Operation completed successfully"
QgsGeometryCheck.Result.Canceled.__doc__ = "User canceled calculation"
QgsGeometryCheck.Result.DuplicatedUniqueId.__doc__ = "Found duplicated unique ID value"
QgsGeometryCheck.Result.InvalidReferenceLayer.__doc__ = "Missed or invalid reference layer"
QgsGeometryCheck.Result.GeometryOverlayError.__doc__ = "Error performing geometry overlay operation"
QgsGeometryCheck.Result.__doc__ = """
.. versionadded:: 4.0
* ``Success``: Operation completed successfully
* ``Canceled``: User canceled calculation
* ``DuplicatedUniqueId``: Found duplicated unique ID value
* ``InvalidReferenceLayer``: Missed or invalid reference layer
* ``GeometryOverlayError``: Error performing geometry overlay operation
"""
# --
QgsGeometryCheck.Flags.baseClass = QgsGeometryCheck
Flags = QgsGeometryCheck # dirty hack since SIP seems to introduce the flags in module
try:
@ -11,8 +28,8 @@ try:
except (NameError, AttributeError):
pass
try:
QgsGeometryCheck.__virtual_methods__ = ['prepare', 'isCompatible', 'flags', 'availableResolutionMethods', 'resolutionMethods']
QgsGeometryCheck.__abstract_methods__ = ['compatibleGeometryTypes', 'collectErrors', 'description', 'id', 'checkType']
QgsGeometryCheck.__virtual_methods__ = ['prepare', 'isCompatible', 'flags', 'collectErrors', 'availableResolutionMethods', 'resolutionMethods']
QgsGeometryCheck.__abstract_methods__ = ['compatibleGeometryTypes', 'description', 'id', 'checkType']
QgsGeometryCheck.__group__ = ['vector', 'geometry_checker']
except (NameError, AttributeError):
pass

View File

@ -127,6 +127,15 @@ the system is aware of the available geometry checks.
LayerCheck
};
enum class Result
{
Success,
Canceled,
DuplicatedUniqueId,
InvalidReferenceLayer,
GeometryOverlayError
};
enum Flag
{
AvailableInValidation
@ -193,7 +202,7 @@ A list of geometry types for which this check can be performed.
Flags for this geometry check.
%End
virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors /In,Out/, QStringList &messages /In,Out/, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0;
virtual Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors /In,Out/, QStringList &messages /In,Out/, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const;
%Docstring
The main worker method. Check all features available from
``featurePools`` and write errors found to ``errors``. Other status
@ -201,6 +210,9 @@ messages can be written to ``messages``. Progress should be reported to
``feedback``. Only features and layers listed in ``ids`` should be
checked.
:return: QgsGeometryCheck.Result.Success in case of success or error
value on failure.
.. versionadded:: 3.4
%End
@ -257,6 +269,7 @@ Returns the context
};
/************************************************************************

View File

@ -24,9 +24,20 @@ Base configuration for geometry checks.
#include "qgsgeometrycheckcontext.h"
%End
public:
QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *mProject );
QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *mProject = 0, const int uniqueIdFieldIndex = -1 );
%Docstring
Creates a new QgsGeometryCheckContext.
:param precision: The precision used to define gemetry check tolerance.
Tolerance is calculated as pow(10, -precision)
:param mapCrs: The coordinate system in which calculations should be
done
:param transformContext: The coordinate transform context
:param mProject: The project used to resolve additional layers
:param uniqueIdFieldIndex: The index of the unique ID field used to
identify features. If set to valid field
index, geometry checker will fail if this
field is not unique (since QGIS 4.0)
%End
const double tolerance;
@ -37,6 +48,8 @@ Creates a new QgsGeometryCheckContext.
const QgsCoordinateTransformContext transformContext;
const int uniqueIdFieldIndex;
const QgsProject *project() const;
%Docstring
The project can be used to resolve additional layers.

View File

@ -132,7 +132,7 @@ Subclasses need to implement the processGeometry method.
Creates a new single geometry check.
%End
virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = 0, const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const ${SIP_FINAL};
virtual QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = 0, const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const ${SIP_FINAL};
virtual QList<QgsSingleGeometryCheckError *> processGeometry( const QgsGeometry &geometry ) const = 0;

View File

@ -22,6 +22,7 @@
#include "qgscesiumutils.h"
#include "qgscoordinatetransform.h"
#include "qgsgeotransform.h"
#include "qgsgltfutils.h"
#include "qgsgltf3dutils.h"
#include "qgsquantizedmeshtiles.h"
#include "qgsraycastingutils_p.h"
@ -124,7 +125,7 @@ void QgsTiledSceneChunkLoader::start()
errors.append( QStringLiteral( "Failed to parse tile from '%1'" ).arg( uri ) );
}
}
else if ( format == "cesiumtiles" )
else if ( format == QLatin1String( "cesiumtiles" ) )
{
const QgsCesiumUtils::TileContents tileContent = QgsCesiumUtils::extractGltfFromTileContent( content );
if ( tileContent.gltf.isEmpty() )
@ -132,6 +133,21 @@ void QgsTiledSceneChunkLoader::start()
entityTransform.tileTransform.translate( tileContent.rtcCenter );
mEntity = QgsGltf3DUtils::gltfToEntity( tileContent.gltf, entityTransform, uri, &errors );
}
else if ( format == QLatin1String( "draco" ) )
{
QgsGltfUtils::I3SNodeContext i3sContext;
i3sContext.initFromTile( tile, mFactory.mLayerCrs, mFactory.mBoundsTransform.sourceCrs(), mFactory.mRenderContext.transformContext() );
QString dracoLoadError;
tinygltf::Model model;
if ( !QgsGltfUtils::loadDracoModel( content, i3sContext, model, &dracoLoadError ) )
{
errors.append( dracoLoadError );
return;
}
mEntity = QgsGltf3DUtils::parsedGltfToEntity( model, entityTransform, QString(), &errors );
}
else
return; // unsupported tile content type
@ -173,11 +189,19 @@ Qt3DCore::QEntity *QgsTiledSceneChunkLoader::createEntity( Qt3DCore::QEntity *pa
///
QgsTiledSceneChunkLoaderFactory::QgsTiledSceneChunkLoaderFactory( const Qgs3DRenderContext &context, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, double zValueScale, double zValueOffset )
QgsTiledSceneChunkLoaderFactory::QgsTiledSceneChunkLoaderFactory(
const Qgs3DRenderContext &context,
const QgsTiledSceneIndex &index,
QgsCoordinateReferenceSystem tileCrs,
QgsCoordinateReferenceSystem layerCrs,
double zValueScale,
double zValueOffset
)
: mRenderContext( context )
, mIndex( index )
, mZValueScale( zValueScale )
, mZValueOffset( zValueOffset )
, mLayerCrs( layerCrs )
{
mBoundsTransform = QgsCoordinateTransform( tileCrs, context.crs(), context.transformContext() );
}
@ -346,8 +370,17 @@ void QgsTiledSceneChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
///
QgsTiledSceneLayerChunkedEntity::QgsTiledSceneLayerChunkedEntity( Qgs3DMapSettings *map, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, double maximumScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset )
: QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, zValueScale, zValueOffset ), true )
QgsTiledSceneLayerChunkedEntity::QgsTiledSceneLayerChunkedEntity(
Qgs3DMapSettings *map,
const QgsTiledSceneIndex &index,
QgsCoordinateReferenceSystem tileCrs,
QgsCoordinateReferenceSystem layerCrs,
double maximumScreenError,
bool showBoundingBoxes,
double zValueScale,
double zValueOffset
)
: QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, layerCrs, zValueScale, zValueOffset ), true )
, mIndex( index )
{
setShowBoundingBoxes( showBoundingBoxes );

View File

@ -84,8 +84,12 @@ class QgsTiledSceneChunkLoaderFactory : public QgsChunkLoaderFactory
Q_OBJECT
public:
QgsTiledSceneChunkLoaderFactory(
const Qgs3DRenderContext &context, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs,
double zValueScale, double zValueOffset
const Qgs3DRenderContext &context,
const QgsTiledSceneIndex &index,
QgsCoordinateReferenceSystem tileCrs,
QgsCoordinateReferenceSystem layerCrs,
double zValueScale,
double zValueOffset
);
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
@ -104,6 +108,7 @@ class QgsTiledSceneChunkLoaderFactory : public QgsChunkLoaderFactory
double mZValueScale = 1.0;
double mZValueOffset = 0;
QgsCoordinateTransform mBoundsTransform;
QgsCoordinateReferenceSystem mLayerCrs;
QSet<long long> mPendingHierarchyFetches;
QSet<long long> mFutureHierarchyFetches;
};
@ -123,7 +128,7 @@ class QgsTiledSceneLayerChunkedEntity : public QgsChunkedEntity
{
Q_OBJECT
public:
explicit QgsTiledSceneLayerChunkedEntity( Qgs3DMapSettings *map, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, double maximumScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset );
explicit QgsTiledSceneLayerChunkedEntity( Qgs3DMapSettings *map, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, QgsCoordinateReferenceSystem layerCrs, double maximumScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset );
~QgsTiledSceneLayerChunkedEntity();

View File

@ -66,7 +66,7 @@ Qt3DCore::QEntity *QgsTiledSceneLayer3DRenderer::createEntity( Qgs3DMapSettings
QgsTiledSceneIndex index = tsl->dataProvider()->index();
return new QgsTiledSceneLayerChunkedEntity( map, index, tsl->dataProvider()->sceneCrs(), maximumScreenError(), showBoundingBoxes(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zScale(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zOffset() );
return new QgsTiledSceneLayerChunkedEntity( map, index, tsl->dataProvider()->sceneCrs(), tsl->dataProvider()->crs(), maximumScreenError(), showBoundingBoxes(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zScale(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zOffset() );
}
void QgsTiledSceneLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckAngleAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckAngleAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckAngleAlgorithm *QgsGeometryCheckAngleAlgorithm::createInstance() const
@ -75,7 +75,6 @@ void QgsGeometryCheckAngleAlgorithm::initAlgorithm( const QVariantMap &configura
{
Q_UNUSED( configuration )
// inputs
addParameter(
new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
@ -89,7 +88,6 @@ void QgsGeometryCheckAngleAlgorithm::initAlgorithm( const QVariantMap &configura
QStringLiteral( "MIN_ANGLE" ), QObject::tr( "Minimum angle (in degrees)" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0, 180.0
) );
// outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "ERRORS" ), QObject::tr( "Small angle errors" ), Qgis::ProcessingSourceType::VectorPoint
) );
@ -131,8 +129,8 @@ QVariantMap QgsGeometryCheckAngleAlgorithm::processAlgorithm( const QVariantMap
if ( !input )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
if ( uniqueIdFieldIdx == -1 )
throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
@ -149,9 +147,7 @@ QVariantMap QgsGeometryCheckAngleAlgorithm::processAlgorithm( const QVariantMap
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -173,7 +169,19 @@ QVariantMap QgsGeometryCheckAngleAlgorithm::processAlgorithm( const QVariantMap
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( featurePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( featurePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckAreaAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckAreaAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckAreaAlgorithm *QgsGeometryCheckAreaAlgorithm::createInstance() const
@ -75,7 +75,6 @@ void QgsGeometryCheckAreaAlgorithm::initAlgorithm( const QVariantMap &configurat
{
Q_UNUSED( configuration )
// inputs
addParameter(
new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
@ -89,7 +88,6 @@ void QgsGeometryCheckAreaAlgorithm::initAlgorithm( const QVariantMap &configurat
QStringLiteral( "AREATHRESHOLD" ), QObject::tr( "Area threshold" ), 0, QStringLiteral( "INPUT" )
) );
// outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "ERRORS" ), QObject::tr( "Small polygons errors" ), Qgis::ProcessingSourceType::VectorPoint
) );
@ -134,8 +132,8 @@ QVariantMap QgsGeometryCheckAreaAlgorithm::processAlgorithm( const QVariantMap &
if ( !input )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
if ( uniqueIdFieldIdx == -1 )
throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
@ -156,9 +154,7 @@ QVariantMap QgsGeometryCheckAreaAlgorithm::processAlgorithm( const QVariantMap &
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -180,7 +176,19 @@ QVariantMap QgsGeometryCheckAreaAlgorithm::processAlgorithm( const QVariantMap &
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( featurePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( featurePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -64,7 +64,7 @@ QString QgsGeometryCheckContainedAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckContainedAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckContainedAlgorithm *QgsGeometryCheckContainedAlgorithm::createInstance() const
@ -76,7 +76,6 @@ void QgsGeometryCheckContainedAlgorithm::initAlgorithm( const QVariantMap &confi
{
Q_UNUSED( configuration )
// inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint )
@ -90,7 +89,6 @@ void QgsGeometryCheckContainedAlgorithm::initAlgorithm( const QVariantMap &confi
QStringLiteral( "POLYGONS" ), QObject::tr( "Polygon layers" ), Qgis::ProcessingSourceType::VectorPolygon
) );
// outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "ERRORS" ), QObject::tr( "Errors from contained features" ), Qgis::ProcessingSourceType::VectorPoint
) );
@ -139,7 +137,6 @@ QVariantMap QgsGeometryCheckContainedAlgorithm::processAlgorithm( const QVariant
if ( polygonLayers.isEmpty() )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "POLYGONS" ) ) );
const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
if ( uniqueIdFieldIdx == -1 )
@ -163,9 +160,7 @@ QVariantMap QgsGeometryCheckContainedAlgorithm::processAlgorithm( const QVariant
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -194,7 +189,19 @@ QVariantMap QgsGeometryCheckContainedAlgorithm::processAlgorithm( const QVariant
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckDangleAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckDangleAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckDangleAlgorithm *QgsGeometryCheckDangleAlgorithm::createInstance() const
@ -118,7 +118,6 @@ QgsFields QgsGeometryCheckDangleAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckDangleAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -150,8 +149,7 @@ QVariantMap QgsGeometryCheckDangleAlgorithm::processAlgorithm( const QVariantMap
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -169,7 +167,19 @@ QVariantMap QgsGeometryCheckDangleAlgorithm::processAlgorithm( const QVariantMap
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -62,7 +62,7 @@ QString QgsGeometryCheckDegeneratePolygonAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckDegeneratePolygonAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckDegeneratePolygonAlgorithm *QgsGeometryCheckDegeneratePolygonAlgorithm::createInstance() const
@ -144,9 +144,7 @@ QVariantMap QgsGeometryCheckDegeneratePolygonAlgorithm::processAlgorithm( const
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, project->crs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -164,7 +162,19 @@ QVariantMap QgsGeometryCheckDegeneratePolygonAlgorithm::processAlgorithm( const
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckDuplicateAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckDuplicateAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckDuplicateAlgorithm *QgsGeometryCheckDuplicateAlgorithm::createInstance() const
@ -124,7 +124,6 @@ QgsFields QgsGeometryCheckDuplicateAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckDuplicateAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -140,7 +139,6 @@ QVariantMap QgsGeometryCheckDuplicateAlgorithm::processAlgorithm( const QVariant
throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
const QgsField uniqueIdField = input->fields().at( uniqueIdFieldIdx );
QgsFields fields = outputFields();
fields.append( uniqueIdField );
@ -155,8 +153,7 @@ QVariantMap QgsGeometryCheckDuplicateAlgorithm::processAlgorithm( const QVariant
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -174,7 +171,19 @@ QVariantMap QgsGeometryCheckDuplicateAlgorithm::processAlgorithm( const QVariant
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckDuplicateNodesAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckDuplicateNodesAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckDuplicateNodesAlgorithm *QgsGeometryCheckDuplicateNodesAlgorithm::createInstance() const
@ -119,7 +119,6 @@ QgsFields QgsGeometryCheckDuplicateNodesAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckDuplicateNodesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -150,9 +149,7 @@ QVariantMap QgsGeometryCheckDuplicateNodesAlgorithm::processAlgorithm( const QVa
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -172,7 +169,19 @@ QVariantMap QgsGeometryCheckDuplicateNodesAlgorithm::processAlgorithm( const QVa
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckFollowBoundariesAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckFollowBoundariesAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckFollowBoundariesAlgorithm *QgsGeometryCheckFollowBoundariesAlgorithm::createInstance() const
@ -122,7 +122,6 @@ QgsFields QgsGeometryCheckFollowBoundariesAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckFollowBoundariesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -153,9 +152,7 @@ QVariantMap QgsGeometryCheckFollowBoundariesAlgorithm::processAlgorithm( const Q
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -175,7 +172,23 @@ QVariantMap QgsGeometryCheckFollowBoundariesAlgorithm::processAlgorithm( const Q
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( featurePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( featurePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
else if ( res == QgsGeometryCheck::Result::InvalidReferenceLayer )
{
throw QgsProcessingException( QObject::tr( "Invalid reference layer." ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -67,7 +67,7 @@ QString QgsGeometryCheckGapAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckGapAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckGapAlgorithm *QgsGeometryCheckGapAlgorithm::createInstance() const
@ -139,7 +139,6 @@ QgsFields QgsGeometryCheckGapAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckGapAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -166,8 +165,8 @@ QVariantMap QgsGeometryCheckGapAlgorithm::processAlgorithm( const QVariantMap &p
if ( !sink_errors )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "ERRORS" ) ) );
QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
if ( uniqueIdFieldIdx == -1 )
throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
@ -182,9 +181,7 @@ QVariantMap QgsGeometryCheckGapAlgorithm::processAlgorithm( const QVariantMap &p
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -211,7 +208,23 @@ QVariantMap QgsGeometryCheckGapAlgorithm::processAlgorithm( const QVariantMap &p
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
else if ( res == QgsGeometryCheck::Result::GeometryOverlayError )
{
throw QgsProcessingException( QObject::tr( "Failed to perform geometry overlay operation." ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckHoleAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckHoleAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckHoleAlgorithm *QgsGeometryCheckHoleAlgorithm::createInstance() const
@ -75,7 +75,6 @@ void QgsGeometryCheckHoleAlgorithm::initAlgorithm( const QVariantMap &configurat
{
Q_UNUSED( configuration )
// inputs
addParameter(
new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
@ -86,7 +85,6 @@ void QgsGeometryCheckHoleAlgorithm::initAlgorithm( const QVariantMap &configurat
QStringLiteral( "UNIQUE_ID" ), QObject::tr( "Unique feature identifier" ), QString(), QStringLiteral( "INPUT" )
) );
// outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "ERRORS" ), QObject::tr( "Holes errors" ), Qgis::ProcessingSourceType::VectorPoint
) );
@ -132,8 +130,8 @@ QVariantMap QgsGeometryCheckHoleAlgorithm::processAlgorithm( const QVariantMap &
if ( !input )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
if ( uniqueIdFieldIdx == -1 )
throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
@ -154,9 +152,7 @@ QVariantMap QgsGeometryCheckHoleAlgorithm::processAlgorithm( const QVariantMap &
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -174,7 +170,19 @@ QVariantMap QgsGeometryCheckHoleAlgorithm::processAlgorithm( const QVariantMap &
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( featurePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( featurePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckLineIntersectionAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckLineIntersectionAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckLineIntersectionAlgorithm *QgsGeometryCheckLineIntersectionAlgorithm::createInstance() const
@ -118,7 +118,6 @@ QgsFields QgsGeometryCheckLineIntersectionAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckLineIntersectionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -149,9 +148,7 @@ QVariantMap QgsGeometryCheckLineIntersectionAlgorithm::processAlgorithm( const Q
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -169,7 +166,19 @@ QVariantMap QgsGeometryCheckLineIntersectionAlgorithm::processAlgorithm( const Q
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckLineLayerIntersectionAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckLineLayerIntersectionAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckLineLayerIntersectionAlgorithm *QgsGeometryCheckLineLayerIntersectionAlgorithm::createInstance() const
@ -124,7 +124,6 @@ QgsFields QgsGeometryCheckLineLayerIntersectionAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckLineLayerIntersectionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -159,9 +158,7 @@ QVariantMap QgsGeometryCheckLineLayerIntersectionAlgorithm::processAlgorithm( co
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QVariantMap configurationCheck;
@ -185,7 +182,19 @@ QVariantMap QgsGeometryCheckLineLayerIntersectionAlgorithm::processAlgorithm( co
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -64,7 +64,7 @@ QString QgsGeometryCheckMissingVertexAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckMissingVertexAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckMissingVertexAlgorithm *QgsGeometryCheckMissingVertexAlgorithm::createInstance() const
@ -76,7 +76,6 @@ void QgsGeometryCheckMissingVertexAlgorithm::initAlgorithm( const QVariantMap &c
{
Q_UNUSED( configuration )
// inputs
addParameter(
new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
@ -87,7 +86,6 @@ void QgsGeometryCheckMissingVertexAlgorithm::initAlgorithm( const QVariantMap &c
QStringLiteral( "UNIQUE_ID" ), QObject::tr( "Unique feature identifier" ), QString(), QStringLiteral( "INPUT" )
) );
// outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "ERRORS" ), QObject::tr( "Missing vertices errors" ), Qgis::ProcessingSourceType::VectorPoint
) );
@ -133,8 +131,8 @@ QVariantMap QgsGeometryCheckMissingVertexAlgorithm::processAlgorithm( const QVar
if ( !input )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
if ( uniqueIdFieldIdx == -1 )
throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
@ -151,9 +149,7 @@ QVariantMap QgsGeometryCheckMissingVertexAlgorithm::processAlgorithm( const QVar
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -170,7 +166,19 @@ QVariantMap QgsGeometryCheckMissingVertexAlgorithm::processAlgorithm( const QVar
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( featurePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( featurePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckMultipartAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckMultipartAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckMultipartAlgorithm *QgsGeometryCheckMultipartAlgorithm::createInstance() const
@ -122,7 +122,6 @@ QgsFields QgsGeometryCheckMultipartAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckMultipartAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -153,8 +152,7 @@ QVariantMap QgsGeometryCheckMultipartAlgorithm::processAlgorithm( const QVariant
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -172,7 +170,19 @@ QVariantMap QgsGeometryCheckMultipartAlgorithm::processAlgorithm( const QVariant
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( featurePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( featurePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckOverlapAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckOverlapAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckOverlapAlgorithm *QgsGeometryCheckOverlapAlgorithm::createInstance() const
@ -119,7 +119,6 @@ QgsFields QgsGeometryCheckOverlapAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckOverlapAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -128,8 +127,8 @@ QVariantMap QgsGeometryCheckOverlapAlgorithm::processAlgorithm( const QVariantMa
if ( !input )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
const QString uniqueIdFieldName( parameterAsString( parameters, QStringLiteral( "UNIQUE_ID" ), context ) );
const int uniqueIdFieldIdx = input->fields().indexFromName( uniqueIdFieldName );
if ( uniqueIdFieldIdx == -1 )
throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( uniqueIdFieldName ) );
@ -153,9 +152,7 @@ QVariantMap QgsGeometryCheckOverlapAlgorithm::processAlgorithm( const QVariantMa
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -177,7 +174,19 @@ QVariantMap QgsGeometryCheckOverlapAlgorithm::processAlgorithm( const QVariantMa
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );
@ -187,6 +196,11 @@ QVariantMap QgsGeometryCheckOverlapAlgorithm::processAlgorithm( const QVariantMa
for ( const QgsGeometryCheckError *error : checkErrors )
{
if ( feedback->isCanceled() )
{
break;
}
const QgsGeometryOverlapCheckError *overlapError = dynamic_cast<const QgsGeometryOverlapCheckError *>( error );
if ( !overlapError )
break;

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckPointCoveredByLineAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckPointCoveredByLineAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckPointCoveredByLineAlgorithm *QgsGeometryCheckPointCoveredByLineAlgorithm::createInstance() const
@ -118,7 +118,6 @@ QgsFields QgsGeometryCheckPointCoveredByLineAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckPointCoveredByLineAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_errors;
@ -148,9 +147,7 @@ QVariantMap QgsGeometryCheckPointCoveredByLineAlgorithm::processAlgorithm( const
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -179,7 +176,19 @@ QVariantMap QgsGeometryCheckPointCoveredByLineAlgorithm::processAlgorithm( const
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckPointInPolygonAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckPointInPolygonAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckPointInPolygonAlgorithm *QgsGeometryCheckPointInPolygonAlgorithm::createInstance() const
@ -118,7 +118,6 @@ QgsFields QgsGeometryCheckPointInPolygonAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckPointInPolygonAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_errors;
@ -148,9 +147,7 @@ QVariantMap QgsGeometryCheckPointInPolygonAlgorithm::processAlgorithm( const QVa
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -178,7 +175,19 @@ QVariantMap QgsGeometryCheckPointInPolygonAlgorithm::processAlgorithm( const QVa
}
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckSegmentLengthAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckSegmentLengthAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckSegmentLengthAlgorithm *QgsGeometryCheckSegmentLengthAlgorithm::createInstance() const
@ -123,7 +123,6 @@ QgsFields QgsGeometryCheckSegmentLengthAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckSegmentLengthAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -153,9 +152,7 @@ QVariantMap QgsGeometryCheckSegmentLengthAlgorithm::processAlgorithm( const QVar
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -177,7 +174,19 @@ QVariantMap QgsGeometryCheckSegmentLengthAlgorithm::processAlgorithm( const QVar
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckSelfContactAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckSelfContactAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckSelfContactAlgorithm *QgsGeometryCheckSelfContactAlgorithm::createInstance() const
@ -119,7 +119,6 @@ QgsFields QgsGeometryCheckSelfContactAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckSelfContactAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -150,9 +149,7 @@ QVariantMap QgsGeometryCheckSelfContactAlgorithm::processAlgorithm( const QVaria
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -170,7 +167,19 @@ QVariantMap QgsGeometryCheckSelfContactAlgorithm::processAlgorithm( const QVaria
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -63,7 +63,7 @@ QString QgsGeometryCheckSelfIntersectionAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckSelfIntersectionAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckSelfIntersectionAlgorithm *QgsGeometryCheckSelfIntersectionAlgorithm::createInstance() const
@ -123,7 +123,6 @@ QgsFields QgsGeometryCheckSelfIntersectionAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckSelfIntersectionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -154,9 +153,7 @@ QVariantMap QgsGeometryCheckSelfIntersectionAlgorithm::processAlgorithm( const Q
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
// Test detection
QList<QgsGeometryCheckError *> checkErrors;
@ -174,7 +171,19 @@ QVariantMap QgsGeometryCheckSelfIntersectionAlgorithm::processAlgorithm( const Q
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( featurePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( featurePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );
@ -184,6 +193,11 @@ QVariantMap QgsGeometryCheckSelfIntersectionAlgorithm::processAlgorithm( const Q
for ( const QgsGeometryCheckError *error : checkErrors )
{
if ( feedback->isCanceled() )
{
break;
}
const QgsGeometryCheckErrorSingle *singleError = dynamic_cast<const QgsGeometryCheckErrorSingle *>( error );
if ( !singleError )
break;

View File

@ -68,7 +68,7 @@ QString QgsGeometryCheckSliverPolygonAlgorithm::shortHelpString() const
Qgis::ProcessingAlgorithmFlags QgsGeometryCheckSliverPolygonAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
QgsGeometryCheckSliverPolygonAlgorithm *QgsGeometryCheckSliverPolygonAlgorithm::createInstance() const
@ -130,7 +130,6 @@ QgsFields QgsGeometryCheckSliverPolygonAlgorithm::outputFields()
return fields;
}
QVariantMap QgsGeometryCheckSliverPolygonAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest_output;
@ -171,8 +170,8 @@ QVariantMap QgsGeometryCheckSliverPolygonAlgorithm::processAlgorithm( const QVar
QVariantMap configurationCheck;
configurationCheck.insert( "maxArea", maxArea );
configurationCheck.insert( "threshold", maxThinness );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project(), uniqueIdFieldIdx );
const QgsGeometrySliverPolygonCheck check( &checkContext, configurationCheck );
multiStepFeedback.setCurrentStep( 1 );
@ -185,7 +184,19 @@ QVariantMap QgsGeometryCheckSliverPolygonAlgorithm::processAlgorithm( const QVar
multiStepFeedback.setCurrentStep( 2 );
feedback->setProgressText( QObject::tr( "Collecting errors…" ) );
check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
QgsGeometryCheck::Result res = check.collectErrors( checkerFeaturePools, checkErrors, messages, feedback );
if ( res == QgsGeometryCheck::Result::Success )
{
feedback->pushInfo( QObject::tr( "Errors collected successfully." ) );
}
else if ( res == QgsGeometryCheck::Result::Canceled )
{
throw QgsProcessingException( QObject::tr( "Operation was canceled." ) );
}
else if ( res == QgsGeometryCheck::Result::DuplicatedUniqueId )
{
throw QgsProcessingException( QObject::tr( "Field '%1' contains non-unique values and can not be used as unique ID." ).arg( uniqueIdFieldName ) );
}
multiStepFeedback.setCurrentStep( 3 );
feedback->setProgressText( QObject::tr( "Exporting errors…" ) );

View File

@ -72,7 +72,6 @@ void QgsFixGeometryAngleAlgorithm::initAlgorithm( const QVariantMap &configurati
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
) );
@ -99,7 +98,6 @@ void QgsFixGeometryAngleAlgorithm::initAlgorithm( const QVariantMap &configurati
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Small angle fixed layer" ), Qgis::ProcessingSourceType::VectorAnyGeometry
) );
@ -146,7 +144,7 @@ QVariantMap QgsFixGeometryAngleAlgorithm::processAlgorithm( const QVariantMap &p
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in input layer." ).arg( featIdFieldName ) );
QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
const QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
if ( inputFeatIdField.type() != errors->fields().at( errors->fields().indexFromName( featIdFieldName ) ).type() )
throw QgsProcessingException( QObject::tr( "Field %1 does not have the same type as in the error layer." ).arg( featIdFieldName ) );
@ -167,8 +165,7 @@ QVariantMap QgsFixGeometryAngleAlgorithm::processAlgorithm( const QVariantMap &p
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
QVariantMap configurationCheck;
// maximum limit, we know that every feature to process is an error (otherwise it is not treated and marked as obsolete)
@ -280,7 +277,7 @@ bool QgsFixGeometryAngleAlgorithm::prepareAlgorithm( const QVariantMap &paramete
Qgis::ProcessingAlgorithmFlags QgsFixGeometryAngleAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -68,7 +68,6 @@ void QgsFixGeometryAreaAlgorithm::initAlgorithm( const QVariantMap &configuratio
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
) );
@ -76,7 +75,6 @@ void QgsFixGeometryAreaAlgorithm::initAlgorithm( const QVariantMap &configuratio
QStringLiteral( "ERRORS" ), QObject::tr( "Error layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint )
) );
// Specific inputs for this check
QStringList methods;
{
QList<QgsGeometryCheckResolutionMethod> checkMethods = QgsGeometryAreaCheck( nullptr, QVariantMap() ).availableResolutionMethods();
@ -112,7 +110,6 @@ void QgsFixGeometryAreaAlgorithm::initAlgorithm( const QVariantMap &configuratio
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Small polygons merged layer" ), Qgis::ProcessingSourceType::VectorPolygon
) );
@ -159,11 +156,11 @@ QVariantMap QgsFixGeometryAreaAlgorithm::processAlgorithm( const QVariantMap &pa
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in the error layer." ).arg( ringIdxFieldName ) );
if ( errors->fields().indexFromName( vertexIdxFieldName ) == -1 )
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in the error layer." ).arg( vertexIdxFieldName ) );
int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
const int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in input layer." ).arg( featIdFieldName ) );
QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
const QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
if ( inputFeatIdField.type() != errors->fields().at( errors->fields().indexFromName( featIdFieldName ) ).type() )
throw QgsProcessingException( QObject::tr( "Field %1 does not have the same type as in the error layer." ).arg( featIdFieldName ) );
@ -180,8 +177,7 @@ QVariantMap QgsFixGeometryAreaAlgorithm::processAlgorithm( const QVariantMap &pa
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
QVariantMap configurationCheck;
// maximum limit, we know that every feature to process is an error (otherwise it is not treated and marked as obsolete)
@ -307,7 +303,7 @@ bool QgsFixGeometryAreaAlgorithm::prepareAlgorithm( const QVariantMap &parameter
Qgis::ProcessingAlgorithmFlags QgsFixGeometryAreaAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -70,7 +70,6 @@ void QgsFixGeometryDeleteFeaturesAlgorithm::initAlgorithm( const QVariantMap &co
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
QList<int>()
@ -86,7 +85,6 @@ void QgsFixGeometryDeleteFeaturesAlgorithm::initAlgorithm( const QVariantMap &co
QString(), QStringLiteral( "ERRORS" )
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Cleaned layer" ), Qgis::ProcessingSourceType::VectorAnyGeometry
) );

View File

@ -69,7 +69,6 @@ void QgsFixGeometryDuplicateNodesAlgorithm::initAlgorithm( const QVariantMap &co
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
) );
@ -96,7 +95,6 @@ void QgsFixGeometryDuplicateNodesAlgorithm::initAlgorithm( const QVariantMap &co
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Fixed duplicate vertices layer" ), Qgis::ProcessingSourceType::VectorAnyGeometry
) );
@ -143,7 +141,7 @@ QVariantMap QgsFixGeometryDuplicateNodesAlgorithm::processAlgorithm( const QVari
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in input layer." ).arg( featIdFieldName ) );
QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
const QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
if ( inputFeatIdField.type() != errors->fields().at( errors->fields().indexFromName( featIdFieldName ) ).type() )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not have the same type as in the error layer." ).arg( featIdFieldName ) );
@ -164,8 +162,7 @@ QVariantMap QgsFixGeometryDuplicateNodesAlgorithm::processAlgorithm( const QVari
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
const QgsGeometryDuplicateNodesCheck check( &checkContext, QVariantMap() );
@ -269,7 +266,7 @@ bool QgsFixGeometryDuplicateNodesAlgorithm::prepareAlgorithm( const QVariantMap
Qgis::ProcessingAlgorithmFlags QgsFixGeometryDuplicateNodesAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -68,7 +68,6 @@ void QgsFixGeometryGapAlgorithm::initAlgorithm( const QVariantMap &configuration
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
) );
@ -79,7 +78,6 @@ void QgsFixGeometryGapAlgorithm::initAlgorithm( const QVariantMap &configuration
QStringLiteral( "GAPS" ), QObject::tr( "Gaps layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
) );
// Specific inputs for this check
QStringList methods;
{
QList<QgsGeometryCheckResolutionMethod> checkMethods = QgsGeometryGapCheck( nullptr, QVariantMap() ).availableResolutionMethods();
@ -100,7 +98,6 @@ void QgsFixGeometryGapAlgorithm::initAlgorithm( const QVariantMap &configuration
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Gaps-filled layer" ), Qgis::ProcessingSourceType::VectorPolygon
) );
@ -158,7 +155,7 @@ QVariantMap QgsFixGeometryGapAlgorithm::processAlgorithm( const QVariantMap &par
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in the gaps layer." ).arg( errorIdFieldName ) );
if ( neighbors->fields().indexFromName( featIdFieldName ) == -1 )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in the neighbors layer." ).arg( featIdFieldName ) );
int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
const int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in input layer." ).arg( featIdFieldName ) );
@ -184,10 +181,7 @@ QVariantMap QgsFixGeometryGapAlgorithm::processAlgorithm( const QVariantMap &par
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext(
mTolerance, input->sourceCrs(), project->transformContext(), project
);
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
const QgsGeometryGapCheck check( &checkContext, QVariantMap() );
@ -201,7 +195,7 @@ QVariantMap QgsFixGeometryGapAlgorithm::processAlgorithm( const QVariantMap &par
// To add features into the layer, the geometry checker looks for the layer in the project
if ( method == QgsGeometryGapCheck::ResolutionMethod::CreateNewFeature )
{
project->addMapLayer( fixedLayer.get(), false, false );
context.project()->addMapLayer( fixedLayer.get(), false, false );
fixedLayer->startEditing();
}
@ -271,7 +265,7 @@ QVariantMap QgsFixGeometryGapAlgorithm::processAlgorithm( const QVariantMap &par
{
if ( !fixedLayer->commitChanges() )
throw QgsProcessingException( QObject::tr( "Unable to add gap features" ) );
project->removeMapLayer( fixedLayer.get() );
context.project()->removeMapLayer( fixedLayer.get() );
}
progression = 0;
@ -308,7 +302,7 @@ bool QgsFixGeometryGapAlgorithm::prepareAlgorithm( const QVariantMap &parameters
Qgis::ProcessingAlgorithmFlags QgsFixGeometryGapAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -69,7 +69,6 @@ void QgsFixGeometryHoleAlgorithm::initAlgorithm( const QVariantMap &configuratio
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
) );
@ -96,7 +95,6 @@ void QgsFixGeometryHoleAlgorithm::initAlgorithm( const QVariantMap &configuratio
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Holes-filled layer" ), Qgis::ProcessingSourceType::VectorAnyGeometry
) );
@ -143,7 +141,7 @@ QVariantMap QgsFixGeometryHoleAlgorithm::processAlgorithm( const QVariantMap &pa
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in input layer." ).arg( featIdFieldName ) );
QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
const QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
if ( inputFeatIdField.type() != errors->fields().at( errors->fields().indexFromName( featIdFieldName ) ).type() )
throw QgsProcessingException( QObject::tr( "Field %1 does not have the same type as in the error layer." ).arg( featIdFieldName ) );
@ -160,8 +158,7 @@ QVariantMap QgsFixGeometryHoleAlgorithm::processAlgorithm( const QVariantMap &pa
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
const QgsGeometryHoleCheck check( &checkContext, QVariantMap() );
@ -270,7 +267,7 @@ bool QgsFixGeometryHoleAlgorithm::prepareAlgorithm( const QVariantMap &parameter
Qgis::ProcessingAlgorithmFlags QgsFixGeometryHoleAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -69,7 +69,6 @@ void QgsFixGeometryMissingVertexAlgorithm::initAlgorithm( const QVariantMap &con
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
) );
@ -97,7 +96,6 @@ void QgsFixGeometryMissingVertexAlgorithm::initAlgorithm( const QVariantMap &con
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Border vertices fixed layer" ), Qgis::ProcessingSourceType::VectorPolygon
) );
@ -140,11 +138,11 @@ QVariantMap QgsFixGeometryMissingVertexAlgorithm::processAlgorithm( const QVaria
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in the error layer." ).arg( ringIdxFieldName ) );
if ( errors->fields().indexFromName( vertexIdxFieldName ) == -1 )
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in the error layer." ).arg( vertexIdxFieldName ) );
int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
const int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field %1 does not exist in input layer." ).arg( featIdFieldName ) );
QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
const QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
if ( inputFeatIdField.type() != errors->fields().at( errors->fields().indexFromName( featIdFieldName ) ).type() )
throw QgsProcessingException( QObject::tr( "Field %1 does not have the same type as in the error layer." ).arg( featIdFieldName ) );
@ -165,8 +163,7 @@ QVariantMap QgsFixGeometryMissingVertexAlgorithm::processAlgorithm( const QVaria
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
const QgsGeometryMissingVertexCheck check( &checkContext, QVariantMap() );
@ -276,7 +273,7 @@ bool QgsFixGeometryMissingVertexAlgorithm::prepareAlgorithm( const QVariantMap &
Qgis::ProcessingAlgorithmFlags QgsFixGeometryMissingVertexAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -71,7 +71,6 @@ void QgsFixGeometryMultipartAlgorithm::initAlgorithm( const QVariantMap &configu
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) << static_cast<int>( Qgis::ProcessingSourceType::VectorLine )
) );
@ -83,7 +82,6 @@ void QgsFixGeometryMultipartAlgorithm::initAlgorithm( const QVariantMap &configu
QStringLiteral( "id" ), QStringLiteral( "ERRORS" )
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Strictly-multipart layer" ), Qgis::ProcessingSourceType::VectorAnyGeometry
) );
@ -121,7 +119,7 @@ QVariantMap QgsFixGeometryMultipartAlgorithm::processAlgorithm( const QVariantMa
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in input layer." ).arg( featIdFieldName ) );
QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
const QgsField inputFeatIdField = input->fields().at( inputIdFieldIndex );
if ( inputFeatIdField.type() != errors->fields().at( errors->fields().indexFromName( featIdFieldName ) ).type() )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not have the same type as in the error layer." ).arg( featIdFieldName ) );
@ -142,8 +140,7 @@ QVariantMap QgsFixGeometryMultipartAlgorithm::processAlgorithm( const QVariantMa
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
const QgsGeometryMultipartCheck check( &checkContext, QVariantMap() );
@ -243,7 +240,7 @@ bool QgsFixGeometryMultipartAlgorithm::prepareAlgorithm( const QVariantMap &para
Qgis::ProcessingAlgorithmFlags QgsFixGeometryMultipartAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -69,7 +69,6 @@ void QgsFixGeometryOverlapAlgorithm::initAlgorithm( const QVariantMap &configura
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
) );
@ -91,7 +90,6 @@ void QgsFixGeometryOverlapAlgorithm::initAlgorithm( const QVariantMap &configura
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "No-overlap layer" ), Qgis::ProcessingSourceType::VectorPolygon
) );
@ -163,8 +161,7 @@ QVariantMap QgsFixGeometryOverlapAlgorithm::processAlgorithm( const QVariantMap
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
const QgsGeometryOverlapCheck check( &checkContext, QVariantMap() );
@ -280,7 +277,7 @@ bool QgsFixGeometryOverlapAlgorithm::prepareAlgorithm( const QVariantMap &parame
Qgis::ProcessingAlgorithmFlags QgsFixGeometryOverlapAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -69,7 +69,6 @@ void QgsFixGeometrySelfIntersectionAlgorithm::initAlgorithm( const QVariantMap &
{
Q_UNUSED( configuration )
// Inputs
addParameter( new QgsProcessingParameterFeatureSource(
QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
QList<int>()
@ -80,7 +79,6 @@ void QgsFixGeometrySelfIntersectionAlgorithm::initAlgorithm( const QVariantMap &
QStringLiteral( "ERRORS" ), QObject::tr( "Error layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint )
) );
// Specific inputs for this check
QStringList methods;
{
QList<QgsGeometryCheckResolutionMethod> checkMethods = QgsGeometrySelfIntersectionCheck( nullptr, QVariantMap() ).availableResolutionMethods();
@ -121,7 +119,6 @@ void QgsFixGeometrySelfIntersectionAlgorithm::initAlgorithm( const QVariantMap &
Qgis::ProcessingFieldParameterDataType::Numeric
) );
// Outputs
addParameter( new QgsProcessingParameterFeatureSink(
QStringLiteral( "OUTPUT" ), QObject::tr( "Self-intersections fixed layer" ), Qgis::ProcessingSourceType::VectorPolygon
) );
@ -157,7 +154,6 @@ QVariantMap QgsFixGeometrySelfIntersectionAlgorithm::processAlgorithm( const QVa
const QString segment1FieldName = parameterAsString( parameters, QStringLiteral( "SEGMENT_1" ), context );
const QString segment2FieldName = parameterAsString( parameters, QStringLiteral( "SEGMENT_2" ), context );
// Specific inputs for this check
const int method = parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context );
// Verify that input fields exists
@ -173,7 +169,7 @@ QVariantMap QgsFixGeometrySelfIntersectionAlgorithm::processAlgorithm( const QVa
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in the error layer." ).arg( segment1FieldName ) );
if ( errors->fields().indexFromName( segment2FieldName ) == -1 )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in the error layer." ).arg( segment2FieldName ) );
int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
const int inputIdFieldIndex = input->fields().indexFromName( featIdFieldName );
if ( inputIdFieldIndex == -1 )
throw QgsProcessingException( QObject::tr( "Field \"%1\" does not exist in input layer." ).arg( featIdFieldName ) );
@ -199,8 +195,7 @@ QVariantMap QgsFixGeometrySelfIntersectionAlgorithm::processAlgorithm( const QVa
if ( !sink_report )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "REPORT" ) ) );
const QgsProject *project = QgsProject::instance();
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), project->transformContext(), project );
QgsGeometryCheckContext checkContext = QgsGeometryCheckContext( mTolerance, input->sourceCrs(), context.transformContext(), context.project() );
const QgsGeometrySelfIntersectionCheck check( &checkContext, QVariantMap() );
@ -357,7 +352,7 @@ bool QgsFixGeometrySelfIntersectionAlgorithm::prepareAlgorithm( const QVariantMa
Qgis::ProcessingAlgorithmFlags QgsFixGeometrySelfIntersectionAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading;
return QgsProcessingAlgorithm::flags() | Qgis::ProcessingAlgorithmFlag::NoThreading | Qgis::ProcessingAlgorithmFlag::RequiresProject;
}
///@endcond

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryanglecheck.h"
#include "qgsgeometryutils.h"
@ -24,13 +25,29 @@ QList<Qgis::GeometryType> QgsGeometryAngleCheck::compatibleGeometryTypes() const
return factoryCompatibleGeometryTypes();
}
void QgsGeometryAngleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryAngleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, context() );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -69,6 +86,7 @@ void QgsGeometryAngleCheck::collectErrors( const QMap<QString, QgsFeaturePool *>
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryAngleCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -39,7 +39,7 @@ class ANALYSIS_EXPORT QgsGeometryAngleCheck : public QgsGeometryCheck
, mMinAngle( configuration.value( QStringLiteral( "minAngle" ), 0.0 ).toDouble() )
{}
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
QList<Qgis::GeometryType> compatibleGeometryTypes() const override;

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryengine.h"
#include "qgsgeometrycollection.h"
@ -20,13 +21,29 @@
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryAreaCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryAreaCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
const double layerToMapUnits = scaleFactor( layerFeature.layer() );
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
@ -39,6 +56,7 @@ void QgsGeometryAreaCheck::collectErrors( const QMap<QString, QgsFeaturePool *>
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryAreaCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const

View File

@ -44,7 +44,7 @@ class ANALYSIS_EXPORT QgsGeometryAreaCheck : public QgsGeometryCheck
, mAreaThreshold( configurationValue<double>( "areaThreshold" ) )
{}
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
QString id() const override { return factoryId(); }

View File

@ -46,6 +46,18 @@ QgsGeometryCheck::Flags QgsGeometryCheck::flags() const
return QgsGeometryCheck::Flags();
}
QgsGeometryCheck::Result QgsGeometryCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors SIP_INOUT, QStringList &messages SIP_INOUT, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( featurePools )
Q_UNUSED( errors )
Q_UNUSED( messages )
Q_UNUSED( feedback )
Q_UNUSED( ids )
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, QgsGeometryCheck::Changes &changes ) const
{
Q_UNUSED( featurePools )
@ -175,3 +187,26 @@ double QgsGeometryCheck::scaleFactor( const QPointer<QgsVectorLayer> &layer ) co
}
return scaleFactor;
}
QgsGeometryCheck::Result QgsGeometryCheck::checkUniqueId( const QgsGeometryCheckerUtils::LayerFeature layerFeature, QMap< QString, QSet<QVariant> > &uniqueIds ) const
{
const int uniqueIdFieldIndex = mContext->uniqueIdFieldIndex;
if ( uniqueIdFieldIndex == -1 )
{
return QgsGeometryCheck::Result::Success;
}
auto uniqueIdIt = uniqueIds.find( layerFeature.layerId() );
if ( uniqueIdIt == uniqueIds.end() )
{
uniqueIdIt = uniqueIds.insert( layerFeature.layerId(), QSet<QVariant>() );
}
const QVariant uniqueIdValue = layerFeature.feature().attribute( uniqueIdFieldIndex );
if ( uniqueIdIt.value().contains( uniqueIdValue ) )
{
return QgsGeometryCheck::Result::DuplicatedUniqueId;
}
uniqueIdIt.value().insert( uniqueIdValue );
return QgsGeometryCheck::Result::Success;
}

View File

@ -157,6 +157,16 @@ class ANALYSIS_EXPORT QgsGeometryCheck
LayerCheck //!< The check controls a whole layer (topology checks)
};
//! Result of the geometry checker operation \since QGIS 4.0
enum class Result : int
{
Success = 0, //!< Operation completed successfully
Canceled = 1, //!< User canceled calculation
DuplicatedUniqueId = 2, //!< Found duplicated unique ID value
InvalidReferenceLayer = 3, //!< Missed or invalid reference layer
GeometryOverlayError = 4 //!< Error performing geometry overlay operation
};
/**
* Flags for geometry checks.
*/
@ -269,15 +279,17 @@ class ANALYSIS_EXPORT QgsGeometryCheck
* Check all features available from \a featurePools and write errors found to \a errors.
* Other status messages can be written to \a messages.
* Progress should be reported to \a feedback. Only features and layers listed in \a ids should be checked.
* \returns QgsGeometryCheck::Result::Success in case of success or error value on failure.
*
* \since QGIS 3.4
*/
virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors SIP_INOUT, QStringList &messages SIP_INOUT, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0;
virtual Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors SIP_INOUT, QStringList &messages SIP_INOUT, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const;
/**
* Fixes the error \a error with the specified \a method.
* Is executed on the main thread.
*
*
* \see availableResolutionMethods()
* \since QGIS 3.4
*/
@ -370,6 +382,14 @@ class ANALYSIS_EXPORT QgsGeometryCheck
* \since QGIS 3.4
*/
double scaleFactor( const QPointer<QgsVectorLayer> &layer ) const SIP_SKIP;
/**
* Checks that there are no duplicated unique IDs
* \returns QgsGeometryCheck::Result::Success in case if there are no duplicates or error value on failure.
*
* \since QGIS 4.0
*/
Result checkUniqueId( const QgsGeometryCheckerUtils::LayerFeature layerFeature, QMap< QString, QSet<QVariant> > &uniqueIds ) const SIP_SKIP;
};
#endif // QGS_GEOMETRY_CHECK_H

View File

@ -16,11 +16,12 @@
#include "qgsgeometrycheckcontext.h"
#include <QThread>
QgsGeometryCheckContext::QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *project )
QgsGeometryCheckContext::QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *project, const int uniqueIdFieldIndex )
: tolerance( std::pow( 10, -precision ) )
, reducedTolerance( std::pow( 10, -precision / 2 ) )
, mapCrs( mapCrs )
, transformContext( transformContext )
, uniqueIdFieldIndex( uniqueIdFieldIndex )
, mProject( project )
{
}

View File

@ -33,8 +33,15 @@ class ANALYSIS_EXPORT QgsGeometryCheckContext
public:
/**
* Creates a new QgsGeometryCheckContext.
* \param precision The precision used to define gemetry check tolerance. Tolerance is calculated as pow(10, -precision)
* \param mapCrs The coordinate system in which calculations should be done
* \param transformContext The coordinate transform context
* \param mProject The project used to resolve additional layers
* \param uniqueIdFieldIndex The index of the unique ID field used to identify features. If set to valid field index, geometry checker will fail if
* this field is not unique (since QGIS 4.0)
*
*/
QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *mProject );
QgsGeometryCheckContext( int precision, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &transformContext, const QgsProject *mProject = nullptr, const int uniqueIdFieldIndex = -1 );
/**
* The tolerance to allow for in geometry checks.
@ -61,6 +68,13 @@ class ANALYSIS_EXPORT QgsGeometryCheckContext
*/
const QgsCoordinateTransformContext transformContext;
/**
* The index of the unique ID field used to identify features.
*
* \since QGIS 4.0
*/
const int uniqueIdFieldIndex;
/**
* The project can be used to resolve additional layers.
*

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryengine.h"
#include "qgsgeometrycontainedcheck.h"
@ -20,12 +21,27 @@
#include "qgsvectorlayer.h"
void QgsGeometryContainedCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryContainedCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeatureA, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsRectangle bboxA = layerFeatureA.geometry().boundingBox();
std::unique_ptr<QgsGeometryEngine> geomEngineA( QgsGeometry::createGeometryEngine( layerFeatureA.geometry().constGet(), mContext->tolerance ) );
if ( !geomEngineA->isValid() )
@ -36,6 +52,11 @@ void QgsGeometryContainedCheck::collectErrors( const QMap<QString, QgsFeaturePoo
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, featureIds.keys(), bboxA, compatibleGeometryTypes(), mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( layerFeatureA == layerFeatureB )
{
continue;
@ -58,6 +79,7 @@ void QgsGeometryContainedCheck::collectErrors( const QMap<QString, QgsFeaturePoo
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryContainedCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -46,7 +46,8 @@ class ANALYSIS_EXPORT QgsGeometryContainedCheckError : public QgsGeometryCheckEr
/**
* \ingroup analysis
* \brief A contained check.
* \brief A contained check. QMap<QString, QSet<QVariant>> uniqueIds;
const QString uniqueIdFieldName = context()->uniqueIdFieldName;
*/
class ANALYSIS_EXPORT QgsGeometryContainedCheck : public QgsGeometryCheck
{
@ -61,7 +62,7 @@ class ANALYSIS_EXPORT QgsGeometryContainedCheck : public QgsGeometryCheck
explicit QgsGeometryContainedCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( context, configuration ) {}
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
QString id() const override { return factoryId(); }

View File

@ -13,19 +13,36 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrydanglecheck.h"
#include "qgslinestring.h"
#include "qgsvectorlayer.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryDangleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryDangleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -53,6 +70,11 @@ void QgsGeometryDangleCheck::collectErrors( const QMap<QString, QgsFeaturePool *
const QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, QList<QString>() << layerFeature.layer()->id(), line->boundingBox(), { Qgis::GeometryType::Line }, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet();
for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart )
{
@ -92,6 +114,7 @@ void QgsGeometryDangleCheck::collectErrors( const QMap<QString, QgsFeaturePool *
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryDangleCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

View File

@ -34,7 +34,7 @@ class ANALYSIS_EXPORT QgsGeometryDangleCheck : public QgsGeometryCheck
void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
QString description() const override { return factoryDescription(); }
QString id() const override { return factoryId(); }

View File

@ -13,20 +13,36 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrydegeneratepolygoncheck.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryDegeneratePolygonCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -40,6 +56,7 @@ void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMap<QString, QgsFe
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryDegeneratePolygonCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -37,7 +37,7 @@ class ANALYSIS_EXPORT QgsGeometryDegeneratePolygonCheck : public QgsGeometryChec
explicit QgsGeometryDegeneratePolygonCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( context, configuration ) {}
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryengine.h"
#include "qgsgeometryduplicatecheck.h"
@ -39,13 +40,28 @@ QString QgsGeometryDuplicateCheckError::duplicatesString( const QMap<QString, Qg
}
void QgsGeometryDuplicateCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryDuplicateCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QSet<QVariant>> uniqueIds;
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 )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeatureA, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
// Ensure each pair of layers only gets compared once: remove the current layer from the layerIds, but add it to the layerList for layerFeaturesB
layerIds.removeOne( layerFeatureA.layer()->id() );
@ -63,6 +79,11 @@ void QgsGeometryDuplicateCheck::collectErrors( const QMap<QString, QgsFeaturePoo
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList<QString>() << layerFeatureA.layer()->id() << layerIds, bboxA, { geomType }, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
// only report overlaps within same layer once
if ( layerFeatureA.layer()->id() == layerFeatureB.layer()->id() && layerFeatureB.feature().id() >= layerFeatureA.feature().id() )
{
@ -86,6 +107,7 @@ void QgsGeometryDuplicateCheck::collectErrors( const QMap<QString, QgsFeaturePoo
errors.append( new QgsGeometryDuplicateCheckError( this, layerFeatureA, geomA.constGet()->centroid(), featurePools, duplicates ) );
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryDuplicateCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -62,7 +62,7 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateCheck : public QgsGeometryCheck
public:
explicit QgsGeometryDuplicateCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration )
: QgsGeometryCheck( context, configuration ) {}
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }

View File

@ -13,22 +13,37 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryduplicatenodescheck.h"
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryDuplicateNodesCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
const double sqrTolerance = mContext->tolerance * mContext->tolerance;
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -49,6 +64,7 @@ void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap<QString, QgsFeatu
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryDuplicateNodesCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -33,7 +33,7 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateNodesCheck : public QgsGeometryCheck
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Line, Qgis::GeometryType::Polygon }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Duplicate node" ); }

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryfollowboundariescheck.h"
#include "qgsgeometryengine.h"
@ -36,20 +37,35 @@ QgsGeometryFollowBoundariesCheck::~QgsGeometryFollowBoundariesCheck()
delete mIndex;
}
void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result 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;
return QgsGeometryCheck::Result::InvalidReferenceLayer;
}
QMap<QString, QSet<QVariant>> uniqueIds;
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
featureIds.remove( mCheckLayer->id() ); // Don't check layer against itself
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
// The geometry to crs of the check layer
@ -78,6 +94,11 @@ void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap<QString, QgsFea
QgsFeature refFeature;
while ( refFeatureIt.nextFeature( refFeature ) )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
const QgsAbstractGeometry *refGeom = refFeature.geometry().constGet();
std::unique_ptr<QgsGeometryEngine> refgeomEngine( QgsGeometry::createGeometryEngine( refGeom, mContext->tolerance ) );
const QgsGeometry reducedRefGeom( refgeomEngine->buffer( -mContext->tolerance, 0 ) );
@ -89,6 +110,7 @@ void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap<QString, QgsFea
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryFollowBoundariesCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

View File

@ -36,7 +36,7 @@ class ANALYSIS_EXPORT QgsGeometryFollowBoundariesCheck : public QgsGeometryCheck
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Polygon }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Polygon does not follow boundaries" ); }

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryengine.h"
#include "qgsgeometrygapcheck.h"
@ -52,7 +53,7 @@ void QgsGeometryGapCheck::prepare( const QgsGeometryCheckContext *context, const
}
}
void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result 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 );
@ -70,6 +71,11 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
while ( iterator.nextFeature( feature ) )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
const QgsGeometry geom = feature.geometry();
const QgsGeometry gg = geom.buffer( mAllowedGapsBuffer, 20 );
allowedGaps.append( gg );
@ -85,22 +91,26 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
}
QVector<QgsGeometry> geomList;
QMap<QString, QSet<QVariant>> uniqueIds;
const 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() );
if ( feedback && feedback->isCanceled() )
{
geomList.clear();
break;
return QgsGeometryCheck::Result::Canceled;
}
}
if ( geomList.isEmpty() )
{
return;
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
geomList.append( layerFeature.geometry() );
}
std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( nullptr, mContext->tolerance ) );
@ -111,7 +121,7 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
if ( !unionGeom )
{
messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
return;
return QgsGeometryCheck::Result::GeometryOverlayError;
}
// Get envelope of union
@ -121,7 +131,7 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
if ( !envelope )
{
messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
return;
return QgsGeometryCheck::Result::GeometryOverlayError;
}
// Buffer envelope
@ -137,13 +147,18 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
if ( !diffGeom )
{
messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
return;
return QgsGeometryCheck::Result::GeometryOverlayError;
}
// For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
QgsGeometryPartIterator parts = diffGeom->parts();
while ( parts.hasNext() )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
const QgsAbstractGeometry *gapGeom = parts.next();
// Skip the gap between features and boundingbox
const double spacing = context()->tolerance;
@ -167,6 +182,11 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
gapGeomEngine->prepareGeometry();
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
const QgsGeometry geom = layerFeature.geometry();
if ( gapGeomEngine->distance( geom.constGet() ) < mContext->tolerance )
{
@ -190,6 +210,7 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
const QgsRectangle gapBbox = gapGeom->boundingBox();
errors.append( new QgsGeometryGapCheckError( this, QString(), QgsGeometry( gapGeom->clone() ), neighboringIds, area, gapBbox, gapAreaBBox ) );
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryGapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
@ -255,8 +276,7 @@ void QgsGeometryGapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featu
case CreateNewFeature:
{
QgsGeometryGapCheckError *gapCheckError = static_cast<QgsGeometryGapCheckError *>( error );
QgsProject *project = QgsProject::instance();
QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( project->mapLayer( gapCheckError->neighbors().keys().first() ) );
QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( context()->project()->mapLayer( gapCheckError->neighbors().keys().first() ) );
if ( layer )
{
const QgsGeometry geometry = error->geometry();

View File

@ -107,7 +107,7 @@ class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck
void prepare( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) override;
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometryholecheck.h"
#include "qgscurve.h"
@ -20,14 +21,29 @@
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryHoleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryHoleCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -44,6 +60,7 @@ void QgsGeometryHoleCheck::collectErrors( const QMap<QString, QgsFeaturePool *>
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryHoleCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -33,7 +33,7 @@ class ANALYSIS_EXPORT QgsGeometryHoleCheck : public QgsGeometryCheck
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Polygon }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Polygon with hole" ); }

View File

@ -13,21 +13,37 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrylineintersectioncheck.h"
#include "qgslinestring.h"
#include "qgsvectorlayer.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryLineIntersectionCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryLineIntersectionCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
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 )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeatureA, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
// Ensure each pair of layers only gets compared once: remove the current layer from the layerIds, but add it to the layerList for layerFeaturesB
layerIds.removeOne( layerFeatureA.layer()->id() );
@ -45,7 +61,12 @@ void QgsGeometryLineIntersectionCheck::collectErrors( const QMap<QString, QgsFea
const QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( featurePools, QList<QString>() << layerFeatureA.layer()->id() << layerIds, line->boundingBox(), { Qgis::GeometryType::Line }, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB )
{
// > : only report intersections within same layer once
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
// only report intersections within same layer once
if ( layerFeatureA.layer()->id() == layerFeatureB.layer()->id() && layerFeatureB.feature().id() > layerFeatureA.feature().id() )
{
continue;
@ -73,6 +94,7 @@ void QgsGeometryLineIntersectionCheck::collectErrors( const QMap<QString, QgsFea
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryLineIntersectionCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

View File

@ -34,7 +34,7 @@ class ANALYSIS_EXPORT QgsGeometryLineIntersectionCheck : public QgsGeometryCheck
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Line }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Intersection" ); }

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrylinelayerintersectioncheck.h"
#include "qgspolygon.h"
@ -20,15 +21,29 @@
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
featureIds.remove( mCheckLayer ); // Don't check layer against itself
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -43,6 +58,11 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap<QString, Q
const QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, QStringList() << mCheckLayer, line->boundingBox(), { Qgis::GeometryType::Line, Qgis::GeometryType::Polygon }, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet();
for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart )
{
@ -71,6 +91,7 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap<QString, Q
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryLineLayerIntersectionCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

View File

@ -35,7 +35,7 @@ class ANALYSIS_EXPORT QgsGeometryLineLayerIntersectionCheck : public QgsGeometry
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Line }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Intersection" ); }

View File

@ -30,23 +30,30 @@ QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryC
{}
void QgsGeometryMissingVertexCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result 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, QSet<QVariant>> uniqueIds;
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
QgsFeaturePool *featurePool = featurePools.value( featureIds.firstKey() );
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
break;
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsGeometry geometry = layerFeature.geometry();
@ -68,6 +75,7 @@ void QgsGeometryMissingVertexCheck::collectErrors( const QMap<QString, QgsFeatur
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryMissingVertexCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -98,7 +98,7 @@ class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck
* Creates a new missing vertex geometry check with \a context and the provided \a geometryCheckConfiguration.
*/
explicit QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration );
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;

View File

@ -28,15 +28,26 @@ QgsGeometryOverlapCheck::QgsGeometryOverlapCheck( const QgsGeometryCheckContext
{
}
void QgsGeometryOverlapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryOverlapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QSet<QVariant>> uniqueIds;
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 )
{
if ( feedback && feedback->isCanceled() )
break;
return QgsGeometryCheck::Result::Canceled;
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeatureA, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
// Ensure each pair of layers only gets compared once: remove the current layer from the layerIds, but add it to the layerList for layerFeaturesB
layerIds.removeOne( layerFeatureA.layer()->id() );
@ -55,9 +66,9 @@ void QgsGeometryOverlapCheck::collectErrors( const QMap<QString, QgsFeaturePool
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB )
{
if ( feedback && feedback->isCanceled() )
break;
return QgsGeometryCheck::Result::Canceled;
// > : only report overlaps within same layer once
// only report overlaps within same layer once
if ( layerFeatureA.layerId() == layerFeatureB.layerId() && layerFeatureB.feature().id() >= layerFeatureA.feature().id() )
{
continue;
@ -89,6 +100,7 @@ void QgsGeometryOverlapCheck::collectErrors( const QMap<QString, QgsFeaturePool
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryOverlapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const

View File

@ -107,7 +107,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck
*/
QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration );
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;

View File

@ -13,20 +13,35 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrypointcoveredbylinecheck.h"
#include "qgslinestring.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -42,6 +57,11 @@ void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap<QString, QgsF
const QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, featureIds.keys(), rect, { Qgis::GeometryType::Line }, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet();
for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart )
{
@ -68,6 +88,7 @@ void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap<QString, QgsF
errors.append( new QgsGeometryCheckError( this, layerFeature, *point, QgsVertexId( iPart, 0, 0 ) ) );
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryPointCoveredByLineCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

View File

@ -34,7 +34,7 @@ class ANALYSIS_EXPORT QgsGeometryPointCoveredByLineCheck : public QgsGeometryChe
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Point }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Point not covered by line" ); }

View File

@ -13,17 +13,32 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrypointinpolygoncheck.h"
#include "qgsgeometryengine.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometryPointInPolygonCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometryPointInPolygonCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
@ -41,6 +56,11 @@ void QgsGeometryPointInPolygonCheck::collectErrors( const QMap<QString, QgsFeatu
const QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, featureIds.keys(), rect, { Qgis::GeometryType::Polygon }, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
++nTested;
const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet();
std::unique_ptr<QgsGeometryEngine> testGeomEngine( QgsGeometry::createGeometryEngine( testGeom, mContext->reducedTolerance ) );
@ -60,6 +80,7 @@ void QgsGeometryPointInPolygonCheck::collectErrors( const QMap<QString, QgsFeatu
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometryPointInPolygonCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

View File

@ -34,7 +34,7 @@ class ANALYSIS_EXPORT QgsGeometryPointInPolygonCheck : public QgsGeometryCheck
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Point }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Point not in polygon" ); }

View File

@ -13,20 +13,36 @@
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgsgeometrycheckcontext.h"
#include "qgsgeometrysegmentlengthcheck.h"
#include "qgsgeometryutils.h"
#include "qgsfeaturepool.h"
#include "qgsgeometrycheckerror.h"
void QgsGeometrySegmentLengthCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsGeometrySegmentLengthCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const double layerToMapUnits = scaleFactor( layerFeature.layer() );
const double minLength = mMinLengthMapUnits / layerToMapUnits;
@ -56,6 +72,7 @@ void QgsGeometrySegmentLengthCheck::collectErrors( const QMap<QString, QgsFeatur
}
}
}
return QgsGeometryCheck::Result::Success;
}
void QgsGeometrySegmentLengthCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const

View File

@ -35,7 +35,7 @@ class ANALYSIS_EXPORT QgsGeometrySegmentLengthCheck : public QgsGeometryCheck
static QList<Qgis::GeometryType> factoryCompatibleGeometryTypes() { return { Qgis::GeometryType::Line, Qgis::GeometryType::Polygon }; }
static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); }
QList<Qgis::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override;
QgsGeometryCheck::Result collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, 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;
Q_DECL_DEPRECATED QStringList resolutionMethods() const override;
static QString factoryDescription() { return tr( "Minimal segment length" ); }

View File

@ -13,22 +13,39 @@ email : matthias@opengis.ch
* *
***************************************************************************/
#include "qgsfeedback.h"
#include "qgssinglegeometrycheck.h"
#include "qgsgeometrycheckcontext.h"
#include "qgspoint.h"
void QgsSingleGeometryCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
QgsGeometryCheck::Result QgsSingleGeometryCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
{
Q_UNUSED( messages )
QMap<QString, QSet<QVariant>> uniqueIds;
const QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
{
if ( feedback && feedback->isCanceled() )
{
return QgsGeometryCheck::Result::Canceled;
}
if ( context()->uniqueIdFieldIndex != -1 )
{
QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
if ( result != QgsGeometryCheck::Result::Success )
{
return result;
}
}
const auto singleErrors = processGeometry( layerFeature.geometry() );
for ( const auto error : singleErrors )
errors.append( convertToGeometryCheckError( error, layerFeature ) );
}
return QgsGeometryCheck::Result::Success;
}
QgsGeometryCheckErrorSingle *QgsSingleGeometryCheck::convertToGeometryCheckError( QgsSingleGeometryCheckError *singleGeometryCheckError, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const

View File

@ -155,7 +155,7 @@ class ANALYSIS_EXPORT QgsSingleGeometryCheck : public QgsGeometryCheck
: QgsGeometryCheck( context, configuration )
{}
void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const QgsGeometryCheck::LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const FINAL;
QgsGeometryCheck::Result 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.

View File

@ -401,6 +401,7 @@ set(QGIS_CORE_SRCS
tiledscene/qgscesiumtilesdataprovider.cpp
tiledscene/qgscesiumutils.cpp
tiledscene/qgsesrii3sdataprovider.cpp
tiledscene/qgsgltfutils.cpp
tiledscene/qgsquantizedmeshdataprovider.cpp
tiledscene/qgsquantizedmeshtiles.cpp
@ -2079,6 +2080,7 @@ set(QGIS_CORE_HDRS
tiledscene/qgscesiumtilesdataprovider.h
tiledscene/qgscesiumutils.h
tiledscene/qgsesrii3sdataprovider.h
tiledscene/qgsgltfutils.h
tiledscene/qgsquantizedmeshdataprovider.h
tiledscene/qgsquantizedmeshtiles.h

View File

@ -41,6 +41,7 @@
#include "qgsvtpkvectortiledataprovider.h"
#include "qgscesiumtilesdataprovider.h"
#include "qgsesrii3sdataprovider.h"
#include "qgstiledsceneprovidermetadata.h"
#ifdef HAVE_EPT
@ -245,6 +246,9 @@ void QgsProviderRegistry::init()
metadata = new QgsQuantizedMeshProviderMetadata();
mProviders[ metadata->key() ] = metadata;
metadata = new QgsEsriI3SProviderMetadata();
mProviders[ metadata->key() ] = metadata;
}
#ifdef HAVE_STATIC_PROVIDERS

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
/***************************************************************************
qgsesrii3sdataprovider.h
--------------------------------------
Date : July 2025
Copyright : (C) 2025 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* 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 QGSESRII3SDATAPROVIDER_H
#define QGSESRII3SDATAPROVIDER_H
#include "qgis_core.h"
#include "qgstiledscenedataprovider.h"
#include "qgis.h"
#include "qgsprovidermetadata.h"
#define SIP_NO_FILE
class QgsEsriI3SDataProviderSharedData;
///@cond PRIVATE
/**
* \ingroup core
* Data provider implementation for Esri I3S
* \since QGIS 4.0
*/
class CORE_EXPORT QgsEsriI3SDataProvider final: public QgsTiledSceneDataProvider
{
Q_OBJECT
public:
//! Constructor for QgsEsriI3SDataProvider
QgsEsriI3SDataProvider( const QString &uri,
const QgsDataProvider::ProviderOptions &providerOptions,
Qgis::DataProviderReadFlags flags = Qgis::DataProviderReadFlags() );
QgsEsriI3SDataProvider( const QgsEsriI3SDataProvider &other );
QgsEsriI3SDataProvider &operator=( const QgsEsriI3SDataProvider &other ) = delete;
~QgsEsriI3SDataProvider() final;
Qgis::DataProviderFlags flags() const override;
Qgis::TiledSceneProviderCapabilities capabilities() const final;
QgsEsriI3SDataProvider *clone() const final;
QgsCoordinateReferenceSystem crs() const final;
QgsRectangle extent() const final;
bool isValid() const final;
QString name() const final;
QString description() const final;
QString htmlMetadata() const final;
const QgsCoordinateReferenceSystem sceneCrs() const final;
const QgsTiledSceneBoundingVolume &boundingVolume() const final;
QgsTiledSceneIndex index() const final;
QgsDoubleRange zRange() const final;
private:
bool loadFromRestService( const QString &uri, json &layerJson, QString &i3sVersion );
bool loadFromSlpk( const QString &uri, json &layerJson, QString &i3sVersion );
bool checkI3SVersion( const QString &i3sVersion );
bool mIsValid = false;
std::shared_ptr<QgsEsriI3SDataProviderSharedData> mShared; //!< Mutable data shared between provider instances
};
/**
* \ingroup core
* Data provider metadata implementation for Esri I3S
* \since QGIS 4.0
*/
class QgsEsriI3SProviderMetadata : public QgsProviderMetadata
{
Q_OBJECT
public:
QgsEsriI3SProviderMetadata();
QIcon icon() const override;
QgsProviderMetadata::ProviderMetadataCapabilities capabilities() const override;
QgsEsriI3SDataProvider *createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags = Qgis::DataProviderReadFlags() ) override;
QString filters( Qgis::FileFilterType type ) override;
ProviderCapabilities providerCapabilities() const override;
QList< Qgis::LayerType > supportedLayerTypes() const override;
};
///@endcond
#endif // QGSESRII3SDATAPROVIDER_H

View File

@ -19,6 +19,8 @@
#include "qgsmatrix4x4.h"
#include "qgsconfig.h"
#include "qgslogger.h"
#include "qgstiledscenetile.h"
#include "qgsziputils.h"
#include <QImage>
#include <QMatrix4x4>
@ -430,13 +432,32 @@ void dumpDracoModelInfo( draco::Mesh *dracoMesh )
bool QgsGltfUtils::loadDracoModel( const QByteArray &data, const I3SNodeContext &context, tinygltf::Model &model, QString *errors )
{
//
// SLPK and Extracted SLPK have the files gzipped
//
QByteArray dataExtracted;
if ( data.startsWith( QByteArray( "\x1f\x8b", 2 ) ) )
{
if ( !QgsZipUtils::decodeGzip( data, dataExtracted ) )
{
if ( errors )
*errors = "Failed to decode gzipped model";
return false;
}
}
else
{
dataExtracted = data;
}
//
// load the model in decoder and do basic sanity checks
//
draco::Decoder decoder;
draco::DecoderBuffer decoderBuffer;
decoderBuffer.Init( data.constData(), data.size() );
decoderBuffer.Init( dataExtracted.constData(), dataExtracted.size() );
draco::StatusOr<draco::EncodedGeometryType> geometryTypeStatus = decoder.GetEncodedGeometryType( &decoderBuffer );
if ( !geometryTypeStatus.ok() )
@ -790,4 +811,17 @@ bool QgsGltfUtils::writeGltfModel( const tinygltf::Model &model, const QString &
return res;
}
void QgsGltfUtils::I3SNodeContext::initFromTile( const QgsTiledSceneTile &tile, const QgsCoordinateReferenceSystem &layerCrs, const QgsCoordinateReferenceSystem &sceneCrs, const QgsCoordinateTransformContext &transformContext )
{
const QVariantMap tileMetadata = tile.metadata();
materialInfo = tileMetadata[QStringLiteral( "material" )].toMap();
isGlobalMode = sceneCrs.type() == Qgis::CrsType::Geocentric;
if ( isGlobalMode )
{
nodeCenterEcef = tile.boundingVolume().box().center();
datasetToSceneTransform = QgsCoordinateTransform( layerCrs, sceneCrs, transformContext );
}
}
///@endcond

View File

@ -41,6 +41,7 @@ class QMatrix4x4;
class QImage;
class QgsMatrix4x4;
class QgsTiledSceneTile;
class QgsVector3D;
namespace tinygltf
@ -173,8 +174,13 @@ class CORE_EXPORT QgsGltfUtils
* geometry of a I3S node.
* \since QGIS 4.0
*/
struct I3SNodeContext
struct CORE_EXPORT I3SNodeContext
{
//! Initialize the node content from tile's info
void initFromTile( const QgsTiledSceneTile &tile,
const QgsCoordinateReferenceSystem &layerCrs,
const QgsCoordinateReferenceSystem &sceneCrs,
const QgsCoordinateTransformContext &transformContext );
/**
* Material parsed from I3S material definition of the node. See

View File

@ -59,6 +59,7 @@ QgsTiledSceneLayerRenderer::QgsTiledSceneLayerRenderer( QgsTiledSceneLayer *laye
mRenderer.reset( layer->renderer()->clone() );
mSceneCrs = layer->dataProvider()->sceneCrs();
mLayerCrs = layer->dataProvider()->crs();
mClippingRegions = QgsMapClippingUtils::collectClippingRegionsForLayer( *renderContext(), layer );
mLayerBoundingVolume = layer->dataProvider()->boundingVolume();
@ -438,6 +439,21 @@ bool QgsTiledSceneLayerRenderer::renderTileContent( const QgsTiledSceneTile &til
}
if ( !res ) return false;
}
else if ( format == QLatin1String( "draco" ) )
{
QgsGltfUtils::I3SNodeContext i3sContext;
i3sContext.initFromTile( tile, mLayerCrs, mSceneCrs, context.renderContext().transformContext() );
QString errors;
if ( !QgsGltfUtils::loadDracoModel( tileContent, i3sContext, model, &errors ) )
{
if ( !mErrors.contains( errors ) )
mErrors.append( errors );
QgsDebugError( QStringLiteral( "Error raised reading %1: %2" )
.arg( contentUri, errors ) );
return false;
}
}
else
return false;

View File

@ -117,6 +117,7 @@ class CORE_EXPORT QgsTiledSceneLayerRenderer: public QgsMapLayerRenderer
QList< QgsMapClippingRegion > mClippingRegions;
QgsCoordinateReferenceSystem mSceneCrs;
QgsCoordinateReferenceSystem mLayerCrs;
QgsTiledSceneBoundingVolume mLayerBoundingVolume;
QgsTiledSceneIndex mIndex;

View File

@ -149,7 +149,7 @@ QVector<QgsDataItem *> QgsMssqlConnectionItem::createChildren()
}
// build sql statement
QString query = QgsMssqlConnection::buildQueryForTables( mName );
const QString query = QgsMssqlConnection::buildQueryForTables( mName );
const bool disableInvalidGeometryHandling = QgsMssqlConnection::isInvalidGeometryHandlingDisabled( mName );
@ -477,19 +477,16 @@ QgsMssqlLayerItem *QgsMssqlSchemaItem::addLayer( const QgsMssqlLayerProperty &la
QString tip = tr( "%1 as %2 in %3" ).arg( layerProperty.geometryColName, QgsWkbTypes::displayString( wkbType ), layerProperty.srid );
Qgis::BrowserLayerType layerType;
Qgis::WkbType flatType = QgsWkbTypes::flatType( wkbType );
switch ( flatType )
const Qgis::GeometryType geomType = QgsWkbTypes::geometryType( wkbType );
switch ( geomType )
{
case Qgis::WkbType::Point:
case Qgis::WkbType::MultiPoint:
case Qgis::GeometryType::Point:
layerType = Qgis::BrowserLayerType::Point;
break;
case Qgis::WkbType::LineString:
case Qgis::WkbType::MultiLineString:
case Qgis::GeometryType::Line:
layerType = Qgis::BrowserLayerType::Line;
break;
case Qgis::WkbType::Polygon:
case Qgis::WkbType::MultiPolygon:
case Qgis::GeometryType::Polygon:
layerType = Qgis::BrowserLayerType::Polygon;
break;
default:

View File

@ -20,6 +20,24 @@
#include "qgstest.h"
#include "qgsvectorlayer.h"
class DummyFeedback : public QgsProcessingFeedback
{
Q_OBJECT
public:
void reportError( const QString &error, bool fatalError = false )
{
Q_UNUSED( fatalError );
mErrors.append( error );
};
QStringList errors() { return mErrors; };
void clear() { mErrors.clear(); };
private:
QStringList mErrors;
};
class TestQgsProcessingCheckGeometry : public QgsTest
{
Q_OBJECT
@ -79,6 +97,8 @@ class TestQgsProcessingCheckGeometry : public QgsTest
void holeAlg();
void missingVertexAlg();
void duplicatedId();
private:
QgsVectorLayer *mLineLayer = nullptr;
QgsVectorLayer *mPolygonLayer = nullptr;
@ -649,6 +669,7 @@ void TestQgsProcessingCheckGeometry::gapAlg()
bool ok = false;
QgsProcessingFeedback feedback;
auto context = std::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
@ -689,6 +710,7 @@ void TestQgsProcessingCheckGeometry::gapAlg()
bool ok = false;
QgsProcessingFeedback feedback;
auto context = std::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
@ -857,5 +879,324 @@ void TestQgsProcessingCheckGeometry::multipartAlg()
QCOMPARE( errorsLayer->featureCount(), expectedErrorCount );
}
void TestQgsProcessingCheckGeometry::duplicatedId()
{
auto polygonLayer = std::make_unique<QgsVectorLayer>( QStringLiteral( "Polygon?crs=epsg:4326&field=pk:int&field=fid:string" ), QStringLiteral( "poly" ), QStringLiteral( "memory" ) );
QVERIFY( polygonLayer->isValid() );
QgsFeature f;
f.setAttributes( QgsAttributes() << 1 << QLatin1String( "1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "MultiPolygon (((-0.439 0.995, 0.24 1.079, 0.112 1.031, 0.336 0.576, -0.285 0.573, -0.439 0.995)))" ) ) );
polygonLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << 2 << QLatin1String( "2" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "MultiPolygon (((-0.439 0.995, 0.24 1.079, 0.112 1.031, 0.336 0.576, -0.285 0.573, -0.439 0.995)))" ) ) );
polygonLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << 3 << QLatin1String( "1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "MultiPolygon (((-0.439 0.995, 0.24 1.079, 0.112 1.031, 0.336 0.576, -0.285 0.573, -0.439 0.995)))" ) ) );
polygonLayer->dataProvider()->addFeature( f );
QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << polygonLayer.get() );
auto lineLayer = std::make_unique<QgsVectorLayer>( QStringLiteral( "LineString?crs=epsg:4326&field=pk:int&field=fid:string" ), QStringLiteral( "line" ), QStringLiteral( "memory" ) );
QVERIFY( lineLayer->isValid() );
f.setAttributes( QgsAttributes() << 1 << QLatin1String( "1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 15)" ) ) );
lineLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << 2 << QLatin1String( "2" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (4 2, 0 20)" ) ) );
lineLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << 3 << QLatin1String( "1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (1 1, 5 3)" ) ) );
lineLayer->dataProvider()->addFeature( f );
QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << lineLayer.get() );
auto pointLayer = std::make_unique<QgsVectorLayer>( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=fid:string" ), QStringLiteral( "point" ), QStringLiteral( "memory" ) );
QVERIFY( pointLayer->isValid() );
f.setAttributes( QgsAttributes() << 1 << QLatin1String( "1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Point (1 1)" ) ) );
pointLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << 2 << QLatin1String( "2" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Point (0.5 0.5)" ) ) );
pointLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << 3 << QLatin1String( "1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Point (1 1)" ) ) );
pointLayer->dataProvider()->addFeature( f );
QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << pointLayer.get() );
std::unique_ptr<QgsProcessingAlgorithm> alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryangle" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), QStringLiteral( "fid" ) );
parameters.insert( QStringLiteral( "MIN_ANGLE" ), 15 );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
DummyFeedback feedback;
const std::unique_ptr<QgsProcessingContext> context = std::make_unique<QgsProcessingContext>();
context->setProject( QgsProject::instance() );
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryarea" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "AREATHRESHOLD" ), 0.04 );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryhole" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrymissingvertex" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrycontained" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "POLYGONS" ), QList<QVariant>() << QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), QStringLiteral( "fid" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrydegeneratepolygon" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrysegmentlength" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "MIN_SEGMENT_LENGTH" ), 0.03 );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryselfintersection" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrydangle" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( lineLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryduplicatenodes" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryfollowboundaries" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "REF_LAYER" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryoverlap" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "MIN_OVERLAP_AREA" ), 0.01 );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometryselfcontact" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrysliverpolygon" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "MAX_AREA" ), 0.04 );
parameters.insert( QStringLiteral( "THRESHOLD" ), 20 );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrygap" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "GAP_THRESHOLD" ), 0.01 );
parameters.insert( QStringLiteral( "NEIGHBORS" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrypointinpolygon" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( pointLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "POLYGONS" ), QVariantList() << QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrypointcoveredbyline" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( pointLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "LINES" ), QVariantList() << QVariant::fromValue( lineLayer->id() ) );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrylinelayerintersection" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( lineLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "CHECK_LAYER" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrylineintersection" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( lineLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:checkgeometrymultipart" ) ) );
QVERIFY( alg != nullptr );
parameters.clear();
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( polygonLayer->id() ) );
parameters.insert( QStringLiteral( "UNIQUE_ID" ), "fid" );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ERRORS" ), QgsProcessing::TEMPORARY_OUTPUT );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( feedback.errors().contains( QStringLiteral( "Field 'fid' contains non-unique values and can not be used as unique ID." ) ) );
feedback.clear();
}
QGSTEST_MAIN( TestQgsProcessingCheckGeometry )
#include "testqgsprocessingcheckgeometry.moc"

View File

@ -507,6 +507,10 @@ void TestQgsProcessingFixGeometry::fixGapAlg()
QVERIFY( gapsLayer.isValid() );
QVERIFY( neighborsLayer.isValid() );
QgsProject::instance()->addMapLayer( &sourceLayer );
QgsProject::instance()->addMapLayer( &gapsLayer );
QgsProject::instance()->addMapLayer( &neighborsLayer );
QFETCH( QStringList, reportList );
QFETCH( int, method );
QFETCH( int, featureCount );
@ -528,6 +532,7 @@ void TestQgsProcessingFixGeometry::fixGapAlg()
bool ok = false;
QgsProcessingFeedback feedback;
auto context = std::make_unique<QgsProcessingContext>();
context->setProject( QgsProject::instance() );
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );

View File

@ -75,6 +75,7 @@ ADD_PYTHON_TEST(PyQgsElevationProfileManagerModel test_qgselevationprofilemanage
ADD_PYTHON_TEST(PyQgsElevationUtils test_qgselevationutils.py)
ADD_PYTHON_TEST(PyQgsEllipsoidUtils test_qgsellipsoidutils.py)
ADD_PYTHON_TEST(PyQgsEmbeddedSymbolRenderer test_qgsembeddedsymbolrenderer.py)
ADD_PYTHON_TEST(PyQgsEsriI3sLayer test_qgsesrii3slayer.py)
ADD_PYTHON_TEST(PyQgsExifTools test_qgsexiftools.py)
ADD_PYTHON_TEST(PyQgsExpression test_qgsexpression.py)
ADD_PYTHON_TEST(PyQgsExpressionPreviewWidget test_qgsexpressionpreviewwidget.py)

View File

@ -0,0 +1,756 @@
"""QGIS Unit tests for ESRI I3S tiled scene layer
.. note:: 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.
"""
__author__ = "Martin Dobias"
__date__ = "03/09/2025"
__copyright__ = "Copyright 2025, The QGIS Project"
import os
import tempfile
import gzip
from qgis.PyQt.QtCore import QUrl
from qgis.core import (
Qgis,
QgsTiledSceneLayer,
QgsCoordinateReferenceSystem,
QgsMatrix4x4,
QgsOrientedBox3D,
QgsTiledSceneRequest,
)
from qgis.testing import start_app, unittest
start_app()
def _make_tmp_eslpk_dataset(
temp_dir, layer_json_str, nodepage_json_str, i3s_version="1.8"
):
"""Creates files needed for a basic "Extracted SLPK" dataset"""
metadata_file = os.path.join(temp_dir, "metadata.json")
with open(metadata_file, "w", encoding="utf-8") as f:
f.write('{ "I3SVersion": "' + i3s_version + '" }')
layer_file = os.path.join(temp_dir, "3dSceneLayer.json.gz")
with gzip.open(layer_file, "wt", encoding="utf-8") as f:
f.write(layer_json_str)
nodepages_dir = os.path.join(temp_dir, "nodepages")
os.mkdir(nodepages_dir)
nodepage_0_file = os.path.join(nodepages_dir, "0.json.gz")
with gzip.open(nodepage_0_file, "wt", encoding="utf-8") as f:
f.write(nodepage_json_str)
class TestQgsEsriI3sLayer(unittest.TestCase):
def test_invalid_source(self):
layer = QgsTiledSceneLayer("file:///nope", "my layer", "esrii3s")
self.assertFalse(layer.dataProvider().isValid())
def test_invalid_json(self):
with tempfile.TemporaryDirectory() as temp_dir:
layer_json = """
{
"featurecollection": {}
}
"""
_make_tmp_eslpk_dataset(temp_dir, layer_json, "")
layer = QgsTiledSceneLayer("file://" + temp_dir, "my layer", "esrii3s")
self.assertFalse(layer.dataProvider().isValid())
self.assertEqual(
layer.error().summary(),
"Invalid I3S source: missing layer type.",
)
def test_old_i3s_version(self):
with tempfile.TemporaryDirectory() as temp_dir:
_make_tmp_eslpk_dataset(temp_dir, "", "", "1.6")
layer = QgsTiledSceneLayer("file://" + temp_dir, "my layer", "esrii3s")
self.assertFalse(layer.dataProvider().isValid())
self.assertEqual(
layer.error().summary(),
"Unsupported I3S version: 1.6",
)
def test_valid_global(self):
"""Test using a "global" dataset - i.e. using EPSG:4326"""
with tempfile.TemporaryDirectory(delete=False) as temp_dir:
layer_json = """
{
"id": 0,
"layerType": "IntegratedMesh",
"version": "1111-2222-3333",
"capabilities": ["View", "Query"],
"spatialReference": {
"wkid": 4326,
"latestWkid": 4326,
"vcsWkid": 3855,
"latestVcsWkid": 3855
},
"nodePages": {
"nodesPerPage": 64,
"lodSelectionMetricType": "maxScreenThresholdSQ"
}
}
"""
nodepage_json = """
{
"nodes": [
{
"index": 0,
"obb": {
"center": [-117.53759594675871, 34.12419117052764, 411.5930244093761],
"halfSize": [67.92003, 86.17007, 14.765519],
"quaternion": [0.206154981448711, 0.8528643536373323, 0.4652900171822784, 0.11673781661986028]
}
}
]
}
"""
_make_tmp_eslpk_dataset(temp_dir, layer_json, nodepage_json)
layer = QgsTiledSceneLayer("file://" + temp_dir, "my layer", "esrii3s")
self.assertTrue(layer.dataProvider().isValid())
self.assertEqual(layer.crs(), QgsCoordinateReferenceSystem("EPSG:4979"))
self.assertEqual(layer.dataProvider().sceneCrs().authid(), "EPSG:4978")
self.assertAlmostEqual(layer.extent().xMinimum(), -117.538339, 3)
self.assertAlmostEqual(layer.extent().xMaximum(), -117.536852, 3)
self.assertAlmostEqual(layer.extent().yMinimum(), 34.12340679, 3)
self.assertAlmostEqual(layer.extent().yMaximum(), 34.12497554, 3)
self.assertAlmostEqual(
layer.dataProvider().boundingVolume().box().centerX(),
-2443825.362862,
3,
)
self.assertAlmostEqual(
layer.dataProvider().boundingVolume().box().centerY(),
-4687033.280016,
3,
)
self.assertAlmostEqual(
layer.dataProvider().boundingVolume().box().centerZ(), 3558089.694926, 3
)
self.assertAlmostEqual(layer.dataProvider().zRange().lower(), 394.495, 3)
self.assertAlmostEqual(layer.dataProvider().zRange().upper(), 428.693, 3)
# check that version, tileset version, and z range are in html metadata
self.assertIn("1.8", layer.dataProvider().htmlMetadata())
self.assertIn("1111-2222-3333", layer.dataProvider().htmlMetadata())
self.assertIn("394.495 - 428.693", layer.dataProvider().htmlMetadata())
def test_valid_local(self):
"""Test using a "local" dataset - with projected CRS"""
with tempfile.TemporaryDirectory(delete=False) as temp_dir:
layer_json = """
{
"id": 0,
"layerType": "3DObject",
"version": "9FC7A46A-C550-4E1D-9001-DDCF825B5501",
"capabilities": ["View", "Query"],
"spatialReference": {
"wkid": 102067,
"latestWkid": 5514
},
"nodePages": {
"nodesPerPage": 64,
"lodSelectionMetricType": "maxScreenThresholdSQ"
},
"fullExtent": {
"xmin": -503064.4241950555,
"xmax": -490815.7221285819,
"ymin": -1209277.75240015844,
"ymax": -1199847.185454726,
"spatialReference": {
"wkid": 102067,
"latestWkid": 5514
},
"zmin": 197.438445862085445,
"zmax": 475.336097820065049
}
}
"""
nodepage_json = """
{
"nodes": [
{
"index": 0,
"obb" : {
"center": [-497670.6852373213, -1204678.5884475496, 295.70377745447672],
"halfSize": [6618.03271484375, 227.39961242675781, 4249.75927734375],
"quaternion": [0.12047340803502149, 0.70138504165507976, 0.6935764321960135, 0.11178959701679769]
}
}
]
}
"""
_make_tmp_eslpk_dataset(temp_dir, layer_json, nodepage_json)
layer = QgsTiledSceneLayer("file://" + temp_dir, "my layer", "esrii3s")
self.assertTrue(layer.dataProvider().isValid())
self.assertEqual(layer.crs(), QgsCoordinateReferenceSystem("EPSG:5514"))
self.assertEqual(layer.dataProvider().sceneCrs().authid(), "EPSG:5514")
self.assertAlmostEqual(layer.extent().xMinimum(), -503064.424195, 3)
self.assertAlmostEqual(layer.extent().xMaximum(), -490815.722128, 3)
self.assertAlmostEqual(layer.extent().yMinimum(), -1209277.752400, 3)
self.assertAlmostEqual(layer.extent().yMaximum(), -1199847.185454, 3)
self.assertAlmostEqual(
layer.dataProvider().boundingVolume().box().centerX(),
-497670.685237,
3,
)
self.assertAlmostEqual(
layer.dataProvider().boundingVolume().box().centerY(),
-1204678.588447,
3,
)
self.assertAlmostEqual(
layer.dataProvider().boundingVolume().box().centerZ(), 295.703777, 3
)
self.assertAlmostEqual(layer.dataProvider().zRange().lower(), 197.438445, 3)
self.assertAlmostEqual(layer.dataProvider().zRange().upper(), 475.336097, 3)
# check that version, tileset version, and z range are in html metadata
self.assertIn("1.8", layer.dataProvider().htmlMetadata())
self.assertIn(
"9FC7A46A-C550-4E1D-9001-DDCF825B5501",
layer.dataProvider().htmlMetadata(),
)
self.assertIn("197.438 - 475.336", layer.dataProvider().htmlMetadata())
def compare_boxes(self, box1: QgsOrientedBox3D, box2: QgsOrientedBox3D) -> bool:
"""
Compares two QgsOrientedBox3D objects within 4 decimal places
"""
fail_message = (
f"QgsOrientedBox3D([{box1.centerX():.4f}, {box1.centerY():.4f}, {box1.centerZ():.4f}], [{box1.halfAxes()[0]:.4f}, {box1.halfAxes()[1]:.4f},{box1.halfAxes()[2]:.4f},{box1.halfAxes()[3]:.4f},{box1.halfAxes()[4]:.4f},{box1.halfAxes()[5]:.4f},{box1.halfAxes()[6]:.4f},{box1.halfAxes()[7]:.4f},{box1.halfAxes()[8]:.4f}])"
"!="
f"QgsOrientedBox3D([{box2.centerX():.4f}, {box2.centerY():.4f}, {box2.centerZ():.4f}], [{box2.halfAxes()[0]:.4f}, {box2.halfAxes()[1]:.4f},{box2.halfAxes()[2]:.4f},{box2.halfAxes()[3]:.4f},{box2.halfAxes()[4]:.4f},{box2.halfAxes()[5]:.4f},{box2.halfAxes()[6]:.4f},{box2.halfAxes()[7]:.4f},{box2.halfAxes()[8]:.4f}])"
)
self.assertAlmostEqual(box1.centerX(), box2.centerX(), 4, fail_message)
self.assertAlmostEqual(box1.centerY(), box2.centerY(), 4, fail_message)
self.assertAlmostEqual(box1.centerZ(), box2.centerZ(), 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[0], box2.halfAxes()[0], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[1], box2.halfAxes()[1], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[2], box2.halfAxes()[2], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[3], box2.halfAxes()[3], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[4], box2.halfAxes()[4], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[5], box2.halfAxes()[5], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[6], box2.halfAxes()[6], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[7], box2.halfAxes()[7], 4, fail_message)
self.assertAlmostEqual(box1.halfAxes()[8], box2.halfAxes()[8], 4, fail_message)
def compare_transforms(
self, transform1: QgsMatrix4x4, transform2: QgsMatrix4x4
) -> bool:
"""
Compares two QgsMatrix4x4 objects within 4 decimal places
"""
data1 = transform1.data()
data2 = transform2.data()
fail_message = (
f"QgsMatrix4x4({data1[0]:.4f}, {data1[4]:.4f}, {data1[8]:.4f}, {data1[12]:.4f}, "
f"{data1[1]:.4f}, {data1[5]:.4f}, {data1[9]:.4f}, {data1[13]:.4f}, "
f"{data1[2]:.4f}, {data1[6]:.4f}, {data1[10]:.4f}, {data1[14]:.4f}, "
f"{data1[3]:.4f}, {data1[7]:.4f}, {data1[11]:.4f}, {data1[15]:.4f})"
"!="
f"QgsMatrix4x4({data2[0]:.4f}, {data2[4]:.4f}, {data2[8]:.4f}, {data2[12]:.4f}, "
f"{data2[1]:.4f}, {data2[5]:.4f}, {data2[9]:.4f}, {data2[13]:.4f}, "
f"{data2[2]:.4f}, {data2[6]:.4f}, {data2[10]:.4f}, {data2[14]:.4f}, "
f"{data2[3]:.4f}, {data2[7]:.4f}, {data2[11]:.4f}, {data2[15]:.4f})"
)
for i in range(16):
self.assertAlmostEqual(data1[i], data2[i], 4, fail_message)
def test_index(self):
# TODO: use Rancho
with tempfile.TemporaryDirectory(delete=False) as temp_dir:
layer_json = """
{
"id": 0,
"layerType": "IntegratedMesh",
"version": "1111-2222-3333",
"capabilities": ["View", "Query"],
"spatialReference": {
"wkid": 4326,
"latestWkid": 4326,
"vcsWkid": 3855,
"latestVcsWkid": 3855
},
"nodePages": {
"nodesPerPage": 64,
"lodSelectionMetricType": "maxScreenThresholdSQ"
},
"materialDefinitions" : [{
"doubleSided" : true,
"pbrMetallicRoughness" : {
"baseColorTexture" : {
"textureSetDefinitionId" : 0
},
"metallicFactor" : 0
}
}],
"textureSetDefinitions" : [{
"formats" : [{
"name" : "0",
"format" : "jpg"
},
{
"name" : "0_0_1",
"format" : "dds"
}]
}],
"geometryDefinitions" : [{
"geometryBuffers" : [{
"offset" : 8,
"position" : {
"type" : "Float32",
"component" : 3
},
"normal" : {
"type" : "Float32",
"component" : 3
},
"uv0" : {
"type" : "Float32",
"component" : 2
},
"color" : {
"type" : "UInt8",
"component" : 4
},
"featureId" : {
"type" : "UInt64",
"component" : 1,
"binding" : "per-feature"
},
"faceRange" : {
"type" : "UInt32",
"component" : 2,
"binding" : "per-feature"
}
},
{
"compressedAttributes" : {
"encoding" : "draco",
"attributes" : ["position", "uv0", "feature-index"]
}
}]
}]
}
"""
nodepage_json = """
{
"nodes" : [
{
"index" : 0,
"lodThreshold" : 196349.54374999998,
"obb" : {
"center" : [-117.53759594675871, 34.12419117052764, 411.5930244093761],
"halfSize" : [67.92003, 86.17007, 14.765519],
"quaternion" : [0.206154981448711, 0.8528643536373323, 0.4652900171822784, 0.11673781661986028]
},
"children" : [1, 2, 3, 4]
},
{
"index" : 1,
"parentIndex" : 0,
"lodThreshold" : 785398.1749999999,
"obb" : {
"center" : [-117.53793932189886, 34.123819641151094, 410.4816717179492],
"halfSize" : [34.66071, 43.807545, 8.943618],
"quaternion" : [-0.16656774214665032, -0.4782915919434134, 0.8335042596514344, 0.22082343511347818]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 16,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 16,
"vertexCount" : 60000,
"featureCount" : 1
}
},
"children" : [5, 6, 7, 8]
},
{
"index" : 2,
"parentIndex" : 0,
"lodThreshold" : 785398.1749999999,
"obb" : {
"center" : [-117.53724992475546, 34.123821545486685, 410.2505478467792],
"halfSize" : [9.228004, 34.19266, 44.047585],
"quaternion" : [0.8140355254912096, 0.5257760547630878, -0.216103082756028, 0.11918540640260444]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 37,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 37,
"vertexCount" : 59997,
"featureCount" : 1
}
}
},
{
"index" : 3,
"parentIndex" : 0,
"lodThreshold" : 785398.1749999999,
"obb" : {
"center" : [-117.53795948214312, 34.124566053867625, 412.3858846835792],
"halfSize" : [43.66336, 34.043926, 10.792544],
"quaternion" : [-0.397611945430886, -0.21999843379435893, 0.7659425991790682, -0.4547937606668636]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 58,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 58,
"vertexCount" : 59997,
"featureCount" : 1
}
}
},
{
"index" : 4,
"parentIndex" : 0,
"lodThreshold" : 785398.1749999999,
"obb" : {
"center" : [-117.53724435770368, 34.124570367218126, 414.2313824603334],
"halfSize" : [34.85811, 43.642643, 10.348813],
"quaternion" : [0.44530053235754224, -0.11174393593578129, -0.20527452317737305, 0.8643396894728205]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 79,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 79,
"vertexCount" : 60000,
"featureCount" : 1
}
}
},
{
"index" : 5,
"parentIndex" : 1,
"lodThreshold" : 1767145.8937499998,
"obb" : {
"center" : [-117.53812892736529, 34.12366471837075, 410.5171263786033],
"halfSize" : [16.485355, 16.677244, 1.7911408],
"quaternion" : [0.19655535620659972, 0.8626227061906642, 0.45742106616689565, 0.08952109772300766]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 0,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 0,
"vertexCount" : 13791,
"featureCount" : 1
}
}
},
{
"index" : 6,
"parentIndex" : 1,
"lodThreshold" : 1767145.8937499998,
"obb" : {
"center" : [-117.53777576262333, 34.12362478044946, 410.1134819108993],
"halfSize" : [21.521395, 3.2239213, 17.154518],
"quaternion" : [-0.33920143557919363, -0.6148235345765982, 0.04024529502917849, 0.7108549244816181]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 5,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 5,
"vertexCount" : 20673,
"featureCount" : 1
}
}
},
{
"index" : 7,
"parentIndex" : 1,
"lodThreshold" : 1767145.8937499998,
"obb" : {
"center" : [-117.53813350608226, 34.1240024065191, 411.1757797691971],
"halfSize" : [21.572693, 2.083206, 16.931368],
"quaternion" : [0.7021424175592919, 0.03536027529687852, 0.6126886538878836, 0.36105164421724356]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 10,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 10,
"vertexCount" : 40731,
"featureCount" : 1
}
}
},
{
"index" : 8,
"parentIndex" : 1,
"lodThreshold" : 1767145.8937499998,
"obb" : {
"center" : [-117.53777285367366, 34.12400549129491, 412.516752644442],
"halfSize" : [18.18257, 6.2166224, 22.5802],
"quaternion" : [0.9312857555826259, -0.2857478245548806, -0.04241912677533244, 0.22193611669728053]
},
"mesh" : {
"material" : {
"definition" : 0,
"resource" : 15,
"texelCountHint" : 16777216
},
"geometry" : {
"definition" : 0,
"resource" : 15,
"vertexCount" : 38001,
"featureCount" : 1
}
}
}
]}
"""
_make_tmp_eslpk_dataset(temp_dir, layer_json, nodepage_json)
layer = QgsTiledSceneLayer("file://" + temp_dir, "my layer", "esrii3s")
self.assertTrue(layer.dataProvider().isValid())
index = layer.dataProvider().index()
self.assertTrue(index.isValid())
root_tile = index.rootTile()
self.assertEqual(root_tile.id(), 0)
self.assertEqual(
root_tile.refinementProcess(), Qgis.TileRefinementProcess.Replacement
)
self.assertAlmostEqual(root_tile.geometricError(), 5.51488, 3)
self.assertEqual(root_tile.metadata(), {})
self.assertEqual(root_tile.resources(), {})
self.compare_boxes(
root_tile.boundingVolume().box(),
QgsOrientedBox3D(
[-2443825.3629, -4687033.2800, 3558089.6949],
[
-60.2956,
31.2621,
-0.4944,
20.9402,
41.5349,
72.5372,
5.7728,
11.0081,
-7.9698,
],
),
)
self.compare_transforms(
root_tile.transform(),
QgsMatrix4x4(
1.0,
0.0,
0.0,
-2443825.3629,
0.0,
1.0,
0.0,
-4687033.2800,
0.0,
0.0,
1.0,
3558089.6949,
0.0,
0.0,
0.0,
1.0,
),
)
self.assertEqual(index.parentTileId(root_tile.id()), -1)
self.assertEqual(index.childTileIds(root_tile.id()), [1, 2, 3, 4])
self.assertEqual(index.parentTileId(1), 0)
self.assertEqual(index.parentTileId(2), 0)
self.assertEqual(index.parentTileId(3), 0)
self.assertEqual(index.parentTileId(4), 0)
child_tile0 = index.getTile(1)
self.assertEqual(
child_tile0.resources(),
{"content": "file://" + temp_dir + "/nodes/16/geometries/1.bin.gz"},
)
self.assertEqual(
child_tile0.metadata(),
{
"contentFormat": "draco",
"gltfUpAxis": int(Qgis.Axis.Z),
"material": {
"doubleSided": True,
"pbrBaseColorFactor": [1.0, 1.0, 1.0, 1.0],
"pbrBaseColorTexture": "file://"
+ temp_dir
+ "/nodes/16/textures/0.jpg",
},
},
)
self.assertAlmostEqual(child_tile0.geometricError(), 1.40184, 3)
self.assertEqual(
child_tile0.refinementProcess(), Qgis.TileRefinementProcess.Replacement
)
self.compare_boxes(
child_tile0.boundingVolume().box(),
QgsOrientedBox3D(
[-2443863.7165, -4687038.3195, 3558054.9531],
[
-29.3571,
18.2818,
-2.3026,
-9.1461,
-19.4921,
-38.1511,
-4.3726,
-6.4730,
4.3554,
],
),
)
self.assertEqual(index.childTileIds(child_tile0.id()), [5, 6, 7, 8])
self.assertEqual(index.parentTileId(5), 1)
self.assertEqual(index.parentTileId(6), 1)
self.assertEqual(index.parentTileId(7), 1)
self.assertEqual(index.parentTileId(8), 1)
child_tile00 = index.getTile(5)
self.assertEqual(
child_tile00.resources(),
{"content": "file://" + temp_dir + "/nodes/0/geometries/1.bin.gz"},
)
self.assertAlmostEqual(child_tile00.geometricError(), 0.35578, 3)
self.assertEqual(
child_tile00.refinementProcess(), Qgis.TileRefinementProcess.Replacement
)
self.compare_boxes(
child_tile00.boundingVolume().box(),
QgsOrientedBox3D(
[-2443883.6980, -4687038.8068, 3558040.7461],
[
-14.9473,
6.9404,
0.4183,
4.2895,
8.4097,
13.7480,
0.5987,
1.3505,
-1.0129,
],
),
)
self.assertEqual(index.childTileIds(5), [])
child_tile1 = index.getTile(2)
self.assertEqual(
child_tile1.resources(),
{"content": "file://" + temp_dir + "/nodes/37/geometries/1.bin.gz"},
)
self.assertAlmostEqual(child_tile1.geometricError(), 1.40952, 3)
self.assertEqual(
child_tile1.refinementProcess(), Qgis.TileRefinementProcess.Replacement
)
self.compare_boxes(
child_tile1.boundingVolume().box(),
QgsOrientedBox3D(
[-2443807.1775, -4687067.4496, 3558054.9984],
[
3.2641,
7.4238,
-4.4032,
31.0303,
-14.3168,
-1.1352,
-9.9768,
-18.5566,
-38.6821,
],
),
)
self.assertEqual(index.childTileIds(2), [])
# getTiles() tests
# request to get tiles at max. resolution
# (nodes 0 and 1 are not present as they are replaced by children)
self.assertEqual(
index.getTiles(QgsTiledSceneRequest()), [5, 6, 7, 8, 2, 3, 4]
)
# request with coarse geometric error set
request = QgsTiledSceneRequest()
request.setRequiredGeometricError(10)
self.assertEqual(index.getTiles(request), [0])
# request with more detailed geometric error set
request = QgsTiledSceneRequest()
request.setRequiredGeometricError(5)
self.assertEqual(index.getTiles(request), [1, 2, 3, 4])
# restrict request to one parent tile
request = QgsTiledSceneRequest()
request.setParentTileId(1)
self.assertEqual(index.getTiles(request), [5, 6, 7, 8])
if __name__ == "__main__":
unittest.main()

View File

@ -51,7 +51,7 @@ class TestProviderTiledSceneMetadata(QgsProviderMetadata):
def filters(self, _type: Qgis.FileFilterType):
if _type == Qgis.FileFilterType.TiledScene:
return "Scene Layer Packages (*.slpk *.SLPK)"
return "Test Tiled Scene Filter (*.ttsf)"
class TestQgsProviderRegistry(QgisTestCase):
@ -299,18 +299,20 @@ class TestQgsProviderRegistry(QgisTestCase):
registry = QgsProviderRegistry.instance()
self.assertEqual(
registry.fileTiledSceneFilters(),
"All Supported Files (tileset.json TILESET.JSON);;"
"All Supported Files (tileset.json TILESET.JSON *.slpk *.SLPK);;"
"All Files (*.*);;"
"Cesium 3D Tiles (tileset.json TILESET.JSON)",
"Cesium 3D Tiles (tileset.json TILESET.JSON);;"
"ESRI Scene layer package (*.slpk *.SLPK)",
)
registry.registerProvider(TestProviderTiledSceneMetadata("slpk"))
self.assertEqual(
registry.fileTiledSceneFilters(),
"All Supported Files (tileset.json TILESET.JSON *.slpk *.SLPK);;"
"All Supported Files (tileset.json TILESET.JSON *.slpk *.SLPK *.ttsf);;"
"All Files (*.*);;"
"Cesium 3D Tiles (tileset.json TILESET.JSON);;"
"Scene Layer Packages (*.slpk *.SLPK)",
"ESRI Scene layer package (*.slpk *.SLPK);;"
"Test Tiled Scene Filter (*.ttsf)",
)