28using namespace Qt::StringLiterals;
31static const double eps = 1e-6;
39 std::unique_ptr<QgsMesh3DAveragingMethod> ret;
45 ret = std::make_unique<QgsMeshMultiLevelsAveragingMethod>();
48 ret = std::make_unique<QgsMeshSigmaAveragingMethod>();
51 ret = std::make_unique<QgsMeshRelativeHeightAveragingMethod>();
54 ret = std::make_unique<QgsMeshElevationAveragingMethod>();
66 if ( !hasValidInputs() )
69 const bool isVector = block3d.
isVector();
70 const int count = block3d.
count();
72 QVector<double> valuesFaces( isVector ? 2 * count : count, std::numeric_limits<double>::quiet_NaN() );
75 const QVector<double> volumeValues = block3d.
values();
77 int startVolumeIndex = 0;
78 for (
int faceIndex = 0; faceIndex < count; ++faceIndex )
85 const int volumesBelowFaceCount = verticalLevelsCount[faceIndex];
86 if ( volumesBelowFaceCount <= 0 )
89 const int startVerticalLevelIndex = startVolumeIndex + faceIndex;
90 Q_ASSERT( verticalLevels.size() >= startVerticalLevelIndex + volumesBelowFaceCount + 1 );
91 QVector<double> verticalLevelsForFace = verticalLevels.mid( startVerticalLevelIndex, volumesBelowFaceCount + 1 );
92 double faceLevelTop = verticalLevelsForFace[0];
93 double faceLevelBottom = verticalLevelsForFace[verticalLevelsForFace.size() - 1];
96 if ( faceLevelTop < faceLevelBottom )
98 std::swap( faceLevelTop, faceLevelBottom );
101 double methodLevelTop = std::numeric_limits<double>::quiet_NaN();
102 double methodLevelBottom = std::numeric_limits<double>::quiet_NaN();
104 int singleVerticalIndex = -1;
105 volumeRangeForFace( methodLevelTop, methodLevelBottom, singleVerticalIndex, verticalLevelsForFace );
107 if ( singleVerticalIndex != -1 )
109 int volumeIndex = singleVerticalIndex + startVolumeIndex;
112 valuesFaces[2 * faceIndex] = volumeValues.at( 2 * volumeIndex );
113 valuesFaces[2 * faceIndex + 1] = volumeValues.at( 2 * volumeIndex + 1 );
117 valuesFaces[faceIndex] = volumeValues.at( volumeIndex );
120 else if ( !std::isnan( methodLevelTop ) && !std::isnan( methodLevelBottom ) )
123 if ( methodLevelTop < methodLevelBottom )
125 std::swap( methodLevelTop, methodLevelBottom );
129 if ( ( methodLevelTop >= faceLevelBottom ) && ( methodLevelBottom <= faceLevelTop ) )
131 averageVolumeValuesForFace( faceIndex, volumesBelowFaceCount, startVolumeIndex, methodLevelTop, methodLevelBottom, isVector, verticalLevelsForFace, volumeValues, valuesFaces );
136 startVolumeIndex += volumesBelowFaceCount;
147void QgsMesh3DAveragingMethod::averageVolumeValuesForFace(
149 int volumesBelowFaceCount,
150 int startVolumeIndex,
151 double methodLevelTop,
152 double methodLevelBottom,
154 const QVector<double> &verticalLevelsForFace,
155 const QVector<double> &volumeValues,
156 QVector<double> &valuesFaces
159 double totalAveragedHeight = 0;
164 for (
int relativeVolumeIndex = 0; relativeVolumeIndex < volumesBelowFaceCount; ++relativeVolumeIndex )
166 const int volumeIndex = startVolumeIndex + relativeVolumeIndex;
167 double volumeLevelTop = verticalLevelsForFace[relativeVolumeIndex];
168 double volumeLevelBottom = verticalLevelsForFace[relativeVolumeIndex + 1];
169 if ( volumeLevelTop < volumeLevelBottom )
171 std::swap( volumeLevelTop, volumeLevelBottom );
174 const double intersectionLevelTop = std::min( methodLevelTop, volumeLevelTop );
175 const double intersectionLevelBottom = std::max( methodLevelBottom, volumeLevelBottom );
176 const double effectiveInterval = intersectionLevelTop - intersectionLevelBottom;
178 if ( effectiveInterval > eps )
182 const double x = volumeValues[2 * volumeIndex];
183 const double y = volumeValues[2 * volumeIndex + 1];
184 if ( !std::isnan( x ) && !std::isnan( y ) )
186 nSumX += x * effectiveInterval;
187 nSumY += y * effectiveInterval;
188 totalAveragedHeight += effectiveInterval;
193 const double x = volumeValues[volumeIndex];
194 if ( !std::isnan( x ) )
196 nSumX += x * effectiveInterval;
197 totalAveragedHeight += effectiveInterval;
204 if ( totalAveragedHeight > eps )
208 valuesFaces[2 * faceIndex] = nSumX / totalAveragedHeight;
209 valuesFaces[2 * faceIndex + 1] = nSumY / totalAveragedHeight;
213 valuesFaces[faceIndex] = nSumX / totalAveragedHeight;
228 , mStartVerticalLevel( startLevel )
229 , mEndVerticalLevel( endLevel )
232 if ( mStartVerticalLevel > mEndVerticalLevel )
234 std::swap( mStartVerticalLevel, mEndVerticalLevel );
244 , mStartVerticalLevel( verticalLevel )
245 , mEndVerticalLevel( verticalLevel )
253 QDomElement elem = doc.createElement( u
"multi-vertical-layers-settings"_s );
261 const QDomElement settings = elem.firstChildElement( u
"multi-vertical-layers-settings"_s );
262 if ( !settings.isNull() )
264 mStartVerticalLevel = settings.attribute( u
"start-layer-index"_s ).toInt();
265 mEndVerticalLevel = settings.attribute( u
"end-layer-index"_s ).toInt();
266 if ( mStartVerticalLevel > mEndVerticalLevel )
268 std::swap( mStartVerticalLevel, mEndVerticalLevel );
291 return mStartVerticalLevel;
296 return mEndVerticalLevel;
299bool QgsMeshMultiLevelsAveragingMethod::hasValidInputs()
const
301 return mStartVerticalLevel >= 1 && mEndVerticalLevel >= mStartVerticalLevel;
304void QgsMeshMultiLevelsAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
double &endVerticalLevel,
int &singleVerticalIndex,
const QVector<double> &verticalLevels )
const
306 Q_ASSERT( mStartVerticalLevel <= mEndVerticalLevel );
310 const int startIndex = mStartVerticalLevel - 1;
311 if ( mStartVerticalLevel == mEndVerticalLevel )
313 if ( startIndex >= 0 && startIndex < verticalLevels.size() - 1 )
314 singleVerticalIndex = startIndex;
318 if ( startIndex >= 0 && startIndex < verticalLevels.size() )
323 if ( mEndVerticalLevel >= 0 && mEndVerticalLevel < verticalLevels.size() )
335 const int volumesBelowFaceCount = verticalLevels.size() - 1;
336 const int startIndex = volumesBelowFaceCount - mEndVerticalLevel;
337 if ( mStartVerticalLevel == mEndVerticalLevel )
339 if ( startIndex >= 0 && startIndex < verticalLevels.size() - 1 )
340 singleVerticalIndex = startIndex;
344 if ( startIndex >= 0 && startIndex < verticalLevels.size() )
353 const int endIndex = volumesBelowFaceCount - mStartVerticalLevel + 1;
354 if ( endIndex >= 0 && endIndex < verticalLevels.size() )
371 if ( mStartFraction > mEndFraction )
373 std::swap( mStartFraction, mEndFraction );
381 QDomElement elem = doc.createElement( u
"sigma-settings"_s );
383 elem.setAttribute( u
"end-fraction"_s,
endFraction() );
389 const QDomElement settings = elem.firstChildElement( u
"sigma-settings"_s );
390 if ( !settings.isNull() )
392 mStartFraction = settings.attribute( u
"start-fraction"_s ).toDouble();
393 mEndFraction = settings.attribute( u
"end-fraction"_s ).toDouble();
394 if ( mStartFraction > mEndFraction )
396 std::swap( mStartFraction, mEndFraction );
418 return mStartFraction;
426bool QgsMeshSigmaAveragingMethod::hasValidInputs()
const
428 return mStartFraction >= 0 && mEndFraction >= mStartFraction && mEndFraction <= 1;
431void QgsMeshSigmaAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
double &endVerticalLevel,
int &,
const QVector<double> &verticalLevels )
const
433 const double top = verticalLevels[0];
434 const double bot = verticalLevels[verticalLevels.size() - 1];
435 const double diff = top - bot;
437 if ( mStartFraction < 0 )
438 startVerticalLevel = bot;
440 startVerticalLevel = bot + diff * mStartFraction;
442 if ( mEndFraction > 1 )
443 endVerticalLevel = top;
445 endVerticalLevel = bot + diff * mEndFraction;
450 return mCountedFromTop;
455 return mStartVerticalLevel == mEndVerticalLevel;
465 , mStartHeight( startDepth )
466 , mEndHeight( endDepth )
469 if ( mStartHeight > mEndHeight )
471 std::swap( mStartHeight, mEndHeight );
479 QDomElement elem = doc.createElement( u
"relative-height-settings"_s );
480 elem.setAttribute( u
"start-height"_s,
startHeight() );
481 elem.setAttribute( u
"end-height"_s,
endHeight() );
487 const QDomElement settings = elem.firstChildElement( u
"relative-height-settings"_s );
488 if ( !settings.isNull() )
490 mStartHeight = settings.attribute( u
"start-height"_s ).toDouble();
491 mEndHeight = settings.attribute( u
"end-height"_s ).toDouble();
492 if ( mStartHeight > mEndHeight )
494 std::swap( mStartHeight, mEndHeight );
524bool QgsMeshRelativeHeightAveragingMethod::hasValidInputs()
const
526 return mStartHeight >= 0 && mEndHeight >= mStartHeight;
529void QgsMeshRelativeHeightAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
double &endVerticalLevel,
int &,
const QVector<double> &verticalLevels )
const
533 const double top = verticalLevels[0];
534 startVerticalLevel = top - mStartHeight;
535 endVerticalLevel = top - mEndHeight;
539 const double bot = verticalLevels[verticalLevels.size() - 1];
540 startVerticalLevel = bot + mStartHeight;
541 endVerticalLevel = bot + mEndHeight;
547 return mCountedFromTop;
559 if ( mEndElevation > mStartElevation )
561 std::swap( mEndElevation, mStartElevation );
569 QDomElement elem = doc.createElement( u
"elevation-settings"_s );
571 elem.setAttribute( u
"end-elevation"_s,
endElevation() );
577 const QDomElement settings = elem.firstChildElement( u
"elevation-settings"_s );
578 if ( !settings.isNull() )
580 mStartElevation = settings.attribute( u
"start-elevation"_s ).toDouble();
581 mEndElevation = settings.attribute( u
"end-elevation"_s ).toDouble();
582 if ( mEndElevation > mStartElevation )
584 std::swap( mEndElevation, mStartElevation );
606 return mStartElevation;
611 return mEndElevation;
614bool QgsMeshElevationAveragingMethod::hasValidInputs()
const
616 return mStartElevation <= 0.0 && mEndElevation <= mStartElevation;
619void QgsMeshElevationAveragingMethod::volumeRangeForFace(
double &startVerticalLevel,
double &endVerticalLevel,
int &,
const QVector<double> &verticalLevels )
const
621 Q_UNUSED( verticalLevels )
622 startVerticalLevel = mStartElevation;
623 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).