diff --git a/src/lib/marble/declarative/RouteRelationModel.cpp b/src/lib/marble/declarative/RouteRelationModel.cpp index 3186ab7e8..b9ac3b839 100644 --- a/src/lib/marble/declarative/RouteRelationModel.cpp +++ b/src/lib/marble/declarative/RouteRelationModel.cpp @@ -1,179 +1,171 @@ // // 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 2017 Sergey Popov // #include "RouteRelationModel.h" #include "MarbleDirs.h" #include "osm/OsmPlacemarkData.h" namespace Marble { RouteRelationModel::RouteRelationModel(QObject *parent) : QAbstractListModel(parent) { m_networks[QStringLiteral("iwn")] = tr("International walking route"); m_networks[QStringLiteral("nwn")] = tr("National walking route"); m_networks[QStringLiteral("rwn")] = tr("Regional walking route"); m_networks[QStringLiteral("lwn")] = tr("Local walking route"); m_networks[QStringLiteral("icn")] = tr("International cycling route"); m_networks[QStringLiteral("ncn")] = tr("National cycling route"); m_networks[QStringLiteral("rcn")] = tr("Regional cycling route"); m_networks[QStringLiteral("lcn")] = tr("Local cycling route"); m_networks[QStringLiteral("US:TX:FM")] = tr("Farm to Market Road", "State or county road in Texas, USA"); m_networks[QStringLiteral("regional")] = tr("Regional route"); m_networks[QStringLiteral("national")] = tr("National route"); m_networks[QStringLiteral("municipal")] = tr("Municipal route"); m_networks[QStringLiteral("territorial")] = tr("Territorial route"); m_networks[QStringLiteral("local")] = tr("Local route"); m_networks[QStringLiteral("prefectural")] = tr("Prefectural route"); m_networks[QStringLiteral("US")] = tr("United States route"); } void RouteRelationModel::setRelations(const QSet &relations) { if (!m_relations.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_relations.count() - 1); m_relations.clear(); endRemoveRows(); } if (!relations.isEmpty()) { beginInsertRows(QModelIndex(), 0, relations.count() - 1); m_relations.reserve(relations.size()); std::copy_if(relations.begin(), relations.end(), std::back_inserter(m_relations), [](const GeoDataRelation * relation) { return relation->relationType() >= GeoDataRelation::RouteRoad && relation->relationType() <= GeoDataRelation::RouteSled; }); std::sort(m_relations.begin(), m_relations.end(), [](const GeoDataRelation * a, const GeoDataRelation * b) { - if (a->relationType() == b->relationType()) { - auto const refA = a->osmData().tagValue(QStringLiteral("ref")); - auto const refB = b->osmData().tagValue(QStringLiteral("ref")); - if (refA == refB) { - return a->name() < b->name(); - } - return refA < refB; - } - return a->relationType() < b->relationType(); + return *a < *b; }); endInsertRows(); } } int RouteRelationModel::rowCount(const QModelIndex & parent) const { return parent.isValid() ? 0 : m_relations.count(); } QVariant RouteRelationModel::data(const QModelIndex & index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_relations.count()) { return QVariant(); } if (role == Qt::DisplayRole) { return m_relations.at(index.row())->name(); } else if (role == IconSource) { switch (m_relations.at(index.row())->relationType()) { case GeoDataRelation::RouteRoad: return svgFile("material/maps/ic_directions_car_48px.svg"); case GeoDataRelation::RouteDetour: return svgFile("material/maps/ic_directions_car_48px.svg"); case GeoDataRelation::RouteFerry: return svgFile("material/maps/ic_directions_boat_48px.svg"); case GeoDataRelation::RouteTrain: return svgFile("material/maps/ic_directions_railway_48px.svg"); case GeoDataRelation::RouteSubway: return svgFile("material/maps/ic_directions_subway_48px.svg"); case GeoDataRelation::RouteTram: return svgFile("material/maps/ic_tram_48px.svg"); case GeoDataRelation::RouteBus: return svgFile("material/maps/ic_directions_bus_48px.svg"); case GeoDataRelation::RouteTrolleyBus: return svgFile("material/maps/ic_directions_bus_48px.svg"); case GeoDataRelation::RouteBicycle: return svgFile("material/maps/ic_directions_bike_48px.svg"); case GeoDataRelation::RouteMountainbike: return svgFile("material/maps/ic_directions_bike_48px.svg"); case GeoDataRelation::RouteFoot: return svgFile("material/maps/ic_directions_walk_48px.svg"); case GeoDataRelation::RouteHiking: return svgFile("thenounproject/204712-hiker.svg"); case GeoDataRelation::RouteHorse: return svgFile("thenounproject/78374-horse-riding.svg"); case GeoDataRelation::RouteInlineSkates: return svgFile("thenounproject/101965-inline-skater.svg"); case GeoDataRelation::RouteSkiDownhill: return svgFile("thenounproject/2412-skiing-downhill.svg"); case GeoDataRelation::RouteSkiNordic: return svgFile("thenounproject/30231-skiing-cross-country.svg"); case GeoDataRelation::RouteSkitour: return svgFile("thenounproject/29366-skitour.svg"); case GeoDataRelation::RouteSled: return svgFile("thenounproject/365217-sled.svg"); case GeoDataRelation::UnknownType: return QVariant(QString()); } } else if (role == Description) { return m_relations.at(index.row())->osmData().tagValue(QStringLiteral("description")); } else if (role == Network) { auto const network = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("network")); auto iter = m_networks.find(network); if (iter != m_networks.end()) { return *iter; } auto const fields = network.split(':', QString::SkipEmptyParts); for (auto const &field: fields) { auto iter = m_networks.find(field); if (iter != m_networks.end()) { return *iter; } } return network; } else if (role == RouteColor) { auto const color = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("colour")); return color.isEmpty() ? QStringLiteral("white") : color; } else if (role == TextColor) { auto const colorValue = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("colour")); auto const color = QColor(colorValue.isEmpty() ? QStringLiteral("white") : colorValue); return color.valueF() > 0.85 ? QStringLiteral("black") : QStringLiteral("white"); } else if (role == RouteFrom) { return m_relations.at(index.row())->osmData().tagValue(QStringLiteral("from")); } else if (role == RouteTo) { return m_relations.at(index.row())->osmData().tagValue(QStringLiteral("to")); } else if (role == RouteRef) { auto const ref = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("ref")); return ref.isEmpty() ? m_relations.at(index.row())->name() : ref; } else if (role == RouteVia) { auto const viaValue = m_relations.at(index.row())->osmData().tagValue(QStringLiteral("via")); auto viaList = viaValue.split(';', QString::SkipEmptyParts); for (auto &via: viaList) { via = via.trimmed(); } return viaList; } else if (role == OsmId) { return m_relations.at(index.row())->osmData().oid(); } else if (role == RouteVisible) { return m_relations.at(index.row())->isVisible(); } return QVariant(); } QHash RouteRelationModel::roleNames() const { QHash roles; roles[Qt::DisplayRole] = "display"; roles[IconSource] = "iconSource"; roles[Description] = "description"; roles[Network] = "network"; roles[RouteColor] = "routeColor"; roles[TextColor] = "textColor"; roles[RouteFrom] = "routeFrom"; roles[RouteTo] = "routeTo"; roles[RouteRef] = "routeRef"; roles[RouteVia] = "routeVia"; roles[OsmId] = "oid"; roles[RouteVisible] = "routeVisible"; return roles; } QString RouteRelationModel::svgFile(const QString &path) const { #ifdef Q_OS_ANDROID return MarbleDirs::path(QStringLiteral("svg/%1").arg(path)); #else return QStringLiteral("file:///") + MarbleDirs::path(QStringLiteral("svg/%1").arg(path)); #endif } } diff --git a/src/lib/marble/geodata/data/GeoDataRelation.cpp b/src/lib/marble/geodata/data/GeoDataRelation.cpp index 5492c7ecb..4ecda4962 100644 --- a/src/lib/marble/geodata/data/GeoDataRelation.cpp +++ b/src/lib/marble/geodata/data/GeoDataRelation.cpp @@ -1,166 +1,180 @@ // // 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 2017 Dennis Nienhüser #include "GeoDataRelation.h" #include "GeoDataTypes.h" #include "OsmPlacemarkData.h" #include namespace Marble { class GeoDataRelationPrivate { public: QSet m_features; OsmPlacemarkData m_osmData; QSet m_memberIds; mutable GeoDataRelation::RelationType m_relationType = GeoDataRelation::UnknownType; mutable bool m_relationTypeDirty = true; static QHash s_relationTypes; }; QHash GeoDataRelationPrivate::s_relationTypes; GeoDataRelation::GeoDataRelation() : GeoDataFeature(), d_ptr(new GeoDataRelationPrivate) { // nothing to do } GeoDataRelation::~GeoDataRelation() { delete d_ptr; } GeoDataRelation::GeoDataRelation(const GeoDataRelation &other) : GeoDataFeature(other), d_ptr(new GeoDataRelationPrivate) { Q_D(GeoDataRelation); d->m_features = other.d_func()->m_features; d->m_osmData = other.d_func()->m_osmData; d->m_memberIds = other.d_func()->m_memberIds; d->m_relationType = other.d_func()->m_relationType; d->m_relationTypeDirty = other.d_func()->m_relationTypeDirty; } GeoDataRelation &GeoDataRelation::operator=(GeoDataRelation other) // passed by value { GeoDataFeature::operator=(other); std::swap(*this->d_ptr, *other.d_ptr); return *this; } +bool GeoDataRelation::operator<(const GeoDataRelation &other) const +{ + if (relationType() == other.relationType()) { + Q_D(const GeoDataRelation); + auto const refA = d->m_osmData.tagValue(QStringLiteral("ref")); + auto const refB = other.osmData().tagValue(QStringLiteral("ref")); + if (refA == refB) { + return name() < other.name(); + } + return refA < refB; + } + return relationType() < other.relationType(); +} + const char *GeoDataRelation::nodeType() const { return GeoDataTypes::GeoDataRelationType; } GeoDataFeature *GeoDataRelation::clone() const { return new GeoDataRelation(*this); } void GeoDataRelation::addMember(const GeoDataFeature *feature, qint64 id, const QString &role) { Q_D(GeoDataRelation); d->m_features << feature; d->m_osmData.addRelation(id, role); d->m_memberIds << id; } QSet GeoDataRelation::members() const { Q_D(const GeoDataRelation); return d->m_features; } OsmPlacemarkData &GeoDataRelation::osmData() { Q_D(GeoDataRelation); d->m_relationTypeDirty = true; return d->m_osmData; } const OsmPlacemarkData &GeoDataRelation::osmData() const { Q_D(const GeoDataRelation); return d->m_osmData; } GeoDataRelation::RelationType GeoDataRelation::relationType() const { Q_D(const GeoDataRelation); if (!d->m_relationTypeDirty) { return d->m_relationType; } if (GeoDataRelationPrivate::s_relationTypes.isEmpty()) { auto &map = GeoDataRelationPrivate::s_relationTypes; map["road"] = RouteRoad; map["detour"] = RouteDetour; map["ferry"] = RouteFerry; map["train"] = RouteTrain; map["subway"] = RouteSubway; map["tram"] = RouteTram; map["bus"] = RouteBus; map["trolleybus"] = RouteTrolleyBus; map["bicycle"] = RouteBicycle; map["mtb"] = RouteMountainbike; map["foot"] = RouteFoot; map["hiking"] = GeoDataRelation::RouteHiking; map["horse"] = RouteHorse; map["inline_skates"] = RouteInlineSkates; } d->m_relationType = GeoDataRelation::UnknownType; d->m_relationTypeDirty = false; if (d->m_osmData.containsTag(QStringLiteral("type"), QStringLiteral("route"))) { auto const route = d->m_osmData.tagValue(QStringLiteral("route")); if (route == QStringLiteral("piste")) { auto const piste = d->m_osmData.tagValue(QStringLiteral("piste:type")); if (piste == QStringLiteral("downhill")) { d->m_relationType = RouteSkiDownhill; } else if (piste == QStringLiteral("nordic")) { d->m_relationType = RouteSkiNordic; } else if (piste == QStringLiteral("skitour")) { d->m_relationType = RouteSkitour; } else if (piste == QStringLiteral("sled")) { d->m_relationType = RouteSled; } } else { d->m_relationType = GeoDataRelationPrivate::s_relationTypes.value(route, UnknownType); } } return d->m_relationType; } QSet GeoDataRelation::memberIds() const { Q_D(const GeoDataRelation); return d->m_memberIds; } bool GeoDataRelation::containsAnyOf(const QSet &memberIds) const { Q_D(const GeoDataRelation); #if QT_VERSION >= 0x050600 // intersects was introduced in Qt 5.6. return d->m_memberIds.intersects(memberIds); #else return !(d->m_memberIds.intersect(memberIds).isEmpty()); #endif } } diff --git a/src/lib/marble/geodata/data/GeoDataRelation.h b/src/lib/marble/geodata/data/GeoDataRelation.h index 68e3cbad7..dc7d150eb 100644 --- a/src/lib/marble/geodata/data/GeoDataRelation.h +++ b/src/lib/marble/geodata/data/GeoDataRelation.h @@ -1,72 +1,74 @@ // // 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 2017 Dennis Nienhüser #ifndef MARBLE_GEODATARELATION_H #define MARBLE_GEODATARELATION_H #include "GeoDataCoordinates.h" #include "GeoDataPlacemark.h" #include "geodata_export.h" namespace Marble { class GeoDataRelationPrivate; class GEODATA_EXPORT GeoDataRelation: public GeoDataFeature { public: enum RelationType { UnknownType, RouteRoad, RouteDetour, RouteFerry, RouteTrain, RouteSubway, RouteTram, RouteBus, RouteTrolleyBus, RouteBicycle, RouteMountainbike, RouteFoot, RouteHiking, RouteHorse, RouteInlineSkates, RouteSkiDownhill, RouteSkiNordic, RouteSkitour, RouteSled, }; GeoDataRelation(); ~GeoDataRelation(); GeoDataRelation(const GeoDataRelation &other); GeoDataRelation & operator=(GeoDataRelation other); + bool operator<(const GeoDataRelation &other) const; + const char* nodeType() const override; GeoDataFeature * clone() const override; void addMember(const GeoDataFeature* feature, qint64 id, const QString &role); QSet members() const; OsmPlacemarkData &osmData(); const OsmPlacemarkData &osmData() const; RelationType relationType() const; QSet memberIds() const; bool containsAnyOf(const QSet &memberIds) const; private: GeoDataRelationPrivate* d_ptr; Q_DECLARE_PRIVATE(GeoDataRelation) }; } #endif