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