File indexing completed on 2024-04-28 03:49:28
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org> 0004 // 0005 0006 #include "RouteRequest.h" 0007 0008 #include "GeoDataLineString.h" 0009 #include "GeoDataPlacemark.h" 0010 #include "GeoDataData.h" 0011 #include "GeoDataExtendedData.h" 0012 #include "MarbleColors.h" 0013 #include "MarbleDirs.h" 0014 0015 #include <QMap> 0016 #include <QPainter> 0017 #include <QDebug> 0018 0019 namespace Marble 0020 { 0021 0022 struct PixmapElement 0023 { 0024 int index; 0025 0026 int size; 0027 0028 explicit PixmapElement( int index=-1, int size=0 ); 0029 0030 bool operator < ( const PixmapElement &other ) const; 0031 }; 0032 0033 class RouteRequestPrivate 0034 { 0035 public: 0036 QVector<GeoDataPlacemark> m_route; 0037 0038 QMap<PixmapElement, QPixmap> m_pixmapCache; 0039 0040 RoutingProfile m_routingProfile; 0041 0042 /** Determines a suitable index for inserting a via point */ 0043 int viaIndex( const GeoDataCoordinates &position ) const; 0044 }; 0045 0046 PixmapElement::PixmapElement( int index_, int size_ ) : 0047 index( index_ ), size( size_ ) 0048 { 0049 // nothing to do 0050 } 0051 0052 bool PixmapElement::operator <(const PixmapElement &other) const 0053 { 0054 return index < other.index || size < other.size; 0055 } 0056 0057 int RouteRequestPrivate::viaIndex( const GeoDataCoordinates &position ) const 0058 { 0059 /** @todo: Works, but does not look elegant at all */ 0060 0061 // Iterates over all ordered trip point pairs (P,Q) and finds the triple 0062 // (P,position,Q) or (P,Q,position) with minimum length 0063 qreal minLength = -1.0; 0064 int result = 0; 0065 GeoDataLineString viaFirst; 0066 GeoDataLineString viaSecond; 0067 for ( int i = 0; i < m_route.size(); ++i ) { 0068 Q_ASSERT( viaFirst.size() < 4 && viaSecond.size() < 4 ); 0069 if ( viaFirst.size() == 3 ) { 0070 viaFirst.remove( 0 ); 0071 viaFirst.remove( 0 ); 0072 } 0073 0074 if ( viaSecond.size() == 3 ) { 0075 viaSecond.remove( 0 ); 0076 viaSecond.remove( 0 ); 0077 } 0078 0079 if ( viaFirst.size() == 1 ) { 0080 viaFirst.append( position ); 0081 } 0082 0083 viaFirst.append( m_route[i].coordinate() ); 0084 viaSecond.append( m_route[i].coordinate() ); 0085 0086 if ( viaSecond.size() == 2 ) { 0087 viaSecond.append( position ); 0088 } 0089 0090 if ( viaFirst.size() == 3 ) { 0091 qreal len = viaFirst.length( EARTH_RADIUS ); 0092 if ( minLength < 0.0 || len < minLength ) { 0093 minLength = len; 0094 result = i; 0095 } 0096 } 0097 0098 /** @todo: Assumes that destination is the last point */ 0099 if ( viaSecond.size() == 3 && i + 1 < m_route.size() ) { 0100 qreal len = viaSecond.length( EARTH_RADIUS ); 0101 if ( minLength < 0.0 || len < minLength ) { 0102 minLength = len; 0103 result = i + 1; 0104 } 0105 } 0106 } 0107 0108 Q_ASSERT( 0 <= result && result <= m_route.size() ); 0109 return result; 0110 } 0111 0112 RouteRequest::RouteRequest( QObject *parent ) : 0113 QObject( parent ), d( new RouteRequestPrivate ) 0114 { 0115 // nothing to do 0116 } 0117 0118 RouteRequest::~RouteRequest() 0119 { 0120 delete d; 0121 } 0122 0123 int RouteRequest::size() const 0124 { 0125 return d->m_route.size(); 0126 } 0127 0128 GeoDataCoordinates RouteRequest::source() const 0129 { 0130 GeoDataCoordinates result; 0131 if ( d->m_route.size() ) { 0132 result = d->m_route.first().coordinate(); 0133 } 0134 return result; 0135 } 0136 0137 GeoDataCoordinates RouteRequest::destination() const 0138 { 0139 GeoDataCoordinates result; 0140 if ( d->m_route.size() ) { 0141 result = d->m_route.last().coordinate(); 0142 } 0143 return result; 0144 } 0145 0146 GeoDataCoordinates RouteRequest::at( int position ) const 0147 { 0148 return d->m_route.at( position ).coordinate(); 0149 } 0150 0151 QPixmap RouteRequest::pixmap(int position, int size, int margin ) const 0152 { 0153 PixmapElement const element( position, size ); 0154 0155 if ( !d->m_pixmapCache.contains( element ) ) { 0156 // Transparent background 0157 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 0158 int const imageSize = size > 0 ? size : ( smallScreen ? 32 : 16 ); 0159 QImage result( imageSize, imageSize, QImage::Format_ARGB32_Premultiplied ); 0160 result.fill( qRgba( 0, 0, 0, 0 ) ); 0161 0162 // Paint a colored circle 0163 QPainter painter( &result ); 0164 painter.setRenderHint( QPainter::Antialiasing, true ); 0165 painter.setPen( QColor( Qt::black ) ); 0166 bool const isVisited = visited( position ); 0167 QColor const backgroundColor = isVisited ? Oxygen::aluminumGray4 : Oxygen::forestGreen4; 0168 painter.setBrush( QBrush( backgroundColor ) ); 0169 painter.setPen( Qt::black ); 0170 int const iconSize = imageSize - 2 * margin; 0171 painter.drawEllipse( margin, margin, iconSize, iconSize ); 0172 0173 char const text = char( 'A' + position ); 0174 0175 // Choose a suitable font size 0176 QFont font = painter.font(); 0177 int fontSize = 20; 0178 while ( fontSize-- > 0 ) { 0179 font.setPointSize( fontSize ); 0180 QFontMetrics const fontMetric( font ); 0181 if ( fontMetric.horizontalAdvance( text ) <= iconSize && fontMetric.height( ) <= iconSize ) { 0182 break; 0183 } 0184 } 0185 0186 Q_ASSERT( fontSize ); 0187 font.setPointSize( fontSize ); 0188 painter.setFont( font ); 0189 0190 // Paint a character denoting the position (0=A, 1=B, 2=C, ...) 0191 painter.drawText( 0, 0, imageSize, imageSize, Qt::AlignCenter, QString( text ) ); 0192 0193 d->m_pixmapCache.insert( element, QPixmap::fromImage( result ) ); 0194 } 0195 0196 return d->m_pixmapCache[element]; 0197 } 0198 0199 void RouteRequest::clear() 0200 { 0201 for ( int i=d->m_route.size()-1; i>=0; --i ) { 0202 remove( i ); 0203 } 0204 } 0205 0206 void RouteRequest::insert( int index, const GeoDataCoordinates &coordinates, const QString &name ) 0207 { 0208 GeoDataPlacemark placemark; 0209 placemark.setCoordinate( coordinates ); 0210 placemark.setName( name ); 0211 insert(index, placemark); 0212 } 0213 0214 void RouteRequest::insert(int index, const GeoDataPlacemark &placemark) 0215 { 0216 d->m_route.insert( index, placemark ); 0217 emit positionAdded( index ); 0218 } 0219 0220 void RouteRequest::swap(int index1, int index2) 0221 { 0222 if (index1 < 0 || index2 < 0 || index1 > d->m_route.size()-1 || index2 > d->m_route.size()-1) { 0223 return; 0224 } 0225 0226 qSwap(d->m_route[index1], d->m_route[index2]); 0227 0228 emit positionChanged(index1, d->m_route[index1].coordinate()); 0229 emit positionChanged(index2, d->m_route[index2].coordinate()); 0230 } 0231 0232 void RouteRequest::append( const GeoDataCoordinates &coordinates, const QString &name ) 0233 { 0234 GeoDataPlacemark placemark; 0235 placemark.setCoordinate( coordinates ); 0236 placemark.setName( name ); 0237 append( placemark ); 0238 } 0239 0240 void RouteRequest::append( const GeoDataPlacemark &placemark ) 0241 { 0242 d->m_route.append( placemark ); 0243 emit positionAdded( d->m_route.size()-1 ); 0244 } 0245 0246 void RouteRequest::remove( int index ) 0247 { 0248 if ( index >= 0 && index < d->m_route.size() ) { 0249 d->m_route.remove( index ); 0250 emit positionRemoved( index ); 0251 } 0252 } 0253 0254 void RouteRequest::addVia( const GeoDataCoordinates &position ) 0255 { 0256 GeoDataPlacemark placemark; 0257 placemark.setCoordinate( position ); 0258 addVia(placemark); 0259 } 0260 0261 void RouteRequest::addVia(const GeoDataPlacemark &placemark) 0262 { 0263 int index = d->viaIndex( placemark.coordinate() ); 0264 d->m_route.insert( index, placemark ); 0265 emit positionAdded( index ); 0266 } 0267 0268 void RouteRequest::setPosition( int index, const GeoDataCoordinates &position, const QString &name ) 0269 { 0270 if ( index >= 0 && index < d->m_route.size() ) { 0271 d->m_route[index].setName( name ); 0272 if ( d->m_route[index].coordinate() != position ) { 0273 d->m_route[index].setCoordinate( position ); 0274 setVisited( index, false ); 0275 emit positionChanged( index, position ); 0276 } 0277 } 0278 } 0279 0280 void RouteRequest::setName( int index, const QString &name ) 0281 { 0282 if ( index >= 0 && index < d->m_route.size() ) { 0283 d->m_route[index].setName( name ); 0284 } 0285 } 0286 0287 QString RouteRequest::name( int index ) const 0288 { 0289 QString result; 0290 if ( index >= 0 && index < d->m_route.size() ) { 0291 result = d->m_route[index].name(); 0292 } 0293 return result; 0294 } 0295 0296 void RouteRequest::setVisited( int index, bool visited ) 0297 { 0298 if ( index >= 0 && index < d->m_route.size() ) { 0299 d->m_route[index].extendedData().addValue(GeoDataData(QStringLiteral("routingVisited"), visited)); 0300 QMap<PixmapElement, QPixmap>::iterator iter = d->m_pixmapCache.begin(); 0301 while ( iter != d->m_pixmapCache.end() ) { 0302 if ( iter.key().index == index ) { 0303 iter = d->m_pixmapCache.erase( iter ); 0304 } else { 0305 ++iter; 0306 } 0307 } 0308 emit positionChanged( index, d->m_route[index].coordinate() ); 0309 } 0310 } 0311 0312 bool RouteRequest::visited( int index ) const 0313 { 0314 bool visited = false; 0315 if ( index >= 0 && index < d->m_route.size() ) { 0316 if (d->m_route[index].extendedData().contains(QStringLiteral("routingVisited"))) { 0317 visited = d->m_route[index].extendedData().value(QStringLiteral("routingVisited")).value().toBool(); 0318 } 0319 } 0320 return visited; 0321 } 0322 0323 void RouteRequest::reverse() 0324 { 0325 std::reverse(d->m_route.begin(), d->m_route.end()); 0326 int const total = d->m_route.size(); 0327 for (int i = 0; i < total; ++i) { 0328 setVisited( i, false ); 0329 } 0330 } 0331 0332 void RouteRequest::setRoutingProfile( const RoutingProfile &profile ) 0333 { 0334 d->m_routingProfile = profile; 0335 emit routingProfileChanged(); 0336 } 0337 0338 RoutingProfile RouteRequest::routingProfile() const 0339 { 0340 return d->m_routingProfile; 0341 } 0342 0343 GeoDataPlacemark &RouteRequest::operator []( int index ) 0344 { 0345 return d->m_route[index]; 0346 } 0347 0348 const GeoDataPlacemark &RouteRequest::operator [](int index) const 0349 { 0350 return d->m_route[index]; 0351 } 0352 0353 } // namespace Marble 0354 0355 0356 #include "moc_RouteRequest.cpp"