Marble

ViewportParams.cpp
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#include "ViewportParams.h"
9
10#include <QRect>
11
12#include <QPainterPath>
13#include <QRegion>
14
15#include "AzimuthalEquidistantProjection.h"
16#include "EquirectProjection.h"
17#include "GeoDataLatLonAltBox.h"
18#include "GnomonicProjection.h"
19#include "LambertAzimuthalProjection.h"
20#include "MarbleDebug.h"
21#include "MercatorProjection.h"
22#include "SphericalProjection.h"
23#include "StereographicProjection.h"
24#include "VerticalPerspectiveProjection.h"
25
26namespace Marble
27{
28
29class ViewportParamsPrivate
30{
31public:
32 ViewportParamsPrivate(Projection projection, qreal centerLongitude, qreal centerLatitude, int radius, const QSize &size);
33
34 static const AbstractProjection *abstractProjection(Projection projection);
35
36 // These two go together. m_currentProjection points to one of
37 // the static Projection classes at the bottom.
38 Projection m_projection;
39 const AbstractProjection *m_currentProjection;
40
41 // Parameters that determine the painting
42 qreal m_centerLongitude;
43 qreal m_centerLatitude;
44 qreal m_heading;
45 Quaternion m_planetAxis; // Position, coded in a quaternion
46 matrix m_planetAxisMatrix;
47 int m_radius; // Zoom level (pixels / globe radius)
48 qreal m_angularResolution;
49
50 QSize m_size; // width, height
51
52 bool m_dirtyBox;
53 GeoDataLatLonAltBox m_viewLatLonAltBox;
54
55 static const SphericalProjection s_sphericalProjection;
56 static const EquirectProjection s_equirectProjection;
57 static const MercatorProjection s_mercatorProjection;
58 static const GnomonicProjection s_gnomonicProjection;
59 static const StereographicProjection s_stereographicProjection;
60 static const LambertAzimuthalProjection s_lambertAzimuthalProjection;
61 static const AzimuthalEquidistantProjection s_azimuthalEquidistantProjection;
62 static const VerticalPerspectiveProjection s_verticalPerspectiveProjection;
63
64 GeoDataCoordinates m_focusPoint;
65};
66
67const SphericalProjection ViewportParamsPrivate::s_sphericalProjection;
68const EquirectProjection ViewportParamsPrivate::s_equirectProjection;
69const MercatorProjection ViewportParamsPrivate::s_mercatorProjection;
70const GnomonicProjection ViewportParamsPrivate::s_gnomonicProjection;
71const StereographicProjection ViewportParamsPrivate::s_stereographicProjection;
72const LambertAzimuthalProjection ViewportParamsPrivate::s_lambertAzimuthalProjection;
73const AzimuthalEquidistantProjection ViewportParamsPrivate::s_azimuthalEquidistantProjection;
74const VerticalPerspectiveProjection ViewportParamsPrivate::s_verticalPerspectiveProjection;
75
76ViewportParamsPrivate::ViewportParamsPrivate(Projection projection, qreal centerLongitude, qreal centerLatitude, int radius, const QSize &size)
77 : m_projection(projection)
78 , m_currentProjection(abstractProjection(projection))
79 , m_centerLongitude(centerLongitude)
80 , m_centerLatitude(centerLatitude)
81 , m_heading(0)
82 , m_planetAxis()
83 , m_planetAxisMatrix()
84 , m_radius(radius)
85 , m_angularResolution(4.0 / abs(m_radius))
86 , m_size(size)
87 , m_dirtyBox(true)
88 , m_viewLatLonAltBox()
89{
90}
91
92const AbstractProjection *ViewportParamsPrivate::abstractProjection(Projection projection)
93{
94 switch (projection) {
95 case Spherical:
96 return &s_sphericalProjection;
97 case Equirectangular:
98 return &s_equirectProjection;
99 case Mercator:
100 return &s_mercatorProjection;
101 case Gnomonic:
102 return &s_gnomonicProjection;
103 case Stereographic:
104 return &s_stereographicProjection;
105 case LambertAzimuthal:
106 return &s_lambertAzimuthalProjection;
108 return &s_azimuthalEquidistantProjection;
110 return &s_verticalPerspectiveProjection;
111 }
112
113 return nullptr;
114}
115
116ViewportParams::ViewportParams()
117 : d(new ViewportParamsPrivate(Spherical, 0, 0, 2000, QSize(100, 100)))
118{
119 centerOn(d->m_centerLongitude, d->m_centerLatitude);
120}
121
122ViewportParams::ViewportParams(Projection projection, qreal centerLongitude, qreal centerLatitude, int radius, const QSize &size)
123 : d(new ViewportParamsPrivate(projection, centerLongitude, centerLatitude, radius, size))
124{
125 centerOn(d->m_centerLongitude, d->m_centerLatitude);
126}
127
128ViewportParams::~ViewportParams()
129{
130 delete d;
131}
132
133// ================================================================
134// Getters and setters
135
136Projection ViewportParams::projection() const
137{
138 return d->m_projection;
139}
140
141const AbstractProjection *ViewportParams::currentProjection() const
142{
143 return d->m_currentProjection;
144}
145
146void ViewportParams::setProjection(Projection newProjection)
147{
148 d->m_projection = newProjection;
149 d->m_currentProjection = ViewportParamsPrivate::abstractProjection(newProjection);
150
151 // We now need to reset the planetAxis to make sure
152 // that it's a valid axis orientation!
153 // So this line is important (although it might look odd) ! :
154 centerOn(d->m_centerLongitude, d->m_centerLatitude);
155}
156
157int ViewportParams::polarity() const
158{
159 // For mercator this just gives the extreme latitudes
160 // instead of the actual poles but it works fine as well:
161 GeoDataCoordinates northPole(0.0, +currentProjection()->maxLat());
162 GeoDataCoordinates southPole(0.0, -currentProjection()->maxLat());
163
164 bool globeHidesN, globeHidesS;
165 qreal x;
166 qreal yN, yS;
167
168 currentProjection()->screenCoordinates(northPole, this, x, yN, globeHidesN);
169 currentProjection()->screenCoordinates(southPole, this, x, yS, globeHidesS);
170
171 int polarity = 0;
172
173 // case of the flat map:
174 if (!globeHidesN && !globeHidesS) {
175 if (yN < yS) {
176 polarity = +1;
177 }
178 if (yS < yN) {
179 polarity = -1;
180 }
181 } else {
182 if (!globeHidesN && yN < height() / 2) {
183 polarity = +1;
184 }
185 if (!globeHidesN && yN > height() / 2) {
186 polarity = -1;
187 }
188 if (!globeHidesS && yS > height() / 2) {
189 polarity = +1;
190 }
191 if (!globeHidesS && yS < height() / 2) {
192 polarity = -1;
193 }
194 }
195
196 return polarity;
197}
198
199int ViewportParams::radius() const
200{
201 return d->m_radius;
202}
203
204void ViewportParams::setRadius(int newRadius)
205{
206 if (newRadius > 0) {
207 d->m_dirtyBox = true;
208
209 d->m_radius = newRadius;
210 d->m_angularResolution = 4.0 / d->m_radius;
211 }
212}
213
214void ViewportParams::centerOn(qreal lon, qreal lat)
215{
216 if (!d->m_currentProjection->traversablePoles()) {
217 if (lat > d->m_currentProjection->maxLat())
218 lat = d->m_currentProjection->maxLat();
219
220 if (lat < d->m_currentProjection->minLat())
221 lat = d->m_currentProjection->minLat();
222 } else {
223 while (lat > M_PI)
224 lat -= 2 * M_PI;
225 while (lat < -M_PI)
226 lat += 2 * M_PI;
227 }
228
229 while (lon > M_PI)
230 lon -= 2 * M_PI;
231 while (lon < -M_PI)
232 lon += 2 * M_PI;
233
234 d->m_centerLongitude = lon;
235 d->m_centerLatitude = lat;
236
237 const Quaternion roll = Quaternion::fromEuler(0, 0, d->m_heading);
238 const Quaternion quat = Quaternion::fromEuler(-lat, lon, 0.0);
239
240 d->m_planetAxis = quat * roll;
241 d->m_planetAxis.normalize();
242
243 d->m_dirtyBox = true;
244 d->m_planetAxis.inverse().toMatrix(d->m_planetAxisMatrix);
245 d->m_planetAxis.normalize();
246}
247
248void ViewportParams::setHeading(qreal heading)
249{
250 d->m_heading = heading;
251
252 const Quaternion roll = Quaternion::fromEuler(0, 0, heading);
253
254 const qreal centerLat = centerLatitude();
255 const qreal centerLon = centerLongitude();
256
257 const Quaternion quat = Quaternion::fromEuler(-centerLat, centerLon, 0);
258
259 d->m_planetAxis = quat * roll;
260 d->m_planetAxis.normalize();
261
262 d->m_dirtyBox = true;
263 d->m_planetAxis.inverse().toMatrix(d->m_planetAxisMatrix);
264 d->m_planetAxis.normalize();
265}
266
267qreal ViewportParams::heading() const
268{
269 return d->m_heading;
270}
271
272Quaternion ViewportParams::planetAxis() const
273{
274 return d->m_planetAxis;
275}
276
277const matrix &ViewportParams::planetAxisMatrix() const
278{
279 return d->m_planetAxisMatrix;
280}
281
282int ViewportParams::width() const
283{
284 return d->m_size.width();
285}
286
287int ViewportParams::height() const
288{
289 return d->m_size.height();
290}
291
292QSize ViewportParams::size() const
293{
294 return d->m_size;
295}
296
297void ViewportParams::setWidth(int newWidth)
298{
299 setSize(QSize(newWidth, height()));
300}
301
302void ViewportParams::setHeight(int newHeight)
303{
304 setSize(QSize(width(), newHeight));
305}
306
307void ViewportParams::setSize(const QSize &newSize)
308{
309 if (newSize == d->m_size)
310 return;
311
312 d->m_dirtyBox = true;
313
314 d->m_size = newSize;
315}
316
317// ================================================================
318// Other functions
319
320qreal ViewportParams::centerLongitude() const
321{
322 return d->m_centerLongitude;
323}
324
325qreal ViewportParams::centerLatitude() const
326{
327 return d->m_centerLatitude;
328}
329
330const GeoDataLatLonAltBox &ViewportParams::viewLatLonAltBox() const
331{
332 if (d->m_dirtyBox) {
333 d->m_viewLatLonAltBox = d->m_currentProjection->latLonAltBox(QRect(QPoint(0, 0), d->m_size), this);
334 d->m_dirtyBox = false;
335 }
336
337 return d->m_viewLatLonAltBox;
338}
339
340GeoDataLatLonAltBox ViewportParams::latLonAltBox(const QRect &screenRect) const
341{
342 return d->m_currentProjection->latLonAltBox(screenRect, this);
343}
344
345qreal ViewportParams::angularResolution() const
346{
347 // We essentially divide the diameter by 180 deg and
348 // take half of the result as a guess for the angle per pixel resolution.
349 // d->m_angularResolution = 0.25 * M_PI / fabs( (qreal)(d->m_radius);
350 return d->m_angularResolution;
351}
352
353bool ViewportParams::resolves(const GeoDataLatLonBox &latLonBox, qreal pixel) const
354{
355 return latLonBox.width() + latLonBox.height() > pixel * d->m_angularResolution;
356}
357
358bool ViewportParams::resolves(const GeoDataLatLonAltBox &latLonAltBox, qreal pixel, qreal altitude) const
359{
360 return latLonAltBox.width() + latLonAltBox.height() > pixel * d->m_angularResolution || latLonAltBox.maxAltitude() - latLonAltBox.minAltitude() > altitude;
361}
362
363bool ViewportParams::resolves(const GeoDataCoordinates &coord1, const GeoDataCoordinates &coord2) const
364{
365 qreal lon1, lat1;
366 coord1.geoCoordinates(lon1, lat1);
367
368 qreal lon2, lat2;
369 coord2.geoCoordinates(lon2, lat2);
370
371 // We take the manhattan length as an approximation for the distance
372 return (fabs(lon2 - lon1) + fabs(lat2 - lat1) > d->m_angularResolution);
373}
374
375bool ViewportParams::screenCoordinates(const qreal lon, const qreal lat, qreal &x, qreal &y) const
376{
377 return d->m_currentProjection->screenCoordinates(lon, lat, this, x, y);
378}
379
380bool ViewportParams::screenCoordinates(const GeoDataCoordinates &geopoint, qreal &x, qreal &y, bool &globeHidesPoint) const
381{
382 return d->m_currentProjection->screenCoordinates(geopoint, this, x, y, globeHidesPoint);
383}
384
385bool ViewportParams::screenCoordinates(const GeoDataCoordinates &geopoint, qreal &x, qreal &y) const
386{
387 return d->m_currentProjection->screenCoordinates(geopoint, this, x, y);
388}
389
391 qreal *x,
392 qreal &y,
393 int &pointRepeatNum,
394 const QSizeF &size,
395 bool &globeHidesPoint) const
396{
397 return d->m_currentProjection->screenCoordinates(coordinates, this, x, y, pointRepeatNum, size, globeHidesPoint);
398}
399
400bool ViewportParams::screenCoordinates(const GeoDataLineString &lineString, QList<QPolygonF *> &polygons) const
401{
402 return d->m_currentProjection->screenCoordinates(lineString, this, polygons);
403}
404
405bool ViewportParams::geoCoordinates(const int x, const int y, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit) const
406{
407 return d->m_currentProjection->geoCoordinates(x, y, this, lon, lat, unit);
408}
409
410bool ViewportParams::mapCoversViewport() const
411{
412 return d->m_currentProjection->mapCoversViewport(this);
413}
414
415QPainterPath ViewportParams::mapShape() const
416{
417 return d->m_currentProjection->mapShape(this);
418}
419
420QRegion ViewportParams::mapRegion() const
421{
422 return d->m_currentProjection->mapRegion(this);
423}
424
426{
427 if (d->m_focusPoint.isValid()) {
428 return d->m_focusPoint;
429 } else {
430 const qreal lon = d->m_centerLongitude;
431 const qreal lat = d->m_centerLatitude;
432
433 return {lon, lat, 0.0, GeoDataCoordinates::Radian};
434 }
435}
436
438{
439 d->m_focusPoint = focusPoint;
440}
441
443{
444 d->m_focusPoint = GeoDataCoordinates();
445}
446
447}
This file contains the headers for EquirectProjection.
This file contains the headers for MercatorProjection.
This file contains the headers for SphericalProjection.
This file contains the headers for ViewportParams.
A 3d point representation.
void geoCoordinates(qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit) const
use this function to get the longitude and latitude with one call - use the unit parameter to switch ...
A class to implement the spherical projection used by the "Globe" view.
A class to implement the Equirectangular projection used by the "Flat Map" view.
A 3d point representation.
Unit
enum used constructor to specify the units used
A LineString that allows to store a contiguous set of line segments.
A class to implement the spherical projection used by the "Globe" view.
A class to implement the spherical projection used by the "Globe" view.
A class to implement the Mercator projection.
A class to implement the spherical projection used by the "Globe" view.
A class to implement the spherical projection used by the "Globe" view.
A class to implement the spherical projection used by the "Globe" view.
bool screenCoordinates(const qreal lon, const qreal lat, qreal &x, qreal &y) const
Get the screen coordinates corresponding to geographical coordinates in the map.
void setFocusPoint(const GeoDataCoordinates &focusPoint)
Change the point of focus, overridding any previously set focus point.
void setRadius(int radius)
Change the radius of the planet.
void resetFocusPoint()
Invalidate any focus point set with setFocusPoint.
bool geoCoordinates(const int x, const int y, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Degree) const
Get the earth coordinates corresponding to a pixel in the map.
GeoDataCoordinates focusPoint() const
QAction * roll(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
Projection
This enum is used to choose the projection shown in the view.
@ Mercator
Mercator projection.
@ VerticalPerspective
Vertical perspective projection.
@ AzimuthalEquidistant
Azimuthal Equidistant projection.
@ Gnomonic
Gnomonic projection.
@ LambertAzimuthal
Lambert Azimuthal Equal-Area projection.
@ Equirectangular
Flat projection ("plate carree")
@ Spherical
Spherical projection ("Orthographic")
@ Stereographic
Stereographic projection.
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:52:10 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.