1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
4 // SPDX-FileCopyrightText: 2008 Jens-Michael Hoffmann <jensmh@gmx.de>
5 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6 //
7
8
9 #include "ViewportParams.h"
10
11 #include <QRect>
12
13 #include <QPainterPath>
14 #include <QRegion>
15
16 #include "MarbleDebug.h"
17 #include "GeoDataLatLonAltBox.h"
18 #include "SphericalProjection.h"
19 #include "EquirectProjection.h"
20 #include "MercatorProjection.h"
21 #include "GnomonicProjection.h"
22 #include "LambertAzimuthalProjection.h"
23 #include "AzimuthalEquidistantProjection.h"
24 #include "StereographicProjection.h"
25 #include "VerticalPerspectiveProjection.h"
26
27
28 namespace Marble
29 {
30
31 class ViewportParamsPrivate
32 {
33 public:
34 ViewportParamsPrivate( Projection projection,
35 qreal centerLongitude, qreal centerLatitude,
36 int radius,
37 const QSize &size );
38
39 static const AbstractProjection *abstractProjection( Projection projection );
40
41 // These two go together. m_currentProjection points to one of
42 // the static Projection classes at the bottom.
43 Projection m_projection;
44 const AbstractProjection *m_currentProjection;
45
46 // Parameters that determine the painting
47 qreal m_centerLongitude;
48 qreal m_centerLatitude;
49 qreal m_heading;
50 Quaternion m_planetAxis; // Position, coded in a quaternion
51 matrix m_planetAxisMatrix;
52 int m_radius; // Zoom level (pixels / globe radius)
53 qreal m_angularResolution;
54
55 QSize m_size; // width, height
56
57
58 bool m_dirtyBox;
59 GeoDataLatLonAltBox m_viewLatLonAltBox;
60
61 static const SphericalProjection s_sphericalProjection;
62 static const EquirectProjection s_equirectProjection;
63 static const MercatorProjection s_mercatorProjection;
64 static const GnomonicProjection s_gnomonicProjection;
65 static const StereographicProjection s_stereographicProjection;
66 static const LambertAzimuthalProjection s_lambertAzimuthalProjection;
67 static const AzimuthalEquidistantProjection s_azimuthalEquidistantProjection;
68 static const VerticalPerspectiveProjection s_verticalPerspectiveProjection;
69
70 GeoDataCoordinates m_focusPoint;
71 };
72
73 const SphericalProjection ViewportParamsPrivate::s_sphericalProjection;
74 const EquirectProjection ViewportParamsPrivate::s_equirectProjection;
75 const MercatorProjection ViewportParamsPrivate::s_mercatorProjection;
76 const GnomonicProjection ViewportParamsPrivate::s_gnomonicProjection;
77 const StereographicProjection ViewportParamsPrivate::s_stereographicProjection;
78 const LambertAzimuthalProjection ViewportParamsPrivate::s_lambertAzimuthalProjection;
79 const AzimuthalEquidistantProjection ViewportParamsPrivate::s_azimuthalEquidistantProjection;
80 const VerticalPerspectiveProjection ViewportParamsPrivate::s_verticalPerspectiveProjection;
81
ViewportParamsPrivate(Projection projection,qreal centerLongitude,qreal centerLatitude,int radius,const QSize & size)82 ViewportParamsPrivate::ViewportParamsPrivate( Projection projection,
83 qreal centerLongitude, qreal centerLatitude,
84 int radius,
85 const QSize &size )
86 : m_projection( projection ),
87 m_currentProjection( abstractProjection( projection ) ),
88 m_centerLongitude( centerLongitude ),
89 m_centerLatitude( centerLatitude ),
90 m_heading( 0 ),
91 m_planetAxis(),
92 m_planetAxisMatrix(),
93 m_radius( radius ),
94 m_angularResolution(4.0 / abs(m_radius)),
95 m_size( size ),
96 m_dirtyBox( true ),
97 m_viewLatLonAltBox()
98 {
99 }
100
abstractProjection(Projection projection)101 const AbstractProjection *ViewportParamsPrivate::abstractProjection(Projection projection)
102 {
103 switch ( projection ) {
104 case Spherical:
105 return &s_sphericalProjection;
106 case Equirectangular:
107 return &s_equirectProjection;
108 case Mercator:
109 return &s_mercatorProjection;
110 case Gnomonic:
111 return &s_gnomonicProjection;
112 case Stereographic:
113 return &s_stereographicProjection;
114 case LambertAzimuthal:
115 return &s_lambertAzimuthalProjection;
116 case AzimuthalEquidistant:
117 return &s_azimuthalEquidistantProjection;
118 case VerticalPerspective:
119 return &s_verticalPerspectiveProjection;
120 }
121
122 return nullptr;
123 }
124
125
ViewportParams()126 ViewportParams::ViewportParams()
127 : d( new ViewportParamsPrivate( Spherical, 0, 0, 2000, QSize( 100, 100 ) ) )
128 {
129 centerOn( d->m_centerLongitude, d->m_centerLatitude );
130 }
131
ViewportParams(Projection projection,qreal centerLongitude,qreal centerLatitude,int radius,const QSize & size)132 ViewportParams::ViewportParams( Projection projection,
133 qreal centerLongitude, qreal centerLatitude,
134 int radius,
135 const QSize &size )
136 : d( new ViewportParamsPrivate( projection, centerLongitude, centerLatitude, radius, size ) )
137 {
138 centerOn( d->m_centerLongitude, d->m_centerLatitude );
139 }
140
~ViewportParams()141 ViewportParams::~ViewportParams()
142 {
143 delete d;
144 }
145
146
147 // ================================================================
148 // Getters and setters
149
150
projection() const151 Projection ViewportParams::projection() const
152 {
153 return d->m_projection;
154 }
155
currentProjection() const156 const AbstractProjection *ViewportParams::currentProjection() const
157 {
158 return d->m_currentProjection;
159 }
160
setProjection(Projection newProjection)161 void ViewportParams::setProjection(Projection newProjection)
162 {
163 d->m_projection = newProjection;
164 d->m_currentProjection = ViewportParamsPrivate::abstractProjection( newProjection );
165
166 // We now need to reset the planetAxis to make sure
167 // that it's a valid axis orientation!
168 // So this line is important (although it might look odd) ! :
169 centerOn( d->m_centerLongitude, d->m_centerLatitude );
170 }
171
polarity() const172 int ViewportParams::polarity() const
173 {
174 // For mercator this just gives the extreme latitudes
175 // instead of the actual poles but it works fine as well:
176 GeoDataCoordinates northPole( 0.0, +currentProjection()->maxLat() );
177 GeoDataCoordinates southPole( 0.0, -currentProjection()->maxLat() );
178
179 bool globeHidesN, globeHidesS;
180 qreal x;
181 qreal yN, yS;
182
183 currentProjection()->screenCoordinates( northPole, this,
184 x, yN, globeHidesN );
185 currentProjection()->screenCoordinates( southPole, this,
186 x, yS, globeHidesS );
187
188 int polarity = 0;
189
190 // case of the flat map:
191 if ( !globeHidesN && !globeHidesS ) {
192 if ( yN < yS ) {
193 polarity = +1;
194 }
195 if ( yS < yN ) {
196 polarity = -1;
197 }
198 }
199 else {
200 if ( !globeHidesN && yN < height() / 2 ) {
201 polarity = +1;
202 }
203 if ( !globeHidesN && yN > height() / 2 ) {
204 polarity = -1;
205 }
206 if ( !globeHidesS && yS > height() / 2 ) {
207 polarity = +1;
208 }
209 if ( !globeHidesS && yS < height() / 2 ) {
210 polarity = -1;
211 }
212 }
213
214 return polarity;
215 }
216
radius() const217 int ViewportParams::radius() const
218 {
219 return d->m_radius;
220 }
221
setRadius(int newRadius)222 void ViewportParams::setRadius(int newRadius)
223 {
224 if ( newRadius > 0 ) {
225 d->m_dirtyBox = true;
226
227 d->m_radius = newRadius;
228 d->m_angularResolution = 4.0 / d->m_radius;
229 }
230 }
231
centerOn(qreal lon,qreal lat)232 void ViewportParams::centerOn( qreal lon, qreal lat )
233 {
234 if ( !d->m_currentProjection->traversablePoles() ) {
235 if ( lat > d->m_currentProjection->maxLat() )
236 lat = d->m_currentProjection->maxLat();
237
238 if ( lat < d->m_currentProjection->minLat() )
239 lat = d->m_currentProjection->minLat();
240 } else {
241 while ( lat > M_PI )
242 lat -= 2 * M_PI;
243 while ( lat < -M_PI )
244 lat += 2 * M_PI;
245 }
246
247 while ( lon > M_PI )
248 lon -= 2 * M_PI;
249 while ( lon < -M_PI )
250 lon += 2 * M_PI;
251
252 d->m_centerLongitude = lon;
253 d->m_centerLatitude = lat;
254
255 const Quaternion roll = Quaternion::fromEuler( 0, 0, d->m_heading );
256 const Quaternion quat = Quaternion::fromEuler( -lat, lon, 0.0 );
257
258 d->m_planetAxis = quat * roll;
259 d->m_planetAxis.normalize();
260
261 d->m_dirtyBox = true;
262 d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix );
263 d->m_planetAxis.normalize();
264 }
265
setHeading(qreal heading)266 void ViewportParams::setHeading( qreal heading )
267 {
268 d->m_heading = heading;
269
270 const Quaternion roll = Quaternion::fromEuler( 0, 0, heading );
271
272 const qreal centerLat = centerLatitude();
273 const qreal centerLon = centerLongitude();
274
275 const Quaternion quat = Quaternion::fromEuler( -centerLat, centerLon, 0 );
276
277 d->m_planetAxis = quat * roll;
278 d->m_planetAxis.normalize();
279
280 d->m_dirtyBox = true;
281 d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix );
282 d->m_planetAxis.normalize();
283 }
284
heading() const285 qreal ViewportParams::heading() const
286 {
287 return d->m_heading;
288 }
289
planetAxis() const290 Quaternion ViewportParams::planetAxis() const
291 {
292 return d->m_planetAxis;
293 }
294
planetAxisMatrix() const295 const matrix &ViewportParams::planetAxisMatrix() const
296 {
297 return d->m_planetAxisMatrix;
298 }
299
width() const300 int ViewportParams::width() const
301 {
302 return d->m_size.width();
303 }
304
height() const305 int ViewportParams::height() const
306 {
307 return d->m_size.height();
308 }
309
size() const310 QSize ViewportParams::size() const
311 {
312 return d->m_size;
313 }
314
315
setWidth(int newWidth)316 void ViewportParams::setWidth(int newWidth)
317 {
318 setSize( QSize( newWidth, height() ) );
319 }
320
setHeight(int newHeight)321 void ViewportParams::setHeight(int newHeight)
322 {
323 setSize( QSize( width(), newHeight ) );
324 }
325
setSize(const QSize & newSize)326 void ViewportParams::setSize(const QSize& newSize)
327 {
328 if ( newSize == d->m_size )
329 return;
330
331 d->m_dirtyBox = true;
332
333 d->m_size = newSize;
334 }
335
336 // ================================================================
337 // Other functions
338
centerLongitude() const339 qreal ViewportParams::centerLongitude() const
340 {
341 return d->m_centerLongitude;
342 }
343
centerLatitude() const344 qreal ViewportParams::centerLatitude() const
345 {
346 return d->m_centerLatitude;
347 }
348
viewLatLonAltBox() const349 const GeoDataLatLonAltBox& ViewportParams::viewLatLonAltBox() const
350 {
351 if (d->m_dirtyBox) {
352 d->m_viewLatLonAltBox = d->m_currentProjection->latLonAltBox( QRect( QPoint( 0, 0 ),
353 d->m_size ),
354 this );
355 d->m_dirtyBox = false;
356 }
357
358 return d->m_viewLatLonAltBox;
359 }
360
latLonAltBox(const QRect & screenRect) const361 GeoDataLatLonAltBox ViewportParams::latLonAltBox( const QRect &screenRect ) const
362 {
363 return d->m_currentProjection->latLonAltBox( screenRect, this );
364 }
365
angularResolution() const366 qreal ViewportParams::angularResolution() const
367 {
368 // We essentially divide the diameter by 180 deg and
369 // take half of the result as a guess for the angle per pixel resolution.
370 // d->m_angularResolution = 0.25 * M_PI / fabs( (qreal)(d->m_radius);
371 return d->m_angularResolution;
372 }
373
resolves(const GeoDataLatLonBox & latLonBox,qreal pixel) const374 bool ViewportParams::resolves ( const GeoDataLatLonBox &latLonBox, qreal pixel ) const
375 {
376 return latLonBox.width() + latLonBox.height() > pixel * d->m_angularResolution;
377 }
378
379
resolves(const GeoDataLatLonAltBox & latLonAltBox,qreal pixel,qreal altitude) const380 bool ViewportParams::resolves ( const GeoDataLatLonAltBox &latLonAltBox, qreal pixel, qreal altitude ) const
381 {
382 return latLonAltBox.width() + latLonAltBox.height() > pixel * d->m_angularResolution
383 || latLonAltBox.maxAltitude() - latLonAltBox.minAltitude() > altitude;
384 }
385
resolves(const GeoDataCoordinates & coord1,const GeoDataCoordinates & coord2) const386 bool ViewportParams::resolves ( const GeoDataCoordinates &coord1,
387 const GeoDataCoordinates &coord2 ) const
388 {
389 qreal lon1, lat1;
390 coord1.geoCoordinates( lon1, lat1 );
391
392 qreal lon2, lat2;
393 coord2.geoCoordinates( lon2, lat2 );
394
395 // We take the manhattan length as an approximation for the distance
396 return ( fabs( lon2 - lon1 ) + fabs( lat2 - lat1 ) > d->m_angularResolution );
397 }
398
399
screenCoordinates(const qreal lon,const qreal lat,qreal & x,qreal & y) const400 bool ViewportParams::screenCoordinates( const qreal lon, const qreal lat,
401 qreal &x, qreal &y ) const
402 {
403 return d->m_currentProjection->screenCoordinates( lon, lat, this, x, y );
404 }
405
screenCoordinates(const GeoDataCoordinates & geopoint,qreal & x,qreal & y,bool & globeHidesPoint) const406 bool ViewportParams::screenCoordinates( const GeoDataCoordinates &geopoint,
407 qreal &x, qreal &y,
408 bool &globeHidesPoint ) const
409 {
410 return d->m_currentProjection->screenCoordinates( geopoint, this, x, y, globeHidesPoint );
411 }
412
screenCoordinates(const GeoDataCoordinates & geopoint,qreal & x,qreal & y) const413 bool ViewportParams::screenCoordinates( const GeoDataCoordinates &geopoint,
414 qreal &x, qreal &y ) const
415 {
416 return d->m_currentProjection->screenCoordinates( geopoint, this, x, y );
417 }
418
screenCoordinates(const GeoDataCoordinates & coordinates,qreal * x,qreal & y,int & pointRepeatNum,const QSizeF & size,bool & globeHidesPoint) const419 bool ViewportParams::screenCoordinates( const GeoDataCoordinates &coordinates,
420 qreal *x, qreal &y, int &pointRepeatNum,
421 const QSizeF& size,
422 bool &globeHidesPoint ) const
423 {
424 return d->m_currentProjection->screenCoordinates( coordinates, this, x, y, pointRepeatNum, size, globeHidesPoint );
425 }
426
427
screenCoordinates(const GeoDataLineString & lineString,QVector<QPolygonF * > & polygons) const428 bool ViewportParams::screenCoordinates( const GeoDataLineString &lineString,
429 QVector<QPolygonF*> &polygons ) const
430 {
431 return d->m_currentProjection->screenCoordinates( lineString, this, polygons );
432 }
433
geoCoordinates(const int x,const int y,qreal & lon,qreal & lat,GeoDataCoordinates::Unit unit) const434 bool ViewportParams::geoCoordinates( const int x, const int y,
435 qreal &lon, qreal &lat,
436 GeoDataCoordinates::Unit unit ) const
437 {
438 return d->m_currentProjection->geoCoordinates( x, y, this, lon, lat, unit );
439 }
440
mapCoversViewport() const441 bool ViewportParams::mapCoversViewport() const
442 {
443 return d->m_currentProjection->mapCoversViewport( this );
444 }
445
mapShape() const446 QPainterPath ViewportParams::mapShape() const
447 {
448 return d->m_currentProjection->mapShape( this );
449 }
450
mapRegion() const451 QRegion ViewportParams::mapRegion() const
452 {
453 return d->m_currentProjection->mapRegion( this );
454 }
455
focusPoint() const456 GeoDataCoordinates ViewportParams::focusPoint() const
457 {
458 if (d->m_focusPoint.isValid()) {
459 return d->m_focusPoint;
460 }
461 else {
462 const qreal lon = d->m_centerLongitude;
463 const qreal lat = d->m_centerLatitude;
464
465 return GeoDataCoordinates(lon, lat, 0.0, GeoDataCoordinates::Radian);
466 }
467
468 }
469
setFocusPoint(const GeoDataCoordinates & focusPoint)470 void ViewportParams::setFocusPoint(const GeoDataCoordinates &focusPoint)
471 {
472 d->m_focusPoint = focusPoint;
473 }
474
resetFocusPoint()475 void ViewportParams::resetFocusPoint()
476 {
477 d->m_focusPoint = GeoDataCoordinates();
478 }
479
480 }
481