File indexing completed on 2025-04-20 06:39:23
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org> 0004 // SPDX-FileCopyrightText: 2010 Niko Sams <niko.sams@gmail.com> 0005 // 0006 0007 #include "RoutinoRunner.h" 0008 0009 #include "MarbleDebug.h" 0010 #include "MarbleDirs.h" 0011 #include "routing/RouteRequest.h" 0012 #include "routing/instructions/WaypointParser.h" 0013 #include "routing/instructions/InstructionTransformation.h" 0014 #include "GeoDataDocument.h" 0015 #include "GeoDataExtendedData.h" 0016 #include "GeoDataData.h" 0017 #include "GeoDataPlacemark.h" 0018 #include "GeoDataLineString.h" 0019 0020 #include <QProcess> 0021 #include <QTemporaryFile> 0022 #include <MarbleMap.h> 0023 #include <MarbleModel.h> 0024 #include <routing/RoutingManager.h> 0025 #include <routing/RoutingProfilesModel.h> 0026 0027 namespace Marble 0028 { 0029 0030 class RoutinoRunnerPrivate 0031 { 0032 public: 0033 QDir m_mapDir; 0034 0035 WaypointParser m_parser; 0036 0037 QByteArray retrieveWaypoints( const QStringList ¶ms ) const; 0038 0039 static GeoDataDocument* createDocument( GeoDataLineString* routeWaypoints, const QVector<GeoDataPlacemark*> instructions ); 0040 0041 static GeoDataLineString* parseRoutinoOutput( const QByteArray &content ); 0042 0043 QVector<GeoDataPlacemark*> parseRoutinoInstructions( const QByteArray &content ) const; 0044 0045 RoutinoRunnerPrivate(); 0046 }; 0047 0048 RoutinoRunnerPrivate::RoutinoRunnerPrivate() 0049 { 0050 m_parser.setLineSeparator("\n"); 0051 m_parser.setFieldSeparator(QLatin1Char('\t')); 0052 m_parser.setFieldIndex( WaypointParser::RoadName, 10 ); 0053 } 0054 0055 class TemporaryDir 0056 { 0057 public: 0058 TemporaryDir() { 0059 QTemporaryFile f; 0060 f.setAutoRemove( false ); 0061 f.open(); 0062 m_dirName = f.fileName(); 0063 f.close(); 0064 f.remove(); 0065 QFileInfo( m_dirName ).dir().mkdir( m_dirName ); 0066 } 0067 0068 ~TemporaryDir() { 0069 QDir dir( m_dirName ); 0070 QFileInfoList entries = dir.entryInfoList( QDir::Files ); 0071 for ( const QFileInfo &file: entries ) { 0072 QFile( file.absoluteFilePath() ).remove(); 0073 } 0074 dir.rmdir( dir.absolutePath() ); 0075 } 0076 0077 QString dirName() const 0078 { 0079 return m_dirName; 0080 } 0081 private: 0082 QString m_dirName; 0083 }; 0084 0085 QByteArray RoutinoRunnerPrivate::retrieveWaypoints( const QStringList ¶ms ) const 0086 { 0087 TemporaryDir dir; 0088 QProcess routinoProcess; 0089 routinoProcess.setWorkingDirectory( dir.dirName() ); 0090 0091 QStringList routinoParams; 0092 routinoParams << params; 0093 routinoParams << QLatin1String("--dir=") + m_mapDir.absolutePath(); 0094 routinoParams << "--output-text-all"; 0095 mDebug() << routinoParams; 0096 routinoProcess.start( "routino-router", routinoParams ); 0097 if ( !routinoProcess.waitForStarted( 5000 ) ) { 0098 mDebug() << "Couldn't start routino-router from the current PATH. Install it to retrieve routing results from routino."; 0099 return nullptr; 0100 } 0101 0102 if ( routinoProcess.waitForFinished(60 * 1000) ) { 0103 mDebug() << routinoProcess.readAll(); 0104 mDebug() << "routino finished"; 0105 QFile file(routinoProcess.workingDirectory() + QLatin1String("/shortest-all.txt")); 0106 if ( !file.exists() ) { 0107 file.setFileName(routinoProcess.workingDirectory() + QLatin1String("/quickest-all.txt")); 0108 } 0109 if ( !file.exists() ) { 0110 mDebug() << "Can't get results"; 0111 } else { 0112 file.open( QIODevice::ReadOnly ); 0113 return file.readAll(); 0114 } 0115 } 0116 else { 0117 mDebug() << "Couldn't stop routino"; 0118 } 0119 return nullptr; 0120 } 0121 0122 GeoDataLineString* RoutinoRunnerPrivate::parseRoutinoOutput( const QByteArray &content ) 0123 { 0124 GeoDataLineString* routeWaypoints = new GeoDataLineString; 0125 0126 const QStringList lines = QString::fromUtf8(content).split(QLatin1Char('\n')); 0127 mDebug() << lines.count() << "lines"; 0128 for( const QString &line: lines ) { 0129 if (line.startsWith(QLatin1Char('#'))) { 0130 //skip comment 0131 continue; 0132 } 0133 const QStringList fields = line.split(QLatin1Char('\t')); 0134 if ( fields.size() >= 10 ) { 0135 qreal lon = fields.at(1).trimmed().toDouble(); 0136 qreal lat = fields.at(0).trimmed().toDouble(); 0137 GeoDataCoordinates coordinates( lon, lat, 0.0, GeoDataCoordinates::Degree ); 0138 routeWaypoints->append( coordinates ); 0139 } 0140 } 0141 0142 return routeWaypoints; 0143 } 0144 0145 QVector<GeoDataPlacemark*> RoutinoRunnerPrivate::parseRoutinoInstructions( const QByteArray &content ) const 0146 { 0147 QVector<GeoDataPlacemark*> result; 0148 QTextStream stream( content ); 0149 stream.setCodec("UTF8"); 0150 stream.setAutoDetectUnicode( true ); 0151 0152 RoutingInstructions directions = InstructionTransformation::process( m_parser.parse( stream ) ); 0153 for( int i=0; i<directions.size(); ++i ) { 0154 GeoDataPlacemark* placemark = new GeoDataPlacemark( directions[i].instructionText() ); 0155 GeoDataExtendedData extendedData; 0156 GeoDataData turnType; 0157 turnType.setName(QStringLiteral("turnType")); 0158 turnType.setValue( QVariant::fromValue( int( directions[i].turnType() ) ) ); 0159 extendedData.addValue( turnType ); 0160 GeoDataData roadName; 0161 roadName.setName(QStringLiteral("roadName")); 0162 roadName.setValue( directions[i].roadName() ); 0163 extendedData.addValue( roadName ); 0164 placemark->setExtendedData( extendedData ); 0165 Q_ASSERT( !directions[i].points().isEmpty() ); 0166 GeoDataLineString* geometry = new GeoDataLineString; 0167 QVector<RoutingWaypoint> items = directions[i].points(); 0168 for (int j=0; j<items.size(); ++j ) { 0169 RoutingPoint point = items[j].point(); 0170 GeoDataCoordinates coordinates( point.lon(), point.lat(), 0.0, GeoDataCoordinates::Degree ); 0171 geometry->append( coordinates ); 0172 } 0173 placemark->setGeometry( geometry ); 0174 result.push_back( placemark ); 0175 } 0176 0177 return result; 0178 } 0179 0180 GeoDataDocument* RoutinoRunnerPrivate::createDocument( GeoDataLineString* routeWaypoints, const QVector<GeoDataPlacemark*> instructions ) 0181 { 0182 if ( !routeWaypoints || routeWaypoints->isEmpty() ) { 0183 return nullptr; 0184 } 0185 0186 GeoDataDocument* result = new GeoDataDocument(); 0187 GeoDataPlacemark* routePlacemark = new GeoDataPlacemark; 0188 routePlacemark->setName( "Route" ); 0189 routePlacemark->setGeometry( routeWaypoints ); 0190 result->append( routePlacemark ); 0191 0192 QString name = "%1 %2 (Routino)"; 0193 QString unit = QLatin1String( "m" ); 0194 qreal length = routeWaypoints->length( EARTH_RADIUS ); 0195 if (length >= 1000) { 0196 length /= 1000.0; 0197 unit = "km"; 0198 } 0199 result->setName( name.arg( length, 0, 'f', 1 ).arg( unit ) ); 0200 0201 for( GeoDataPlacemark* placemark: instructions ) 0202 { 0203 result->append( placemark ); 0204 } 0205 0206 return result; 0207 } 0208 0209 RoutinoRunner::RoutinoRunner( QObject *parent ) : 0210 RoutingRunner( parent ), 0211 d( new RoutinoRunnerPrivate ) 0212 { 0213 // Check installation 0214 d->m_mapDir = QDir(MarbleDirs::localPath() + QLatin1String("/maps/earth/routino/")); 0215 } 0216 0217 RoutinoRunner::~RoutinoRunner() 0218 { 0219 delete d; 0220 } 0221 0222 void RoutinoRunner::retrieveRoute( const RouteRequest *route ) 0223 { 0224 mDebug(); 0225 0226 if ( ! QFileInfo( d->m_mapDir, "nodes.mem" ).exists() ) 0227 { 0228 emit routeCalculated( nullptr ); 0229 return; 0230 } 0231 0232 QStringList params; 0233 for( int i=0; i<route->size(); ++i ) 0234 { 0235 double fLon = route->at(i).longitude( GeoDataCoordinates::Degree ); 0236 double fLat = route->at(i).latitude( GeoDataCoordinates::Degree ); 0237 params << QString("--lat%1=%2").arg(i+1).arg(fLat, 0, 'f', 8); 0238 params << QString("--lon%1=%2").arg(i+1).arg(fLon, 0, 'f', 8); 0239 } 0240 0241 QHash<QString, QVariant> settings = route->routingProfile().pluginSettings()[QStringLiteral("routino")]; 0242 QString transport = settings[QStringLiteral("transport")].toString(); 0243 params << QString( "--transport=%0" ).arg( transport ); 0244 0245 if (settings[QStringLiteral("method")] == QLatin1String("shortest")) { 0246 params << "--shortest"; 0247 } else { 0248 params << "--quickest"; 0249 } 0250 /* 0251 if ( route->avoidFeatures() & RouteRequest::AvoidHighway ) { 0252 params << "--highway-motorway=0"; 0253 } 0254 */ 0255 0256 QByteArray output = d->retrieveWaypoints( params ); 0257 GeoDataLineString* wayPoints = d->parseRoutinoOutput( output ); 0258 QVector<GeoDataPlacemark*> instructions = d->parseRoutinoInstructions( output ); 0259 0260 GeoDataDocument* result = d->createDocument( wayPoints, instructions ); 0261 mDebug() << this << "routeCalculated"; 0262 emit routeCalculated( result ); 0263 } 0264 0265 } // namespace Marble 0266 0267 #include "moc_RoutinoRunner.cpp"