Marble

MercatorProjection.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007-2008 Inge Wallin <[email protected]>
4 // SPDX-FileCopyrightText: 2007-2012 Torsten Rahn <[email protected]>
5 //
6 
7 
8 // Local
9 #include "MercatorProjection.h"
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 
23 using namespace Marble;
24 
27  m_lastCenterLat(200.0),
28  m_lastCenterLatInv(0.0)
29 {
30  setMinLat( minValidLat() );
31  setMaxLat( maxValidLat() );
32 }
33 
34 MercatorProjection::~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 
163 bool 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 }
A 3d point representation.
qreal gdInv(qreal x)
This method is a fast Mac Laurin power series approximation of the.
Definition: MarbleMath.h:73
QString name() const override
Returns the user-visible name of the projection.
qreal height() const const
A class that defines a 3D bounding box for geographic data.
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 ...
int right() const const
int left() const const
QString description() const override
Returns a short user description of the projection that can be used in tooltips or dialogs.
int bottom() const const
int top() const const
A public class that controls what is visible in the viewport of a Marble map.
qreal minValidLat() const override
Returns the minimum (southern) latitude that is mathematically defined and reasonable.
A base class for the Equirectangular and Mercator projections in Marble.
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.
qreal minLat() const
Returns the arbitrarily chosen minimum (southern) latitude.
Binds a QML item to a specific geodetic location in screen coordinates.
Unit
enum used constructor to specify the units used
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.
MercatorProjection()
Construct a new MercatorProjection.
GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const override
Returns a GeoDataLatLonAltBox bounding box of the given screenrect inside the given viewport.
bool mapCoversViewport(const ViewportParams *viewport) const override
Returns whether the projected data fully obstructs the current viewport.
QIcon icon() const override
Returns an icon for the projection.
qreal maxLat() const
Returns the arbitrarily chosen maximum (northern) latitude.
QString tr(const char *sourceText, const char *disambiguation, int n)
qreal maxValidLat() const override
Returns the maximum (northern) latitude that is mathematically defined and reasonable.
qreal width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Dec 11 2023 04:09:40 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.