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
8// Local
10
11#include "MarbleDebug.h"
12
13// Marble
14#include "ViewportParams.h"
15#include "GeoDataLatLonAltBox.h"
16
17#include "MathHelper.h"
18#include "GeoDataPoint.h"
19#include "MarbleMath.h"
20
21#include <QIcon>
22
23using namespace Marble;
24
27 m_lastCenterLat(200.0),
28 m_lastCenterLatInv(0.0)
29{
30 setMinLat( minValidLat() );
31 setMaxLat( maxValidLat() );
32}
33
34MercatorProjection::~MercatorProjection()
35{
36}
37
39{
40 return QObject::tr( "Mercator" );
41}
42
44{
45 return QObject::tr( "<p><b>Mercator Projection</b></p><p>Applications: popular standard map projection for navigation.</p>" );
46}
47
49{
50 return QIcon(QStringLiteral(":/icons/map-mercator.png"));
51}
52
54{
55 // This is the max value where gd( lat ) is defined.
56 return +85.05113 * DEG2RAD;
57}
58
60{
61 // This is the min value where gd( lat ) is defined.
62 return -85.05113 * DEG2RAD;
63}
64
66 const ViewportParams *viewport,
67 qreal &x, qreal &y, bool &globeHidesPoint ) const
68{
69 globeHidesPoint = false;
70 qreal lon;
71 qreal originalLat;
72
73 geopoint.geoCoordinates( lon, originalLat );
74 qreal const lat = qBound(minLat(), originalLat, maxLat());
75 const bool isLatValid = lat == originalLat;
76
77 // Convenience variables
78 int radius = viewport->radius();
79 qreal width = (qreal)(viewport->width());
80 qreal height = (qreal)(viewport->height());
81
82 qreal rad2Pixel = 2 * radius / M_PI;
83
84 const qreal centerLon = viewport->centerLongitude();
85 const qreal centerLat = viewport->centerLatitude();
86 if (centerLat != m_lastCenterLat) {
87 m_lastCenterLatInv = gdInv(centerLat);
88 m_lastCenterLat = centerLat;
89 }
90
91 // Let (x, y) be the position on the screen of the placemark..
92 x = ( width / 2 + rad2Pixel * ( lon - centerLon ) );
93 y = ( height / 2 - rad2Pixel * ( gdInv( lat ) - m_lastCenterLatInv ) );
94
95 // Return true if the calculated point is inside the screen area,
96 // otherwise return false.
97 return isLatValid && ( ( 0 <= y && y < height )
98 && ( ( 0 <= x && x < width )
99 || ( 0 <= x - 4 * radius && x - 4 * radius < width )
100 || ( 0 <= x + 4 * radius && x + 4 * radius < width ) ) );
101}
102
104 const ViewportParams *viewport,
105 qreal *x, qreal &y, int &pointRepeatNum,
106 const QSizeF& size,
107 bool &globeHidesPoint ) const
108{
109 pointRepeatNum = 0;
110 // On flat projections the observer's view onto the point won't be
111 // obscured by the target planet itself.
112 globeHidesPoint = false;
113
114 // Convenience variables
115 int radius = viewport->radius();
116 qreal width = (qreal)(viewport->width());
117 qreal height = (qreal)(viewport->height());
118
119 // Let (itX, y) be the first guess for one possible position on screen..
120 qreal itX;
121 bool visible = screenCoordinates( coordinates, viewport, itX, y);
122
123 // Make sure that the requested point is within the visible y range:
124 if ( 0 <= y + size.height() / 2.0 && y < height + size.height() / 2.0 ) {
125 // For the repetition case the same geopoint gets displayed on
126 // the map many times.across the longitude.
127
128 int xRepeatDistance = 4 * radius;
129
130 // Finding the leftmost positive x value
131 if ( itX + size.width() / 2.0 >= xRepeatDistance ) {
132 const int repeatNum = (int)( ( itX + size.width() / 2.0 ) / xRepeatDistance );
133 itX = itX - repeatNum * xRepeatDistance;
134 }
135 if ( itX + size.width() / 2.0 < 0 ) {
136 itX += xRepeatDistance;
137 }
138 // the requested point is out of the visible x range:
139 if ( itX > width + size.width() / 2.0 ) {
140 return false;
141 }
142
143 // Now iterate through all visible x screen coordinates for the point
144 // from left to right.
145 int itNum = 0;
146 while ( itX - size.width() / 2.0 < width ) {
147 *x = itX;
148 ++x;
149 ++itNum;
150 itX += xRepeatDistance;
151 }
152
153 pointRepeatNum = itNum;
154
155 return visible;
156 }
157
158 // the requested point is out of the visible y range:
159 return false;
160}
161
162
163bool MercatorProjection::geoCoordinates( const int x, const int y,
164 const ViewportParams *viewport,
165 qreal& lon, qreal& lat,
166 GeoDataCoordinates::Unit unit ) const
167{
168 const int radius = viewport->radius();
169 Q_ASSERT( radius > 0 );
170
171 // Calculate translation of center point
172 const qreal centerLon = viewport->centerLongitude();
173 const qreal centerLat = viewport->centerLatitude();
174
175 // Calculate how many pixel are being represented per radians.
176 const float rad2Pixel = (qreal)( 2 * radius )/M_PI;
177 const qreal pixel2Rad = M_PI / (2 * radius);
178
179 {
180 const int halfImageWidth = viewport->width() / 2;
181 const int xPixels = x - halfImageWidth;
182 lon = xPixels * pixel2Rad + centerLon;
183
184 while ( lon > M_PI ) lon -= 2*M_PI;
185 while ( lon < -M_PI ) lon += 2*M_PI;
186
187 if ( unit == GeoDataCoordinates::Degree ) {
188 lon *= RAD2DEG;
189 }
190 }
191
192 {
193 const int halfImageHeight = viewport->height() / 2;
194 const int yCenterOffset = (int)( asinh( tan( centerLat ) ) * rad2Pixel );
195 const int yTop = halfImageHeight - 2 * radius + yCenterOffset;
196 const int yBottom = yTop + 4 * radius;
197 if ( y >= yTop && y < yBottom ) {
198 lat = gd( ( ( halfImageHeight + yCenterOffset ) - y)
199 * pixel2Rad );
200
201 if ( unit == GeoDataCoordinates::Degree ) {
202 lat *= RAD2DEG;
203 }
204
205 return true; // lat successfully calculated
206 }
207 }
208
209 return false; // lat unchanged
210}
211
212
214 const ViewportParams *viewport ) const
215{
216 qreal west;
217 qreal north = 85*DEG2RAD;
218 geoCoordinates( screenRect.left(), screenRect.top(), viewport, west, north, GeoDataCoordinates::Radian );
219
220 qreal east;
221 qreal south = -85*DEG2RAD;
222 geoCoordinates( screenRect.right(), screenRect.bottom(), viewport, east, south, GeoDataCoordinates::Radian );
223
224 // For the case where the whole viewport gets covered there is a
225 // pretty dirty and generic detection algorithm:
227 latLonAltBox.setNorth( north, GeoDataCoordinates::Radian );
228 latLonAltBox.setSouth( south, GeoDataCoordinates::Radian );
229 latLonAltBox.setWest( west, GeoDataCoordinates::Radian );
230 latLonAltBox.setEast( east, GeoDataCoordinates::Radian );
231 latLonAltBox.setMinAltitude( -100000000.0 );
232 latLonAltBox.setMaxAltitude( 100000000000000.0 );
233
234 // The remaining algorithm should be pretty generic for all kinds of
235 // flat projections:
236
237 // If the whole globe is visible we can easily calculate
238 // analytically the lon-/lat- range.
239 // qreal pitch = GeoDataPoint::normalizeLat( viewport->planetAxis().pitch() );
240
241 int xRepeatDistance = 4 * viewport->radius();
242 if ( viewport->width() >= xRepeatDistance ) {
243 latLonAltBox.setWest( -M_PI );
244 latLonAltBox.setEast( +M_PI );
245 }
246
247 return latLonAltBox;
248}
249
250
252{
253 int radius = viewport->radius();
254 int height = viewport->height();
255
256 // Calculate translation of center point
257 const qreal centerLat = viewport->centerLatitude();
258
259 // Calculate how many pixel are being represented per radians.
260 const float rad2Pixel = (float)( 2 * radius )/M_PI;
261
262 int yCenterOffset = (int)( asinh( tan( centerLat ) ) * rad2Pixel );
263 int yTop = height / 2 - 2 * radius + yCenterOffset;
264 int yBottom = yTop + 4 * radius;
265
266 return !(yTop >= 0 || yBottom < height);
267}
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:73
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 Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.