Hi all,

in OSRM plugin the api v5 implementation is only valid for qt 5 .
Also instructions like before are missing.

i am working on qt 4.8 , because of this i implemented api v5 for 
OsrmRunner.cpp ,
find attached working code, maybe someone needs or like to merge to qt5,

br, konrad
//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2012      Dennis Nienhüser <[email protected]>
//

#include "OSRMRunner.h"

#include "MarbleAbstractRunner.h"
#include "MarbleDebug.h"
#include "MarbleLocale.h"
#include "GeoDataDocument.h"
#include "GeoDataPlacemark.h"
#include "GeoDataExtendedData.h"
#include "routing/Maneuver.h"
#include "TinyWebBrowser.h"

#include <QtCore/QString>
#include <QtCore/QVector>
#include <QtCore/QUrl>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtScript/QScriptValue>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptValueIterator>

namespace Marble
{

QVector<QPair<GeoDataCoordinates,QString> > OSRMRunner:: m_cachedHints;

QString OSRMRunner:: m_hintChecksum;

OSRMRunner::OSRMRunner( QObject *parent ) :
    MarbleAbstractRunner( parent ),
    m_networkAccessManager( new QNetworkAccessManager( this ) )
{
    connect( m_networkAccessManager, SIGNAL( finished( QNetworkReply * ) ),
             this, SLOT( retrieveData( QNetworkReply * ) ) );
}

OSRMRunner::~OSRMRunner()
{
    // nothing to do
}

GeoDataFeature::GeoDataVisualCategory OSRMRunner::category() const
{
    return GeoDataFeature::OsmSite;
}

void OSRMRunner::retrieveRoute(const RouteRequest *route)
{
        if (route->size() < 2) {
                return;

        }

        QString url = "http://router.project-osrm.org/route/v1/driving/";;
        GeoDataCoordinates::Unit const degree = GeoDataCoordinates::Degree;
        for (int i = 0; i<route->size(); ++i) {
                GeoDataCoordinates const coordinates = route->at(i);
                url += QString::number(coordinates.longitude(degree), 'f', 6);
                url += ',';
                url += QString::number(coordinates.latitude(degree), 'f', 6);
                if (i + 1<route->size()) {
                        url += ';';

                }

        }

        //add options
        url += 
"?alternatives=false&overview=full&geometries=polyline6&steps=true";

        QNetworkRequest request = QNetworkRequest(QUrl(url));
        request.setRawHeader("User-Agent", TinyWebBrowser::userAgent("Browser", 
"OSRMRunner"));
        QNetworkReply *reply = m_networkAccessManager->get(request);
        connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
                this, SLOT(handleError(QNetworkReply::NetworkError)));

        QEventLoop eventLoop;

        QTimer timer;
        timer.setSingleShot(true);
        timer.setInterval(15000);

        connect(&timer, SIGNAL(timeout()),
                &eventLoop, SLOT(quit()));
        connect(this, SIGNAL(routeCalculated(GeoDataDocument*)),
                &eventLoop, SLOT(quit()));

        // @todo FIXME Must currently be done in the main thread, see bug 257376
        QTimer::singleShot(0, this, SLOT(get()));
        timer.start();

        eventLoop.exec();
}

void OSRMRunner::retrieveData( QNetworkReply *reply )
{
    if ( reply->isFinished() ) {
        QByteArray data = reply->readAll();
        reply->deleteLater();
        GeoDataDocument* document = parse( data );

        if ( !document ) {
            mDebug() << "Failed to parse the downloaded route data" << data;
        }

        emit routeCalculated( document );
    }
}

void OSRMRunner::handleError( QNetworkReply::NetworkError error )
{
    mDebug() << " Error when retrieving OSRM route: " << error;
}

void OSRMRunner::append(QString *input, const QString &key, const QString 
&value) const
{
        *input += QLatin1Char('&') + key + QLatin1Char('=') + value;
}

