QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgsalgorithmexportmesh.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmexportmesh.cpp
3 ---------------------------
4 begin : October 2020
5 copyright : (C) 2020 by Vincent Cloarec
6 email : vcloarec at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
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 std::unique_ptr<QgsPolygon> 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 double gridSpacing = parameterAsDouble( parameters, QStringLiteral( "GRID_SPACING" ), context );
630 QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
631 if ( extent.isEmpty() )
632 extent = mTriangularMesh.extent();
633 int pointXCount = int( extent.width() / gridSpacing ) + 1;
634 int pointYCount = int( extent.height() / gridSpacing ) + 1;
635
636 for ( int ix = 0; ix < pointXCount; ++ix )
637 {
638 for ( int iy = 0; iy < pointYCount; ++iy )
639 {
640 QgsPoint point( extent.xMinimum() + ix * gridSpacing, extent.yMinimum() + iy * gridSpacing );
641 int triangularFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
642 if ( triangularFaceIndex >= 0 )
643 {
644 //extract dataset values for the point
645 QgsAttributes attributes;
646 int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces().at( triangularFaceIndex );
647 for ( int i = 0; i < mDataPerGroup.count(); ++i )
648 {
649 const DataGroup &dataGroup = mDataPerGroup.at( i );
650 bool faceActive = dataGroup.activeFaces.active( nativeFaceIndex );
651 if ( !faceActive )
652 continue;
653 QgsMeshDatasetValue value = extractDatasetValue(
654 point,
655 nativeFaceIndex,
656 triangularFaceIndex,
657 mTriangularMesh,
658 dataGroup.activeFaces,
659 dataGroup.datasetValues,
660 dataGroup.metadata
661 );
662
663 if ( dataGroup.metadata.isVector() )
664 {
665 QVector<double> vector = vectorValue( dataGroup.datasetValues.value( i ), mExportVectorOption );
666 for ( double v : vector )
667 {
668 attributes.append( v );
669 }
670 }
671 else
672 attributes.append( value.scalar() );
673 }
674 QgsFeature feat;
675 QgsGeometry geom( point.clone() );
676 try
677 {
678 geom.transform( mTransform );
679 }
680 catch ( QgsCsException & )
681 {
682 geom = QgsGeometry( point.clone() );
683 feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) );
684 }
685 feat.setGeometry( geom );
686 feat.setAttributes( attributes );
687
688 sink->addFeature( feat );
689 }
690 }
691 }
692
693 sink->finalize();
694
695 QVariantMap ret;
696 ret[QStringLiteral( "OUTPUT" )] = identifier;
697
698 return ret;
699}
700
701QSet<int> QgsExportMeshOnGridAlgorithm::supportedDataType()
702{
703 return QSet<int>(
707 );
708}
709
710QString QgsMeshRasterizeAlgorithm::name() const
711{
712 return QStringLiteral( "meshrasterize" );
713}
714
715QString QgsMeshRasterizeAlgorithm::displayName() const
716{
717 return QObject::tr( "Rasterize mesh dataset" );
718}
719
720QString QgsMeshRasterizeAlgorithm::group() const
721{
722 return QObject::tr( "Mesh" );
723}
724
725QString QgsMeshRasterizeAlgorithm::groupId() const
726{
727 return QStringLiteral( "mesh" );
728}
729
730QString QgsMeshRasterizeAlgorithm::shortHelpString() const
731{
732 return QObject::tr( "This algorithm creates a raster layer from a mesh dataset.\n"
733 "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"
734 "1D meshes are not supported." );
735}
736
737QString QgsMeshRasterizeAlgorithm::shortDescription() const
738{
739 return QObject::tr( "Creates a raster layer from a mesh dataset" );
740}
741
742QgsProcessingAlgorithm *QgsMeshRasterizeAlgorithm::createInstance() const
743{
744 return new QgsMeshRasterizeAlgorithm();
745}
746
747void QgsMeshRasterizeAlgorithm::initAlgorithm( const QVariantMap &configuration )
748{
749 Q_UNUSED( configuration );
750
751 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
752
754 QStringLiteral( "DATASET_GROUPS" ),
755 QObject::tr( "Dataset groups" ),
756 QStringLiteral( "INPUT" ),
757 supportedDataType(),
758 true
759 ) );
760
762 QStringLiteral( "DATASET_TIME" ),
763 QObject::tr( "Dataset time" ),
764 QStringLiteral( "INPUT" ),
765 QStringLiteral( "DATASET_GROUPS" )
766 ) );
767
768 addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ), QVariant(), true ) );
769 addParameter( new QgsProcessingParameterDistance( QStringLiteral( "PIXEL_SIZE" ), QObject::tr( "Pixel size" ), 1, QStringLiteral( "INPUT" ), false ) );
770 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
771
772 std::unique_ptr<QgsProcessingParameterString> createOptsParam = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "CREATE_OPTIONS" ), QObject::tr( "Creation options" ), QVariant(), false, true );
773 createOptsParam->setMetadata( QVariantMap( { { QStringLiteral( "widget_wrapper" ), QVariantMap( { { QStringLiteral( "widget_type" ), QStringLiteral( "rasteroptions" ) } } ) } } ) );
774 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
775 addParameter( createOptsParam.release() );
776
777 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output raster layer" ) ) );
778}
779
780bool QgsMeshRasterizeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
781{
782 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
783
784 if ( !meshLayer || !meshLayer->isValid() )
785 return false;
786
787 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
788 if ( !outputCrs.isValid() )
789 outputCrs = meshLayer->crs();
790 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
791 if ( !meshLayer->nativeMesh() )
792 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
793
794 mTriangularMesh.update( meshLayer->nativeMesh(), mTransform );
795
796 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
797
798 if ( feedback )
799 {
800 feedback->setProgressText( QObject::tr( "Preparing data" ) );
801 }
802
803 // Extract the date time used to export dataset values under a relative time
804 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
805 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
806
807 extractDatasetValues( datasetGroups, meshLayer, *meshLayer->nativeMesh(), relativeTime, supportedDataType(), mDataPerGroup, feedback );
808
809 mLayerRendererSettings = meshLayer->rendererSettings();
810
811 return true;
812}
813
814QVariantMap QgsMeshRasterizeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
815{
816 if ( feedback )
817 {
818 if ( feedback->isCanceled() )
819 return QVariantMap();
820 feedback->setProgress( 0 );
821 feedback->setProgressText( QObject::tr( "Creating raster layer" ) );
822 }
823
824 //First, if present, average 3D staked dataset value to 2D face value
825 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
826 for ( DataGroup &dataGroup : mDataPerGroup )
827 {
828 if ( dataGroup.dataset3dStakedValue.isValid() )
829 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
830 }
831
832 // create raster
833 double pixelSize = parameterAsDouble( parameters, QStringLiteral( "PIXEL_SIZE" ), context );
834 QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
835 if ( extent.isEmpty() )
836 extent = mTriangularMesh.extent();
837
838 int width = extent.width() / pixelSize;
839 int height = extent.height() / pixelSize;
840
841 const QString createOptions = parameterAsString( parameters, QStringLiteral( "CREATE_OPTIONS" ), context ).trimmed();
842 const QString fileName = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
843 const QFileInfo fileInfo( fileName );
844 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fileInfo.suffix() );
845 QgsRasterFileWriter rasterFileWriter( fileName );
846 rasterFileWriter.setOutputProviderKey( QStringLiteral( "gdal" ) );
847 if ( !createOptions.isEmpty() )
848 {
849 rasterFileWriter.setCreateOptions( createOptions.split( '|' ) );
850 }
851 rasterFileWriter.setOutputFormat( outputFormat );
852
853 std::unique_ptr<QgsRasterDataProvider> rasterDataProvider(
854 rasterFileWriter.createMultiBandRaster( Qgis::DataType::Float64, width, height, extent, mTransform.destinationCrs(), mDataPerGroup.count() )
855 );
856 rasterDataProvider->setEditable( true );
857
858 for ( int i = 0; i < mDataPerGroup.count(); ++i )
859 {
860 const DataGroup &dataGroup = mDataPerGroup.at( i );
861 QgsRasterBlockFeedback rasterBlockFeedBack;
862 if ( feedback )
863 QObject::connect( &rasterBlockFeedBack, &QgsFeedback::canceled, feedback, &QgsFeedback::cancel );
864
865 if ( dataGroup.datasetValues.isValid() )
866 {
867 std::unique_ptr<QgsRasterBlock> block( QgsMeshUtils::exportRasterBlock(
868 mTriangularMesh,
869 dataGroup.datasetValues,
870 dataGroup.activeFaces,
871 dataGroup.metadata.dataType(),
872 mTransform,
873 pixelSize,
874 extent,
875 &rasterBlockFeedBack
876 ) );
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>(
909 );
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 ) );
959
961 QStringLiteral( "DATASET_TIME" ),
962 QObject::tr( "Dataset time" ),
963 QStringLiteral( "INPUT" ),
964 QStringLiteral( "DATASET_GROUPS" )
965 ) );
966
967 addParameter( new QgsProcessingParameterNumber(
968 QStringLiteral( "INCREMENT" ), QObject::tr( "Increment between contour levels" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true
969 ) );
970
971 addParameter( new QgsProcessingParameterNumber(
972 QStringLiteral( "MINIMUM" ), QObject::tr( "Minimum contour level" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true
973 ) );
974 addParameter( new QgsProcessingParameterNumber(
975 QStringLiteral( "MAXIMUM" ), QObject::tr( "Maximum contour level" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true
976 ) );
977
978 std::unique_ptr<QgsProcessingParameterString> contourLevelList = std::make_unique<QgsProcessingParameterString>(
979 QStringLiteral( "CONTOUR_LEVEL_LIST" ), QObject::tr( "List of contours level" ), QVariant(), false, true
980 );
981 contourLevelList->setHelp( QObject::tr( "Comma separated list of values to export. If filled, the increment, minimum and maximum settings are ignored." ) );
982 addParameter( contourLevelList.release() );
983
984 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS_OUTPUT" ), QObject::tr( "Output coordinate system" ), QVariant(), true ) );
985
986
987 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT_LINES" ), QObject::tr( "Exported contour lines" ), Qgis::ProcessingSourceType::VectorLine ) );
988 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT_POLYGONS" ), QObject::tr( "Exported contour polygons" ), Qgis::ProcessingSourceType::VectorPolygon ) );
989}
990
991bool QgsMeshContoursAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
992{
993 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
994
995 if ( !meshLayer || !meshLayer->isValid() )
996 return false;
997
998 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
999 if ( !outputCrs.isValid() )
1000 outputCrs = meshLayer->crs();
1001 mTransform = QgsCoordinateTransform( meshLayer->crs(), outputCrs, context.transformContext() );
1002 if ( !meshLayer->nativeMesh() )
1003 meshLayer->updateTriangularMesh( mTransform ); //necessary to load the native mesh
1004
1005 mTriangularMesh.update( meshLayer->nativeMesh(), mTransform );
1006 mNativeMesh = *meshLayer->nativeMesh();
1007
1008 // Prepare levels
1009 mLevels.clear();
1010 // First, try with the levels list
1011 QString levelsString = parameterAsString( parameters, QStringLiteral( "CONTOUR_LEVEL_LIST" ), context );
1012 if ( !levelsString.isEmpty() )
1013 {
1014 QStringList levelStringList = levelsString.split( ',' );
1015 if ( !levelStringList.isEmpty() )
1016 {
1017 for ( const QString &stringVal : levelStringList )
1018 {
1019 bool ok;
1020 double val = stringVal.toDouble( &ok );
1021 if ( ok )
1022 mLevels.append( val );
1023 else
1024 throw QgsProcessingException( QObject::tr( "Invalid format for level values, must be numbers separated with comma" ) );
1025
1026 if ( mLevels.count() >= 2 )
1027 if ( mLevels.last() <= mLevels.at( mLevels.count() - 2 ) )
1028 throw QgsProcessingException( QObject::tr( "Invalid format for level values, must be different numbers and in increasing order" ) );
1029 }
1030 }
1031 }
1032
1033 if ( mLevels.isEmpty() )
1034 {
1035 double minimum = parameterAsDouble( parameters, QStringLiteral( "MINIMUM" ), context );
1036 double maximum = parameterAsDouble( parameters, QStringLiteral( "MAXIMUM" ), context );
1037 double interval = parameterAsDouble( parameters, QStringLiteral( "INCREMENT" ), context );
1038
1039 if ( interval <= 0 )
1040 throw QgsProcessingException( QObject::tr( "Invalid interval value, must be greater than zero" ) );
1041
1042 if ( minimum >= maximum )
1043 throw QgsProcessingException( QObject::tr( "Invalid minimum and maximum values, minimum must be lesser than maximum" ) );
1044
1045 if ( interval > ( maximum - minimum ) )
1046 throw QgsProcessingException( QObject::tr( "Invalid minimum, maximum and interval values, difference between minimum and maximum must be greater or equal than interval" ) );
1047
1048 int intervalCount = ( maximum - minimum ) / interval;
1049
1050 mLevels.reserve( intervalCount );
1051 for ( int i = 0; i < intervalCount; ++i )
1052 {
1053 mLevels.append( minimum + i * interval );
1054 }
1055 }
1056
1057 // Prepare data
1058 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
1059
1060 if ( feedback )
1061 {
1062 feedback->setProgressText( QObject::tr( "Preparing data" ) );
1063 }
1064
1065 // Extract the date time used to export dataset values under a relative time
1066 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
1067 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
1068
1069 mDateTimeString = meshLayer->formatTime( relativeTime.hours() );
1070
1071 extractDatasetValues( datasetGroups, meshLayer, mNativeMesh, relativeTime, supportedDataType(), mDataPerGroup, feedback );
1072
1073 mLayerRendererSettings = meshLayer->rendererSettings();
1074
1075 return true;
1076}
1077
1078QVariantMap QgsMeshContoursAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1079{
1080 //First, if present, average 3D staked dataset value to 2D face value
1081 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
1082 for ( DataGroup &dataGroup : mDataPerGroup )
1083 {
1084 if ( dataGroup.dataset3dStakedValue.isValid() )
1085 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
1086 }
1087
1088 // Create vector layers
1089 QgsFields polygonFields;
1090 QgsFields lineFields;
1091 polygonFields.append( QgsField( QObject::tr( "group" ), QMetaType::Type::QString ) );
1092 polygonFields.append( QgsField( QObject::tr( "time" ), QMetaType::Type::QString ) );
1093 polygonFields.append( QgsField( QObject::tr( "min_value" ), QMetaType::Type::Double ) );
1094 polygonFields.append( QgsField( QObject::tr( "max_value" ), QMetaType::Type::Double ) );
1095 lineFields.append( QgsField( QObject::tr( "group" ), QMetaType::Type::QString ) );
1096 lineFields.append( QgsField( QObject::tr( "time" ), QMetaType::Type::QString ) );
1097 lineFields.append( QgsField( QObject::tr( "value" ), QMetaType::Type::Double ) );
1098
1099 QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS_OUTPUT" ), context );
1100
1101 QString lineIdentifier;
1102 QString polygonIdentifier;
1103 std::unique_ptr<QgsFeatureSink> sinkPolygons( parameterAsSink(
1104 parameters,
1105 QStringLiteral( "OUTPUT_POLYGONS" ),
1106 context,
1107 polygonIdentifier,
1108 polygonFields,
1110 outputCrs
1111 ) );
1112 std::unique_ptr<QgsFeatureSink> sinkLines( parameterAsSink(
1113 parameters,
1114 QStringLiteral( "OUTPUT_LINES" ),
1115 context,
1116 lineIdentifier,
1117 lineFields,
1119 outputCrs
1120 ) );
1121
1122 if ( !sinkLines || !sinkPolygons )
1123 return QVariantMap();
1124
1125
1126 for ( int i = 0; i < mDataPerGroup.count(); ++i )
1127 {
1128 DataGroup dataGroup = mDataPerGroup.at( i );
1129 bool scalarDataOnVertices = dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
1130 int count = scalarDataOnVertices ? mNativeMesh.vertices.count() : mNativeMesh.faces.count();
1131
1132 QVector<double> values;
1133 if ( dataGroup.datasetValues.isValid() )
1134 {
1135 // vals could be scalar or vectors, for contour rendering we want always magnitude
1136 values = QgsMeshLayerUtils::calculateMagnitudes( dataGroup.datasetValues );
1137 }
1138 else
1139 {
1140 values = QVector<double>( count, std::numeric_limits<double>::quiet_NaN() );
1141 }
1142
1143 if ( ( !scalarDataOnVertices ) )
1144 {
1145 values = QgsMeshLayerUtils::interpolateFromFacesData(
1146 values,
1147 mNativeMesh,
1148 &dataGroup.activeFaces,
1150 );
1151 }
1152
1153 QgsMeshContours contoursExported( mTriangularMesh, mNativeMesh, values, dataGroup.activeFaces );
1154
1155 QgsAttributes firstAttributes;
1156 firstAttributes.append( dataGroup.metadata.name() );
1157 firstAttributes.append( mDateTimeString );
1158
1159 for ( double level : std::as_const( mLevels ) )
1160 {
1161 QgsGeometry line = contoursExported.exportLines( level, feedback );
1162 if ( feedback->isCanceled() )
1163 return QVariantMap();
1164 if ( line.isEmpty() )
1165 continue;
1166 QgsAttributes lineAttributes = firstAttributes;
1167 lineAttributes.append( level );
1168
1169 QgsFeature lineFeat;
1170 lineFeat.setGeometry( line );
1171 lineFeat.setAttributes( lineAttributes );
1172
1173 if ( !sinkLines->addFeature( lineFeat, QgsFeatureSink::FastInsert ) )
1174 throw QgsProcessingException( writeFeatureError( sinkLines.get(), parameters, QStringLiteral( "OUTPUT_LINES" ) ) );
1175 }
1176
1177 for ( int l = 0; l < mLevels.count() - 1; ++l )
1178 {
1179 QgsGeometry polygon = contoursExported.exportPolygons( mLevels.at( l ), mLevels.at( l + 1 ), feedback );
1180 if ( feedback->isCanceled() )
1181 return QVariantMap();
1182
1183 if ( polygon.isEmpty() )
1184 continue;
1185 QgsAttributes polygonAttributes = firstAttributes;
1186 polygonAttributes.append( mLevels.at( l ) );
1187 polygonAttributes.append( mLevels.at( l + 1 ) );
1188
1189 QgsFeature polygonFeature;
1190 polygonFeature.setGeometry( polygon );
1191 polygonFeature.setAttributes( polygonAttributes );
1192 sinkPolygons->addFeature( polygonFeature );
1193 }
1194
1195 if ( feedback )
1196 {
1197 feedback->setProgress( 100 * i / mDataPerGroup.count() );
1198 }
1199 }
1200
1201 if ( sinkPolygons )
1202 sinkPolygons->finalize();
1203 if ( sinkLines )
1204 sinkLines->finalize();
1205
1206 QVariantMap ret;
1207 ret[QStringLiteral( "OUTPUT_LINES" )] = lineIdentifier;
1208 ret[QStringLiteral( "OUTPUT_POLYGONS" )] = polygonIdentifier;
1209
1210 return ret;
1211}
1212
1213QString QgsMeshExportCrossSection::name() const
1214{
1215 return QStringLiteral( "meshexportcrosssection" );
1216}
1217
1218QString QgsMeshExportCrossSection::displayName() const
1219{
1220 return QObject::tr( "Export cross section dataset values on lines from mesh" );
1221}
1222
1223QString QgsMeshExportCrossSection::group() const
1224{
1225 return QObject::tr( "Mesh" );
1226}
1227
1228QString QgsMeshExportCrossSection::groupId() const
1229{
1230 return QStringLiteral( "mesh" );
1231}
1232
1233QString QgsMeshExportCrossSection::shortHelpString() const
1234{
1235 return QObject::tr( "This algorithm extracts mesh's dataset values from line contained in a vector layer.\n"
1236 "Each line is discretized with a resolution distance parameter for extraction of values on its vertices." );
1237}
1238
1239QString QgsMeshExportCrossSection::shortDescription() const
1240{
1241 return QObject::tr( "Extracts a mesh dataset's values from lines contained in a vector layer" );
1242}
1243
1244QgsProcessingAlgorithm *QgsMeshExportCrossSection::createInstance() const
1245{
1246 return new QgsMeshExportCrossSection();
1247}
1248
1249void QgsMeshExportCrossSection::initAlgorithm( const QVariantMap &configuration )
1250{
1251 Q_UNUSED( configuration );
1252
1253 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
1254
1256 QStringLiteral( "DATASET_GROUPS" ),
1257 QObject::tr( "Dataset groups" ),
1258 QStringLiteral( "INPUT" ),
1259 supportedDataType()
1260 ) );
1261
1262 addParameter( new QgsProcessingParameterMeshDatasetTime(
1263 QStringLiteral( "DATASET_TIME" ),
1264 QObject::tr( "Dataset time" ),
1265 QStringLiteral( "INPUT" ),
1266 QStringLiteral( "DATASET_GROUPS" )
1267 ) );
1268
1269 QList<int> datatype;
1270 datatype << static_cast<int>( Qgis::ProcessingSourceType::VectorLine );
1271 addParameter( new QgsProcessingParameterFeatureSource(
1272 QStringLiteral( "INPUT_LINES" ), QObject::tr( "Lines for data export" ), datatype, QVariant(), false
1273 ) );
1274
1275 addParameter( new QgsProcessingParameterDistance(
1276 QStringLiteral( "RESOLUTION" ), QObject::tr( "Line segmentation resolution" ), 10.0, QStringLiteral( "INPUT_LINES" ), false, 0
1277 ) );
1278
1279 addParameter( new QgsProcessingParameterNumber(
1280 QStringLiteral( "COORDINATES_DIGITS" ), QObject::tr( "Digits count for coordinates" ), Qgis::ProcessingNumberParameterType::Integer, 2
1281 ) );
1282
1283 addParameter( new QgsProcessingParameterNumber(
1284 QStringLiteral( "DATASET_DIGITS" ), QObject::tr( "Digits count for dataset value" ), Qgis::ProcessingNumberParameterType::Integer, 2
1285 ) );
1286
1287 addParameter( new QgsProcessingParameterFileDestination(
1288 QStringLiteral( "OUTPUT" ), QObject::tr( "Exported data CSV file" ), QObject::tr( "CSV file (*.csv)" )
1289 ) );
1290}
1291
1292bool QgsMeshExportCrossSection::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1293{
1294 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
1295
1296 if ( !meshLayer || !meshLayer->isValid() )
1297 return false;
1298
1299 mMeshLayerCrs = meshLayer->crs();
1300 mTriangularMesh.update( meshLayer->nativeMesh() );
1301 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
1302
1303 if ( feedback )
1304 {
1305 feedback->setProgressText( QObject::tr( "Preparing data" ) );
1306 }
1307
1308 // Extract the date time used to export dataset values under a relative time
1309 QVariant parameterTimeVariant = parameters.value( QStringLiteral( "DATASET_TIME" ) );
1310 QgsInterval relativeTime = datasetRelativetime( parameterTimeVariant, meshLayer, context );
1311
1312 extractDatasetValues( datasetGroups, meshLayer, *meshLayer->nativeMesh(), relativeTime, supportedDataType(), mDataPerGroup, feedback );
1313
1314 mLayerRendererSettings = meshLayer->rendererSettings();
1315
1316 return true;
1317}
1318
1319QVariantMap QgsMeshExportCrossSection::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1320{
1321 if ( feedback )
1322 feedback->setProgress( 0 );
1323 //First, if present, average 3D staked dataset value to 2D face value
1324 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
1325 for ( DataGroup &dataGroup : mDataPerGroup )
1326 {
1327 if ( dataGroup.dataset3dStakedValue.isValid() )
1328 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
1329 }
1330 double resolution = parameterAsDouble( parameters, QStringLiteral( "RESOLUTION" ), context );
1331 int datasetDigits = parameterAsInt( parameters, QStringLiteral( "DATASET_DIGITS" ), context );
1332 int coordDigits = parameterAsInt( parameters, QStringLiteral( "COORDINATES_DIGITS" ), context );
1333
1334 std::unique_ptr<QgsProcessingFeatureSource> featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT_LINES" ), context ) );
1335 if ( !featureSource )
1336 throw QgsProcessingException( QObject::tr( "Input lines vector layer required" ) );
1337
1338 QgsCoordinateTransform transform( featureSource->sourceCrs(), mMeshLayerCrs, context.transformContext() );
1339
1340 QString outputFileName = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );
1341 QFile file( outputFileName );
1342 if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1343 throw QgsProcessingException( QObject::tr( "Unable to create the output file" ) );
1344
1345 QTextStream textStream( &file );
1346#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1347 textStream.setCodec( "UTF-8" );
1348#endif
1349 QStringList header;
1350 header << QStringLiteral( "fid" ) << QStringLiteral( "x" ) << QStringLiteral( "y" ) << QObject::tr( "offset" );
1351 for ( const DataGroup &datagroup : std::as_const( mDataPerGroup ) )
1352 header << datagroup.metadata.name();
1353 textStream << header.join( ',' ) << QStringLiteral( "\n" );
1354
1355 long long featCount = featureSource->featureCount();
1356 long long featCounter = 0;
1357 QgsFeatureIterator featIt = featureSource->getFeatures();
1358 QgsFeature feat;
1359 while ( featIt.nextFeature( feat ) )
1360 {
1361 QgsFeatureId fid = feat.id();
1362 QgsGeometry line = feat.geometry();
1363 try
1364 {
1365 line.transform( transform );
1366 }
1367 catch ( QgsCsException & )
1368 {
1369 line = feat.geometry();
1370 feedback->reportError( QObject::tr( "Could not transform line to mesh CRS" ) );
1371 }
1372
1373 if ( line.isEmpty() )
1374 continue;
1375 double offset = 0;
1376 while ( offset <= line.length() )
1377 {
1378 if ( feedback->isCanceled() )
1379 return QVariantMap();
1380
1381 QStringList textLine;
1382 QgsPointXY point = line.interpolate( offset ).asPoint();
1383 int triangularFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
1384 textLine << QString::number( fid ) << QString::number( point.x(), 'f', coordDigits ) << QString::number( point.y(), 'f', coordDigits ) << QString::number( offset, 'f', coordDigits );
1385 if ( triangularFaceIndex >= 0 )
1386 {
1387 //extract dataset values for the point
1388 QgsAttributes attributes;
1389 int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces().at( triangularFaceIndex );
1390 for ( int i = 0; i < mDataPerGroup.count(); ++i )
1391 {
1392 const DataGroup &dataGroup = mDataPerGroup.at( i );
1393 bool faceActive = dataGroup.activeFaces.active( nativeFaceIndex );
1394 if ( !faceActive )
1395 continue;
1396 QgsMeshDatasetValue value = extractDatasetValue(
1397 point,
1398 nativeFaceIndex,
1399 triangularFaceIndex,
1400 mTriangularMesh,
1401 dataGroup.activeFaces,
1402 dataGroup.datasetValues,
1403 dataGroup.metadata
1404 );
1405
1406 if ( abs( value.x() ) == std::numeric_limits<double>::quiet_NaN() )
1407 textLine << QString( ' ' );
1408 else
1409 textLine << QString::number( value.scalar(), 'f', datasetDigits );
1410 }
1411 }
1412 else
1413 for ( int i = 0; i < mDataPerGroup.count(); ++i )
1414 textLine << QString( ' ' );
1415
1416 textStream << textLine.join( ',' ) << QStringLiteral( "\n" );
1417
1418 offset += resolution;
1419 }
1420
1421 if ( feedback )
1422 {
1423 feedback->setProgress( 100.0 * featCounter / featCount );
1424 if ( feedback->isCanceled() )
1425 return QVariantMap();
1426 }
1427 }
1428
1429 file.close();
1430
1431 QVariantMap ret;
1432 ret[QStringLiteral( "OUTPUT" )] = outputFileName;
1433 return ret;
1434}
1435
1436QString QgsMeshExportTimeSeries::name() const
1437{
1438 return QStringLiteral( "meshexporttimeseries" );
1439}
1440
1441QString QgsMeshExportTimeSeries::displayName() const
1442{
1443 return QObject::tr( "Export time series values from points of a mesh dataset" );
1444}
1445
1446QString QgsMeshExportTimeSeries::group() const
1447{
1448 return QObject::tr( "Mesh" );
1449}
1450
1451QString QgsMeshExportTimeSeries::groupId() const
1452{
1453 return QStringLiteral( "mesh" );
1454}
1455
1456QString QgsMeshExportTimeSeries::shortHelpString() const
1457{
1458 return QObject::tr( "This algorithm extracts mesh's dataset time series values from points contained in a vector layer.\n"
1459 "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." );
1460}
1461
1462QString QgsMeshExportTimeSeries::shortDescription() const
1463{
1464 return QObject::tr( "Extracts a mesh dataset's time series values from points contained in a vector layer" );
1465}
1466
1467QgsProcessingAlgorithm *QgsMeshExportTimeSeries::createInstance() const
1468{
1469 return new QgsMeshExportTimeSeries();
1470}
1471
1472void QgsMeshExportTimeSeries::initAlgorithm( const QVariantMap &configuration )
1473{
1474 Q_UNUSED( configuration );
1475
1476 addParameter( new QgsProcessingParameterMeshLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input mesh layer" ) ) );
1477
1479 QStringLiteral( "DATASET_GROUPS" ),
1480 QObject::tr( "Dataset groups" ),
1481 QStringLiteral( "INPUT" ),
1482 supportedDataType()
1483 ) );
1484
1485 addParameter( new QgsProcessingParameterMeshDatasetTime(
1486 QStringLiteral( "STARTING_TIME" ),
1487 QObject::tr( "Starting time" ),
1488 QStringLiteral( "INPUT" ),
1489 QStringLiteral( "DATASET_GROUPS" )
1490 ) );
1491
1492 addParameter( new QgsProcessingParameterMeshDatasetTime(
1493 QStringLiteral( "FINISHING_TIME" ),
1494 QObject::tr( "Finishing time" ),
1495 QStringLiteral( "INPUT" ),
1496 QStringLiteral( "DATASET_GROUPS" )
1497 ) );
1498
1499 addParameter( new QgsProcessingParameterNumber(
1500 QStringLiteral( "TIME_STEP" ), QObject::tr( "Time step (hours)" ), Qgis::ProcessingNumberParameterType::Double, 0, true, 0
1501 ) );
1502
1503 QList<int> datatype;
1504 datatype << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint );
1505 addParameter( new QgsProcessingParameterFeatureSource(
1506 QStringLiteral( "INPUT_POINTS" ), QObject::tr( "Points for data export" ), datatype, QVariant(), false
1507 ) );
1508
1509 addParameter( new QgsProcessingParameterNumber(
1510 QStringLiteral( "COORDINATES_DIGITS" ), QObject::tr( "Digits count for coordinates" ), Qgis::ProcessingNumberParameterType::Integer, 2
1511 ) );
1512
1513 addParameter( new QgsProcessingParameterNumber(
1514 QStringLiteral( "DATASET_DIGITS" ), QObject::tr( "Digits count for dataset value" ), Qgis::ProcessingNumberParameterType::Integer, 2
1515 ) );
1516
1517 addParameter( new QgsProcessingParameterFileDestination(
1518 QStringLiteral( "OUTPUT" ), QObject::tr( "Exported data CSV file" ), QObject::tr( "CSV file (*.csv)" )
1519 ) );
1520}
1521
1522bool QgsMeshExportTimeSeries::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1523{
1524 QgsMeshLayer *meshLayer = parameterAsMeshLayer( parameters, QStringLiteral( "INPUT" ), context );
1525
1526 if ( !meshLayer || !meshLayer->isValid() )
1527 return false;
1528
1529 mMeshLayerCrs = meshLayer->crs();
1530 mTriangularMesh.update( meshLayer->nativeMesh() );
1531
1532 QList<int> datasetGroups = QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( parameters.value( QStringLiteral( "DATASET_GROUPS" ) ) );
1533
1534 if ( feedback )
1535 {
1536 feedback->setProgressText( QObject::tr( "Preparing data" ) );
1537 }
1538
1539 // Extract the date times used to export dataset values
1540 QVariant parameterStartTimeVariant = parameters.value( QStringLiteral( "STARTING_TIME" ) );
1541 QgsInterval relativeStartTime = datasetRelativetime( parameterStartTimeVariant, meshLayer, context );
1542
1543 QVariant parameterEndTimeVariant = parameters.value( QStringLiteral( "FINISHING_TIME" ) );
1544 QgsInterval relativeEndTime = datasetRelativetime( parameterEndTimeVariant, meshLayer, context );
1545
1546 // calculate time steps
1547 qint64 timeStepInterval = parameterAsDouble( parameters, QStringLiteral( "TIME_STEP" ), context ) * 1000 * 3600;
1548 if ( timeStepInterval == 0 )
1549 {
1550 //take the first time step of the first temporal dataset group
1551 for ( int groupIndex : datasetGroups )
1552 {
1553 QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( QgsMeshDatasetIndex( groupIndex, 0 ) );
1554 if ( !meta.isTemporal() && meshLayer->datasetCount( QgsMeshDatasetIndex( groupIndex, 0 ) ) < 2 )
1555 continue;
1556 else
1557 {
1558 timeStepInterval = meshLayer->datasetRelativeTimeInMilliseconds( QgsMeshDatasetIndex( groupIndex, 1 ) )
1559 - meshLayer->datasetRelativeTimeInMilliseconds( QgsMeshDatasetIndex( groupIndex, 0 ) );
1560 break;
1561 }
1562 }
1563 }
1564
1565 mRelativeTimeSteps.clear();
1566 mTimeStepString.clear();
1567 if ( timeStepInterval != 0 )
1568 {
1569 mRelativeTimeSteps.append( relativeStartTime.seconds() * 1000 );
1570 while ( mRelativeTimeSteps.last() < relativeEndTime.seconds() * 1000 )
1571 mRelativeTimeSteps.append( mRelativeTimeSteps.last() + timeStepInterval );
1572
1573 for ( qint64 relativeTimeStep : std::as_const( mRelativeTimeSteps ) )
1574 {
1575 mTimeStepString.append( meshLayer->formatTime( relativeTimeStep / 3600.0 / 1000.0 ) );
1576 }
1577 }
1578
1579 //Extract needed dataset values
1580 for ( int i = 0; i < datasetGroups.count(); ++i )
1581 {
1582 int groupIndex = datasetGroups.at( i );
1583 QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( QgsMeshDatasetIndex( groupIndex, 0 ) );
1584 if ( supportedDataType().contains( meta.dataType() ) )
1585 {
1586 mGroupIndexes.append( groupIndex );
1587 mGroupsMetadata[groupIndex] = meta;
1588 int valueCount = meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ? mTriangularMesh.vertices().count() : meshLayer->nativeMesh()->faceCount();
1589
1590 if ( !mRelativeTimeSteps.isEmpty() )
1591 {
1592 //QMap<qint64, DataGroup> temporalGroup;
1593 QgsMeshDatasetIndex lastDatasetIndex;
1594 for ( qint64 relativeTimeStep : std::as_const( mRelativeTimeSteps ) )
1595 {
1596 QMap<int, int> &groupIndexToData = mRelativeTimeToData[relativeTimeStep];
1597 QgsInterval timeStepInterval( relativeTimeStep / 1000.0 );
1598 QgsMeshDatasetIndex datasetIndex = meshLayer->datasetIndexAtRelativeTime( timeStepInterval, groupIndex );
1599 if ( !datasetIndex.isValid() )
1600 continue;
1601 if ( datasetIndex != lastDatasetIndex )
1602 {
1603 DataGroup dataGroup;
1604 dataGroup.metadata = meta;
1605 dataGroup.datasetValues = meshLayer->datasetValues( datasetIndex, 0, valueCount );
1606 dataGroup.activeFaces = meshLayer->areFacesActive( datasetIndex, 0, meshLayer->nativeMesh()->faceCount() );
1607 if ( dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVolumes )
1608 {
1609 dataGroup.dataset3dStakedValue = meshLayer->dataset3dValues( datasetIndex, 0, valueCount );
1610 }
1611 mDatasets.append( dataGroup );
1612 lastDatasetIndex = datasetIndex;
1613 }
1614 groupIndexToData[groupIndex] = mDatasets.count() - 1;
1615 }
1616 }
1617 else
1618 {
1619 // we have only static dataset group
1620 QMap<int, int> &groupIndexToData = mRelativeTimeToData[0];
1621 QgsMeshDatasetIndex datasetIndex( groupIndex, 0 );
1622 DataGroup dataGroup;
1623 dataGroup.metadata = meta;
1624 dataGroup.datasetValues = meshLayer->datasetValues( datasetIndex, 0, valueCount );
1625 dataGroup.activeFaces = meshLayer->areFacesActive( datasetIndex, 0, meshLayer->nativeMesh()->faceCount() );
1626 if ( dataGroup.metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVolumes )
1627 {
1628 dataGroup.dataset3dStakedValue = meshLayer->dataset3dValues( datasetIndex, 0, valueCount );
1629 }
1630 mDatasets.append( dataGroup );
1631 groupIndexToData[groupIndex] = mDatasets.count() - 1;
1632 }
1633 }
1634
1635 if ( feedback )
1636 feedback->setProgress( 100 * i / datasetGroups.count() );
1637 }
1638
1639 mLayerRendererSettings = meshLayer->rendererSettings();
1640
1641 return true;
1642}
1643
1644
1645QVariantMap QgsMeshExportTimeSeries::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1646{
1647 if ( feedback )
1648 feedback->setProgress( 0 );
1649 //First, if present, average 3D staked dataset value to 2D face value
1650 const QgsMesh3DAveragingMethod *avgMethod = mLayerRendererSettings.averagingMethod();
1651
1652 for ( DataGroup &dataGroup : mDatasets )
1653 {
1654 if ( dataGroup.dataset3dStakedValue.isValid() )
1655 dataGroup.datasetValues = avgMethod->calculate( dataGroup.dataset3dStakedValue );
1656 }
1657
1658 int datasetDigits = parameterAsInt( parameters, QStringLiteral( "DATASET_DIGITS" ), context );
1659 int coordDigits = parameterAsInt( parameters, QStringLiteral( "COORDINATES_DIGITS" ), context );
1660
1661 std::unique_ptr<QgsProcessingFeatureSource> featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT_POINTS" ), context ) );
1662 if ( !featureSource )
1663 throw QgsProcessingException( QObject::tr( "Input points vector layer required" ) );
1664
1665 QgsCoordinateTransform transform( featureSource->sourceCrs(), mMeshLayerCrs, context.transformContext() );
1666
1667 QString outputFileName = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );
1668 QFile file( outputFileName );
1669 if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1670 throw QgsProcessingException( QObject::tr( "Unable to create the output file" ) );
1671
1672 QTextStream textStream( &file );
1673#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1674 textStream.setCodec( "UTF-8" );
1675#endif
1676 QStringList header;
1677 header << QStringLiteral( "fid" ) << QStringLiteral( "x" ) << QStringLiteral( "y" ) << QObject::tr( "time" );
1678
1679 for ( int gi : std::as_const( mGroupIndexes ) )
1680 header << mGroupsMetadata.value( gi ).name();
1681
1682 textStream << header.join( ',' ) << QStringLiteral( "\n" );
1683
1684 long long featCount = featureSource->featureCount();
1685 long long featCounter = 0;
1686 QgsFeatureIterator featIt = featureSource->getFeatures();
1687 QgsFeature feat;
1688 while ( featIt.nextFeature( feat ) )
1689 {
1690 QgsFeatureId fid = feat.id();
1691 QgsGeometry geom = feat.geometry();
1692 try
1693 {
1694 geom.transform( transform );
1695 }
1696 catch ( QgsCsException & )
1697 {
1698 geom = feat.geometry();
1699 feedback->reportError( QObject::tr( "Could not transform line to mesh CRS" ) );
1700 }
1701
1702 if ( geom.isEmpty() )
1703 continue;
1704
1705 QgsPointXY point = geom.asPoint();
1706 int triangularFaceIndex = mTriangularMesh.faceIndexForPoint_v2( point );
1707
1708 if ( triangularFaceIndex >= 0 )
1709 {
1710 int nativeFaceIndex = mTriangularMesh.trianglesToNativeFaces().at( triangularFaceIndex );
1711 if ( !mRelativeTimeSteps.isEmpty() )
1712 {
1713 for ( int timeIndex = 0; timeIndex < mRelativeTimeSteps.count(); ++timeIndex )
1714 {
1715 qint64 timeStep = mRelativeTimeSteps.at( timeIndex );
1716 QStringList textLine;
1717 textLine << QString::number( fid )
1718 << QString::number( point.x(), 'f', coordDigits )
1719 << QString::number( point.y(), 'f', coordDigits )
1720 << mTimeStepString.at( timeIndex );
1721
1722 if ( mRelativeTimeToData.contains( timeStep ) )
1723 {
1724 const QMap<int, int> &groupToData = mRelativeTimeToData.value( timeStep );
1725 for ( int groupIndex : std::as_const( mGroupIndexes ) )
1726 {
1727 if ( !groupToData.contains( groupIndex ) )
1728 continue;
1729 int dataIndex = groupToData.value( groupIndex );
1730 if ( dataIndex < 0 || dataIndex > mDatasets.count() - 1 )
1731 continue;
1732
1733 const DataGroup &dataGroup = mDatasets.at( dataIndex );
1734 QgsMeshDatasetValue value = extractDatasetValue( point, nativeFaceIndex, triangularFaceIndex, mTriangularMesh, dataGroup.activeFaces, dataGroup.datasetValues, dataGroup.metadata );
1735 if ( abs( value.x() ) == std::numeric_limits<double>::quiet_NaN() )
1736 textLine << QString( ' ' );
1737 else
1738 textLine << QString::number( value.scalar(), 'f', datasetDigits );
1739 }
1740 }
1741 textStream << textLine.join( ',' ) << QStringLiteral( "\n" );
1742 }
1743 }
1744 else
1745 {
1746 QStringList textLine;
1747 textLine << QString::number( fid )
1748 << QString::number( point.x(), 'f', coordDigits )
1749 << QString::number( point.y(), 'f', coordDigits )
1750 << QObject::tr( "static dataset" );
1751 const QMap<int, int> &groupToData = mRelativeTimeToData.value( 0 );
1752 for ( int groupIndex : std::as_const( mGroupIndexes ) )
1753 {
1754 if ( !groupToData.contains( groupIndex ) )
1755 continue;
1756 int dataIndex = groupToData.value( groupIndex );
1757 if ( dataIndex < 0 || dataIndex > mDatasets.count() - 1 )
1758 continue;
1759 const DataGroup &dataGroup = mDatasets.at( dataIndex );
1760 QgsMeshDatasetValue value = extractDatasetValue( point, nativeFaceIndex, triangularFaceIndex, mTriangularMesh, dataGroup.activeFaces, dataGroup.datasetValues, dataGroup.metadata );
1761 if ( abs( value.x() ) == std::numeric_limits<double>::quiet_NaN() )
1762 textLine << QString( ' ' );
1763 else
1764 textLine << QString::number( value.scalar(), 'f', datasetDigits );
1765 }
1766 textStream << textLine.join( ',' ) << QStringLiteral( "\n" );
1767 }
1768 }
1769 featCounter++;
1770 if ( feedback )
1771 {
1772 feedback->setProgress( 100.0 * featCounter / featCount );
1773 if ( feedback->isCanceled() )
1774 return QVariantMap();
1775 }
1776 }
1777
1778 file.close();
1779
1780 QVariantMap ret;
1781 ret[QStringLiteral( "OUTPUT" )] = outputFileName;
1782 return ret;
1783}
1784
@ 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: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
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.