QGIS API Documentation 3.99.0-Master (752b475928d)
Loading...
Searching...
No Matches
qgsalgorithmexportmesh.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmexportmesh.cpp
3 ---------------------------
4 begin : October 2020
5 copyright : (C) 2020 by Vincent Cloarec
6 email : vcloarec at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgslinestring.h"
21#include "qgsmeshcontours.h"
22#include "qgsmeshdataset.h"
23#include "qgsmeshlayer.h"
26#include "qgsmeshlayerutils.h"
27#include "qgspolygon.h"
29#include "qgsrasterfilewriter.h"
30
31#include <QTextStream>
32
34
35
36static QgsFields createFields( const QList<QgsMeshDatasetGroupMetadata> &groupMetadataList, int vectorOption )
37{
38 QgsFields fields;
39 for ( const QgsMeshDatasetGroupMetadata &meta : groupMetadataList )
40 {
41 if ( meta.isVector() )
42 {
43 if ( vectorOption == 0 || vectorOption == 2 )
44 {
45 fields.append( QgsField( QStringLiteral( "%1_x" ).arg( meta.name() ), QMetaType::Type::Double ) );
46 fields.append( QgsField( QStringLiteral( "%1_y" ).arg( meta.name() ), QMetaType::Type::Double ) );
47 }
48
49 if ( vectorOption == 1 || vectorOption == 2 )
50 {
51 fields.append( QgsField( QStringLiteral( "%1_mag" ).arg( meta.name() ), QMetaType::Type::Double ) );
52 fields.append( QgsField( QStringLiteral( "%1_dir" ).arg( meta.name() ), QMetaType::Type::Double ) );
53 }
54 }
55 else
56 fields.append( QgsField( meta.name(), QMetaType::Type::Double ) );
57 }
58 return fields;
59}
60
61static QVector<double> vectorValue( const QgsMeshDatasetValue &value, int exportOption )
62{
63 QVector<double> ret( exportOption == 2 ? 4 : 2 );
64
65 if ( exportOption == 0 || exportOption == 2 )
66 {
67 ret[0] = value.x();
68 ret[1] = value.y();
69 }
70 if ( exportOption == 1 || exportOption == 2 )
71 {
72 double x = value.x();
73 double y = value.y();
74 double magnitude = sqrt( x * x + y * y );
75 double direction = ( asin( x / magnitude ) ) / M_PI * 180;
76 if ( y < 0 )
77 direction = 180 - direction;
78
79 if ( exportOption == 1 )
80 {
81 ret[0] = magnitude;
82 ret[1] = direction;
83 }
84 if ( exportOption == 2 )
85 {
86 ret[2] = magnitude;
87 ret[3] = direction;
88 }
89 }
90 return ret;
91}
92
93static void addAttributes( const QgsMeshDatasetValue &value, QgsAttributes &attributes, bool isVector, int vectorOption )
94{
95 if ( isVector )
96 {
97 QVector<double> vectorValues = vectorValue( value, vectorOption );
98 for ( double v : vectorValues )
99 {
100 if ( v == std::numeric_limits<double>::quiet_NaN() )
101 attributes.append( QVariant() );
102 else
103 attributes.append( v );
104 }
105 }
106 else
107 {
108 if ( value.scalar() == std::numeric_limits<double>::quiet_NaN() )
109 attributes.append( QVariant() );
110 else
111 attributes.append( value.scalar() );
112 }
113}
114
115static QgsMeshDatasetValue extractDatasetValue(
116 const QgsPointXY &point,
117 int nativeFaceIndex,
118 int triangularFaceIndex,
119 const QgsTriangularMesh &triangularMesh,
120 const QgsMeshDataBlock &activeFaces,
121 const QgsMeshDataBlock &datasetValues,
122 const QgsMeshDatasetGroupMetadata &metadata
123)
124{
125 bool faceActive = activeFaces.active( nativeFaceIndex );
127 if ( faceActive )
128 {
129 switch ( metadata.dataType() )
130 {
132 //not supported
133 break;
136 {
137 value = datasetValues.value( nativeFaceIndex );
138 }
139 break;
140
142 {
143 const QgsMeshFace &face = triangularMesh.triangles()[triangularFaceIndex];
144 const int v1 = face[0], v2 = face[1], v3 = face[2];
145 const QgsPoint p1 = triangularMesh.vertices()[v1], p2 = triangularMesh.vertices()[v2], p3 = triangularMesh.vertices()[v3];
146 const QgsMeshDatasetValue val1 = datasetValues.value( v1 );
147 const QgsMeshDatasetValue val2 = datasetValues.value( v2 );
148 const QgsMeshDatasetValue val3 = datasetValues.value( v3 );
149 const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
150 double y = std::numeric_limits<double>::quiet_NaN();
151 bool isVector = metadata.isVector();
152 if ( isVector )
153 y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
154
155 value = QgsMeshDatasetValue( x, y );
156 }
157 break;
158 }
159 }
160
161 return value;
162}
163
164QString QgsExportMeshOnElement::group() const
165{
166 return QObject::tr( "Mesh" );
167}
168
169QString QgsExportMeshOnElement::groupId() const
170{
171 return QStringLiteral( "mesh" );
172}
173
174QString QgsExportMeshVerticesAlgorithm::shortHelpString() const
175{
176 return QObject::tr( "This algorithm exports a mesh layer's vertices to a point vector layer, with the dataset values on vertices as attribute values." );
177}
178
179QString QgsExportMeshVerticesAlgorithm::shortDescription() const
180{
181 return QObject::tr( "Exports mesh vertices to a point vector layer." );
182}
183
184QString QgsExportMeshVerticesAlgorithm::name() const
185{
186 return QStringLiteral( "exportmeshvertices" );
187}
188
189QString QgsExportMeshVerticesAlgorithm::displayName() const
190{
191 return QObject::tr( "Export mesh vertices" );
192}
193
194QgsProcessingAlgorithm *QgsExportMeshVerticesAlgorithm::createInstance() const
195{
196 return new QgsExportMeshVerticesAlgorithm();
197}
198
199QgsGeometry QgsExportMeshVerticesAlgorithm::meshElement( int index ) const
200{
201 return QgsGeometry( new QgsPoint( mNativeMesh.vertex( index ) ) );
202}
203
204void QgsExportMeshOnElement::initAlgorithm( const QVariantMap &configuration )
205{
206 Q_UNUSED( configuration );
207
208 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
209
210
212 QStringLiteral( "DATASET_GROUPS" ),
213 QObject::tr( "Dataset groups" ),
214 QStringLiteral( "INPUT" ),
215 supportedDataType(), true
216 ) );
217
219 QStringLiteral( "DATASET_TIME" ),
220 QObject::tr( "Dataset time" ),
221 QStringLiteral( "INPUT" ),
222 QStringLiteral( "DATASET_GROUPS" )
223 ) );
224
225 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
226
227 QStringList exportVectorOptions;
228 exportVectorOptions << QObject::tr( "Cartesian (x,y)" )
229 << QObject::tr( "Polar (magnitude,degree)" )
230 << QObject::tr( "Cartesian and Polar" );
231 addParameter( new QgsProcessingParameterEnum( QStringLiteral( "VECTOR_OPTION" ), QObject::tr( "Export vector option" ), exportVectorOptions, false, 0 ) );
232 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output vector layer" ), sinkType() ) );
233}
234
235static QgsInterval datasetRelativetime( const QVariant parameterTimeVariant, QgsMeshLayer *meshLayer, const QgsProcessingContext &context )
236{
237 QgsInterval relativeTime( 0 );
238 QDateTime layerReferenceTime = static_cast<QgsMeshLayerTemporalProperties *>( meshLayer->temporalProperties() )->referenceTime();
239 QString timeType = QgsProcessingParameterMeshDatasetTime::valueAsTimeType( parameterTimeVariant );
240
241 if ( timeType == QLatin1String( "dataset-time-step" ) )
242 {
244 relativeTime = meshLayer->datasetRelativeTime( datasetIndex );
245 }
246 else if ( timeType == QLatin1String( "defined-date-time" ) )
247 {
248 QDateTime dateTime = QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( parameterTimeVariant );
249 if ( dateTime.isValid() )
250 relativeTime = QgsInterval( layerReferenceTime.secsTo( dateTime ) );
251 }
252 else if ( timeType == QLatin1String( "current-context-time" ) )
253 {
254 QDateTime dateTime = context.currentTimeRange().begin();
255 if ( dateTime.isValid() )
256 relativeTime = QgsInterval( layerReferenceTime.secsTo( dateTime ) );
257 }
258
259 return relativeTime;
260}
261
262
263bool QgsExportMeshOnElement::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
264{
265 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
266
267 if ( !meshLayer || !meshLayer->isValid() )
268 return false;
269
270 if ( meshLayer->isEditable() )
271 throw QgsProcessingException( QObject::tr( "Input mesh layer in edit mode is not supported" ) );
272
273 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
274 if ( !outputCrs.isValid() )
275 outputCrs = meshLayer->crs();
276 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
277 if ( !meshLayer->nativeMesh() )
278 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
279
280 mNativeMesh = *meshLayer->nativeMesh();
281
282 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
283
284 if ( feedback )
285 {
286 feedback->setProgressText( QObject::tr( "Preparing data" ) );
287 }
288
289 // Extract the date time used to export dataset values under a relative time
290 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
291 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
292
293 switch ( meshElementType() )
294 {
295 case QgsMesh::Face:
296 mElementCount = mNativeMesh.faceCount();
297 break;
298 case QgsMesh::Vertex:
299 mElementCount = mNativeMesh.vertexCount();
300 break;
301 case QgsMesh::Edge:
302 mElementCount = mNativeMesh.edgeCount();
303 break;
304 }
305
306 for ( int i = 0; i < datasetGroups.count(); ++i )
307 {
308 int groupIndex = datasetGroups.at( i );
309 QgsMeshDatasetIndex datasetIndex = meshLayer->datasetIndexAtRelativeTime( relativeTime, groupIndex );
310
311 DataGroup dataGroup;
312 dataGroup.metadata = meshLayer->datasetGroupMetadata( datasetIndex );
313 if ( supportedDataType().contains( dataGroup.metadata.dataType() ) )
314 {
315 dataGroup.datasetValues = meshLayer->datasetValues( datasetIndex, 0, mElementCount );
316 mDataPerGroup.append( dataGroup );
317 }
318 if ( feedback )
319 feedback->setProgress( 100 * i / datasetGroups.count() );
320 }
321
322 mExportVectorOption = parameterAsInt( parameters, QStringLiteral( "VECTOR_OPTION" ), context );
323
324 return true;
325}
326
327QVariantMap QgsExportMeshOnElement::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
328{
329 if ( feedback )
330 {
331 if ( feedback->isCanceled() )
332 return QVariantMap();
333 feedback->setProgress( 0 );
334 feedback->setProgressText( QObject::tr( "Creating output vector layer" ) );
335 }
336
337 QList<QgsMeshDatasetGroupMetadata> metaList;
338 metaList.reserve( mDataPerGroup.size() );
339 for ( const DataGroup &dataGroup : std::as_const( mDataPerGroup ) )
340 metaList.append( dataGroup.metadata );
341 QgsFields fields = createFields( metaList, mExportVectorOption );
342
343 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
344 QString identifier;
345 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, identifier, fields, sinkGeometryType(), outputCrs ) );
346 if ( !sink )
347 return QVariantMap();
348
349 if ( feedback )
350 {
351 if ( feedback->isCanceled() )
352 return QVariantMap();
353 feedback->setProgress( 0 );
354 feedback->setProgressText( QObject::tr( "Creating points for each vertices" ) );
355 }
356
357 for ( int i = 0; i < mElementCount; ++i )
358 {
359 QgsAttributes attributes;
360 for ( const DataGroup &dataGroup : std::as_const( mDataPerGroup ) )
361 {
362 const QgsMeshDatasetValue &value = dataGroup.datasetValues.value( i );
363 addAttributes( value, attributes, dataGroup.metadata.isVector(), mExportVectorOption );
364 }
365
366 QgsFeature feat;
367 QgsGeometry geom = meshElement( i );
368 try
369 {
370 geom.transform( mTransform );
371 }
372 catch ( QgsCsException & )
373 {
374 geom = meshElement( i );
375 if ( feedback )
376 feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
377 }
378 feat.setGeometry( geom );
379 feat.setAttributes( attributes );
380
381 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
382 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
383
384 if ( feedback )
385 {
386 if ( feedback->isCanceled() )
387 return QVariantMap();
388 feedback->setProgress( 100 * i / mElementCount );
389 }
390 }
391
392 sink->finalize();
393
394 QVariantMap ret;
395 ret[QStringLiteral( "OUTPUT" )] = identifier;
396
397 return ret;
398}
399
400QString QgsExportMeshFacesAlgorithm::shortHelpString() const
401{
402 return QObject::tr( "This algorithm exports a mesh layer's faces to a polygon vector layer, with the dataset values on faces as attribute values." );
403}
404
405QString QgsExportMeshFacesAlgorithm::shortDescription() const
406{
407 return QObject::tr( "Exports mesh faces to a polygon vector layer." );
408}
409
410QString QgsExportMeshFacesAlgorithm::name() const
411{
412 return QStringLiteral( "exportmeshfaces" );
413}
414
415QString QgsExportMeshFacesAlgorithm::displayName() const
416{
417 return QObject::tr( "Export mesh faces" );
418}
419
420QgsProcessingAlgorithm *QgsExportMeshFacesAlgorithm::createInstance() const
421{
422 return new QgsExportMeshFacesAlgorithm();
423}
424
425QgsGeometry QgsExportMeshFacesAlgorithm::meshElement( int index ) const
426{
427 const QgsMeshFace &face = mNativeMesh.face( index );
428 QVector<QgsPoint> vertices( face.size() );
429 for ( int i = 0; i < face.size(); ++i )
430 vertices[i] = mNativeMesh.vertex( face.at( i ) );
431 auto polygon = std::make_unique<QgsPolygon>();
432 polygon->setExteriorRing( new QgsLineString( vertices ) );
433 return QgsGeometry( polygon.release() );
434}
435
436QString QgsExportMeshEdgesAlgorithm::shortHelpString() const
437{
438 return QObject::tr( "This algorithm exports a mesh layer's edges to a line vector layer, with the dataset values on edges as attribute values." );
439}
440
441QString QgsExportMeshEdgesAlgorithm::shortDescription() const
442{
443 return QObject::tr( "Exports mesh edges to a line vector layer." );
444}
445
446QString QgsExportMeshEdgesAlgorithm::name() const
447{
448 return QStringLiteral( "exportmeshedges" );
449}
450
451QString QgsExportMeshEdgesAlgorithm::displayName() const
452{
453 return QObject::tr( "Export mesh edges" );
454}
455
456QgsProcessingAlgorithm *QgsExportMeshEdgesAlgorithm::createInstance() const
457{
458 return new QgsExportMeshEdgesAlgorithm();
459}
460
461QgsGeometry QgsExportMeshEdgesAlgorithm::meshElement( int index ) const
462{
463 const QgsMeshEdge &edge = mNativeMesh.edge( index );
464 QVector<QgsPoint> vertices( 2 );
465 vertices[0] = mNativeMesh.vertex( edge.first );
466 vertices[1] = mNativeMesh.vertex( edge.second );
467 return QgsGeometry( new QgsLineString( vertices ) );
468}
469
470
471QString QgsExportMeshOnGridAlgorithm::name() const { return QStringLiteral( "exportmeshongrid" ); }
472
473QString QgsExportMeshOnGridAlgorithm::displayName() const { return QObject::tr( "Export mesh on grid" ); }
474
475QString QgsExportMeshOnGridAlgorithm::group() const { return QObject::tr( "Mesh" ); }
476
477QString QgsExportMeshOnGridAlgorithm::groupId() const { return QStringLiteral( "mesh" ); }
478
479QString QgsExportMeshOnGridAlgorithm::shortHelpString() const
480{
481 return QObject::tr( "This algorithm exports a mesh layer's dataset values to a gridded point vector layer, with the dataset values on each point as attribute values.\n"
482 "For data on volume (3D stacked dataset values), the exported dataset values are averaged on faces using the method defined in the mesh layer properties (default is Multi level averaging method).\n"
483 "1D meshes are not supported." );
484}
485
486QString QgsExportMeshOnGridAlgorithm::shortDescription() const
487{
488 return QObject::tr( "Exports mesh dataset values to a gridded point vector layer." );
489}
490
491QgsProcessingAlgorithm *QgsExportMeshOnGridAlgorithm::createInstance() const
492{
493 return new QgsExportMeshOnGridAlgorithm();
494}
495
496void QgsExportMeshOnGridAlgorithm::initAlgorithm( const QVariantMap &configuration )
497{
498 Q_UNUSED( configuration );
499
500 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
501
503 QStringLiteral( "DATASET_GROUPS" ),
504 QObject::tr( "Dataset groups" ),
505 QStringLiteral( "INPUT" ),
506 supportedDataType()
507 ) );
508
510 QStringLiteral( "DATASET_TIME" ),
511 QObject::tr( "Dataset time" ),
512 QStringLiteral( "INPUT" ),
513 QStringLiteral( "DATASET_GROUPS" )
514 ) );
515
516 addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ), QVariant(), true ) );
517
518 addParameter( new QgsProcessingParameterDistance( QStringLiteral( "GRID_SPACING" ), QObject::tr( "Grid spacing" ), 10, QStringLiteral( "INPUT" ), false ) );
519
520 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
521
522 QStringList exportVectorOptions;
523 exportVectorOptions << QObject::tr( "Cartesian (x,y)" )
524 << QObject::tr( "Polar (magnitude,degree)" )
525 << QObject::tr( "Cartesian and Polar" );
526 addParameter( new QgsProcessingParameterEnum( QStringLiteral( "VECTOR_OPTION" ), QObject::tr( "Export vector option" ), exportVectorOptions, false, 0 ) );
527 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output vector layer" ), Qgis::ProcessingSourceType::VectorPoint ) );
528}
529
530static void extractDatasetValues( const QList<int> &datasetGroups, QgsMeshLayer *meshLayer, const QgsMesh &nativeMesh, const QgsInterval &relativeTime, const QSet<int> supportedDataType, QList<DataGroup> &datasetPerGroup, QgsProcessingFeedback *feedback )
531{
532 for ( int i = 0; i < datasetGroups.count(); ++i )
533 {
534 int groupIndex = datasetGroups.at( i );
535 QgsMeshDatasetIndex datasetIndex = meshLayer->datasetIndexAtRelativeTime( relativeTime, groupIndex );
536
537 DataGroup dataGroup;
538 dataGroup.metadata = meshLayer->datasetGroupMetadata( datasetIndex );
539 if ( supportedDataType.contains( dataGroup.metadata.dataType() ) )
540 {
541 int valueCount = dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ? nativeMesh.vertices.count() : nativeMesh.faceCount();
542 dataGroup.datasetValues = meshLayer->datasetValues( datasetIndex, 0, valueCount );
543 dataGroup.activeFaces = meshLayer->areFacesActive( datasetIndex, 0, nativeMesh.faceCount() );
544 if ( dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVolumes )
545 {
546 dataGroup.dataset3dStakedValue = meshLayer->dataset3dValues( datasetIndex, 0, valueCount );
547 }
548 datasetPerGroup.append( dataGroup );
549 }
550 if ( feedback )
551 feedback->setProgress( 100 * i / datasetGroups.count() );
552 }
553}
554
555bool QgsExportMeshOnGridAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
556{
557 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
558
559 if ( !meshLayer || !meshLayer->isValid() )
560 return false;
561
562 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
563 if ( !outputCrs.isValid() )
564 outputCrs = meshLayer->crs();
565 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
566 if ( !meshLayer->nativeMesh() )
567 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
568
569 const QgsMesh &nativeMesh = *meshLayer->nativeMesh();
570
571 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
572
573 if ( feedback )
574 {
575 feedback->setProgressText( QObject::tr( "Preparing data" ) );
576 }
577
578 // Extract the date time used to export dataset values under a relative time
579 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
580 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
581
582 extractDatasetValues( datasetGroups, meshLayer, nativeMesh, relativeTime, supportedDataType(), mDataPerGroup, feedback );
583 mTriangularMesh.update( meshLayer->nativeMesh(), mTransform );
584
585 mExportVectorOption = parameterAsInt( parameters, QStringLiteral( "VECTOR_OPTION" ), context );
586
587 return true;
588}
589
590QVariantMap QgsExportMeshOnGridAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
591{
592 if ( feedback )
593 {
594 if ( feedback->isCanceled() )
595 return QVariantMap();
596 feedback->setProgress( 0 );
597 feedback->setProgressText( QObject::tr( "Creating output vector layer" ) );
598 }
599
600 //First, if present, average 3D staked dataset value to 2D face value
601 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
602 for ( DataGroup &dataGroup : mDataPerGroup )
603 {
604 if ( dataGroup.dataset3dStakedValue.isValid() )
605 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
606 }
607
608 QList<QgsMeshDatasetGroupMetadata> metaList;
609 metaList.reserve( mDataPerGroup.size() );
610 for ( const DataGroup &dataGroup : std::as_const( mDataPerGroup ) )
611 metaList.append( dataGroup.metadata );
612 QgsFields fields = createFields( metaList, mExportVectorOption );
613
614 //create sink
615 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
616 QString identifier;
617 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, identifier, fields, Qgis::WkbType::Point, outputCrs ) );
618 if ( !sink )
619 return QVariantMap();
620
621 if ( feedback )
622 {
623 if ( feedback->isCanceled() )
624 return QVariantMap();
625 feedback->setProgress( 0 );
626 feedback->setProgressText( QObject::tr( "Creating gridded points" ) );
627 }
628
629 // grid definition
630 const double gridSpacing = parameterAsDouble( parameters, QStringLiteral( "GRID_SPACING" ), context );
631 if ( qgsDoubleNear( gridSpacing, 0 ) )
632 {
633 throw QgsProcessingException( QObject::tr( "Grid spacing cannot be 0" ) );
634 }
635
636 QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
637 if ( extent.isEmpty() )
638 extent = mTriangularMesh.extent();
639 int pointXCount = int( extent.width() / gridSpacing ) + 1;
640 int pointYCount = int( extent.height() / gridSpacing ) + 1;
641
642 for ( int ix = 0; ix < pointXCount; ++ix )
643 {
644 for ( int iy = 0; iy < pointYCount; ++iy )
645 {
646 QgsPoint point( extent.xMinimum() + ix * gridSpacing, extent.yMinimum() + iy * gridSpacing );
647 int triangularFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
648 if ( triangularFaceIndex >= 0 )
649 {
650 //extract dataset values for the point
651 QgsAttributes attributes;
652 int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces().at( triangularFaceIndex );
653 for ( int i = 0; i < mDataPerGroup.count(); ++i )
654 {
655 const DataGroup &dataGroup = mDataPerGroup.at( i );
656 bool faceActive = dataGroup.activeFaces.active( nativeFaceIndex );
657 if ( !faceActive )
658 continue;
659 QgsMeshDatasetValue value = extractDatasetValue(
660 point,
661 nativeFaceIndex,
662 triangularFaceIndex,
663 mTriangularMesh,
664 dataGroup.activeFaces,
665 dataGroup.datasetValues,
666 dataGroup.metadata
667 );
668
669 if ( dataGroup.metadata.isVector() )
670 {
671 QVector<double> vector = vectorValue( dataGroup.datasetValues.value( i ), mExportVectorOption );
672 for ( double v : vector )
673 {
674 attributes.append( v );
675 }
676 }
677 else
678 attributes.append( value.scalar() );
679 }
680 QgsFeature feat;
681 QgsGeometry geom( point.clone() );
682 try
683 {
684 geom.transform( mTransform );
685 }
686 catch ( QgsCsException & )
687 {
688 geom = QgsGeometry( point.clone() );
689 feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
690 }
691 feat.setGeometry( geom );
692 feat.setAttributes( attributes );
693
694 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
695 {
696 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QString() ) );
697 }
698 }
699 }
700 }
701
702 sink->finalize();
703
704 QVariantMap ret;
705 ret[QStringLiteral( "OUTPUT" )] = identifier;
706
707 return ret;
708}
709
710QSet<int> QgsExportMeshOnGridAlgorithm::supportedDataType()
711{
712 return QSet<int>(
716 );
717}
718
719QString QgsMeshRasterizeAlgorithm::name() const
720{
721 return QStringLiteral( "meshrasterize" );
722}
723
724QString QgsMeshRasterizeAlgorithm::displayName() const
725{
726 return QObject::tr( "Rasterize mesh dataset" );
727}
728
729QString QgsMeshRasterizeAlgorithm::group() const
730{
731 return QObject::tr( "Mesh" );
732}
733
734QString QgsMeshRasterizeAlgorithm::groupId() const
735{
736 return QStringLiteral( "mesh" );
737}
738
739QString QgsMeshRasterizeAlgorithm::shortHelpString() const
740{
741 return QObject::tr( "This algorithm creates a raster layer from a mesh dataset.\n"
742 "For data on volume (3D stacked dataset values), the exported dataset values are averaged on faces using the method defined in the mesh layer properties (default is Multi level averaging method).\n"
743 "1D meshes are not supported." );
744}
745
746QString QgsMeshRasterizeAlgorithm::shortDescription() const
747{
748 return QObject::tr( "Creates a raster layer from a mesh dataset." );
749}
750
751QgsProcessingAlgorithm *QgsMeshRasterizeAlgorithm::createInstance() const
752{
753 return new QgsMeshRasterizeAlgorithm();
754}
755
756void QgsMeshRasterizeAlgorithm::initAlgorithm( const QVariantMap &configuration )
757{
758 Q_UNUSED( configuration );
759
760 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
761
763 QStringLiteral( "DATASET_GROUPS" ),
764 QObject::tr( "Dataset groups" ),
765 QStringLiteral( "INPUT" ),
766 supportedDataType(),
767 true
768 ) );
769
771 QStringLiteral( "DATASET_TIME" ),
772 QObject::tr( "Dataset time" ),
773 QStringLiteral( "INPUT" ),
774 QStringLiteral( "DATASET_GROUPS" )
775 ) );
776
777 addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ), QVariant(), true ) );
778 addParameter( new QgsProcessingParameterDistance( QStringLiteral( "PIXEL_SIZE" ), QObject::tr( "Pixel size" ), 1, QStringLiteral( "INPUT" ), false ) );
779 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
780
781 // backwards compatibility parameter
782 // TODO QGIS 4: remove parameter and related logic
783 auto createOptsParam = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "CREATE_OPTIONS" ), QObject::tr( "Creation options" ), QVariant(), false, true );
784 createOptsParam->setMetadata( QVariantMap( { { QStringLiteral( "widget_wrapper" ), QVariantMap( { { QStringLiteral( "widget_type" ), QStringLiteral( "rasteroptions" ) } } ) } } ) );
785 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Hidden );
786 addParameter( createOptsParam.release() );
787
788 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "CREATION_OPTIONS" ), QObject::tr( "Creation options" ), QVariant(), false, true );
789 creationOptsParam->setMetadata( QVariantMap( { { QStringLiteral( "widget_wrapper" ), QVariantMap( { { QStringLiteral( "widget_type" ), QStringLiteral( "rasteroptions" ) } } ) } } ) );
790 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
791 addParameter( creationOptsParam.release() );
792
793 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output raster layer" ) ) );
794}
795
796bool QgsMeshRasterizeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
797{
798 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
799
800 if ( !meshLayer || !meshLayer->isValid() )
801 return false;
802
803 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
804 if ( !outputCrs.isValid() )
805 outputCrs = meshLayer->crs();
806 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
807 if ( !meshLayer->nativeMesh() )
808 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
809
810 mTriangularMesh.update( meshLayer->nativeMesh(), mTransform );
811
812 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
813
814 if ( feedback )
815 {
816 feedback->setProgressText( QObject::tr( "Preparing data" ) );
817 }
818
819 // Extract the date time used to export dataset values under a relative time
820 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
821 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
822
823 extractDatasetValues( datasetGroups, meshLayer, *meshLayer->nativeMesh(), relativeTime, supportedDataType(), mDataPerGroup, feedback );
824
825 mLayerRendererSettings = meshLayer->rendererSettings();
826
827 return true;
828}
829
830QVariantMap QgsMeshRasterizeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
831{
832 if ( feedback )
833 {
834 if ( feedback->isCanceled() )
835 return QVariantMap();
836 feedback->setProgress( 0 );
837 feedback->setProgressText( QObject::tr( "Creating raster layer" ) );
838 }
839
840 //First, if present, average 3D staked dataset value to 2D face value
841 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
842 for ( DataGroup &dataGroup : mDataPerGroup )
843 {
844 if ( dataGroup.dataset3dStakedValue.isValid() )
845 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
846 }
847
848 // create raster
849 const double pixelSize = parameterAsDouble( parameters, QStringLiteral( "PIXEL_SIZE" ), context );
850 if ( qgsDoubleNear( pixelSize, 0 ) )
851 {
852 throw QgsProcessingException( QObject::tr( "Pixel size cannot be 0" ) );
853 }
854
855 QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
856 if ( extent.isEmpty() )
857 extent = mTriangularMesh.extent();
858
859 int width = extent.width() / pixelSize;
860 int height = extent.height() / pixelSize;
861
862 QString creationOptions = parameterAsString( parameters, QStringLiteral( "CREATION_OPTIONS" ), context ).trimmed();
863 // handle backwards compatibility parameter CREATE_OPTIONS
864 const QString optionsString = parameterAsString( parameters, QStringLiteral( "CREATE_OPTIONS" ), context );
865 if ( !optionsString.isEmpty() )
866 creationOptions = optionsString;
867
868 const QString fileName = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
869 const QString outputFormat = parameterAsOutputRasterFormat( parameters, QStringLiteral( "OUTPUT" ), context );
870 QgsRasterFileWriter rasterFileWriter( fileName );
871 rasterFileWriter.setOutputProviderKey( QStringLiteral( "gdal" ) );
872 if ( !creationOptions.isEmpty() )
873 {
874 rasterFileWriter.setCreationOptions( creationOptions.split( '|' ) );
875 }
876 rasterFileWriter.setOutputFormat( outputFormat );
877
878 std::unique_ptr<QgsRasterDataProvider> rasterDataProvider(
879 rasterFileWriter.createMultiBandRaster( Qgis::DataType::Float64, width, height, extent, mTransform.destinationCrs(), mDataPerGroup.count() )
880 );
881 rasterDataProvider->setEditable( true );
882
883 for ( int i = 0; i < mDataPerGroup.count(); ++i )
884 {
885 const DataGroup &dataGroup = mDataPerGroup.at( i );
886 QgsRasterBlockFeedback rasterBlockFeedBack;
887 if ( feedback )
888 QObject::connect( &rasterBlockFeedBack, &QgsFeedback::canceled, feedback, &QgsFeedback::cancel );
889
890 if ( dataGroup.datasetValues.isValid() )
891 {
892 std::unique_ptr<QgsRasterBlock> block( QgsMeshUtils::exportRasterBlock(
893 mTriangularMesh,
894 dataGroup.datasetValues,
895 dataGroup.activeFaces,
896 dataGroup.metadata.dataType(),
897 mTransform,
898 pixelSize,
899 extent,
900 &rasterBlockFeedBack
901 ) );
902
903 if ( !rasterDataProvider->writeBlock( block.get(), i + 1 ) )
904 {
905 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( rasterDataProvider->error().summary() ) );
906 }
907 rasterDataProvider->setNoDataValue( i + 1, block->noDataValue() );
908 }
909 else
910 rasterDataProvider->setNoDataValue( i + 1, std::numeric_limits<double>::quiet_NaN() );
911
912 if ( feedback )
913 {
914 if ( feedback->isCanceled() )
915 return QVariantMap();
916 feedback->setProgress( 100 * i / mDataPerGroup.count() );
917 }
918 }
919
920 rasterDataProvider->setEditable( false );
921
922 if ( feedback )
923 feedback->setProgress( 100 );
924
925 QVariantMap ret;
926 ret[QStringLiteral( "OUTPUT" )] = fileName;
927
928 return ret;
929}
930
931QSet<int> QgsMeshRasterizeAlgorithm::supportedDataType()
932{
933 return QSet<int>(
937 );
938}
939
940QString QgsMeshContoursAlgorithm::name() const
941{
942 return QStringLiteral( "meshcontours" );
943}
944
945QString QgsMeshContoursAlgorithm::displayName() const
946{
947 return QObject::tr( "Export contours" );
948}
949
950QString QgsMeshContoursAlgorithm::group() const
951{
952 return QObject::tr( "Mesh" );
953}
954
955QString QgsMeshContoursAlgorithm::groupId() const
956{
957 return QStringLiteral( "mesh" );
958}
959
960QString QgsMeshContoursAlgorithm::shortHelpString() const
961{
962 return QObject::tr( "This algorithm creates contours as a vector layer from a mesh scalar dataset." );
963}
964
965QString QgsMeshContoursAlgorithm::shortDescription() const
966{
967 return QObject::tr( "Creates contours as vector layer from mesh scalar dataset." );
968}
969
970QgsProcessingAlgorithm *QgsMeshContoursAlgorithm::createInstance() const
971{
972 return new QgsMeshContoursAlgorithm();
973}
974
975void QgsMeshContoursAlgorithm::initAlgorithm( const QVariantMap &configuration )
976{
977 Q_UNUSED( configuration );
978
979 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
980
982 QStringLiteral( "DATASET_GROUPS" ),
983 QObject::tr( "Dataset groups" ),
984 QStringLiteral( "INPUT" ),
985 supportedDataType()
986 ) );
987
989 QStringLiteral( "DATASET_TIME" ),
990 QObject::tr( "Dataset time" ),
991 QStringLiteral( "INPUT" ),
992 QStringLiteral( "DATASET_GROUPS" )
993 ) );
994
995 addParameter( new QgsProcessingParameterNumber(
996 QStringLiteral( "INCREMENT" ), QObject::tr( "Increment between contour levels" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true
997 ) );
998
999 addParameter( new QgsProcessingParameterNumber(
1000 QStringLiteral( "MINIMUM" ), QObject::tr( "Minimum contour level" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true
1001 ) );
1002 addParameter( new QgsProcessingParameterNumber(
1003 QStringLiteral( "MAXIMUM" ), QObject::tr( "Maximum contour level" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true
1004 ) );
1005
1006 auto contourLevelList = std::make_unique<QgsProcessingParameterString>(
1007 QStringLiteral( "CONTOUR_LEVEL_LIST" ), QObject::tr( "List of contours level" ), QVariant(), false, true
1008 );
1009 contourLevelList->setHelp( QObject::tr( "Comma separated list of values to export. If filled, the increment, minimum and maximum settings are ignored." ) );
1010 addParameter( contourLevelList.release() );
1011
1012 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
1013
1014
1015 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT_LINES" ), QObject::tr( "Exported contour lines" ), Qgis::ProcessingSourceType::VectorLine ) );
1016 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT_POLYGONS" ), QObject::tr( "Exported contour polygons" ), Qgis::ProcessingSourceType::VectorPolygon ) );
1017}
1018
1019bool QgsMeshContoursAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1020{
1021 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
1022
1023 if ( !meshLayer || !meshLayer->isValid() )
1024 return false;
1025
1026 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
1027 if ( !outputCrs.isValid() )
1028 outputCrs = meshLayer->crs();
1029 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
1030 if ( !meshLayer->nativeMesh() )
1031 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
1032
1033 mTriangularMesh.update( meshLayer->nativeMesh(), mTransform );
1034 mNativeMesh = *meshLayer->nativeMesh();
1035
1036 // Prepare levels
1037 mLevels.clear();
1038 // First, try with the levels list
1039 QString levelsString = parameterAsString( parameters, QStringLiteral( "CONTOUR_LEVEL_LIST" ), context );
1040 if ( !levelsString.isEmpty() )
1041 {
1042 QStringList levelStringList = levelsString.split( ',' );
1043 if ( !levelStringList.isEmpty() )
1044 {
1045 for ( const QString &stringVal : levelStringList )
1046 {
1047 bool ok;
1048 double val = stringVal.toDouble( &ok );
1049 if ( ok )
1050 mLevels.append( val );
1051 else
1052 throw QgsProcessingException( QObject::tr( "Invalid format for level values, must be numbers separated with comma" ) );
1053
1054 if ( mLevels.count() >= 2 )
1055 if ( mLevels.last() <= mLevels.at( mLevels.count() - 2 ) )
1056 throw QgsProcessingException( QObject::tr( "Invalid format for level values, must be different numbers and in increasing order" ) );
1057 }
1058 }
1059 }
1060
1061 if ( mLevels.isEmpty() )
1062 {
1063 double minimum = parameterAsDouble( parameters, QStringLiteral( "MINIMUM" ), context );
1064 double maximum = parameterAsDouble( parameters, QStringLiteral( "MAXIMUM" ), context );
1065 double interval = parameterAsDouble( parameters, QStringLiteral( "INCREMENT" ), context );
1066
1067 if ( interval <= 0 )
1068 throw QgsProcessingException( QObject::tr( "Invalid interval value, must be greater than zero" ) );
1069
1070 if ( minimum >= maximum )
1071 throw QgsProcessingException( QObject::tr( "Invalid minimum and maximum values, minimum must be lesser than maximum" ) );
1072
1073 if ( interval > ( maximum - minimum ) )
1074 throw QgsProcessingException( QObject::tr( "Invalid minimum, maximum and interval values, difference between minimum and maximum must be greater or equal than interval" ) );
1075
1076 int intervalCount = ( maximum - minimum ) / interval;
1077
1078 mLevels.reserve( intervalCount );
1079 for ( int i = 0; i < intervalCount; ++i )
1080 {
1081 mLevels.append( minimum + i * interval );
1082 }
1083 }
1084
1085 // Prepare data
1086 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
1087
1088 if ( feedback )
1089 {
1090 feedback->setProgressText( QObject::tr( "Preparing data" ) );
1091 }
1092
1093 // Extract the date time used to export dataset values under a relative time
1094 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
1095 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
1096
1097 mDateTimeString = meshLayer->formatTime( relativeTime.hours() );
1098
1099 extractDatasetValues( datasetGroups, meshLayer, mNativeMesh, relativeTime, supportedDataType(), mDataPerGroup, feedback );
1100
1101 mLayerRendererSettings = meshLayer->rendererSettings();
1102
1103 return true;
1104}
1105
1106QVariantMap QgsMeshContoursAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1107{
1108 //First, if present, average 3D staked dataset value to 2D face value
1109 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
1110 for ( DataGroup &dataGroup : mDataPerGroup )
1111 {
1112 if ( dataGroup.dataset3dStakedValue.isValid() )
1113 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
1114 }
1115
1116 // Create vector layers
1117 QgsFields polygonFields;
1118 QgsFields lineFields;
1119 polygonFields.append( QgsField( QObject::tr( "group" ), QMetaType::Type::QString ) );
1120 polygonFields.append( QgsField( QObject::tr( "time" ), QMetaType::Type::QString ) );
1121 polygonFields.append( QgsField( QObject::tr( "min_value" ), QMetaType::Type::Double ) );
1122 polygonFields.append( QgsField( QObject::tr( "max_value" ), QMetaType::Type::Double ) );
1123 lineFields.append( QgsField( QObject::tr( "group" ), QMetaType::Type::QString ) );
1124 lineFields.append( QgsField( QObject::tr( "time" ), QMetaType::Type::QString ) );
1125 lineFields.append( QgsField( QObject::tr( "value" ), QMetaType::Type::Double ) );
1126
1127 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
1128
1129 QString lineIdentifier;
1130 QString polygonIdentifier;
1131 std::unique_ptr<QgsFeatureSink> sinkPolygons( parameterAsSink(
1132 parameters,
1133 QStringLiteral( "OUTPUT_POLYGONS" ),
1134 context,
1135 polygonIdentifier,
1136 polygonFields,
1138 outputCrs
1139 ) );
1140 std::unique_ptr<QgsFeatureSink> sinkLines( parameterAsSink(
1141 parameters,
1142 QStringLiteral( "OUTPUT_LINES" ),
1143 context,
1144 lineIdentifier,
1145 lineFields,
1147 outputCrs
1148 ) );
1149
1150 if ( !sinkLines || !sinkPolygons )
1151 return QVariantMap();
1152
1153
1154 for ( int i = 0; i < mDataPerGroup.count(); ++i )
1155 {
1156 DataGroup dataGroup = mDataPerGroup.at( i );
1157 bool scalarDataOnVertices = dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
1158 int count = scalarDataOnVertices ? mNativeMesh.vertices.count() : mNativeMesh.faces.count();
1159
1160 QVector<double> values;
1161 if ( dataGroup.datasetValues.isValid() )
1162 {
1163 // vals could be scalar or vectors, for contour rendering we want always magnitude
1164 values = QgsMeshLayerUtils::calculateMagnitudes( dataGroup.datasetValues );
1165 }
1166 else
1167 {
1168 values = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
1169 }
1170
1171 if ( ( !scalarDataOnVertices ) )
1172 {
1173 values = QgsMeshLayerUtils::interpolateFromFacesData(
1174 values,
1175 mNativeMesh,
1176 &dataGroup.activeFaces,
1178 );
1179 }
1180
1181 QgsMeshContours contoursExported( mTriangularMesh, mNativeMesh, values, dataGroup.activeFaces );
1182
1183 QgsAttributes firstAttributes;
1184 firstAttributes.append( dataGroup.metadata.name() );
1185 firstAttributes.append( mDateTimeString );
1186
1187 for ( double level : std::as_const( mLevels ) )
1188 {
1189 QgsGeometry line = contoursExported.exportLines( level, feedback );
1190 if ( feedback->isCanceled() )
1191 return QVariantMap();
1192 if ( line.isEmpty() )
1193 continue;
1194 QgsAttributes lineAttributes = firstAttributes;
1195 lineAttributes.append( level );
1196
1197 QgsFeature lineFeat;
1198 lineFeat.setGeometry( line );
1199 lineFeat.setAttributes( lineAttributes );
1200
1201 if ( !sinkLines->addFeature( lineFeat, QgsFeatureSink::FastInsert ) )
1202 throw QgsProcessingException( writeFeatureError( sinkLines.get(), parameters, QStringLiteral( "OUTPUT_LINES" ) ) );
1203 }
1204
1205 for ( int l = 0; l < mLevels.count() - 1; ++l )
1206 {
1207 QgsGeometry polygon = contoursExported.exportPolygons( mLevels.at( l ), mLevels.at( l + 1 ), feedback );
1208 if ( feedback->isCanceled() )
1209 return QVariantMap();
1210
1211 if ( polygon.isEmpty() )
1212 continue;
1213 QgsAttributes polygonAttributes = firstAttributes;
1214 polygonAttributes.append( mLevels.at( l ) );
1215 polygonAttributes.append( mLevels.at( l + 1 ) );
1216
1217 QgsFeature polygonFeature;
1218 polygonFeature.setGeometry( polygon );
1219 polygonFeature.setAttributes( polygonAttributes );
1220 sinkPolygons->addFeature( polygonFeature );
1221 }
1222
1223 if ( feedback )
1224 {
1225 feedback->setProgress( 100 * i / mDataPerGroup.count() );
1226 }
1227 }
1228
1229 if ( sinkPolygons )
1230 sinkPolygons->finalize();
1231 if ( sinkLines )
1232 sinkLines->finalize();
1233
1234 QVariantMap ret;
1235 ret[QStringLiteral( "OUTPUT_LINES" )] = lineIdentifier;
1236 ret[QStringLiteral( "OUTPUT_POLYGONS" )] = polygonIdentifier;
1237
1238 return ret;
1239}
1240
1241QString QgsMeshExportCrossSection::name() const
1242{
1243 return QStringLiteral( "meshexportcrosssection" );
1244}
1245
1246QString QgsMeshExportCrossSection::displayName() const
1247{
1248 return QObject::tr( "Export cross section dataset values on lines from mesh" );
1249}
1250
1251QString QgsMeshExportCrossSection::group() const
1252{
1253 return QObject::tr( "Mesh" );
1254}
1255
1256QString QgsMeshExportCrossSection::groupId() const
1257{
1258 return QStringLiteral( "mesh" );
1259}
1260
1261QString QgsMeshExportCrossSection::shortHelpString() const
1262{
1263 return QObject::tr( "This algorithm extracts mesh's dataset values from line contained in a vector layer.\n"
1264 "Each line is discretized with a resolution distance parameter for extraction of values on its vertices." );
1265}
1266
1267QString QgsMeshExportCrossSection::shortDescription() const
1268{
1269 return QObject::tr( "Extracts a mesh dataset's values from lines contained in a vector layer." );
1270}
1271
1272QgsProcessingAlgorithm *QgsMeshExportCrossSection::createInstance() const
1273{
1274 return new QgsMeshExportCrossSection();
1275}
1276
1277void QgsMeshExportCrossSection::initAlgorithm( const QVariantMap &configuration )
1278{
1279 Q_UNUSED( configuration );
1280
1281 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
1282
1284 QStringLiteral( "DATASET_GROUPS" ),
1285 QObject::tr( "Dataset groups" ),
1286 QStringLiteral( "INPUT" ),
1287 supportedDataType()
1288 ) );
1289
1290 addParameter( new QgsProcessingParameterMeshDatasetTime(
1291 QStringLiteral( "DATASET_TIME" ),
1292 QObject::tr( "Dataset time" ),
1293 QStringLiteral( "INPUT" ),
1294 QStringLiteral( "DATASET_GROUPS" )
1295 ) );
1296
1297 QList<int> datatype;
1298 datatype << static_cast<int>( Qgis::ProcessingSourceType::VectorLine );
1299 addParameter( new QgsProcessingParameterFeatureSource(
1300 QStringLiteral( "INPUT_LINES" ), QObject::tr( "Lines for data export" ), datatype, QVariant(), false
1301 ) );
1302
1303 addParameter( new QgsProcessingParameterDistance(
1304 QStringLiteral( "RESOLUTION" ), QObject::tr( "Line segmentation resolution" ), 10.0, QStringLiteral( "INPUT_LINES" ), false, 0
1305 ) );
1306
1307 addParameter( new QgsProcessingParameterNumber(
1308 QStringLiteral( "COORDINATES_DIGITS" ), QObject::tr( "Digits count for coordinates" ), Qgis::ProcessingNumberParameterType::Integer, 2
1309 ) );
1310
1311 addParameter( new QgsProcessingParameterNumber(
1312 QStringLiteral( "DATASET_DIGITS" ), QObject::tr( "Digits count for dataset value" ), Qgis::ProcessingNumberParameterType::Integer, 2
1313 ) );
1314
1315 addParameter( new QgsProcessingParameterFileDestination(
1316 QStringLiteral( "OUTPUT" ), QObject::tr( "Exported data CSV file" ), QObject::tr( "CSV file (*.csv)" )
1317 ) );
1318}
1319
1320bool QgsMeshExportCrossSection::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1321{
1322 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
1323
1324 if ( !meshLayer || !meshLayer->isValid() )
1325 return false;
1326
1327 mMeshLayerCrs = meshLayer->crs();
1328 mTriangularMesh.update( meshLayer->nativeMesh() );
1329 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
1330
1331 if ( feedback )
1332 {
1333 feedback->setProgressText( QObject::tr( "Preparing data" ) );
1334 }
1335
1336 // Extract the date time used to export dataset values under a relative time
1337 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
1338 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
1339
1340 extractDatasetValues( datasetGroups, meshLayer, *meshLayer->nativeMesh(), relativeTime, supportedDataType(), mDataPerGroup, feedback );
1341
1342 mLayerRendererSettings = meshLayer->rendererSettings();
1343
1344 return true;
1345}
1346
1347QVariantMap QgsMeshExportCrossSection::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1348{
1349 if ( feedback )
1350 feedback->setProgress( 0 );
1351 //First, if present, average 3D staked dataset value to 2D face value
1352 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
1353 for ( DataGroup &dataGroup : mDataPerGroup )
1354 {
1355 if ( dataGroup.dataset3dStakedValue.isValid() )
1356 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
1357 }
1358 double resolution = parameterAsDouble( parameters, QStringLiteral( "RESOLUTION" ), context );
1359 int datasetDigits = parameterAsInt( parameters, QStringLiteral( "DATASET_DIGITS" ), context );
1360 int coordDigits = parameterAsInt( parameters, QStringLiteral( "COORDINATES_DIGITS" ), context );
1361
1362 std::unique_ptr<QgsProcessingFeatureSource> featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT_LINES" ), context ) );
1363 if ( !featureSource )
1364 throw QgsProcessingException( QObject::tr( "Input lines vector layer required" ) );
1365
1366 QgsCoordinateTransform transform( featureSource->sourceCrs(), mMeshLayerCrs, context.transformContext() );
1367
1368 QString outputFileName = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );
1369 QFile file( outputFileName );
1370 if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1371 throw QgsProcessingException( QObject::tr( "Unable to create the output file" ) );
1372
1373 QTextStream textStream( &file );
1374#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1375 textStream.setCodec( "UTF-8" );
1376#endif
1377 QStringList header;
1378 header << QStringLiteral( "fid" ) << QStringLiteral( "x" ) << QStringLiteral( "y" ) << QObject::tr( "offset" );
1379 for ( const DataGroup &datagroup : std::as_const( mDataPerGroup ) )
1380 header << datagroup.metadata.name();
1381 textStream << header.join( ',' ) << QStringLiteral( "\n" );
1382
1383 long long featCount = featureSource->featureCount();
1384 long long featCounter = 0;
1385 QgsFeatureIterator featIt = featureSource->getFeatures();
1386 QgsFeature feat;
1387 while ( featIt.nextFeature( feat ) )
1388 {
1389 QgsFeatureId fid = feat.id();
1390 QgsGeometry line = feat.geometry();
1391 try
1392 {
1393 line.transform( transform );
1394 }
1395 catch ( QgsCsException & )
1396 {
1397 line = feat.geometry();
1398 feedback->reportError( QObject::tr( "Could not transform line to mesh CRS" ) );
1399 }
1400
1401 if ( line.isEmpty() )
1402 continue;
1403 double offset = 0;
1404 while ( offset <= line.length() )
1405 {
1406 if ( feedback->isCanceled() )
1407 return QVariantMap();
1408
1409 QStringList textLine;
1410 QgsPointXY point = line.interpolate( offset ).asPoint();
1411 int triangularFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
1412 textLine << QString::number( fid ) << QString::number( point.x(), 'f', coordDigits ) << QString::number( point.y(), 'f', coordDigits ) << QString::number( offset, 'f', coordDigits );
1413 if ( triangularFaceIndex >= 0 )
1414 {
1415 //extract dataset values for the point
1416 QgsAttributes attributes;
1417 int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces().at( triangularFaceIndex );
1418 for ( int i = 0; i < mDataPerGroup.count(); ++i )
1419 {
1420 const DataGroup &dataGroup = mDataPerGroup.at( i );
1421 bool faceActive = dataGroup.activeFaces.active( nativeFaceIndex );
1422 if ( !faceActive )
1423 continue;
1424 QgsMeshDatasetValue value = extractDatasetValue(
1425 point,
1426 nativeFaceIndex,
1427 triangularFaceIndex,
1428 mTriangularMesh,
1429 dataGroup.activeFaces,
1430 dataGroup.datasetValues,
1431 dataGroup.metadata
1432 );
1433
1434 if ( abs( value.x() ) == std::numeric_limits<double>::quiet_NaN() )
1435 textLine << QString( ' ' );
1436 else
1437 textLine << QString::number( value.scalar(), 'f', datasetDigits );
1438 }
1439 }
1440 else
1441 for ( int i = 0; i < mDataPerGroup.count(); ++i )
1442 textLine << QString( ' ' );
1443
1444 textStream << textLine.join( ',' ) << QStringLiteral( "\n" );
1445
1446 offset += resolution;
1447 }
1448
1449 if ( feedback )
1450 {
1451 feedback->setProgress( 100.0 * featCounter / featCount );
1452 if ( feedback->isCanceled() )
1453 return QVariantMap();
1454 }
1455 }
1456
1457 file.close();
1458
1459 QVariantMap ret;
1460 ret[QStringLiteral( "OUTPUT" )] = outputFileName;
1461 return ret;
1462}
1463
1464QString QgsMeshExportTimeSeries::name() const
1465{
1466 return QStringLiteral( "meshexporttimeseries" );
1467}
1468
1469QString QgsMeshExportTimeSeries::displayName() const
1470{
1471 return QObject::tr( "Export time series values from points of a mesh dataset" );
1472}
1473
1474QString QgsMeshExportTimeSeries::group() const
1475{
1476 return QObject::tr( "Mesh" );
1477}
1478
1479QString QgsMeshExportTimeSeries::groupId() const
1480{
1481 return QStringLiteral( "mesh" );
1482}
1483
1484QString QgsMeshExportTimeSeries::shortHelpString() const
1485{
1486 return QObject::tr( "This algorithm extracts mesh's dataset time series values from points contained in a vector layer.\n"
1487 "If the time step is kept to its default value (0 hours), the time step used is the one of the two first datasets of the first selected dataset group." );
1488}
1489
1490QString QgsMeshExportTimeSeries::shortDescription() const
1491{
1492 return QObject::tr( "Extracts a mesh dataset's time series values from points contained in a vector layer." );
1493}
1494
1495QgsProcessingAlgorithm *QgsMeshExportTimeSeries::createInstance() const
1496{
1497 return new QgsMeshExportTimeSeries();
1498}
1499
1500void QgsMeshExportTimeSeries::initAlgorithm( const QVariantMap &configuration )
1501{
1502 Q_UNUSED( configuration );
1503
1504 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
1505
1507 QStringLiteral( "DATASET_GROUPS" ),
1508 QObject::tr( "Dataset groups" ),
1509 QStringLiteral( "INPUT" ),
1510 supportedDataType()
1511 ) );
1512
1513 addParameter( new QgsProcessingParameterMeshDatasetTime(
1514 QStringLiteral( "STARTING_TIME" ),
1515 QObject::tr( "Starting time" ),
1516 QStringLiteral( "INPUT" ),
1517 QStringLiteral( "DATASET_GROUPS" )
1518 ) );
1519
1520 addParameter( new QgsProcessingParameterMeshDatasetTime(
1521 QStringLiteral( "FINISHING_TIME" ),
1522 QObject::tr( "Finishing time" ),
1523 QStringLiteral( "INPUT" ),
1524 QStringLiteral( "DATASET_GROUPS" )
1525 ) );
1526
1527 addParameter( new QgsProcessingParameterNumber(
1528 QStringLiteral( "TIME_STEP" ), QObject::tr( "Time step (hours)" ), Qgis::ProcessingNumberParameterType::Double, 0, true, 0
1529 ) );
1530
1531 QList<int> datatype;
1532 datatype << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint );
1533 addParameter( new QgsProcessingParameterFeatureSource(
1534 QStringLiteral( "INPUT_POINTS" ), QObject::tr( "Points for data export" ), datatype, QVariant(), false
1535 ) );
1536
1537 addParameter( new QgsProcessingParameterNumber(
1538 QStringLiteral( "COORDINATES_DIGITS" ), QObject::tr( "Digits count for coordinates" ), Qgis::ProcessingNumberParameterType::Integer, 2
1539 ) );
1540
1541 addParameter( new QgsProcessingParameterNumber(
1542 QStringLiteral( "DATASET_DIGITS" ), QObject::tr( "Digits count for dataset value" ), Qgis::ProcessingNumberParameterType::Integer, 2
1543 ) );
1544
1545 addParameter( new QgsProcessingParameterFileDestination(
1546 QStringLiteral( "OUTPUT" ), QObject::tr( "Exported data CSV file" ), QObject::tr( "CSV file (*.csv)" )
1547 ) );
1548}
1549
1550bool QgsMeshExportTimeSeries::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1551{
1552 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
1553
1554 if ( !meshLayer || !meshLayer->isValid() )
1555 return false;
1556
1557 mMeshLayerCrs = meshLayer->crs();
1558 mTriangularMesh.update( meshLayer->nativeMesh() );
1559
1560 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
1561
1562 if ( feedback )
1563 {
1564 feedback->setProgressText( QObject::tr( "Preparing data" ) );
1565 }
1566
1567 // Extract the date times used to export dataset values
1568 QVariant parameterStartTimeVariant = parameters.value( QStringLiteral( "STARTING_TIME" ) );
1569 QgsInterval relativeStartTime = datasetRelativetime( parameterStartTimeVariant, meshLayer, context );
1570
1571 QVariant parameterEndTimeVariant = parameters.value( QStringLiteral( "FINISHING_TIME" ) );
1572 QgsInterval relativeEndTime = datasetRelativetime( parameterEndTimeVariant, meshLayer, context );
1573
1574 // calculate time steps
1575 qint64 timeStepInterval = parameterAsDouble( parameters, QStringLiteral( "TIME_STEP" ), context ) * 1000 * 3600;
1576 if ( timeStepInterval == 0 )
1577 {
1578 //take the first time step of the first temporal dataset group
1579 for ( int groupIndex : datasetGroups )
1580 {
1581 QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( QgsMeshDatasetIndex( groupIndex, 0 ) );
1582 if ( !meta.isTemporal() && meshLayer->datasetCount( QgsMeshDatasetIndex( groupIndex, 0 ) ) < 2 )
1583 continue;
1584 else
1585 {
1586 timeStepInterval = meshLayer->datasetRelativeTimeInMilliseconds( QgsMeshDatasetIndex( groupIndex, 1 ) )
1587 - meshLayer->datasetRelativeTimeInMilliseconds( QgsMeshDatasetIndex( groupIndex, 0 ) );
1588 break;
1589 }
1590 }
1591 }
1592
1593 mRelativeTimeSteps.clear();
1594 mTimeStepString.clear();
1595 if ( timeStepInterval != 0 )
1596 {
1597 mRelativeTimeSteps.append( relativeStartTime.seconds() * 1000 );
1598 while ( mRelativeTimeSteps.last() < relativeEndTime.seconds() * 1000 )
1599 mRelativeTimeSteps.append( mRelativeTimeSteps.last() + timeStepInterval );
1600
1601 for ( qint64 relativeTimeStep : std::as_const( mRelativeTimeSteps ) )
1602 {
1603 mTimeStepString.append( meshLayer->formatTime( relativeTimeStep / 3600.0 / 1000.0 ) );
1604 }
1605 }
1606
1607 //Extract needed dataset values
1608 for ( int i = 0; i < datasetGroups.count(); ++i )
1609 {
1610 int groupIndex = datasetGroups.at( i );
1611 QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( QgsMeshDatasetIndex( groupIndex, 0 ) );
1612 if ( supportedDataType().contains( meta.dataType() ) )
1613 {
1614 mGroupIndexes.append( groupIndex );
1615 mGroupsMetadata[groupIndex] = meta;
1616 int valueCount = meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ? mTriangularMesh.vertices().count() : meshLayer->nativeMesh()->faceCount();
1617
1618 if ( !mRelativeTimeSteps.isEmpty() )
1619 {
1620 //QMap<qint64, DataGroup> temporalGroup;
1621 QgsMeshDatasetIndex lastDatasetIndex;
1622 for ( qint64 relativeTimeStep : std::as_const( mRelativeTimeSteps ) )
1623 {
1624 QMap<int, int> &groupIndexToData = mRelativeTimeToData[relativeTimeStep];
1625 QgsInterval timeStepInterval( relativeTimeStep / 1000.0 );
1626 QgsMeshDatasetIndex datasetIndex = meshLayer->datasetIndexAtRelativeTime( timeStepInterval, groupIndex );
1627 if ( !datasetIndex.isValid() )
1628 continue;
1629 if ( datasetIndex != lastDatasetIndex )
1630 {
1631 DataGroup dataGroup;
1632 dataGroup.metadata = meta;
1633 dataGroup.datasetValues = meshLayer->datasetValues( datasetIndex, 0, valueCount );
1634 dataGroup.activeFaces = meshLayer->areFacesActive( datasetIndex, 0, meshLayer->nativeMesh()->faceCount() );
1635 if ( dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVolumes )
1636 {
1637 dataGroup.dataset3dStakedValue = meshLayer->dataset3dValues( datasetIndex, 0, valueCount );
1638 }
1639 mDatasets.append( dataGroup );
1640 lastDatasetIndex = datasetIndex;
1641 }
1642 groupIndexToData[groupIndex] = mDatasets.count() - 1;
1643 }
1644 }
1645 else
1646 {
1647 // we have only static dataset group
1648 QMap<int, int> &groupIndexToData = mRelativeTimeToData[0];
1649 QgsMeshDatasetIndex datasetIndex( groupIndex, 0 );
1650 DataGroup dataGroup;
1651 dataGroup.metadata = meta;
1652 dataGroup.datasetValues = meshLayer->datasetValues( datasetIndex, 0, valueCount );
1653 dataGroup.activeFaces = meshLayer->areFacesActive( datasetIndex, 0, meshLayer->nativeMesh()->faceCount() );
1654 if ( dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVolumes )
1655 {
1656 dataGroup.dataset3dStakedValue = meshLayer->dataset3dValues( datasetIndex, 0, valueCount );
1657 }
1658 mDatasets.append( dataGroup );
1659 groupIndexToData[groupIndex] = mDatasets.count() - 1;
1660 }
1661 }
1662
1663 if ( feedback )
1664 feedback->setProgress( 100 * i / datasetGroups.count() );
1665 }
1666
1667 mLayerRendererSettings = meshLayer->rendererSettings();
1668
1669 return true;
1670}
1671
1672
1673QVariantMap QgsMeshExportTimeSeries::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1674{
1675 if ( feedback )
1676 feedback->setProgress( 0 );
1677 //First, if present, average 3D staked dataset value to 2D face value
1678 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
1679
1680 for ( DataGroup &dataGroup : mDatasets )
1681 {
1682 if ( dataGroup.dataset3dStakedValue.isValid() )
1683 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
1684 }
1685
1686 int datasetDigits = parameterAsInt( parameters, QStringLiteral( "DATASET_DIGITS" ), context );
1687 int coordDigits = parameterAsInt( parameters, QStringLiteral( "COORDINATES_DIGITS" ), context );
1688
1689 std::unique_ptr<QgsProcessingFeatureSource> featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT_POINTS" ), context ) );
1690 if ( !featureSource )
1691 throw QgsProcessingException( QObject::tr( "Input points vector layer required" ) );
1692
1693 QgsCoordinateTransform transform( featureSource->sourceCrs(), mMeshLayerCrs, context.transformContext() );
1694
1695 QString outputFileName = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );
1696 QFile file( outputFileName );
1697 if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1698 throw QgsProcessingException( QObject::tr( "Unable to create the output file" ) );
1699
1700 QTextStream textStream( &file );
1701#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1702 textStream.setCodec( "UTF-8" );
1703#endif
1704 QStringList header;
1705 header << QStringLiteral( "fid" ) << QStringLiteral( "x" ) << QStringLiteral( "y" ) << QObject::tr( "time" );
1706
1707 for ( int gi : std::as_const( mGroupIndexes ) )
1708 header << mGroupsMetadata.value( gi ).name();
1709
1710 textStream << header.join( ',' ) << QStringLiteral( "\n" );
1711
1712 long long featCount = featureSource->featureCount();
1713 long long featCounter = 0;
1714 QgsFeatureIterator featIt = featureSource->getFeatures();
1715 QgsFeature feat;
1716 while ( featIt.nextFeature( feat ) )
1717 {
1718 QgsFeatureId fid = feat.id();
1719 QgsGeometry geom = feat.geometry();
1720 try
1721 {
1722 geom.transform( transform );
1723 }
1724 catch ( QgsCsException & )
1725 {
1726 geom = feat.geometry();
1727 feedback->reportError( QObject::tr( "Could not transform line to mesh CRS" ) );
1728 }
1729
1730 if ( geom.isEmpty() )
1731 continue;
1732
1733 QgsPointXY point = geom.asPoint();
1734 int triangularFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
1735
1736 if ( triangularFaceIndex >= 0 )
1737 {
1738 int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces().at( triangularFaceIndex );
1739 if ( !mRelativeTimeSteps.isEmpty() )
1740 {
1741 for ( int timeIndex = 0; timeIndex < mRelativeTimeSteps.count(); ++timeIndex )
1742 {
1743 qint64 timeStep = mRelativeTimeSteps.at( timeIndex );
1744 QStringList textLine;
1745 textLine << QString::number( fid )
1746 << QString::number( point.x(), 'f', coordDigits )
1747 << QString::number( point.y(), 'f', coordDigits )
1748 << mTimeStepString.at( timeIndex );
1749
1750 if ( mRelativeTimeToData.contains( timeStep ) )
1751 {
1752 const QMap<int, int> &groupToData = mRelativeTimeToData.value( timeStep );
1753 for ( int groupIndex : std::as_const( mGroupIndexes ) )
1754 {
1755 if ( !groupToData.contains( groupIndex ) )
1756 continue;
1757 int dataIndex = groupToData.value( groupIndex );
1758 if ( dataIndex < 0 || dataIndex > mDatasets.count() - 1 )
1759 continue;
1760
1761 const DataGroup &dataGroup = mDatasets.at( dataIndex );
1762 QgsMeshDatasetValue value = extractDatasetValue( point, nativeFaceIndex, triangularFaceIndex, mTriangularMesh, dataGroup.activeFaces, dataGroup.datasetValues, dataGroup.metadata );
1763 if ( abs( value.x() ) == std::numeric_limits<double>::quiet_NaN() )
1764 textLine << QString( ' ' );
1765 else
1766 textLine << QString::number( value.scalar(), 'f', datasetDigits );
1767 }
1768 }
1769 textStream << textLine.join( ',' ) << QStringLiteral( "\n" );
1770 }
1771 }
1772 else
1773 {
1774 QStringList textLine;
1775 textLine << QString::number( fid )
1776 << QString::number( point.x(), 'f', coordDigits )
1777 << QString::number( point.y(), 'f', coordDigits )
1778 << QObject::tr( "static dataset" );
1779 const QMap<int, int> &groupToData = mRelativeTimeToData.value( 0 );
1780 for ( int groupIndex : std::as_const( mGroupIndexes ) )
1781 {
1782 if ( !groupToData.contains( groupIndex ) )
1783 continue;
1784 int dataIndex = groupToData.value( groupIndex );
1785 if ( dataIndex < 0 || dataIndex > mDatasets.count() - 1 )
1786 continue;
1787 const DataGroup &dataGroup = mDatasets.at( dataIndex );
1788 QgsMeshDatasetValue value = extractDatasetValue( point, nativeFaceIndex, triangularFaceIndex, mTriangularMesh, dataGroup.activeFaces, dataGroup.datasetValues, dataGroup.metadata );
1789 if ( abs( value.x() ) == std::numeric_limits<double>::quiet_NaN() )
1790 textLine << QString( ' ' );
1791 else
1792 textLine << QString::number( value.scalar(), 'f', datasetDigits );
1793 }
1794 textStream << textLine.join( ',' ) << QStringLiteral( "\n" );
1795 }
1796 }
1797 featCounter++;
1798 if ( feedback )
1799 {
1800 feedback->setProgress( 100.0 * featCounter / featCount );
1801 if ( feedback->isCanceled() )
1802 return QVariantMap();
1803 }
1804 }
1805
1806 file.close();
1807
1808 QVariantMap ret;
1809 ret[QStringLiteral( "OUTPUT" )] = outputFileName;
1810 return ret;
1811}
1812
@ VectorPoint
Vector point layers.
Definition qgis.h:3534
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3536
@ VectorLine
Vector line layers.
Definition qgis.h:3535
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:381
@ Point
Point.
Definition qgis.h:279
@ LineStringZ
LineStringZ.
Definition qgis.h:296
@ PolygonZ
PolygonZ.
Definition qgis.h:297
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3764
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3763
@ Double
Double/float values.
Definition qgis.h:3804
A vector of attributes.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFeatureId id
Definition qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:69
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void canceled()
Internal routines can connect to this signal if they use event loop.
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:54
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:73
A geometry is the spatial representation of a feature.
double length() const
Returns the planar, 2-dimensional length of geometry.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
A representation of the interval between two datetime values.
Definition qgsinterval.h:47
double seconds() const
Returns the interval duration in seconds.
double hours() const
Returns the interval duration in hours.
Line string geometry type, with support for z-dimension and m-values.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
Abstract class for interpolating 3d stacked mesh data to 2d data.
QgsMeshDataBlock calculate(const QgsMesh3DDataBlock &block3d, QgsFeedback *feedback=nullptr) const
Calculated 2d block values from 3d stacked mesh values.
Exporter of contours lines or polygons from a mesh layer.
A block of integers/doubles from a mesh dataset.
QgsMeshDatasetValue value(int index) const
Returns a value represented by the index For active flag the behavior is undefined.
bool active(int index) const
Returns a value for active flag by the index For scalar and vector 2d the behavior is undefined.
int count() const
Number of items stored in the block.
A collection of dataset group metadata such as whether the data is vector or scalar,...
bool isTemporal() const
Returns whether the dataset group is temporal (contains time-related dataset).
bool isVector() const
Returns whether dataset group has vector data.
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
@ DataOnEdges
Data is defined on edges.
@ DataOnFaces
Data is defined on faces.
@ DataOnVertices
Data is defined on vertices.
@ DataOnVolumes
Data is defined on volumes.
An index that identifies the dataset group (e.g.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
Represents a single mesh dataset value.
double y() const
Returns y value.
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
double x() const
Returns x value.
Implementation of map layer temporal properties for mesh layers.
QDateTime referenceTime() const
Returns the reference time.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
int datasetCount(const QgsMeshDatasetIndex &index) const
Returns the dataset count in the dataset groups.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
void updateTriangularMesh(const QgsCoordinateTransform &transform=QgsCoordinateTransform())
Gets native mesh and updates (creates if it doesn't exist) the base triangular mesh.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetIndex datasetIndexAtRelativeTime(const QgsInterval &relativeTime, int datasetGroupIndex) const
Returns dataset index from datasets group depending on the relative time from the layer reference tim...
QgsMeshDataBlock datasetValues(const QgsMeshDatasetIndex &index, int valueIndex, int count) const
Returns N vector/scalar values from the index from the dataset.
bool isEditable() const override
Returns true if the layer can be edited.
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
QgsInterval datasetRelativeTime(const QgsMeshDatasetIndex &index)
Returns the relative time of the dataset from the reference time of its group.
QgsMapLayerTemporalProperties * temporalProperties() override
Returns the layer's temporal properties.
qint64 datasetRelativeTimeInMilliseconds(const QgsMeshDatasetIndex &index)
Returns the relative time (in milliseconds) of the dataset from the reference time of its group.
QgsMesh3DDataBlock dataset3dValues(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns N vector/scalar values from the face index from the dataset for 3d stacked meshes.
QString formatTime(double hours)
Returns (date) time in hours formatted to human readable form.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
@ NeighbourAverage
Does a simple average of values defined for all surrounding faces/vertices.
static QgsRasterBlock * exportRasterBlock(const QgsMeshLayer &layer, const QgsMeshDatasetIndex &datasetIndex, const QgsCoordinateReferenceSystem &destinationCrs, const QgsCoordinateTransformContext &transformContext, double mapUnitsPerPixel, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Exports mesh layer's dataset values as raster block.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
QgsDateTimeRange currentTimeRange() const
Returns the current time range to use for temporal operations.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
A coordinate reference system parameter for processing algorithms.
A double numeric parameter for distance values.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A rectangular map extent parameter for processing algorithms.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
A parameter for processing algorithms that need a list of mesh dataset groups.
static QList< int > valueAsDatasetGroup(const QVariant &value)
Returns the value as a list if dataset group indexes.
A parameter for processing algorithms that need a list of mesh dataset index from time parameter.
static QString valueAsTimeType(const QVariant &value)
Returns the dataset value time type as a string : current-context-time : the time is store in the pro...
static QgsMeshDatasetIndex timeValueAsDatasetIndex(const QVariant &value)
Returns the value as a QgsMeshDatasetIndex if the value has "dataset-time-step" type.
static QDateTime timeValueAsDefinedDateTime(const QVariant &value)
Returns the value as a QDateTime if the value has "defined-date-time" type.
A mesh layer parameter for processing algorithms.
A numeric parameter for processing algorithms.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
Feedback object tailored for raster block reading.
The raster file writer which allows you to save a raster to a new file.
A rectangle specified with double values.
double xMinimum
double yMinimum
T begin() const
Returns the beginning of the range.
Definition qgsrange.h:446
A triangular/derived mesh with vertices in map coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
Mesh - vertices, edges and faces.
QVector< QgsMeshVertex > vertices
void clear()
Remove all vertices, edges and faces.
int faceCount() const
Returns number of faces.