Marble

MarbleAbstractPresenter.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
4// SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5// SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6// SPDX-FileCopyrightText: 2012 Mohammed Nafees <nafees.technocool@gmail.com>
7// SPDX-FileCopyrightText: 2014 Adam Dabrowski <adamdbrw@gmail.com>
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
25namespace 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
This file contains the headers for MarbleMap.
This file contains the headers for MarbleModel.
This file contains the headers for ViewportParams.
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
@ Instant
Change camera position immediately (no interpolation)
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
int x() const const
int y() const const
QPoint bottomRight() const const
QPoint topLeft() const const
QString arg(Args &&... args) const const
QTextStream & endl(QTextStream &stream)
QTextStream & left(QTextStream &stream)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
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.