Marble

RouteRequest.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <[email protected]>
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 
19 namespace Marble
20 {
21 
22 struct 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 
33 class RouteRequestPrivate
34 {
35 public:
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 
46 PixmapElement::PixmapElement( int index_, int size_ ) :
47  index( index_ ), size( size_ )
48 {
49  // nothing to do
50 }
51 
52 bool PixmapElement::operator <(const PixmapElement &other) const
53 {
54  return index < other.index || size < other.size;
55 }
56 
57 int 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 
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 
151 QPixmap 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 
206 void 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 
214 void RouteRequest::insert(int index, const GeoDataPlacemark &placemark)
215 {
216  d->m_route.insert( index, placemark );
217  emit positionAdded( index );
218 }
219 
220 void 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 
232 void 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 
240 void RouteRequest::append( const GeoDataPlacemark &placemark )
241 {
242  d->m_route.append( placemark );
243  emit positionAdded( d->m_route.size()-1 );
244 }
245 
246 void 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 
261 void 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 
268 void 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 
280 void 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 
287 QString 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 
296 void 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));
300  QMap<PixmapElement, QPixmap>::iterator iter = d->m_pixmapCache.begin();
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 
312 bool 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 
323 void 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 
332 void RouteRequest::setRoutingProfile( const RoutingProfile &profile )
333 {
334  d->m_routingProfile = profile;
335  emit routingProfileChanged();
336 }
337 
338 RoutingProfile RouteRequest::routingProfile() const
339 {
340  return d->m_routingProfile;
341 }
342 
343 GeoDataPlacemark &RouteRequest::operator []( int index )
344 {
345  return d->m_route[index];
346 }
347 
348 const 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.
AlignCenter
void setPen(const QColor &color)
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
Format_ARGB32_Premultiplied
void fill(uint pixelValue)
void drawEllipse(const QRectF &rectangle)
GeoDataCoordinates destination() const
The last point, or a default constructed if empty.
void setPointSize(int pointSize)
int horizontalAdvance(const QString &text, int len) const const
void setName(const QString &value)
Set a new name for this feature.
void drawText(const QPointF &position, const QString &text)
~RouteRequest() override
Destructor.
void setPosition(int index, const GeoDataCoordinates &position, const QString &name=QString())
Change the value of the element at the given position.
void positionChanged(int index, const GeoDataCoordinates &position)
The value of the n-th element was changed.
void positionAdded(int index)
An element was added at the given position.
RouteRequest(QObject *parent=nullptr)
Constructor.
GeoDataCoordinates coordinate(const QDateTime &dateTime=QDateTime(), bool *iconAtCoordinates=nullptr) const
Return the coordinates of the placemark at time dateTime as a GeoDataCoordinates.
void routingProfileChanged()
The routing profile was changed.
void remove(int index)
Remove the element at the given position.
Binds a QML item to a specific geodetic location in screen coordinates.
void swap(int index1, int index2)
Swaps the given elements at the given positions.
void setBrush(const QBrush &brush)
const Key key(const T &value, const Key &defaultKey) const const
a class representing a point of interest on the map
void append(const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element to the end.
void clear()
Remove all elements.
void addVia(const GeoDataCoordinates &position)
Insert a via point.
void positionRemoved(int index)
The element at the given position was removed.
const QFont & font() const const
QPixmap pixmap(int index, int size=-1, int margin=2) const
Returns a pixmap which indicates the position of the element.
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 at(int index) const
Accessor for the n-th position.
void setRenderHint(QPainter::RenderHint hint, bool on)
void setFont(const QFont &font)
int height() const const
void insert(int index, const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element at the given position.
int size() const
Number of points in the route.
GeoDataCoordinates source() const
The first point, or a default constructed if empty.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 21 2023 04:12:28 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.