Mesh export processes, first part : infrastructure and export vertices (#39664)

[processing] Infrastructure for QgsMeshLayer to add processing algorithms, dataset group paramater and dataset time parameter to allow the user to easily choose a dataset.
This commit is contained in:
Vincent Cloarec 2020-11-03 01:05:28 -04:00 committed by GitHub
parent a1fcc7da4a
commit 1fbba79a0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 2723 additions and 19 deletions

View File

@ -257,7 +257,7 @@ Returns the extra dataset groups count handle by the layer
QList<int> datasetGroupsIndexes() const;
%Docstring
Returns the list of indexes of dataset groups count handled by the layer
Returns the list of indexes of dataset groups handled by the layer
.. note::

View File

@ -173,6 +173,24 @@ If not explicitly set, the unit will default to the :py:func:`~QgsProcessingCont
.. seealso:: :py:func:`setDistanceUnit`
.. versionadded:: 3.16
%End
QgsDateTimeRange currentTimeRange() const;
%Docstring
Returns the current time range to use for temporal operations.
.. seealso:: :py:func:`setCurrentTimeRange`
.. versionadded:: 3.18
%End
void setCurrentTimeRange( const QgsDateTimeRange &currentTimeRange );
%Docstring
Sets the ``current`` time range to use for temporal operations.
.. seealso:: :py:func:`currentTimeRange`
.. versionadded:: 3.18
%End
QgsMapLayerStore *temporaryLayerStore();

View File

@ -0,0 +1,180 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/processing/qgsprocessingparametermeshdataset.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsProcessingParameterMeshDatasetGroups : QgsProcessingParameterDefinition
{
%Docstring
A parameter for processing algorithms that need a list of mesh dataset groups
A valid value for this parameter is a list (QVariantList) of dataset groups index in the mesh layer scope
Dataset group index can be evaluated with the method :py:func:`~valueAsDatasetGroup`
.. note::
This parameter is dependent on a mesh layer parameter (see QgsProcessingParameterMeshLayer)
.. versionadded:: 3.18
%End
%TypeHeaderCode
#include "qgsprocessingparametermeshdataset.h"
%End
public:
QgsProcessingParameterMeshDatasetGroups( const QString &name,
const QString &description = QString(),
const QString &meshLayerParameterName = QString(),
QgsMeshDatasetGroupMetadata::DataType dataType = QgsMeshDatasetGroupMetadata::DataOnVertices,
bool optional = false );
%Docstring
Constructor
:param name: name of the parameter
:param description: description of the parameter
:param meshLayerParameterName: name of the associated mesh layer parameter
:param dataType: data type supported by the parameter
:param optional: whether the parameter is optional
%End
virtual QgsProcessingParameterDefinition *clone() const /Factory/;
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
virtual QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const;
virtual QStringList dependsOnOtherParameters() const;
static QString typeName();
%Docstring
Returns the type name for the parameter class.
%End
QString meshLayerParameterName() const;
%Docstring
Returns the name of the mesh layer parameter
%End
QgsMeshDatasetGroupMetadata::DataType dataType() const;
%Docstring
Returns the data type supported by the parameter
%End
static QList<int> valueAsDatasetGroup( const QVariant &value );
%Docstring
Returns the ``value`` as a list if dataset group indexes
%End
};
class QgsProcessingParameterMeshDatasetTime : QgsProcessingParameterDefinition
{
%Docstring
A parameter for processing algorithms that need a list of mesh dataset index from time parameter
A valid value for this parameter is a map (QVariantMap) with in this form:
- "type" : the type of time settings "current-context-time", "defined-date-time", "dataset-time-step" or "none" if all the dataset groups are static
- "value" : nothing if type is "static" or "current-context-time", QDateTime if "defined-date-time" or, for "dataset_time_step", list of two int representing the dataset index that is the reference for the time step
.. note::
This parameter is dependent on a mesh layer parameter (:py:class:`QgsProcessingParameterMeshLayer`)
and on mesh datast group parameter (:py:class:`QgsProcessingParameterMeshDatasetGroups`)
.. versionadded:: 3.18
%End
%TypeHeaderCode
#include "qgsprocessingparametermeshdataset.h"
%End
public:
QgsProcessingParameterMeshDatasetTime(
const QString &name,
const QString &description = QString(),
const QString &meshLayerParameterName = QString(),
const QString &datasetGroupParameterName = QString() );
%Docstring
Constructor
:param name: name of the parameter
:param description: description of the parameter
:param meshLayerParameterName: name of the associated mesh layer parameter (:py:class:`QgsProcessingParameterMeshLayer`)
:param datasetGroupParameterName: name of the associated dataset group parameter (:py:class:`QgsProcessingParameterMeshDatasetGroups`)
%End
virtual QgsProcessingParameterDefinition *clone() const /Factory/;
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
virtual QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const;
virtual QStringList dependsOnOtherParameters() const;
static QString typeName();
%Docstring
Returns the type name for the parameter class.
%End
QString meshLayerParameterName() const;
%Docstring
Returns the name of the mesh layer parameter
%End
QString datasetGroupParameterName() const;
%Docstring
Returns the name of the dataset groups parameter
%End
static QString valueAsTimeType( const QVariant &value );
%Docstring
Returns the ``dataset`` value time type as a string :
current-context-time : the time is store in the processing context (e.g. current canvas time), in this case the value does not contain any time value
defined-date-time : absolute time of type QDateTime
dataset-time-step : a time step of existing dataset, in this case the time takes the form of a QMeshDatasetIndex with value to the corresponding dataset index
static : dataset groups are all static, in this case the value does not contain any time value
%End
static QgsMeshDatasetIndex timeValueAsDatasetIndex( const QVariant &value );
%Docstring
Returns the ``value`` as a QgsMeshDatasetIndex if the value has "dataset-time-step" type.
If the value has the wrong type return an invalid dataset index
.. seealso:: :py:func:`valueAsTimeType`
%End
static QDateTime timeValueAsDefinedDateTime( const QVariant &value );
%Docstring
Returns the ``value`` as a QDateTime if the value has "defined-date-time" type.
If the value has the wrong type return an invalid QDatetime
.. seealso:: :py:func:`valueAsTimeType`
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/processing/qgsprocessingparametermeshdataset.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -230,6 +230,7 @@ their acceptable ranges, defaults, etc.
#include "qgsprocessingparameterfieldmap.h"
#include "qgsprocessingparametertininputlayers.h"
#include "qgsprocessingparametervectortilewriterlayers.h"
#include "qgsprocessingparametermeshdataset.h"
%End
%ConvertToSubClassCode
if ( sipCpp->type() == QgsProcessingParameterBoolean::typeName() )
@ -314,6 +315,10 @@ their acceptable ranges, defaults, etc.
sipType = sipType_QgsProcessingParameterVectorTileWriterLayers;
else if ( sipCpp->type() == QgsProcessingParameterDxfLayers::typeName() )
sipType = sipType_QgsProcessingParameterDxfLayers;
else if ( sipCpp->type() == QgsProcessingParameterMeshDatasetGroups::typeName() )
sipType = sipType_QgsProcessingParameterMeshDatasetGroups;
else if ( sipCpp->type() == QgsProcessingParameterMeshDatasetTime::typeName() )
sipType = sipType_QgsProcessingParameterMeshDatasetTime;
else
sipType = nullptr;
%End

View File

@ -477,6 +477,7 @@
%Include auto_generated/processing/qgsprocessingparameteraggregate.sip
%Include auto_generated/processing/qgsprocessingparameterdxflayers.sip
%Include auto_generated/processing/qgsprocessingparameterfieldmap.sip
%Include auto_generated/processing/qgsprocessingparametermeshdataset.sip
%Include auto_generated/processing/qgsprocessingparameters.sip
%Include auto_generated/processing/qgsprocessingparametertininputlayers.sip
%Include auto_generated/processing/qgsprocessingparametertype.sip

View File

@ -76,6 +76,9 @@ def createContext(feedback=None):
context.setExpressionContext(createExpressionContext())
if iface and iface.mapCanvas() and iface.mapCanvas().mapSettings().isTemporal():
context.setCurrentTimeRange(iface.mapCanvas().mapSettings().temporalRange())
return context

View File

@ -61,6 +61,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmexecutespatialitequeryregistered.cpp
processing/qgsalgorithmexplode.cpp
processing/qgsalgorithmexplodehstore.cpp
processing/qgsalgorithmexportmeshvertices.cpp
processing/qgsalgorithmextendlines.cpp
processing/qgsalgorithmextentfromlayer.cpp
processing/qgsalgorithmextenttolayer.cpp

View File

@ -0,0 +1,291 @@
/***************************************************************************
qgsalgorithmtinmeshcreation.h
---------------------------
begin : October 2020
copyright : (C) 2020 by Vincent Cloarec
email : vcloarec 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. *
* *
***************************************************************************/
#include "qgsalgorithmexportmeshvertices.h"
#include "qgsprocessingparametermeshdataset.h"
#include "qgsmeshdataset.h"
#include "qgsmeshlayer.h"
#include "qgsmeshlayerutils.h"
#include "qgsmeshlayertemporalproperties.h"
///@cond PRIVATE
QString QgsExportMeshVerticesAlgorithm::group() const
{
return QObject::tr( "Mesh" );
}
QString QgsExportMeshVerticesAlgorithm::groupId() const
{
return QStringLiteral( "mesh" );
}
QString QgsExportMeshVerticesAlgorithm::shortHelpString() const
{
return QObject::tr( "Exports a mesh layer's vertices to a point vector layer, with the dataset values on vertices as attribute values" );
}
QString QgsExportMeshVerticesAlgorithm::name() const
{
return QStringLiteral( "exportmeshvertices" );
}
QString QgsExportMeshVerticesAlgorithm::displayName() const
{
return QObject::tr( "Export mesh vertices" );
}
QgsProcessingAlgorithm *QgsExportMeshVerticesAlgorithm::createInstance() const
{
return new QgsExportMeshVerticesAlgorithm();
}
void QgsExportMeshVerticesAlgorithm::initAlgorithm( const QVariantMap &configuration )
{
Q_UNUSED( configuration );
addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input Mesh Layer" ) ) );
addParameter( new QgsProcessingParameterMeshDatasetGroups(
QStringLiteral( "DATASET_GROUPS" ),
QObject::tr( "Dataset groups" ),
QStringLiteral( "INPUT" ),
QgsMeshDatasetGroupMetadata::DataOnVertices ) );
addParameter( new QgsProcessingParameterMeshDatasetTime(
QStringLiteral( "DATASET_TIME" ),
QObject::tr( "Dataset time" ),
QStringLiteral( "INPUT" ),
QStringLiteral( "DATASET_GROUPS" ) ) );
addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
QStringList exportVectorOptions;
exportVectorOptions << QObject::tr( "Cartesian (x,y)" )
<< QObject::tr( "Polar (magnitude,degree)" )
<< QObject::tr( "Cartesian and Polar" );
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "VECTOR_OPTION" ), QObject::tr( "Export vector option" ), exportVectorOptions, false, 0 ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output vector layer" ), QgsProcessing::TypeVectorPoint ) );
}
static QVector<double> vectorValue( const QgsMeshDatasetValue &value, int exportOption )
{
QVector<double> ret( exportOption == 2 ? 4 : 2 );
if ( exportOption == 0 || exportOption == 2 )
{
ret[0] = value.x();
ret[1] = value.y();
}
if ( exportOption == 1 || exportOption == 2 )
{
double x = value.x();
double y = value.y();
double magnitude = sqrt( x * x + y * y );
double direction = ( asin( x / magnitude ) ) / M_PI * 180;
if ( y < 0 )
direction = 180 - direction;
if ( exportOption == 1 )
{
ret[0] = magnitude;
ret[1] = direction;
}
if ( exportOption == 2 )
{
ret[2] = magnitude;
ret[3] = direction;
}
}
return ret;
}
bool QgsExportMeshVerticesAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
if ( !meshLayer || !meshLayer->isValid() )
return false;
QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
if ( !meshLayer->nativeMesh() )
meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
mNativeMesh = *meshLayer->nativeMesh();
QList<int> datasetGroups =
QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
if ( feedback )
{
feedback->setProgressText( QObject::tr( "Preparing data" ) );
}
QDateTime layerReferenceTime = static_cast<QgsMeshLayerTemporalProperties *>( meshLayer->temporalProperties() )->referenceTime();
//! Extract the date time used to export dataset values under a relative time
QgsInterval relativeTime( 0 );
QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
QString timeType = QgsProcessingParameterMeshDatasetTime::valueAsTimeType( parameterTimeVariant );
if ( timeType == QStringLiteral( "dataset-time-step" ) )
{
QgsMeshDatasetIndex datasetIndex = QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( parameterTimeVariant );
relativeTime = meshLayer->datasetRelativeTime( datasetIndex );
}
else if ( timeType == QStringLiteral( "defined-date-time" ) )
{
QDateTime dateTime = QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( parameterTimeVariant );
if ( dateTime.isValid() )
relativeTime = QgsInterval( layerReferenceTime.secsTo( dateTime ) );
}
else if ( timeType == QStringLiteral( "current-context-time" ) )
{
QDateTime dateTime = context.currentTimeRange().begin();
if ( dateTime.isValid() )
relativeTime = QgsInterval( layerReferenceTime.secsTo( dateTime ) );
}
for ( int i = 0; i < datasetGroups.count(); ++i )
{
int groupIndex = datasetGroups.at( i );
QgsMeshDatasetIndex datasetIndex = meshLayer->datasetIndexAtRelativeTime( relativeTime, groupIndex );
DataGroup dataGroup;
dataGroup.metadata = meshLayer->datasetGroupMetadata( datasetIndex );
int dataCount = dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ? mNativeMesh.vertexCount() : mNativeMesh.faceCount();
dataGroup.datasetValues = meshLayer->datasetValues( datasetIndex, 0, dataCount );
dataGroup.activeFaceFlagValues = meshLayer->areFacesActive( datasetIndex, 0, mNativeMesh.faceCount() );
mDataPerGroup.append( dataGroup );
if ( feedback )
feedback->setProgress( 100 * i / datasetGroups.count() );
}
mExportVectorOption = parameterAsInt( parameters, QStringLiteral( "VECTOR_OPTION" ), context );
return true;
}
QVariantMap QgsExportMeshVerticesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
if ( feedback )
{
if ( feedback->isCanceled() )
return QVariantMap();
feedback->setProgress( 0 );
feedback->setProgressText( QObject::tr( "Creating output vector layer" ) );
}
QgsFields fields;
for ( int i = 0; i < mDataPerGroup.count(); ++i )
{
const DataGroup &dataGroup = mDataPerGroup.at( i );
if ( dataGroup.metadata.isVector() )
{
if ( mExportVectorOption == 0 or mExportVectorOption == 2 )
{
fields.append( QStringLiteral( "%1_x" ).arg( dataGroup.metadata.name() ) );
fields.append( QStringLiteral( "%1_y" ).arg( dataGroup.metadata.name() ) );
}
if ( mExportVectorOption == 1 or mExportVectorOption == 2 )
{
fields.append( QStringLiteral( "%1_mag" ).arg( dataGroup.metadata.name() ) );
fields.append( QStringLiteral( "%1_dir" ).arg( dataGroup.metadata.name() ) );
}
}
else
fields.append( dataGroup.metadata.name() );
}
QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
QString identifier;
QgsFeatureSink *sink = parameterAsSink( parameters,
QStringLiteral( "OUTPUT" ),
context,
identifier,
fields,
QgsWkbTypes::PointZ,
outputCrs );
if ( !sink )
return QVariantMap();
if ( feedback )
{
if ( feedback->isCanceled() )
return QVariantMap();
feedback->setProgress( 0 );
feedback->setProgressText( QObject::tr( "Creating points for each vertices" ) );
}
int verticesCount = mNativeMesh.vertexCount();
for ( int i = 0; i < verticesCount; ++i )
{
QgsAttributes attributes;
for ( const DataGroup &dataGroup : mDataPerGroup )
{
const QgsMeshDatasetGroupMetadata &meta = dataGroup.metadata;
const QgsMeshDatasetValue &value = dataGroup.datasetValues.value( i );
if ( meta.isVector() )
{
QVector<double> vector = vectorValue( dataGroup.datasetValues.value( i ), mExportVectorOption );
for ( double value : vector )
{
attributes.append( value );
}
}
else
attributes.append( value.scalar() );
}
QgsFeature feat;
QgsGeometry geom( new QgsPoint( mNativeMesh.vertex( i ) ) );
try
{
geom.transform( mTransform );
}
catch ( QgsCsException &e )
{
geom = QgsGeometry( new QgsPoint( mNativeMesh.vertex( i ) ) );
feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
}
feat.setGeometry( geom );
feat.setAttributes( attributes );
sink->addFeature( feat );
if ( feedback )
{
if ( feedback->isCanceled() )
return QVariantMap();
feedback->setProgress( 100 * i / verticesCount );
}
}
const QString fileName = parameterAsFile( parameters, QStringLiteral( "OUTPUT" ), context );
QVariantMap ret;
ret[QStringLiteral( "OUTPUT" )] = identifier;
return ret;
}
///@endcond PRIVATE

View File

@ -0,0 +1,62 @@
/***************************************************************************
qgsalgorithmtinmeshcreation.h
---------------------------
begin : October 2020
copyright : (C) 2020 by Vincent Cloarec
email : vcloarec 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 QGSALGORITHMEXPORTMESHVERTICES_H
#define QGSALGORITHMEXPORTMESHVERTICES_H
#define SIP_NO_FILE
#include "qgsprocessingalgorithm.h"
#include "qgsmeshdataset.h"
#include "qgsmeshdataprovider.h"
///@cond PRIVATE
class QgsExportMeshVerticesAlgorithm : public QgsProcessingAlgorithm
{
public:
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override;
QString name() const override;
QString displayName() const override;
protected:
QgsProcessingAlgorithm *createInstance() const override;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QVariantMap processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
QgsMesh mNativeMesh;
struct DataGroup
{
QgsMeshDatasetGroupMetadata metadata;
QgsMeshDataBlock datasetValues;
QgsMeshDataBlock activeFaceFlagValues;
};
QList<DataGroup> mDataPerGroup;
QgsCoordinateTransform mTransform;
int mExportVectorOption = 2;
};
///@endcond PRIVATE
#endif // QGSALGORITHMEXPORTMESHVERTICES_H

View File

@ -55,6 +55,7 @@
#include "qgsalgorithmexecutepostgisquery.h"
#include "qgsalgorithmexecutespatialitequery.h"
#include "qgsalgorithmexecutespatialitequeryregistered.h"
#include "qgsalgorithmexportmeshvertices.h"
#include "qgsalgorithmexplode.h"
#include "qgsalgorithmexplodehstore.h"
#include "qgsalgorithmextendlines.h"
@ -283,6 +284,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsExecuteSpatialiteQueryAlgorithm() );
addAlgorithm( new QgsExplodeAlgorithm() );
addAlgorithm( new QgsExplodeHstoreAlgorithm() );
addAlgorithm( new QgsExportMeshVerticesAlgorithm );
addAlgorithm( new QgsExtendLinesAlgorithm() );
addAlgorithm( new QgsExtentFromLayerAlgorithm() );
addAlgorithm( new QgsExtentToLayerAlgorithm() );

View File

@ -164,6 +164,7 @@ SET(QGIS_CORE_SRCS
processing/qgsprocessingparameteraggregate.cpp
processing/qgsprocessingparameterdxflayers.cpp
processing/qgsprocessingparameterfieldmap.cpp
processing/qgsprocessingparametermeshdataset.cpp
processing/qgsprocessingparameters.cpp
processing/qgsprocessingparametertininputlayers.cpp
processing/qgsprocessingparametertype.cpp
@ -1358,6 +1359,7 @@ SET(QGIS_CORE_HDRS
processing/qgsprocessingparameteraggregate.h
processing/qgsprocessingparameterdxflayers.h
processing/qgsprocessingparameterfieldmap.h
processing/qgsprocessingparametermeshdataset.h
processing/qgsprocessingparameters.h
processing/qgsprocessingparametertininputlayers.h
processing/qgsprocessingparametertype.h

View File

@ -123,7 +123,6 @@ QgsDateTimeRange QgsMeshDataProviderTemporalCapabilities::timeExtent( const QDat
if ( !groupReference.isValid() ) //the dataset group has not a valid reference time -->take global reference
groupReference = mGlobalReferenceDateTime;
if ( !groupReference.isValid() )
groupReference = reference;

View File

@ -327,7 +327,7 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer
int extraDatasetGroupCount() const;
/**
* Returns the list of indexes of dataset groups count handled by the layer
* Returns the list of indexes of dataset groups handled by the layer
*
* \note indexes are used to distinguish all the dataset groups handled by the layer (from dataprovider, extra dataset group,...)
* In the layer scope, those indexes can be different from the data provider indexes.

View File

@ -345,17 +345,27 @@ QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
{
assert( nativeMesh );
Q_UNUSED( method );
assert( method == QgsMeshRendererScalarSettings::NeighbourAverage );
// assuming that native vertex count = triangular vertex count
assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
int vertexCount = triangularMesh->vertices().size();
return interpolateFromFacesData( valuesOnFaces, *nativeMesh, active, method );
}
QVector<double> QgsMeshLayerUtils::interpolateFromFacesData( const QVector<double> &valuesOnFaces,
const QgsMesh &nativeMesh,
QgsMeshDataBlock *active,
QgsMeshRendererScalarSettings::DataResamplingMethod method )
{
int vertexCount = nativeMesh.vertexCount();
assert( method == QgsMeshRendererScalarSettings::NeighbourAverage );
QVector<double> res( vertexCount, 0.0 );
// for face datasets do simple average of the valid values of all faces that contains this vertex
QVector<int> count( vertexCount, 0 );
for ( int i = 0; i < nativeMesh->faces.size(); ++i )
for ( int i = 0; i < nativeMesh.faceCount(); ++i )
{
if ( !active || active->active( i ) )
{
@ -363,7 +373,7 @@ QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
if ( !std::isnan( val ) )
{
// assign for all vertices
const QgsMeshFace &face = nativeMesh->faces.at( i );
const QgsMeshFace &face = nativeMesh.faces.at( i );
for ( int j = 0; j < face.size(); ++j )
{
int vertexIndex = face[j];
@ -449,9 +459,21 @@ QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMeshLa
0,
datacount );
if ( vals.isValid() )
return calculateMagnitudeOnVertices( *nativeMesh, metadata, vals, *activeFaceFlagValues, method );
}
QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMesh &nativeMesh,
const QgsMeshDatasetGroupMetadata &groupMetadata,
const QgsMeshDataBlock &datasetValues,
QgsMeshDataBlock &activeFaceFlagValues,
const QgsMeshRendererScalarSettings::DataResamplingMethod method )
{
QVector<double> ret;
bool scalarDataOnVertices = groupMetadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
if ( datasetValues.isValid() )
{
ret = QgsMeshLayerUtils::calculateMagnitudes( vals );
ret = QgsMeshLayerUtils::calculateMagnitudes( datasetValues );
if ( !scalarDataOnVertices )
{
@ -459,8 +481,7 @@ QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMeshLa
ret = QgsMeshLayerUtils::interpolateFromFacesData(
ret,
nativeMesh,
triangularMesh,
activeFaceFlagValues,
&activeFaceFlagValues,
method );
}
}

View File

@ -235,6 +235,18 @@ class CORE_EXPORT QgsMeshLayerUtils
QgsMeshRendererScalarSettings::DataResamplingMethod method
);
/**
* Interpolate values on vertices from values on faces
*
* \since QGIS 3.18
*/
static QVector<double> interpolateFromFacesData(
const QVector<double> &valuesOnFaces,
const QgsMesh &nativeMesh,
QgsMeshDataBlock *active,
QgsMeshRendererScalarSettings::DataResamplingMethod method
);
/**
* Resamples values on vertices to values on faces
*
@ -250,11 +262,11 @@ class CORE_EXPORT QgsMeshLayerUtils
/**
* Calculates magnitude values ont vertices from the given QgsMeshDataBlock.
* If the values are defined on faces,
* If the values are defined on faces, the values are interpolated with the given method
* \param meshLayer the mesh layer
* \param index the dataset index that contains the data
* \param activeFaceFlagValues pointer to the QVector containing active face flag values
* \param method used to inteprolate the values on vertices if needed
* \param method used to interpolate the values on vertices if needed
* \returns magnitude values of the dataset on all the vertices
* \since QGIS 3.14
*/
@ -264,6 +276,25 @@ class CORE_EXPORT QgsMeshLayerUtils
QgsMeshDataBlock *activeFaceFlagValues,
const QgsMeshRendererScalarSettings::DataResamplingMethod method = QgsMeshRendererScalarSettings::NeighbourAverage );
/**
* Calculates magnitude values on vertices from the given QgsMeshDataBlock.
* If the values are defined on faces, the values are interpolated with the given method
* This method is thread safe.
* \param nativeMesh the native mesh
* \param groupMetadata the metadata of the group where come from the dataset values
* \param datasetValues block containing the dataset values
* \param activeFaceFlagValues block containing active face flag values
* \param method used to interpolate the values on vertices if needed
* \returns magnitude values of the dataset on all the vertices
* \since QGIS 3.18
*/
static QVector<double> calculateMagnitudeOnVertices(
const QgsMesh &nativeMesh,
const QgsMeshDatasetGroupMetadata &groupMetadata,
const QgsMeshDataBlock &datasetValues,
QgsMeshDataBlock &activeFaceFlagValues,
const QgsMeshRendererScalarSettings::DataResamplingMethod method = QgsMeshRendererScalarSettings::NeighbourAverage );
/**
* Calculates the bounding box of the triangle
* \param p1 first vertex of the triangle

View File

@ -72,7 +72,7 @@ class CORE_EXPORT QgsMeshTimeSettings
private:
QString mRelativeTimeFormat = QStringLiteral( "d hh:mm:ss" );
QString mAbsoluteTimeFormat = QStringLiteral( "dd.MM.yyyy hh:mm:ss" );
QString mAbsoluteTimeFormat = QStringLiteral( "yyyy-MM-dd HH:mm:ss" );
};
Q_DECLARE_METATYPE( QgsMeshTimeSettings );

View File

@ -128,6 +128,16 @@ QgsMapLayer *QgsProcessingContext::takeResultLayer( const QString &id )
return tempLayerStore.takeMapLayer( tempLayerStore.mapLayer( id ) );
}
QgsDateTimeRange QgsProcessingContext::currentTimeRange() const
{
return mCurrentTimeRange;
}
void QgsProcessingContext::setCurrentTimeRange( const QgsDateTimeRange &currentTimeRange )
{
mCurrentTimeRange = currentTimeRange;
}
QString QgsProcessingContext::ellipsoid() const
{
return mEllipsoid;

View File

@ -216,6 +216,22 @@ class CORE_EXPORT QgsProcessingContext
*/
void setAreaUnit( QgsUnitTypes::AreaUnit areaUnit );
/**
* Returns the current time range to use for temporal operations.
*
* \see setCurrentTimeRange()
* \since QGIS 3.18
*/
QgsDateTimeRange currentTimeRange() const;
/**
* Sets the \a current time range to use for temporal operations.
*
* \see currentTimeRange()
* \since QGIS 3.18
*/
void setCurrentTimeRange( const QgsDateTimeRange &currentTimeRange );
/**
* Returns a reference to the layer store used for storing temporary layers during
* algorithm execution.
@ -619,6 +635,8 @@ class CORE_EXPORT QgsProcessingContext
QgsUnitTypes::DistanceUnit mDistanceUnit = QgsUnitTypes::DistanceUnknownUnit;
QgsUnitTypes::AreaUnit mAreaUnit = QgsUnitTypes::AreaUnknownUnit;
QgsDateTimeRange mCurrentTimeRange;
//! Temporary project owned by the context, used for storing temporarily loaded map layers
QgsMapLayerStore tempLayerStore;
QgsExpressionContext mExpressionContext;

View File

@ -0,0 +1,293 @@
/***************************************************************************
qgsprocessingparametermeshdataset.cpp
---------------------
Date : October 2020
Copyright : (C) 2020 by Vincent Cloarec
Email : vcloarec 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. *
* *
***************************************************************************/
#include "qgsprocessingparametermeshdataset.h"
/// @cond PRIVATE
///
QgsProcessingParameterMeshDatasetGroups::QgsProcessingParameterMeshDatasetGroups( const QString &name,
const QString &description,
const QString &meshLayerParameterName,
const QgsMeshDatasetGroupMetadata::DataType dataType,
bool optional ):
QgsProcessingParameterDefinition( name, description, QVariant(), optional, QString() ),
mMeshLayerParameterName( meshLayerParameterName ),
mDataType( dataType )
{
}
QgsProcessingParameterDefinition *QgsProcessingParameterMeshDatasetGroups::clone() const
{
return new QgsProcessingParameterMeshDatasetGroups( name(), description(), mMeshLayerParameterName, mDataType );
}
QString QgsProcessingParameterMeshDatasetGroups::type() const
{
return typeName();
}
bool QgsProcessingParameterMeshDatasetGroups::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context ) const
{
Q_UNUSED( context );
return valueIsAcceptable( input, mFlags & FlagOptional );
}
QString QgsProcessingParameterMeshDatasetGroups::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
Q_UNUSED( context );
QStringList parts;
const QVariantList variantDatasetGroupIndexes = value.toList();
for ( const QVariant &variantIndex : variantDatasetGroupIndexes )
parts.append( QString::number( variantIndex.toInt() ) );
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
QString QgsProcessingParameterMeshDatasetGroups::asPythonString( QgsProcessing::PythonOutputType outputType ) const
{
switch ( outputType )
{
case QgsProcessing::PythonQgsProcessingAlgorithmSubclass:
{
QString code = QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('%1', '%2'" )
.arg( name(), description() );
if ( !mMeshLayerParameterName.isEmpty() )
code += QStringLiteral( ", meshLayerParameterName=%1" ).arg( QgsProcessingUtils::stringToPythonLiteral( mMeshLayerParameterName ) );
switch ( mDataType )
{
case QgsMeshDatasetGroupMetadata::DataOnFaces:
code += QStringLiteral( ", dataType=QgsMeshDatasetGroupMetadata.DataOnFaces" );
break;
case QgsMeshDatasetGroupMetadata::DataOnVertices:
code += QStringLiteral( ", dataType=QgsMeshDatasetGroupMetadata.DataOnVertices" );
break;
case QgsMeshDatasetGroupMetadata::DataOnVolumes:
code += QStringLiteral( ", dataType=QgsMeshDatasetGroupMetadata.DataOnVolumes" );
break;
case QgsMeshDatasetGroupMetadata::DataOnEdges:
code += QStringLiteral( ", dataType=QgsMeshDatasetGroupMetadata.DataOnEdges" );
break;
}
if ( mFlags & FlagOptional )
code += QStringLiteral( ", optional=True" );
code += ')';
return code;
}
}
return QString();
}
QStringList QgsProcessingParameterMeshDatasetGroups::dependsOnOtherParameters() const
{
if ( mMeshLayerParameterName.isEmpty() )
return QStringList();
else
return QStringList() << mMeshLayerParameterName;
}
QString QgsProcessingParameterMeshDatasetGroups::meshLayerParameterName() const
{
return mMeshLayerParameterName;
}
QList<int> QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( const QVariant &value )
{
if ( !valueIsAcceptable( value, true ) )
return QList<int>();
QVariantList list = value.toList();
QList<int> ret;
for ( const QVariant &v : list )
ret.append( v.toInt() );
return ret;
}
bool QgsProcessingParameterMeshDatasetGroups::valueIsAcceptable( const QVariant &input, bool allowEmpty )
{
if ( !input.isValid() )
return allowEmpty;
if ( input.type() != QVariant::List )
return false;
const QVariantList list = input.toList();
if ( !allowEmpty && list.isEmpty() )
return false;
for ( const QVariant &var : list )
if ( var.type() != QVariant::Int )
return false;
return true;
}
QgsProcessingParameterMeshDatasetTime::QgsProcessingParameterMeshDatasetTime( const QString &name,
const QString &description,
const QString &meshLayerParameterName,
const QString &datasetGroupParameterName )
: QgsProcessingParameterDefinition( name, description, QVariant() )
, mMeshLayerParameterName( meshLayerParameterName )
, mDatasetGroupParameterName( datasetGroupParameterName )
{
}
QgsProcessingParameterDefinition *QgsProcessingParameterMeshDatasetTime::clone() const
{
return new QgsProcessingParameterMeshDatasetTime( name(), description(), mMeshLayerParameterName, mDatasetGroupParameterName );
}
QString QgsProcessingParameterMeshDatasetTime::type() const
{
return typeName();
}
bool QgsProcessingParameterMeshDatasetTime::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context ) const
{
Q_UNUSED( context );
return valueIsAcceptable( input, mFlags & FlagOptional );
}
QString QgsProcessingParameterMeshDatasetTime::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
Q_UNUSED( context );
QStringList parts;
const QVariantMap variantTimeDataset = value.toMap();
parts << QStringLiteral( "'type': " ) + QgsProcessingUtils::variantToPythonLiteral( variantTimeDataset.value( QStringLiteral( "type" ) ).toString() );
if ( variantTimeDataset.value( QStringLiteral( "type" ) ) == QStringLiteral( "dataset-time-step" ) )
{
QVariantList datasetIndex = variantTimeDataset.value( QStringLiteral( "value" ) ).toList();
parts << QStringLiteral( "'value': " ) + QString( "QgsMeshDatasetIndex(%1,%2)" ).arg( datasetIndex.at( 0 ).toString() ).arg( datasetIndex.at( 1 ).toString() );
}
else if ( variantTimeDataset.value( QStringLiteral( "type" ) ) == QStringLiteral( "defined-date-time" ) )
{
parts << QStringLiteral( "'value': " ) + QgsProcessingUtils::variantToPythonLiteral( variantTimeDataset.value( QStringLiteral( "value" ) ) );
}
return parts.join( ',' ).prepend( '{' ).append( '}' );
}
QString QgsProcessingParameterMeshDatasetTime::asPythonString( QgsProcessing::PythonOutputType outputType ) const
{
switch ( outputType )
{
case QgsProcessing::PythonQgsProcessingAlgorithmSubclass:
{
QString code = QStringLiteral( "QgsProcessingParameterMeshDatasetTime('%1', '%2'" )
.arg( name(), description() );
if ( !mMeshLayerParameterName.isEmpty() )
code += QStringLiteral( ", meshLayerParameterName=%1" ).arg( QgsProcessingUtils::stringToPythonLiteral( mMeshLayerParameterName ) );
if ( !mDatasetGroupParameterName.isEmpty() )
code += QStringLiteral( ", datasetGroupParameterName=%1" ).arg( QgsProcessingUtils::stringToPythonLiteral( mDatasetGroupParameterName ) );
if ( mFlags & FlagOptional )
code += QStringLiteral( ", optional=True" );
code += ')';
return code;
}
}
return QString();
}
QStringList QgsProcessingParameterMeshDatasetTime::dependsOnOtherParameters() const
{
QStringList otherParameters;
if ( !mMeshLayerParameterName.isEmpty() )
otherParameters << mMeshLayerParameterName;
if ( !mDatasetGroupParameterName.isEmpty() )
otherParameters << mMeshLayerParameterName << mDatasetGroupParameterName;
return otherParameters;
}
QString QgsProcessingParameterMeshDatasetTime::meshLayerParameterName() const
{
return mMeshLayerParameterName;
}
QString QgsProcessingParameterMeshDatasetTime::datasetGroupParameterName() const
{
return mDatasetGroupParameterName;
}
QString QgsProcessingParameterMeshDatasetTime::valueAsTimeType( const QVariant &value )
{
if ( !valueIsAcceptable( value, false ) )
return QString();
return value.toMap().value( QStringLiteral( "type" ) ).toString();
}
QgsMeshDatasetIndex QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( const QVariant &value )
{
if ( !valueIsAcceptable( value, false ) || valueAsTimeType( value ) != QStringLiteral( "dataset-time-step" ) )
return QgsMeshDatasetIndex( -1, -1 );
QVariantList list = value.toMap().value( QStringLiteral( "value" ) ).toList();
return QgsMeshDatasetIndex( list.at( 0 ).toInt(), list.at( 1 ).toInt() );
}
QDateTime QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( const QVariant &value )
{
if ( !valueIsAcceptable( value, false ) && valueAsTimeType( value ) != QStringLiteral( "defined-date-time" ) )
return QDateTime();
return value.toMap().value( QStringLiteral( "value" ) ).toDateTime();
}
bool QgsProcessingParameterMeshDatasetTime::valueIsAcceptable( const QVariant &input, bool allowEmpty )
{
if ( !input.isValid() )
return allowEmpty;
if ( input.type() != QVariant::Map )
return false;
const QVariantMap map = input.toMap();
if ( ! map.contains( QStringLiteral( "type" ) ) )
return false;
QString type = map.value( QStringLiteral( "type" ) ).toString();
QVariant value = map.value( QStringLiteral( "value" ) );
if ( type == QStringLiteral( "static" ) || type == QStringLiteral( "current-context-time" ) )
return true;
if ( type == QStringLiteral( "dataset-time-step" ) )
{
if ( value.type() != QVariant::List )
return false;
QVariantList list = value.toList();
if ( value.toList().count() != 2 )
return false;
if ( list.at( 0 ).type() != QVariant::Int || list.at( 1 ).type() != QVariant::Int )
return false;
}
else if ( type == QStringLiteral( "defined-date-time" ) )
{
if ( value.type() != QVariant::DateTime )
return false;
}
else
return false;
return true;
}
/// @endcond PRIVATE

View File

@ -0,0 +1,258 @@
/***************************************************************************
qgsprocessingparametermeshdataset.h
---------------------
Date : October 2020
Copyright : (C) 2020 by Vincent Cloarec
Email : vcloarec 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 QGSPROCESSINGPARAMETERMESHDATASET_H
#define QGSPROCESSINGPARAMETERMESHDATASET_H
#include "qgsprocessingparameters.h"
#include "qgsprocessingparametertype.h"
#include "qgsmeshdataset.h"
/**
* A parameter for processing algorithms that need a list of mesh dataset groups
* A valid value for this parameter is a list (QVariantList) of dataset groups index in the mesh layer scope
* Dataset group index can be evaluated with the method valueAsDatasetGroup()
*
* \note This parameter is dependent on a mesh layer parameter (see QgsProcessingParameterMeshLayer)
*
* \ingroup core
* \since QGIS 3.18
*/
class CORE_EXPORT QgsProcessingParameterMeshDatasetGroups : public QgsProcessingParameterDefinition
{
public:
/**
* Constructor
* \param name name of the parameter
* \param description description of the parameter
* \param meshLayerParameterName name of the associated mesh layer parameter
* \param dataType data type supported by the parameter
* \param optional whether the parameter is optional
*/
QgsProcessingParameterMeshDatasetGroups( const QString &name,
const QString &description = QString(),
const QString &meshLayerParameterName = QString(),
QgsMeshDatasetGroupMetadata::DataType dataType = QgsMeshDatasetGroupMetadata::DataOnVertices,
bool optional = false );
QgsProcessingParameterDefinition *clone() const override SIP_FACTORY;
QString type() const override;
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const override;
QStringList dependsOnOtherParameters() const override;
//! Returns the type name for the parameter class.
static QString typeName() { return QStringLiteral( "meshdatasetgroups" ); }
//! Returns the name of the mesh layer parameter
QString meshLayerParameterName() const;
//! Returns the data type supported by the parameter
QgsMeshDatasetGroupMetadata::DataType dataType() const {return mDataType;}
//! Returns the \a value as a list if dataset group indexes
static QList<int> valueAsDatasetGroup( const QVariant &value );
private:
QString mMeshLayerParameterName;
QgsMeshDatasetGroupMetadata::DataType mDataType = QgsMeshDatasetGroupMetadata::DataOnVertices;
static bool valueIsAcceptable( const QVariant &input, bool allowEmpty );
};
#ifndef SIP_RUN
///@cond PRIVATE
/**
* Parameter type definition for QgsProcessingParameterMeshDatasetGroups.
*
* \ingroup core
* \since QGIS 3.18
*/
class CORE_EXPORT QgsProcessingParameterTypeMeshDatasetGroups : public QgsProcessingParameterType
{
public:
QgsProcessingParameterDefinition *create( const QString &name ) const override SIP_FACTORY
{
return new QgsProcessingParameterMeshDatasetGroups( name );
}
QString description() const override
{
return QCoreApplication::translate( "Processing", "An input allowing selection dataset groups from a mesh layer" );
}
QString name() const override
{
return QCoreApplication::translate( "Processing", "Mesh dataset groups" );
}
QString id() const override
{
return QgsProcessingParameterMeshDatasetGroups::typeName();
}
QString pythonImportString() const override
{
return QStringLiteral( "from qgis.core import QgsProcessingParameterMeshDatasetGroups" );
}
QString className() const override
{
return QStringLiteral( "QgsProcessingParameterMeshDatasetGroups" );
}
QStringList acceptedPythonTypes() const override
{
return QStringList() << QObject::tr( "list[int]: list of dataset group indexes, see QgsProcessingParameterMeshDatasetGroups docs" );
}
};
///@endcond
#endif //SIP_RUN
/**
* A parameter for processing algorithms that need a list of mesh dataset index from time parameter
* A valid value for this parameter is a map (QVariantMap) with in this form:
*
* - "type" : the type of time settings "current-context-time", "defined-date-time", "dataset-time-step" or "none" if all the dataset groups are static
* - "value" : nothing if type is "static" or "current-context-time", QDateTime if "defined-date-time" or, for "dataset_time_step", list of two int representing the dataset index that is the reference for the time step
*
* \note This parameter is dependent on a mesh layer parameter (\see QgsProcessingParameterMeshLayer)
* and on mesh datast group parameter (\see QgsProcessingParameterMeshDatasetGroups)
*
* \ingroup core
* \since QGIS 3.18
*/
class CORE_EXPORT QgsProcessingParameterMeshDatasetTime : public QgsProcessingParameterDefinition
{
public:
/**
* Constructor
* \param name name of the parameter
* \param description description of the parameter
* \param meshLayerParameterName name of the associated mesh layer parameter (\see QgsProcessingParameterMeshLayer)
* \param datasetGroupParameterName name of the associated dataset group parameter (\see QgsProcessingParameterMeshDatasetGroups)
*/
QgsProcessingParameterMeshDatasetTime(
const QString &name,
const QString &description = QString(),
const QString &meshLayerParameterName = QString(),
const QString &datasetGroupParameterName = QString() );
QgsProcessingParameterDefinition *clone() const override SIP_FACTORY;
QString type() const override;
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const override;
QStringList dependsOnOtherParameters() const override;
//! Returns the type name for the parameter class.
static QString typeName() { return QStringLiteral( "meshdatasettime" ); }
//! Returns the name of the mesh layer parameter
QString meshLayerParameterName() const;
//! Returns the name of the dataset groups parameter
QString datasetGroupParameterName() const;
/**
* Returns the \a dataset value time type as a string :
* current-context-time : the time is store in the processing context (e.g. current canvas time), in this case the value does not contain any time value
* defined-date-time : absolute time of type QDateTime
* dataset-time-step : a time step of existing dataset, in this case the time takes the form of a QMeshDatasetIndex with value to the corresponding dataset index
* static : dataset groups are all static, in this case the value does not contain any time value
*/
static QString valueAsTimeType( const QVariant &value );
/**
* Returns the \a value as a QgsMeshDatasetIndex if the value has "dataset-time-step" type.
* If the value has the wrong type return an invalid dataset index
*
* \see valueAsTimeType()
*/
static QgsMeshDatasetIndex timeValueAsDatasetIndex( const QVariant &value );
/**
* Returns the \a value as a QDateTime if the value has "defined-date-time" type.
* If the value has the wrong type return an invalid QDatetime
*
* \see valueAsTimeType()
*/
static QDateTime timeValueAsDefinedDateTime( const QVariant &value );
private:
QString mMeshLayerParameterName;
QString mDatasetGroupParameterName;
static bool valueIsAcceptable( const QVariant &input, bool allowEmpty );
};
#ifndef SIP_RUN
///@cond PRIVATE
/**
* Parameter type definition for QgsProcessingParameterMeshDatasetTime.
*
* \ingroup core
* \since QGIS 3.18
*/
class CORE_EXPORT QgsProcessingParameterTypeMeshDatasetTime: public QgsProcessingParameterType
{
public:
QgsProcessingParameterDefinition *create( const QString &name ) const override SIP_FACTORY
{
return new QgsProcessingParameterMeshDatasetTime( name );
}
QString description() const override
{
return QCoreApplication::translate( "Processing", "An input allowing selection of dataset index from a mesh layer by time setting" );
}
QString name() const override
{
return QCoreApplication::translate( "Processing", "Mesh dataset time" );
}
QString id() const override
{
return QgsProcessingParameterMeshDatasetTime::typeName();
}
QString pythonImportString() const override
{
return QStringLiteral( "from qgis.core import QgsProcessingParameterMeshDatasetTime" );
}
QString className() const override
{
return QStringLiteral( "QgsProcessingParameterMeshDatasetTime" );
}
QStringList acceptedPythonTypes() const override
{
return QStringList() << QObject::tr( "dict{}: dictionary, see QgsProcessingParameterMeshDatasetTime docs" );
}
};
///@endcond
#endif //SIP_RUN
#endif // QGSPROCESSINGPARAMETERMESHDATASET_H

View File

@ -337,6 +337,7 @@ class CORE_EXPORT QgsProcessingParameterDefinition
#include "qgsprocessingparameterfieldmap.h"
#include "qgsprocessingparametertininputlayers.h"
#include "qgsprocessingparametervectortilewriterlayers.h"
#include "qgsprocessingparametermeshdataset.h"
% End
SIP_CONVERT_TO_SUBCLASS_CODE
if ( sipCpp->type() == QgsProcessingParameterBoolean::typeName() )
@ -421,6 +422,10 @@ class CORE_EXPORT QgsProcessingParameterDefinition
sipType = sipType_QgsProcessingParameterVectorTileWriterLayers;
else if ( sipCpp->type() == QgsProcessingParameterDxfLayers::typeName() )
sipType = sipType_QgsProcessingParameterDxfLayers;
else if ( sipCpp->type() == QgsProcessingParameterMeshDatasetGroups::typeName() )
sipType = sipType_QgsProcessingParameterMeshDatasetGroups;
else if ( sipCpp->type() == QgsProcessingParameterMeshDatasetTime::typeName() )
sipType = sipType_QgsProcessingParameterMeshDatasetTime;
else
sipType = nullptr;
SIP_END

View File

@ -18,6 +18,7 @@
#include "qgsprocessingregistry.h"
#include "qgsvectorfilewriter.h"
#include "qgsprocessingparametertypeimpl.h"
#include "qgsprocessingparametermeshdataset.h"
#include "qgsprocessingparametervectortilewriterlayers.h"
#include "qgsprocessingparametertininputlayers.h"
#include "qgsprocessingparameterfieldmap.h"
@ -71,6 +72,8 @@ QgsProcessingRegistry::QgsProcessingRegistry( QObject *parent SIP_TRANSFERTHIS )
addParameterType( new QgsProcessingParameterTypeAggregate() );
addParameterType( new QgsProcessingParameterTypeTinInputLayers() );
addParameterType( new QgsProcessingParameterTypeDxfLayers() );
addParameterType( new QgsProcessingParameterTypeMeshDatasetGroups() );
addParameterType( new QgsProcessingParameterTypeMeshDatasetTime() );
}
QgsProcessingRegistry::~QgsProcessingRegistry()

View File

@ -603,6 +603,18 @@ QString QgsProcessingUtils::variantToPythonLiteral( const QVariant &value )
return parts.join( ',' ).prepend( '{' ).append( '}' );
}
case QVariant::DateTime:
{
const QDateTime dateTime = value.toDateTime();
return QStringLiteral( "QDateTime(QDate(%1, %2, %3), QTime(%4, %5, %6))" )
.arg( dateTime.date().year() )
.arg( dateTime.date().month() )
.arg( dateTime.date().day() )
.arg( dateTime.time().hour() )
.arg( dateTime.time().minute() )
.arg( dateTime.time().second() );
}
default:
break;
}

View File

@ -300,6 +300,7 @@ SET(QGIS_GUI_SRCS
processing/qgsprocessingmaplayercombobox.cpp
processing/qgsprocessingmatrixmodelerwidget.cpp
processing/qgsprocessingmatrixparameterdialog.cpp
processing/qgsprocessingmeshdatasetwidget.cpp
processing/qgsprocessingmodelerparameterwidget.cpp
processing/qgsprocessingmultipleselectiondialog.cpp
processing/qgsprocessingoutputdestinationwidget.cpp
@ -1061,6 +1062,7 @@ SET(QGIS_GUI_HDRS
processing/qgsprocessingmaplayercombobox.h
processing/qgsprocessingmatrixmodelerwidget.h
processing/qgsprocessingmatrixparameterdialog.h
processing/qgsprocessingmeshdatasetwidget.h
processing/qgsprocessingmodelerparameterwidget.h
processing/qgsprocessingmultipleselectiondialog.h
processing/qgsprocessingoutputdestinationwidget.h

View File

@ -24,6 +24,7 @@
#include "qgsprocessingdxflayerswidgetwrapper.h"
#include "qgsprocessingwidgetwrapperimpl.h"
#include "qgsprocessingtininputlayerswidget.h"
#include "qgsprocessingmeshdatasetwidget.h"
#include "qgsprocessingparameters.h"
#include "qgis.h"
#include "qgslogger.h"
@ -75,6 +76,8 @@ QgsProcessingGuiRegistry::QgsProcessingGuiRegistry()
addParameterWidgetFactory( new QgsProcessingAggregateWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingTinInputLayersWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingDxfLayersWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingMeshDatasetGroupsWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingMeshDatasetTimeWidgetWrapper() );
}
QgsProcessingGuiRegistry::~QgsProcessingGuiRegistry()

View File

@ -0,0 +1,686 @@
/***************************************************************************
qgsprocessingmeshdatasetgroupswidget.h
---------------------
Date : October 2020
Copyright : (C) 2020 by Vincent Cloarec
Email : vcloarec 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. *
* *
***************************************************************************/
#include "qgsprocessingmeshdatasetwidget.h"
#include "qgsdatetimeedit.h"
#include "qgsprocessingmultipleselectiondialog.h"
#include "qgsmeshlayer.h"
#include "qgsmeshlayerutils.h"
#include "qgsmeshlayertemporalproperties.h"
#include "qgspanelwidget.h"
#include "qgsmapcanvas.h"
#include <QLineEdit>
#include <QLabel>
#include <QMenu>
#include <QToolButton>
#include <QVBoxLayout>
/// @cond PRIVATE
QgsProcessingMeshDatasetGroupsWidget::QgsProcessingMeshDatasetGroupsWidget( QWidget *parent, const QgsProcessingParameterMeshDatasetGroups *param )
: QWidget( parent ),
mParam( param )
{
QHBoxLayout *hl = new QHBoxLayout();
hl->setContentsMargins( 0, 0, 0, 0 );
mLineEdit = new QLineEdit();
mLineEdit->setEnabled( false );
hl->addWidget( mLineEdit, 1 );
mToolButton = new QToolButton();
mToolButton->setText( QString( QChar( 0x2026 ) ) );
hl->addWidget( mToolButton );
setLayout( hl );
mLineEdit->setText( tr( "%1 dataset groups selected" ).arg( 0 ) );
mToolButton->setPopupMode( QToolButton::InstantPopup );
QMenu *toolButtonMenu = new QMenu( this );
mActionCurrentActiveDatasetGroups = toolButtonMenu->addAction( tr( "Current Active Dataset Group" ) );
connect( mActionCurrentActiveDatasetGroups,
&QAction::triggered, this, &QgsProcessingMeshDatasetGroupsWidget::selectCurrentActiveDatasetGroup );
mActionAvailableDatasetGroups = toolButtonMenu->addAction( tr( "Select in Available Dataset Groups" ) );
connect( mActionAvailableDatasetGroups, &QAction::triggered, this, &QgsProcessingMeshDatasetGroupsWidget::showDialog );
mToolButton->setMenu( toolButtonMenu );
}
void QgsProcessingMeshDatasetGroupsWidget::setMeshLayer( QgsMeshLayer *layer, bool layerFromProject )
{
mActionCurrentActiveDatasetGroups->setEnabled( layer && layerFromProject );
mActionAvailableDatasetGroups->setEnabled( layer );
if ( mMeshLayer == layer )
return;
mDatasetGroupsNames.clear();
if ( layerFromProject )
mMeshLayer = layer;
else
{
mMeshLayer = nullptr;
if ( layer )
{
QList<int> datasetGroupsIndexes = layer->datasetGroupsIndexes();
for ( int i : datasetGroupsIndexes )
{
QgsMeshDatasetGroupMetadata meta = layer->datasetGroupMetadata( i );
if ( meta.dataType() == mParam->dataType() )
{
mDatasetGroupsNames[i] = meta.name();
}
}
}
}
mValue.clear();
updateSummaryText();
emit changed();
}
void QgsProcessingMeshDatasetGroupsWidget::setValue( const QVariant &value )
{
if ( value.isValid() )
mValue = value.type() == QVariant::List ? value.toList() : QVariantList() << value;
else
mValue.clear();
updateSummaryText();
emit changed();
}
QVariant QgsProcessingMeshDatasetGroupsWidget::value() const
{
return mValue;
}
void QgsProcessingMeshDatasetGroupsWidget::showDialog()
{
QList<int> datasetGroupsIndexes;
QStringList options;
QVariantList availableOptions;
if ( mMeshLayer )
{
datasetGroupsIndexes = mMeshLayer->datasetGroupsIndexes();
for ( int i : datasetGroupsIndexes )
{
QgsMeshDatasetGroupMetadata meta = mMeshLayer->datasetGroupMetadata( i );
if ( meta.dataType() == mParam->dataType() )
{
availableOptions.append( i );
options.append( meta.name() );
}
}
}
else
{
for ( int i : mDatasetGroupsNames.keys() )
{
availableOptions.append( i );
options.append( mDatasetGroupsNames.value( i ) );
}
}
QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
if ( panel && panel->dockMode() )
{
QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, mValue );
widget->setPanelTitle( tr( "Dataset Groups Available" ) );
widget->setValueFormatter( [availableOptions, options]( const QVariant & v ) -> QString
{
const int index = v.toInt();
const int pos = availableOptions.indexOf( index );
return ( pos >= 0 && pos < options.size() ) ? options.at( pos ) : QString();
} );
connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [ = ]()
{
setValue( widget->selectedOptions() );
} );
connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
panel->openPanel( widget );
}
else
{
QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, Qt::WindowFlags() );
dlg.setValueFormatter( [datasetGroupsIndexes, options]( const QVariant & v ) -> QString
{
const int index = v.toInt();
const int pos = datasetGroupsIndexes.indexOf( index );
return ( pos >= 0 && pos < options.size() ) ? options.at( pos ) : QString();
} );
if ( dlg.exec() )
{
setValue( dlg.selectedOptions() );
}
}
}
void QgsProcessingMeshDatasetGroupsWidget::selectCurrentActiveDatasetGroup()
{
QVariantList options;
if ( mMeshLayer && mParam )
{
int scalarDatasetGroup = mMeshLayer->rendererSettings().activeScalarDatasetGroup();
int vectorDatasetGroup = mMeshLayer->rendererSettings().activeVectorDatasetGroup();
if ( scalarDatasetGroup >= 0 && mMeshLayer->datasetGroupMetadata( scalarDatasetGroup ).dataType() == mParam->dataType() )
options.append( scalarDatasetGroup );
if ( vectorDatasetGroup >= 0
&& mMeshLayer->datasetGroupMetadata( vectorDatasetGroup ).dataType() == mParam->dataType()
&& vectorDatasetGroup != scalarDatasetGroup )
options.append( vectorDatasetGroup );
}
setValue( options );
}
QgsProcessingMeshDatasetGroupsWidgetWrapper::QgsProcessingMeshDatasetGroupsWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent ):
QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{}
QString QgsProcessingMeshDatasetGroupsWidgetWrapper::parameterType() const
{
return QgsProcessingParameterMeshDatasetGroups::typeName();
}
QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMeshDatasetGroupsWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
{
return new QgsProcessingMeshDatasetGroupsWidgetWrapper( parameter, type );
}
void QgsProcessingMeshDatasetGroupsWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
{
QgsAbstractProcessingParameterWidgetWrapper::postInitialize( wrappers ); //necessary here?
switch ( type() )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Batch:
{
for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
{
if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterMeshDatasetGroups * >( parameterDefinition() )->meshLayerParameterName() )
{
setMeshLayerWrapperValue( wrapper );
connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [ = ]
{
setMeshLayerWrapperValue( wrapper );
} );
break;
}
}
break;
}
case QgsProcessingGui::Modeler:
break;
}
}
void QgsProcessingMeshDatasetGroupsWidgetWrapper::setMeshLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
{
if ( ! mWidget )
return;
// evaluate value to layer
QgsProcessingContext *context = nullptr;
if ( mProcessingContextGenerator )
context = mProcessingContextGenerator->processingContext();
bool layerFromProject;
QgsMeshLayer *meshLayer;
if ( !context )
{
QgsProcessingContext dummyContext;
meshLayer = QgsProcessingParameters::parameterAsMeshLayer( wrapper->parameterDefinition(), wrapper->parameterValue(), dummyContext );
layerFromProject = false;
}
else
{
meshLayer = QgsProcessingParameters::parameterAsMeshLayer( wrapper->parameterDefinition(), wrapper->parameterValue(), *context );
layerFromProject = context->project() && context->project()->layerStore()->layers<QgsMeshLayer *>().contains( meshLayer );
}
if ( mWidget )
mWidget->setMeshLayer( meshLayer, layerFromProject );
}
QWidget *QgsProcessingMeshDatasetGroupsWidgetWrapper::createWidget() SIP_FACTORY
{
mWidget = new QgsProcessingMeshDatasetGroupsWidget( nullptr, static_cast<const QgsProcessingParameterMeshDatasetGroups *>( parameterDefinition() ) );
connect( mWidget, &QgsProcessingMeshDatasetGroupsWidget::changed, this, [ = ]
{
emit widgetValueHasChanged( this );
} );
return mWidget;
}
void QgsProcessingMeshDatasetGroupsWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
{
Q_UNUSED( context );
if ( mWidget )
mWidget->setValue( value );
}
QVariant QgsProcessingMeshDatasetGroupsWidgetWrapper::widgetValue() const
{
if ( mWidget )
return mWidget->value();
return QVariant();
}
void QgsProcessingMeshDatasetGroupsWidget::updateSummaryText()
{
mLineEdit->setText( tr( "%1 options selected" ).arg( mValue.count() ) );
}
QgsProcessingMeshDatasetTimeWidgetWrapper::QgsProcessingMeshDatasetTimeWidgetWrapper( const QgsProcessingParameterDefinition *parameter,
QgsProcessingGui::WidgetType type,
QWidget *parent )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{
}
QString QgsProcessingMeshDatasetTimeWidgetWrapper::parameterType() const
{
return QgsProcessingParameterMeshDatasetTime::typeName();
}
QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingMeshDatasetTimeWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
{
return new QgsProcessingMeshDatasetTimeWidgetWrapper( parameter, type );
}
void QgsProcessingMeshDatasetTimeWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
{
QgsAbstractProcessingParameterWidgetWrapper::postInitialize( wrappers ); //necessary here?
switch ( type() )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Batch:
{
const QgsAbstractProcessingParameterWidgetWrapper *layerParameterWrapper = nullptr;
const QgsAbstractProcessingParameterWidgetWrapper *datasetGroupsParameterWrapper = nullptr;
for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
{
if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterMeshDatasetTime * >( parameterDefinition() )->meshLayerParameterName() )
layerParameterWrapper = wrapper;
if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterMeshDatasetTime * >( parameterDefinition() )->datasetGroupParameterName() )
datasetGroupsParameterWrapper = wrapper;
}
setMeshLayerWrapperValue( layerParameterWrapper );
setDatasetGroupIndexesWrapperValue( datasetGroupsParameterWrapper );
connect( datasetGroupsParameterWrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [ = ]
{
setMeshLayerWrapperValue( layerParameterWrapper );
setDatasetGroupIndexesWrapperValue( datasetGroupsParameterWrapper );
} );
break;
}
case QgsProcessingGui::Modeler:
break;
}
}
void QgsProcessingMeshDatasetTimeWidgetWrapper::setMeshLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
{
if ( !mWidget )
return;
// evaluate value to layer
QgsProcessingContext *context = nullptr;
if ( mProcessingContextGenerator )
context = mProcessingContextGenerator->processingContext();
bool layerFromProject;
QgsMeshLayer *meshLayer;
if ( !context )
{
QgsProcessingContext dummyContext;
meshLayer = QgsProcessingParameters::parameterAsMeshLayer( wrapper->parameterDefinition(), wrapper->parameterValue(), dummyContext );
layerFromProject = false;
}
else
{
meshLayer = QgsProcessingParameters::parameterAsMeshLayer( wrapper->parameterDefinition(), wrapper->parameterValue(), *context );
layerFromProject = context->project() && context->project()->layerStore()->layers<QgsMeshLayer *>().contains( meshLayer );
}
mWidget->setMeshLayer( meshLayer, layerFromProject );
}
void QgsProcessingMeshDatasetTimeWidgetWrapper::setDatasetGroupIndexesWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *wrapper )
{
if ( !mWidget )
return;
QVariant datasetGroupsVariant = wrapper->parameterValue();
if ( !datasetGroupsVariant.isValid() || datasetGroupsVariant.type() != QVariant::List )
mWidget->setDatasetGroupIndexes( QList<int>() );
QVariantList datasetGroupsListVariant = datasetGroupsVariant.toList();
QList<int> datasetGroupsIndexes;
for ( const QVariant &variantIndex : datasetGroupsListVariant )
datasetGroupsIndexes << variantIndex.toInt();
mWidget->setDatasetGroupIndexes( datasetGroupsIndexes );
}
QWidget *QgsProcessingMeshDatasetTimeWidgetWrapper::createWidget()
{
mWidget = new QgsProcessingMeshDatasetTimeWidget( nullptr, static_cast<const QgsProcessingParameterMeshDatasetTime *>( parameterDefinition() ), widgetContext() );
QgsMapCanvas *canvas = widgetContext().mapCanvas();
if ( canvas )
{
connect( canvas, &QgsMapCanvas::temporalRangeChanged, mWidget, &QgsProcessingMeshDatasetTimeWidget::updateValue );
}
connect( mWidget, &QgsProcessingMeshDatasetTimeWidget::changed, this, [ = ]
{
emit widgetValueHasChanged( this );
} );
return mWidget;
}
void QgsProcessingMeshDatasetTimeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
{
Q_UNUSED( context );
if ( mWidget )
mWidget->setValue( value );
}
QVariant QgsProcessingMeshDatasetTimeWidgetWrapper::widgetValue() const
{
if ( mWidget )
return mWidget->value();
return QVariant();
}
QgsProcessingMeshDatasetTimeWidget::QgsProcessingMeshDatasetTimeWidget( QWidget *parent,
const QgsProcessingParameterMeshDatasetTime *param,
const QgsProcessingParameterWidgetContext &context ):
QWidget( parent ),
mParam( param )
{
setupUi( this );
dateTimeEdit->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
mCanvas = context.mapCanvas();
connect( radioButtonCurrentCanvasTime, &QRadioButton::toggled, [this]( bool isChecked ) {if ( isChecked ) this->updateValue();} );
connect( radioButtonDefinedDateTime, &QRadioButton::toggled, [this]( bool isChecked ) {if ( isChecked ) this->updateValue();} );
connect( radioButtonDatasetGroupTimeStep, &QRadioButton::toggled, [this]( bool isChecked ) {if ( isChecked ) this->updateValue();} );
connect( dateTimeEdit, &QgsDateTimeEdit::dateTimeChanged, this, &QgsProcessingMeshDatasetTimeWidget::updateValue );
connect( comboBoxDatasetTimeStep, qgis::overload<int>::of( &QComboBox::currentIndexChanged ),
this, &QgsProcessingMeshDatasetTimeWidget::updateValue );
updateWidget();
}
void QgsProcessingMeshDatasetTimeWidget::setMeshLayer( QgsMeshLayer *layer, bool layerFromProject )
{
if ( mMeshLayer == layer )
return;
if ( layerFromProject )
{
mMeshLayer = layer;
mReferenceTime = static_cast<QgsMeshLayerTemporalProperties *>( layer->temporalProperties() )->referenceTime();
}
else
{
mMeshLayer = nullptr;
mReferenceTime = layer->dataProvider()->temporalCapabilities()->referenceTime();
storeTimeStepsFromLayer( layer );
}
if ( mReferenceTime.isValid() )
whileBlocking( dateTimeEdit )->setDateTime( mReferenceTime );
updateValue();
}
void QgsProcessingMeshDatasetTimeWidget::setDatasetGroupIndexes( const QList<int> datasetGroupIndexes )
{
if ( datasetGroupIndexes == mDatasetGroupIndexes )
return;
mDatasetGroupIndexes = datasetGroupIndexes;
populateTimeSteps();
updateValue();
}
void QgsProcessingMeshDatasetTimeWidget::setValue( const QVariant &value )
{
if ( !value.isValid() || value.type() != QVariant::Map )
return;
mValue = value.toMap();
if ( !mValue.contains( QStringLiteral( "type" ) ) || !mValue.contains( QStringLiteral( "value" ) ) )
return;
QString type = mValue.value( QStringLiteral( "type" ) ).toString();
setEnabled( true );
if ( type == QStringLiteral( "static" ) )
{
setEnabled( false );
}
else if ( type == QStringLiteral( "dataset-time-step" ) )
{
whileBlocking( radioButtonDatasetGroupTimeStep )->setChecked( true );
whileBlocking( comboBoxDatasetTimeStep )->setCurrentIndex( comboBoxDatasetTimeStep->findData( mValue.value( QStringLiteral( "value" ) ) ) );
}
else if ( type == QStringLiteral( "dataset-time-step" ) )
{
radioButtonDefinedDateTime->setChecked( true );
whileBlocking( dateTimeEdit )->setDate( mValue.value( QStringLiteral( "value" ) ).toDate() );
}
else if ( type == QStringLiteral( "current-context-time" ) )
{
whileBlocking( radioButtonCurrentCanvasTime )->setChecked( true );
}
emit changed();
updateWidget();
}
QVariant QgsProcessingMeshDatasetTimeWidget::value() const
{
return mValue;
}
void QgsProcessingMeshDatasetTimeWidget::updateWidget()
{
bool isStatic = !hasTemporalDataset();
setEnabled( !isStatic );
if ( mCanvas != nullptr && mCanvas->mapSettings().isTemporal() )
{
whileBlocking( radioButtonCurrentCanvasTime )->setEnabled( true && mReferenceTime.isValid() );
labelCurrentCanvasTime->setText( mCanvas->mapSettings().temporalRange().begin().toString( "yyyy-MM-dd HH:mm:ss" ) );
}
else
{
whileBlocking( radioButtonCurrentCanvasTime )->setEnabled( false );
if ( radioButtonCurrentCanvasTime->isChecked() )
whileBlocking( radioButtonDefinedDateTime )->setChecked( true );
}
if ( ! mReferenceTime.isValid() )
whileBlocking( radioButtonDatasetGroupTimeStep )->setChecked( true );
whileBlocking( radioButtonDefinedDateTime )->setEnabled( mReferenceTime.isValid() );
dateTimeEdit->setVisible( radioButtonDefinedDateTime->isChecked() && !isStatic );
labelCurrentCanvasTime->setVisible( radioButtonCurrentCanvasTime->isChecked() && !isStatic );
comboBoxDatasetTimeStep->setVisible( radioButtonDatasetGroupTimeStep->isChecked() && !isStatic );
}
bool QgsProcessingMeshDatasetTimeWidget::hasTemporalDataset() const
{
for ( int index : mDatasetGroupIndexes )
{
if ( mMeshLayer && mMeshLayer->datasetGroupMetadata( index ).isTemporal() )
return true;
else if ( mDatasetTimeSteps.contains( index ) )
return true;
}
return false;
}
void QgsProcessingMeshDatasetTimeWidget::populateTimeSteps()
{
if ( mMeshLayer )
{
populateTimeStepsFromLayer();
return;
}
QMap<quint64, QgsMeshDatasetIndex> timeStep;
for ( int groupIndex : mDatasetGroupIndexes )
{
if ( !mDatasetTimeSteps.contains( groupIndex ) )
continue;
const QList<qint64> relativeTimeSteps = mDatasetTimeSteps.value( groupIndex );
for ( int index = 0; index < relativeTimeSteps.count(); ++index )
{
QgsMeshDatasetIndex datasetIndex( groupIndex, index );
if ( timeStep.contains( relativeTimeSteps.at( index ) ) )
continue;
timeStep[relativeTimeSteps.at( index )] = datasetIndex;
}
}
for ( qint64 key : timeStep.keys() )
{
QString stringTime = QgsMeshLayerUtils::formatTime( key / 1000 / 3600, mReferenceTime, QgsMeshTimeSettings() );
QVariantList data;
const QgsMeshDatasetIndex &index = timeStep.value( key );
data << index.group() << index.dataset();
whileBlocking( comboBoxDatasetTimeStep )->addItem( stringTime, data );
}
}
void QgsProcessingMeshDatasetTimeWidget::populateTimeStepsFromLayer()
{
whileBlocking( comboBoxDatasetTimeStep )->clear();
if ( !mMeshLayer )
return;
QMap<quint64, QgsMeshDatasetIndex> timeStep;
for ( int groupIndex : mDatasetGroupIndexes )
{
QgsMeshDatasetGroupMetadata meta = mMeshLayer->datasetGroupMetadata( groupIndex );
if ( !meta.isTemporal() )
continue;
int datasetCount = mMeshLayer->datasetCount( groupIndex );
for ( int index = 0; index < datasetCount; ++index )
{
QgsMeshDatasetIndex datasetIndex( groupIndex, index );
qint64 relativeTime = mMeshLayer->datasetRelativeTimeInMilliseconds( datasetIndex );
if ( timeStep.contains( relativeTime ) )
continue;
timeStep[relativeTime] = datasetIndex;
}
}
for ( qint64 key : timeStep.keys() )
{
QString stringTime = mMeshLayer->formatTime( key / 1000 / 3600 );
QVariantList data;
const QgsMeshDatasetIndex &index = timeStep.value( key );
data << index.group() << index.dataset();
whileBlocking( comboBoxDatasetTimeStep )->addItem( stringTime, data );
}
}
void QgsProcessingMeshDatasetTimeWidget::storeTimeStepsFromLayer( QgsMeshLayer *layer )
{
mDatasetTimeSteps.clear();
if ( !layer )
return;
QList<int> datasetGroupsList = layer->datasetGroupsIndexes();
for ( int groupIndex : datasetGroupsList )
{
QgsMeshDatasetGroupMetadata meta = layer->datasetGroupMetadata( groupIndex );
if ( !meta.isTemporal() )
continue;
int datasetCount = layer->datasetCount( groupIndex );
QList<qint64> relativeTimeSteps;
relativeTimeSteps.reserve( datasetCount );
for ( int index = 0; index < datasetCount; ++index )
relativeTimeSteps.append( layer->datasetRelativeTimeInMilliseconds( QgsMeshDatasetIndex( groupIndex, index ) ) );
mDatasetTimeSteps[groupIndex] = relativeTimeSteps;
}
}
void QgsProcessingMeshDatasetTimeWidget::buildValue()
{
mValue.clear();
if ( !isEnabled() )
{
mValue[QStringLiteral( "type" )] = QStringLiteral( "static" );
}
else if ( radioButtonDatasetGroupTimeStep->isChecked() )
{
mValue[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
mValue[QStringLiteral( "value" )] = comboBoxDatasetTimeStep->currentData();
}
else if ( radioButtonDefinedDateTime->isChecked() )
{
mValue[QStringLiteral( "type" )] = QStringLiteral( "defined-date-time" );
mValue[QStringLiteral( "value" )] = dateTimeEdit->dateTime();
}
else if ( radioButtonCurrentCanvasTime->isChecked() && mCanvas )
{
mValue[QStringLiteral( "type" )] = QStringLiteral( "current-context-time" );
}
emit changed();
}
void QgsProcessingMeshDatasetTimeWidget::updateValue()
{
updateWidget();
buildValue();
}
///@endcond

View File

@ -0,0 +1,183 @@
/***************************************************************************
qgsprocessingmeshdatasetwidget.h
---------------------
Date : October 2020
Copyright : (C) 2020 by Vincent Cloarec
Email : vcloarec 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 QGSPROCESSINGMESHDATASETWIDGET_H
#define QGSPROCESSINGMESHDATASETWIDGET_H
#include <QAction>
#include "qgsprocessingwidgetwrapperimpl.h"
#include "qgsprocessingparametermeshdataset.h"
#include "ui_qgsprocessingmeshdatasettimewidget.h"
#define SIP_NO_FILE
/// @cond PRIVATE
class GUI_EXPORT QgsProcessingMeshDatasetGroupsWidget : public QWidget
{
Q_OBJECT
public:
QgsProcessingMeshDatasetGroupsWidget( QWidget *parent = nullptr, const QgsProcessingParameterMeshDatasetGroups *param = nullptr );
/**
* Set the mesh layer for populate the dataset group names,
* if \a layerFromProject is false, the layer will not stay referenced in the instance of this object but
* all that is needed is stored in the instance
*/
void setMeshLayer( QgsMeshLayer *layer, bool layerFromProject = false );
void setValue( const QVariant &value );
QVariant value() const;
signals:
void changed();
private slots:
void showDialog();
void selectCurrentActiveDatasetGroup();
private:
const QgsProcessingParameterMeshDatasetGroups *mParam = nullptr;
QVariantList mValue;
QPointer<QLineEdit> mLineEdit = nullptr;
QPointer<QToolButton> mToolButton = nullptr;
QPointer<QAction> mActionCurrentActiveDatasetGroups = nullptr;
QPointer<QAction> mActionAvailableDatasetGroups = nullptr;
QgsMeshLayer *mMeshLayer = nullptr;
QMap<int, QString> mDatasetGroupsNames; //used to store the dataet groups name if layer is not referenced
QStringList datasetGroupsNames();
void updateSummaryText();
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingMeshDatasetGroupsWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface
{
Q_OBJECT
public:
QgsProcessingMeshDatasetGroupsWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard, QWidget *parent = nullptr );
QString parameterType() const override;
QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type ) override;
void postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers ) override;
//! Sets the layer parameter widget wrapper
void setMeshLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *wrapper );
protected:
QStringList compatibleParameterTypes() const override {return QStringList();}
QStringList compatibleOutputTypes() const override {return QStringList();}
QWidget *createWidget() override;
void setWidgetValue( const QVariant &value, QgsProcessingContext &context ) override;
QVariant widgetValue() const override;
private:
QgsProcessingMeshDatasetGroupsWidget *mWidget = nullptr;
std::unique_ptr<QgsMeshLayer> mTemporarytMeshLayer;
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingMeshDatasetTimeWidget : public QWidget, private Ui::QgsProcessingMeshDatasetTimeWidgetBase
{
Q_OBJECT
public:
QgsProcessingMeshDatasetTimeWidget( QWidget *parent = nullptr,
const QgsProcessingParameterMeshDatasetTime *param = nullptr,
const QgsProcessingParameterWidgetContext &context = QgsProcessingParameterWidgetContext() );
/**
* Set the mesh layer for populate the time steps,
* if \a layerFromProject is false, the layer will not stay referenced in the instance of this object but
* all that is needed is stored in the instance
*/
void setMeshLayer( QgsMeshLayer *layer, bool layerFromProject = false );
void setDatasetGroupIndexes( const QList<int> datasetGroupIndexes );
void setValue( const QVariant &value );
QVariant value() const;
public slots:
void updateValue();
signals:
void changed();
private:
const QgsProcessingParameterMeshDatasetTime *mParam = nullptr;
QVariantMap mValue;
QgsMapCanvas *mCanvas;
QLineEdit *mLineEdit = nullptr;
QToolButton *mToolButton = nullptr;
QgsMeshLayer *mMeshLayer = nullptr;
QList<int> mDatasetGroupIndexes;
QDateTime mReferenceTime;
void populateTimeSteps();
bool hasTemporalDataset() const;
//! Populates diretly the time steps combo box with the referenced layer, used if layer comes from project
void populateTimeStepsFromLayer();
//! Stores the dataset time steps to use them later depending of chosen dataset groups (setDatasetGroupIndexes()), used if layer does not come from project
void storeTimeStepsFromLayer( QgsMeshLayer *layer );
QMap<int, QList<qint64>> mDatasetTimeSteps; //used if layer does not come from project
void updateWidget();
void buildValue();
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingMeshDatasetTimeWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface
{
Q_OBJECT
public:
QgsProcessingMeshDatasetTimeWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard, QWidget *parent = nullptr );
QString parameterType() const override;
QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type ) override;
void postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers ) override;
//! Sets the layer parameter widget wrapper
void setMeshLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *wrapper );
//! Sets the dataset group indexes widget wrapper
void setDatasetGroupIndexesWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *wrapper );
protected:
QStringList compatibleParameterTypes() const override {return QStringList();}
QStringList compatibleOutputTypes() const override {return QStringList();}
QWidget *createWidget() override;
void setWidgetValue( const QVariant &value, QgsProcessingContext &context ) override;
QVariant widgetValue() const override;
private:
QgsProcessingMeshDatasetTimeWidget *mWidget = nullptr;
std::unique_ptr<QgsMeshLayer> mTemporarytMeshLayer;
friend class TestProcessingGui;
};
///@endcond
#endif // QGSPROCESSINGMESHDATASETWIDGET_H

View File

@ -1465,7 +1465,6 @@ class GUI_EXPORT QgsProcessingDateTimeWidgetWrapper : public QgsAbstractProcessi
QVariant widgetValue() const override;
QStringList compatibleParameterTypes() const override;
QStringList compatibleOutputTypes() const override;
QString modelerExpressionFormatString() const override;

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsProcessingMeshDatasetTimeWidgetBase</class>
<widget class="QWidget" name="QgsProcessingMeshDatasetTimeWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>465</width>
<height>122</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="radioButtonCurrentCanvasTime">
<property name="text">
<string>Current canvas time</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonDefinedDateTime">
<property name="text">
<string>Defined date/time</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonDatasetGroupTimeStep">
<property name="text">
<string>Dataset group time step</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QComboBox" name="comboBoxDatasetTimeStep"/>
</item>
<item>
<widget class="QDateTimeEdit" name="dateTimeEdit">
<property name="calendarPopup">
<bool>true</bool>
</property>
<property name="timeSpec">
<enum>Qt::UTC</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelCurrentCanvasTime">
<property name="text">
<string>Current canvas time</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -129,7 +129,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="p1">
<layout class="QVBoxLayout" name="verticalLayoutP1">
@ -556,8 +556,6 @@
</customwidgets>
<resources>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -52,6 +52,7 @@
#include "qgsprocessingparameteraggregate.h"
#include "qgsprocessingparametertininputlayers.h"
#include "qgsprocessingparameterdxflayers.h"
#include "qgsprocessingparametermeshdataset.h"
#include "qgsdxfexport.h"
class DummyAlgorithm : public QgsProcessingAlgorithm
@ -605,6 +606,8 @@ class TestQgsProcessing: public QObject
void parameterFieldMapping();
void parameterAggregate();
void parameterTinInputLayers();
void parameterMeshDatasetGroups();
void parameterMeshDatasetTime();
void parameterDxfLayers();
void checkParamValues();
void combineLayerExtent();
@ -8014,6 +8017,140 @@ void TestQgsProcessing::parameterTinInputLayers()
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterTinInputLayers('tin input layer', '')" ) );
}
void TestQgsProcessing::parameterMeshDatasetGroups()
{
QgsProcessingContext context;
QgsProject project;
context.setProject( &project );
std::unique_ptr< QgsProcessingParameterMeshDatasetGroups> def( new QgsProcessingParameterMeshDatasetGroups( QStringLiteral( "dataset groups" ), QStringLiteral( "groups" ) ) );
QVERIFY( def->type() == QStringLiteral( "meshdatasetgroups" ) );
QVERIFY( def->dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices );
QVERIFY( !def->checkValueIsAcceptable( 1 ) );
QVERIFY( !def->checkValueIsAcceptable( 1.0 ) );
QVERIFY( !def->checkValueIsAcceptable( "test" ) );
QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
QVariantList groupsList;
QVERIFY( !def->checkValueIsAcceptable( groupsList ) );
groupsList.append( 0 );
QVERIFY( def->checkValueIsAcceptable( groupsList ) );
groupsList.append( 5 );
QVERIFY( def->checkValueIsAcceptable( groupsList ) );
QVERIFY( def->dependsOnOtherParameters().isEmpty() );
QString valueAsPythonString = def->valueAsPythonString( groupsList, context );
QCOMPARE( valueAsPythonString, QStringLiteral( "[0,5]" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsList ), QList<int>() << 0 << 5 );
QString pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('dataset groups', 'groups', dataType=QgsMeshDatasetGroupMetadata.DataOnVertices)" ) );
// optional, layer parameter and data on faces
def.reset( new QgsProcessingParameterMeshDatasetGroups(
QStringLiteral( "dataset groups" ),
QStringLiteral( "groups" ),
QStringLiteral( "layer parameter" ),
QgsMeshDatasetGroupMetadata::DataOnFaces, true ) );
QVERIFY( def->dataType() == QgsMeshDatasetGroupMetadata::DataOnFaces );
QVERIFY( !def->checkValueIsAcceptable( 1 ) );
QVERIFY( !def->checkValueIsAcceptable( 1.0 ) );
QVERIFY( !def->checkValueIsAcceptable( "test" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
groupsList = QVariantList();
QVERIFY( def->checkValueIsAcceptable( groupsList ) );
groupsList.append( 2 );
QVERIFY( def->checkValueIsAcceptable( groupsList ) );
groupsList.append( 6 );
QVERIFY( def->checkValueIsAcceptable( groupsList ) );
valueAsPythonString = def->valueAsPythonString( groupsList, context );
QCOMPARE( valueAsPythonString, QStringLiteral( "[2,6]" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsList ), QList<int>() << 2 << 6 );
QVERIFY( !def->dependsOnOtherParameters().isEmpty() );
QCOMPARE( def->meshLayerParameterName(), QStringLiteral( "layer parameter" ) );
pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('dataset groups', 'groups', meshLayerParameterName='layer parameter', dataType=QgsMeshDatasetGroupMetadata.DataOnFaces, optional=True)" ) );
}
void TestQgsProcessing::parameterMeshDatasetTime()
{
QgsProcessingContext context;
QgsProject project;
context.setProject( &project );
std::unique_ptr< QgsProcessingParameterMeshDatasetTime> def( new QgsProcessingParameterMeshDatasetTime( QStringLiteral( "dataset groups" ), QStringLiteral( "groups" ) ) );
QVERIFY( def->type() == QStringLiteral( "meshdatasettime" ) );
QVERIFY( !def->checkValueIsAcceptable( 1 ) );
QVERIFY( !def->checkValueIsAcceptable( 1.0 ) );
QVERIFY( !def->checkValueIsAcceptable( "test" ) );
QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
QVariantMap value;
value[QStringLiteral( "test" )] = QStringLiteral( "test" );
QVERIFY( !def->checkValueIsAcceptable( value ) );
value.clear();
value[QStringLiteral( "type" )] = QStringLiteral( "test" );
QVERIFY( !def->checkValueIsAcceptable( value ) );
value[QStringLiteral( "type" )] = QStringLiteral( "static" );
QVERIFY( def->checkValueIsAcceptable( value ) );
QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'static'}" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "static" ) );
value[QStringLiteral( "type" )] = QStringLiteral( "current-context-time" );
QVERIFY( def->checkValueIsAcceptable( value ) );
QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'current-context-time'}" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "current-context-time" ) );
value[QStringLiteral( "type" )] = QStringLiteral( "defined-date-time" );
QVERIFY( !def->checkValueIsAcceptable( value ) );
value[QStringLiteral( "value" )] = QDateTime( QDate( 2123, 1, 2 ), QTime( 1, 2, 3 ) );
QVERIFY( def->checkValueIsAcceptable( value ) );
QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'defined-date-time','value': QDateTime(QDate(2123, 1, 2), QTime(1, 2, 3))}" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "defined-date-time" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ), QDateTime( QDate( 2123, 1, 2 ), QTime( 1, 2, 3 ) ) );
QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ).isValid() );
value.clear();
value[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVERIFY( !def->checkValueIsAcceptable( value ) );
value[QStringLiteral( "value" )] = QVariantList() << 1 << 5;
QVERIFY( def->checkValueIsAcceptable( value ) );
QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'dataset-time-step','value': QgsMeshDatasetIndex(1,5)}" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "dataset-time-step" ) );
QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ).isValid() );
QVERIFY( QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ) == QgsMeshDatasetIndex( 1, 5 ) );
QString pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetTime('dataset groups', 'groups')" ) );
QVERIFY( def->dependsOnOtherParameters().isEmpty() );
def.reset( new QgsProcessingParameterMeshDatasetTime( QStringLiteral( "dataset groups" ), QStringLiteral( "groups" ), QStringLiteral( "layer parameter" ), QStringLiteral( "dataset group parameter" ) ) );
pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetTime('dataset groups', 'groups', meshLayerParameterName='layer parameter', datasetGroupParameterName='dataset group parameter')" ) );
QVERIFY( !def->dependsOnOtherParameters().isEmpty() );
QCOMPARE( def->meshLayerParameterName(), QStringLiteral( "layer parameter" ) );
QCOMPARE( def->datasetGroupParameterName(), QStringLiteral( "dataset group parameter" ) );
}
void TestQgsProcessing::parameterDateTime()
{
QgsProcessingContext context;
@ -11160,6 +11297,7 @@ void TestQgsProcessing::variantToPythonLiteral()
QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a 'string'" ) ), QStringLiteral( "'a \\'string\\''" ) );
QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a \"string\"" ) ), QStringLiteral( "'a \\\"string\\\"'" ) );
QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a \n str\tin\\g" ) ), QStringLiteral( "'a \\n str\\tin\\\\g'" ) );
QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QDateTime( QDate( 2345, 1, 2 ), QTime( 6, 57, 58 ) ) ), QStringLiteral( "QDateTime(QDate(2345, 1, 2), QTime(6, 57, 58))" ) );
QVariantMap map;
map.insert( QStringLiteral( "list" ), QVariantList() << 1 << 2 << "a" );
map.insert( QStringLiteral( "another" ), 4 );
@ -11176,6 +11314,8 @@ void TestQgsProcessing::stringToPythonLiteral()
QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a \n str\tin\\g" ) ), QStringLiteral( "'a \\n str\\tin\\\\g'" ) );
}
void TestQgsProcessing::defaultExtensionsForProvider()
{
DummyProvider3 provider;

View File

@ -149,6 +149,7 @@ class TestQgsProcessingAlgs: public QObject
void exportAtlasLayoutPng();
void tinMeshCreation();
void exportMeshVertices();
private:
@ -227,6 +228,19 @@ void TestQgsProcessingAlgs::initTestCase()
QgsProject::instance()->addMapLayers(
QList<QgsMapLayer *>() << mPolygonLayer );
QVERIFY( mPolygonLayer->isValid() );
//add a mesh layer
QString uri( dataDir + "/mesh/quad_and_triangle.2dm" );
QString meshLayerName = QStringLiteral( "mesh layer" );
QgsMeshLayer *meshLayer = new QgsMeshLayer( uri, meshLayerName, QStringLiteral( "mdal" ) );
// Register the layer with the registry
QgsProject::instance()->addMapLayer( meshLayer );
QVERIFY( meshLayer->isValid() );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_vertex_scalar.dat" );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_vertex_vector.dat" );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_els_face_scalar.dat" );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_els_face_vector.dat" );
QCOMPARE( meshLayer->datasetGroupCount(), 5 );
}
void TestQgsProcessingAlgs::cleanupTestCase()
@ -4797,6 +4811,89 @@ void TestQgsProcessingAlgs::tinMeshCreation()
QVERIFY( qgsDoubleNear( meshLayer.datasetValue( QgsMeshDatasetIndex( 0, 0 ), QgsPointXY( -86.0, 35.0 ) ).scalar(), 1.855, 0.001 ) ) ;
}
void TestQgsProcessingAlgs::exportMeshVertices()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshvertices" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
QVariantList datasetGroup;
datasetGroup << 1 << 2;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( resultLayer );
QVERIFY( resultLayer->isValid() );
QVERIFY( resultLayer->geometryType() == QgsWkbTypes::PointGeometry );
QCOMPARE( resultLayer->featureCount(), 5l );
QgsAttributeList attributeList = resultLayer->attributeList();
QCOMPARE( resultLayer->fields().count(), 5 );
QCOMPARE( resultLayer->fields().at( 0 ).name(), QStringLiteral( "VertexScalarDataset" ) );
QCOMPARE( resultLayer->fields().at( 1 ).name(), QStringLiteral( "VertexVectorDataset_x" ) );
QCOMPARE( resultLayer->fields().at( 2 ).name(), QStringLiteral( "VertexVectorDataset_y" ) );
QCOMPARE( resultLayer->fields().at( 3 ).name(), QStringLiteral( "VertexVectorDataset_mag" ) );
QCOMPARE( resultLayer->fields().at( 4 ).name(), QStringLiteral( "VertexVectorDataset_dir" ) );
QgsFeatureIterator featIt = resultLayer->getFeatures();
QgsFeature feat;
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (1000 2000 20)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.828, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (2000 2000 30)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 3.605, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 56.3099, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (3000 2000 40)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 4.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 4.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 5.0, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 53.130, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (2000 3000 50)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 4.242, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (1000 3000 10)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), -1.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.236, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 116.565, 2 ) );
}
bool TestQgsProcessingAlgs::imageCheck( const QString &testName, const QString &renderedImage )
{
QgsRenderChecker checker;

View File

@ -668,7 +668,7 @@ void TestQgsMapToolIdentifyAction::identifyMesh()
QCOMPARE( results[0].mAttributes[ QStringLiteral( "Scalar Value" )], QStringLiteral( "42" ) );
QCOMPARE( results[0].mDerivedAttributes[QStringLiteral( "Source" )], mesh );
QCOMPARE( results[1].mDerivedAttributes[ QStringLiteral( "Time Step" )], QStringLiteral( "01.01.1950 00:00:00" ) );
QCOMPARE( results[1].mDerivedAttributes[ QStringLiteral( "Time Step" )], QStringLiteral( "1950-01-01 00:00:00" ) );
QCOMPARE( results[1].mLabel, QStringLiteral( "VertexVectorDataset" ) );
QCOMPARE( results[1].mDerivedAttributes[QStringLiteral( "Source" )], vectorDs );

View File

@ -80,6 +80,7 @@
#include "qgsprocessingfeaturesourceoptionswidget.h"
#include "qgsextentwidget.h"
#include "qgsrasterbandcombobox.h"
#include "qgsmeshlayertemporalproperties.h"
#include "qgsmodelgraphicsscene.h"
#include "qgsmodelgraphicsview.h"
#include "qgsmodelcomponentgraphicitem.h"
@ -91,6 +92,7 @@
#include "qgsprocessingtininputlayerswidget.h"
#include "qgsprocessingparameterdxflayers.h"
#include "qgsprocessingdxflayerswidgetwrapper.h"
#include "qgsprocessingmeshdatasetwidget.h"
class TestParamType : public QgsProcessingParameterDefinition
@ -254,6 +256,8 @@ class TestProcessingGui : public QObject
void testFolderOutWrapper();
void testTinInputLayerWrapper();
void testDxfLayersWrapper();
void testMeshDatasetWrapperLayerInProject();
void testMeshDatasetWrapperLayerOutsideProject();
void testModelGraphicsView();
private:
@ -8907,6 +8911,311 @@ void TestProcessingGui::testDxfLayersWrapper()
QCOMPARE( valueAsPythonString, QStringLiteral( "[{'layer': '%1','attributeIndex': -1}]" ).arg( vectorLayer->source() ) );
}
void TestProcessingGui::testMeshDatasetWrapperLayerInProject()
{
QgsProcessingParameterMeshLayer layerDefinition( QStringLiteral( "layer" ), QStringLiteral( "layer" ) );
QgsProcessingMeshLayerWidgetWrapper layerWrapper( &layerDefinition );
QgsProcessingParameterMeshDatasetGroups groupsDefinition( QStringLiteral( "groups" ),
QStringLiteral( "groups" ),
QStringLiteral( "layer" ),
QgsMeshDatasetGroupMetadata::DataOnVertices );
QgsProcessingMeshDatasetGroupsWidgetWrapper groupsWrapper( &groupsDefinition );
QgsProcessingParameterMeshDatasetTime timeDefinition( QStringLiteral( "time" ), QStringLiteral( "time" ), QStringLiteral( "layer" ), QStringLiteral( "groups" ) );
QgsProcessingMeshDatasetTimeWidgetWrapper timeWrapper( &timeDefinition );
QList<QgsAbstractProcessingParameterWidgetWrapper *> wrappers;
wrappers << &layerWrapper << &groupsWrapper << &timeWrapper;
QgsProject project;
QgsProcessingContext context;
context.setProject( &project );
QgsProcessingParameterWidgetContext widgetContext;
std::unique_ptr<QgsMapCanvas> mapCanvas = qgis::make_unique<QgsMapCanvas>();
widgetContext.setMapCanvas( mapCanvas.get() );
widgetContext.setProject( &project );
layerWrapper.setWidgetContext( widgetContext );
groupsWrapper.setWidgetContext( widgetContext );
timeWrapper.setWidgetContext( widgetContext );
TestProcessingContextGenerator generator( context );
layerWrapper.registerProcessingContextGenerator( &generator );
groupsWrapper.registerProcessingContextGenerator( &generator );
timeWrapper.registerProcessingContextGenerator( &generator );
QSignalSpy layerSpy( &layerWrapper, &QgsProcessingMeshLayerWidgetWrapper::widgetValueHasChanged );
QSignalSpy groupsSpy( &groupsWrapper, &QgsProcessingMeshDatasetGroupsWidgetWrapper::widgetValueHasChanged );
QSignalSpy timeSpy( &timeWrapper, &QgsProcessingMeshDatasetTimeWidgetWrapper::widgetValueHasChanged );
std::unique_ptr<QWidget> layerWidget( layerWrapper.createWrappedWidget( context ) );
std::unique_ptr<QWidget> groupWidget( groupsWrapper.createWrappedWidget( context ) );
std::unique_ptr<QWidget> timeWidget( timeWrapper.createWrappedWidget( context ) );
QgsProcessingMeshDatasetGroupsWidget *datasetGroupWidget = qobject_cast<QgsProcessingMeshDatasetGroupsWidget *>( groupWidget.get() );
QgsProcessingMeshDatasetTimeWidget *datasetTimeWidget = qobject_cast<QgsProcessingMeshDatasetTimeWidget *>( timeWidget.get() );
QVERIFY( layerWidget );
QVERIFY( groupWidget );
QVERIFY( datasetGroupWidget );
QVERIFY( timeWidget );
groupsWrapper.postInitialize( wrappers );
timeWrapper.postInitialize( wrappers );
QString dataDir = QString( TEST_DATA_DIR ); //defined in CmakeLists.txt
dataDir += "/mesh";
QString uri( dataDir + "/quad_and_triangle.2dm" );
QString meshLayerName = QStringLiteral( "mesh layer" );
QgsMeshLayer *layer = new QgsMeshLayer( uri, meshLayerName, QStringLiteral( "mdal" ) );
QVERIFY( layer->isValid() );
layer->addDatasets( dataDir + "/quad_and_triangle_vertex_scalar.dat" );
layer->addDatasets( dataDir + "/quad_and_triangle_vertex_vector.dat" );
layer->addDatasets( dataDir + "/quad_and_triangle_els_face_scalar.dat" );
layer->addDatasets( dataDir + "/quad_and_triangle_els_face_vector.dat" );
QgsMeshRendererSettings settings = layer->rendererSettings();
// 1 dataset on vertices and 1 dataset on faces
settings.setActiveScalarDatasetGroup( 1 );
settings.setActiveVectorDatasetGroup( 4 );
layer->setRendererSettings( settings );
QCOMPARE( layer->datasetGroupCount(), 5 );
layerSpy.clear();
groupsSpy.clear();
timeSpy.clear();
// without layer in the project
QString meshOutOfProject( dataDir + "/trap_steady_05_3D.nc" );
layerWrapper.setWidgetValue( meshOutOfProject, context );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 1 );
QCOMPARE( timeSpy.count(), 1 );
QVERIFY( datasetTimeWidget->radioButtonDatasetGroupTimeStep->isChecked() );
QVariantList groups;
groups << 0;
groupsWrapper.setWidgetValue( groups, context );
QVERIFY( groupsDefinition.checkValueIsAcceptable( groupsWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsWrapper.widgetValue() ), QList<int>() << 0 );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "static" ) );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 2 );
QCOMPARE( timeSpy.count(), 3 );
// with layer in the project
layerSpy.clear();
groupsSpy.clear();
timeSpy.clear();
project.addMapLayer( layer );
static_cast<QgsMeshLayerTemporalProperties *>( layer->temporalProperties() )->setReferenceTime(
QDateTime( QDate( 2020, 01, 01 ), QTime( 0, 0, 0 ), Qt::UTC ), layer->dataProvider()->temporalCapabilities() );
layerWrapper.setWidgetValue( meshLayerName, context );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 1 );
QCOMPARE( timeSpy.count(), 2 );
datasetGroupWidget->selectCurrentActiveDatasetGroup();
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 2 );
QCOMPARE( timeSpy.count(), 3 );
QVariant groupsValue = groupsWrapper.widgetValue();
QVERIFY( groupsValue.type() == QVariant::List );
QVariantList groupsList = groupsValue.toList();
QCOMPARE( groupsList.count(), 1 );
QCOMPARE( groupsList.at( 0 ).toInt(), 1 );
QString pythonString = groupsDefinition.valueAsPythonString( groupsValue, context );
QCOMPARE( pythonString, QStringLiteral( "[1]" ) );
QVERIFY( groupsDefinition.checkValueIsAcceptable( groupsValue ) );
QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsValue ), QList<int>( {1} ) );
// 2 datasets on vertices
settings = layer->rendererSettings();
settings.setActiveVectorDatasetGroup( 2 );
layer->setRendererSettings( settings );
datasetGroupWidget->selectCurrentActiveDatasetGroup();
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 3 );
QCOMPARE( timeSpy.count(), 4 );
pythonString = groupsDefinition.valueAsPythonString( groupsWrapper.widgetValue(), context );
QCOMPARE( pythonString, QStringLiteral( "[1,2]" ) );
QVERIFY( groupsDefinition.checkValueIsAcceptable( groupsWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsWrapper.widgetValue() ), QList<int>() << 1 << 2 );
datasetTimeWidget->radioButtonDatasetGroupTimeStep->setChecked( true );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 3 );
QCOMPARE( timeSpy.count(), 4 ); //radioButtonDatasetGroupTimeStep already checked
QVariant timeValue = timeWrapper.widgetValue();
QVERIFY( timeValue.type() == QVariant::Map );
QVariantMap timeValueMap = timeValue.toMap();
QCOMPARE( timeValueMap[QStringLiteral( "type" )].toString(), QStringLiteral( "dataset-time-step" ) );
pythonString = timeDefinition.valueAsPythonString( timeWrapper.widgetValue(), context );
QCOMPARE( pythonString, QStringLiteral( "{'type': 'dataset-time-step','value': QgsMeshDatasetIndex(1,0)}" ) );
QVERIFY( timeDefinition.checkValueIsAcceptable( timeValue ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeValue ), QStringLiteral( "dataset-time-step" ) );
QVERIFY( QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( timeValue ) == QgsMeshDatasetIndex( 1, 0 ) );
datasetTimeWidget->radioButtonDefinedDateTime->setChecked( true );
QDateTime dateTime = QDateTime( QDate( 2020, 1, 1 ), QTime( 0, 1, 0 ), Qt::UTC );
datasetTimeWidget->dateTimeEdit->setDateTime( dateTime );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 3 );
QCOMPARE( timeSpy.count(), 6 );
pythonString = timeDefinition.valueAsPythonString( timeWrapper.widgetValue(), context );
QCOMPARE( pythonString, QStringLiteral( "{'type': 'defined-date-time','value': QDateTime(QDate(2020, 1, 1), QTime(0, 1, 0))}" ) );
QVERIFY( timeDefinition.checkValueIsAcceptable( timeWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "defined-date-time" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( timeWrapper.widgetValue() ), dateTime );
QVERIFY( !datasetTimeWidget->radioButtonCurrentCanvasTime->isEnabled() );
mapCanvas->setTemporalRange( QgsDateTimeRange( QDateTime( QDate( 2021, 1, 1 ), QTime( 0, 3, 0 ), Qt::UTC ), QDateTime( QDate( 2020, 1, 1 ), QTime( 0, 5, 0 ), Qt::UTC ) ) );
QVERIFY( datasetTimeWidget->radioButtonCurrentCanvasTime->isEnabled() );
datasetTimeWidget->radioButtonCurrentCanvasTime->setChecked( true );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 3 );
QCOMPARE( timeSpy.count(), 8 );
pythonString = timeDefinition.valueAsPythonString( timeWrapper.widgetValue(), context );
QCOMPARE( pythonString, QStringLiteral( "{'type': 'current-context-time'}" ) );
QVERIFY( timeDefinition.checkValueIsAcceptable( timeWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "current-context-time" ) );
// 0 dataset on vertices
settings = layer->rendererSettings();
settings.setActiveScalarDatasetGroup( -1 );
settings.setActiveVectorDatasetGroup( -1 );
layer->setRendererSettings( settings );
datasetGroupWidget->selectCurrentActiveDatasetGroup();
QVERIFY( !datasetTimeWidget->isEnabled() );
pythonString = timeDefinition.valueAsPythonString( timeWrapper.widgetValue(), context );
QCOMPARE( pythonString, QStringLiteral( "{'type': 'static'}" ) );
QVERIFY( timeDefinition.checkValueIsAcceptable( timeWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "static" ) );
// 1 static dataset on vertices
settings = layer->rendererSettings();
settings.setActiveScalarDatasetGroup( 0 );
settings.setActiveVectorDatasetGroup( -1 );
layer->setRendererSettings( settings );
datasetGroupWidget->selectCurrentActiveDatasetGroup();
QVERIFY( !datasetTimeWidget->isEnabled() );
pythonString = timeDefinition.valueAsPythonString( timeWrapper.widgetValue(), context );
QCOMPARE( pythonString, QStringLiteral( "{'type': 'static'}" ) );
QVERIFY( timeDefinition.checkValueIsAcceptable( timeWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "static" ) );
}
void TestProcessingGui::testMeshDatasetWrapperLayerOutsideProject()
{
QgsProcessingParameterMeshLayer layerDefinition( QStringLiteral( "layer" ), QStringLiteral( "layer" ) );
QgsProcessingMeshLayerWidgetWrapper layerWrapper( &layerDefinition );
QgsProcessingParameterMeshDatasetGroups groupsDefinition( QStringLiteral( "groups" ),
QStringLiteral( "groups" ),
QStringLiteral( "layer" ),
QgsMeshDatasetGroupMetadata::DataOnFaces );
QgsProcessingMeshDatasetGroupsWidgetWrapper groupsWrapper( &groupsDefinition );
QgsProcessingParameterMeshDatasetTime timeDefinition( QStringLiteral( "time" ), QStringLiteral( "time" ), QStringLiteral( "layer" ), QStringLiteral( "groups" ) );
QgsProcessingMeshDatasetTimeWidgetWrapper timeWrapper( &timeDefinition );
QList<QgsAbstractProcessingParameterWidgetWrapper *> wrappers;
wrappers << &layerWrapper << &groupsWrapper << &timeWrapper;
QgsProject project;
QgsProcessingContext context;
context.setProject( &project );
QgsProcessingParameterWidgetContext widgetContext;
std::unique_ptr<QgsMapCanvas> mapCanvas = qgis::make_unique<QgsMapCanvas>();
widgetContext.setMapCanvas( mapCanvas.get() );
widgetContext.setProject( &project );
layerWrapper.setWidgetContext( widgetContext );
groupsWrapper.setWidgetContext( widgetContext );
timeWrapper.setWidgetContext( widgetContext );
TestProcessingContextGenerator generator( context );
layerWrapper.registerProcessingContextGenerator( &generator );
groupsWrapper.registerProcessingContextGenerator( &generator );
timeWrapper.registerProcessingContextGenerator( &generator );
QSignalSpy layerSpy( &layerWrapper, &QgsProcessingMeshLayerWidgetWrapper::widgetValueHasChanged );
QSignalSpy groupsSpy( &groupsWrapper, &QgsProcessingMeshDatasetGroupsWidgetWrapper::widgetValueHasChanged );
QSignalSpy timeSpy( &timeWrapper, &QgsProcessingMeshDatasetTimeWidgetWrapper::widgetValueHasChanged );
std::unique_ptr<QWidget> layerWidget( layerWrapper.createWrappedWidget( context ) );
std::unique_ptr<QWidget> groupWidget( groupsWrapper.createWrappedWidget( context ) );
std::unique_ptr<QWidget> timeWidget( timeWrapper.createWrappedWidget( context ) );
QgsProcessingMeshDatasetGroupsWidget *datasetGroupWidget = qobject_cast<QgsProcessingMeshDatasetGroupsWidget *>( groupWidget.get() );
QgsProcessingMeshDatasetTimeWidget *datasetTimeWidget = qobject_cast<QgsProcessingMeshDatasetTimeWidget *>( timeWidget.get() );
QVERIFY( layerWidget );
QVERIFY( groupWidget );
QVERIFY( datasetGroupWidget );
QVERIFY( timeWidget );
groupsWrapper.postInitialize( wrappers );
timeWrapper.postInitialize( wrappers );
layerSpy.clear();
groupsSpy.clear();
timeSpy.clear();
QString dataDir = QString( TEST_DATA_DIR ); //defined in CmakeLists.txt
QString meshOutOfProject( dataDir + "/mesh/trap_steady_05_3D.nc" );
layerWrapper.setWidgetValue( meshOutOfProject, context );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 1 );
QCOMPARE( timeSpy.count(), 1 );
QVariantList groups;
groups << 0;
groupsWrapper.setWidgetValue( groups, context );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 2 );
QCOMPARE( timeSpy.count(), 3 );
QVERIFY( groupsDefinition.checkValueIsAcceptable( groupsWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsWrapper.widgetValue() ), QList<int>() << 0 );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "static" ) );
QVERIFY( !datasetTimeWidget->isEnabled() );
groups << 11;
groupsWrapper.setWidgetValue( groups, context );
QCOMPARE( layerSpy.count(), 1 );
QCOMPARE( groupsSpy.count(), 3 );
QCOMPARE( timeSpy.count(), 5 );
QVERIFY( datasetTimeWidget->isEnabled() );
QVERIFY( groupsDefinition.checkValueIsAcceptable( groupsWrapper.widgetValue() ) );
QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsWrapper.widgetValue() ), QList<int>() << 0 << 11 );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "dataset-time-step" ) );
QVERIFY( QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( timeWrapper.widgetValue() ) == QgsMeshDatasetIndex( 11, 0 ) );
QVERIFY( datasetTimeWidget->radioButtonDefinedDateTime->isEnabled() );
QVERIFY( !datasetTimeWidget->radioButtonCurrentCanvasTime->isEnabled() );
datasetTimeWidget->radioButtonDefinedDateTime->setChecked( true );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( timeWrapper.widgetValue() ), QStringLiteral( "defined-date-time" ) );
QCOMPARE( QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( timeWrapper.widgetValue() ),
QDateTime( QDate( 1990, 1, 1 ), QTime( 0, 0, 0 ), Qt::UTC ) );
mapCanvas->setTemporalRange( QgsDateTimeRange( QDateTime( QDate( 2021, 1, 1 ), QTime( 0, 3, 0 ), Qt::UTC ), QDateTime( QDate( 2020, 1, 1 ), QTime( 0, 5, 0 ), Qt::UTC ) ) );
QVERIFY( datasetTimeWidget->radioButtonCurrentCanvasTime->isEnabled() );
}
void TestProcessingGui::testModelGraphicsView()
{
// test model