Marble

MercatorProjection.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2007-2008 Inge Wallin <ingwa@kde.org>
4// SPDX-FileCopyrightText: 2007-2012 Torsten Rahn <rahn@kde.org>
5//
6
7// Local
9
10// Marble
11#include "GeoDataLatLonAltBox.h"
12#include "ViewportParams.h"
13
14#include "MarbleMath.h"
15
16#include <QIcon>
17
18using namespace Marble;
19
22 , m_lastCenterLat(200.0)
23 , m_lastCenterLatInv(0.0)
24{
25 setMinLat(minValidLat());
26 setMaxLat(maxValidLat());
27}
28
29MercatorProjection::~MercatorProjection() = default;
30
32{
33 return QObject::tr("Mercator");
34}
35
37{
38 return QObject::tr("<p><b>Mercator Projection</b></p><p>Applications: popular standard map projection for navigation.</p>");
39}
40
42{
43 return QIcon(QStringLiteral(":/icons/map-mercator.png"));
44}
45
47{
48 // This is the max value where gd( lat ) is defined.
49 return +85.05113 * DEG2RAD;
50}
51
53{
54 // This is the min value where gd( lat ) is defined.
55 return -85.05113 * DEG2RAD;
56}
57
58bool MercatorProjection::screenCoordinates(const GeoDataCoordinates &geopoint, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint) const
59{
60 globeHidesPoint = false;
61 qreal lon;
62 qreal originalLat;
63
64 geopoint.geoCoordinates(lon, originalLat);
65 qreal const lat = qBound(minLat(), originalLat, maxLat());
66 const bool isLatValid = lat == originalLat;
67
68 // Convenience variables
69 int radius = viewport->radius();
70 auto width = (qreal)(viewport->width());
71 auto height = (qreal)(viewport->height());
72
73 qreal rad2Pixel = 2 * radius / M_PI;
74
75 const qreal centerLon = viewport->centerLongitude();
76 const qreal centerLat = viewport->centerLatitude();
77 if (centerLat != m_lastCenterLat) {
78 m_lastCenterLatInv = gdInv(centerLat);
79 m_lastCenterLat = centerLat;
80 }
81
82 // Let (x, y) be the position on the screen of the placemark..
83 x = (width / 2 + rad2Pixel * (lon - centerLon));
84 y = (height / 2 - rad2Pixel * (gdInv(lat) - m_lastCenterLatInv));
85
86 // Return true if the calculated point is inside the screen area,
87 // otherwise return false.
88 return isLatValid
89 && ((0 <= y && y < height)
90 && ((0 <= x && x < width) || (0 <= x - 4 * radius && x - 4 * radius < width) || (0 <= x + 4 * radius && x + 4 * radius < width)));
91}
92
94 const ViewportParams *viewport,
95 qreal *x,
96 qreal &y,
97 int &pointRepeatNum,
98 const QSizeF &size,
99 bool &globeHidesPoint) const
100{
101 pointRepeatNum = 0;
102 // On flat projections the observer's view onto the point won't be
103 // obscured by the target planet itself.
104 globeHidesPoint = false;
105
106 // Convenience variables
107 int radius = viewport->radius();
108 auto width = (qreal)(viewport->width());
109 auto height = (qreal)(viewport->height());
110
111 // Let (itX, y) be the first guess for one possible position on screen..
112 qreal itX;
113 bool visible = screenCoordinates(coordinates, viewport, itX, y);
114
115 // Make sure that the requested point is within the visible y range:
116 if (0 <= y + size.height() / 2.0 && y < height + size.height() / 2.0) {
117 // For the repetition case the same geopoint gets displayed on
118 // the map many times.across the longitude.
119
120 int xRepeatDistance = 4 * radius;
121
122 // Finding the leftmost positive x value
123 if (itX + size.width() / 2.0 >= xRepeatDistance) {
124 const int repeatNum = (int)((itX + size.width() / 2.0) / xRepeatDistance);
125 itX = itX - repeatNum * xRepeatDistance;
126 }
127 if (itX + size.width() / 2.0 < 0) {
128 itX += xRepeatDistance;
129 }
130 // the requested point is out of the visible x range:
131 if (itX > width + size.width() / 2.0) {
132 return false;
133 }
134
135 // Now iterate through all visible x screen coordinates for the point
136 // from left to right.
137 int itNum = 0;
138 while (itX - size.width() / 2.0 < width) {
139 *x = itX;
140 ++x;
141 ++itNum;
142 itX += xRepeatDistance;
143 }
144
145 pointRepeatNum = itNum;
146
147 return visible;
148 }
149
150 // the requested point is out of the visible y range:
151 return false;
152}
153
154bool MercatorProjection::geoCoordinates(const int x, const int y, const ViewportParams *viewport, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit) const
155{
156 const int radius = viewport->radius();
157 Q_ASSERT(radius > 0);
158
159 // Calculate translation of center point
160 const qreal centerLon = viewport->centerLongitude();
161 const qreal centerLat = viewport->centerLatitude();
162
163 // Calculate how many pixel are being represented per radians.
164 const float rad2Pixel = (qreal)(2 * radius) / M_PI;
165 const qreal pixel2Rad = M_PI / (2 * radius);
166
167 {
168 const int halfImageWidth = viewport->width() / 2;
169 const int xPixels = x - halfImageWidth;
170 lon = xPixels * pixel2Rad + centerLon;
171
172 while (lon > M_PI)
173 lon -= 2 * M_PI;
174 while (lon < -M_PI)
175 lon += 2 * M_PI;
176
177 if (unit == GeoDataCoordinates::Degree) {
178 lon *= RAD2DEG;
179 }
180 }
181
182 {
183 const int halfImageHeight = viewport->height() / 2;
184 const int yCenterOffset = (int)(asinh(tan(centerLat)) * rad2Pixel);
185 const int yTop = halfImageHeight - 2 * radius + yCenterOffset;
186 const int yBottom = yTop + 4 * radius;
187 if (y >= yTop && y < yBottom) {
188 lat = gd(((halfImageHeight + yCenterOffset) - y) * pixel2Rad);
189
190 if (unit == GeoDataCoordinates::Degree) {
191 lat *= RAD2DEG;
192 }
193
194 return true; // lat successfully calculated
195 }
196 }
197
198 return false; // lat unchanged
199}
200
202{
203 qreal west;
204 qreal north = 85 * DEG2RAD;
205 geoCoordinates(screenRect.left(), screenRect.top(), viewport, west, north, GeoDataCoordinates::Radian);
206
207 qreal east;
208 qreal south = -85 * DEG2RAD;
209 geoCoordinates(screenRect.right(), screenRect.bottom(), viewport, east, south, GeoDataCoordinates::Radian);
210
211 // For the case where the whole viewport gets covered there is a
212 // pretty dirty and generic detection algorithm:
214 latLonAltBox.setNorth(north, GeoDataCoordinates::Radian);
215 latLonAltBox.setSouth(south, GeoDataCoordinates::Radian);
216 latLonAltBox.setWest(west, GeoDataCoordinates::Radian);
217 latLonAltBox.setEast(east, GeoDataCoordinates::Radian);
218 latLonAltBox.setMinAltitude(-100000000.0);
219 latLonAltBox.setMaxAltitude(100000000000000.0);
220
221 // The remaining algorithm should be pretty generic for all kinds of
222 // flat projections:
223
224 // If the whole globe is visible we can easily calculate
225 // analytically the lon-/lat- range.
226 // qreal pitch = GeoDataPoint::normalizeLat( viewport->planetAxis().pitch() );
227
228 int xRepeatDistance = 4 * viewport->radius();
229 if (viewport->width() >= xRepeatDistance) {
230 latLonAltBox.setWest(-M_PI);
231 latLonAltBox.setEast(+M_PI);
232 }
233
234 return latLonAltBox;
235}
236
238{
239 int radius = viewport->radius();
240 int height = viewport->height();
241
242 // Calculate translation of center point
243 const qreal centerLat = viewport->centerLatitude();
244
245 // Calculate how many pixel are being represented per radians.
246 const float rad2Pixel = (float)(2 * radius) / M_PI;
247
248 int yCenterOffset = (int)(asinh(tan(centerLat)) * rad2Pixel);
249 int yTop = height / 2 - 2 * radius + yCenterOffset;
250 int yBottom = yTop + 4 * radius;
251
252 return !(yTop >= 0 || yBottom < height);
253}
This file contains the headers for MercatorProjection.
This file contains the headers for ViewportParams.
qreal minLat() const
Returns the arbitrarily chosen minimum (southern) latitude.
qreal maxLat() const
Returns the arbitrarily chosen maximum (northern) latitude.
A base class for the Equirectangular and Mercator projections in Marble.
A 3d point representation.
Unit
enum used constructor to specify the units used
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 that defines a 3D bounding box for geographic data.
QIcon icon() const override
Returns an icon for the projection.
GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const override
Returns a GeoDataLatLonAltBox bounding box of the given screenrect inside the given viewport.
qreal minValidLat() const override
Returns the minimum (southern) latitude that is mathematically defined and reasonable.
MercatorProjection()
Construct a new MercatorProjection.
bool screenCoordinates(const GeoDataCoordinates &coordinates, const ViewportParams *params, qreal &x, qreal &y, bool &globeHidesPoint) const override
Get the screen coordinates corresponding to geographical coordinates in the map.
QString description() const override
Returns a short user description of the projection that can be used in tooltips or dialogs.
QString name() const override
Returns the user-visible name of the projection.
bool mapCoversViewport(const ViewportParams *viewport) const override
Returns whether the projected data fully obstructs the current viewport.
qreal maxValidLat() const override
Returns the maximum (northern) latitude that is mathematically defined and reasonable.
bool geoCoordinates(const int x, const int y, const ViewportParams *params, qreal &lon, qreal &lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Degree) const override
Get the earth coordinates corresponding to a pixel in the map.
A public class that controls what is visible in the viewport of a Marble map.
Binds a QML item to a specific geodetic location in screen coordinates.
qreal gdInv(qreal x)
This method is a fast Mac Laurin power series approximation of the.
Definition MarbleMath.h:71
QString tr(const char *sourceText, const char *disambiguation, int n)
int bottom() const const
int left() const const
int right() const const
int top() const const
qreal height() const const
qreal 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:03 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.