1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
4 // SPDX-FileCopyrightText: 2007-2012 Torsten Rahn <rahn@kde.org>
5 // SPDX-FileCopyrightText: 2012 Cezar Mocan <mocancezar@gmail.com>
6 //
7
8 // Local
9 #include "AbstractProjection.h"
10
11 #include "AbstractProjection_p.h"
12
13 #include "MarbleDebug.h"
14 #include <QRegion>
15 #include <QPainterPath>
16
17 // Marble
18 #include "GeoDataLineString.h"
19 #include "GeoDataLinearRing.h"
20 #include "GeoDataLatLonAltBox.h"
21 #include "ViewportParams.h"
22
23 using namespace Marble;
24
AbstractProjection()25 AbstractProjection::AbstractProjection()
26 : d_ptr( new AbstractProjectionPrivate( this ) )
27 {
28 }
29
AbstractProjection(AbstractProjectionPrivate * dd)30 AbstractProjection::AbstractProjection( AbstractProjectionPrivate* dd )
31 : d_ptr( dd )
32 {
33 }
34
~AbstractProjection()35 AbstractProjection::~AbstractProjection()
36 {
37 }
38
AbstractProjectionPrivate(AbstractProjection * parent)39 AbstractProjectionPrivate::AbstractProjectionPrivate( AbstractProjection * parent )
40 : m_maxLat(0),
41 m_minLat(0),
42 m_previousResolution(-1),
43 m_level(-1),
44 q_ptr( parent)
45 {
46 }
47
levelForResolution(qreal resolution) const48 int AbstractProjectionPrivate::levelForResolution(qreal resolution) const {
49 if (m_previousResolution == resolution) return m_level;
50
51 m_previousResolution = resolution;
52
53 if (resolution < 0.0000005) m_level = 17;
54 else if (resolution < 0.0000010) m_level = 16;
55 else if (resolution < 0.0000020) m_level = 15;
56 else if (resolution < 0.0000040) m_level = 14;
57 else if (resolution < 0.0000080) m_level = 13;
58 else if (resolution < 0.0000160) m_level = 12;
59 else if (resolution < 0.0000320) m_level = 11;
60 else if (resolution < 0.0000640) m_level = 10;
61 else if (resolution < 0.0001280) m_level = 9;
62 else if (resolution < 0.0002560) m_level = 8;
63 else if (resolution < 0.0005120) m_level = 7;
64 else if (resolution < 0.0010240) m_level = 6;
65 else if (resolution < 0.0020480) m_level = 5;
66 else if (resolution < 0.0040960) m_level = 4;
67 else if (resolution < 0.0081920) m_level = 3;
68 else if (resolution < 0.0163840) m_level = 2;
69 else m_level = 1;
70
71 return m_level;
72 }
73
maxValidLat() const74 qreal AbstractProjection::maxValidLat() const
75 {
76 return +90.0 * DEG2RAD;
77 }
78
maxLat() const79 qreal AbstractProjection::maxLat() const
80 {
81 Q_D(const AbstractProjection );
82 return d->m_maxLat;
83 }
84
setMaxLat(qreal maxLat)85 void AbstractProjection::setMaxLat( qreal maxLat )
86 {
87 if ( maxLat < maxValidLat() ) {
88 mDebug() << Q_FUNC_INFO << "Trying to set maxLat to a value that is out of the valid range.";
89 return;
90 }
91
92 Q_D( AbstractProjection );
93 d->m_maxLat = maxLat;
94 }
95
minValidLat() const96 qreal AbstractProjection::minValidLat() const
97 {
98 return -90.0 * DEG2RAD;
99 }
100
minLat() const101 qreal AbstractProjection::minLat() const
102 {
103 Q_D( const AbstractProjection );
104 return d->m_minLat;
105 }
106
setMinLat(qreal minLat)107 void AbstractProjection::setMinLat( qreal minLat )
108 {
109 if ( minLat < minValidLat() ) {
110 mDebug() << Q_FUNC_INFO << "Trying to set minLat to a value that is out of the valid range.";
111 return;
112 }
113
114 Q_D( AbstractProjection );
115 d->m_minLat = minLat;
116 }
117
repeatableX() const118 bool AbstractProjection::repeatableX() const
119 {
120 return false;
121 }
122
traversablePoles() const123 bool AbstractProjection::traversablePoles() const
124 {
125 return false;
126 }
127
traversableDateLine() const128 bool AbstractProjection::traversableDateLine() const
129 {
130 return false;
131 }
132
preservationType() const133 AbstractProjection::PreservationType AbstractProjection::preservationType() const
134 {
135 return NoPreservation;
136 }
137
isOrientedNormal() const138 bool AbstractProjection::isOrientedNormal() const
139 {
140 return true;
141 }
142
isClippedToSphere() const143 bool AbstractProjection::isClippedToSphere() const
144 {
145 return false;
146 }
147
clippingRadius() const148 qreal AbstractProjection::clippingRadius() const
149 {
150 return 0;
151 }
152
153
screenCoordinates(const qreal lon,const qreal lat,const ViewportParams * viewport,qreal & x,qreal & y) const154 bool AbstractProjection::screenCoordinates( const qreal lon, const qreal lat,
155 const ViewportParams *viewport,
156 qreal &x, qreal &y ) const
157 {
158 bool globeHidesPoint;
159 GeoDataCoordinates geopoint(lon, lat);
160 return screenCoordinates( geopoint, viewport, x, y, globeHidesPoint );
161 }
162
screenCoordinates(const GeoDataCoordinates & geopoint,const ViewportParams * viewport,qreal & x,qreal & y) const163 bool AbstractProjection::screenCoordinates( const GeoDataCoordinates &geopoint,
164 const ViewportParams *viewport,
165 qreal &x, qreal &y ) const
166 {
167 bool globeHidesPoint;
168
169 return screenCoordinates( geopoint, viewport, x, y, globeHidesPoint );
170 }
171
latLonAltBox(const QRect & screenRect,const ViewportParams * viewport) const172 GeoDataLatLonAltBox AbstractProjection::latLonAltBox( const QRect& screenRect,
173 const ViewportParams *viewport ) const
174 {
175 // For the case where the whole viewport gets covered there is a
176 // pretty dirty and generic detection algorithm:
177
178 // Move along the screenborder and save the highest and lowest lon-lat values.
179 QRect projectedRect = mapRegion( viewport ).boundingRect();
180 QRect mapRect = screenRect.intersected( projectedRect );
181
182 GeoDataLineString boundingLineString;
183
184 qreal lon, lat;
185
186 for ( int x = mapRect.left(); x < mapRect.right(); x += latLonAltBoxSamplingRate ) {
187 if ( geoCoordinates( x, mapRect.bottom(), viewport, lon, lat,
188 GeoDataCoordinates::Radian ) ) {
189 boundingLineString << GeoDataCoordinates( lon, lat );
190 }
191
192 if ( geoCoordinates( x, mapRect.top(),
193 viewport, lon, lat, GeoDataCoordinates::Radian ) ) {
194 boundingLineString << GeoDataCoordinates( lon, lat );
195 }
196 }
197
198 if ( geoCoordinates( mapRect.right(), mapRect.top(), viewport, lon, lat,
199 GeoDataCoordinates::Radian ) ) {
200 boundingLineString << GeoDataCoordinates( lon, lat );
201 }
202
203 if ( geoCoordinates( mapRect.right(), mapRect.bottom(),
204 viewport, lon, lat, GeoDataCoordinates::Radian ) ) {
205 boundingLineString << GeoDataCoordinates( lon, lat );
206 }
207
208 for ( int y = mapRect.bottom(); y < mapRect.top(); y += latLonAltBoxSamplingRate ) {
209 if ( geoCoordinates( mapRect.left(), y, viewport, lon, lat,
210 GeoDataCoordinates::Radian ) ) {
211 boundingLineString << GeoDataCoordinates( lon, lat );
212 }
213
214 if ( geoCoordinates( mapRect.right(), y,
215 viewport, lon, lat, GeoDataCoordinates::Radian ) ) {
216 boundingLineString << GeoDataCoordinates( lon, lat );
217 }
218 }
219
220 GeoDataLatLonAltBox latLonAltBox = boundingLineString.latLonAltBox();
221
222 // Now we need to check whether maxLat (e.g. the north pole) gets displayed
223 // inside the viewport.
224
225 // We need a point on the screen at maxLat that definitely gets displayed:
226
227 // FIXME: Some of the following code can be safely removed as soon as we properly handle
228 // GeoDataLinearRing::latLonAltBox().
229 qreal averageLongitude = ( latLonAltBox.west() + latLonAltBox.east() ) / 2.0;
230
231 GeoDataCoordinates maxLatPoint( averageLongitude, maxLat(), 0.0, GeoDataCoordinates::Radian );
232 GeoDataCoordinates minLatPoint( averageLongitude, minLat(), 0.0, GeoDataCoordinates::Radian );
233
234 qreal dummyX, dummyY; // not needed
235
236 if ( latLonAltBox.north() > maxLat() ||
237 screenCoordinates( maxLatPoint, viewport, dummyX, dummyY ) ) {
238 latLonAltBox.setNorth( maxLat() );
239 }
240 if ( latLonAltBox.north() < minLat() ||
241 screenCoordinates( minLatPoint, viewport, dummyX, dummyY ) ) {
242 latLonAltBox.setSouth( minLat() );
243 }
244
245 latLonAltBox.setMinAltitude( -100000000.0 );
246 latLonAltBox.setMaxAltitude( 100000000000000.0 );
247
248 return latLonAltBox;
249 }
250
251
mapRegion(const ViewportParams * viewport) const252 QRegion AbstractProjection::mapRegion( const ViewportParams *viewport ) const
253 {
254 return QRegion( mapShape( viewport ).toFillPolygon().toPolygon() );
255 }
256