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