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