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