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 "GeoDataData.h"
9#include "GeoDataExtendedData.h"
10#include "GeoDataLineString.h"
11#include "GeoDataPlacemark.h"
12#include "MarbleColors.h"
13#include "MarbleDirs.h"
14
15#include <QDebug>
16#include <QMap>
17#include <QPainter>
18
19namespace Marble
20{
21
22struct PixmapElement {
23 int index;
24
25 int size;
26
27 explicit PixmapElement(int index = -1, int size = 0);
28
29 bool operator<(const PixmapElement &other) const;
30};
31
32class RouteRequestPrivate
33{
34public:
36
37 QMap<PixmapElement, QPixmap> m_pixmapCache;
38
39 RoutingProfile m_routingProfile;
40
41 /** Determines a suitable index for inserting a via point */
42 int viaIndex(const GeoDataCoordinates &position) const;
43};
44
45PixmapElement::PixmapElement(int index_, int size_)
46 : index(index_)
47 , 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)
114 , d(new RouteRequestPrivate)
115{
116 // nothing to do
117}
118
120{
121 delete d;
122}
123
125{
126 return d->m_route.size();
127}
128
130{
131 GeoDataCoordinates result;
132 if (d->m_route.size()) {
133 result = d->m_route.first().coordinate();
134 }
135 return result;
136}
137
139{
140 GeoDataCoordinates result;
141 if (d->m_route.size()) {
142 result = d->m_route.last().coordinate();
143 }
144 return result;
145}
146
148{
149 return d->m_route.at(position).coordinate();
150}
151
152QPixmap RouteRequest::pixmap(int position, int size, int margin) const
153{
154 PixmapElement const element(position, size);
155
156 if (!d->m_pixmapCache.contains(element)) {
157 // Transparent background
158 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
159 int const imageSize = size > 0 ? size : (smallScreen ? 32 : 16);
160 QImage result(imageSize, imageSize, QImage::Format_ARGB32_Premultiplied);
161 result.fill(qRgba(0, 0, 0, 0));
162
163 // Paint a colored circle
164 QPainter painter(&result);
166 painter.setPen(QColor(Qt::black));
167 bool const isVisited = visited(position);
168 QColor const backgroundColor = isVisited ? Oxygen::aluminumGray4 : Oxygen::forestGreen4;
169 painter.setBrush(QBrush(backgroundColor));
170 painter.setPen(Qt::black);
171 int const iconSize = imageSize - 2 * margin;
172 painter.drawEllipse(margin, margin, iconSize, iconSize);
173
174 char const text = char('A' + position);
175
176 // Choose a suitable font size
177 QFont font = painter.font();
178 int fontSize = 20;
179 while (fontSize-- > 0) {
180 font.setPointSize(fontSize);
181 QFontMetrics const fontMetric(font);
182 if (fontMetric.horizontalAdvance(QString(text)) <= iconSize && fontMetric.height() <= iconSize) {
183 break;
184 }
185 }
186
187 Q_ASSERT(fontSize);
188 font.setPointSize(fontSize);
189 painter.setFont(font);
190
191 // Paint a character denoting the position (0=A, 1=B, 2=C, ...)
192 painter.drawText(0, 0, imageSize, imageSize, Qt::AlignCenter, QString(text));
193
194 d->m_pixmapCache.insert(element, QPixmap::fromImage(result));
195 }
196
197 return d->m_pixmapCache[element];
198}
199
201{
202 for (int i = d->m_route.size() - 1; i >= 0; --i) {
203 remove(i);
204 }
205}
206
207void RouteRequest::insert(int index, const GeoDataCoordinates &coordinates, const QString &name)
208{
209 GeoDataPlacemark placemark;
210 placemark.setCoordinate(coordinates);
211 placemark.setName(name);
212 insert(index, placemark);
213}
214
215void RouteRequest::insert(int index, const GeoDataPlacemark &placemark)
216{
217 d->m_route.insert(index, placemark);
218 Q_EMIT positionAdded(index);
219}
220
221void RouteRequest::swap(int index1, int index2)
222{
223 if (index1 < 0 || index2 < 0 || index1 > d->m_route.size() - 1 || index2 > d->m_route.size() - 1) {
224 return;
225 }
226
227 qSwap(d->m_route[index1], d->m_route[index2]);
228
229 Q_EMIT positionChanged(index1, d->m_route[index1].coordinate());
230 Q_EMIT positionChanged(index2, d->m_route[index2].coordinate());
231}
232
233void RouteRequest::append(const GeoDataCoordinates &coordinates, const QString &name)
234{
235 GeoDataPlacemark placemark;
236 placemark.setCoordinate(coordinates);
237 placemark.setName(name);
238 append(placemark);
239}
240
241void RouteRequest::append(const GeoDataPlacemark &placemark)
242{
243 d->m_route.append(placemark);
244 Q_EMIT positionAdded(d->m_route.size() - 1);
245}
246
247void RouteRequest::remove(int index)
248{
249 if (index >= 0 && index < d->m_route.size()) {
250 d->m_route.remove(index);
251 Q_EMIT positionRemoved(index);
252 }
253}
254
256{
257 GeoDataPlacemark placemark;
258 placemark.setCoordinate(position);
259 addVia(placemark);
260}
261
262void RouteRequest::addVia(const GeoDataPlacemark &placemark)
263{
264 int index = d->viaIndex(placemark.coordinate());
265 d->m_route.insert(index, placemark);
266 Q_EMIT positionAdded(index);
267}
268
269void RouteRequest::setPosition(int index, const GeoDataCoordinates &position, const QString &name)
270{
271 if (index >= 0 && index < d->m_route.size()) {
272 d->m_route[index].setName(name);
273 if (d->m_route[index].coordinate() != position) {
274 d->m_route[index].setCoordinate(position);
275 setVisited(index, false);
276 Q_EMIT positionChanged(index, position);
277 }
278 }
279}
280
281void RouteRequest::setName(int index, const QString &name)
282{
283 if (index >= 0 && index < d->m_route.size()) {
284 d->m_route[index].setName(name);
285 }
286}
287
288QString RouteRequest::name(int index) const
289{
290 QString result;
291 if (index >= 0 && index < d->m_route.size()) {
292 result = d->m_route[index].name();
293 }
294 return result;
295}
296
297void RouteRequest::setVisited(int index, bool visited)
298{
299 if (index >= 0 && index < d->m_route.size()) {
300 d->m_route[index].extendedData().addValue(GeoDataData(QStringLiteral("routingVisited"), visited));
301 QMap<PixmapElement, QPixmap>::iterator iter = d->m_pixmapCache.begin();
302 while (iter != d->m_pixmapCache.end()) {
303 if (iter.key().index == index) {
304 iter = d->m_pixmapCache.erase(iter);
305 } else {
306 ++iter;
307 }
308 }
309 Q_EMIT positionChanged(index, d->m_route[index].coordinate());
310 }
311}
312
313bool RouteRequest::visited(int index) const
314{
315 bool visited = false;
316 if (index >= 0 && index < d->m_route.size()) {
317 if (d->m_route[index].extendedData().contains(QStringLiteral("routingVisited"))) {
318 visited = d->m_route[index].extendedData().value(QStringLiteral("routingVisited")).value().toBool();
319 }
320 }
321 return visited;
322}
323
324void RouteRequest::reverse()
325{
326 std::reverse(d->m_route.begin(), d->m_route.end());
327 int const total = d->m_route.size();
328 for (int i = 0; i < total; ++i) {
329 setVisited(i, false);
330 }
331}
332
333void RouteRequest::setRoutingProfile(const RoutingProfile &profile)
334{
335 d->m_routingProfile = profile;
337}
338
339RoutingProfile RouteRequest::routingProfile() const
340{
341 return d->m_routingProfile;
342}
343
344GeoDataPlacemark &RouteRequest::operator[](int index)
345{
346 return d->m_route[index];
347}
348
349const GeoDataPlacemark &RouteRequest::operator[](int index) const
350{
351 return d->m_route[index];
352}
353
354} // namespace Marble
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)
int height() const const
int horizontalAdvance(QChar ch) const const
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)
Q_EMITQ_EMIT
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 Mon Nov 18 2024 12:15:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.