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