File indexing completed on 2024-05-05 03:55:01
0001 # SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0002 # SPDX-License-Identifier: LGPL-2.0-or-later 0003 0004 from config import * 0005 import io 0006 import os 0007 from qgis import * 0008 from qgis.core import * 0009 0010 def tzIdToEnum(tzId): 0011 return tzId.replace('/', '_').replace('-', '_') 0012 0013 def normalizedTz(tzId): 0014 if tzId in TZID_MAP: 0015 return TZID_MAP[tzId] 0016 return tzId 0017 0018 def normalizedCountry(code): 0019 if code in ISO3166_1_MAP: 0020 return ISO3166_1_MAP[code] 0021 return code 0022 0023 # Generate IANA timezone names string table 0024 # This allows us to reference timezones by a uint16_t in other data tables 0025 class TimezoneStringTableTask(QgsTask): 0026 def __init__(self, context): 0027 super().__init__('Generate timezone string table', QgsTask.CanCancel) 0028 self.context = context 0029 0030 def run(self): 0031 QgsMessageLog.logMessage('Generating timezone string table', LOG_CATEGORY, Qgis.Info) 0032 tzLayer = self.context['tzLayer'] 0033 tzIds = set() 0034 for tz in tzLayer.getFeatures(): 0035 tzIds.add(tz['tzid']) 0036 tzIds = list(tzIds) 0037 tzIds.sort() 0038 offsets = [0] 0039 0040 out = open('../../data/timezone_name_table.cpp', 'w') 0041 out.write("""/* 0042 * SPDX-License-Identifier: ODbL-1.0 0043 * SPDX-FileCopyrightText: OpenStreetMap contributors 0044 * 0045 * Autogenerated using QGIS - do not edit! 0046 */ 0047 0048 static constexpr const char timezone_name_table[] = 0049 """) 0050 for tzId in tzIds: 0051 out.write(f" \"{tzId}\\0\"\n") 0052 offsets.append(offsets[-1] + len(tzId) + 1) 0053 out.seek(out.tell() - 1, io.SEEK_SET) # to make clang-format happy 0054 out.write(";\n") 0055 out.close() 0056 0057 out = open('../../data/timezone_names_p.h', 'w') 0058 out.write("""/* 0059 * SPDX-License-Identifier: ODbL-1.0 0060 * SPDX-FileCopyrightText: OpenStreetMap contributors 0061 * 0062 * Autogenerated using QGIS - do not edit! 0063 */ 0064 0065 #ifndef TIMEZONE_NAMES_P_H 0066 #define TIMEZONE_NAMES_P_H 0067 0068 #include <cstdint> 0069 0070 enum Tz : uint16_t { 0071 """) 0072 for i in range(len(tzIds)): 0073 out.write(f" {tzIdToEnum(tzIds[i])} = {offsets[i]},\n") 0074 out.write(f" Undefined = {offsets[-1] - 1},\n") # points to the last null byte 0075 out.write("};\n\n#endif\n") 0076 out.close() 0077 return True 0078 0079 # Computes country/country subdivision to timezone mapping 0080 class RegionToTimezoneMapTask(QgsTask): 0081 def __init__(self, context): 0082 super().__init__('Computing region to timezone mapping', QgsTask.CanCancel) 0083 self.context = context 0084 0085 def run(self): 0086 QgsMessageLog.logMessage('Computing region to timezone mapping', LOG_CATEGORY, Qgis.Info) 0087 countryLayer = self.context['countryLayer'] 0088 tzLayer = self.context['tzLayer'] 0089 countryToTz = {} 0090 for country in countryLayer.getFeatures(): 0091 countryCode = country['ISO3166-1'] 0092 if not countryCode in countryToTz: 0093 countryToTz[countryCode] = set() 0094 countryGeom = country.geometry() 0095 countryArea = countryGeom.area() 0096 for tz in tzLayer.getFeatures(): 0097 tzId = normalizedTz(tz['tzId']) 0098 if tz.geometry().intersects(countryGeom): 0099 # filter out intersection noise along the boundaries 0100 area = tz.geometry().intersection(countryGeom).area() 0101 tzAreaRatio = area / tz.geometry().area() 0102 countryAreaRatio = area / countryArea 0103 if tzAreaRatio > 0.01 or countryAreaRatio > 0.1: 0104 countryToTz[countryCode].add(tzId) 0105 0106 out = open('../../data/country_timezone_map.cpp', 'w') 0107 out.write("""/* 0108 * SPDX-License-Identifier: ODbL-1.0 0109 * SPDX-FileCopyrightText: OpenStreetMap contributors 0110 * 0111 * Autogenerated using QGIS - do not edit! 0112 */ 0113 0114 #include "isocodes_p.h" 0115 #include "mapentry_p.h" 0116 #include "timezone_names_p.h" 0117 0118 static constexpr const MapEntry<uint16_t> country_timezone_map[] = { 0119 """) 0120 countries = list(countryToTz) 0121 countries.sort() 0122 for country in countries: 0123 if len(countryToTz[country]) == 1: 0124 out.write(f" {{IsoCodes::alpha2CodeToKey(\"{country}\"), Tz::{tzIdToEnum(list(countryToTz[country])[0])}}},\n") 0125 0126 out.write("};\n") 0127 out.close() 0128 0129 # for countries with more than one tz, match against subdivisions 0130 subdivToTz = {} 0131 subdivLayer = self.context['subdivLayer'] 0132 tzLayer = self.context['tzLayer'] 0133 for subdiv in subdivLayer.getFeatures(): 0134 code = subdiv['ISO3166-2'] 0135 country = code[:2] 0136 if len(countryToTz[country]) <= 1: 0137 continue 0138 if not code in subdivToTz: 0139 subdivToTz[code] = {} 0140 subdivGeom = subdiv.geometry() 0141 subdivArea = subdivGeom.area() 0142 for tz in tzLayer.getFeatures(): 0143 tzId = normalizedTz(tz['tzId']) 0144 if tz.geometry().intersects(subdivGeom): 0145 # filter out intersection noise along the boundaries 0146 area = tz.geometry().intersection(subdivGeom).area() 0147 tzAreaRatio = area / tz.geometry().area() 0148 subdivAreaRatio = area / subdivArea 0149 if tzAreaRatio > 0.01 or subdivAreaRatio > 0.1: 0150 if not tzId in subdivToTz[code]: 0151 subdivToTz[code][tzId] = area 0152 else: 0153 subdivToTz[code][tzId] += area 0154 0155 out = open('../../data/subdivision_timezone_map.cpp', 'w') 0156 out.write("""/* 0157 * SPDX-License-Identifier: ODbL-1.0 0158 * SPDX-FileCopyrightText: OpenStreetMap contributors 0159 * 0160 * Autogenerated using QGIS - do not edit! 0161 */ 0162 0163 #include "isocodes_p.h" 0164 #include "mapentry_p.h" 0165 #include "timezone_names_p.h" 0166 0167 static constexpr const MapEntry<uint32_t> subdivision_timezone_map[] = { 0168 """) 0169 subdivs = list(subdivToTz) 0170 subdivs.sort() 0171 for subdiv in subdivs: 0172 # sort by area, biggest one first 0173 tzs = list(subdivToTz[subdiv]) 0174 tzs.sort(key = lambda x: subdivToTz[subdiv][x], reverse = True) 0175 for tz in tzs: 0176 out.write(f" {{IsoCodes::subdivisionCodeToKey(\"{subdiv}\"), Tz::{tzIdToEnum(tz)}}},\n") 0177 if len(subdivToTz[subdiv]) == 0: 0178 out.write(f" // {subdiv}\n") 0179 0180 out.write("};\n") 0181 out.close() 0182 0183 self.context['countryToTimezoneMap'] = countryToTz 0184 self.context['subdivisionToTimezoneMap'] = subdivToTz 0185 return True