Marble

RoutingModel.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
4//
5
6#include "RoutingModel.h"
7
8#include "Planet.h"
9#include "PlanetFactory.h"
10#include "Route.h"
11#include "RouteRequest.h"
12#include "PositionTracking.h"
13#include "MarbleGlobal.h"
14#include "GeoDataAccuracy.h"
15
16#include <QPixmap>
17
18namespace Marble
19{
20
21class RoutingModelPrivate
22{
23public:
24 enum RouteDeviation
25 {
26 Unknown,
27 OnRoute,
28 OffRoute
29 };
30
31 explicit RoutingModelPrivate(PositionTracking *positionTracking, RouteRequest *request);
32
33 Route m_route;
34
35 PositionTracking *const m_positionTracking;
36 RouteRequest* const m_request;
37 QHash<int, QByteArray> m_roleNames;
38 RouteDeviation m_deviation;
39
40 void updateViaPoints( const GeoDataCoordinates &position );
41};
42
43RoutingModelPrivate::RoutingModelPrivate(PositionTracking *positionTracking, RouteRequest *request) :
44 m_positionTracking(positionTracking),
45 m_request(request),
46 m_deviation(Unknown)
47{
48 // nothing to do
49}
50
51void RoutingModelPrivate::updateViaPoints( const GeoDataCoordinates &position )
52{
53 // Mark via points visited after approaching them in a range of 500m or less
54 qreal const threshold = 500 / EARTH_RADIUS;
55 for( int i=0; i<m_request->size(); ++i ) {
56 if ( !m_request->visited( i ) ) {
57 if (position.sphericalDistanceTo(m_request->at(i)) < threshold) {
58 m_request->setVisited( i, true );
59 }
60 }
61 }
62}
63
64RoutingModel::RoutingModel(RouteRequest *request, PositionTracking *positionTracking, QObject *parent) :
65 QAbstractListModel(parent),
66 d(new RoutingModelPrivate(positionTracking, request))
67{
68 QObject::connect( d->m_positionTracking, SIGNAL(gpsLocation(GeoDataCoordinates,qreal)),
69 this, SLOT(updatePosition(GeoDataCoordinates,qreal)) );
70
72 roles.insert( Qt::DisplayRole, "display" );
73 roles.insert( RoutingModel::TurnTypeIconRole, "turnTypeIcon" );
74 roles.insert( RoutingModel::LongitudeRole, "longitude" );
75 roles.insert( RoutingModel::LatitudeRole, "latitude" );
76 d->m_roleNames = roles;
77}
78
79RoutingModel::~RoutingModel()
80{
81 delete d;
82}
83
84int RoutingModel::rowCount ( const QModelIndex &parent ) const
85{
86 return parent.isValid() ? 0 : d->m_route.turnPoints().size();
87}
88
89QVariant RoutingModel::headerData ( int section, Qt::Orientation orientation, int role ) const
90{
91 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0 ) {
92 return QString( "Instruction" );
93 }
94
95 return QAbstractListModel::headerData( section, orientation, role );
96}
97
98QVariant RoutingModel::data ( const QModelIndex & index, int role ) const
99{
100 if ( !index.isValid() ) {
101 return QVariant();
102 }
103
104 if ( index.row() < d->m_route.turnPoints().size() && index.column() == 0 ) {
105 const RouteSegment &segment = d->m_route.at( index.row() );
106 switch ( role ) {
107 case Qt::DisplayRole:
108 case Qt::ToolTipRole:
109 return segment.maneuver().instructionText();
111 {
112 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
113 if ( segment.maneuver().hasWaypoint() ) {
114 int const size = smallScreen ? 64 : 32;
115 return d->m_request->pixmap( segment.maneuver().waypointIndex(), size, size/4 );
116 }
117
118 QPixmap const pixmap = segment.maneuver().directionPixmap();
119 return smallScreen ? pixmap : pixmap.scaled( 32, 32 );
120 }
121 case Qt::SizeHintRole:
122 {
123 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
124 int const size = smallScreen ? 64 : 32;
125 return QSize( size, size );
126 }
127 case RoutingModel::CoordinateRole:
128 return QVariant::fromValue( segment.maneuver().position() );
129 case RoutingModel::LongitudeRole:
130 return QVariant(segment.maneuver().position().longitude(GeoDataCoordinates::Degree));
131 case RoutingModel::LatitudeRole:
132 return QVariant(segment.maneuver().position().latitude(GeoDataCoordinates::Degree));
133 case RoutingModel::TurnTypeIconRole:
134 return segment.maneuver().directionPixmap();
135 default:
136 return QVariant();
137 }
138 }
139
140 return QVariant();
141}
142
143QHash<int, QByteArray> RoutingModel::roleNames() const
144{
145 return d->m_roleNames;
146}
147
148void RoutingModel::setRoute( const Route &route )
149{
150 d->m_route = route;
151 d->m_deviation = RoutingModelPrivate::Unknown;
152
153 beginResetModel();
154 endResetModel();
155 emit currentRouteChanged();
156}
157
158void RoutingModel::exportGpx( QIODevice *device ) const
159{
160 QString content = QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
161 "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" creator=\"Marble\" version=\"1.1\" "
162 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
163 "xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 "
164 "http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"
165 "<metadata>\n <link href=\"http://edu.kde.org/marble\">\n "
166 "<text>Marble Virtual Globe</text>\n </link>\n</metadata>\n"
167 " <rte>\n <name>Route</name>\n");
168 bool hasAltitude = false;
169 for ( int i=0; !hasAltitude && i<d->m_route.size(); ++i ) {
170 hasAltitude = d->m_route.at( i ).maneuver().position().altitude() != 0.0;
171 }
172 for ( int i=0; i<d->m_route.size(); ++i ) {
173 const Maneuver &maneuver = d->m_route.at( i ).maneuver();
174 qreal lon = maneuver.position().longitude( GeoDataCoordinates::Degree );
175 qreal lat = maneuver.position().latitude( GeoDataCoordinates::Degree );
176 QString const text = maneuver.instructionText();
177 content += QString( " <rtept lat=\"%1\" lon=\"%2\">\n" ).arg( lat, 0, 'f', 7 ).arg( lon, 0, 'f', 7 );
178 content += QString( " <name>%1</name>\n").arg( text );
179 if ( hasAltitude ) {
180 content += QString( " <ele>%1</ele>\n" ).arg( maneuver.position().altitude(), 0, 'f', 2 );
181 }
182 content += QString( " </rtept>\n" );
183 }
184 content += QLatin1String(" </rte>\n"
185 "<trk>\n <name>Route</name>\n <trkseg>\n");
186 GeoDataLineString points = d->m_route.path();
187 hasAltitude = false;
188 for ( int i=0; !hasAltitude && i<points.size(); ++i ) {
189 hasAltitude = points[i].altitude() != 0.0;
190 }
191 for ( int i=0; i<points.size(); ++i ) {
192 GeoDataCoordinates const &point = points[i];
193 qreal lon = point.longitude( GeoDataCoordinates::Degree );
194 qreal lat = point.latitude( GeoDataCoordinates::Degree );
195 content += QString( " <trkpt lat=\"%1\" lon=\"%2\">\n" ).arg( lat, 0, 'f', 7 ).arg( lon, 0, 'f', 7 );
196 if ( hasAltitude ) {
197 content += QString( " <ele>%1</ele>\n" ).arg( point.altitude(), 0, 'f', 2 );
198 }
199 content += QString( " </trkpt>\n" );
200 }
201 content += QLatin1String(" </trkseg>\n </trk>\n"
202 "</gpx>\n");
203
204 device->write( content.toUtf8() );
205}
206
207void RoutingModel::clear()
208{
209 d->m_route = Route();
210 beginResetModel();
211 endResetModel();
212 emit currentRouteChanged();
213}
214
215int RoutingModel::rightNeighbor( const GeoDataCoordinates &position, RouteRequest const *const route ) const
216{
217 Q_ASSERT( route && "Must not pass a null route ");
218
219 // Quick result for trivial cases
220 if ( route->size() < 3 ) {
221 return route->size() - 1;
222 }
223
224 // Generate an ordered list of all waypoints
225 GeoDataLineString points = d->m_route.path();
226 QMap<int,int> mapping;
227
228 // Force first mapping point to match the route start
229 mapping[0] = 0;
230
231 // Calculate the mapping between waypoints and via points
232 // Need two for loops to avoid getting stuck in local minima
233 for ( int j=1; j<route->size()-1; ++j ) {
234 qreal minDistance = -1.0;
235 for ( int i=mapping[j-1]; i<points.size(); ++i ) {
236 const qreal distance = points[i].sphericalDistanceTo(route->at(j));
237 if (minDistance < 0.0 || distance < minDistance ) {
238 mapping[j] = i;
239 minDistance = distance;
240 }
241 }
242 }
243
244 // Determine waypoint with minimum distance to the provided position
245 qreal minWaypointDistance = -1.0;
246 int waypoint=0;
247 for ( int i=0; i<points.size(); ++i ) {
248 const qreal waypointDistance = points[i].sphericalDistanceTo(position);
249 if ( minWaypointDistance < 0.0 || waypointDistance < minWaypointDistance ) {
250 minWaypointDistance = waypointDistance;
251 waypoint = i;
252 }
253 }
254
255 // Force last mapping point to match the route destination
256 mapping[route->size()-1] = points.size()-1;
257
258 // Determine neighbor based on the mapping
260 for ( ; iter != mapping.constEnd(); ++iter ) {
261 if ( iter.value() > waypoint ) {
262 int index = iter.key();
263 Q_ASSERT( index >= 0 && index <= route->size() );
264 return index;
265 }
266 }
267
268 return route->size()-1;
269}
270
271void RoutingModel::updatePosition( const GeoDataCoordinates& location, qreal speed )
272{
273 d->m_route.setPosition( location );
274
275 d->updateViaPoints( location );
276 const qreal planetRadius = PlanetFactory::construct("earth").radius();
277 const qreal distance = planetRadius * location.sphericalDistanceTo(d->m_route.positionOnRoute());
278 emit positionChanged();
279
280 qreal deviation = 0.0;
281 if ( d->m_positionTracking && d->m_positionTracking->accuracy().vertical > 0.0 ) {
282 deviation = qMax<qreal>( d->m_positionTracking->accuracy().vertical, d->m_positionTracking->accuracy().horizontal );
283 }
284 qreal const threshold = deviation + qBound(10.0, speed*10.0, 150.0);
285
286 RoutingModelPrivate::RouteDeviation const deviated = distance < threshold ? RoutingModelPrivate::OnRoute : RoutingModelPrivate::OffRoute;
287 if ( d->m_deviation != deviated ) {
288 d->m_deviation = deviated;
289 emit deviatedFromRoute( deviated == RoutingModelPrivate::OffRoute );
290 }
291}
292
293bool RoutingModel::deviatedFromRoute() const
294{
295 return d->m_deviation == RoutingModelPrivate::OffRoute;
296}
297
298const Route & RoutingModel::route() const
299{
300 return d->m_route;
301}
302
303} // namespace Marble
304
305#include "moc_RoutingModel.cpp"
QVariant location(const QVariant &res)
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)
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const
iterator insert(const Key &key, const T &value)
qint64 write(const QByteArray &data)
const_iterator constBegin() const const
const_iterator constEnd() const const
size_type size() const const
int column() const const
bool isValid() const const
int row() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QString arg(Args &&... args) const const
QByteArray toUtf8() const const
DisplayRole
Orientation
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:58 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.