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