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