Coverage for mapology/prefixes.py: 32.95%

140 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-04 20:27:38 +00:00

1"""Render the prefix page apps from the prefix abd prefix table stores.""" 

2 

3import collections 

4import copy 

5import datetime as dti 

6import json 

7import operator 

8import os 

9import pathlib 

10import sys 

11from typing import List, Union 

12 

13import mapology.country as cc 

14import mapology.db as db 

15import mapology.hull as hull 

16import mapology.template_loader as template 

17from mapology import BASE_URL, DEBUG, ENCODING, FOOTER_HTML, FS_PREFIX_PATH, LIB_PATH, PATH_NAV, country_blurb, log 

18 

19THIS_YY_INT = int(dti.datetime.now(dti.UTC).strftime('%y')) 

20 

21HTML_TEMPLATE = os.getenv('GEO_PREFIX_HTML_TEMPLATE', '') 

22HTML_TEMPLATE_IS_EXTERNAL = bool(HTML_TEMPLATE) 

23if not HTML_TEMPLATE: 23 ↛ 26line 23 didn't jump to line 26, because the condition on line 23 was never false

24 HTML_TEMPLATE = 'prefix_page_template.html' 

25 

26AIRP = 'airport' 

27RUNW = 'runways' 

28FREQ = 'frequencies' 

29LOCA = 'localizers' 

30GLID = 'glideslopes' 

31 

32CC_HINT = 'CC_HINT' 

33City = 'City' 

34CITY = City.upper() 

35ICAO = 'ICAO' 

36IC_PREFIX = 'IC_PREFIX' 

37IC_PREFIX_ICAO = f'{IC_PREFIX}_{ICAO}' 

38ITEM = 'ITEM' 

39KIND = 'KIND' 

40PATH = '/PATH' 

41BASE_URL_TARGET = 'BASE_URL' 

42ANCHOR = 'ANCHOR' 

43TEXT = 'TEXT' 

44URL = 'URL' 

45ZOOM = 'ZOOM' 

46DEFAULT_ZOOM = 4 

47FOOTER_HTML_KEY = 'FOOTER_HTML' 

48LIB_PATH_KEY = 'LIB_PATH' 

49 

50icao = 'icao_lower' 

51LAT_LON = 'LAT_LON' 

52cc_page = 'cc_page' 

53Cc_page = 'Cc_page' 

54 

55ATTRIBUTION = f'{KIND} {ITEM} of ' 

56 

57Point = collections.namedtuple('Point', ['label', 'lat', 'lon']) 

58 

59# GOOGLE_MAPS_URL = f'https://www.google.com/maps/search/?api=1&query={{lat}}%2c{{lon}}' # Map + pin Documented 

60GOOGLE_MAPS_URL = 'https://maps.google.com/maps?t=k&q=loc:{lat}+{lon}' # Sat + pin Undocumented 

61 

62REGION_COUNTRY_DATA = { 

63 'region': '', 

64 'url': '', 

65 'country': '', 

66} 

67 

68 

69def main(argv: Union[List[str], None] = None) -> int: 

70 """Drive the prefix renderings.""" 

71 argv = sys.argv[1:] if argv is None else argv 

72 if argv: 72 ↛ 76line 72 didn't jump to line 76, because the condition on line 72 was never false

73 print('usage: mapology prefix') 

74 return 2 

75 

76 store_index = db.load_index('store') 

77 table_index = db.load_index('table') 

78 hulls_index = db.load_index('hulls') 

79 region_country_index = db.load_index('region_country') 

80 

81 prefix_hull_store = copy.deepcopy(hull.THE_HULLS) 

82 slash = '/' 

83 prefixes = sorted(store_index.keys()) 

84 num_prefixes = len(prefixes) 

85 many = num_prefixes > 10 # tenfold magic 

86 numbers = ('latitude', 'longitude', 'elevation') 

87 for current, prefix in enumerate(sorted(prefixes), start=1): 

88 with open(store_index[prefix], 'rt', encoding=ENCODING) as handle: 

89 prefix_store = json.load(handle) 

90 

91 with open(table_index[prefix], 'rt', encoding=ENCODING) as handle: 

92 table_store = json.load(handle) 

93 

94 hulls_index[prefix] = db.hull_path(prefix) # noqa 

95 

96 region_name = table_store['name'] 

97 cc_hint = cc.FROM_ICAO_PREFIX.get(prefix, 'No Country Entry Present') 

98 my_prefix_path = f'{FS_PREFIX_PATH}/{prefix}' 

99 

100 region_country_index[prefix] = str(db.DB_FOLDER_PATHS['region_country'] / f'{prefix}.json') 

101 region_country_data = copy.deepcopy(REGION_COUNTRY_DATA) 

102 region_country_data['region'] = prefix 

103 region_country_data['url'] = f'{BASE_URL}/{FS_PREFIX_PATH}/{prefix}/' 

104 region_country_data['country'] = f'{cc_hint}' 

105 with open(region_country_index[prefix], 'wt', encoding=ENCODING) as handle: 

106 json.dump(region_country_data, handle, indent=2) 

107 log.info(str(region_country_data)) 

108 

109 airports = sorted(table_store['airports'], key=operator.itemgetter('icao')) 

110 

111 message = f'processing {current :>3d}/{num_prefixes} {prefix} --> ({region_name}) ...' 

112 if not many or not current % 10 or current == num_prefixes: 

113 log.info(message) 

114 

115 if DEBUG: 

116 log.debug('%s - %s' % (prefix, region_name)) 

117 data_rows = [] 

118 trial_coords = [] 

119 for airport in airports: 

120 trial_coords.append((airport['latitude'], airport['longitude'])) 

121 row = [str(cell) if key not in numbers else f'{round(cell, 3) :7.03f}' for key, cell in airport.items()] 

