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