File indexing completed on 2025-01-05 03:58:56

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2007 Andrew Manson <g.real.ate@gmail.com>
0004 // SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org>
0005 //
0006 
0007 #include "GeoDataLatLonAltBox.h"
0008 
0009 #include <QDataStream>
0010 
0011 #include "GeoDataCoordinates.h"
0012 #include "GeoDataLineString.h"
0013 #include "GeoDataTypes.h"
0014 
0015 #include "digikam_debug.h"
0016 
0017 namespace Marble
0018 {
0019 
0020 class GeoDataLatLonAltBoxPrivate
0021 {
0022  public:
0023     GeoDataLatLonAltBoxPrivate()
0024         : m_minAltitude( 0 ),
0025           m_maxAltitude( 0 ),
0026           m_altitudeMode( ClampToGround )
0027     {
0028     }
0029 
0030     qreal m_minAltitude;
0031     qreal m_maxAltitude;
0032     AltitudeMode m_altitudeMode;
0033 };
0034 
0035 bool operator==( GeoDataLatLonAltBox const& lhs, GeoDataLatLonAltBox const& rhs )
0036 {
0037     return lhs.west() == rhs.west() &&
0038            lhs.east() == rhs.east() &&
0039            lhs.north() == rhs.north() &&
0040            lhs.south() == rhs.south() &&
0041            lhs.rotation() == rhs.rotation() &&
0042            lhs.d->m_minAltitude == rhs.d->m_minAltitude &&
0043            lhs.d->m_maxAltitude == rhs.d->m_maxAltitude &&
0044            lhs.d->m_altitudeMode == rhs.d->m_altitudeMode;
0045 }
0046 
0047 GeoDataLatLonAltBox& GeoDataLatLonAltBox::operator=( const GeoDataLatLonAltBox &other )
0048 {
0049     GeoDataLatLonBox::operator=( other );
0050 
0051     *d = *other.d;
0052     return *this;
0053 }
0054 
0055 GeoDataLatLonAltBox& GeoDataLatLonAltBox::operator=( const GeoDataCoordinates &other )
0056 {
0057     setWest( other.longitude() );
0058     setEast( other.longitude() );
0059     setNorth( other.latitude() );
0060     setSouth( other.latitude() );
0061     setMinAltitude( other.altitude() );
0062     setMaxAltitude( other.altitude() );
0063     return *this;
0064 }
0065 
0066 GeoDataLatLonAltBox::GeoDataLatLonAltBox()
0067     : GeoDataLatLonBox(),
0068       d( new GeoDataLatLonAltBoxPrivate )
0069 {
0070 }
0071 
0072 GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataLatLonAltBox & other )
0073     : GeoDataLatLonBox( other ),
0074       d( new GeoDataLatLonAltBoxPrivate( *other.d ))
0075 {
0076 }
0077 
0078 GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataLatLonBox &other, qreal minAltitude, qreal maxAltitude )
0079     : GeoDataLatLonBox( other ),
0080       d( new GeoDataLatLonAltBoxPrivate )
0081 {
0082     setWest(  other.west() );
0083     setEast(  other.east() );
0084     setNorth( other.north() );
0085     setSouth( other.south() );
0086     setRotation( other.rotation() );
0087 
0088     d->m_minAltitude = minAltitude;
0089     d->m_maxAltitude = maxAltitude;
0090 }
0091 
0092 
0093 GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataCoordinates & coordinates )
0094     : GeoDataLatLonBox(),
0095       d( new GeoDataLatLonAltBoxPrivate )
0096 {
0097     setWest( coordinates.longitude() );
0098     setEast( coordinates.longitude() );
0099     setNorth( coordinates.latitude() );
0100     setSouth( coordinates.latitude() );
0101 
0102     d->m_minAltitude = coordinates.altitude();
0103     d->m_maxAltitude = coordinates.altitude();
0104 }
0105 
0106 
0107 GeoDataLatLonAltBox::~GeoDataLatLonAltBox()
0108 {
0109     delete d;
0110 }
0111 
0112 const char* GeoDataLatLonAltBox::nodeType() const
0113 {
0114     return GeoDataTypes::GeoDataLatLonAltBoxType;
0115 }
0116 
0117 qreal GeoDataLatLonAltBox::minAltitude() const
0118 {
0119     return d->m_minAltitude;
0120 }
0121 
0122 void GeoDataLatLonAltBox::setMinAltitude( const qreal minAltitude )
0123 {
0124     d->m_minAltitude = minAltitude;
0125 }
0126 
0127 qreal GeoDataLatLonAltBox::maxAltitude() const
0128 {
0129     return d->m_maxAltitude;
0130 }
0131 
0132 void GeoDataLatLonAltBox::setMaxAltitude( const qreal maxAltitude )
0133 {
0134     d->m_maxAltitude = maxAltitude;
0135 }
0136 
0137 AltitudeMode GeoDataLatLonAltBox::altitudeMode() const
0138 {
0139     return d->m_altitudeMode;
0140 }
0141 
0142 GeoDataCoordinates GeoDataLatLonAltBox::center() const
0143 {
0144     if ( isEmpty() )
0145         return GeoDataCoordinates();
0146     if( crossesDateLine() )
0147         return GeoDataCoordinates( GeoDataCoordinates::normalizeLon(east() + 2 * M_PI - (east() + 2 * M_PI - west()) / 2),
0148                                 north() - (north() - south()) / 2,
0149                                 d->m_maxAltitude - (d->m_maxAltitude - d->m_minAltitude) / 2);
0150     else
0151         return GeoDataCoordinates( east() - (east() - west()) / 2,
0152                                 north() - (north() - south()) / 2,
0153                                 d->m_maxAltitude - (d->m_maxAltitude - d->m_minAltitude) / 2);
0154 }
0155 
0156 void GeoDataLatLonAltBox::setAltitudeMode( const AltitudeMode altitudeMode )
0157 {
0158     d->m_altitudeMode = altitudeMode;
0159 }
0160 
0161 bool GeoDataLatLonAltBox::contains( const GeoDataCoordinates &point ) const
0162 {
0163     if ( !GeoDataLatLonBox::contains( point ) )
0164         return false;
0165 
0166     if ( point.altitude() < d->m_minAltitude || point.altitude() > d->m_maxAltitude ) {
0167         return false;
0168     }
0169 
0170     return true;
0171 }
0172 
0173 bool GeoDataLatLonAltBox::contains( const GeoDataLatLonAltBox &other ) const
0174 {
0175     // check the contain criterion for the altitude first as this is trivial:
0176 
0177     // qCDebug(DIGIKAM_MARBLE_LOG) << "this " << this->toString(GeoDataCoordinates::Degree);
0178     // qCDebug(DIGIKAM_MARBLE_LOG) << "other" << other.toString(GeoDataCoordinates::Degree);
0179 
0180     if ( d->m_maxAltitude >= other.maxAltitude() && d->m_minAltitude <= other.minAltitude() ) {
0181         return GeoDataLatLonBox::contains( other );
0182     }
0183 
0184     return false;
0185 }
0186 
0187 bool GeoDataLatLonAltBox::intersects( const GeoDataLatLonAltBox &other ) const
0188 {
0189             // Case 1: maximum altitude of other box intersects:
0190     if (    ( d->m_maxAltitude >= other.maxAltitude() && d->m_minAltitude <= other.maxAltitude() )
0191             // Case 2: maximum altitude of this box intersects:
0192          || ( other.maxAltitude() >= d->m_maxAltitude && other.minAltitude() <= d->m_maxAltitude )
0193             // Case 3: minimum altitude of other box intersects:
0194          || ( d->m_maxAltitude >= other.minAltitude() && d->m_minAltitude <= other.minAltitude() )
0195             // Case 4: minimum altitude of this box intersects:
0196          || ( other.maxAltitude() >= d->m_minAltitude && other.minAltitude() <= d->m_minAltitude ) ) {
0197 
0198         if ( GeoDataLatLonBox::intersects( other ) )
0199             return true;
0200 
0201     }
0202 
0203     return false;
0204 }
0205 
0206 GeoDataLatLonAltBox GeoDataLatLonAltBox::fromLineString(  const GeoDataLineString& lineString  )
0207 {
0208     // If the line string is empty return a boundingbox that contains everything
0209     if ( lineString.size() == 0 ) {
0210         return GeoDataLatLonAltBox();
0211     }
0212 
0213     const qreal altitude = lineString.first().altitude();
0214 
0215     GeoDataLatLonAltBox temp ( GeoDataLatLonBox::fromLineString( lineString ), altitude, altitude );
0216 
0217     qreal maxAltitude = altitude;
0218     qreal minAltitude = altitude;
0219 
0220     // If there's only a single node stored then the boundingbox only contains that point
0221     if ( lineString.size() == 1 ) {
0222         temp.setMinAltitude( minAltitude );
0223         temp.setMaxAltitude( maxAltitude );
0224         return temp;
0225     }
0226 
0227     QVector<GeoDataCoordinates>::ConstIterator it( lineString.constBegin() );
0228     QVector<GeoDataCoordinates>::ConstIterator itEnd( lineString.constEnd() );
0229 
0230     for ( ; it != itEnd; ++it )
0231     {
0232         // Get coordinates and normalize them to the desired range.
0233         const qreal altitude = (it)->altitude();
0234 
0235         // Determining the maximum and minimum altitude
0236         if ( altitude > maxAltitude ) {
0237             maxAltitude = altitude;
0238         } else if ( altitude < minAltitude ) {
0239             minAltitude = altitude;
0240         }
0241     }
0242 
0243     temp.setMinAltitude( minAltitude );
0244     temp.setMaxAltitude( maxAltitude );
0245     return temp;
0246 }
0247 
0248 bool GeoDataLatLonAltBox::isNull() const
0249 {
0250     return GeoDataLatLonBox::isNull() && d->m_maxAltitude == d->m_minAltitude;
0251 }
0252 
0253 void GeoDataLatLonAltBox::clear()
0254 {
0255     GeoDataLatLonBox::clear();
0256     d->m_minAltitude = 0;
0257     d->m_maxAltitude = 0;
0258     d->m_altitudeMode = ClampToGround;
0259 }
0260 
0261 void GeoDataLatLonAltBox::pack( QDataStream& stream ) const
0262 {
0263     GeoDataObject::pack( stream );
0264 
0265     stream << d->m_minAltitude << d->m_maxAltitude;
0266     stream << d->m_altitudeMode;
0267 }
0268 
0269 void GeoDataLatLonAltBox::unpack( QDataStream& stream )
0270 {
0271     GeoDataObject::unpack( stream );
0272 
0273     stream >> d->m_minAltitude >> d->m_maxAltitude;
0274     int a;
0275     stream >> a;
0276     d->m_altitudeMode = static_cast<AltitudeMode>( a );
0277 }
0278 
0279 uint qHash(const GeoDataLatLonAltBox &box, uint seed)
0280 {
0281     seed = ::qHash(box.east(), seed);
0282     seed = ::qHash(box.west(), seed);
0283     seed = ::qHash(box.south(), seed);
0284     seed = ::qHash(box.north(), seed);
0285     seed = ::qHash(box.maxAltitude(), seed);
0286 
0287     return ::qHash(box.minAltitude(), seed);
0288 }
0289 
0290 }