QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsmeshlayerutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshlayerutils.cpp
3 --------------------------
4 begin : August 2018
5 copyright : (C) 2018 by Martin Dobias
6 email : wonder dot sk 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 <limits>
19#include <QTime>
20#include <QDateTime>
21
22#include "qgsmeshlayerutils.h"
23#include "qgsmeshtimesettings.h"
24#include "qgstriangularmesh.h"
25#include "qgslogger.h"
26#include "qgsmeshdataprovider.h"
27#include "qgsmesh3daveraging.h"
28#include "qgsmeshlayer.h"
29
30
32
33int QgsMeshLayerUtils::datasetValuesCount( const QgsMesh *mesh, QgsMeshDatasetGroupMetadata::DataType dataType )
34{
35 if ( !mesh )
36 return 0;
37
38 switch ( dataType )
39 {
40 case QgsMeshDatasetGroupMetadata::DataType::DataOnEdges:
41 return mesh->edgeCount();
42 case QgsMeshDatasetGroupMetadata::DataType::DataOnFaces:
43 return mesh->faceCount();
44 case QgsMeshDatasetGroupMetadata::DataType::DataOnVertices:
45 return mesh->vertexCount();
46 case QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes:
47 return mesh->faceCount(); // because they are averaged to faces
48 }
49 return 0;
50}
51
52QgsMeshDatasetGroupMetadata::DataType QgsMeshLayerUtils::datasetValuesType( const QgsMeshDatasetGroupMetadata::DataType &type )
53{
54 // data on volumes are averaged to 2D data on faces
55 if ( type == QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes )
56 return QgsMeshDatasetGroupMetadata::DataType::DataOnFaces;
57
58 return type;
59}
60
61QgsMeshDataBlock QgsMeshLayerUtils::datasetValues(
62 const QgsMeshLayer *meshLayer,
64 int valueIndex,
65 int count )
66{
67 QgsMeshDataBlock block;
68 if ( !meshLayer )
69 return block;
70
71
72 if ( !meshLayer->datasetCount( index ) )
73 return block;
74
75 if ( !index.isValid() )
76 return block;
77
78 const QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( index.group() );
79 if ( meta.dataType() != QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes )
80 {
81 block = meshLayer->datasetValues( index, valueIndex, count );
82 if ( block.isValid() )
83 return block;
84 }
85 else
86 {
87 const QgsMesh3dAveragingMethod *averagingMethod = meshLayer->rendererSettings().averagingMethod();
88 if ( !averagingMethod )
89 return block;
90
91 const QgsMesh3dDataBlock block3d = meshLayer->dataset3dValues( index, valueIndex, count );
92 if ( !block3d.isValid() )
93 return block;
94
95 block = averagingMethod->calculate( block3d );
96 }
97 return block;
98}
99
100QVector<QgsVector> QgsMeshLayerUtils::griddedVectorValues( const QgsMeshLayer *meshLayer,
101 const QgsMeshDatasetIndex index,
102 double xSpacing,
103 double ySpacing,
104 const QSize &size,
105 const QgsPointXY &minCorner )
106{
107 QVector<QgsVector> vectors;
108
109 if ( !meshLayer || !index.isValid() )
110 return vectors;
111
112 const QgsTriangularMesh *triangularMesh = meshLayer->triangularMesh();
113 const QgsMesh *nativeMesh = meshLayer->nativeMesh();
114
115 if ( !triangularMesh || !nativeMesh )
116 return vectors;
117
118 const QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( index );
119 if ( !meta.isVector() )
120 return vectors;
121
122 // extract vector dataset
123 const bool vectorDataOnVertices = meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
124 const int datacount = vectorDataOnVertices ? nativeMesh->vertices.count() : nativeMesh->faces.count();
125 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues( meshLayer, index, 0, datacount );
126
127 const QgsMeshDataBlock isFacesActive = meshLayer->areFacesActive( index, 0, nativeMesh->faceCount() );
128 const QgsMeshDatasetGroupMetadata::DataType dataType = meta.dataType();
129
131 return vectors;
132
133 try
134 {
135 vectors.reserve( size.height()*size.width() );
136 }
137 catch ( ... )
138 {
139 QgsDebugMsgLevel( "Unable to store the arrow grid in memory", 1 );
140 return QVector<QgsVector>();
141 }
142
143 for ( int iy = 0; iy < size.height(); ++iy )
144 {
145 const double y = minCorner.y() + iy * ySpacing;
146 for ( int ix = 0; ix < size.width(); ++ix )
147 {
148 const double x = minCorner.x() + ix * xSpacing;
149 const QgsPoint point( x, y );
150 const int faceIndex = triangularMesh->faceIndexForPoint_v2( point );
151 int nativeFaceIndex = -1;
152 if ( faceIndex != -1 )
153 nativeFaceIndex = triangularMesh->trianglesToNativeFaces().at( faceIndex );
155 if ( nativeFaceIndex != -1 && isFacesActive.active( nativeFaceIndex ) )
156 {
157 switch ( dataType )
158 {
161 value = vals.value( nativeFaceIndex );
162 break;
164 {
165 const QgsMeshFace &face = triangularMesh->triangles()[faceIndex];
166 const int v1 = face[0], v2 = face[1], v3 = face[2];
167 const QgsPoint p1 = triangularMesh->vertices()[v1], p2 = triangularMesh->vertices()[v2], p3 = triangularMesh->vertices()[v3];
168 const QgsMeshDatasetValue val1 = vals.value( v1 );
169 const QgsMeshDatasetValue val2 = vals.value( v2 );
170 const QgsMeshDatasetValue val3 = vals.value( v3 );
171 const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
172 const double y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
173 value = QgsMeshDatasetValue( x, y );
174 }
175 break;
177 break;
178 }
179 }
180 vectors.append( QgsVector( value.x(), value.y() ) );
181 }
182 }
183 return vectors;
184}
185
186QVector<double> QgsMeshLayerUtils::calculateMagnitudes( const QgsMeshDataBlock &block )
187{
188 Q_ASSERT( QgsMeshDataBlock::ActiveFlagInteger != block.type() );
189 const int count = block.count();
190 QVector<double> ret( count );
191
192 for ( int i = 0; i < count; ++i )
193 {
194 const double mag = block.value( i ).scalar();
195 ret[i] = mag;
196 }
197 return ret;
198}
199
200QgsRectangle QgsMeshLayerUtils::boundingBoxToScreenRectangle(
201 const QgsMapToPixel &mtp,
202 const QgsRectangle &bbox
203)
204{
205 const QgsPointXY topLeft = mtp.transform( bbox.xMinimum(), bbox.yMaximum() );
206 const QgsPointXY topRight = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
207 const QgsPointXY bottomLeft = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
208 const QgsPointXY bottomRight = mtp.transform( bbox.xMaximum(), bbox.yMinimum() );
209
210 const double xMin = std::min( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
211 const double xMax = std::max( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
212 const double yMin = std::min( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );
213 const double yMax = std::max( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );
214
215 const QgsRectangle ret( xMin, yMin, xMax, yMax );
216 return ret;
217}
218
219void QgsMeshLayerUtils::boundingBoxToScreenRectangle(
220 const QgsMapToPixel &mtp,
221 const QSize &outputSize,
222 const QgsRectangle &bbox,
223 int &leftLim,
224 int &rightLim,
225 int &bottomLim,
226 int &topLim )
227{
228 const QgsRectangle screenBBox = boundingBoxToScreenRectangle( mtp, bbox );
229
230 bottomLim = std::max( int( screenBBox.yMinimum() ), 0 );
231 topLim = std::min( int( screenBBox.yMaximum() ), outputSize.height() - 1 );
232 leftLim = std::max( int( screenBBox.xMinimum() ), 0 );
233 rightLim = std::min( int( screenBBox.xMaximum() ), outputSize.width() - 1 );
234}
235
236static void lamTol( double &lam )
237{
238 const static double eps = 1e-6;
239 if ( ( lam < 0.0 ) && ( lam > -eps ) )
240 {
241 lam = 0.0;
242 }
243}
244
245static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
246 double &lam1, double &lam2, double &lam3 )
247{
248 // Compute vectors
249 const double xa = pA.x();
250 const double ya = pA.y();
251 const double v0x = pC.x() - xa ;
252 const double v0y = pC.y() - ya ;
253 const double v1x = pB.x() - xa ;
254 const double v1y = pB.y() - ya ;
255 const double v2x = pP.x() - xa ;
256 const double v2y = pP.y() - ya ;
257
258 // Compute dot products
259 const double dot00 = v0x * v0x + v0y * v0y;
260 const double dot01 = v0x * v1x + v0y * v1y;
261 const double dot02 = v0x * v2x + v0y * v2y;
262 const double dot11 = v1x * v1x + v1y * v1y;
263 const double dot12 = v1x * v2x + v1y * v2y;
264
265 // Compute barycentric coordinates
266 double invDenom = dot00 * dot11 - dot01 * dot01;
267 if ( invDenom == 0 )
268 return false;
269 invDenom = 1.0 / invDenom;
270 lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
271 lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
272 lam3 = 1.0 - lam1 - lam2;
273
274 // Apply some tolerance to lam so we can detect correctly border points
275 lamTol( lam1 );
276 lamTol( lam2 );
277 lamTol( lam3 );
278
279 // Return if POI is outside triangle
280 if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
281 {
282 return false;
283 }
284
285 return true;
286}
287
288double QgsMeshLayerUtils::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
289 double val1, double val2, double val3, const QgsPointXY &pt )
290{
291 double lam1, lam2, lam3;
292 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
293 return std::numeric_limits<double>::quiet_NaN();
294
295 return lam1 * val3 + lam2 * val2 + lam3 * val1;
296}
297
298double QgsMeshLayerUtils::interpolateZForPoint( const QgsTriangularMesh &mesh, double x, double y )
299{
300 const QgsPointXY point( x, y );
301 const int faceIndex = mesh.faceIndexForPoint_v2( point );
302 if ( faceIndex < 0 || faceIndex >= mesh.triangles().count() )
303 return std::numeric_limits<float>::quiet_NaN();
304
305 const QgsMeshFace &face = mesh.triangles().at( faceIndex );
306
307 const QgsPoint p1 = mesh.vertices().at( face.at( 0 ) );
308 const QgsPoint p2 = mesh.vertices().at( face.at( 1 ) );
309 const QgsPoint p3 = mesh.vertices().at( face.at( 2 ) );
310
311 return QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, p1.z(), p2.z(), p3.z(), point );
312}
313
314double QgsMeshLayerUtils::interpolateFromVerticesData( double fraction, double val1, double val2 )
315{
316 if ( std::isnan( val1 ) || std::isnan( val2 ) || ( fraction < 0 ) || ( fraction > 1 ) )
317 {
318 return std::numeric_limits<double>::quiet_NaN();
319 }
320 return val1 + ( val2 - val1 ) * fraction;
321}
322
323QgsMeshDatasetValue QgsMeshLayerUtils::interpolateFromVerticesData( double fraction, const QgsMeshDatasetValue &val1, const QgsMeshDatasetValue &val2 )
324{
325 return QgsMeshDatasetValue( interpolateFromVerticesData( fraction, val1.x(), val2.x() ),
326 interpolateFromVerticesData( fraction, val1.y(), val2.y() ) );
327}
328
329
330QgsVector QgsMeshLayerUtils::interpolateVectorFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, QgsVector vect1, QgsVector vect2, QgsVector vect3, const QgsPointXY &pt )
331{
332 double lam1, lam2, lam3;
333 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
334 return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
335
336 return vect3 * lam1 + vect2 * lam2 + vect1 * lam3;
337}
338
339double QgsMeshLayerUtils::interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
340 double val, const QgsPointXY &pt )
341{
342 double lam1, lam2, lam3;
343 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
344 return std::numeric_limits<double>::quiet_NaN();
345
346 return val;
347}
348
349QgsVector QgsMeshLayerUtils::interpolateVectorFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
350 QgsVector vect, const QgsPointXY &pt )
351{
352 double lam1, lam2, lam3;
353 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
354 return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
355
356 return vect;
357}
358
359
360QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
361 QVector<double> valuesOnFaces,
362 const QgsMesh *nativeMesh,
363 const QgsTriangularMesh *triangularMesh,
364 QgsMeshDataBlock *active,
366{
367 assert( nativeMesh );
368 Q_UNUSED( method );
369 Q_UNUSED( triangularMesh );
370
371 // assuming that native vertex count = triangular vertex count
372 assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
373
374 return interpolateFromFacesData( valuesOnFaces, *nativeMesh, active, method );
375}
376
377QVector<double> QgsMeshLayerUtils::interpolateFromFacesData( const QVector<double> &valuesOnFaces,
378 const QgsMesh &nativeMesh,
379 QgsMeshDataBlock *active,
381{
382
383 QgsMeshDataBlock activeFace;
384 if ( active )
385 activeFace = *active;
386 else
387 {
389 activeFace.setValid( true );
390 }
391
392 return interpolateFromFacesData( valuesOnFaces, nativeMesh, activeFace, method );
393
394}
395
396QVector<double> QgsMeshLayerUtils::interpolateFromFacesData( const QVector<double> &valuesOnFaces, const QgsMesh &nativeMesh, const QgsMeshDataBlock &active, QgsMeshRendererScalarSettings::DataResamplingMethod method )
397{
398 const int vertexCount = nativeMesh.vertexCount();
399 Q_UNUSED( method );
401
402 QVector<double> res( vertexCount, 0.0 );
403 // for face datasets do simple average of the valid values of all faces that contains this vertex
404 QVector<int> count( vertexCount, 0 );
405
406 for ( int i = 0; i < nativeMesh.faceCount(); ++i )
407 {
408 if ( active.active( i ) )
409 {
410 const double val = valuesOnFaces[ i ];
411 if ( !std::isnan( val ) )
412 {
413 // assign for all vertices
414 const QgsMeshFace &face = nativeMesh.faces.at( i );
415 for ( int j = 0; j < face.size(); ++j )
416 {
417 const int vertexIndex = face[j];
418 res[vertexIndex] += val;
419 count[vertexIndex] += 1;
420 }
421 }
422 }
423 }
424
425 for ( int i = 0; i < vertexCount; ++i )
426 {
427 if ( count.at( i ) > 0 )
428 {
429 res[i] = res[i] / double( count.at( i ) );
430 }
431 else
432 {
433 res[i] = std::numeric_limits<double>::quiet_NaN();
434 }
435 }
436
437 return res;
438}
439
440QVector<double> QgsMeshLayerUtils::resampleFromVerticesToFaces(
441 const QVector<double> valuesOnVertices,
442 const QgsMesh *nativeMesh,
443 const QgsTriangularMesh *triangularMesh,
444 const QgsMeshDataBlock *active,
446{
447 assert( nativeMesh );
448 Q_UNUSED( method );
450
451 // assuming that native vertex count = triangular vertex count
452 Q_UNUSED( triangularMesh );
453 assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
454
455 QVector<double> ret( nativeMesh->faceCount(), std::numeric_limits<double>::quiet_NaN() );
456
457 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
458 {
459 const QgsMeshFace face = nativeMesh->face( i );
460 if ( active->active( i ) && face.count() > 2 )
461 {
462 double value = 0;
463 for ( int j = 0; j < face.count(); ++j )
464 {
465 value += valuesOnVertices.at( face.at( j ) );
466 }
467 ret[i] = value / face.count();
468 }
469 }
470
471 return ret;
472}
473
474QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMeshLayer *meshLayer,
475 const QgsMeshDatasetIndex index,
476 QgsMeshDataBlock *activeFaceFlagValues,
478{
479 QVector<double> ret;
480
481 if ( !meshLayer && !index.isValid() )
482 return ret;
483
484 const QgsTriangularMesh *triangularMesh = meshLayer->triangularMesh();
485 const QgsMesh *nativeMesh = meshLayer->nativeMesh();
486 if ( !triangularMesh || !nativeMesh )
487 return ret;
488
489 const QgsMeshDatasetGroupMetadata metadata = meshLayer->datasetGroupMetadata( index );
490 const bool scalarDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
491
492 // populate scalar values
493 const int datacount = scalarDataOnVertices ? nativeMesh->vertices.count() : nativeMesh->faces.count();
494 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
495 meshLayer,
496 index,
497 0,
498 datacount );
499
500 QgsMeshDataBlock activeFace;
501 if ( !activeFaceFlagValues )
502 {
504 activeFace.setValid( true );
505 }
506 else
507 activeFace = *activeFaceFlagValues;
508
509 return calculateMagnitudeOnVertices( *nativeMesh, metadata, vals, activeFace, method );
510}
511
512QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMesh &nativeMesh,
513 const QgsMeshDatasetGroupMetadata &groupMetadata,
514 const QgsMeshDataBlock &datasetValues,
515 const QgsMeshDataBlock &activeFaceFlagValues,
517{
518 QVector<double> ret;
519 const bool scalarDataOnVertices = groupMetadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
520
521 if ( datasetValues.isValid() )
522 {
523 ret = QgsMeshLayerUtils::calculateMagnitudes( datasetValues );
524
525 if ( !scalarDataOnVertices )
526 {
527 //Need to interpolate data on vertices
528 ret = QgsMeshLayerUtils::interpolateFromFacesData(
529 ret,
530 nativeMesh,
531 activeFaceFlagValues,
532 method );
533 }
534 }
535 return ret;
536}
537
538QgsRectangle QgsMeshLayerUtils::triangleBoundingBox( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 )
539{
540 // p1
541 double xMin = p1.x();
542 double xMax = p1.x();
543 double yMin = p1.y();
544 double yMax = p1.y();
545
546 //p2
547 xMin = ( ( xMin < p2.x() ) ? xMin : p2.x() );
548 xMax = ( ( xMax > p2.x() ) ? xMax : p2.x() );
549 yMin = ( ( yMin < p2.y() ) ? yMin : p2.y() );
550 yMax = ( ( yMax > p2.y() ) ? yMax : p2.y() );
551
552 // p3
553 xMin = ( ( xMin < p3.x() ) ? xMin : p3.x() );
554 xMax = ( ( xMax > p3.x() ) ? xMax : p3.x() );
555 yMin = ( ( yMin < p3.y() ) ? yMin : p3.y() );
556 yMax = ( ( yMax > p3.y() ) ? yMax : p3.y() );
557
558 const QgsRectangle bbox( xMin, yMin, xMax, yMax );
559 return bbox;
560}
561
562QString QgsMeshLayerUtils::formatTime( double hours, const QDateTime &referenceTime, const QgsMeshTimeSettings &settings )
563{
564 QString ret;
565
566 if ( referenceTime.isValid() )
567 {
568 const QString format( settings.absoluteTimeFormat() );
569 QDateTime dateTime( referenceTime );
570 const qint64 seconds = static_cast<qint64>( hours * 3600.0 );
571 dateTime = dateTime.addSecs( seconds );
572 ret = dateTime.toString( format );
573 if ( ret.isEmpty() ) // error
574 ret = dateTime.toString();
575 }
576 else
577 {
578 QString format( settings.relativeTimeFormat() );
579 format = format.trimmed();
580 const int totalHours = static_cast<int>( hours );
581
582 if ( format == QLatin1String( "hh:mm:ss.zzz" ) )
583 {
584 const int ms = static_cast<int>( hours * 3600.0 * 1000 );
585 const int seconds = ms / 1000;
586 const int z = ms % 1000;
587 int m = seconds / 60;
588 const int s = seconds % 60;
589 const int h = m / 60;
590 m = m % 60;
591 ret = QStringLiteral( "%1:%2:%3.%4" ).
592 arg( h, 2, 10, QLatin1Char( '0' ) ).
593 arg( m, 2, 10, QLatin1Char( '0' ) ).
594 arg( s, 2, 10, QLatin1Char( '0' ) ).
595 arg( z, 3, 10, QLatin1Char( '0' ) );
596 }
597 else if ( format == QLatin1String( "hh:mm:ss" ) )
598 {
599 const int seconds = static_cast<int>( hours * 3600.0 );
600 int m = seconds / 60;
601 const int s = seconds % 60;
602 const int h = m / 60;
603 m = m % 60;
604 ret = QStringLiteral( "%1:%2:%3" ).
605 arg( h, 2, 10, QLatin1Char( '0' ) ).
606 arg( m, 2, 10, QLatin1Char( '0' ) ).
607 arg( s, 2, 10, QLatin1Char( '0' ) );
608
609 }
610 else if ( format == QLatin1String( "d hh:mm:ss" ) )
611 {
612 const int seconds = static_cast<int>( hours * 3600.0 );
613 int m = seconds / 60;
614 const int s = seconds % 60;
615 int h = m / 60;
616 m = m % 60;
617 const int d = totalHours / 24;
618 h = totalHours % 24;
619 ret = QStringLiteral( "%1 d %2:%3:%4" ).
620 arg( d ).
621 arg( h, 2, 10, QLatin1Char( '0' ) ).
622 arg( m, 2, 10, QLatin1Char( '0' ) ).
623 arg( s, 2, 10, QLatin1Char( '0' ) );
624 }
625 else if ( format == QLatin1String( "d hh" ) )
626 {
627 const int d = totalHours / 24;
628 const int h = totalHours % 24;
629 ret = QStringLiteral( "%1 d %2" ).
630 arg( d ).
631 arg( h );
632 }
633 else if ( format == QLatin1String( "d" ) )
634 {
635 const int d = totalHours / 24;
636 ret = QString::number( d );
637 }
638 else if ( format == QLatin1String( "ss" ) )
639 {
640 const int seconds = static_cast<int>( hours * 3600.0 );
641 ret = QString::number( seconds );
642 }
643 else // "hh"
644 {
645 ret = QString::number( hours );
646 }
647 }
648 return ret;
649}
650
651QVector<QVector3D> QgsMeshLayerUtils::calculateNormals( const QgsTriangularMesh &triangularMesh, const QVector<double> &verticalMagnitude, bool isRelative )
652{
653 QVector<QVector3D> normals( triangularMesh.vertices().count() );
654 for ( const auto &face : triangularMesh.triangles() )
655 {
656 for ( int i = 0; i < 3; i++ )
657 {
658 const int index( face.at( i ) );
659 const int index1( face.at( ( i + 1 ) % 3 ) );
660 const int index2( face.at( ( i + 2 ) % 3 ) );
661
662 if ( std::isnan( verticalMagnitude[index] ) ||
663 std::isnan( verticalMagnitude[index1] ) ||
664 std::isnan( verticalMagnitude[index2] ) )
665 continue;
666
667 const QgsMeshVertex &vert( triangularMesh.vertices().at( index ) );
668 const QgsMeshVertex &otherVert1( triangularMesh.vertices().at( index1 ) );
669 const QgsMeshVertex &otherVert2( triangularMesh.vertices().at( index2 ) );
670
671 float adjustRelative = 0;
672 float adjustRelative1 = 0;
673 float adjustRelative2 = 0;
674
675 if ( isRelative )
676 {
677 adjustRelative = vert.z();
678 adjustRelative1 = otherVert1.z();
679 adjustRelative2 = otherVert2.z();
680 }
681
682 const QVector3D v1( float( otherVert1.x() - vert.x() ),
683 float( otherVert1.y() - vert.y() ),
684 float( verticalMagnitude[index1] - verticalMagnitude[index] + adjustRelative1 - adjustRelative ) );
685 const QVector3D v2( float( otherVert2.x() - vert.x() ),
686 float( otherVert2.y() - vert.y() ),
687 float( verticalMagnitude[index2] - verticalMagnitude[index] + adjustRelative2 - adjustRelative ) );
688
689 normals[index] += QVector3D::crossProduct( v1, v2 );
690 }
691 }
692
693 return normals;
694}
695
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:90
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.
QgsMesh3dDataBlock is a block of 3d stacked mesh data related N faces defined on base mesh frame.
bool isValid() const
Whether the block is valid.
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 isValid() const
Whether the block is valid.
DataType type() const
Type of data stored in the block.
@ ActiveFlagInteger
Integer boolean flag whether face is active.
bool active(int index) const
Returns a value for active flag by the index For scalar and vector 2d the behavior is undefined.
int count() const
Number of items stored in the block.
void setValid(bool valid)
Sets block validity.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
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.
DataType
Location of where data is specified for datasets in the dataset group.
@ 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.
int group() const
Returns a group index.
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.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:100
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.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
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.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
DataResamplingMethod
Resampling of value from dataset.
@ NeighbourAverage
Does a simple average of values defined for all surrounding faces/vertices.
QgsMesh3dAveragingMethod * averagingMethod() const
Returns averaging method for conversion of 3d stacked mesh data to 2d data.
Represents a mesh time settings for mesh datasets.
QString relativeTimeFormat() const
Returns format used for relative time.
QString absoluteTimeFormat() const
Returns format used for absolute time.
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
double z
Definition: qgspoint.h:54
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
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
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.
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
int faceIndexForPoint_v2(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing and don't use geos to be faster.
A class to represent a vector.
Definition: qgsvector.h:30
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QVector< int > QgsMeshFace
List of vertex indexes.
Mesh - vertices, edges and faces.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
QVector< QgsMeshFace > faces
int faceCount() const
Returns number of faces.
int edgeCount() const
Returns number of edge.