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