てーしたはなしではねーんだけれども。
METAR Search (with simple parsing) で、ICAO コードのプレフィクスだけ入力すると地図でジャンプ出来ると便利だなーと思って。
SkyVector からお取り寄せてさらに必要情報だけにそぎ落としたこんな csv:
1 code,name,lon,lat
2 LECO,A Coruna Airport,-8.37733333,43.302
3 KOCH,A L Mangham Jr Regional Airport,-94.71016667,31.57783333
4 ...
を持っていて、また、「プレフィクスと国のマッピングテーブル」として、こんな(スラッシュ区切りの) csv:
1 Country/ICAO Code Prefixes
2 AFGHANISTAN/OA
3 ALGERIA/DA
4 ...
5 CHILE/SC,NE
6 CHINA/RC,VH,ZB,ZG,ZH,ZJ,ZL,ZP,ZS,ZT,ZU,ZW,ZY,VM
7 COLOMBIA/SK
8 ...
9 US VIRGIN ISLANDS/MI,TI
10 USA/K,X,PA,PB,PF,PJ,PL,PM,PO,PP,PH,PW
11 UZBEKHISTAN/UT
を持ってた。今は「国」そのものには興味はなくて、地図でざっくり飛べればいい、ってニーズなので、「プレフィクス→プレフィクスごと位置平均」のマッピングを作りたい、ってわけだ。javascript から使いたいので json が出力な。
1 # -*- coding: utf-8 -*-
2 import csv
3 from math import degrees, radians, cos, sin, atan, atan2
4 import cmath
5 import numpy as np
6
7 def mean_of_angle(a):
8 """
9 see http://hhsprings.pinoko.jp/site-hhs/2016/07/mean-of-circular-quantities-%E3%81%A7%E6%82%A9%E3%82%80%E3%80%82/
10 """
11 rho = sum(cmath.rect(1, radians(d)) for d in a)
12 if np.isclose(rho, 0):
13 return np.nan # or None
14 rho /= len(a)
15 th = cmath.phase(rho)
16 return degrees(th)
17
18 # Country/ICAO Code Prefixes
19 prefixes = {1: set(), 2: set(), 3: set()}
20 for row in csv.reader(open("icao_code_prefixes.txt"), delimiter="/"):
21 if row[0] == "Country":
22 continue
23 spl = row[1].split(",")
24 for p in spl:
25 prefixes[len(p)].add(p)
26
27 lons = {}
28 lats = {}
29
30 # code, name, lon, lat
31 for row in csv.reader(open("knownloc_data_raw.csv")):
32 if row[0] == "code":
33 continue
34 code, _, lon, lat = row
35 lon, lat = float(lon), float(lat)
36
37 for plen, prs in [(plen, prefixes[plen]) for plen in reversed(prefixes.keys())]:
38 p = code[:plen]
39 if p in prs:
40 if p not in lons:
41 lons[p] = []
42 lats[p] = []
43 lons[p].append(lon)
44 lats[p].append(lat)
45 break
46
47 result = {}
48 for p in lons:
49 result[p] = (round(mean_of_angle(lons[p]), 3), round(mean_of_angle(lats[p]), 3))
50
51 import json
52 # json.dumps の indent を使うと折り返しすぎちゃってかえって読みにくいの…
53 print(json.dumps(result).replace("],", "],\n").replace("{", "{\n ").replace("}", "\n}"))
てわけで、手持ちのデータからはこんなテーブルが出来た:
1 var prefixlocmap = {
2 "AG": [160.053, -9.428],
3 "AY": [147.124, -6.024],
4 "BG": [-50.753, 67.282],
5 "BI": [-19.902, 64.809],
6 "C": [-95.686, 54.286],
7 "DA": [3.421, 32.443],
8 "DB": [2.383, 6.356],
9 "DF": [-2.917, 11.761],
10 "DG": [-0.168, 5.604],
11 "DI": [-6.136, 6.691],
12 "DN": [6.008, 8.002],
13 "DR": [6.309, 14.521],
14 "DT": [9.836, 34.8],
15 "DX": [1.168, 8.313],
16 "EB": [4.376, 50.838],
17 "ED": [9.753, 51.003],
18 "EE": [24.259, 58.77],
19 "EF": [25.311, 63.25],
20 "EG": [-2.784, 52.711],
21 "EH": [5.302, 52.058],
22 "EI": [-8.057, 53.208],
23 "EK": [9.139, 56.258],
24 "EL": [6.205, 49.623],
25 "EN": [14.798, 65.876],
26 "EP": [19.175, 51.989],
27 "ES": [16.25, 60.708],
28 "ET": [9.495, 50.935],
29 "EV": [22.204, 56.933],
30 "EY": [23.465, 55.367],
31 "FA": [26.299, -28.489],
32 "FB": [25.963, -21.115],
33 "FC": [13.569, -4.533],
34 "FD": [31.512, -26.444],
35 "FE": [18.52, 4.398],
36 "FG": [9.256, 2.829],
37 "FH": [-14.394, -7.97],
38 "FI": [60.522, -20.094],
39 "FJ": [72.412, -7.313],
40 "FK": [12.051, 6.106],
41 "FL": [27.646, -15.381],
42 "FMC": [44.276, -12.172],
43 "FME": [55.471, -21.106],
44 "FMM": [48.435, -18.456],
45 "FMN": [47.33, -14.492],
46 "FMS": [46.956, -25.038],
47 "FN": [15.087, -10.747],
48 "FO": [11.369, -0.365],
49 "FP": [6.713, 0.378],
50 "FQ": [35.766, -18.227],
51 "FS": [55.607, -4.497],
52 "FT": [17.58, 10.936],
53 "FV": [29.128, -18.896],
54 "FW": [34.373, -14.732],
55 "FX": [27.558, -29.444],
56 "FY": [17.949, -21.952],
57 "GA": [-7.763, 14.771],
58 "GB": [-16.658, 13.342],
59 "GC": [-16.078, 28.291],
60 "GE": [-2.956, 35.28],
61 "GF": [-13.195, 8.617],
62 "GG": [-15.656, 11.889],
63 "GM": [-7.36, 32.107],
64 "GO": [-15.214, 14.136],
65 "GQ": [-13.198, 19.655],
66 "GU": [-13.612, 9.577],
67 "GV": [-23.594, 16.162],
68 "HA": [38.799, 8.975],
69 "HD": [43.16, 11.547],
70 "HE": [32.208, 28.435],
71 "HK": [37.235, -1.595],
72 "HL": [13.278, 32.895],
73 "HR": [30.138, -1.968],
74 "HS": [31.244, 15.057],
75 "HT": [35.314, -5.649],
76 "HU": [32.441, 0.041],
77 "K": [-93.174, 38.189],
78 "LA": [19.721, 41.415],
79 "LB": [25.862, 42.743],
80 "LC": [33.151, 34.834],
81 "LD": [16.04, 44.375],
82 "LE": [-2.577, 40.37],
83 "LF": [2.368, 46.471],
84 "LG": [23.915, 38.156],
85 "LH": [19.282, 46.957],
86 "LI": [12.301, 42.476],
87 "LJ": [14.822, 46.019],
88 "LK": [15.353, 49.756],
89 "LL": [35.048, 31.895],
90 "LM": [14.477, 35.858],
91 "LO": [14.229, 47.587],
92 "LP": [-17.828, 37.588],
93 "LQ": [18.05, 44.127],
94 "LR": [25.048, 45.977],
95 "LS": [8.099, 46.83],
96 "LT": [34.86, 38.985],
97 "LU": [28.308, 47.543],
98 "LW": [21.182, 41.571],
99 "LX": [-5.35, 36.151],
100 "LY": [20.246, 43.841],
101 "LZ": [19.045, 48.734],
102 "MB": [-71.704, 21.609],
103 "MD": [-69.822, 18.879],
104 "MG": [-90.344, 15.086],
105 "MH": [-86.917, 15.188],
106 "MK": [-77.35, 18.22],
107 "MM": [-100.935, 22.452],
108 "MN": [-84.444, 12.726],
109 "MP": [-80.559, 8.845],
110 "MR": [-84.229, 10.125],
111 "MS": [-89.091, 13.566],
112 "MT": [-72.244, 19.156],
113 "MU": [-78.928, 21.683],
114 "MW": [-80.62, 19.49],
115 "MY": [-78.081, 25.799],
116 "MZ": [-88.308, 17.539],
117 "NC": [-160.399, -15.788],
118 "NF": [178.002, -17.901],
119 "NFT": [-174.483, -19.868],
120 "NG": [173.146, 1.381],
121 "NGF": [179.196, -8.525],
122 "NI": [-169.927, -19.078],
123 "NS": [-171.354, -14.081],
124 "NT": [-149.611, -17.557],
125 "NW": [166.216, -22.016],
126 "NZ": [174.044, -40.609],
127 "OA": [66.217, 35.161],
128 "OB": [50.634, 26.271],
129 "OE": [42.615, 24.589],
130 "OI": [52.257, 32.583],
131 "OJ": [35.668, 31.102],
132 "OK": [47.98, 29.227],
133 "OL": [35.49, 33.819],
134 "OM": [55.38, 24.919],
135 "OO": [56.187, 20.316],
136 "OP": [71.143, 30.091],
137 "OR": [44.987, 33.59],
138 "OS": [37.888, 35.284],
139 "OT": [51.496, 25.218],
140 "OY": [48.433, 14.657],
141 "PA": [-154.929, 61.825],
142 "PF": [-159.165, 63.255],
143 "PG": [145.264, 14.272],
144 "PH": [-157.286, 20.967],
145 "PK": [169.502, 7.893],
146 "PL": [-157.35, 1.986],
147 "PM": [-177.381, 28.201],
148 "PP": [-162.723, 65.319],
149 "PT": [149.138, 7.334],
150 "PW": [166.637, 19.282],
151 "RC": [120.845, 23.783],
152 "RJ": [137.033, 36.549],
153 "RK": [127.323, 36.303],
154 "RO": [126.309, 25.472],
155 "RP": [121.791, 11.788],
156 "SA": [-63.074, -35.45],
157 "SB": [-48.69, -15.524],
158 "SC": [-72.462, -38.846],
159 "SE": [-79.902, -1.229],
160 "SF": [-57.767, -51.683],
161 "SG": [-56.065, -25.973],
162 "SK": [-74.934, 6.521],
163 "SL": [-64.292, -16.812],
164 "SM": [-55.197, 5.631],
165 "SO": [-52.362, 4.82],
166 "SP": [-75.116, -10.561],
167 "SU": [-56.606, -33.954],
168 "SV": [-68.369, 9.569],
169 "SY": [-58.181, 6.652],
170 "TA": [-61.793, 17.137],
171 "TB": [-59.492, 13.075],
172 "TD": [-61.347, 15.442],
173 "TF": [-61.261, 15.428],
174 "TG": [-61.786, 12.004],
175 "TI": [-64.888, 18.019],
176 "TJ": [-66.448, 18.35],
177 "TK": [-62.654, 17.258],
178 "TL": [-60.972, 13.876],
179 "TN": [-66.666, 14.47],
180 "TQ": [-63.054, 18.205],
181 "TR": [-62.193, 16.791],
182 "TT": [-61.085, 10.873],
183 "TU": [-64.542, 18.445],
184 "TV": [-61.278, 12.923],
185 "TX": [-64.679, 32.364],
186 "UA": [68.084, 48.656],
187 "UB": [47.505, 40.254],
188 "UC": [74.657, 42.085],
189 "UD": [44.128, 40.449],
190 "UE": [129.773, 62.093],
191 "UG": [43.012, 41.819],
192 "UH": [151.147, 53.924],
193 "UI": [106.707, 53.118],
194 "UK": [29.888, 48.821],
195 "UL": [34.049, 62.019],
196 "UM": [26.863, 53.727],
197 "UN": [84.956, 54.99],
198 "UR": [43.057, 44.957],
199 "US": [65.165, 57.981],
200 "UT": [65.443, 39.711],
201 "UU": [38.485, 54.934],
202 "UW": [50.742, 54.039],
203 "VA": [74.671, 21.868],
204 "VC": [80.505, 6.732],
205 "VD": [104.328, 12.479],
206 "VE": [87.99, 24.334],
207 "VG": [91.11, 23.05],
208 "VH": [113.915, 22.309],
209 "VI": [76.946, 29.371],
210 "VL": [103.347, 18.127],
211 "VM": [113.591, 22.149],
212 "VN": [85.358, 27.697],
213 "VO": [78.677, 13.523],
214 "VQ": [89.425, 27.404],
215 "VR": [73.529, 4.192],
216 "VT": [100.82, 13.585],
217 "VV": [106.756, 13.817],
218 "VY": [96.055, 19.304],
219 "WA": [120.171, -4.255],
220 "WB": [114.857, 4.453],
221 "WI": [103.283, -1.551],
222 "WM": [101.777, 4.423],
223 "WS": [103.92, 1.377],
224 "Y": [139.526, -27.094],
225 "ZB": [114.619, 39.215],
226 "ZG": [112.51, 24.266],
227 "ZH": [114.025, 32.651],
228 "ZJ": [109.936, 19.119],
229 "ZL": [106.185, 35.48],
230 "ZM": [106.767, 47.843],
231 "ZP": [102.943, 25.104],
232 "ZS": [119.892, 30.319],
233 "ZU": [105.796, 28.946],
234 "ZW": [81.748, 41.727],
235 "ZY": [124.241, 42.558]
236 };
むろんこれを「絶対的な正」として使うのはやめてください。これはあくまでも「ワタシに既知な情報だけを使って平均を取ったもの」に過ぎませんし、また、狭い国ならともかくアメリカ(「K」)なんかはこんなん全然役にも立たない。せめて州のレベルまで絞り込めないとちっとも嬉しかないわけね。
まぁともあれ METAR Search (with simple parsing) が少しは使いやすくなったかなと思う。