122 # monkey patching 

123 # ensure cycles are state with two digits zero left padded 

124 year, cyc = row[6].split(slash) 

125 row[6] = f'{year}/{int(cyc) :02d}' 

126 # create a link to the airport page on the ICAO cell of the airport in the table row 

127 # example: '<a href="AGAT/" class="nd" title="AGAT(Atoifi, Solomon Islands)">AGAT</a>' 

128 an_icao = row[2] 

129 a_name = row[8] 

130 row[2] = f'<a href="{an_icao}/" class="nd" title="{a_name}">{an_icao}</a>' 

131 if DEBUG: 

132 log.info('- | %s |' % (' | '.join(row))) 

133 data_rows.append( 

134 f'<tr><td>{row[0]}</td><td>{row[1]}</td><td>{row[2]}</td>' 

135 f'<td class="ra">{row[3]}</td><td class="ra">{row[4]}</td><td class="ra">{row[5]}</td>' 

136 f'<td class="la">{row[6]}</td><td class="ra">{row[7]}</td>' 

137 f'<td class="la">{row[8]}</td></tr>' 

138 ) 

139 

140 prefix_hull = hull.extract_prefix_hull(prefix, region_name, trial_coords) 

141 hull.update_hull_store(prefix_hull_store, prefix_hull) 

142 

143 min_lat, min_lon = 90, 180 

144 max_lat, max_lon = -90, -180 

145 ra_count = 0 # region_airports_count 

146 cc_count = 1 # HACK A DID ACK TODO: do not fix country count to wun 

147 for airport in airports: 

148 ra_count += 1 

149 # name has eg. "<a href='KLGA/' target='_blank' title='KLGA(La Guardia, New York, USA)'>KLGA</a>" 

150 # name_mix = airport['name'] 

151 # name_mix has eg. "KLGA(La Guardia, New York, USA)'>KLGA</a>" LATER TODO 

152 # code, rest = name_mix.split('(', 1) 

153 # name = rest.split(')', 1)[0] 

154 # coords = airport["geometry"]["coordinates"] 

155 lon, lat = airport['longitude'], airport['latitude'] 

156 min_lat = min(min_lat, lat) 

157 min_lon = min(min_lon, lon) 

158 max_lat = max(max_lat, lat) 

159 max_lon = max(max_lon, lon) 

160 

161 prefix_lat = 0.5 * (max_lat + min_lat) 

162 prefix_lon = 0.5 * (max_lon + min_lon) 

163 bbox_disp = f'[({", ".join(f"{round(v, 3) :7.03f}" for v in (min_lat, min_lon,max_lat, max_lon))}]' 

164 log.debug('Identified bounding box lat, lon in %s for prefix %s' % (bbox_disp, prefix)) 

165 log.debug(('Set center of prefix map to lat, lon = (%f, %f) for prefix %s' % (prefix_lat, prefix_lon, prefix))) 

166 prefix_root = pathlib.Path(FS_PREFIX_PATH) 

167 map_folder = pathlib.Path(prefix_root, prefix) 

168 map_folder.mkdir(parents=True, exist_ok=True) 

169 geojson_path = str(pathlib.Path(map_folder, f'{prefix.lower()}-geo.json')) 

170 with open(geojson_path, 'wt', encoding=ENCODING) as geojson_handle: 

171 json.dump(prefix_store, geojson_handle, indent=2) 

172 

173 html_dict = { 

174 ANCHOR: f'prefix/{prefix}/', 

175 CC_HINT: cc_hint, 

176 cc_page: country_blurb(cc_hint), 

177 Cc_page: country_blurb(cc_hint).title(), 

178 LAT_LON: f'{prefix_lat},{prefix_lon}', 

179 LIB_PATH_KEY: LIB_PATH, 

180 PATH: PATH_NAV, 

181 BASE_URL_TARGET: BASE_URL, 

182 ZOOM: str(DEFAULT_ZOOM), 

183 IC_PREFIX: prefix, 

184 'IrealCAO': ICAO, 

185 'ic_prefix_lower-geo.json': f'{prefix.lower()}-geo.json', 

186 'REGION_AIRPORT_COUNT_DISPLAY': f'{ra_count} airport{"" if ra_count == 1 else "s"}', 

187 'COUNTRY_COUNT_DISPLAY': f'{cc_count} region{"" if cc_count == 1 else "s"}', 

188 'BBOX': f' contained in lat, lon bounding box {bbox_disp}', 

189 FOOTER_HTML_KEY: FOOTER_HTML, 

190 'DATA_ROWS': '\n'.join(data_rows) + '\n', 

191 } 

192 html_page = template.load_html(HTML_TEMPLATE, HTML_TEMPLATE_IS_EXTERNAL) 

193 for key, replacement in html_dict.items(): 

194 html_page = html_page.replace(key, replacement) 

195 

196 html_path = pathlib.Path(my_prefix_path, 'index.html') 

197 with open(html_path, 'wt', encoding=ENCODING) as html_handle: 

198 html_handle.write(html_page) 

199 

200 with open(hulls_index[prefix], 'wt', encoding=ENCODING) as handle: 

201 json.dump(prefix_hull, handle, indent=2) 

202 

203 db.dump_index('hulls', hulls_index) 

204 

205 with open(pathlib.Path(FS_PREFIX_PATH) / 'region-hulls-geo.json', 'wt', encoding=ENCODING) as handle: 

206 json.dump(prefix_hull_store, handle, indent=2) 

207 

208 db.dump_index('region_country', region_country_index) 

209 

210 return 0 

211 

212 

213if __name__ == '__main__': 213 ↛ 214line 213 didn't jump to line 214, because the condition on line 213 was never true

214 sys.exit(main(sys.argv[1:]))