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