1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007 Andrew Manson <g.real.ate@gmail.com>
4 // SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org>
5 //
6 
7 
8 #include "GeoDataLatLonAltBox.h"
9 
10 #include "MarbleDebug.h"
11 #include "GeoDataCoordinates.h"
12 #include "GeoDataLineString.h"
13 
14 #include "GeoDataTypes.h"
15 
16 #include <QDataStream>
17 
18 namespace Marble
19 {
20 
21 class GeoDataLatLonAltBoxPrivate
22 {
23  public:
GeoDataLatLonAltBoxPrivate()24     GeoDataLatLonAltBoxPrivate()
25         : m_minAltitude( 0 ),
26           m_maxAltitude( 0 ),
27           m_altitudeMode( ClampToGround )
28     {
29     }
30 
31     qreal m_minAltitude;
32     qreal m_maxAltitude;
33     AltitudeMode m_altitudeMode;
34 };
35 
operator ==(GeoDataLatLonAltBox const & lhs,GeoDataLatLonAltBox const & rhs)36 bool operator==( GeoDataLatLonAltBox const& lhs, GeoDataLatLonAltBox const& rhs )
37 {
38     return lhs.west() == rhs.west() &&
39            lhs.east() == rhs.east() &&
40            lhs.north() == rhs.north() &&
41            lhs.south() == rhs.south() &&
42            lhs.rotation() == rhs.rotation() &&
43            lhs.d->m_minAltitude == rhs.d->m_minAltitude &&
44            lhs.d->m_maxAltitude == rhs.d->m_maxAltitude &&
45            lhs.d->m_altitudeMode == rhs.d->m_altitudeMode;
46 }
47 
operator =(const GeoDataLatLonAltBox & other)48 GeoDataLatLonAltBox& GeoDataLatLonAltBox::operator=( const GeoDataLatLonAltBox &other )
49 {
50     GeoDataLatLonBox::operator=( other );
51 
52     *d = *other.d;
53     return *this;
54 }
55 
operator =(const GeoDataCoordinates & other)56 GeoDataLatLonAltBox& GeoDataLatLonAltBox::operator=( const GeoDataCoordinates &other )
57 {
58     setWest( other.longitude() );
59     setEast( other.longitude() );
60     setNorth( other.latitude() );
61     setSouth( other.latitude() );
62     setMinAltitude( other.altitude() );
63     setMaxAltitude( other.altitude() );
64     return *this;
65 }
66 
GeoDataLatLonAltBox()67 GeoDataLatLonAltBox::GeoDataLatLonAltBox()
68     : GeoDataLatLonBox(),
69       d( new GeoDataLatLonAltBoxPrivate )
70 {
71 }
72 
GeoDataLatLonAltBox(const GeoDataLatLonAltBox & other)73 GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataLatLonAltBox & other )
74     : GeoDataLatLonBox( other ),
75       d( new GeoDataLatLonAltBoxPrivate( *other.d ))
76 {
77 }
78 
GeoDataLatLonAltBox(const GeoDataLatLonBox & other,qreal minAltitude,qreal maxAltitude)79 GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataLatLonBox &other, qreal minAltitude, qreal maxAltitude )
80     : GeoDataLatLonBox( other ),
81       d( new GeoDataLatLonAltBoxPrivate )
82 {
83     setWest(  other.west() );
84     setEast(  other.east() );
85     setNorth( other.north() );
86     setSouth( other.south() );
87     setRotation( other.rotation() );
88 
89     d->m_minAltitude = minAltitude;
90     d->m_maxAltitude = maxAltitude;
91 }
92 
93 
GeoDataLatLonAltBox(const GeoDataCoordinates & coordinates)94 GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataCoordinates & coordinates )
95     : GeoDataLatLonBox(),
96       d( new GeoDataLatLonAltBoxPrivate )
97 {
98     setWest( coordinates.longitude() );
99     setEast( coordinates.longitude() );
100     setNorth( coordinates.latitude() );
101     setSouth( coordinates.latitude() );
102 
103     d->m_minAltitude = coordinates.altitude();
104     d->m_maxAltitude = coordinates.altitude();
105 }
106 
107 
~GeoDataLatLonAltBox()108 GeoDataLatLonAltBox::~GeoDataLatLonAltBox()
109 {
110     delete d;
111 }
112 
nodeType() const113 const char* GeoDataLatLonAltBox::nodeType() const
114 {
115     return GeoDataTypes::GeoDataLatLonAltBoxType;
116 }
117 
minAltitude() const118 qreal GeoDataLatLonAltBox::minAltitude() const
119 {
120     return d->m_minAltitude;
121 }
122 
setMinAltitude(const qreal minAltitude)123 void GeoDataLatLonAltBox::setMinAltitude( const qreal minAltitude )
124 {
125     d->m_minAltitude = minAltitude;
126 }
127 
maxAltitude() const128 qreal GeoDataLatLonAltBox::maxAltitude() const
129 {
130     return d->m_maxAltitude;
131 }
132 
setMaxAltitude(const qreal maxAltitude)133 void GeoDataLatLonAltBox::setMaxAltitude( const qreal maxAltitude )
134 {
135     d->m_maxAltitude = maxAltitude;
136 }
137 
altitudeMode() const138 AltitudeMode GeoDataLatLonAltBox::altitudeMode() const
139 {
140     return d->m_altitudeMode;
141 }
142 
center() const143 GeoDataCoordinates GeoDataLatLonAltBox::center() const
144 {
145     if ( isEmpty() )
146         return GeoDataCoordinates();
147     if( crossesDateLine() )
148         return GeoDataCoordinates( GeoDataCoordinates::normalizeLon(east() + 2 * M_PI - (east() + 2 * M_PI - west()) / 2),
149                                 north() - (north() - south()) / 2,
150                                 d->m_maxAltitude - (d->m_maxAltitude - d->m_minAltitude) / 2);
151     else
152         return GeoDataCoordinates( east() - (east() - west()) / 2,
153                                 north() - (north() - south()) / 2,
154                                 d->m_maxAltitude - (d->m_maxAltitude - d->m_minAltitude) / 2);
155 }
156 
setAltitudeMode(const AltitudeMode altitudeMode)157 void GeoDataLatLonAltBox::setAltitudeMode( const AltitudeMode altitudeMode )
158 {
159     d->m_altitudeMode = altitudeMode;
160 }
161 
contains(const GeoDataCoordinates & point) const162 bool GeoDataLatLonAltBox::contains( const GeoDataCoordinates &point ) const
163 {
164     if ( !GeoDataLatLonBox::contains( point ) )
165         return false;
166 
167     if ( point.altitude() < d->m_minAltitude || point.altitude() > d->m_maxAltitude ) {
168         return false;
169     }
170 
171     return true;
172 }
173 
contains(const GeoDataLatLonAltBox & other) const174 bool GeoDataLatLonAltBox::contains( const GeoDataLatLonAltBox &other ) const
175 {
176     // check the contain criterion for the altitude first as this is trivial:
177 
178     // mDebug() << "this " << this->toString(GeoDataCoordinates::Degree);
179     // mDebug() << "other" << other.toString(GeoDataCoordinates::Degree);
180 
181     if ( d->m_maxAltitude >= other.maxAltitude() && d->m_minAltitude <= other.minAltitude() ) {
182         return GeoDataLatLonBox::contains( other );
183     }
184 
185     return false;
186 }
187 
intersects(const GeoDataLatLonAltBox & other) const188 bool GeoDataLatLonAltBox::intersects( const GeoDataLatLonAltBox &other ) const
189 {
190             // Case 1: maximum altitude of other box intersects:
191     if (    ( d->m_maxAltitude >= other.maxAltitude() && d->m_minAltitude <= other.maxAltitude() )
192             // Case 2: maximum altitude of this box intersects:
193          || ( other.maxAltitude() >= d->m_maxAltitude && other.minAltitude() <= d->m_maxAltitude )
194             // Case 3: minimum altitude of other box intersects:
195          || ( d->m_maxAltitude >= other.minAltitude() && d->m_minAltitude <= other.minAltitude() )
196             // Case 4: minimum altitude of this box intersects:
197          || ( other.maxAltitude() >= d->m_minAltitude && other.minAltitude() <= d->m_minAltitude ) ) {
198 
199         if ( GeoDataLatLonBox::intersects( other ) )
200             return true;
201 
202     }
203 
204     return false;
205 }
206 
fromLineString(const GeoDataLineString & lineString)207 GeoDataLatLonAltBox GeoDataLatLonAltBox::fromLineString(  const GeoDataLineString& lineString  )
208 {
209     // If the line string is empty return a boundingbox that contains everything
210     if ( lineString.size() == 0 ) {
211         return GeoDataLatLonAltBox();
212     }
213 
214     const qreal altitude = lineString.first().altitude();
215 
216     GeoDataLatLonAltBox temp ( GeoDataLatLonBox::fromLineString( lineString ), altitude, altitude );
217 
218     qreal maxAltitude = altitude;
219     qreal minAltitude = altitude;
220 
221     // If there's only a single node stored then the boundingbox only contains that point
222     if ( lineString.size() == 1 ) {
223         temp.setMinAltitude( minAltitude );
224         temp.setMaxAltitude( maxAltitude );
225         return temp;
226     }
227 
228     QVector<GeoDataCoordinates>::ConstIterator it( lineString.constBegin() );
229     QVector<GeoDataCoordinates>::ConstIterator itEnd( lineString.constEnd() );
230 
231     for ( ; it != itEnd; ++it )
232     {
233         // Get coordinates and normalize them to the desired range.
234         const qreal altitude = (it)->altitude();
235 
236         // Determining the maximum and minimum altitude
237         if ( altitude > maxAltitude ) {
238             maxAltitude = altitude;
239         } else if ( altitude < minAltitude ) {
240             minAltitude = altitude;
241         }
242     }
243 
244     temp.setMinAltitude( minAltitude );
245     temp.setMaxAltitude( maxAltitude );
246     return temp;
247 }
248 
isNull() const249 bool GeoDataLatLonAltBox::isNull() const
250 {
251     return GeoDataLatLonBox::isNull() && d->m_maxAltitude == d->m_minAltitude;
252 }
253 
clear()254 void GeoDataLatLonAltBox::clear()
255 {
256     GeoDataLatLonBox::clear();
257     d->m_minAltitude = 0;
258     d->m_maxAltitude = 0;
259     d->m_altitudeMode = ClampToGround;
260 }
261 
pack(QDataStream & stream) const262 void GeoDataLatLonAltBox::pack( QDataStream& stream ) const
263 {
264     GeoDataObject::pack( stream );
265 
266     stream << d->m_minAltitude << d->m_maxAltitude;
267     stream << d->m_altitudeMode;
268 }
269 
unpack(QDataStream & stream)270 void GeoDataLatLonAltBox::unpack( QDataStream& stream )
271 {
272     GeoDataObject::unpack( stream );
273 
274     stream >> d->m_minAltitude >> d->m_maxAltitude;
275     int a;
276     stream >> a;
277     d->m_altitudeMode = static_cast<AltitudeMode>( a );
278 }
279 
qHash(const GeoDataLatLonAltBox & box,uint seed)280 uint qHash(const GeoDataLatLonAltBox &box, uint seed)
281 {
282     seed = ::qHash(box.east(), seed);
283     seed = ::qHash(box.west(), seed);
284     seed = ::qHash(box.south(), seed);
285     seed = ::qHash(box.north(), seed);
286     seed = ::qHash(box.maxAltitude(), seed);
287 
288     return ::qHash(box.minAltitude(), seed);
289 }
290 
291 }
292