Marble

ViewportParams.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007 Inge Wallin <[email protected]>
4 // SPDX-FileCopyrightText: 2008 Jens-Michael Hoffmann <[email protected]>
5 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <[email protected]>
6 //
7 
8 
9 #include "ViewportParams.h"
10 
11 #include <QRect>
12 
13 #include <QPainterPath>
14 #include <QRegion>
15 
16 #include "MarbleDebug.h"
17 #include "GeoDataLatLonAltBox.h"
18 #include "SphericalProjection.h"
19 #include "EquirectProjection.h"
20 #include "MercatorProjection.h"
21 #include "GnomonicProjection.h"
22 #include "LambertAzimuthalProjection.h"
23 #include "AzimuthalEquidistantProjection.h"
24 #include "StereographicProjection.h"
25 #include "VerticalPerspectiveProjection.h"
26 
27 
28 namespace Marble
29 {
30 
31 class ViewportParamsPrivate
32 {
33 public:
34  ViewportParamsPrivate( Projection projection,
35  qreal centerLongitude, qreal centerLatitude,
36  int radius,
37  const QSize &size );
38 
39  static const AbstractProjection *abstractProjection( Projection projection );
40 
41  // These two go together. m_currentProjection points to one of
42  // the static Projection classes at the bottom.
43  Projection m_projection;
44  const AbstractProjection *m_currentProjection;
45 
46  // Parameters that determine the painting
47  qreal m_centerLongitude;
48  qreal m_centerLatitude;
49  qreal m_heading;
50  Quaternion m_planetAxis; // Position, coded in a quaternion
51  matrix m_planetAxisMatrix;
52  int m_radius; // Zoom level (pixels / globe radius)
53  qreal m_angularResolution;
54 
55  QSize m_size; // width, height
56 
57 
58  bool m_dirtyBox;
59  GeoDataLatLonAltBox m_viewLatLonAltBox;
60 
61  static const SphericalProjection s_sphericalProjection;
62  static const EquirectProjection s_equirectProjection;
63  static const MercatorProjection s_mercatorProjection;
64  static const GnomonicProjection s_gnomonicProjection;
65  static const StereographicProjection s_stereographicProjection;
66  static const LambertAzimuthalProjection s_lambertAzimuthalProjection;
67  static const AzimuthalEquidistantProjection s_azimuthalEquidistantProjection;
68  static const VerticalPerspectiveProjection s_verticalPerspectiveProjection;
69 
70  GeoDataCoordinates m_focusPoint;
71 };
72 
73 const SphericalProjection ViewportParamsPrivate::s_sphericalProjection;
74 const EquirectProjection ViewportParamsPrivate::s_equirectProjection;
75 const MercatorProjection ViewportParamsPrivate::s_mercatorProjection;
76 const GnomonicProjection ViewportParamsPrivate::s_gnomonicProjection;
77 const StereographicProjection ViewportParamsPrivate::s_stereographicProjection;
78 const LambertAzimuthalProjection ViewportParamsPrivate::s_lambertAzimuthalProjection;
79 const AzimuthalEquidistantProjection ViewportParamsPrivate::s_azimuthalEquidistantProjection;
80 const VerticalPerspectiveProjection ViewportParamsPrivate::s_verticalPerspectiveProjection;
81 
82 ViewportParamsPrivate::ViewportParamsPrivate( Projection projection,
83  qreal centerLongitude, qreal centerLatitude,
84  int radius,
85  const QSize &size )
86  : m_projection( projection ),
87  m_currentProjection( abstractProjection( projection ) ),
88  m_centerLongitude( centerLongitude ),
89  m_centerLatitude( centerLatitude ),
90  m_heading( 0 ),
91  m_planetAxis(),
92  m_planetAxisMatrix(),
93  m_radius( radius ),
94  m_angularResolution(4.0 / abs(m_radius)),
95  m_size( size ),
96  m_dirtyBox( true ),
97  m_viewLatLonAltBox()
98 {
99 }
100 
101 const AbstractProjection *ViewportParamsPrivate::abstractProjection(Projection projection)
102 {
103  switch ( projection ) {
104  case Spherical:
105  return &s_sphericalProjection;
106  case Equirectangular:
107  return &s_equirectProjection;
108  case Mercator:
109  return &s_mercatorProjection;
110  case Gnomonic:
111  return &s_gnomonicProjection;
112  case Stereographic:
113  return &s_stereographicProjection;
114  case LambertAzimuthal:
115  return &s_lambertAzimuthalProjection;
117  return &s_azimuthalEquidistantProjection;
118  case VerticalPerspective:
119  return &s_verticalPerspectiveProjection;
120  }
121 
122  return nullptr;
123 }
124 
125 
126 ViewportParams::ViewportParams()
127  : d( new ViewportParamsPrivate( Spherical, 0, 0, 2000, QSize( 100, 100 ) ) )
128 {
129  centerOn( d->m_centerLongitude, d->m_centerLatitude );
130 }
131 
132 ViewportParams::ViewportParams( Projection projection,
133  qreal centerLongitude, qreal centerLatitude,
134  int radius,
135  const QSize &size )
136  : d( new ViewportParamsPrivate( projection, centerLongitude, centerLatitude, radius, size ) )
137 {
138  centerOn( d->m_centerLongitude, d->m_centerLatitude );
139 }
140 
141 ViewportParams::~ViewportParams()
142 {
143  delete d;
144 }
145 
146 
147 // ================================================================
148 // Getters and setters
149 
150 
151 Projection ViewportParams::projection() const
152 {
153  return d->m_projection;
154 }
155 
156 const AbstractProjection *ViewportParams::currentProjection() const
157 {
158  return d->m_currentProjection;
159 }
160 
161 void ViewportParams::setProjection(Projection newProjection)
162 {
163  d->m_projection = newProjection;
164  d->m_currentProjection = ViewportParamsPrivate::abstractProjection( newProjection );
165 
166  // We now need to reset the planetAxis to make sure
167  // that it's a valid axis orientation!
168  // So this line is important (although it might look odd) ! :
169  centerOn( d->m_centerLongitude, d->m_centerLatitude );
170 }
171 
172 int ViewportParams::polarity() const
173 {
174  // For mercator this just gives the extreme latitudes
175  // instead of the actual poles but it works fine as well:
176  GeoDataCoordinates northPole( 0.0, +currentProjection()->maxLat() );
177  GeoDataCoordinates southPole( 0.0, -currentProjection()->maxLat() );
178 
179  bool globeHidesN, globeHidesS;
180  qreal x;
181  qreal yN, yS;
182 
183  currentProjection()->screenCoordinates( northPole, this,
184  x, yN, globeHidesN );
185  currentProjection()->screenCoordinates( southPole, this,
186  x, yS, globeHidesS );
187 
188  int polarity = 0;
189 
190  // case of the flat map:
191  if ( !globeHidesN && !globeHidesS ) {
192  if ( yN < yS ) {
193  polarity = +1;
194  }
195  if ( yS < yN ) {
196  polarity = -1;
197  }
198  }
199  else {
200  if ( !globeHidesN && yN < height() / 2 ) {
201  polarity = +1;
202  }
203  if ( !globeHidesN && yN > height() / 2 ) {
204  polarity = -1;
205  }
206  if ( !globeHidesS && yS > height() / 2 ) {
207  polarity = +1;
208  }
209  if ( !globeHidesS && yS < height() / 2 ) {
210  polarity = -1;
211  }
212  }
213 
214  return polarity;
215 }
216 
217 int ViewportParams::radius() const
218 {
219  return d->m_radius;
220 }
221 
222 void ViewportParams::setRadius(int newRadius)
223 {
224  if ( newRadius > 0 ) {
225  d->m_dirtyBox = true;
226 
227  d->m_radius = newRadius;
228  d->m_angularResolution = 4.0 / d->m_radius;
229  }
230 }
231 
232 void ViewportParams::centerOn( qreal lon, qreal lat )
233 {
234  if ( !d->m_currentProjection->traversablePoles() ) {
235  if ( lat > d->m_currentProjection->maxLat() )
236  lat = d->m_currentProjection->maxLat();
237 
238  if ( lat < d->m_currentProjection->minLat() )
239  lat = d->m_currentProjection->minLat();
240  } else {
241  while ( lat > M_PI )
242  lat -= 2 * M_PI;
243  while ( lat < -M_PI )
244  lat += 2 * M_PI;
245  }
246 
247  while ( lon > M_PI )
248  lon -= 2 * M_PI;
249  while ( lon < -M_PI )
250  lon += 2 * M_PI;
251 
252  d->m_centerLongitude = lon;
253  d->m_centerLatitude = lat;
254 
255  const Quaternion roll = Quaternion::fromEuler( 0, 0, d->m_heading );
256  const Quaternion quat = Quaternion::fromEuler( -lat, lon, 0.0 );
257 
258  d->m_planetAxis = quat * roll;
259  d->m_planetAxis.normalize();
260 
261  d->m_dirtyBox = true;
262  d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix );
263  d->m_planetAxis.normalize();
264 }
265 
266 void ViewportParams::setHeading( qreal heading )
267 {
268  d->m_heading = heading;
269 
270  const Quaternion roll = Quaternion::fromEuler( 0, 0, heading );
271 
272  const qreal centerLat = centerLatitude();
273  const qreal centerLon = centerLongitude();
274 
275  const Quaternion quat = Quaternion::fromEuler( -centerLat, centerLon, 0 );
276 
277  d->m_planetAxis = quat * roll;
278  d->m_planetAxis.normalize();
279 
280  d->m_dirtyBox = true;
281  d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix );
282  d->m_planetAxis.normalize();
283 }
284 
285 qreal ViewportParams::heading() const
286 {
287  return d->m_heading;
288 }
289 
290 Quaternion ViewportParams::planetAxis() const
291 {
292  return d->m_planetAxis;
293 }
294 
295 const matrix &ViewportParams::planetAxisMatrix() const
296 {
297  return d->m_planetAxisMatrix;
298 }
299 
300 int ViewportParams::width() const
301 {
302  return d->m_size.width();
303 }
304 
305 int ViewportParams::height() const
306 {
307  return d->m_size.height();
308 }
309 
310 QSize ViewportParams::size() const
311 {
312  return d->m_size;
313 }
314 
315 
316 void ViewportParams::setWidth(int newWidth)
317 {
318  setSize( QSize( newWidth, height() ) );
319 }
320 
321 void ViewportParams::setHeight(int newHeight)
322 {
323  setSize( QSize( width(), newHeight ) );
324 }
325 
326 void ViewportParams::setSize(const QSize& newSize)
327 {
328  if ( newSize == d->m_size )
329  return;
330 
331  d->m_dirtyBox = true;
332 
333  d->m_size = newSize;
334 }
335 
336 // ================================================================
337 // Other functions
338 
339 qreal ViewportParams::centerLongitude() const
340 {
341  return d->m_centerLongitude;
342 }
343 
344 qreal ViewportParams::centerLatitude() const
345 {
346  return d->m_centerLatitude;
347 }
348 
349 const GeoDataLatLonAltBox& ViewportParams::viewLatLonAltBox() const
350 {
351  if (d->m_dirtyBox) {
352  d->m_viewLatLonAltBox = d->m_currentProjection->latLonAltBox( QRect( QPoint( 0, 0 ),
353  d->m_size ),
354  this );
355  d->m_dirtyBox = false;
356  }
357 
358  return d->m_viewLatLonAltBox;
359 }
360 
361 GeoDataLatLonAltBox ViewportParams::latLonAltBox( const QRect &screenRect ) const
362 {
363  return d->m_currentProjection->latLonAltBox( screenRect, this );
364 }
365 
366 qreal ViewportParams::angularResolution() const
367 {
368  // We essentially divide the diameter by 180 deg and
369  // take half of the result as a guess for the angle per pixel resolution.
370  // d->m_angularResolution = 0.25 * M_PI / fabs( (qreal)(d->m_radius);
371  return d->m_angularResolution;
372 }
373 
374 bool ViewportParams::resolves ( const GeoDataLatLonBox &latLonBox, qreal pixel ) const
375 {
376  return latLonBox.width() + latLonBox.height() > pixel * d->m_angularResolution;
377 }
378 
379 
380 bool ViewportParams::resolves ( const GeoDataLatLonAltBox &latLonAltBox, qreal pixel, qreal altitude ) const
381 {
382  return latLonAltBox.width() + latLonAltBox.height() > pixel * d->m_angularResolution
383  || latLonAltBox.maxAltitude() - latLonAltBox.minAltitude() > altitude;
384 }
385 
386 bool ViewportParams::resolves ( const GeoDataCoordinates &coord1,
387  const GeoDataCoordinates &coord2 ) const
388 {
389  qreal lon1, lat1;
390  coord1.geoCoordinates( lon1, lat1 );
391 
392  qreal lon2, lat2;
393  coord2.geoCoordinates( lon2, lat2 );
394 
395  // We take the manhattan length as an approximation for the distance
396  return ( fabs( lon2 - lon1 ) + fabs( lat2 - lat1 ) > d->m_angularResolution );
397 }
398 
399 
400 bool ViewportParams::screenCoordinates( const qreal lon, const qreal lat,
401  qreal &x, qreal &y ) const
402 {
403  return d->m_currentProjection->screenCoordinates( lon, lat, this, x, y );
404 }
405 
407  qreal &x, qreal &y,
408  bool &globeHidesPoint ) const
409 {
410  return d->m_currentProjection->screenCoordinates( geopoint, this, x, y, globeHidesPoint );
411 }
412 
414  qreal &x, qreal &y ) const
415 {
416  return d->m_currentProjection->screenCoordinates( geopoint, this, x, y );
417 }
418 
420  qreal *x, qreal &y, int &pointRepeatNum,
421  const QSizeF& size,
422  bool &globeHidesPoint ) const
423 {
424  return d->m_currentProjection->screenCoordinates( coordinates, this, x, y, pointRepeatNum, size, globeHidesPoint );
425 }
426 
427 
429  QVector<QPolygonF*> &polygons ) const
430 {
431  return d->m_currentProjection->screenCoordinates( lineString, this, polygons );
432 }
433 
434 bool ViewportParams::geoCoordinates( const int x, const int y,
435  qreal &lon, qreal &lat,
436  GeoDataCoordinates::Unit unit ) const
437 {
438  return d->m_currentProjection->geoCoordinates( x, y, this, lon, lat, unit );
439 }
440 
441 bool ViewportParams::mapCoversViewport() const
442 {
443  return d->m_currentProjection->mapCoversViewport( this );
444 }
445 
446 QPainterPath ViewportParams::mapShape() const
447 {
448  return d->m_currentProjection->mapShape( this );
449 }
450 
451 QRegion ViewportParams::mapRegion() const
452 {
453  return d->m_currentProjection->mapRegion( this );
454 }
455 
457 {
458  if (d->m_focusPoint.isValid()) {
459  return d->m_focusPoint;
460  }
461  else {
462  const qreal lon = d->m_centerLongitude;
463  const qreal lat = d->m_centerLatitude;
464 
465  return GeoDataCoordinates(lon, lat, 0.0, GeoDataCoordinates::Radian);
466  }
467 
468 }
469 
471 {
472  d->m_focusPoint = focusPoint;
473 }
474 
476 {
477  d->m_focusPoint = GeoDataCoordinates();
478 }
479 
480 }
A 3d point representation.
@ VerticalPerspective
Vertical perspective projection.
Definition: MarbleGlobal.h:49
void resetFocusPoint()
Invalidate any focus point set with setFocusPoint.
@ AzimuthalEquidistant
Azimuthal Equidistant projection.
Definition: MarbleGlobal.h:48
QAction * roll(const QObject *recvr, const char *slot, QObject *parent)
@ Stereographic
Stereographic projection.
Definition: MarbleGlobal.h:46
A LineString that allows to store a contiguous set of line segments.
@ Spherical
Spherical projection ("Orthographic")
Definition: MarbleGlobal.h:42
@ LambertAzimuthal
Lambert Azimuthal Equal-Area projection.
Definition: MarbleGlobal.h:47
Binds a QML item to a specific geodetic location in screen coordinates.
Projection
This enum is used to choose the projection shown in the view.
Definition: MarbleGlobal.h:41
bool screenCoordinates(const qreal lon, const qreal lat, const ViewportParams *viewport, qreal &x, qreal &y) const
Get the screen coordinates corresponding to geographical coordinates in the map.
Unit
enum used constructor to specify the units used
GeoDataCoordinates focusPoint() const
void setRadius(int radius)
Change the radius of the planet.
void setFocusPoint(const GeoDataCoordinates &focusPoint)
Change the point of focus, overridding any previously set focus point.
@ Mercator
Mercator projection.
Definition: MarbleGlobal.h:44
bool geoCoordinates(const int x, const int y, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Degree) const
Get the earth coordinates corresponding to a pixel in the map.
@ Gnomonic
Gnomonic projection.
Definition: MarbleGlobal.h:45
bool screenCoordinates(const qreal lon, const qreal lat, qreal &x, qreal &y) const
Get the screen coordinates corresponding to geographical coordinates in the map.
@ Equirectangular
Flat projection ("plate carree")
Definition: MarbleGlobal.h:43
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu May 26 2022 04:07:51 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.