Marble

MarbleAbstractPresenter.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <[email protected]>
4 // SPDX-FileCopyrightText: 2007 Inge Wallin <[email protected]>
5 // SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <[email protected]>
6 // SPDX-FileCopyrightText: 2012 Mohammed Nafees <[email protected]>
7 // SPDX-FileCopyrightText: 2014 Adam Dabrowski <[email protected]>
8 //
9 
10 #include <MarbleAbstractPresenter.h>
11 #include <QtMath>
12 #include <Quaternion.h>
13 #include <ViewportParams.h>
14 #include <MarbleLocale.h>
15 #include "MarbleMap.h"
16 #include "MarbleModel.h"
17 #include <Planet.h>
18 #include "GeoDataGeometry.h"
19 #include "GeoDataLatLonAltBox.h"
20 #include <GeoDataPlacemark.h>
21 #include <GeoDataLookAt.h>
22 #include <MarbleClock.h>
23 #include <MarbleDebug.h>
24 
25 namespace Marble
26 {
27  MarbleAbstractPresenter::MarbleAbstractPresenter(MarbleMap *map, QObject *parent) :
28  QObject(parent)
29  ,m_map(map)
30  ,m_physics(this)
31  ,m_animationsEnabled(false)
32  ,m_logzoom(0)
33  ,m_zoomStep(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 60 : 40)
34  ,m_viewAngle(110)
35  {
36  }
37 
38  MarbleAbstractPresenter::~MarbleAbstractPresenter()
39  {
40  }
41 
42  qreal MarbleAbstractPresenter::zoom(qreal radius) const
43  {
44  return (200.0 * log(radius));
45  }
46 
47  qreal MarbleAbstractPresenter::radius(qreal zoom) const
48  {
49  return pow(M_E, (zoom / 200.0));
50  }
51 
52  void MarbleAbstractPresenter::rotateBy(const qreal deltaLon, const qreal deltaLat, FlyToMode mode)
53  {
54 
55  GeoDataLookAt target = lookAt();
56  GeoDataCoordinates coords(map()->viewport()->centerLongitude(), map()->viewport()->centerLatitude());
57  GeoDataCoordinates movedCoords = coords.moveByBearing(-map()->heading() * DEG2RAD, -deltaLat * DEG2RAD);
58  movedCoords = movedCoords.moveByBearing((-map()->heading() - 90) * DEG2RAD, -deltaLon * DEG2RAD * map()->viewport()->polarity());
59 
60  target.setLongitude(movedCoords.longitude());
61  target.setLatitude(movedCoords.latitude());
62 
63  flyTo(target, mode);
64  }
65 
66  void MarbleAbstractPresenter::flyTo(const GeoDataLookAt &newLookAt, FlyToMode mode)
67  {
68  if (!m_animationsEnabled || mode == Instant)
69  {
70  const int radius = qRound(radiusFromDistance(newLookAt.range() * METER2KM));
71  qreal const zoomVal = zoom(radius);
72 
73  // Prevent exceeding zoom range. Note: Bounding to range is not useful here
74  if (qRound(zoomVal) >= minimumZoom() && qRound(zoomVal) <= maximumZoom())
75  {
76  map()->setRadius(radius);
77  m_logzoom = qRound(zoom(radius));
78 
79  GeoDataCoordinates::Unit deg = GeoDataCoordinates::Degree;
80  map()->centerOn(newLookAt.longitude(deg), newLookAt.latitude(deg));
81 
82  emit zoomChanged(m_logzoom);
83  emit distanceChanged(distanceString());
84  }
85  }
86  else
87  {
88  m_physics.flyTo(newLookAt, mode);
89  }
90  }
91 
92  QString MarbleAbstractPresenter::distanceString() const
93  {
94  // distance() returns data in km, so translating to meters
95  qreal dist = distance() * KM2METER, convertedDistance;
96 
97  MarbleLocale::MeasureUnit unit;
98  MarbleLocale *locale = MarbleGlobal::getInstance()->locale();
99  locale->meterToTargetUnit(dist, locale->measurementSystem(),
100  convertedDistance, unit);
101  QString unitString = locale->unitAbbreviation(unit);
102 
103  return QString("%L1 %2").arg(convertedDistance, 8, 'f', 1, QLatin1Char(' '))
104  .arg(unitString);
105  }
106 
107  GeoDataLookAt MarbleAbstractPresenter::lookAt() const
108  {
109  GeoDataLookAt result;
110 
111  result.setLongitude(map()->viewport()->centerLongitude());
112  result.setLatitude(map()->viewport()->centerLatitude());
113  result.setAltitude(0.0);
114  result.setRange(distance() * KM2METER);
115 
116  return result;
117  }
118 
119  qreal MarbleAbstractPresenter::distance() const
120  {
121  return distanceFromRadius(radius());
122  }
123 
124  qreal MarbleAbstractPresenter::distanceFromRadius(qreal radius) const
125  {
126  // Due to Marble's orthographic projection ("we have no focus")
127  // it's actually not possible to calculate a "real" distance.
128  // Additionally the viewing angle of the earth doesn't adjust to
129  // the window's size.
130  //
131  // So the only possible workaround is to come up with a distance
132  // definition which gives a reasonable approximation of
133  // reality. Therefore we assume that the average window width
134  // (about 800 pixels) equals the viewing angle of a human being.
135 
136  return (model()->planet()->radius() * 0.4
137  / radius / tan(0.5 * m_viewAngle * DEG2RAD));
138  }
139 
140  qreal MarbleAbstractPresenter::radiusFromDistance(qreal distance) const
141  {
142  return model()->planet()->radius() /
143  (distance * tan(0.5 * m_viewAngle * DEG2RAD) / 0.4 );
144  }
145 
146  int MarbleAbstractPresenter::polarity() const
147  {
148  return map()->viewport()->polarity();
149  }
150 
151  int MarbleAbstractPresenter::zoom() const
152  {
153  return m_logzoom;
154  }
155 
156  int MarbleAbstractPresenter::minimumZoom() const
157  {
158  return map()->minimumZoom();
159  }
160 
161  int MarbleAbstractPresenter::maximumZoom() const
162  {
163  return map()->maximumZoom();
164  }
165 
166  void MarbleAbstractPresenter::setZoom(int newZoom, FlyToMode mode)
167  {
168  // It won't fly anyway. So we should do everything to keep the zoom value.
169  if (!m_animationsEnabled || mode == Instant)
170  {
171  // Check for under and overflow.
172  if (newZoom < minimumZoom())
173  newZoom = minimumZoom();
174  else if (newZoom > maximumZoom())
175  newZoom = maximumZoom();
176 
177  // Prevent infinite loops.
178  if (newZoom == m_logzoom)
179  return;
180 
181  map()->setRadius(radius(newZoom));
182  m_logzoom = newZoom;
183 
184  emit zoomChanged(m_logzoom);
185  emit distanceChanged(distanceString());
186  }
187  else
188  {
189  GeoDataLookAt target = lookAt();
190  target.setRange(KM2METER * distanceFromZoom(newZoom));
191  flyTo(target, mode);
192  }
193  }
194 
195  void MarbleAbstractPresenter::zoomView(int zoom, FlyToMode mode)
196  {
197  setZoom(zoom, mode);
198  }
199 
200  void MarbleAbstractPresenter::zoomViewBy(int zoomStep, FlyToMode mode)
201  {
202  setZoom(zoom() + zoomStep, mode);
203  }
204 
205  void MarbleAbstractPresenter::zoomIn(FlyToMode mode)
206  {
207  if (map()->tileZoomLevel() < 0)
208  {
209  zoomViewBy(m_zoomStep, mode);
210  }
211  else
212  {
213  qreal radiusVal = map()->preferredRadiusCeil(map()->radius() / 0.95);
214  radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) );
215 
216  GeoDataLookAt target = lookAt();
217  target.setRange(KM2METER * distanceFromRadius(radiusVal));
218 
219  flyTo(target, mode);
220  }
221  }
222 
223  void MarbleAbstractPresenter::zoomOut(FlyToMode mode)
224  {
225  if (map()->tileZoomLevel() <= 0)
226  {
227  zoomViewBy(-m_zoomStep, mode);
228  }
229  else
230  {
231  qreal radiusVal = map()->preferredRadiusFloor(map()->radius() * 0.95);
232  radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) );
233 
234  GeoDataLookAt target = lookAt();
235  target.setRange(KM2METER * distanceFromRadius(radiusVal));
236 
237  flyTo(target, mode);
238  }
239  }
240 
241  void MarbleAbstractPresenter::zoomAtBy(const QPoint &pos, int zoomStep)
242  {
243  qreal radiusVal;
244  if (map()->tileZoomLevel() <= 0) {
245  radiusVal = radius(zoom() + zoomStep);
246  } else {
247 
248  radiusVal = zoomStep > 0 ? map()->preferredRadiusCeil(map()->radius() / 0.95) :
249  map()->preferredRadiusFloor(map()->radius() * 0.95);
250  radiusVal = qBound( radius(minimumZoom()), radiusVal, radius(maximumZoom()) );
251  }
252 
253  zoomAt(pos, distanceFromRadius(radiusVal));
254  }
255 
256  qreal MarbleAbstractPresenter::distanceFromZoom(qreal zoom) const
257  {
258  return distanceFromRadius(radius(zoom));
259  }
260 
261  qreal MarbleAbstractPresenter::zoomFromDistance(qreal distance) const
262  {
263  return zoom(radiusFromDistance(distance));
264  }
265 
266  void MarbleAbstractPresenter::goHome(FlyToMode mode)
267  {
268  qreal homeLon = 0;
269  qreal homeLat = 0;
270  int homeZoom = 0;
271  model()->home(homeLon, homeLat, homeZoom);
272 
273  GeoDataLookAt target;
274  target.setLongitude(homeLon, GeoDataCoordinates::Degree);
275  target.setLatitude(homeLat, GeoDataCoordinates::Degree);
276  target.setRange(1000 * distanceFromZoom(homeZoom));
277 
278  flyTo(target, mode);
279  }
280 
281  void MarbleAbstractPresenter::moveByStep(int stepsRight, int stepsDown, FlyToMode mode)
282  {
283  int polarity = map()->viewport()->polarity();
284  qreal left = polarity * stepsRight * moveStep();
285  qreal down = stepsDown * moveStep();
286  rotateBy(left, down, mode);
287  }
288 
289  qreal MarbleAbstractPresenter::moveStep() const
290  {
291  int width = map()->width();
292  int height = map()->height();
293 
294  if (radius() < qSqrt((qreal)(width * width + height * height)))
295  return 180.0 * 0.1;
296  else
297  return 180.0 * qAtan((qreal)width
298  / (qreal)(2 * radius())) * 0.2;
299  }
300 
301  int MarbleAbstractPresenter::radius() const
302  {
303  return map()->radius();
304  }
305 
306  void MarbleAbstractPresenter::setRadius(int radiusVal)
307  {
308  Q_ASSERT(radiusVal >= 0);
309  bool adjustRadius = radiusVal != map()->radius();
310 
311  qreal const zoomVal = zoom(radiusVal);
312 
313  // Prevent exceeding zoom range
314  if (zoomVal < minimumZoom())
315  {
316  radiusVal = radius(minimumZoom());
317  adjustRadius = true;
318  }
319  else if (zoomVal > maximumZoom())
320  {
321  radiusVal = radius(maximumZoom());
322  adjustRadius = true;
323  }
324 
325  if (adjustRadius)
326  {
327  map()->setRadius(radiusVal);
328  m_logzoom = qRound(zoomVal);
329 
330  emit zoomChanged(m_logzoom);
331  emit distanceChanged(distanceString());
332  }
333  }
334 
335 
336  //Moved from MarbleWidgetInputHandlerPrivate - fits more here now
337  void MarbleAbstractPresenter::zoomAt(const QPoint &pos, qreal newDistance)
338  {
339  Q_ASSERT(newDistance > 0.0);
340 
341  qreal destLat;
342  qreal destLon;
343  if (!map()->geoCoordinates(pos.x(), pos.y(), destLon, destLat, GeoDataCoordinates::Degree))
344  {
345  return;
346  }
347 
348  ViewportParams* now = map()->viewport();
349  qreal x(0), y(0);
350  if (!now->screenCoordinates(destLon * DEG2RAD, destLat * DEG2RAD, x, y))
351  {
352  return;
353  }
354 
355  ViewportParams soon;
356  soon.setProjection(now->projection());
357  soon.centerOn(now->centerLongitude(), now->centerLatitude());
358  soon.setSize(now->size());
359 
360  qreal newRadius = radiusFromDistance(newDistance);
361  soon.setRadius(newRadius);
362 
363  qreal mouseLon, mouseLat;
364  if (!soon.geoCoordinates(int(x), int(y), mouseLon, mouseLat, GeoDataCoordinates::Degree ))
365  {
366  return;
367  }
368 
369  const qreal lon = destLon - (mouseLon - map()->centerLongitude());
370  const qreal lat = destLat - (mouseLat - map()->centerLatitude());
371 
372  GeoDataLookAt lookAt;
373  lookAt.setLongitude(lon, GeoDataCoordinates::Degree);
374  lookAt.setLatitude(lat, GeoDataCoordinates::Degree);
375  lookAt.setAltitude(0.0);
376  lookAt.setRange(newDistance * KM2METER);
377 
378  map()->viewport()->setFocusPoint(GeoDataCoordinates(destLon, destLat, 0, GeoDataCoordinates::Degree));
379  flyTo(lookAt, Linear);
380  }
381 
382  void MarbleAbstractPresenter::moveTo(const QPoint &pos, qreal factor)
383  {
384  Q_ASSERT(factor > 0.0);
385 
386  qreal destLat;
387  qreal destLon;
388  map()->geoCoordinates(pos.x(), pos.y(), destLon, destLat, GeoDataCoordinates::Radian);
389 
390  GeoDataLookAt lookAt;
391  lookAt.setLongitude(destLon);
392  lookAt.setLatitude(destLat);
393  lookAt.setAltitude(0.0);
394  lookAt.setRange(distance() * factor * KM2METER);
395 
396  flyTo(lookAt);
397  }
398 
399  void MarbleAbstractPresenter::centerOn(const qreal lon, const qreal lat, bool animated)
400  {
401  GeoDataCoordinates target(lon, lat, 0.0, GeoDataCoordinates::Degree);
402  centerOn(target, animated);
403  }
404 
405  void MarbleAbstractPresenter::centerOn(const GeoDataCoordinates &position, bool animated)
406  {
407  GeoDataLookAt target = lookAt();
408  target.setCoordinates(position);
409  flyTo(target, animated ? Automatic : Instant);
410  }
411 
412  void MarbleAbstractPresenter::centerOn(const GeoDataLatLonBox &box, bool animated)
413  {
414  if (box.isEmpty())
415  {
416  return;
417  }
418 
419  int newRadius = radius();
420  ViewportParams* viewparams = map()->viewport();
421  //prevent divide by zero
422  if(box.height() && box.width())
423  {
424  //work out the needed zoom level
425  int const horizontalRadius = ( 0.25 * M_PI ) * (viewparams->height() / box.height());
426  int const verticalRadius = ( 0.25 * M_PI ) * (viewparams->width() / box.width());
427  newRadius = qMin<int>(horizontalRadius, verticalRadius );
428  newRadius = qMax<int>(radius(minimumZoom()), qMin<int>(newRadius, radius(maximumZoom())));
429  }
430 
431  //move the map
432  GeoDataLookAt target;
433  target.setCoordinates(box.center());
434  target.setAltitude(box.center().altitude());
435  target.setRange(KM2METER * distanceFromRadius(newRadius));
436  flyTo(target, animated ? Automatic : Instant);
437  }
438 
439  void MarbleAbstractPresenter::centerOn(const GeoDataPlacemark& placemark, bool animated)
440  {
441  const GeoDataLookAt *lookAt(placemark.lookAt());
442  if (lookAt)
443  {
444  flyTo(*lookAt, animated ? Automatic : Instant);
445  }
446  else
447  {
448  bool icon;
449  GeoDataCoordinates coords = placemark.coordinate(model()->clock()->dateTime(), &icon);
450  if (icon)
451  {
452  centerOn(coords, animated);
453  }
454  else
455  {
456  centerOn(placemark.geometry()->latLonAltBox(), animated);
457  }
458  }
459  }
460 
461  void MarbleAbstractPresenter::headingOn(qreal heading)
462  {
463  map()->setHeading(heading);
464  }
465 
466  void MarbleAbstractPresenter::setCenterLatitude(qreal lat, FlyToMode mode)
467  {
468  centerOn(centerLongitude(), lat, mode);
469  }
470 
471  void MarbleAbstractPresenter::setCenterLongitude(qreal lon, FlyToMode mode)
472  {
473  centerOn(lon, centerLatitude(), mode);
474  }
475 
476  qreal MarbleAbstractPresenter::centerLatitude() const
477  {
478  return map()->centerLatitude();
479  }
480 
481  qreal MarbleAbstractPresenter::centerLongitude() const
482  {
483  return map()->centerLongitude();
484  }
485 
486  ViewContext MarbleAbstractPresenter::viewContext() const
487  {
488  return map()->viewContext();
489  }
490 
491  void MarbleAbstractPresenter::setViewContext(ViewContext viewContext)
492  {
493  map()->setViewContext(viewContext);
494  }
495 
496  bool MarbleAbstractPresenter::animationsEnabled() const
497  {
498  return m_animationsEnabled;
499  }
500 
501  void MarbleAbstractPresenter::setAnimationsEnabled(bool enabled)
502  {
503  m_animationsEnabled = enabled;
504  }
505 
506  int MarbleAbstractPresenter::logzoom() const
507  {
508  return m_logzoom;
509  }
510 
511  void MarbleAbstractPresenter::setLogzoom(int value)
512  {
513  m_logzoom = value;
514  }
515 
516  int MarbleAbstractPresenter::zoomStep() const
517  {
518  return m_zoomStep;
519  }
520 
521  qreal MarbleAbstractPresenter::viewAngle() const
522  {
523  return m_viewAngle;
524  }
525 
526  MarbleMap* MarbleAbstractPresenter::map()
527  {
528  return m_map;
529  }
530 
531  const MarbleMap* MarbleAbstractPresenter::map() const
532  {
533  return m_map;
534  }
535 
536  MarbleModel* MarbleAbstractPresenter::model()
537  {
538  return m_map->model();
539  }
540 
541  const MarbleModel* MarbleAbstractPresenter::model() const
542  {
543  return m_map->model();
544  }
545 
546  ViewportParams* MarbleAbstractPresenter::viewport()
547  {
548  return map()->viewport();
549  }
550 
551  const ViewportParams* MarbleAbstractPresenter::viewport() const
552  {
553  return map()->viewport();
554  }
555 
556  void MarbleAbstractPresenter::setDistance(qreal newDistance)
557  {
558  qreal minDistance = 0.001;
559 
560  if (newDistance <= minDistance)
561  {
562  mDebug() << "Invalid distance: 0 m";
563  newDistance = minDistance;
564  }
565 
566  int newRadius = radiusFromDistance(newDistance);
567  setRadius(newRadius);
568  }
569 
570  void MarbleAbstractPresenter::setSelection(const QRect& region)
571  {
572  QPoint tl = region.topLeft();
573  QPoint br = region.bottomRight();
574  mDebug() << "Selection region: (" << tl.x() << ", " << tl.y() << ") ("
575  << br.x() << ", " << br.y() << ")" << endl;
576 
577  const GeoDataLatLonAltBox box = viewport()->latLonAltBox(region);
578 
579  emit regionSelected(box);
580  }
581 
582 }
583 
584 #include "moc_MarbleAbstractPresenter.cpp"
585 
ViewContext
This enum is used to choose context in which map quality gets used.
Definition: MarbleGlobal.h:66
QPoint topLeft() const const
QTextStream & endl(QTextStream &stream)
QTextStream & left(QTextStream &stream)
int x() const const
int y() const const
QPoint bottomRight() const const
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
FlyToMode
Describes possible flight mode (interpolation between source and target camera positions)
Definition: MarbleGlobal.h:162
@ Automatic
A sane value is chosen automatically depending on animation settings and the action.
Definition: MarbleGlobal.h:163
LocaleWrapper locale()
@ Instant
Change camera position immediately (no interpolation)
Definition: MarbleGlobal.h:164
Unit
enum used constructor to specify the units used
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
@ Linear
Linear interpolation of lon, lat and distance to ground.
Definition: MarbleGlobal.h:165
QFuture< void > map(Sequence &sequence, MapFunctor function)
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 21 2023 04:12:27 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.