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