GeoDataLineString *OSRMRunner::decodePolyline( const QString &geometry ) const
{
    // See 
https://developers.google.com/maps/documentation/utilities/polylinealgorithm
    GeoDataLineString* lineString = new GeoDataLineString;
    int coordinates[2] = { 0, 0 };
    int const length = geometry.length();
    for( int i=0; i<length; /* increment happens below */ ) {
        for ( int j=0; j<2; ++j ) { // lat and lon
            int block( 0 ), shift( 0 ), result( 0 );
            do {
                block = geometry.at( i++ /* increment for outer loop */ 
).toAscii() - 63;
                result |= ( block & 0x1F ) << shift;
                shift += 5;
            } while ( block >= 0x20 );
            coordinates[j] += ( ( result & 1 ) != 0 ? ~( result >> 1 ) : ( 
result >> 1 ) );
        }
                //enz
                int value = coordinates[1];
                if(1)//value>10000000)
                        lineString->append( GeoDataCoordinates( double( 
coordinates[1] ) / 1E6,
                                                double( coordinates[0] ) / 1E6,
                                                0.0, GeoDataCoordinates::Degree 
) );
                else
                        lineString->append( GeoDataCoordinates( double( 
coordinates[1] ) / 1E5,
                                                double( coordinates[0] ) / 1E5,
                                                0.0, GeoDataCoordinates::Degree 
) );
    }
    return lineString;
}
/** enz: changed parsing related to new OSRM api */
RoutingInstruction::TurnType OSRMRunner::parseTurnType( const QString 
&instruction ) const
{   
    if ( instruction == "straight" ) {
        return RoutingInstruction::Straight;
    } else if ( instruction == "slight right" ) {
        return RoutingInstruction::SlightRight;
    } else if ( instruction == "right" ) {
        return RoutingInstruction::Right;
    } else if ( instruction == "sharp right" ) {
        return RoutingInstruction::SharpRight;
    } else if ( instruction == "turn around" ) {
        return RoutingInstruction::TurnAround;
    } 
        else if (instruction == "turn") {
         return RoutingInstruction::TurnAround;
        }
        else if (instruction == "sharp left") {
        return RoutingInstruction::SharpLeft;
    } else if ( instruction == "left" ) {
        return RoutingInstruction::Left;
    } else if ( instruction == "slight left" ) {
        return RoutingInstruction::SlightLeft;
    } else if ( instruction == "continue" ) {
        return RoutingInstruction::Continue;
    } else if ( instruction == "roundabout first exit"  ){
         return RoutingInstruction::RoundaboutFirstExit;
        }
        else if (instruction == "roundabout second exit"){
                return RoutingInstruction::RoundaboutSecondExit;
        }
        else if (instruction == "roundabout third exit"){
                return RoutingInstruction::RoundaboutThirdExit;
        }
        else if (instruction == "roundabout exit"){
        return RoutingInstruction::RoundaboutExit;
        
    }

    // ignoring ReachViaPoint = 9;
    // ignoring StayOnRoundAbout = 13;
    // ignoring StartAtEndOfStreet = 14;
    // ignoring ReachedYourDestination = 15;

    return RoutingInstruction::Unknown;
}

