File indexing completed on 2024-05-12 04:42:47
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "gbfsvehicletypes.h" 0008 0009 #include "gbfs.h" 0010 #include "gbfsreader.h" 0011 #include "gbfsservice.h" 0012 #include "gbfsstore.h" 0013 0014 #include <QDebug> 0015 #include <QJsonArray> 0016 #include <QJsonDocument> 0017 #include <QJsonObject> 0018 0019 using namespace KPublicTransport; 0020 0021 namespace KPublicTransport { 0022 static bool operator<(const GBFSVehicleType &lhs, const GBFSVehicleType &rhs) 0023 { 0024 return lhs.typeId < rhs.typeId; 0025 } 0026 static bool operator<(const GBFSVehicleType &lhs, QStringView rhs) 0027 { 0028 return lhs.typeId < rhs; 0029 } 0030 } 0031 0032 template <typename T> 0033 struct value_map_entry 0034 { 0035 const char *name; 0036 T value; 0037 }; 0038 0039 static constexpr const value_map_entry<GBFSVehicleType::FormFactor> form_factor_map[] = { 0040 { "bicycle", GBFSVehicleType::Bicycle }, 0041 { "car", GBFSVehicleType::Car }, 0042 { "cargo_bicycle", GBFSVehicleType::CargoBicycle }, 0043 { "moped", GBFSVehicleType::Moped }, 0044 { "scooter", GBFSVehicleType::Scooter }, 0045 { "scooter_seating", GBFSVehicleType::Scooter }, 0046 { "scooter_standing", GBFSVehicleType::Scooter }, 0047 { "other", GBFSVehicleType::Other }, 0048 }; 0049 0050 static constexpr const value_map_entry<GBFSVehicleType::PropulsionType> propulsion_map[] = { 0051 { "human", GBFSVehicleType::Human }, 0052 { "electric_assist", GBFSVehicleType::ElectricAssist }, 0053 { "electric", GBFSVehicleType::Electric }, 0054 { "combustion", GBFSVehicleType::Combustion }, 0055 { "combustion_diesel", GBFSVehicleType::Combustion }, 0056 { "hybrid", GBFSVehicleType::Combustion }, 0057 { "plug_in_hybrid", GBFSVehicleType::Combustion }, 0058 }; 0059 0060 template <typename T, std::size_t N> 0061 static T lookupValue(const value_map_entry<T>(&map)[N], QStringView name) 0062 { 0063 for (const auto &entry : map) { 0064 if (name.compare(QLatin1String(entry.name), Qt::CaseInsensitive) == 0) { 0065 return entry.value; 0066 } 0067 } 0068 qDebug() << "unknown value:" << name; 0069 return {}; 0070 } 0071 0072 // some services don't prove a vehicle_types file but use somewhat descriptive fixed values 0073 // try to support that as well to the extend possible 0074 struct fallback_entry { 0075 GBFSVehicleType::FormFactor formFactor; 0076 GBFSVehicleType::PropulsionType propulsionType; 0077 }; 0078 static constexpr const value_map_entry<fallback_entry> fallback_type_map[] = { 0079 { "bike", { GBFSVehicleType::Bicycle, GBFSVehicleType::Human } }, 0080 { "moped", { GBFSVehicleType::Moped, GBFSVehicleType::UndefinedPropulsion } }, 0081 { "scooter", { GBFSVehicleType::Scooter, GBFSVehicleType::UndefinedPropulsion } }, 0082 { "ebike", { GBFSVehicleType::Bicycle, GBFSVehicleType::ElectricAssist } }, 0083 { "electric_moped", { GBFSVehicleType::Moped, GBFSVehicleType::Electric } }, 0084 }; 0085 0086 GBFSVehicleType GBFSVehicleType::fromJson(const QJsonObject &obj) 0087 { 0088 GBFSVehicleType v; 0089 v.typeId = obj.value(QLatin1String("vehicle_type_id")).toString(); 0090 v.name = obj.value(QLatin1String("name")).toString(); 0091 v.formFactor = lookupValue(form_factor_map, obj.value(QLatin1String("form_factor")).toString()); 0092 v.propulsionType = lookupValue(propulsion_map, obj.value(QLatin1String("propulsion_type")).toString()); 0093 return v; 0094 } 0095 0096 GBFSVehicleTypes::GBFSVehicleTypes(const GBFSService &feed) 0097 { 0098 GBFSStore store(feed.systemId); 0099 const auto doc = store.loadData(GBFS::VehicleTypes); 0100 const auto types = GBFSReader::dataValue(doc, QLatin1String("vehicle_types")).toArray(); 0101 0102 m_vehicleTypes.reserve(types.size()); 0103 for (const auto &typeVal : types) { 0104 auto v = GBFSVehicleType::fromJson(typeVal.toObject()); 0105 if (!v.typeId.isEmpty()) { 0106 m_vehicleTypes.push_back(std::move(v)); 0107 } 0108 } 0109 0110 std::sort(m_vehicleTypes.begin(), m_vehicleTypes.end()); 0111 qDebug() << "Found" << m_vehicleTypes.size() << "vehicle types."; 0112 } 0113 0114 GBFSVehicleTypes::~GBFSVehicleTypes() = default; 0115 0116 GBFSVehicleType GBFSVehicleTypes::vehicleType(QStringView typeId) const 0117 { 0118 if (typeId.empty()) { 0119 return {}; 0120 } 0121 0122 const auto it = std::lower_bound(m_vehicleTypes.begin(), m_vehicleTypes.end(), typeId); 0123 if (it != m_vehicleTypes.end() && (*it).typeId == typeId) { 0124 return (*it); 0125 } 0126 0127 // fallback for non-compliant services without vehicle_types file 0128 if (m_vehicleTypes.empty()) { 0129 for (const auto &val : fallback_type_map) { 0130 if (QLatin1String(val.name) == typeId) { 0131 GBFSVehicleType v; 0132 v.formFactor = val.value.formFactor; 0133 v.propulsionType = val.value.propulsionType; 0134 return v; 0135 } 0136 } 0137 qDebug() << "unknown fallback vehicle type:" << typeId; 0138 } 0139 0140 return {}; 0141 }