Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsmesh3daveraging.cpp
3 : : ----------------------
4 : : begin : November 2019
5 : : copyright : (C) 2019 by Peter Petrik
6 : : email : zilolv 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 <memory>
19 : :
20 : : #include "qgsmesh3daveraging.h"
21 : : #include "qgsmeshdataprovider.h"
22 : : #include "qgsmeshrenderersettings.h"
23 : : #include "qgsfeedback.h"
24 : :
25 : : // threshold for length intervals, to avoid division by 0
26 : : static const double eps = 1e-6;
27 : :
28 : 0 : QgsMesh3dAveragingMethod::QgsMesh3dAveragingMethod( Method method )
29 : 0 : : mMethod( method )
30 : 0 : {
31 : 0 : }
32 : :
33 : 0 : QgsMesh3dAveragingMethod *QgsMesh3dAveragingMethod::createFromXml( const QDomElement &elem )
34 : : {
35 : 0 : std::unique_ptr<QgsMesh3dAveragingMethod> ret;
36 : :
37 : 0 : QgsMesh3dAveragingMethod::Method method = static_cast<QgsMesh3dAveragingMethod::Method>(
38 : 0 : elem.attribute( QStringLiteral( "method" ) ).toInt() );
39 : 0 : switch ( method )
40 : : {
41 : : case QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod:
42 : 0 : ret.reset( new QgsMeshMultiLevelsAveragingMethod() );
43 : 0 : break;
44 : : case QgsMesh3dAveragingMethod::SigmaAveragingMethod:
45 : 0 : ret.reset( new QgsMeshSigmaAveragingMethod() );
46 : 0 : break;
47 : : case QgsMesh3dAveragingMethod::RelativeHeightAveragingMethod:
48 : 0 : ret.reset( new QgsMeshRelativeHeightAveragingMethod() );
49 : 0 : break;
50 : : case QgsMesh3dAveragingMethod::ElevationAveragingMethod:
51 : 0 : ret.reset( new QgsMeshElevationAveragingMethod() );
52 : 0 : break;
53 : : }
54 : 0 : ret->readXml( elem );
55 : 0 : return ret.release();
56 : 0 : }
57 : :
58 : 0 : QgsMeshDataBlock QgsMesh3dAveragingMethod::calculate( const QgsMesh3dDataBlock &block3d, QgsFeedback *feedback ) const
59 : : {
60 : 0 : if ( !block3d.isValid() )
61 : 0 : return QgsMeshDataBlock();
62 : :
63 : 0 : if ( !hasValidInputs() )
64 : 0 : return QgsMeshDataBlock();
65 : :
66 : 0 : bool isVector = block3d.isVector();
67 : 0 : int count = block3d.count();
68 : 0 : QgsMeshDataBlock result( isVector ? QgsMeshDataBlock::Vector2DDouble : QgsMeshDataBlock::ScalarDouble, count );
69 : 0 : QVector<double> valuesFaces( isVector ? 2 * count : count, std::numeric_limits<double>::quiet_NaN() );
70 : 0 : const QVector<int> verticalLevelsCount = block3d.verticalLevelsCount();
71 : 0 : const QVector<double> verticalLevels = block3d.verticalLevels();
72 : 0 : const QVector<double> volumeValues = block3d.values();
73 : :
74 : 0 : int startVolumeIndex = 0;
75 : 0 : for ( int faceIndex = 0; faceIndex < count; ++faceIndex )
76 : : {
77 : 0 : if ( feedback && feedback->isCanceled() )
78 : : {
79 : 0 : return QgsMeshDataBlock();
80 : : }
81 : :
82 : 0 : int volumesBelowFaceCount = verticalLevelsCount[faceIndex];
83 : 0 : if ( volumesBelowFaceCount <= 0 )
84 : 0 : continue;
85 : :
86 : 0 : int startVerticalLevelIndex = startVolumeIndex + faceIndex;
87 : : Q_ASSERT( verticalLevels.size() >= startVerticalLevelIndex + volumesBelowFaceCount + 1 );
88 : 0 : QVector<double> verticalLevelsForFace = verticalLevels.mid( startVerticalLevelIndex, volumesBelowFaceCount + 1 );
89 : 0 : double faceLevelTop = verticalLevelsForFace[0];
90 : 0 : double faceLevelBottom = verticalLevelsForFace[verticalLevelsForFace.size() - 1];
91 : :
92 : : // the level is value below surface, so top level (-0.1m) is usually higher number than bottom level (e.g. -1.2m)
93 : 0 : if ( faceLevelTop < faceLevelBottom )
94 : : {
95 : 0 : std::swap( faceLevelTop, faceLevelBottom );
96 : 0 : }
97 : :
98 : 0 : double methodLevelTop = std::numeric_limits<double>::quiet_NaN();
99 : 0 : double methodLevelBottom = std::numeric_limits<double>::quiet_NaN();
100 : :
101 : 0 : volumeRangeForFace( methodLevelTop,
102 : : methodLevelBottom,
103 : : verticalLevelsForFace );
104 : :
105 : 0 : if ( !std::isnan( methodLevelTop ) && !std::isnan( methodLevelBottom ) )
106 : : {
107 : : // the level is value below surface, so top level (-0.1m) is usually higher number than bottom level (e.g. -1.2m)
108 : 0 : if ( methodLevelTop < methodLevelBottom )
109 : : {
110 : 0 : std::swap( methodLevelTop, methodLevelBottom );
111 : 0 : }
112 : :
113 : : // check if we are completely outside the limits
114 : 0 : if ( ( methodLevelTop >= faceLevelBottom ) && ( methodLevelBottom <= faceLevelTop ) )
115 : : {
116 : 0 : averageVolumeValuesForFace(
117 : 0 : faceIndex,
118 : 0 : volumesBelowFaceCount,
119 : 0 : startVolumeIndex,
120 : 0 : methodLevelTop,
121 : 0 : methodLevelBottom,
122 : 0 : isVector,
123 : : verticalLevelsForFace,
124 : : volumeValues,
125 : : valuesFaces
126 : : );
127 : 0 : }
128 : 0 : }
129 : :
130 : : // move to next face and associated volumes
131 : 0 : startVolumeIndex += volumesBelowFaceCount;
132 : 0 : }
133 : 0 : result.setValues( valuesFaces );
134 : 0 : return result;
135 : 0 : }
136 : :
137 : 0 : QgsMesh3dAveragingMethod::Method QgsMesh3dAveragingMethod::method() const
138 : : {
139 : 0 : return mMethod;
140 : : }
141 : :
142 : 0 : void QgsMesh3dAveragingMethod::averageVolumeValuesForFace(
143 : : int faceIndex,
144 : : int volumesBelowFaceCount,
145 : : int startVolumeIndex,
146 : : double methodLevelTop,
147 : : double methodLevelBottom,
148 : : bool isVector,
149 : : const QVector<double> &verticalLevelsForFace,
150 : : const QVector<double> &volumeValues,
151 : : QVector<double> &valuesFaces
152 : : ) const
153 : : {
154 : 0 : double totalAveragedHeight = 0;
155 : 0 : double nSumX = 0.0;
156 : 0 : double nSumY = 0.0;
157 : :
158 : : // Now go through all volumes below face and check if we need to take that volume into consideration
159 : 0 : for ( int relativeVolumeIndex = 0; relativeVolumeIndex < volumesBelowFaceCount; ++relativeVolumeIndex )
160 : : {
161 : 0 : const int volumeIndex = startVolumeIndex + relativeVolumeIndex;
162 : 0 : double volumeLevelTop = verticalLevelsForFace[relativeVolumeIndex];
163 : 0 : double volumeLevelBottom = verticalLevelsForFace[relativeVolumeIndex + 1];
164 : 0 : if ( volumeLevelTop < volumeLevelBottom )
165 : : {
166 : 0 : std::swap( volumeLevelTop, volumeLevelBottom );
167 : 0 : }
168 : :
169 : 0 : const double intersectionLevelTop = std::min( methodLevelTop, volumeLevelTop );
170 : 0 : const double intersectionLevelBottom = std::max( methodLevelBottom, volumeLevelBottom );
171 : 0 : const double effectiveInterval = intersectionLevelTop - intersectionLevelBottom;
172 : :
173 : 0 : if ( effectiveInterval > eps )
174 : : {
175 : 0 : if ( isVector )
176 : : {
177 : 0 : const double x = volumeValues[2 * volumeIndex ];
178 : 0 : const double y = volumeValues[ 2 * volumeIndex + 1 ];
179 : 0 : if ( ! std::isnan( x ) &&
180 : 0 : ! std::isnan( y )
181 : : )
182 : : {
183 : 0 : nSumX += x * effectiveInterval;
184 : 0 : nSumY += y * effectiveInterval;
185 : 0 : totalAveragedHeight += effectiveInterval;
186 : 0 : }
187 : 0 : }
188 : : else
189 : : {
190 : 0 : const double x = volumeValues[ volumeIndex ];
191 : 0 : if ( ! std::isnan( x ) )
192 : : {
193 : 0 : nSumX += x * effectiveInterval;
194 : 0 : totalAveragedHeight += effectiveInterval;
195 : 0 : }
196 : : }
197 : 0 : }
198 : 0 : }
199 : :
200 : : // calculate average
201 : 0 : if ( totalAveragedHeight > eps )
202 : : {
203 : 0 : if ( isVector )
204 : : {
205 : 0 : valuesFaces[2 * faceIndex] = nSumX / totalAveragedHeight;
206 : 0 : valuesFaces[2 * faceIndex + 1 ] = nSumY / totalAveragedHeight;
207 : 0 : }
208 : : else
209 : : {
210 : 0 : valuesFaces[faceIndex] = nSumX / totalAveragedHeight;
211 : : }
212 : 0 : }
213 : 0 : }
214 : :
215 : 0 : bool QgsMesh3dAveragingMethod::equals( const QgsMesh3dAveragingMethod *a, const QgsMesh3dAveragingMethod *b )
216 : : {
217 : 0 : if ( a )
218 : 0 : return a->equals( b );
219 : : else
220 : 0 : return !b;
221 : 0 : }
222 : :
223 : 0 : QgsMeshMultiLevelsAveragingMethod::QgsMeshMultiLevelsAveragingMethod( int startLevel, int endLevel, bool countedFromTop )
224 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod )
225 : 0 : , mStartVerticalLevel( startLevel )
226 : 0 : , mEndVerticalLevel( endLevel )
227 : 0 : , mCountedFromTop( countedFromTop )
228 : 0 : {
229 : 0 : if ( mStartVerticalLevel > mEndVerticalLevel )
230 : : {
231 : 0 : std::swap( mStartVerticalLevel, mEndVerticalLevel );
232 : 0 : }
233 : 0 : }
234 : :
235 : 0 : QgsMeshMultiLevelsAveragingMethod::QgsMeshMultiLevelsAveragingMethod()
236 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod )
237 : 0 : {
238 : 0 : }
239 : :
240 : 0 : QgsMeshMultiLevelsAveragingMethod::QgsMeshMultiLevelsAveragingMethod( int verticalLevel, bool countedFromTop )
241 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod )
242 : 0 : , mStartVerticalLevel( verticalLevel )
243 : 0 : , mEndVerticalLevel( verticalLevel )
244 : 0 : , mCountedFromTop( countedFromTop )
245 : 0 : {
246 : 0 : }
247 : :
248 : 0 : QgsMeshMultiLevelsAveragingMethod::~QgsMeshMultiLevelsAveragingMethod() = default;
249 : :
250 : 0 : QDomElement QgsMeshMultiLevelsAveragingMethod::writeXml( QDomDocument &doc ) const
251 : : {
252 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "multi-vertical-layers-settings" ) );
253 : 0 : elem.setAttribute( QStringLiteral( "start-layer-index" ), startVerticalLevel() );
254 : 0 : elem.setAttribute( QStringLiteral( "end-layer-index" ), endVerticalLevel() );
255 : 0 : return elem;
256 : 0 : }
257 : :
258 : 0 : void QgsMeshMultiLevelsAveragingMethod::readXml( const QDomElement &elem )
259 : : {
260 : 0 : QDomElement settings = elem.firstChildElement( QStringLiteral( "multi-vertical-layers-settings" ) );
261 : 0 : if ( !settings.isNull() )
262 : : {
263 : 0 : mStartVerticalLevel = settings.attribute( QStringLiteral( "start-layer-index" ) ).toInt();
264 : 0 : mEndVerticalLevel = settings.attribute( QStringLiteral( "end-layer-index" ) ).toInt();
265 : 0 : if ( mStartVerticalLevel > mEndVerticalLevel )
266 : : {
267 : 0 : std::swap( mStartVerticalLevel, mEndVerticalLevel );
268 : 0 : }
269 : 0 : }
270 : 0 : }
271 : :
272 : 0 : bool QgsMeshMultiLevelsAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
273 : : {
274 : 0 : if ( !other || other->method() != method() )
275 : 0 : return false;
276 : :
277 : 0 : const QgsMeshMultiLevelsAveragingMethod *otherMethod = static_cast<const QgsMeshMultiLevelsAveragingMethod *>( other );
278 : :
279 : 0 : return ( otherMethod->startVerticalLevel() == startVerticalLevel() ) &&
280 : 0 : ( otherMethod->endVerticalLevel() == endVerticalLevel() ) &&
281 : 0 : ( otherMethod->countedFromTop() == countedFromTop() );
282 : 0 : }
283 : :
284 : 0 : QgsMesh3dAveragingMethod *QgsMeshMultiLevelsAveragingMethod::clone() const
285 : : {
286 : 0 : return new QgsMeshMultiLevelsAveragingMethod( startVerticalLevel(), endVerticalLevel(), countedFromTop() );
287 : 0 : }
288 : :
289 : :
290 : 0 : int QgsMeshMultiLevelsAveragingMethod::startVerticalLevel() const
291 : : {
292 : 0 : return mStartVerticalLevel;
293 : : }
294 : :
295 : 0 : int QgsMeshMultiLevelsAveragingMethod::endVerticalLevel() const
296 : : {
297 : 0 : return mEndVerticalLevel;
298 : : }
299 : :
300 : 0 : bool QgsMeshMultiLevelsAveragingMethod::hasValidInputs() const
301 : : {
302 : 0 : return mStartVerticalLevel >= 1 && mEndVerticalLevel >= mStartVerticalLevel;
303 : : }
304 : :
305 : 0 : void QgsMeshMultiLevelsAveragingMethod::volumeRangeForFace(
306 : : double &startVerticalLevel,
307 : : double &endVerticalLevel,
308 : : const QVector<double> &verticalLevels
309 : : ) const
310 : : {
311 : : Q_ASSERT( mStartVerticalLevel <= mEndVerticalLevel );
312 : :
313 : 0 : if ( countedFromTop() )
314 : : {
315 : 0 : int startIndex = mStartVerticalLevel - 1;
316 : 0 : if ( startIndex >= 0 && startIndex < verticalLevels.size() )
317 : : {
318 : 0 : startVerticalLevel = verticalLevels[ startIndex ];
319 : 0 : }
320 : :
321 : 0 : if ( mEndVerticalLevel >= 0 && mEndVerticalLevel < verticalLevels.size() )
322 : : {
323 : 0 : endVerticalLevel = verticalLevels[ mEndVerticalLevel ];
324 : 0 : }
325 : : else
326 : : {
327 : 0 : endVerticalLevel = verticalLevels[ verticalLevels.size() - 1 ];
328 : : }
329 : 0 : }
330 : : else
331 : : {
332 : 0 : int volumesBelowFaceCount = verticalLevels.size() - 1;
333 : 0 : int startIndex = volumesBelowFaceCount - mEndVerticalLevel;
334 : 0 : if ( startIndex >= 0 && startIndex < verticalLevels.size() )
335 : : {
336 : 0 : startVerticalLevel = verticalLevels[ startIndex ];
337 : 0 : }
338 : : else
339 : : {
340 : 0 : startVerticalLevel = verticalLevels[ 0 ];
341 : : }
342 : :
343 : 0 : int endIndex = volumesBelowFaceCount - mStartVerticalLevel + 1;
344 : 0 : if ( endIndex >= 0 && endIndex < verticalLevels.size() )
345 : 0 : {
346 : 0 : endVerticalLevel = verticalLevels[ endIndex ];
347 : 0 : }
348 : : }
349 : 0 : }
350 : :
351 : 0 : QgsMeshSigmaAveragingMethod::QgsMeshSigmaAveragingMethod()
352 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::SigmaAveragingMethod )
353 : 0 : {
354 : 0 : }
355 : :
356 : 0 : QgsMeshSigmaAveragingMethod::QgsMeshSigmaAveragingMethod( double startFraction, double endFraction )
357 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::SigmaAveragingMethod )
358 : 0 : , mStartFraction( startFraction )
359 : 0 : , mEndFraction( endFraction )
360 : 0 : {
361 : 0 : if ( mStartFraction > mEndFraction )
362 : : {
363 : 0 : std::swap( mStartFraction, mEndFraction );
364 : 0 : }
365 : 0 : }
366 : :
367 : 0 : QgsMeshSigmaAveragingMethod::~QgsMeshSigmaAveragingMethod() = default;
368 : :
369 : 0 : QDomElement QgsMeshSigmaAveragingMethod::writeXml( QDomDocument &doc ) const
370 : : {
371 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "sigma-settings" ) );
372 : 0 : elem.setAttribute( QStringLiteral( "start-fraction" ), startFraction() );
373 : 0 : elem.setAttribute( QStringLiteral( "end-fraction" ), endFraction() );
374 : 0 : return elem;
375 : 0 : }
376 : :
377 : 0 : void QgsMeshSigmaAveragingMethod::readXml( const QDomElement &elem )
378 : : {
379 : 0 : QDomElement settings = elem.firstChildElement( QStringLiteral( "sigma-settings" ) );
380 : 0 : if ( !settings.isNull() )
381 : : {
382 : 0 : mStartFraction = settings.attribute( QStringLiteral( "start-fraction" ) ).toDouble();
383 : 0 : mEndFraction = settings.attribute( QStringLiteral( "end-fraction" ) ).toDouble();
384 : 0 : if ( mStartFraction > mEndFraction )
385 : : {
386 : 0 : std::swap( mStartFraction, mEndFraction );
387 : 0 : }
388 : 0 : }
389 : 0 : }
390 : :
391 : 0 : bool QgsMeshSigmaAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
392 : : {
393 : 0 : if ( !other || other->method() != method() )
394 : 0 : return false;
395 : :
396 : 0 : const QgsMeshSigmaAveragingMethod *otherMethod = static_cast<const QgsMeshSigmaAveragingMethod *>( other );
397 : 0 :
398 : 0 : return qgsDoubleNear( otherMethod->startFraction(), startFraction() ) && qgsDoubleNear( otherMethod->endFraction(), endFraction() ) ;
399 : 0 : }
400 : :
401 : 0 : QgsMesh3dAveragingMethod *QgsMeshSigmaAveragingMethod::clone() const
402 : : {
403 : 0 : return new QgsMeshSigmaAveragingMethod( startFraction(), endFraction() );
404 : 0 : }
405 : :
406 : 0 : double QgsMeshSigmaAveragingMethod::startFraction() const
407 : : {
408 : 0 : return mStartFraction;
409 : : }
410 : :
411 : 0 : double QgsMeshSigmaAveragingMethod::endFraction() const
412 : : {
413 : 0 : return mEndFraction;
414 : : }
415 : :
416 : 0 : bool QgsMeshSigmaAveragingMethod::hasValidInputs() const
417 : : {
418 : 0 : return mStartFraction >= 0 && mEndFraction >= mStartFraction && mEndFraction <= 1;
419 : : }
420 : :
421 : 0 : void QgsMeshSigmaAveragingMethod::volumeRangeForFace(
422 : : double &startVerticalLevel,
423 : : double &endVerticalLevel,
424 : : const QVector<double> &verticalLevels
425 : : ) const
426 : : {
427 : 0 : const double top = verticalLevels[ 0 ];
428 : 0 : const double bot = verticalLevels[ verticalLevels.size() - 1 ];
429 : 0 : const double diff = top - bot;
430 : :
431 : 0 : if ( mStartFraction < 0 )
432 : 0 : startVerticalLevel = bot;
433 : : else
434 : 0 : startVerticalLevel = bot + diff * mStartFraction;
435 : :
436 : 0 : if ( mEndFraction > 1 )
437 : 0 : endVerticalLevel = top;
438 : : else
439 : 0 : endVerticalLevel = bot + diff * mEndFraction;
440 : 0 : }
441 : :
442 : 0 : bool QgsMeshMultiLevelsAveragingMethod::countedFromTop() const
443 : : {
444 : 0 : return mCountedFromTop;
445 : : }
446 : :
447 : 0 : bool QgsMeshMultiLevelsAveragingMethod::isSingleLevel() const
448 : : {
449 : 0 : return mStartVerticalLevel == mEndVerticalLevel;
450 : : }
451 : :
452 : :
453 : 0 : QgsMeshRelativeHeightAveragingMethod::QgsMeshRelativeHeightAveragingMethod()
454 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::RelativeHeightAveragingMethod )
455 : 0 : {
456 : 0 : }
457 : :
458 : 0 : QgsMeshRelativeHeightAveragingMethod::QgsMeshRelativeHeightAveragingMethod( double startDepth, double endDepth, bool countedFromTop )
459 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::RelativeHeightAveragingMethod )
460 : 0 : , mStartHeight( startDepth )
461 : 0 : , mEndHeight( endDepth )
462 : 0 : , mCountedFromTop( countedFromTop )
463 : 0 : {
464 : 0 : if ( mStartHeight > mEndHeight )
465 : : {
466 : 0 : std::swap( mStartHeight, mEndHeight );
467 : 0 : }
468 : 0 : }
469 : :
470 : 0 : QgsMeshRelativeHeightAveragingMethod::~QgsMeshRelativeHeightAveragingMethod() = default;
471 : :
472 : 0 : QDomElement QgsMeshRelativeHeightAveragingMethod::writeXml( QDomDocument &doc ) const
473 : : {
474 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "relative-height-settings" ) );
475 : 0 : elem.setAttribute( QStringLiteral( "start-height" ), startHeight() );
476 : 0 : elem.setAttribute( QStringLiteral( "end-height" ), endHeight() );
477 : 0 : return elem;
478 : 0 : }
479 : :
480 : 0 : void QgsMeshRelativeHeightAveragingMethod::readXml( const QDomElement &elem )
481 : : {
482 : 0 : QDomElement settings = elem.firstChildElement( QStringLiteral( "relative-height-settings" ) );
483 : 0 : if ( !settings.isNull() )
484 : : {
485 : 0 : mStartHeight = settings.attribute( QStringLiteral( "start-height" ) ).toDouble();
486 : 0 : mEndHeight = settings.attribute( QStringLiteral( "end-height" ) ).toDouble();
487 : 0 : if ( mStartHeight > mEndHeight )
488 : : {
489 : 0 : std::swap( mStartHeight, mEndHeight );
490 : 0 : }
491 : 0 : }
492 : 0 : }
493 : :
494 : 0 : bool QgsMeshRelativeHeightAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
495 : : {
496 : 0 : if ( !other || other->method() != method() )
497 : 0 : return false;
498 : :
499 : 0 : const QgsMeshRelativeHeightAveragingMethod *otherMethod = static_cast<const QgsMeshRelativeHeightAveragingMethod *>( other );
500 : :
501 : 0 : return qgsDoubleNear( otherMethod->startHeight(), startHeight() ) &&
502 : 0 : qgsDoubleNear( otherMethod->endHeight(), endHeight() ) &&
503 : 0 : otherMethod->countedFromTop() == countedFromTop();
504 : 0 : }
505 : :
506 : 0 : QgsMesh3dAveragingMethod *QgsMeshRelativeHeightAveragingMethod::clone() const
507 : : {
508 : 0 : return new QgsMeshRelativeHeightAveragingMethod( startHeight(), endHeight(), countedFromTop() );
509 : 0 : }
510 : :
511 : 0 : double QgsMeshRelativeHeightAveragingMethod::startHeight() const
512 : : {
513 : 0 : return mStartHeight;
514 : : }
515 : :
516 : 0 : double QgsMeshRelativeHeightAveragingMethod::endHeight() const
517 : : {
518 : 0 : return mEndHeight;
519 : : }
520 : :
521 : 0 : bool QgsMeshRelativeHeightAveragingMethod::hasValidInputs() const
522 : : {
523 : 0 : return mStartHeight >= 0 && mEndHeight >= mStartHeight;
524 : : }
525 : :
526 : 0 : void QgsMeshRelativeHeightAveragingMethod::volumeRangeForFace(
527 : : double &startVerticalLevel,
528 : : double &endVerticalLevel,
529 : : const QVector<double> &verticalLevels ) const
530 : : {
531 : 0 : if ( countedFromTop() )
532 : : {
533 : 0 : const double top = verticalLevels[ 0 ];
534 : 0 : startVerticalLevel = top - mStartHeight;
535 : 0 : endVerticalLevel = top - mEndHeight;
536 : 0 : }
537 : : else
538 : : {
539 : 0 : const double bot = verticalLevels[verticalLevels.size() - 1];
540 : 0 : startVerticalLevel = bot + mStartHeight;
541 : 0 : endVerticalLevel = bot + mEndHeight;
542 : : }
543 : 0 : }
544 : :
545 : 0 : bool QgsMeshRelativeHeightAveragingMethod::countedFromTop() const
546 : : {
547 : 0 : return mCountedFromTop;
548 : : }
549 : :
550 : 0 : QgsMeshElevationAveragingMethod::QgsMeshElevationAveragingMethod()
551 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::ElevationAveragingMethod )
552 : 0 : {
553 : 0 : }
554 : :
555 : 0 : QgsMeshElevationAveragingMethod::QgsMeshElevationAveragingMethod( double startElevation, double endElevation )
556 : 0 : : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::ElevationAveragingMethod )
557 : 0 : , mStartElevation( startElevation )
558 : 0 : , mEndElevation( endElevation )
559 : 0 : {
560 : 0 : if ( mEndElevation > mStartElevation )
561 : : {
562 : 0 : std::swap( mEndElevation, mStartElevation );
563 : 0 : }
564 : 0 : }
565 : :
566 : 0 : QgsMeshElevationAveragingMethod::~QgsMeshElevationAveragingMethod() = default;
567 : :
568 : 0 : QDomElement QgsMeshElevationAveragingMethod::writeXml( QDomDocument &doc ) const
569 : : {
570 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "elevation-settings" ) );
571 : 0 : elem.setAttribute( QStringLiteral( "start-elevation" ), startElevation() );
572 : 0 : elem.setAttribute( QStringLiteral( "end-elevation" ), endElevation() );
573 : 0 : return elem;
574 : 0 : }
575 : :
576 : 0 : void QgsMeshElevationAveragingMethod::readXml( const QDomElement &elem )
577 : : {
578 : 0 : QDomElement settings = elem.firstChildElement( QStringLiteral( "elevation-settings" ) );
579 : 0 : if ( !settings.isNull() )
580 : : {
581 : 0 : mStartElevation = settings.attribute( QStringLiteral( "start-elevation" ) ).toDouble();
582 : 0 : mEndElevation = settings.attribute( QStringLiteral( "end-elevation" ) ).toDouble();
583 : 0 : if ( mEndElevation > mStartElevation )
584 : : {
585 : 0 : std::swap( mEndElevation, mStartElevation );
586 : 0 : }
587 : 0 : }
588 : 0 : }
589 : :
590 : 0 : bool QgsMeshElevationAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
591 : : {
592 : 0 : if ( !other || other->method() != method() )
593 : 0 : return false;
594 : :
595 : 0 : const QgsMeshElevationAveragingMethod *otherMethod = static_cast<const QgsMeshElevationAveragingMethod *>( other );
596 : :
597 : 0 : return qgsDoubleNear( otherMethod->startElevation(), startElevation() ) && qgsDoubleNear( otherMethod->endElevation(), endElevation() ) ;
598 : 0 : }
599 : :
600 : 0 : QgsMesh3dAveragingMethod *QgsMeshElevationAveragingMethod::clone() const
601 : : {
602 : 0 : return new QgsMeshElevationAveragingMethod( startElevation(), endElevation() );
603 : 0 : }
604 : :
605 : 0 : double QgsMeshElevationAveragingMethod::startElevation() const
606 : : {
607 : 0 : return mStartElevation;
608 : : }
609 : :
610 : 0 : double QgsMeshElevationAveragingMethod::endElevation() const
611 : : {
612 : 0 : return mEndElevation;
613 : : }
614 : :
615 : 0 : bool QgsMeshElevationAveragingMethod::hasValidInputs() const
616 : : {
617 : 0 : return mStartElevation <= 0.0 && mEndElevation <= mStartElevation;
618 : : }
619 : :
620 : 0 : void QgsMeshElevationAveragingMethod::volumeRangeForFace(
621 : : double &startVerticalLevel,
622 : : double &endVerticalLevel,
623 : : const QVector<double> &verticalLevels ) const
624 : : {
625 : 0 : Q_UNUSED( verticalLevels )
626 : 0 : startVerticalLevel = mStartElevation;
627 : 0 : endVerticalLevel = mEndElevation;
628 : 0 : }
|