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"