28using namespace Qt::StringLiterals;
31static const double eps = 1e-6;
40 std::unique_ptr<QgsMesh3DAveragingMethod> ret;
43 elem.attribute( u
"method"_s ).toInt() );
47 ret = std::make_unique<QgsMeshMultiLevelsAveragingMethod>( );
50 ret = std::make_unique<QgsMeshSigmaAveragingMethod>( );
53 ret = std::make_unique<QgsMeshRelativeHeightAveragingMethod>( );
56 ret = std::make_unique<QgsMeshElevationAveragingMethod>( );
68 if ( !hasValidInputs() )
71 const bool isVector = block3d.
isVector();
72 const int count = block3d.
count();
74 QVector<double> valuesFaces( isVector ? 2 * count : count, std::numeric_limits<double>::quiet_NaN() );
77 const QVector<double> volumeValues = block3d.
values();
79 int startVolumeIndex = 0;
80 for (
int faceIndex = 0; faceIndex < count; ++faceIndex )
87 const int volumesBelowFaceCount = verticalLevelsCount[faceIndex];
88 if ( volumesBelowFaceCount <= 0 )
91 const int startVerticalLevelIndex = startVolumeIndex + faceIndex;
92 Q_ASSERT( verticalLevels.size() >= startVerticalLevelIndex + volumesBelowFaceCount + 1 );
93 QVector<double> verticalLevelsForFace = verticalLevels.mid( startVerticalLevelIndex, volumesBelowFaceCount + 1 );
94 double faceLevelTop = verticalLevelsForFace[0];
95 double faceLevelBottom = verticalLevelsForFace[verticalLevelsForFace.size() - 1];
98 if ( faceLevelTop < faceLevelBottom )
100 std::swap( faceLevelTop, faceLevelBottom );
103 double methodLevelTop = std::numeric_limits<double>::quiet_NaN();
104 double methodLevelBottom = std::numeric_limits<double>::quiet_NaN();
106 int singleVerticalIndex = -1;
107 volumeRangeForFace( methodLevelTop,
110 verticalLevelsForFace );
112 if ( singleVerticalIndex != -1 )
114 int volumeIndex = singleVerticalIndex + startVolumeIndex;
117 valuesFaces[2 * faceIndex] = volumeValues.at( 2 * volumeIndex );
118 valuesFaces[2 * faceIndex + 1 ] = volumeValues.at( 2 * volumeIndex + 1 );
122 valuesFaces[faceIndex] = volumeValues.at( volumeIndex );
125 else if ( !std::isnan( methodLevelTop ) && !std::isnan( methodLevelBottom ) )
128 if ( methodLevelTop < methodLevelBottom )
130 std::swap( methodLevelTop, methodLevelBottom );
134 if ( ( methodLevelTop >= faceLevelBottom ) && ( methodLevelBottom <= faceLevelTop ) )
136 averageVolumeValuesForFace(
138 volumesBelowFaceCount,
143 verticalLevelsForFace,
151 startVolumeIndex += volumesBelowFaceCount;
162void QgsMesh3DAveragingMethod::averageVolumeValuesForFace(
164 int volumesBelowFaceCount,
165 int startVolumeIndex,
166 double methodLevelTop,
167 double methodLevelBottom,
169 const QVector<double> &verticalLevelsForFace,
170 const QVector<double> &volumeValues,
171 QVector<double> &valuesFaces
174 double totalAveragedHeight = 0;
179 for (
int relativeVolumeIndex = 0; relativeVolumeIndex < volumesBelowFaceCount; ++relativeVolumeIndex )
181 const int volumeIndex = startVolumeIndex + relativeVolumeIndex;
182 double volumeLevelTop = verticalLevelsForFace[relativeVolumeIndex];
183 double volumeLevelBottom = verticalLevelsForFace[relativeVolumeIndex + 1];
184 if ( volumeLevelTop < volumeLevelBottom )
186 std::swap( volumeLevelTop, volumeLevelBottom );
189 const double intersectionLevelTop = std::min( methodLevelTop, volumeLevelTop );
190 const double intersectionLevelBottom = std::max( methodLevelBottom, volumeLevelBottom );
191 const double effectiveInterval = intersectionLevelTop - intersectionLevelBottom;
193 if ( effectiveInterval > eps )
197 const double x = volumeValues[2 * volumeIndex ];
198 const double y = volumeValues[ 2 * volumeIndex + 1 ];
199 if ( ! std::isnan( x ) &&
203 nSumX += x * effectiveInterval;
204 nSumY += y * effectiveInterval;
205 totalAveragedHeight += effectiveInterval;
210 const double x = volumeValues[ volumeIndex ];
211 if ( ! std::isnan( x ) )
213 nSumX += x * effectiveInterval;
214 totalAveragedHeight += effectiveInterval;
221 if ( totalAveragedHeight > eps )
225 valuesFaces[2 * faceIndex] = nSumX / totalAveragedHeight;
226 valuesFaces[2 * faceIndex + 1 ] = nSumY / totalAveragedHeight;
230 valuesFaces[faceIndex] = nSumX / totalAveragedHeight;
245 , mStartVerticalLevel( startLevel )
246 , mEndVerticalLevel( endLevel )
249 if ( mStartVerticalLevel > mEndVerticalLevel )
251 std::swap( mStartVerticalLevel, mEndVerticalLevel );
262 , mStartVerticalLevel( verticalLevel )
263 , mEndVerticalLevel( verticalLevel )
272 QDomElement elem = doc.createElement( u
"multi-vertical-layers-settings"_s );
280 const QDomElement settings = elem.firstChildElement( u
"multi-vertical-layers-settings"_s );
281 if ( !settings.isNull() )
283 mStartVerticalLevel = settings.attribute( u
"start-layer-index"_s ).toInt();
284 mEndVerticalLevel = settings.attribute( u
"end-layer-index"_s ).toInt();
285 if ( mStartVerticalLevel > mEndVerticalLevel )
287 std::swap( mStartVerticalLevel, mEndVerticalLevel );
312 return mStartVerticalLevel;
317 return mEndVerticalLevel;
320bool QgsMeshMultiLevelsAveragingMethod::hasValidInputs()
const
322 return mStartVerticalLevel >= 1 && mEndVerticalLevel >= mStartVerticalLevel;
325void QgsMeshMultiLevelsAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
326 double &endVerticalLevel,
327 int &singleVerticalIndex,
328 const QVector<double> &verticalLevels )
const
330 Q_ASSERT( mStartVerticalLevel <= mEndVerticalLevel );
334 const int startIndex = mStartVerticalLevel - 1;
335 if ( mStartVerticalLevel == mEndVerticalLevel )
337 if ( startIndex >= 0 && startIndex < verticalLevels.size() - 1 )
338 singleVerticalIndex = startIndex;
342 if ( startIndex >= 0 && startIndex < verticalLevels.size() )
347 if ( mEndVerticalLevel >= 0 && mEndVerticalLevel < verticalLevels.size() )
359 const int volumesBelowFaceCount = verticalLevels.size() - 1;
360 const int startIndex = volumesBelowFaceCount - mEndVerticalLevel;
361 if ( mStartVerticalLevel == mEndVerticalLevel )
363 if ( startIndex >= 0 && startIndex < verticalLevels.size() - 1 )
364 singleVerticalIndex = startIndex;
368 if ( startIndex >= 0 && startIndex < verticalLevels.size() )
377 const int endIndex = volumesBelowFaceCount - mStartVerticalLevel + 1;
378 if ( endIndex >= 0 && endIndex < verticalLevels.size() )
396 if ( mStartFraction > mEndFraction )
398 std::swap( mStartFraction, mEndFraction );
406 QDomElement elem = doc.createElement( u
"sigma-settings"_s );
408 elem.setAttribute( u
"end-fraction"_s,
endFraction() );
414 const QDomElement settings = elem.firstChildElement( u
"sigma-settings"_s );
415 if ( !settings.isNull() )
417 mStartFraction = settings.attribute( u
"start-fraction"_s ).toDouble();
418 mEndFraction = settings.attribute( u
"end-fraction"_s ).toDouble();
419 if ( mStartFraction > mEndFraction )
421 std::swap( mStartFraction, mEndFraction );
443 return mStartFraction;
451bool QgsMeshSigmaAveragingMethod::hasValidInputs()
const
453 return mStartFraction >= 0 && mEndFraction >= mStartFraction && mEndFraction <= 1;
456void QgsMeshSigmaAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
457 double &endVerticalLevel,
459 const QVector<double> &verticalLevels )
const
461 const double top = verticalLevels[ 0 ];
462 const double bot = verticalLevels[ verticalLevels.size() - 1 ];
463 const double diff = top - bot;
465 if ( mStartFraction < 0 )
466 startVerticalLevel = bot;
468 startVerticalLevel = bot + diff * mStartFraction;
470 if ( mEndFraction > 1 )
471 endVerticalLevel = top;
473 endVerticalLevel = bot + diff * mEndFraction;
478 return mCountedFromTop;
483 return mStartVerticalLevel == mEndVerticalLevel;
494 , mStartHeight( startDepth )
495 , mEndHeight( endDepth )
498 if ( mStartHeight > mEndHeight )
500 std::swap( mStartHeight, mEndHeight );
508 QDomElement elem = doc.createElement( u
"relative-height-settings"_s );
509 elem.setAttribute( u
"start-height"_s,
startHeight() );
510 elem.setAttribute( u
"end-height"_s,
endHeight() );
516 const QDomElement settings = elem.firstChildElement( u
"relative-height-settings"_s );
517 if ( !settings.isNull() )
519 mStartHeight = settings.attribute( u
"start-height"_s ).toDouble();
520 mEndHeight = settings.attribute( u
"end-height"_s ).toDouble();
521 if ( mStartHeight > mEndHeight )
523 std::swap( mStartHeight, mEndHeight );
555bool QgsMeshRelativeHeightAveragingMethod::hasValidInputs()
const
557 return mStartHeight >= 0 && mEndHeight >= mStartHeight;
560void QgsMeshRelativeHeightAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
561 double &endVerticalLevel,
563 const QVector<double> &verticalLevels )
const
567 const double top = verticalLevels[ 0 ];
568 startVerticalLevel = top - mStartHeight;
569 endVerticalLevel = top - mEndHeight;
573 const double bot = verticalLevels[verticalLevels.size() - 1];
574 startVerticalLevel = bot + mStartHeight;
575 endVerticalLevel = bot + mEndHeight;
581 return mCountedFromTop;
594 if ( mEndElevation > mStartElevation )
596 std::swap( mEndElevation, mStartElevation );
604 QDomElement elem = doc.createElement( u
"elevation-settings"_s );
606 elem.setAttribute( u
"end-elevation"_s,
endElevation() );
612 const QDomElement settings = elem.firstChildElement( u
"elevation-settings"_s );
613 if ( !settings.isNull() )
615 mStartElevation = settings.attribute( u
"start-elevation"_s ).toDouble();
616 mEndElevation = settings.attribute( u
"end-elevation"_s ).toDouble();
617 if ( mEndElevation > mStartElevation )
619 std::swap( mEndElevation, mStartElevation );
641 return mStartElevation;
646 return mEndElevation;
649bool QgsMeshElevationAveragingMethod::hasValidInputs()
const
651 return mStartElevation <= 0.0 && mEndElevation <= mStartElevation;
654void QgsMeshElevationAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
655 double &endVerticalLevel,
657 const QVector<double> &verticalLevels )
const
659 Q_UNUSED( verticalLevels )
660 startVerticalLevel = mStartElevation;
661 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).