/** enz: completely changed parsing related to new OSRM api v5 */
GeoDataDocument *OSRMRunner::parse(const QByteArray &input)
{
        QScriptEngine engine;
        // Qt requires parentheses around json code
        QScriptValue const data = engine.evaluate("(" + 
QString::fromUtf8(input) + ")");

        GeoDataDocument* result = 0;
        bool ret = data.property("routes").isArray();
        if (!ret)return result;

        result = new GeoDataDocument();
        if (result == NULL)return result;
        result->setName("Open Source Routing Machine");
        GeoDataPlacemark* routePlacemark = new GeoDataPlacemark;
        routePlacemark->setName("Route");
        QScriptValue rt = data.property("routes").property("0");
        GeoDataLineString* routeWaypoints = 
decodePolyline(rt.property("geometry").toString());
        routePlacemark->setGeometry(routeWaypoints);

        qreal duration = 
data.property("routes").property("0").property("duration").toNumber();
        qreal distance = 
data.property("routes").property("0").property("distance").toNumber();

        QString tunit = "min";
        duration /= 60.0;//min
        if (duration > 60.0)
        {
                duration /= 60.0;//h
                tunit = "h";
        }

        QString name = "%1 %2 %3 %4 (OSRM)";
        QString unit = "m";
        qreal length = routeWaypoints->length(EARTH_RADIUS);
        if (length >= 1000) {
                length /= 1000.0;
                unit = "km";
        }
        result->setName(name.arg(length, 0, 'f', 1).arg(unit).arg(duration, 0, 
'f', 1).arg(tunit));
        result->append(routePlacemark);

        if (routeWaypoints)
        {
                int x = routeWaypoints->size();
                if (x < 2)return result;
        }
        else return result;

        if (data.property("waypoints").isArray())
        {
                QScriptValue wp = data.property("waypoints");
                QVariantList details = wp.toVariant().toList();
                int wpcount = details.count();
                for (int wpx = 0; wpx < wpcount; wpx++)
                {
                        bool first = true;
                        GeoDataCoordinates pos1;
                        QScriptValue leg = 
data.property("routes").property("0").property("legs").property(wpx).property("steps");
                        bool ret = leg.isArray();
                        QVariantList details = leg.toVariant().toList();
                        int count = details.count();
                        for (int i = 0; i < count; i++)
                        {
                                //GeoDataLineString *lineString = new 
GeoDataLineString;
                                GeoDataPlacemark* instruction = new 
GeoDataPlacemark;

                                QScriptValue step = leg.property(i);
                                QString road = step.property("name").toString();
                                QScriptValue man = step.property("maneuver");
                                QString type = man.property("type").toString();
                                QString mod = 
man.property("modifier").toString();
                                int exit=-1;
                                if (type == "roundabout")
                                {
                                        exit = man.property("exit").toInteger();
                                        switch (exit)
                                        {
                                                case 1: mod = "roundabout first 
exit"; break;
                                                case 2: mod = "roundabout 
second exit"; break;
                                                case 3: mod = "roundabout third 
exit"; break;
                                                default: mod = "roundabout 
exit"; break;
                                        }
                                }

                                qreal lon = 
man.property("location").property("0").toNumber();
                                qreal lat = 
man.property("location").property("1").toNumber();
                                GeoDataCoordinates pos(lon, lat, 0, 
GeoDataCoordinates::Degree);
                                if (1)
                                {
                                        QString geom = 
step.property("geometry").toString();
                                        GeoDataLineString *lineString = 
decodePolyline(geom);
                                        pos1 = pos;
                                        instruction->setGeometry(lineString);
                                        //      result->append(instruction);
                                        //      instruction = new 
GeoDataPlacemark;
                                }

                                GeoDataExtendedData extendedData;
                                GeoDataData turnTypeData;
                                turnTypeData.setName("turnType");
                                RoutingInstruction::TurnType turnType = 
parseTurnType(mod);
                                turnTypeData.setValue(turnType);
                                extendedData.addValue(turnTypeData);
                                if (!road.isEmpty()) {
                                        GeoDataData roadName;
                                        roadName.setName("roadName");
                                        roadName.setValue(road);
                                        extendedData.addValue(roadName);
                                }

                                if (first) {
                                        turnType = RoutingInstruction::Continue;
                                        first = false;
                                }

                                if (turnType == RoutingInstruction::Unknown) {
                                        instruction->setName(mod);
                                }
                                else {
                                        
instruction->setName(RoutingInstruction::generateRoadInstruction(turnType, 
road));
                                }
                                instruction->setExtendedData(extendedData);
                                result->append(instruction);
                        }//for steps count
                }//for legs count
        }

        return result;
}

} // namespace Marble

#include "moc_OSRMRunner.cpp"

Reply via email to