Marble

AutoNavigation.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2010 Siddharth Srivastava <akssps011@gmail.com>
4// SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
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
25namespace Marble {
26
27class Q_DECL_HIDDEN AutoNavigation::Private
28{
29public:
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
69AutoNavigation::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
82void 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
111GeoDataCoordinates 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
261void 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
294void 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
308AutoNavigation::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
316AutoNavigation::~AutoNavigation()
317{
318 delete d;
319}
320
321void 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
352void AutoNavigation::setAutoZoom( bool autoZoom )
353{
354 d->m_adjustZoom = autoZoom;
355 emit autoZoomToggled( autoZoom );
356}
357
358void AutoNavigation::setRecenter( CenterMode recenterMode )
359{
360 d->m_recenterMode = recenterMode;
361 emit recenterModeChanged( recenterMode );
362}
363
364void AutoNavigation::inhibitAutoAdjustments()
365{
366 if ( !d->m_selfInteraction ) {
367 d->m_lastWidgetInteraction.start();
368 }
369}
370
371AutoNavigation::CenterMode AutoNavigation::recenterMode() const
372{
373 return d->m_recenterMode;
374}
375
376bool AutoNavigation::autoZoom() const
377{
378 return d->m_adjustZoom;
379}
380
381} // namespace Marble
382
383#include "moc_AutoNavigation.cpp"
This file contains the headers for MarbleModel.
This file contains the headers for ViewportParams.
Binds a QML item to a specific geodetic location in screen coordinates.
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void setX(qreal x)
void setY(qreal y)
qreal x() const const
qreal y() const const
bool contains(const QPoint &point, bool proper) const const
void setCoords(int x1, int y1, int x2, int y2)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:57 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.