27static const double eps = 1e-6;
36 std::unique_ptr<QgsMesh3DAveragingMethod> ret;
39 elem.attribute( QStringLiteral(
"method" ) ).toInt() );
43 ret = std::make_unique<QgsMeshMultiLevelsAveragingMethod>( );
46 ret = std::make_unique<QgsMeshSigmaAveragingMethod>( );
49 ret = std::make_unique<QgsMeshRelativeHeightAveragingMethod>( );
52 ret = std::make_unique<QgsMeshElevationAveragingMethod>( );
64 if ( !hasValidInputs() )
67 const bool isVector = block3d.
isVector();
68 const int count = block3d.
count();
70 QVector<double> valuesFaces( isVector ? 2 * count : count, std::numeric_limits<double>::quiet_NaN() );
73 const QVector<double> volumeValues = block3d.
values();
75 int startVolumeIndex = 0;
76 for (
int faceIndex = 0; faceIndex < count; ++faceIndex )
83 const int volumesBelowFaceCount = verticalLevelsCount[faceIndex];
84 if ( volumesBelowFaceCount <= 0 )
87 const int startVerticalLevelIndex = startVolumeIndex + faceIndex;
88 Q_ASSERT( verticalLevels.size() >= startVerticalLevelIndex + volumesBelowFaceCount + 1 );
89 QVector<double> verticalLevelsForFace = verticalLevels.mid( startVerticalLevelIndex, volumesBelowFaceCount + 1 );
90 double faceLevelTop = verticalLevelsForFace[0];
91 double faceLevelBottom = verticalLevelsForFace[verticalLevelsForFace.size() - 1];
94 if ( faceLevelTop < faceLevelBottom )
96 std::swap( faceLevelTop, faceLevelBottom );
99 double methodLevelTop = std::numeric_limits<double>::quiet_NaN();
100 double methodLevelBottom = std::numeric_limits<double>::quiet_NaN();
102 int singleVerticalIndex = -1;
103 volumeRangeForFace( methodLevelTop,
106 verticalLevelsForFace );
108 if ( singleVerticalIndex != -1 )
110 int volumeIndex = singleVerticalIndex + startVolumeIndex;
113 valuesFaces[2 * faceIndex] = volumeValues.at( 2 * volumeIndex );
114 valuesFaces[2 * faceIndex + 1 ] = volumeValues.at( 2 * volumeIndex + 1 );
118 valuesFaces[faceIndex] = volumeValues.at( volumeIndex );
121 else if ( !std::isnan( methodLevelTop ) && !std::isnan( methodLevelBottom ) )
124 if ( methodLevelTop < methodLevelBottom )
126 std::swap( methodLevelTop, methodLevelBottom );
130 if ( ( methodLevelTop >= faceLevelBottom ) && ( methodLevelBottom <= faceLevelTop ) )
132 averageVolumeValuesForFace(
134 volumesBelowFaceCount,
139 verticalLevelsForFace,
147 startVolumeIndex += volumesBelowFaceCount;
158void QgsMesh3DAveragingMethod::averageVolumeValuesForFace(
160 int volumesBelowFaceCount,
161 int startVolumeIndex,
162 double methodLevelTop,
163 double methodLevelBottom,
165 const QVector<double> &verticalLevelsForFace,
166 const QVector<double> &volumeValues,
167 QVector<double> &valuesFaces
170 double totalAveragedHeight = 0;
175 for (
int relativeVolumeIndex = 0; relativeVolumeIndex < volumesBelowFaceCount; ++relativeVolumeIndex )
177 const int volumeIndex = startVolumeIndex + relativeVolumeIndex;
178 double volumeLevelTop = verticalLevelsForFace[relativeVolumeIndex];
179 double volumeLevelBottom = verticalLevelsForFace[relativeVolumeIndex + 1];
180 if ( volumeLevelTop < volumeLevelBottom )
182 std::swap( volumeLevelTop, volumeLevelBottom );
185 const double intersectionLevelTop = std::min( methodLevelTop, volumeLevelTop );
186 const double intersectionLevelBottom = std::max( methodLevelBottom, volumeLevelBottom );
187 const double effectiveInterval = intersectionLevelTop - intersectionLevelBottom;
189 if ( effectiveInterval > eps )
193 const double x = volumeValues[2 * volumeIndex ];
194 const double y = volumeValues[ 2 * volumeIndex + 1 ];
195 if ( ! std::isnan( x ) &&
199 nSumX += x * effectiveInterval;
200 nSumY += y * effectiveInterval;
201 totalAveragedHeight += effectiveInterval;
206 const double x = volumeValues[ volumeIndex ];
207 if ( ! std::isnan( x ) )
209 nSumX += x * effectiveInterval;
210 totalAveragedHeight += effectiveInterval;
217 if ( totalAveragedHeight > eps )
221 valuesFaces[2 * faceIndex] = nSumX / totalAveragedHeight;
222 valuesFaces[2 * faceIndex + 1 ] = nSumY / totalAveragedHeight;
226 valuesFaces[faceIndex] = nSumX / totalAveragedHeight;
241 , mStartVerticalLevel( startLevel )
242 , mEndVerticalLevel( endLevel )
245 if ( mStartVerticalLevel > mEndVerticalLevel )
247 std::swap( mStartVerticalLevel, mEndVerticalLevel );
258 , mStartVerticalLevel( verticalLevel )
259 , mEndVerticalLevel( verticalLevel )
268 QDomElement elem = doc.createElement( QStringLiteral(
"multi-vertical-layers-settings" ) );
270 elem.setAttribute( QStringLiteral(
"end-layer-index" ),
endVerticalLevel() );
276 const QDomElement settings = elem.firstChildElement( QStringLiteral(
"multi-vertical-layers-settings" ) );
277 if ( !settings.isNull() )
279 mStartVerticalLevel = settings.attribute( QStringLiteral(
"start-layer-index" ) ).toInt();
280 mEndVerticalLevel = settings.attribute( QStringLiteral(
"end-layer-index" ) ).toInt();
281 if ( mStartVerticalLevel > mEndVerticalLevel )
283 std::swap( mStartVerticalLevel, mEndVerticalLevel );
308 return mStartVerticalLevel;
313 return mEndVerticalLevel;
316bool QgsMeshMultiLevelsAveragingMethod::hasValidInputs()
const
318 return mStartVerticalLevel >= 1 && mEndVerticalLevel >= mStartVerticalLevel;
321void QgsMeshMultiLevelsAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
322 double &endVerticalLevel,
323 int &singleVerticalIndex,
324 const QVector<double> &verticalLevels )
const
326 Q_ASSERT( mStartVerticalLevel <= mEndVerticalLevel );
330 const int startIndex = mStartVerticalLevel - 1;
331 if ( mStartVerticalLevel == mEndVerticalLevel )
333 if ( startIndex >= 0 && startIndex < verticalLevels.size() - 1 )
334 singleVerticalIndex = startIndex;
338 if ( startIndex >= 0 && startIndex < verticalLevels.size() )
343 if ( mEndVerticalLevel >= 0 && mEndVerticalLevel < verticalLevels.size() )
355 const int volumesBelowFaceCount = verticalLevels.size() - 1;
356 const int startIndex = volumesBelowFaceCount - mEndVerticalLevel;
357 if ( mStartVerticalLevel == mEndVerticalLevel )
359 if ( startIndex >= 0 && startIndex < verticalLevels.size() - 1 )
360 singleVerticalIndex = startIndex;
364 if ( startIndex >= 0 && startIndex < verticalLevels.size() )
373 const int endIndex = volumesBelowFaceCount - mStartVerticalLevel + 1;
374 if ( endIndex >= 0 && endIndex < verticalLevels.size() )
392 if ( mStartFraction > mEndFraction )
394 std::swap( mStartFraction, mEndFraction );
402 QDomElement elem = doc.createElement( QStringLiteral(
"sigma-settings" ) );
403 elem.setAttribute( QStringLiteral(
"start-fraction" ),
startFraction() );
404 elem.setAttribute( QStringLiteral(
"end-fraction" ),
endFraction() );
410 const QDomElement settings = elem.firstChildElement( QStringLiteral(
"sigma-settings" ) );
411 if ( !settings.isNull() )
413 mStartFraction = settings.attribute( QStringLiteral(
"start-fraction" ) ).toDouble();
414 mEndFraction = settings.attribute( QStringLiteral(
"end-fraction" ) ).toDouble();
415 if ( mStartFraction > mEndFraction )
417 std::swap( mStartFraction, mEndFraction );
439 return mStartFraction;
447bool QgsMeshSigmaAveragingMethod::hasValidInputs()
const
449 return mStartFraction >= 0 && mEndFraction >= mStartFraction && mEndFraction <= 1;
452void QgsMeshSigmaAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
453 double &endVerticalLevel,
455 const QVector<double> &verticalLevels )
const
457 const double top = verticalLevels[ 0 ];
458 const double bot = verticalLevels[ verticalLevels.size() - 1 ];
459 const double diff = top - bot;
461 if ( mStartFraction < 0 )
462 startVerticalLevel = bot;
464 startVerticalLevel = bot + diff * mStartFraction;
466 if ( mEndFraction > 1 )
467 endVerticalLevel = top;
469 endVerticalLevel = bot + diff * mEndFraction;
474 return mCountedFromTop;
479 return mStartVerticalLevel == mEndVerticalLevel;
490 , mStartHeight( startDepth )
491 , mEndHeight( endDepth )
494 if ( mStartHeight > mEndHeight )
496 std::swap( mStartHeight, mEndHeight );
504 QDomElement elem = doc.createElement( QStringLiteral(
"relative-height-settings" ) );
505 elem.setAttribute( QStringLiteral(
"start-height" ),
startHeight() );
506 elem.setAttribute( QStringLiteral(
"end-height" ),
endHeight() );
512 const QDomElement settings = elem.firstChildElement( QStringLiteral(
"relative-height-settings" ) );
513 if ( !settings.isNull() )
515 mStartHeight = settings.attribute( QStringLiteral(
"start-height" ) ).toDouble();
516 mEndHeight = settings.attribute( QStringLiteral(
"end-height" ) ).toDouble();
517 if ( mStartHeight > mEndHeight )
519 std::swap( mStartHeight, mEndHeight );
551bool QgsMeshRelativeHeightAveragingMethod::hasValidInputs()
const
553 return mStartHeight >= 0 && mEndHeight >= mStartHeight;
556void QgsMeshRelativeHeightAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
557 double &endVerticalLevel,
559 const QVector<double> &verticalLevels )
const
563 const double top = verticalLevels[ 0 ];
564 startVerticalLevel = top - mStartHeight;
565 endVerticalLevel = top - mEndHeight;
569 const double bot = verticalLevels[verticalLevels.size() - 1];
570 startVerticalLevel = bot + mStartHeight;
571 endVerticalLevel = bot + mEndHeight;
577 return mCountedFromTop;
590 if ( mEndElevation > mStartElevation )
592 std::swap( mEndElevation, mStartElevation );
600 QDomElement elem = doc.createElement( QStringLiteral(
"elevation-settings" ) );
601 elem.setAttribute( QStringLiteral(
"start-elevation" ),
startElevation() );
602 elem.setAttribute( QStringLiteral(
"end-elevation" ),
endElevation() );
608 const QDomElement settings = elem.firstChildElement( QStringLiteral(
"elevation-settings" ) );
609 if ( !settings.isNull() )
611 mStartElevation = settings.attribute( QStringLiteral(
"start-elevation" ) ).toDouble();
612 mEndElevation = settings.attribute( QStringLiteral(
"end-elevation" ) ).toDouble();
613 if ( mEndElevation > mStartElevation )
615 std::swap( mEndElevation, mStartElevation );
637 return mStartElevation;
642 return mEndElevation;
645bool QgsMeshElevationAveragingMethod::hasValidInputs()
const
647 return mStartElevation <= 0.0 && mEndElevation <= mStartElevation;
650void QgsMeshElevationAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
651 double &endVerticalLevel,
653 const QVector<double> &verticalLevels )
const
655 Q_UNUSED( verticalLevels )
656 startVerticalLevel = mStartElevation;
657 endVerticalLevel = mEndElevation;
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
static QgsMesh3DAveragingMethod * createFromXml(const QDomElement &elem)
Creates the instance from XML by calling readXml of derived classes.
static bool equals(const QgsMesh3DAveragingMethod *a, const QgsMesh3DAveragingMethod *b)
Returns whether two methods equal.
QgsMeshDataBlock calculate(const QgsMesh3DDataBlock &block3d, QgsFeedback *feedback=nullptr) const
Calculated 2d block values from 3d stacked mesh values.
Method method() const
Returns type of averaging method.
Method
Type of averaging method.
@ RelativeHeightAveragingMethod
Method to average values defined by range of relative length units to the surface or bed level.
@ MultiLevelsAveragingMethod
Method to average values from selected vertical layers.
@ ElevationAveragingMethod
Method to average values defined by range of absolute length units to the model's datum.
@ SigmaAveragingMethod
Method to average values between 0 (bed level) and 1 (surface).
QgsMesh3DAveragingMethod(Method method)
Ctor.
A block of 3d stacked mesh data related N faces defined on base mesh frame.
QVector< double > values() const
Returns the values at volume centers.
bool isVector() const
Whether we store vector values.
int count() const
Number of 2d faces for which the volume data is stored in the block.
QVector< int > verticalLevelsCount() const
Returns number of vertical level above 2d faces.
bool isValid() const
Whether the block is valid.
QVector< double > verticalLevels() const
Returns the vertical levels height.
A block of integers/doubles from a mesh dataset.
@ ScalarDouble
Scalar double values.
@ Vector2DDouble
Vector double pairs (x1, y1, x2, y2, ... ).
void setValues(const QVector< double > &vals)
Sets values.
double startElevation() const
Returns start elevation.
QgsMeshElevationAveragingMethod()
~QgsMeshElevationAveragingMethod() override
void readXml(const QDomElement &elem) override
Reads configuration from the given DOM element.
double endElevation() const
Returns end elevation.
QgsMesh3DAveragingMethod * clone() const override
Clone the instance.
QDomElement writeXml(QDomDocument &doc) const override
Writes configuration to a new DOM element.
bool equals(const QgsMesh3DAveragingMethod *other) const override
Returns whether method equals to other.
QDomElement writeXml(QDomDocument &doc) const override
Writes configuration to a new DOM element.
QgsMeshMultiLevelsAveragingMethod()
Constructs single level averaging method for 1st (top) vertical level.
~QgsMeshMultiLevelsAveragingMethod() override
int endVerticalLevel() const
Returns ending vertical level.
bool equals(const QgsMesh3DAveragingMethod *other) const override
Returns whether method equals to other.
QgsMesh3DAveragingMethod * clone() const override
Clone the instance.
bool countedFromTop() const
Returns whether the start and end vertical levels are indexed from top (surface) or bottom (bed) leve...
bool isSingleLevel() const
Returns whether the averaging method selects only a single vertical level.
void readXml(const QDomElement &elem) override
Reads configuration from the given DOM element.
int startVerticalLevel() const
Returns starting vertical level.
void readXml(const QDomElement &elem) override
Reads configuration from the given DOM element.
double startHeight() const
Returns starting depth/height.
~QgsMeshRelativeHeightAveragingMethod() override
QgsMeshRelativeHeightAveragingMethod()
Constructs default depth averaging method.
bool countedFromTop() const
Returns whether the start and end vertical levels are relative to top (surface) or bottom (bed) level...
bool equals(const QgsMesh3DAveragingMethod *other) const override
Returns whether method equals to other.
QDomElement writeXml(QDomDocument &doc) const override
Writes configuration to a new DOM element.
double endHeight() const
Returns ending depth/height.
QgsMesh3DAveragingMethod * clone() const override
Clone the instance.
double endFraction() const
Returns ending fraction.
QgsMeshSigmaAveragingMethod()
Constructs the sigma method for whole value range 0-1.
void readXml(const QDomElement &elem) override
Reads configuration from the given DOM element.
bool equals(const QgsMesh3DAveragingMethod *other) const override
Returns whether method equals to other.
QDomElement writeXml(QDomDocument &doc) const override
Writes configuration to a new DOM element.
QgsMesh3DAveragingMethod * clone() const override
Clone the instance.
~QgsMeshSigmaAveragingMethod() override
double startFraction() const
Returns starting fraction.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).