6#include "AlternativeRoutesModel.h" 
    8#include "GeoDataDocument.h" 
    9#include "GeoDataExtendedData.h" 
   10#include "GeoDataFolder.h" 
   11#include "GeoDataLatLonAltBox.h" 
   12#include "GeoDataLineString.h" 
   13#include "GeoDataPlacemark.h" 
   15#include <QElapsedTimer> 
   22class Q_DECL_HIDDEN AlternativeRoutesModel::Private
 
   30    bool filter(
const GeoDataDocument *document) 
const;
 
   39    static qreal similarity(
const GeoDataDocument *routeA, 
const GeoDataDocument *routeB);
 
   44    static qreal 
distance(
const GeoDataLineString &wayPoints, 
const GeoDataCoordinates &position);
 
   50    static qreal bearing(
const GeoDataCoordinates &one, 
const GeoDataCoordinates &two);
 
   56    static qreal 
distance(
const GeoDataCoordinates &satellite, 
const GeoDataCoordinates &lineA, 
const GeoDataCoordinates &lineB);
 
   61    static GeoDataCoordinates coordinates(
const GeoDataCoordinates &
start, qreal distance, qreal bearing);
 
   67    static qreal unidirectionalSimilarity(
const GeoDataDocument *routeA, 
const GeoDataDocument *routeB);
 
   72    static bool higherScore(
const GeoDataDocument *one, 
const GeoDataDocument *two);
 
   77    static qreal instructionScore(
const GeoDataDocument *document);
 
   79    static const GeoDataLineString *waypoints(
const GeoDataDocument *document);
 
   81    static int nonZero(
const QImage &image);
 
   83    static QPolygonF polygon(
const GeoDataLineString &lineString, qreal x, qreal y, qreal sx, qreal sy);
 
   86    QList<GeoDataDocument *> m_routes;
 
   89    QList<GeoDataDocument *> m_restrainedRoutes;
 
   92    QElapsedTimer m_responseTime;
 
   97AlternativeRoutesModel::Private::Private()
 
  103int AlternativeRoutesModel::Private::nonZero(
const QImage &image)
 
  105    QRgb 
