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
390bool ViewportParams::screenCoordinates(const GeoDataCoordinates &coordinates,
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
425GeoDataCoordinates ViewportParams::focusPoint() const
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
437void ViewportParams::setFocusPoint(const GeoDataCoordinates &focusPoint)
438{
439 d->m_focusPoint = focusPoint;
440}
441
442void ViewportParams::resetFocusPoint()
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.
Unit
enum used constructor to specify the units used
A LineString that allows to store a contiguous set of line segments.
QAction * roll(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
@ 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-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:37:04 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.