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