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