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