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
« 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."""
3import collections
4import copy
5import datetime as dti
6import json
7import operator
8import os
9import pathlib
10import sys
11from typing import List, Union
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
19THIS_YY_INT = int(dti.datetime.now(dti.UTC).strftime('%y'))
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'
26AIRP = 'airport'
27RUNW = 'runways'
28FREQ = 'frequencies'
29LOCA = 'localizers'
30GLID = 'glideslopes'
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'
50icao = 'icao_lower'
51LAT_LON = 'LAT_LON'
52cc_page = 'cc_page'
53Cc_page = 'Cc_page'
55ATTRIBUTION = f'{KIND} {ITEM} of '
57Point = collections.namedtuple('Point', ['label', 'lat', 'lon'])
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
62REGION_COUNTRY_DATA = {
63 'region': '',
64 'url': '',
65 'country': '',
66}
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
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')
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)
91 with open(table_index[prefix], 'rt', encoding=ENCODING) as handle:
92 table_store = json.load(handle)
94 hulls_index[prefix] = db.hull_path(prefix) # noqa
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}'
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))
109 airports = sorted(table_store['airports'], key=operator.itemgetter('icao'))
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)
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 )
140 prefix_hull = hull.extract_prefix_hull(prefix, region_name, trial_coords)
141 hull.update_hull_store(prefix_hull_store, prefix_hull)
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)
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)
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)
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)
200 with open(hulls_index[prefix], 'wt', encoding=ENCODING) as handle:
201 json.dump(prefix_hull, handle, indent=2)
203 db.dump_index('hulls', hulls_index)
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)
208 db.dump_index('region_country', region_country_index)
210 return 0
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:]))