Marble

AutoNavigation.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Siddharth Srivastava <[email protected]>
4 // SPDX-FileCopyrightText: 2011 Bernhard Beschow <[email protected]>
5 //
6 
7 
8 #include "AutoNavigation.h"
9 
10 #include "GeoDataCoordinates.h"
11 #include "PositionTracking.h"
12 #include "MarbleDebug.h"
13 #include "MarbleModel.h"
14 #include "ViewportParams.h"
15 #include "RoutingManager.h"
16 #include "RoutingModel.h"
17 #include "Route.h"
18 
19 #include <QWidget>
20 #include <QRect>
21 #include <QPointF>
22 #include <QTimer>
23 #include <cmath>
24 
25 namespace Marble {
26 
27 class Q_DECL_HIDDEN AutoNavigation::Private
28 {
29 public:
30 
31  AutoNavigation *const m_parent;
32  const MarbleModel *const m_model;
33  const ViewportParams *const m_viewport;
34  const PositionTracking *const m_tracking;
35  AutoNavigation::CenterMode m_recenterMode;
36  bool m_adjustZoom;
37  QTimer m_lastWidgetInteraction;
38  bool m_selfInteraction;
39 
40  /** Constructor */
41  Private( MarbleModel *model, const ViewportParams *viewport, AutoNavigation *parent );
42 
43  /**
44  * @brief To center on when reaching custom defined border
45  * @param position current gps location
46  * @param speed optional speed argument
47  */
48  void moveOnBorderToCenter( const GeoDataCoordinates &position, qreal speed );
49 
50  /**
51  * For calculating intersection point of projected LineString from
52  * current gps location with the map border
53  * @param position current gps location
54  */
55  GeoDataCoordinates findIntersection( qreal currentX, qreal currentY ) const;
56 
57  /**
58  * @brief Adjust the zoom value of the map
59  * @param currentPosition current location of the gps device
60  */
61  void adjustZoom( const GeoDataCoordinates &currentPosition, qreal speed );
62 
63  /**
64  * Center the widget on the given position unless recentering is currently inhibited
65  */
66  void centerOn( const GeoDataCoordinates &position );
67 };
68 
69 AutoNavigation::Private::Private( MarbleModel *model, const ViewportParams *viewport, AutoNavigation *parent ) :
70  m_parent( parent ),
71  m_model( model ),
72  m_viewport( viewport ),
73  m_tracking( model->positionTracking() ),
74  m_recenterMode( AutoNavigation::DontRecenter ),
75  m_adjustZoom( 0 ),
76  m_selfInteraction( false )
77 {
78  m_lastWidgetInteraction.setInterval( 10 * 1000 );
79  m_lastWidgetInteraction.setSingleShot( true );
80 }
81 
82 void AutoNavigation::Private::moveOnBorderToCenter( const GeoDataCoordinates &position, qreal )
83 {
84  qreal x = 0.0;
85  qreal y = 0.0;
86  //recenter if initially the gps location is not visible on the screen
87  if(!( m_viewport->screenCoordinates( position, x, y ) ) ) {
88  centerOn( position );
89  }
90  qreal centerLon = m_viewport->centerLongitude();
91  qreal centerLat = m_viewport->centerLatitude();
92 
93  qreal centerX = 0.0;
94  qreal centerY = 0.0;
95 
96  m_viewport->screenCoordinates( centerLon, centerLat, centerX, centerY );
97 
98  const qreal borderRatio = 0.25;
99  //defining the default border distance from map center
100  int shiftX = qRound( centerX * borderRatio );
101  int shiftY = qRound( centerY * borderRatio );
102 
103  QRect recenterBorderBound;
104  recenterBorderBound.setCoords( centerX-shiftX, centerY-shiftY, centerX+shiftX, centerY+shiftY );
105 
106  if( !recenterBorderBound.contains( x,y ) ) {
107  centerOn( position );
108  }
109 }
110 
111 GeoDataCoordinates AutoNavigation::Private::findIntersection( qreal currentX, qreal currentY ) const
112 {
113  qreal direction = m_tracking->direction();
114  if ( direction >= 360 ) {
115  direction = fmod( direction,360.0 );
116  }
117 
118  const qreal width = m_viewport->width();
119  const qreal height = m_viewport->height();
120 
121  QPointF intercept;
122  QPointF destinationHorizontal;
123  QPointF destinationVertical;
124  QPointF destination;
125 
126  bool crossHorizontal = false;
127  bool crossVertical = false;
128 
129  //calculation of intersection point
130  if( 0 < direction && direction < 90 ) {
131  const qreal angle = direction * DEG2RAD;
132 
133  //Intersection with line x = width
134  intercept.setX( width - currentX );
135  intercept.setY( intercept.x() / tan( angle ) );
136  destinationVertical.setX( width );
137  destinationVertical.setY( currentY-intercept.y() );
138 
139  //Intersection with line y = 0
140  intercept.setY( currentY );
141  intercept.setX( intercept.y() * tan( angle ) );
142  destinationHorizontal.setX( currentX + intercept.x() );
143  destinationHorizontal.setY( 0 );
144 
145  if ( destinationVertical.y() < 0 ) {
146  crossHorizontal = true;
147  }
148  else if( destinationHorizontal.x() > width ) {
149  crossVertical = true;
150  }
151 
152  }
153  else if( 270 < direction && direction < 360 ) {
154  const qreal angle = (direction - 270) * DEG2RAD;
155 
156  //Intersection with line y = 0
157  intercept.setY( currentY );
158  intercept.setX( intercept.y() / tan( angle ) );
159  destinationHorizontal.setX( currentX - intercept.x() );
160  destinationHorizontal.setY( 0 );
161 
162  //Intersection with line x = 0
163  intercept.setX( currentX );
164  intercept.setY( intercept.x() * tan( angle ) );
165  destinationVertical.setY( currentY - intercept.y() );
166  destinationVertical.setX( 0 );
167 
168  if( destinationHorizontal.x() > width ) {
169  crossVertical = true;
170  }
171  else if( destinationVertical.y() < 0 ) {
172  crossHorizontal = true;
173  }
174 
175  }
176  else if( 180 < direction && direction < 270 ) {
177  const qreal angle = (direction - 180) * DEG2RAD;
178 
179  //Intersection with line x = 0
180  intercept.setX( currentX );
181  intercept.setY( intercept.x() / tan( angle ) );
182  destinationVertical.setY( currentY + intercept.y() );
183  destinationVertical.setX( 0 );
184 
185  //Intersection with line y = height
186  intercept.setY( currentY );
187  intercept.setX( intercept.y() * tan( angle ) );
188  destinationHorizontal.setX( currentX - intercept.x() );
189  destinationHorizontal.setY( height );
190 
191  if ( destinationVertical.y() > height ) {
192  crossHorizontal = true;
193  }
194  else if ( destinationHorizontal.x() < 0 ) {
195  crossVertical = true;
196  }
197 
198  }
199  else if( 90 < direction && direction < 180 ) {
200  const qreal angle = (direction - 90) * DEG2RAD;
201 
202  //Intersection with line y = height
203  intercept.setY( height - currentY );
204  intercept.setX( intercept.y() / tan( angle ) );
205  destinationHorizontal.setX( currentX + intercept.x() );
206  destinationHorizontal.setY( height );
207 
208  //Intersection with line x = width
209  intercept.setX( width - currentX );
210  intercept.setY( intercept.x() * tan( angle ) );
211  destinationVertical.setX( width );
212  destinationVertical.setY( currentY + intercept.y() );
213 
214  if ( destinationHorizontal.x() > width ) {
215  crossVertical = true;
216  }
217  else if( destinationVertical.y() > height ) {
218  crossHorizontal = true;
219  }
220 
221  }
222  else if( direction == 0 ) {
223  destinationHorizontal.setX( currentX );
224  destinationHorizontal.setY( 0 );
225  crossHorizontal = true;
226  }
227  else if( direction == 90 ) {
228  destinationVertical.setX( width );
229  destinationVertical.setY( currentY );
230  crossVertical = true;
231  }
232  else if( direction == 180 ) {
233  destinationHorizontal.setX( currentX );
234  destinationHorizontal.setY( height );
235  crossHorizontal = true;
236  }
237  else if( direction == 270 ) {
238  destinationVertical.setX( 0 );
239  destinationVertical.setY( currentY );
240  crossVertical = true;
241  }
242 
243  if ( crossHorizontal == true && crossVertical == false ) {
244  destination.setX( destinationHorizontal.x() );
245  destination.setY( destinationHorizontal.y() );
246  }
247  else if ( crossVertical == true && crossHorizontal == false ) {
248  destination.setX( destinationVertical.x() );
249  destination.setY( destinationVertical.y() );
250  }
251 
252  qreal destinationLon = 0.0;
253  qreal destinationLat = 0.0;
254  m_viewport->geoCoordinates( destination.x(), destination.y(), destinationLon, destinationLat,
255  GeoDataCoordinates::Radian );
256  GeoDataCoordinates destinationCoord( destinationLon, destinationLat, GeoDataCoordinates::Radian );
257 
258  return destinationCoord;
259 }
260 
261 void AutoNavigation::Private::adjustZoom( const GeoDataCoordinates &currentPosition, qreal speed )
262 {
263  qreal currentX = 0;
264  qreal currentY = 0;
265  if( !m_viewport->screenCoordinates(currentPosition, currentX, currentY ) ) {
266  return;
267  }
268 
269  const GeoDataCoordinates destination = findIntersection( currentX, currentY );
270 
271  const qreal greatCircleDistance = currentPosition.sphericalDistanceTo(destination);
272  qreal radius = m_model->planetRadius();
273  qreal distance = greatCircleDistance * radius;
274 
275  if( speed != 0 ) {
276  // time (in seconds) remaining to reach the border of the map
277  qreal remainingTime = distance / speed;
278 
279  // tolerance time limits (in seconds) before auto zooming
280  qreal thresholdLow = 15;
281  qreal thresholdHigh = 120;
282 
283  m_selfInteraction = true;
284  if ( remainingTime < thresholdLow ) {
285  emit m_parent->zoomOut( Instant );
286  }
287  else if ( remainingTime > thresholdHigh ) {
288  emit m_parent->zoomIn( Instant );
289  }
290  m_selfInteraction = false;
291  }
292 }
293 
294 void AutoNavigation::Private::centerOn( const GeoDataCoordinates &position )
295 {
296  m_selfInteraction = true;
297  RoutingManager const * routingManager = m_model->routingManager();
298  RoutingModel const * routingModel = routingManager->routingModel();
299  if (!routingManager->guidanceModeEnabled() || routingModel->deviatedFromRoute()){
300  emit m_parent->centerOn( position, false );
301  } else {
302  GeoDataCoordinates positionOnRoute = routingModel->route().positionOnRoute();
303  emit m_parent->centerOn( positionOnRoute, false );
304  }
305  m_selfInteraction = false;
306 }
307 
308 AutoNavigation::AutoNavigation( MarbleModel *model, const ViewportParams *viewport, QObject *parent ) :
309  QObject( parent ),
310  d( new AutoNavigation::Private( model, viewport, this ) )
311 {
312  connect( d->m_tracking, SIGNAL(gpsLocation(GeoDataCoordinates,qreal)),
313  this, SLOT(adjust(GeoDataCoordinates,qreal)) );
314 }
315 
316 AutoNavigation::~AutoNavigation()
317 {
318  delete d;
319 }
320 
321 void AutoNavigation::adjust( const GeoDataCoordinates &position, qreal speed )
322 {
323  if ( d->m_lastWidgetInteraction.isActive() ) {
324  return;
325  }
326 
327  switch( d->m_recenterMode ) {
328  case DontRecenter:
329  /* nothing to do */
330  break;
331  case AlwaysRecenter:
332  d->centerOn( position );
333  break;
334  case RecenterOnBorder:
335  d->moveOnBorderToCenter( position, speed );
336  break;
337  }
338 
339  if ( d->m_adjustZoom ) {
340  switch( d->m_recenterMode ) {
341  case DontRecenter:
342  /* nothing to do */
343  break;
344  case AlwaysRecenter:
345  case RecenterOnBorder: // fallthrough
346  d->adjustZoom( position, speed );
347  break;
348  }
349  }
350 }
351 
352 void AutoNavigation::setAutoZoom( bool autoZoom )
353 {
354  d->m_adjustZoom = autoZoom;
355  emit autoZoomToggled( autoZoom );
356 }
357 
358 void AutoNavigation::setRecenter( CenterMode recenterMode )
359 {
360  d->m_recenterMode = recenterMode;
361  emit recenterModeChanged( recenterMode );
362 }
363 
364 void AutoNavigation::inhibitAutoAdjustments()
365 {
366  if ( !d->m_selfInteraction ) {
367  d->m_lastWidgetInteraction.start();
368  }
369 }
370 
371 AutoNavigation::CenterMode AutoNavigation::recenterMode() const
372 {
373  return d->m_recenterMode;
374 }
375 
376 bool AutoNavigation::autoZoom() const
377 {
378  return d->m_adjustZoom;
379 }
380 
381 } // namespace Marble
382 
383 #include "moc_AutoNavigation.cpp"
bool contains(const QRect &rectangle, bool proper) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setCoords(int x1, int y1, int x2, int y2)
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void setX(qreal x)
void setY(qreal y)
Binds a QML item to a specific geodetic location in screen coordinates.
@ Instant
Change camera position immediately (no interpolation)
Definition: MarbleGlobal.h:164
qreal x() const const
qreal y() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Oct 2 2023 03:52:07 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.