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