Marble

RouteRequest.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 "RouteRequest.h"
7
8#include "GeoDataLineString.h"
9#include "GeoDataPlacemark.h"
10#include "GeoDataData.h"
11#include "GeoDataExtendedData.h"
12#include "MarbleColors.h"
13#include "MarbleDirs.h"
14
15#include <QMap>
16#include <QPainter>
17#include <QDebug>
18
19namespace Marble
20{
21
22struct PixmapElement
23{
24 int index;
25
26 int size;
27
28 explicit PixmapElement( int index=-1, int size=0 );
29
30 bool operator < ( const PixmapElement &other ) const;
31};
32
33class RouteRequestPrivate
34{
35public:
37
38 QMap<PixmapElement, QPixmap> m_pixmapCache;
39
40 RoutingProfile m_routingProfile;
41
42 /** Determines a suitable index for inserting a via point */
43 int viaIndex( const GeoDataCoordinates &position ) const;
44};
45
46PixmapElement::PixmapElement( int index_, int size_ ) :
47 index( index_ ), size( size_ )
48{
49 // nothing to do
50}
51
52bool PixmapElement::operator <(const PixmapElement &other) const
53{
54 return index < other.index || size < other.size;
55}
56
57int RouteRequestPrivate::viaIndex( const GeoDataCoordinates &position ) const
58{
59 /** @todo: Works, but does not look elegant at all */
60
61 // Iterates over all ordered trip point pairs (P,Q) and finds the triple
62 // (P,position,Q) or (P,Q,position) with minimum length
63 qreal minLength = -1.0;
64 int result = 0;
65 GeoDataLineString viaFirst;
66 GeoDataLineString viaSecond;
67 for ( int i = 0; i < m_route.size(); ++i ) {
68 Q_ASSERT( viaFirst.size() < 4 && viaSecond.size() < 4 );
69 if ( viaFirst.size() == 3 ) {
70 viaFirst.remove( 0 );
71 viaFirst.remove( 0 );
72 }
73
74 if ( viaSecond.size() == 3 ) {
75 viaSecond.remove( 0 );
76 viaSecond.remove( 0 );
77 }
78
79 if ( viaFirst.size() == 1 ) {
80 viaFirst.append( position );
81 }
82
83 viaFirst.append( m_route[i].coordinate() );
84 viaSecond.append( m_route[i].coordinate() );
85
86 if ( viaSecond.size() == 2 ) {
87 viaSecond.append( position );
88 }
89
90 if ( viaFirst.size() == 3 ) {
91 qreal len = viaFirst.length( EARTH_RADIUS );
92 if ( minLength < 0.0 || len < minLength ) {
93 minLength = len;
94 result = i;
95 }
96 }
97
98 /** @todo: Assumes that destination is the last point */
99 if ( viaSecond.size() == 3 && i + 1 < m_route.size() ) {
100 qreal len = viaSecond.length( EARTH_RADIUS );
101 if ( minLength < 0.0 || len < minLength ) {
102 minLength = len;
103 result = i + 1;
104 }
105 }
106 }
107
108 Q_ASSERT( 0 <= result && result <= m_route.size() );
109 return result;
110}
111
112RouteRequest::RouteRequest( QObject *parent ) :
113 QObject( parent ), d( new RouteRequestPrivate )
114{
115 // nothing to do
116}
117
119{
120 delete d;
121}
122
124{
125 return d->m_route.size();
126}
127
129{
130 GeoDataCoordinates result;
131 if ( d->m_route.size() ) {
132 result = d->m_route.first().coordinate();
133 }
134 return result;
135}
136
138{
139 GeoDataCoordinates result;
140 if ( d->m_route.size() ) {
141 result = d->m_route.last().coordinate();
142 }
143 return result;
144}
145
147{
148 return d->m_route.at( position ).coordinate();
149}
150
151QPixmap RouteRequest::pixmap(int position, int size, int margin ) const
152{
153 PixmapElement const element( position, size );
154
155 if ( !d->m_pixmapCache.contains( element ) ) {
156 // Transparent background
157 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
158 int const imageSize = size > 0 ? size : ( smallScreen ? 32 : 16 );
159 QImage result( imageSize, imageSize, QImage::Format_ARGB32_Premultiplied );
160 result.fill( qRgba( 0, 0, 0, 0 ) );
161
162 // Paint a colored circle
163 QPainter painter( &result );
164 painter.setRenderHint( QPainter::Antialiasing, true );
165 painter.setPen( QColor( Qt::black ) );
166 bool const isVisited = visited( position );
167 QColor const backgroundColor = isVisited ? Oxygen::aluminumGray4 : Oxygen::forestGreen4;
168 painter.setBrush( QBrush( backgroundColor ) );
169 painter.setPen( Qt::black );
170 int const iconSize = imageSize - 2 * margin;
171 painter.drawEllipse( margin, margin, iconSize, iconSize );
172
173 char const text = char( 'A' + position );
174
175 // Choose a suitable font size
176 QFont font = painter.font();
177 int fontSize = 20;
178 while ( fontSize-- > 0 ) {
179 font.setPointSize( fontSize );
180 QFontMetrics const fontMetric( font );
181 if ( fontMetric.horizontalAdvance( text ) <= iconSize && fontMetric.height( ) <= iconSize ) {
182 break;
183 }
184 }
185
186 Q_ASSERT( fontSize );
187 font.setPointSize( fontSize );
188 painter.setFont( font );
189
190 // Paint a character denoting the position (0=A, 1=B, 2=C, ...)
191 painter.drawText( 0, 0, imageSize, imageSize, Qt::AlignCenter, QString( text ) );
192
193 d->m_pixmapCache.insert( element, QPixmap::fromImage( result ) );
194 }
195
196 return d->m_pixmapCache[element];
197}
198
200{
201 for ( int i=d->m_route.size()-1; i>=0; --i ) {
202 remove( i );
203 }
204}
205
206void RouteRequest::insert( int index, const GeoDataCoordinates &coordinates, const QString &name )
207{
208 GeoDataPlacemark placemark;
209 placemark.setCoordinate( coordinates );
210 placemark.setName( name );
211 insert(index, placemark);
212}
213
214void RouteRequest::insert(int index, const GeoDataPlacemark &placemark)
215{
216 d->m_route.insert( index, placemark );
217 emit positionAdded( index );
218}
219
220void RouteRequest::swap(int index1, int index2)
221{
222 if (index1 < 0 || index2 < 0 || index1 > d->m_route.size()-1 || index2 > d->m_route.size()-1) {
223 return;
224 }
225
226 qSwap(d->m_route[index1], d->m_route[index2]);
227
228 emit positionChanged(index1, d->m_route[index1].coordinate());
229 emit positionChanged(index2, d->m_route[index2].coordinate());
230}
231
232void RouteRequest::append( const GeoDataCoordinates &coordinates, const QString &name )
233{
234 GeoDataPlacemark placemark;
235 placemark.setCoordinate( coordinates );
236 placemark.setName( name );
237 append( placemark );
238}
239
240void RouteRequest::append( const GeoDataPlacemark &placemark )
241{
242 d->m_route.append( placemark );
243 emit positionAdded( d->m_route.size()-1 );
244}
245
246void RouteRequest::remove( int index )
247{
248 if ( index >= 0 && index < d->m_route.size() ) {
249 d->m_route.remove( index );
250 emit positionRemoved( index );
251 }
252}
253
255{
256 GeoDataPlacemark placemark;
257 placemark.setCoordinate( position );
258 addVia(placemark);
259}
260
261void RouteRequest::addVia(const GeoDataPlacemark &placemark)
262{
263 int index = d->viaIndex( placemark.coordinate() );
264 d->m_route.insert( index, placemark );
265 emit positionAdded( index );
266}
267
268void RouteRequest::setPosition( int index, const GeoDataCoordinates &position, const QString &name )
269{
270 if ( index >= 0 && index < d->m_route.size() ) {
271 d->m_route[index].setName( name );
272 if ( d->m_route[index].coordinate() != position ) {
273 d->m_route[index].setCoordinate( position );
274 setVisited( index, false );
275 emit positionChanged( index, position );
276 }
277 }
278}
279
280void RouteRequest::setName( int index, const QString &name )
281{
282 if ( index >= 0 && index < d->m_route.size() ) {
283 d->m_route[index].setName( name );
284 }
285}
286
287QString RouteRequest::name( int index ) const
288{
289 QString result;
290 if ( index >= 0 && index < d->m_route.size() ) {
291 result = d->m_route[index].name();
292 }
293 return result;
294}
295
296void RouteRequest::setVisited( int index, bool visited )
297{
298 if ( index >= 0 && index < d->m_route.size() ) {
299 d->m_route[index].extendedData().addValue(GeoDataData(QStringLiteral("routingVisited"), visited));
301 while ( iter != d->m_pixmapCache.end() ) {
302 if ( iter.key().index == index ) {
303 iter = d->m_pixmapCache.erase( iter );
304 } else {
305 ++iter;
306 }
307 }
308 emit positionChanged( index, d->m_route[index].coordinate() );
309 }
310}
311
312bool RouteRequest::visited( int index ) const
313{
314 bool visited = false;
315 if ( index >= 0 && index < d->m_route.size() ) {
316 if (d->m_route[index].extendedData().contains(QStringLiteral("routingVisited"))) {
317 visited = d->m_route[index].extendedData().value(QStringLiteral("routingVisited")).value().toBool();
318 }
319 }
320 return visited;
321}
322
323void RouteRequest::reverse()
324{
325 std::reverse(d->m_route.begin(), d->m_route.end());
326 int const total = d->m_route.size();
327 for (int i = 0; i < total; ++i) {
328 setVisited( i, false );
329 }
330}
331
332void RouteRequest::setRoutingProfile( const RoutingProfile &profile )
333{
334 d->m_routingProfile = profile;
336}
337
338RoutingProfile RouteRequest::routingProfile() const
339{
340 return d->m_routingProfile;
341}
342
343GeoDataPlacemark &RouteRequest::operator []( int index )
344{
345 return d->m_route[index];
346}
347
348const GeoDataPlacemark &RouteRequest::operator [](int index) const
349{
350 return d->m_route[index];
351}
352
353} // namespace Marble
354
355
356#include "moc_RouteRequest.cpp"
A 3d point representation.
void setName(const QString &value)
Set a new name for this feature.
a class representing a point of interest on the map
GeoDataCoordinates coordinate(const QDateTime &dateTime=QDateTime(), bool *iconAtCoordinates=nullptr) const
Return the coordinates of the placemark at time dateTime as a GeoDataCoordinates.
void setCoordinate(qreal longitude, qreal latitude, qreal altitude=0, GeoDataCoordinates::Unit _unit=GeoDataCoordinates::Radian)
Set the coordinate of the placemark in longitude and latitude.
GeoDataCoordinates source() const
The first point, or a default constructed if empty.
int size() const
Number of points in the route.
~RouteRequest() override
Destructor.
void positionAdded(int index)
An element was added at the given position.
void clear()
Remove all elements.
void positionChanged(int index, const GeoDataCoordinates &position)
The value of the n-th element was changed.
void swap(int index1, int index2)
Swaps the given elements at the given positions.
void setPosition(int index, const GeoDataCoordinates &position, const QString &name=QString())
Change the value of the element at the given position.
void insert(int index, const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element at the given position.
void routingProfileChanged()
The routing profile was changed.
void addVia(const GeoDataCoordinates &position)
Insert a via point.
void positionRemoved(int index)
The element at the given position was removed.
GeoDataCoordinates at(int index) const
Accessor for the n-th position.
void append(const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element to the end.
GeoDataCoordinates destination() const
The last point, or a default constructed if empty.
QPixmap pixmap(int index, int size=-1, int margin=2) const
Returns a pixmap which indicates the position of the element.
void remove(int index)
Remove the element at the given position.
Binds a QML item to a specific geodetic location in screen coordinates.
void setPointSize(int pointSize)
Format_ARGB32_Premultiplied
void fill(Qt::GlobalColor color)
iterator begin()
bool contains(const Key &key) const const
iterator end()
iterator erase(const_iterator first, const_iterator last)
iterator insert(const Key &key, const T &value)
T qobject_cast(QObject *object)
void drawEllipse(const QPoint &center, int rx, int ry)
void drawText(const QPoint &position, const QString &text)
const QFont & font() const const
void setBrush(Qt::BrushStyle style)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
AlignCenter
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:49:05 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.