diff --git a/wip/274/274-lyrics-index.csv b/wip/274/274-lyrics-index.csv new file mode 100644 index 0000000..028fd21 --- /dev/null +++ b/wip/274/274-lyrics-index.csv @@ -0,0 +1,100 @@ +start,stop,start_s,stop_s,sung,object,kind,km,lat,lon,km_from,km_to +00:14.54,00:17.89,14.54,17.89,夕張郡由仁町川端,夕張郡由仁町川端,district,38.41,42.91715,141.90896,38.41,38.41 +00:17.89,00:19.67,17.89,19.67,川端橋,川端橋,bridge,38.41,42.91715,141.90896,, +00:25.04,00:28.41,25.04,28.41,夕張郡栗山町滝下,夕張郡栗山町滝下,district,39.80,42.91197,141.91642,39.80,41.86 +00:28.41,00:32.06,28.41,32.06,川端発電所,北海道企業局 川端発電所,power station,39.80,42.91197,141.91642,, +00:32.06,00:35.44,32.06,35.44,滝下八幡神社,滝下八幡神社,Shinto shrine,40.16,42.91183,141.92472,, +00:35.44,00:39.84,35.44,39.84,滝ノ下信号場,滝ノ下信号場,place (locality),40.57,42.90630,141.92320,, +00:39.84,00:42.01,39.84,42.01,雨霧橋,雨霧橋,bridge,41.86,42.91003,141.94197,, +00:42.45,00:45.15,42.45,45.15,夕張郡由仁町川端,夕張郡由仁町川端,district,43.03,42.91604,141.95220,43.03,43.03 +00:45.15,00:46.69,45.15,46.69,竜仙橋,竜仙橋,bridge,43.03,42.91604,141.95220,, +00:47.63,00:50.08,47.63,50.08,夕張郡栗山町滝下,夕張郡栗山町滝下,district,44.00,42.91449,141.96321,44.00,44.00 +00:50.08,00:52.88,50.08,52.88,草木橋,草木橋,bridge,44.00,42.91449,141.96321,, +00:52.88,00:54.73,52.88,54.73,滝の上こ線橋,滝の上こ線橋,bridge,44.00,42.91449,141.96321,, +00:56.44,00:59.95,56.44,59.95,夕張市滝ノ上,夕張市滝ノ上,district,44.62,42.91152,141.97021,44.62,45.29 +00:59.95,01:03.47,59.95,63.47,滝の上発電所,北海道企業局 滝の上発電所,power station,44.62,42.91152,141.97021,, +01:03.47,01:06.94,63.47,66.94,滝ノ上信号場,滝ノ上信号場,railway signal post,45.21,42.91000,141.97642,, +01:06.94,01:08.78,66.94,68.78,滝ノ上神社,滝ノ上神社,Shinto shrine,45.29,42.90979,141.97873,, +01:10.50,01:12.05,70.50,72.05,夕張市紅葉山,夕張市紅葉山,district,49.71,42.91482,142.02439,49.71,55.60 +01:12.05,01:13.86,72.05,73.86,十三里信号場,十三里信号場,place (locality),49.71,42.91482,142.02439,, +01:13.86,01:16.44,73.86,76.44,十三里橋,十三里橋,bridge,50.28,42.91772,142.03043,, +01:16.44,01:18.78,76.44,78.78,紅葉山駐車帯,紅葉山駐車帯,parking,50.52,42.91840,142.03331,, +01:18.78,01:21.06,78.78,81.06,瑞穂橋,瑞穂橋,bridge,52.11,42.93244,142.03548,, +01:21.06,01:24.44,81.06,84.44,セブンイレブン夕張紅葉山店,セブンイレブン夕張紅葉山店,convenience store,52.47,42.93507,142.03683,, +01:24.44,01:27.06,84.44,87.06,新夕張駅,新夕張駅,railway station,52.54,42.93670,142.03648,, +01:27.06,01:30.02,87.06,90.02,紅葉山郵便局,紅葉山郵便局,post office,52.61,42.93642,142.03925,, +01:30.02,01:32.30,90.02,92.30,紅葉山神社,紅葉山神社,Shinto shrine,52.85,42.93788,142.03956,, +01:32.30,01:35.42,92.30,95.42,セイコーマートもみじやま店,セイコーマートもみじやま店,convenience store,52.99,42.93910,142.04167,, +01:35.42,01:37.92,95.42,97.92,紅葉山橋,紅葉山橋,bridge (structure),53.01,42.94353,142.04306,, +01:37.92,01:39.46,97.92,99.46,紅楓橋,紅楓橋,bridge,53.13,42.93855,142.04317,, +01:39.46,01:41.54,99.46,101.54,ポン久留喜橋,ポン久留喜橋,bridge,53.69,42.93697,142.04976,, +01:41.54,01:43.84,101.54,103.84,久留喜橋,久留喜橋,bridge,53.69,42.93697,142.04976,, +01:43.84,01:45.05,103.84,105.05,楓橋,楓橋,bridge,55.41,42.92957,142.06562,, +01:45.05,01:46.47,105.05,106.47,桂橋,桂橋,bridge,55.60,42.93026,142.06775,, +01:46.84,01:48.90,106.84,108.90,夕張市楓,夕張市楓,district,56.60,42.93414,142.07860,56.60,57.84 +01:48.90,01:50.84,108.90,110.84,千紫橋,千紫橋,bridge,56.60,42.93414,142.07860,, +01:50.84,01:52.07,110.84,112.07,楓駐車帯,楓駐車帯,parking,57.08,42.93352,142.08412,, +01:52.07,01:53.93,112.07,113.93,万紅橋,万紅橋,bridge,57.25,42.93274,142.08627,, +01:53.93,01:55.61,113.93,115.61,登川神社,登川神社,Shinto shrine,57.44,42.93395,142.08819,, +01:55.61,01:57.32,115.61,117.32,楓信号場,楓信号場,railway signal post,57.76,42.93157,142.09230,, +01:57.32,01:59.00,117.32,119.00,登川橋,登川橋,bridge,57.84,42.93167,142.09327,, +01:59.00,02:00.74,119.00,120.74,登川郵便局,登川郵便局,post office,57.84,42.93185,142.09335,, +02:01.26,02:02.93,121.26,122.93,夕張市登川,夕張市登川,district,58.23,42.92922,142.09647,58.23,60.29 +02:02.93,02:04.87,122.93,124.87,花園橋,花園橋,bridge,58.23,42.92922,142.09647,, +02:04.87,02:06.63,124.87,126.63,登川トンネル,登川トンネル,tunnel,60.29,42.92478,142.11599,, +03:30.80,03:34.55,210.80,214.55,勇払郡むかわ町穂別長和,勇払郡むかわ町穂別長和,district,62.36,42.90892,142.12482,62.36,62.94 +03:34.55,03:36.77,214.55,216.77,オマナイ橋,オマナイ橋,bridge,62.36,42.90892,142.12482,, +03:36.77,03:39.41,216.77,219.41,桂木橋,桂木橋,bridge,62.94,42.90434,142.12726,, +03:41.13,03:44.62,221.13,224.62,勇払郡むかわ町穂別稲里,勇払郡むかわ町穂別稲里,district,64.44,42.89157,142.12305,64.44,71.86 +03:44.62,03:46.67,224.62,226.67,大和橋,大和橋,bridge,64.44,42.89157,142.12305,, +03:46.67,03:48.38,226.67,228.38,豊進橋,豊進橋,bridge,66.69,42.87371,142.13048,, +03:48.38,03:51.61,228.38,231.61,豊進神社,豊進神社,Shinto shrine,66.78,42.87318,142.13135,, +03:51.61,03:53.14,231.61,233.14,豊里橋,豊里橋,bridge,67.48,42.87311,142.13854,, +03:53.14,03:55.52,233.14,235.52,稲豊橋,稲豊橋,bridge,67.60,42.87313,142.14000,, +03:55.52,03:57.78,235.52,237.78,稲里橋,稲里橋,bridge,68.63,42.87144,142.15164,, +03:57.78,04:01.08,237.78,241.08,稲里簡易郵便局,稲里簡易郵便局,post office,69.29,42.86734,142.15774,, +04:01.08,04:03.50,241.08,243.50,佐主橋,佐主橋,bridge,69.48,42.86894,142.16085,, +04:03.50,04:05.59,243.50,245.59,穂別ダム神社,穂別ダム神社,Shinto shrine,71.86,42.88475,142.17737,, +04:05.96,04:10.08,245.96,250.08,勇払郡むかわ町穂別長和,勇払郡むかわ町穂別長和,district,72.02,42.88619,142.17748,72.02,75.28 +04:10.08,04:13.02,250.08,253.02,長和トンネル,長和トンネル,tunnel,72.02,42.88619,142.17748,, +04:13.02,04:15.39,253.02,255.39,穂別大橋,穂別大橋,bridge,72.29,42.88820,142.17918,, +04:15.39,04:18.81,255.39,258.81,山水橋,山水橋,bridge,72.90,42.89187,142.18476,, +04:18.81,04:23.20,258.81,263.20,沼の沢橋,沼の沢橋,bridge,74.31,42.88830,142.20015,, +04:23.20,04:24.97,263.20,264.97,稲里トンネル,稲里トンネル,tunnel,75.28,42.88343,142.20951,, +04:26.00,04:29.74,266.00,269.74,勇払郡むかわ町穂別福山,勇払郡むかわ町穂別福山,district,76.97,42.87802,142.22782,76.97,88.25 +04:29.74,04:31.50,269.74,271.50,ペンケ橋,ペンケ橋,bridge,76.97,42.87802,142.22782,, +04:31.50,04:33.62,271.50,273.62,下福山橋,下福山橋,bridge,77.05,42.88572,142.23487,, +04:33.62,04:35.02,273.62,275.02,オロロップ橋,オロロップ橋,bridge,77.55,42.87850,142.22868,, +04:35.02,04:37.07,275.02,277.07,福山橋,福山橋,bridge,79.11,42.89249,142.24390,, +04:37.07,04:38.70,277.07,278.70,福山神社,福山神社,Shinto shrine,79.67,42.89114,142.25175,, +04:38.70,04:41.02,278.70,281.02,福山大橋,福山大橋,bridge,80.24,42.88678,142.25416,, +04:41.02,04:43.29,281.02,283.29,千坂橋,千坂橋,bridge,85.40,42.88901,142.29725,, +04:43.29,04:45.12,283.29,285.12,オコタン橋,オコタン橋,bridge,85.87,42.89179,142.30059,, +04:45.12,04:46.22,285.12,286.22,モトツトンネル,モトツトンネル,tunnel,86.24,42.89321,142.30463,, +04:46.22,04:48.98,286.22,288.98,モトツ橋にモトツシェルター,モトツ橋,bridge,87.12,42.89931,142.31156,, +04:46.22,04:48.98,286.22,288.98,モトツ橋にモトツシェルター,モトツシェルター,shed,87.12,42.89931,142.31156,, +04:48.98,04:51.39,288.98,291.39,福山トンネル,福山トンネル,tunnel,87.21,42.89988,142.31229,, +04:51.39,04:53.61,291.39,293.61,福穂橋,福穂橋,bridge,88.04,42.90322,142.32132,, +04:53.61,04:55.52,293.61,295.52,穂高トンネル,穂高トンネル,tunnel,88.25,42.90398,142.32368,, +05:22.60,05:25.76,322.60,325.76,勇払郡占冠村中央,勇払郡占冠村中央,district,90.58,42.90958,142.35128,90.58,96.43 +05:25.76,05:28.88,325.76,328.88,穂高橋,穂高橋,bridge,90.58,42.90958,142.35128,, +05:28.88,05:31.52,328.88,331.52,高翠橋,高翠橋,bridge,90.90,42.90889,142.35485,, +05:31.52,05:34.95,331.52,334.95,占翠橋,占翠橋,bridge,91.14,42.90869,142.35768,, +05:34.95,05:38.08,334.95,338.08,名石駐車場,名石駐車場,parking,93.58,42.90980,142.36986,, +05:38.08,05:40.99,338.08,340.99,名石橋,名石橋,bridge,93.67,42.91029,142.37052,, +05:40.99,05:43.96,340.99,343.96,赤岩大橋,赤岩大橋,bridge,94.35,42.91317,142.37731,, +05:43.96,05:48.62,343.96,348.62,渓流大橋,渓流大橋,bridge,95.37,42.91502,142.38677,, +05:48.62,05:50.73,348.62,350.73,日高トンネル,日高トンネル,tunnel,96.43,42.91588,142.39911,, +07:11.39,07:14.14,431.39,434.14,沙流郡日高町日高,沙流郡日高町日高,district,100.60,42.89710,142.42716,100.60,100.60 +07:14.14,07:16.38,434.14,436.38,日夕橋,日夕橋,bridge,100.60,42.89710,142.42716,, +07:28.85,07:31.68,448.85,451.68,沙流郡日高町宮下町,沙流郡日高町宮下町,district,102.36,42.88427,142.43553,102.36,102.36 +07:31.68,07:34.09,451.68,454.09,日高神社,日高神社,Shinto shrine,102.36,42.88427,142.43553,, +07:42.90,07:45.51,462.90,465.51,沙流郡日高町本町東,沙流郡日高町本町東,district,103.12,42.87791,142.44001,103.12,103.29 +07:45.51,07:49.88,465.51,469.88,新川橋,新川橋,bridge,103.12,42.87791,142.44001,, +07:49.88,07:52.76,469.88,472.76,セイコーマート日高道の駅店,セイコーマート日高道の駅店,convenience store,103.29,42.87734,142.44216,, +07:52.76,07:55.11,472.76,475.11,日高郵便局,日高郵便局,post office,103.29,42.87748,142.44195,, +07:56.69,07:59.36,476.69,479.36,沙流郡日高町千栄,沙流郡日高町千栄,district,104.52,42.87724,142.45788,104.52,104.52 +07:59.36,08:02.15,479.36,482.15,北電日高発電所,北海道電力 日高発電所,power station,104.52,42.87724,142.45788,, +08:03.90,08:06.38,483.90,486.38,沙流郡日高町日高,沙流郡日高町日高,district,106.22,42.89314,142.46755,106.22,106.22 +08:06.38,08:09.44,486.38,489.44,右左府発電所,右左府発電所,power station,106.22,42.89314,142.46755,, +08:10.93,08:12.66,490.93,492.66,東に続く,,closing,,,,, diff --git a/wip/274/274-lyrics-index.py b/wip/274/274-lyrics-index.py new file mode 100644 index 0000000..abced6d --- /dev/null +++ b/wip/274/274-lyrics-index.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Build the lyric index: every sung line resolved to its roster entry and timing. + +Reads 274.lrc and 274-lookup.csv and writes 274-lyrics-index.csv, one row per +resolved object per lyric line, in song order. + + start / stop the line's own timestamp and the next timestamp in the file, + blank spacer lines included — so every line has a stop. + sung the text exactly as it appears in the lyric + object the roster's full name for it (the power stations carry their + operator prefix there but are sung without it) + kind, lat, lon from the roster + +Two lines need more than a name lookup: + + * モトツ橋にモトツシェルター is one structure driven through and two facilities + in the registers, so it emits TWO rows sharing one timing. + * The district headers (勇払郡むかわ町穂別福山 and the rest) are addresses, not + points, and three of them repeat because the road re-enters the district — + so they cannot resolve by name alone. Each is resolved from the run of + objects it introduces: the header takes the first object's coordinates, and + km_from / km_to span the run. + + Not from 274-districts.json, which is sampled along the road and therefore + cannot describe a district the road does not physically enter. 宮下町 has no + band at all, 本町東 exists only as 本町東一丁目, and 千栄's band sits at + 107.25-107.75 while the station it introduces is at km 104.52, 666 m off the + carriageway inside 千栄. + + python3 274-lyrics-index.py +""" +import csv +import io +import os +import re +import sys + +HERE = os.path.dirname(os.path.abspath(__file__)) +p = lambda n: os.path.join(HERE, n) + +# lyric text -> roster object name(s). The sung form drops operator prefixes, +# and one line names two facilities. +ALIAS = { + '川端発電所': ['北海道企業局 川端発電所'], + '滝の上発電所': ['北海道企業局 滝の上発電所'], + '北電日高発電所': ['北海道電力 日高発電所'], + 'モトツ橋にモトツシェルター': ['モトツ橋', 'モトツシェルター'], +} +# sung lines that are deliberately not objects +NON_OBJECT = {'東に続く'} + +TS = re.compile(r'^\[(\d+):(\d+(?:\.\d+)?)\](.*)$') + + +def mmss(t): + return '%02d:%05.2f' % (int(t // 60), t % 60) + + +def read_lrc(path): + out = [] + for line in io.open(path, encoding='utf-8'): + m = TS.match(line.rstrip('\n')) + if m: + out.append((int(m.group(1)) * 60 + float(m.group(2)), m.group(3).strip())) + return out + + +def main(): + rows = list(csv.DictReader(io.open(p('274-lookup.csv'), encoding='utf-8'))) + by_obj = {r['object']: r for r in rows} + addresses = {r['address'] for r in rows} + + lrc = read_lrc(p('274.lrc')) + texts = [(i, t, s) for i, (t, s) in enumerate(lrc) if s] + + def targets(sung): + if sung in ALIAS: + return [by_obj[n] for n in ALIAS[sung] if n in by_obj] + if sung in by_obj: + return [by_obj[sung]] + return [] + + def run_after(i, sung): + """The objects a district header introduces, up to the next header.""" + got = [] + for j, _t, s in texts: + if j <= i: + continue + if s in addresses and not targets(s): + break + got.extend(targets(s)) + return [r for r in got if r['address'] == sung] or got + + out, unresolved = [], [] + for i, t, sung in texts: + stop = lrc[i + 1][0] if i + 1 < len(lrc) else None + got = targets(sung) + + if got: + for r in got: + out.append(dict( + start=t, stop=stop, sung=sung, object=r['object'], + kind=r['kind'], km=float(r['km']), + lat=float(r['lat']), lon=float(r['lon']), + km_from='', km_to='')) + continue + + # district header: resolve from the run of objects it introduces + run = run_after(i, sung) if sung in addresses else [] + if run: + kms = [float(r['km']) for r in run] + first = run[0] + out.append(dict( + start=t, stop=stop, sung=sung, object=sung, kind='district', + km=float(first['km']), lat=float(first['lat']), lon=float(first['lon']), + km_from='%.2f' % min(kms), km_to='%.2f' % max(kms))) + elif sung in NON_OBJECT: + out.append(dict(start=t, stop=stop, sung=sung, object='', kind='closing', + km='', lat='', lon='', km_from='', km_to='')) + else: + unresolved.append((t, sung)) + + cols = ['start', 'stop', 'start_s', 'stop_s', 'sung', 'object', 'kind', + 'km', 'lat', 'lon', 'km_from', 'km_to'] + with io.open(p('274-lyrics-index.csv'), 'w', encoding='utf-8', newline='') as f: + w = csv.writer(f) + w.writerow(cols) + for r in out: + w.writerow([ + mmss(r['start']), mmss(r['stop']) if r['stop'] is not None else '', + '%.2f' % r['start'], '%.2f' % r['stop'] if r['stop'] is not None else '', + r['sung'], r['object'], r['kind'], + ('%.2f' % r['km']) if r['km'] != '' else '', + ('%.5f' % r['lat']) if r['lat'] != '' else '', + ('%.5f' % r['lon']) if r['lon'] != '' else '', + r['km_from'], r['km_to']]) + + kinds = {} + for r in out: + kinds[r['kind']] = kinds.get(r['kind'], 0) + 1 + print('%d lyric lines -> %d index rows' % (len(texts), len(out))) + print(' ' + ', '.join('%s %d' % kv for kv in sorted(kinds.items()))) + if unresolved: + print('UNRESOLVED:', file=sys.stderr) + for t, s in unresolved: + print(' %s %s' % (mmss(t), s), file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + main()