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"