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 }