File indexing completed on 2024-04-21 03:54:30

0001 /*
0002     SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "kcountrysubdivision.h"
0008 #include "isocodes_p.h"
0009 #include "isocodescache_p.h"
0010 #include "kcountry.h"
0011 #include "ki18n_logging.h"
0012 #include "klocalizedstring.h"
0013 #include "spatial_index_p.h"
0014 #include "timezonedata_p.h"
0015 
0016 #include <cstring>
0017 
0018 static_assert(sizeof(KCountrySubdivision) == 4);
0019 
0020 KCountrySubdivision::KCountrySubdivision()
0021     : d(0)
0022 {
0023 }
0024 
0025 KCountrySubdivision::KCountrySubdivision(const KCountrySubdivision &) = default;
0026 KCountrySubdivision::~KCountrySubdivision() = default;
0027 KCountrySubdivision &KCountrySubdivision::operator=(const KCountrySubdivision &) = default;
0028 
0029 bool KCountrySubdivision::operator==(const KCountrySubdivision &other) const
0030 {
0031     return d == other.d;
0032 }
0033 
0034 bool KCountrySubdivision::operator!=(const KCountrySubdivision &other) const
0035 {
0036     return d != other.d;
0037 }
0038 
0039 bool KCountrySubdivision::isValid() const
0040 {
0041     return d != 0;
0042 }
0043 
0044 QString KCountrySubdivision::code() const
0045 {
0046     if (d == 0) {
0047         return {};
0048     }
0049 
0050     QString code;
0051     code.reserve(6);
0052     code += country().alpha2();
0053     code += QLatin1Char('-');
0054 
0055     auto key = d & 0xffff;
0056     while (key) {
0057         const auto c = IsoCodes::mapFromAlphaNumKey(key);
0058         if (c) {
0059             code.insert(3, QLatin1Char(c));
0060         }
0061         key /= IsoCodes::AlphaNumKeyFactor;
0062     }
0063 
0064     return code;
0065 }
0066 
0067 QString KCountrySubdivision::name() const
0068 {
0069     if (d == 0) {
0070         return {};
0071     }
0072 
0073     auto cache = IsoCodesCache::instance();
0074     cache->loadIso3166_2();
0075     const auto it = std::lower_bound(cache->subdivisionNameMapBegin(), cache->subdivisionNameMapEnd(), d);
0076     if (it != cache->subdivisionNameMapEnd() && (*it).key == d) {
0077         return i18nd("iso_3166-2", cache->subdivisionStringTableLookup((*it).value));
0078     }
0079     return {};
0080 }
0081 
0082 KCountry KCountrySubdivision::country() const
0083 {
0084     KCountry c;
0085     c.d = d >> 16;
0086     return c;
0087 }
0088 
0089 KCountrySubdivision KCountrySubdivision::parent() const
0090 {
0091     KCountrySubdivision s;
0092     if (d == 0) {
0093         return s;
0094     }
0095 
0096     auto cache = IsoCodesCache::instance();
0097     cache->loadIso3166_2();
0098 
0099     const auto it = std::lower_bound(cache->subdivisionParentMapBegin(), cache->subdivisionParentMapEnd(), d);
0100     if (it != cache->subdivisionParentMapEnd() && (*it).key == d) {
0101         s.d = (d & 0xffff0000) | (uint32_t)(*it).value;
0102     }
0103 
0104     return s;
0105 }
0106 
0107 QList<const char *> KCountrySubdivision::timeZoneIds() const
0108 {
0109     QList<const char *> tzs;
0110     if (d == 0) {
0111         return tzs;
0112     }
0113 
0114     const auto [subdivBegin, subdivEnd] = std::equal_range(TimezoneData::subdivisionTimezoneMapBegin(), TimezoneData::subdivisionTimezoneMapEnd(), d);
0115     if (subdivBegin != subdivEnd) {
0116         tzs.reserve(std::distance(subdivBegin, subdivEnd));
0117         for (auto it = subdivBegin; it != subdivEnd; ++it) {
0118             tzs.push_back(TimezoneData::ianaIdLookup((*it).value));
0119         }
0120         return tzs;
0121     }
0122 
0123     const auto countryIt = std::lower_bound(TimezoneData::countryTimezoneMapBegin(), TimezoneData::countryTimezoneMapEnd(), uint16_t(d >> 16));
0124     if (countryIt != TimezoneData::countryTimezoneMapEnd() && (*countryIt).key == (d >> 16)) {
0125         tzs.push_back(TimezoneData::ianaIdLookup((*countryIt).value));
0126     }
0127 
0128     return tzs;
0129 }
0130 
0131 QList<KCountrySubdivision> KCountrySubdivision::subdivisions() const
0132 {
0133     if (d == 0) {
0134         return {};
0135     }
0136 
0137     QList<KCountrySubdivision> l;
0138     auto cache = IsoCodesCache::instance();
0139     cache->loadIso3166_2();
0140     // we don't have a parent->child map, instead we use the child->parent map
0141     // that is sorted by country (due to the country being in the two most significant bytes of its key),
0142     // so we don't need to do a full sequential search here
0143     auto it = std::lower_bound(cache->subdivisionParentMapBegin(), cache->subdivisionParentMapEnd(), d >> 16, [](auto lhs, auto rhs) {
0144         return (lhs.key >> 16) < rhs;
0145     });
0146     for (; it != cache->subdivisionParentMapEnd() && ((*it).key >> 16) == (d >> 16); ++it) {
0147         if ((*it).value == (d & 0xffff)) {
0148             KCountrySubdivision s;
0149             s.d = (*it).key;
0150             l.push_back(s);
0151         }
0152     }
0153 
0154     return l;
0155 }
0156 
0157 static uint32_t validatedSubdivisionKey(uint32_t key)
0158 {
0159     if (!key) {
0160         return 0;
0161     }
0162 
0163     auto cache = IsoCodesCache::instance();
0164     cache->loadIso3166_2();
0165 
0166     const auto it = std::lower_bound(cache->subdivisionNameMapBegin(), cache->subdivisionNameMapEnd(), key);
0167     if (it != cache->subdivisionNameMapEnd() && (*it).key == key) {
0168         return key;
0169     }
0170     return 0;
0171 }
0172 
0173 KCountrySubdivision KCountrySubdivision::fromCode(QStringView code)
0174 {
0175     KCountrySubdivision s;
0176     s.d = validatedSubdivisionKey(IsoCodes::subdivisionCodeToKey(code));
0177     return s;
0178 }
0179 
0180 KCountrySubdivision KCountrySubdivision::fromCode(const char *code)
0181 {
0182     KCountrySubdivision s;
0183     if (code) {
0184         s.d = validatedSubdivisionKey(IsoCodes::subdivisionCodeToKey(code, std::strlen(code)));
0185     }
0186     return s;
0187 }
0188 
0189 KCountrySubdivision KCountrySubdivision::fromLocation(float latitude, float longitude)
0190 {
0191     const auto entry = SpatialIndex::lookup(latitude, longitude);
0192     KCountrySubdivision s;
0193     if (entry.m_subdiv & 0xffff) {
0194         s.d = entry.m_subdiv;
0195     }
0196     return s;
0197 }
0198 
0199 QStringList KCountrySubdivision::timeZoneIdsStringList() const
0200 {
0201     const auto tzIds = timeZoneIds();
0202     QStringList l;
0203     l.reserve(tzIds.size());
0204     std::transform(tzIds.begin(), tzIds.end(), std::back_inserter(l), [](const char *tzId) {
0205         return QString::fromUtf8(tzId);
0206     });
0207     return l;
0208 }
0209 
0210 #include "moc_kcountrysubdivision.cpp"