QGIS API Documentation 3.99.0-Master (357b655ed83)
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(
68 const QgsMeshLayer *meshLayer,
70 int valueIndex,
71 int count )
72{
73 QgsMeshDataBlock block;
74 if ( !meshLayer )
75 return block;
76
77
78 if ( !meshLayer->datasetCount( index ) )
79 return block;
80
81 if ( !index.isValid() )
82 return block;
83
84 const QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( index.group() );
86 {
87 block = meshLayer->datasetValues( index, valueIndex, count );
88 if ( block.isValid() )
89 return block;
90 }
91 else
92 {
93 const QgsMesh3DAveragingMethod *averagingMethod = meshLayer->rendererSettings().averagingMethod();
94 if ( !averagingMethod )
95 return block;
96
97 const QgsMesh3DDataBlock block3d = meshLayer->dataset3dValues( index, valueIndex, count );
98 if ( !block3d.isValid() )
99 return block;
100
101 block = averagingMethod->calculate( block3d );
102 }
103 return block;
104}
105
106QVector<QgsVector> QgsMeshLayerUtils::griddedVectorValues( const QgsMeshLayer *meshLayer,
107 const QgsMeshDatasetIndex index,
108 double xSpacing,
109 double ySpacing,
110 const QSize &size,
111 const QgsPointXY &minCorner )
112{
113 QVector<QgsVector> vectors;
114
115 if ( !meshLayer || !index.isValid() )
116 return vectors;
117
118 const QgsTriangularMesh *triangularMesh = meshLayer->triangularMesh();
119 const QgsMesh *nativeMesh = meshLayer->nativeMesh();
120
121 if ( !triangularMesh || !nativeMesh )
122 return vectors;
123
124 const QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( index );
125 if ( !meta.isVector() )
126 return vectors;
127
128 // extract vector dataset
129 const bool vectorDataOnVertices = meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
130 const int datacount = vectorDataOnVertices ? nativeMesh->vertices.count() : nativeMesh->faces.count();
131 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues( meshLayer, index, 0, datacount );
132
133 const QgsMeshDataBlock isFacesActive = meshLayer->areFacesActive( index, 0, nativeMesh->faceCount() );
134 const QgsMeshDatasetGroupMetadata::DataType dataType = meta.dataType();
135
137 return vectors;
138
139 try
140 {
141 vectors.reserve( size.height()*size.width() );
142 }
143 catch ( ... )
144 {
145 QgsDebugMsgLevel( "Unable to store the arrow grid in memory", 1 );
146 return QVector<QgsVector>();
147 }
148
149 for ( int iy = 0; iy < size.height(); ++iy )
150 {
151 const double y = minCorner.y() + iy * ySpacing;
152 for ( int ix = 0; ix < size.width(); ++ix )
153 {
154 const double x = minCorner.x() + ix * xSpacing;
155 const QgsPoint point( x, y );
156 const int faceIndex = triangularMesh->faceIndexForPoint_v2( point );
157 int nativeFaceIndex = -1;
158 if ( faceIndex != -1 )
159 nativeFaceIndex = triangularMesh->trianglesToNativeFaces().at( faceIndex );
161 if ( nativeFaceIndex != -1 && isFacesActive.active( nativeFaceIndex ) )
162 {
163 switch ( dataType )
164 {
167 value = vals.value( nativeFaceIndex );
168 break;
170 {
171 const QgsMeshFace &face = triangularMesh->triangles()[faceIndex];
172 const int v1 = face[0], v2 = face[1], v3 = face[2];
173 const QgsPoint p1 = triangularMesh->vertices()[v1], p2 = triangularMesh->vertices()[v2], p3 = triangularMesh->vertices()[v3];
174 const QgsMeshDatasetValue val1 = vals.value( v1 );
175 const QgsMeshDatasetValue val2 = vals.value( v2 );
176 const QgsMeshDatasetValue val3 = vals.value( v3 );
177 const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
178 const double y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
179 value = QgsMeshDatasetValue( x, y );
180 }
181 break;
183 break;
184 }
185 }
186 vectors.append( QgsVector( value.x(), value.y() ) );
187 }
188 }
189 return vectors;
190}
191
192QVector<double> QgsMeshLayerUtils::calculateMagnitudes( const QgsMeshDataBlock &block )
193{
194 Q_ASSERT( QgsMeshDataBlock::ActiveFlagInteger != block.type() );
195 const int count = block.count();
196 QVector<double> ret( count );
197
198 for ( int i = 0; i < count; ++i )
199 {
200 const double mag = block.value( i ).scalar();
201 ret[i] = mag;
202 }
203 return ret;
204}
205
206QgsRectangle QgsMeshLayerUtils::boundingBoxToScreenRectangle(
207 const QgsMapToPixel &mtp,
208 const QgsRectangle &bbox,
209 double devicePixelRatio
210)
211{
212 const QgsPointXY topLeft = mtp.transform( bbox.xMinimum(), bbox.yMaximum() ) * devicePixelRatio;
213 const QgsPointXY topRight = mtp.transform( bbox.xMaximum(), bbox.yMaximum() ) * devicePixelRatio;
214 const QgsPointXY bottomLeft = mtp.transform( bbox.xMinimum(), bbox.yMinimum() ) * devicePixelRatio;
215 const QgsPointXY bottomRight = mtp.transform( bbox.xMaximum(), bbox.yMinimum() ) * devicePixelRatio;
216
217 const double xMin = std::min( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
218 const double xMax = std::max( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
219 const double yMin = std::min( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );
220 const double yMax = std::max( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );
221
222 const QgsRectangle ret( xMin, yMin, xMax, yMax );
223 return ret;
224}
225
226void QgsMeshLayerUtils::boundingBoxToScreenRectangle(
227 const QgsMapToPixel &mtp,
228 const QSize &outputSize,
229 const QgsRectangle &bbox,
230 int &leftLim,
231 int &rightLim,
232 int &bottomLim,
233 int &topLim,
234 double devicePixelRatio )
235{
236 const QgsRectangle screenBBox = boundingBoxToScreenRectangle( mtp, bbox, devicePixelRatio );
237
238 bottomLim = std::max( int( screenBBox.yMinimum() ), 0 );
239 topLim = std::min( int( screenBBox.yMaximum() ), outputSize.height() - 1 );
240 leftLim = std::max( int( screenBBox.xMinimum() ), 0 );
241 rightLim = std::min( int( screenBBox.xMaximum() ), outputSize.width() - 1 );
242}
243
244static void lamTol( double &lam )
245{
246 const static double eps = 1e-6;
247 if ( ( lam < 0.0 ) && ( lam > -eps ) )
248 {
249 lam = 0.0;
250 }
251}
252
253static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
254 double &lam1, double &lam2, double &lam3 )
255{
256 // Compute vectors
257 const double xa = pA.x();
258 const double ya = pA.y();
259 const double v0x = pC.x() - xa ;
260 const double v0y = pC.y() - ya ;
261 const double v1x = pB.x() - xa ;
262 const double v1y = pB.y() - ya ;
263 const double v2x = pP.x() - xa ;
264 const double v2y = pP.y() - ya ;
265
266 // Compute dot products
267 const double dot00 = v0x * v0x + v0y * v0y;
268 const double dot01 = v0x * v1x + v0y * v1y;
269 const double dot02 = v0x * v2x + v0y * v2y;
270 const double dot11 = v1x * v1x + v1y * v1y;
271 const double dot12 = v1x * v2x + v1y * v2y;
272
273 // Compute barycentric coordinates
274 double invDenom = dot00 * dot11 - dot01 * dot01;
275 if ( invDenom == 0 )
276 return false;
277 invDenom = 1.0 / invDenom;
278 lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
279 lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
280 lam3 = 1.0 - lam1 - lam2;
281
282 // Apply some tolerance to lam so we can detect correctly border points
283 lamTol( lam1 );
284 lamTol( lam2 );
285 lamTol( lam3 );
286
287 // Return if POI is outside triangle
288 if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
289 {
290 return false;
291 }
292
293 return true;
294}
295
296bool QgsMeshLayerUtils::calculateBarycentricCoordinates(
297 const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
298 double &lam1, double &lam2, double &lam3 )
299{
300 return E3T_physicalToBarycentric( pA, pB, pC, pP, lam1, lam2, lam3 );
301}
302
303double QgsMeshLayerUtils::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
304 double val1, double val2, double val3, const QgsPointXY &pt )
305{
306 double lam1, lam2, lam3;
307 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
308 return std::numeric_limits<double>::quiet_NaN();
309
310 return lam1 * val3 + lam2 * val2 + lam3 * val1;
311}
312
313double QgsMeshLayerUtils::interpolateZForPoint( const QgsTriangularMesh &mesh, double x, double y )
314{
315 const QgsPointXY point( x, y );
316 const int faceIndex = mesh.faceIndexForPoint_v2( point );
317 if ( faceIndex < 0 || faceIndex >= mesh.triangles().count() )
318 return std::numeric_limits<float>::quiet_NaN();
319
320 const QgsMeshFace &face = mesh.triangles().at( faceIndex );
321
322 const QgsPoint p1 = mesh.vertices().at( face.at( 0 ) );
323 const QgsPoint p2 = mesh.vertices().at( face.at( 1 ) );
324 const QgsPoint p3 = mesh.vertices().at( face.at( 2 ) );
325
326 return QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, p1.z(), p2.z(), p3.z(), point );
327}
328
329double QgsMeshLayerUtils::interpolateFromVerticesData( double fraction, double val1, double val2 )
330{
331 if ( std::isnan( val1 ) || std::isnan( val2 ) || ( fraction < 0 ) || ( fraction > 1 ) )
332 {
333 return std::numeric_limits<double>::quiet_NaN();
334 }
335 return val1 + ( val2 - val1 ) * fraction;
336}
337
338QgsMeshDatasetValue QgsMeshLayerUtils::interpolateFromVerticesData( double fraction, const QgsMeshDatasetValue &val1, const QgsMeshDatasetValue &val2 )
339{
340 return QgsMeshDatasetValue( interpolateFromVerticesData( fraction, val1.x(), val2.x() ),
341 interpolateFromVerticesData( fraction, val1.y(), val2.y() ) );
342}
343
344
345QgsVector QgsMeshLayerUtils::interpolateVectorFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, QgsVector vect1, QgsVector vect2, QgsVector vect3, const QgsPointXY &pt )
346{
347 double lam1, lam2, lam3;
348 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
349 return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
350
351 return vect3 * lam1 + vect2 * lam2 + vect1 * lam3;
352}
353
354double QgsMeshLayerUtils::interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
355 double val, const QgsPointXY &pt )
356{
357 double lam1, lam2, lam3;
358 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
359 return std::numeric_limits<double>::quiet_NaN();
360
361 return val;
362}
363
364QgsVector QgsMeshLayerUtils::interpolateVectorFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
365 QgsVector vect, const QgsPointXY &pt )
366{
367 double lam1, lam2, lam3;
368 if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
369 return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
370
371 return vect;
372}
373
374
375QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
376 QVector<double> valuesOnFaces,
377 const QgsMesh *nativeMesh,
378 const QgsTriangularMesh *triangularMesh,
379 QgsMeshDataBlock *active,
381{
382 assert( nativeMesh );
383 Q_UNUSED( method );
384 Q_UNUSED( triangularMesh );
385
386 // assuming that native vertex count = triangular vertex count
387 assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
388
389 return interpolateFromFacesData( valuesOnFaces, *nativeMesh, active, method );
390}
391
392QVector<double> QgsMeshLayerUtils::interpolateFromFacesData( const QVector<double> &valuesOnFaces,
393 const QgsMesh &nativeMesh,
394 QgsMeshDataBlock *active,
396{
397
398 QgsMeshDataBlock activeFace;
399 if ( active )
400 activeFace = *active;
401 else
402 {
404 activeFace.setValid( true );
405 }
406
407 return interpolateFromFacesData( valuesOnFaces, nativeMesh, activeFace, method );
408
409}
410
411QVector<double> QgsMeshLayerUtils::interpolateFromFacesData( const QVector<double> &valuesOnFaces, const QgsMesh &nativeMesh, const QgsMeshDataBlock &active, QgsMeshRendererScalarSettings::DataResamplingMethod method )
412{
413 const int vertexCount = nativeMesh.vertexCount();
414 Q_UNUSED( method );
416
417 QVector<double> res( vertexCount, 0.0 );
418 // for face datasets do simple average of the valid values of all faces that contains this vertex
419 QVector<int> count( vertexCount, 0 );
420
421 for ( int i = 0; i < nativeMesh.faceCount(); ++i )
422 {
423 if ( active.active( i ) )
424 {
425 const double val = valuesOnFaces[ i ];
426 if ( !std::isnan( val ) )
427 {
428 // assign for all vertices
429 const QgsMeshFace &face = nativeMesh.faces.at( i );
430 for ( int j = 0; j < face.size(); ++j )
431 {
432 const int vertexIndex = face[j];
433 res[vertexIndex] += val;
434 count[vertexIndex] += 1;
435 }
436 }
437 }
438 }
439
440 for ( int i = 0; i < vertexCount; ++i )
441 {
442 if ( count.at( i ) > 0 )
443 {
444 res[i] = res[i] / double( count.at( i ) );
445 }
446 else
447 {
448 res[i] = std::numeric_limits<double>::quiet_NaN();
449 }
450 }
451
452 return res;
453}
454
455QVector<double> QgsMeshLayerUtils::resampleFromVerticesToFaces(
456 const QVector<double> valuesOnVertices,
457 const QgsMesh *nativeMesh,
458 const QgsTriangularMesh *triangularMesh,
459 const QgsMeshDataBlock *active,
461{
462 assert( nativeMesh );
463 Q_UNUSED( method );
465
466 // assuming that native vertex count = triangular vertex count
467 Q_UNUSED( triangularMesh );
468 assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
469
470 QVector<double> ret( nativeMesh->faceCount(), std::numeric_limits<double>::quiet_NaN() );
471
472 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
473 {
474 const QgsMeshFace face = nativeMesh->face( i );
475 if ( active->active( i ) && face.count() > 2 )
476 {
477 double value = 0;
478 for ( int j = 0; j < face.count(); ++j )
479 {
480 value += valuesOnVertices.at( face.at( j ) );
481 }
482 ret[i] = value / face.count();
483 }
484 }
485
486 return ret;
487}
488
489QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMeshLayer *meshLayer,
490 const QgsMeshDatasetIndex index,
491 QgsMeshDataBlock *activeFaceFlagValues,
493{
494 QVector<double> ret;
495
496 if ( !meshLayer || !index.isValid() )
497 return ret;
498
499 const QgsTriangularMesh *triangularMesh = meshLayer->triangularMesh();
500 const QgsMesh *nativeMesh = meshLayer->nativeMesh();
501 if ( !triangularMesh || !nativeMesh )
502 return ret;
503
504 const QgsMeshDatasetGroupMetadata metadata = meshLayer->datasetGroupMetadata( index );
505 const bool scalarDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
506
507 // populate scalar values
508 const int datacount = scalarDataOnVertices ? nativeMesh->vertices.count() : nativeMesh->faces.count();
509 const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
510 meshLayer,
511 index,
512 0,
513 datacount );
514
515 QgsMeshDataBlock activeFace;
516 if ( !activeFaceFlagValues )
517 {
519 activeFace.setValid( true );
520 }
521 else
522 activeFace = *activeFaceFlagValues;
523
524 return calculateMagnitudeOnVertices( *nativeMesh, metadata, vals, activeFace, method );
525}
526
527QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMesh &nativeMesh,
528 const QgsMeshDatasetGroupMetadata &groupMetadata,
529 const QgsMeshDataBlock &datasetValues,
530 const QgsMeshDataBlock &activeFaceFlagValues,
532{
533 QVector<double> ret;
534 const bool scalarDataOnVertices = groupMetadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
535
536 if ( datasetValues.isValid() )
537 {
538 ret = QgsMeshLayerUtils::calculateMagnitudes( datasetValues );
539
540 if ( !scalarDataOnVertices )
541 {
542 //Need to interpolate data on vertices
543 ret = QgsMeshLayerUtils::interpolateFromFacesData(
544 ret,
545 nativeMesh,
546 activeFaceFlagValues,
547 method );
548 }
549 }
550 return ret;
551}
552
553QgsRectangle QgsMeshLayerUtils::triangleBoundingBox( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 )
554{
555 // p1
556 double xMin = p1.x();
557 double xMax = p1.x();
558 double yMin = p1.y();
559 double yMax = p1.y();
560
561 //p2
562 xMin = ( ( xMin < p2.x() ) ? xMin : p2.x() );
563 xMax = ( ( xMax > p2.x() ) ? xMax : p2.x() );
564 yMin = ( ( yMin < p2.y() ) ? yMin : p2.y() );
565 yMax = ( ( yMax > p2.y() ) ? yMax : p2.y() );
566
567 // p3
568 xMin = ( ( xMin < p3.x() ) ? xMin : p3.x() );
569 xMax = ( ( xMax > p3.x() ) ? xMax : p3.x() );
570 yMin = ( ( yMin < p3.y() ) ? yMin : p3.y() );
571 yMax = ( ( yMax > p3.y() ) ? yMax : p3.y() );
572
573 const QgsRectangle bbox( xMin, yMin, xMax, yMax );
574 return bbox;
575}
576
577QString QgsMeshLayerUtils::formatTime( double hours, const QDateTime &referenceTime, const QgsMeshTimeSettings &settings )
578{
579 QString ret;
580
581 if ( referenceTime.isValid() )
582 {
583 const QString format( settings.absoluteTimeFormat() );
584 QDateTime dateTime( referenceTime );
585 const qint64 seconds = static_cast<qint64>( hours * 3600.0 );
586 dateTime = dateTime.addSecs( seconds );
587 ret = dateTime.toString( format );
588 if ( ret.isEmpty() ) // error
589 ret = dateTime.toString();
590 }
591 else
592 {
593 QString format( settings.relativeTimeFormat() );
594 format = format.trimmed();
595 const int totalHours = static_cast<int>( hours );
596
597 if ( format == "hh:mm:ss.zzz"_L1 )
598 {
599 const int ms = static_cast<int>( hours * 3600.0 * 1000 );
600 const int seconds = ms / 1000;
601 const int z = ms % 1000;
602 int m = seconds / 60;
603 const int s = seconds % 60;
604 const int h = m / 60;
605 m = m % 60;
606 ret = u"%1:%2:%3.%4"_s.
607 arg( h, 2, 10, '0'_L1 ).
608 arg( m, 2, 10, '0'_L1 ).
609 arg( s, 2, 10, '0'_L1 ).
610 arg( z, 3, 10, '0'_L1 );
611 }
612 else if ( format == "hh:mm:ss"_L1 )
613 {
614 const int seconds = static_cast<int>( hours * 3600.0 );
615 int m = seconds / 60;
616 const int s = seconds % 60;
617 const int h = m / 60;
618 m = m % 60;
619 ret = u"%1:%2:%3"_s.
620 arg( h, 2, 10, '0'_L1 ).
621 arg( m, 2, 10, '0'_L1 ).
622 arg( s, 2, 10, '0'_L1 );
623
624 }
625 else if ( format == "d hh:mm:ss"_L1 )
626 {
627 const int seconds = static_cast<int>( hours * 3600.0 );
628 int m = seconds / 60;
629 const int s = seconds % 60;
630 int h = m / 60;
631 m = m % 60;
632 const int d = totalHours / 24;
633 h = totalHours % 24;
634 ret = u"%1 d %2:%3:%4"_s.
635 arg( d ).
636 arg( h, 2, 10, '0'_L1 ).
637 arg( m, 2, 10, '0'_L1 ).
638 arg( s, 2, 10, '0'_L1 );
639 }
640 else if ( format == "d hh"_L1 )
641 {
642 const int d = totalHours / 24;
643 const int h = totalHours % 24;
644 ret = u"%1 d %2"_s.
645 arg( d ).
646 arg( h );
647 }
648 else if ( format == "d"_L1 )
649 {
650 const int d = totalHours / 24;
651 ret = QString::number( d );
652 }
653 else if ( format == "ss"_L1 )
654 {
655 const int seconds = static_cast<int>( hours * 3600.0 );
656 ret = QString::number( seconds );
657 }
658 else // "hh"
659 {
660 ret = QString::number( hours );
661 }
662 }
663 return ret;
664}
665
666QVector<QVector3D> QgsMeshLayerUtils::calculateNormals( const QgsTriangularMesh &triangularMesh, const QVector<double> &verticalMagnitude, bool isRelative )
667{
668 QVector<QVector3D> normals( triangularMesh.vertices().count() );
669 for ( const auto &face : triangularMesh.triangles() )
670 {
671 for ( int i = 0; i < 3; i++ )
672 {
673 const int index( face.at( i ) );
674 const int index1( face.at( ( i + 1 ) % 3 ) );
675 const int index2( face.at( ( i + 2 ) % 3 ) );
676
677 if ( std::isnan( verticalMagnitude[index] ) ||
678 std::isnan( verticalMagnitude[index1] ) ||
679 std::isnan( verticalMagnitude[index2] ) )
680 continue;
681
682 const QgsMeshVertex &vert( triangularMesh.vertices().at( index ) );
683 const QgsMeshVertex &otherVert1( triangularMesh.vertices().at( index1 ) );
684 const QgsMeshVertex &otherVert2( triangularMesh.vertices().at( index2 ) );
685
686 float adjustRelative = 0;
687 float adjustRelative1 = 0;
688 float adjustRelative2 = 0;
689
690 if ( isRelative )
691 {
692 adjustRelative = vert.z();
693 adjustRelative1 = otherVert1.z();
694 adjustRelative2 = otherVert2.z();
695 }
696
697 const QVector3D v1( float( otherVert1.x() - vert.x() ),
698 float( otherVert1.y() - vert.y() ),
699 float( verticalMagnitude[index1] - verticalMagnitude[index] + adjustRelative1 - adjustRelative ) );
700 const QVector3D v2( float( otherVert2.x() - vert.x() ),
701 float( otherVert2.y() - vert.y() ),
702 float( verticalMagnitude[index2] - verticalMagnitude[index] + adjustRelative2 - adjustRelative ) );
703
704 normals[index] += QVector3D::crossProduct( v1, v2 );
705 }
706 }
707
708 return normals;
709}
710
711bool QgsMeshLayerUtils::haveSameParentQuantity( const QgsMeshLayer *layer, const QgsMeshDatasetIndex &index1, const QgsMeshDatasetIndex &index2 )
712{
713 const QgsMeshDatasetGroupMetadata metadata1 = layer->datasetGroupMetadata( index1 );
714 if ( metadata1.parentQuantityName().isEmpty() )
715 return false;
716
717 const QgsMeshDatasetGroupMetadata metadata2 = layer->datasetGroupMetadata( index2 );
718 if ( metadata2.parentQuantityName().isEmpty() )
719 return false;
720
721 return metadata1.parentQuantityName().compare( metadata2.parentQuantityName(), Qt::CaseInsensitive ) == 0;
722}
723
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.