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