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