const black = qRgb(0, 0, 0);
 
  107    for (
int y = 0; y < image.
height(); ++y) {
 
  108        QRgb *destLine = (QRgb *)image.
scanLine(y);
 
  109        for (
int x = 0; x < image.
width(); ++x) {
 
  110            count += destLine[x] == 
black ? 0 : 1;
 
  116QPolygonF AlternativeRoutesModel::Private::polygon(
const GeoDataLineString &lineString, qreal x, qreal y, qreal sx, qreal sy)
 
  119    for (
int i = 0; i < lineString.size(); ++i) {
 
  120        poly << 
QPointF(qAbs((lineString)[i].longitude() - x) * sx, qAbs((lineString)[i].latitude() - y) * sy);
 
  125bool AlternativeRoutesModel::Private::filter(
const GeoDataDocument *document)
 const 
  127    for (
int i = 0; i < m_routes.size(); ++i) {
 
  128        qreal similarity = Private::similarity(document, m_routes.at(i));
 
  129        if (similarity > 0.8) {
 
  137qreal AlternativeRoutesModel::Private::similarity(
const GeoDataDocument *routeA, 
const GeoDataDocument *routeB)
 
  139    return qMax<qreal>(unidirectionalSimilarity(routeA, routeB), unidirectionalSimilarity(routeB, routeA));
 
  142qreal AlternativeRoutesModel::Private::distance(
const GeoDataLineString &wayPoints, 
const GeoDataCoordinates &position)
 
  144    Q_ASSERT(!wayPoints.isEmpty());
 
  145    qreal minDistance = 0;
 
  146    for (
int i = 1; i < wayPoints.size(); ++i) {
 
  147        qreal dist = 
distance(position, wayPoints.at(i - 1), wayPoints.at(i));
 
  148        if (minDistance <= 0 || dist < minDistance) {
 
  158    qreal delta = two.longitude() - one.longitude();
 
  159    qreal lat1 = one.latitude();
 
  160    qreal lat2 = two.latitude();
 
  161    return fmod(atan2(sin(delta) * cos(lat2), cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta)), 2 * M_PI);
 
  166    qreal lat1 = 
start.latitude();
 
  167    qreal lon1 = 
start.longitude();
 
  168    qreal lat2 = asin(sin(lat1) * cos(distance) + cos(lat1) * sin(distance) * cos(bearing));
 
  169    qreal lon2 = lon1 + atan2(sin(bearing) * sin(distance) * cos(lat1), cos(distance) - sin(lat1) * sin(lat2));
 
  176    qreal bearA = bearing(lineA, satellite);
 
  177    qreal bearB = bearing(lineA, lineB);
 
  178    qreal result = asin(sin(dist) * sin(bearB - bearA));
 
  181    result = acos(cos(dist) / cos(result));
 
  192qreal AlternativeRoutesModel::Private::unidirectionalSimilarity(
const GeoDataDocument *routeA, 
const GeoDataDocument *routeB)
 
  194    const GeoDataLineString *waypointsA = waypoints(routeA);
 
  195    const GeoDataLineString *waypointsB = waypoints(routeB);
 
  196    if (!waypointsA || !waypointsB) {
 
  201    image.
fill(qRgb(0, 0, 0));
 
  202    GeoDataLatLonBox box = GeoDataLatLonBox::fromLineString(*waypointsA);
 
  203    box = box.united(GeoDataLatLonBox::fromLineString(*waypointsB));
 
  204    if (!box.width() || !box.height()) {
 
  208    qreal 
const sw = image.
width() / box.width();
 
  209    qreal 
const sh = image.
height() / box.height();
 
  214    painter.drawPoints(Private::polygon(*waypointsA, box.west(), box.north(), sw, sh));
 
  215    int const countA = Private::nonZero(image);
 
  217    painter.drawPoints(Private::polygon(*waypointsB, box.west(), box.north(), sw, sh));
 
  218    int const countB = Private::nonZero(image);
 
  219    Q_ASSERT(countA <= countB);
 
  220    return countB ? 1.0 - qreal(countB - countA) / countB : 0;
 
  223bool AlternativeRoutesModel::Private::higherScore(
const GeoDataDocument *one, 
const GeoDataDocument *two)
 
  225    qreal instructionScoreA = instructionScore(one);
 
  226    qreal instructionScoreB = instructionScore(two);
 
  227    if (instructionScoreA != instructionScoreB) {
 
  228        return instructionScoreA > instructionScoreB;
 
  231    qreal lengthA = waypoints(one)->length(EARTH_RADIUS);
 
  232    qreal lengthB = waypoints(two)->length(EARTH_RADIUS);
 
  234    return lengthA < lengthB;
 
  237qreal AlternativeRoutesModel::Private::instructionScore(
const GeoDataDocument *document)
 
  239    bool hasInstructions = 
false;
 
  243    for (
const GeoDataFolder *folder : std::as_const(folders)) {
 
  244        for (
const GeoDataPlacemark *placemark : folder->placemarkList()) {
 
  245            if (!blacklist.
contains(placemark->name())) {
 
  246                hasInstructions = 
true;
 
  252    for (
const GeoDataPlacemark *placemark : document->placemarkList()) {
 
  253        if (!blacklist.
contains(placemark->name())) {
 
  254            hasInstructions = 
true;
 
  256            if (placemark->extendedData().contains(QStringLiteral(
"turnType"))) {
 
  262    return hasInstructions ? 0.5 : 0.0;
 
  265const GeoDataLineString *AlternativeRoutesModel::Private::waypoints(
const GeoDataDocument *document)
 
  268    for (
const GeoDataFolder *folder : std::as_const(folders)) {
 
  269        for (
const GeoDataPlacemark *placemark : folder->placemarkList()) {
 
  270            const GeoDataGeometry *geometry = placemark->geometry();
 
  271            const auto lineString = 
dynamic_cast<const GeoDataLineString *
>(geometry);
 
  278    for (
const GeoDataPlacemark *placemark : document->placemarkList()) {
 
  279        const GeoDataGeometry *geometry = placemark->geometry();
 
  280        const auto lineString = 
dynamic_cast<const GeoDataLineString *
>(geometry);
 
  289AlternativeRoutesModel::AlternativeRoutesModel(
QObject *parent)
 
  296AlternativeRoutesModel::~AlternativeRoutesModel()
 
  302int AlternativeRoutesModel::rowCount(
const QModelIndex &)
 const 
  304    return d->m_routes.size();
 
  322        return d->m_routes.at(index.
row())->name();
 
  328const GeoDataDocument *AlternativeRoutesModel::route(
int index)
 const 
  330    if (index >= 0 && index < d->m_routes.size()) {
 
  331        return d->m_routes.at(index);
 
  337void AlternativeRoutesModel::newRequest(RouteRequest *)
 
  339    d->m_responseTime.start();
 
  340    d->m_currentIndex = -1;
 
  344void AlternativeRoutesModel::addRestrainedRoutes()
 
  346    Q_ASSERT(d->m_routes.isEmpty());
 
  347    std::sort(d->m_restrainedRoutes.begin(), d->m_restrainedRoutes.end(), Private::higherScore);
 
  349    for (GeoDataDocument *route : std::as_const(d->m_restrainedRoutes)) {
 
  350        if (!d->filter(route)) {
 
  351            int affected = d->m_routes.size();
 
  352            beginInsertRows(
QModelIndex(), affected, affected);
 
  354            d->m_routes.push_back(route);
 
  359    d->m_restrainedRoutes.clear();
 
  360    Q_ASSERT(!d->m_routes.isEmpty());
 
  364void AlternativeRoutesModel::addRoute(GeoDataDocument *document, WritePolicy policy)
 
  366    if (policy != Instant) {
 
  367        if (d->m_routes.isEmpty()) {
 
  368            d->m_restrainedRoutes.push_back(document);
 
  370            if (d->m_restrainedRoutes.isEmpty()) {
 
  372                const int responseTime = d->m_responseTime.elapsed();
 
  373                const int timeout = qMin<int>(500, qMax<int>(50, responseTime * 2));
 
  380        for (
int i = 0; i < d->m_routes.size(); ++i) {
 
  381            qreal similarity = Private::similarity(document, d->m_routes.at(i));
 
  382            if (similarity > 0.8) {
 
  383                if (Private::higherScore(document, d->m_routes.at(i))) {
 
  384                    d->m_routes[i] = document;
 
  386                    Q_EMIT dataChanged(changed, changed);
 
  394    const int affected = d->m_routes.size();
 
  395    beginInsertRows(
QModelIndex(), affected, affected);
 
  396    d->m_routes.push_back(document);
 
  400const GeoDataLineString *AlternativeRoutesModel::waypoints(
const GeoDataDocument *document)
 
  402    return Private::waypoints(document);
 
  405void AlternativeRoutesModel::setCurrentRoute(
int index)
 
  407    if (index >= 0 && index < rowCount() && d->m_currentIndex != index) {
 
  408        d->m_currentIndex = index;
 
  409        Q_EMIT currentRouteChanged(currentRoute());
 
  410        Q_EMIT currentRouteChanged(d->m_currentIndex);
 
  414const GeoDataDocument *AlternativeRoutesModel::currentRoute()
 const 
  416    const GeoDataDocument *result = 
nullptr;
 
  417    if (d->m_currentIndex >= 0 && d->m_currentIndex < rowCount()) {
 
  418        result = d->m_routes[d->m_currentIndex];
 
  424void AlternativeRoutesModel::clear()
 
  428    d->m_currentIndex = -1;
 
  436#include "moc_AlternativeRoutesModel.cpp" 
A 3d point representation.
 
qreal sphericalDistanceTo(const GeoDataCoordinates &other) const
This method calculates the shortest distance between two points on a sphere.
 
Q_SCRIPTABLE QString start(QString train="")
 
Q_SCRIPTABLE Q_NOREPLY void start()
 
Binds a QML item to a specific geodetic location in screen coordinates.
 
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
 
void fill(Qt::GlobalColor color)
 
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
 
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)