File indexing completed on 2024-05-12 04:42:47
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "linemetadata.h" 0008 #include "linemetadata_data.cpp" 0009 #include "datatypes/lineutil_p.h" 0010 0011 #include <osm/datatypes.h> 0012 #include <osm/ztile.h> 0013 0014 #include <QColor> 0015 #include <QDebug> 0016 #include <QString> 0017 #include <QUrl> 0018 0019 using namespace KPublicTransport; 0020 0021 // ensure index storage space suffices 0022 // should this fail more space needs to be allocated in LineMetaDataContent 0023 static_assert(sizeof(line_name_stringtab) < (1 << 13)); 0024 static_assert(sizeof(line_logo_stringtab) < (1 << 16)); 0025 0026 static QString lookupName(uint16_t index) 0027 { 0028 return QString::fromUtf8(line_name_stringtab + index); 0029 } 0030 0031 static QString lookupLogo(uint16_t index) 0032 { 0033 return QString::fromUtf8(line_logo_stringtab + index); 0034 } 0035 0036 LineMetaData::LineMetaData() = default; 0037 LineMetaData::LineMetaData(const LineMetaDataContent *dd) 0038 : d(dd) 0039 { 0040 } 0041 0042 LineMetaData::~LineMetaData() = default; 0043 LineMetaData::LineMetaData(const LineMetaData&) = default; 0044 LineMetaData& LineMetaData::operator=(const LineMetaData&) = default; 0045 0046 bool LineMetaData::isNull() const 0047 { 0048 return d == nullptr; 0049 } 0050 0051 QString LineMetaData::name() const 0052 { 0053 return lookupName(d->nameIdx); 0054 } 0055 0056 QColor LineMetaData::color() const 0057 { 0058 return d && d->color.argb() ? QColor(d->color.argb()) : QColor(); 0059 } 0060 0061 QUrl LineMetaData::logoUrl() const 0062 { 0063 if (!d) { 0064 return {}; 0065 } 0066 const auto logoName = lookupLogo(d->logoIdx); 0067 return logoName.isEmpty() ? QUrl() : QUrl(QLatin1String("https://commons.wikimedia.org/wiki/Special:Redirect/file/") + logoName); 0068 } 0069 0070 Line::Mode LineMetaData::mode() const 0071 { 0072 if (!d) { 0073 return Line::Unknown; 0074 } 0075 switch (d->mode) { 0076 case LineMetaDataContent::Tramway: 0077 return Line::Tramway; 0078 case LineMetaDataContent::RapidTransit: 0079 return Line::RapidTransit; 0080 case LineMetaDataContent::Subway: 0081 return Line::Metro; 0082 case LineMetaDataContent::LocalTrain: 0083 return Line::LocalTrain; 0084 case LineMetaDataContent::LongDistanceTrain: 0085 return Line::LongDistanceTrain; 0086 }; 0087 return Line::Unknown; 0088 } 0089 0090 QUrl LineMetaData::modeLogoUrl() const 0091 { 0092 if (!d) { 0093 return {}; 0094 } 0095 const auto logoName = lookupLogo(d->productLogoIdx); 0096 return logoName.isEmpty() ? QUrl() : QUrl(QLatin1String("https://commons.wikimedia.org/wiki/Special:Redirect/file/") + logoName); 0097 } 0098 0099 LineMetaData LineMetaData::find(double latitude, double longitude, const QString &name, Line::Mode mode) 0100 { 0101 OSM::Coordinate coord(latitude, longitude); 0102 0103 // overall quad tree parameters 0104 constexpr const auto depthCount = sizeof(line_data_depthOffsets) / sizeof(LineMetaDataQuadTreeDepthIndex); 0105 static_assert(depthCount > 1, "quad tree depth information error"); 0106 constexpr const uint8_t maxDepth = line_data_depthOffsets[0].depth; 0107 constexpr const uint8_t minDepth = line_data_depthOffsets[depthCount - 1].depth; 0108 static_assert(maxDepth > minDepth, "quad tree depth error"); 0109 static_assert(minDepth > 1, "quad tree depth error"); 0110 0111 // walk through the quad tree bottom up, looking for a tile containing the line we are looking for 0112 OSM::ZTile tile(coord.z() >> (2 * minDepth), minDepth); 0113 for (uint8_t d = minDepth; d <= maxDepth; ++d, tile = tile.parent()) { 0114 // determining quad tree offsets for the current depth 0115 const auto depthOffsetIt = std::lower_bound(std::begin(line_data_depthOffsets), std::end(line_data_depthOffsets), d); 0116 if (depthOffsetIt == std::end(line_data_depthOffsets) || (*depthOffsetIt).depth != d) { 0117 continue; // depth level isn't populated at all (yes, this can happen) 0118 } 0119 const auto treeBegin = std::begin(line_data_zquadtree) + (*depthOffsetIt).offset; 0120 const auto treeEnd = (depthOffsetIt + 1) == std::end(line_data_depthOffsets) ? 0121 std::end(line_data_zquadtree) : 0122 std::begin(line_data_zquadtree) + (*(depthOffsetIt + 1)).offset; 0123 0124 // look up the tile for the given coordinate at depth d 0125 const auto treeIt = std::lower_bound(treeBegin, treeEnd, tile.z); 0126 if (treeIt == treeEnd || (*treeIt).z != tile.z) { 0127 continue; // tile doesn't exist at this depth 0128 } 0129 0130 // iterate over the bucket of this tile and look for the line 0131 // we have to handle two cases: single lines and buckets of lines 0132 if ((*treeIt).lineIdx >= line_data_count) { 0133 auto bucketIt = line_data_bucketTable + (*treeIt).lineIdx - line_data_count; 0134 while ((*bucketIt) != -1) { 0135 const auto d = line_data + (*bucketIt); 0136 if (LineUtil::isSameLineNameStrict(lookupName(d->nameIdx), name) && LineUtil::isCompatibleMode(LineMetaData(d).mode(), mode)) { 0137 return LineMetaData(d); 0138 } 0139 ++bucketIt; 0140 } 0141 } else { 0142 const auto d = line_data + (*treeIt).lineIdx; 0143 if (LineUtil::isSameLineNameStrict(lookupName(d->nameIdx), name) && LineUtil::isCompatibleMode(LineMetaData(d).mode(), mode)) { 0144 return LineMetaData(d); 0145 } 0146 } 0147 } 0148 0149 return {}; 0150 }