gpxpy で extentionsが使えるようになったみたいなので、 少し試してみる意味もあって、 gpxのログを読み込んで、その概要を掃き出すスクリプトを作ってみた。
せっかくなので、どの方面へ出掛けたログかを直感的に見えるように、 小さな画像ファイルを作成する機能も付けてみた。ファイルマネージャとかで気楽にpreviewできるはず。
ログの概要出力はこんな感じ。 私の場合は、extentionsの中身はハートレートだけなので、割と簡単?だった。
画像化の方は、 地理院の三次メッシュ のどこを通過したか をグリッドで表現したつもり。 三次メッシュは、ひとコマ約1kmということだが、 私の場合は自転車のログがほとんどなので、ひとコマ約5kmにしてみた。 以下がその出力例。
上は、mlterm(xterm も ok)を使えば、lsix で画像表示可能という記事を見たので、 インストして試してみた次第。 また、 MOONGIFT にお世話になってしまった。
正しい画像が出来ているかは検証してないが、 まあ、雰囲気だけだから、という感じ。 出発点に何かキーワードが欲しいけど、ノーアイデア。
地理院から
市町村のデータ(市区町村役場データ)を
落としてきて、スタート地点に一番近い庁舎の住所を番地抜きで加えてみた。
引っ越してくる前のやつとの区別はできる。
あと、古いgpxデバイスのログが扱えるかが不安。
[201906] 位置の情報量としては、 同じ地理院の位置参照情報ダウンロードサービス の方が充実しているが、ファイルがでかすぎる。
[201903] 古いデータもやってみた。 gpsの機種が古いので、extensionの判定を追加する必要があったが、なんとか表示できた。 あと、日付も画像に追加した。
[201904] gpsのログは、フォルダにまとめているので、 ファイルマネージャーで使っているrangerの設定ファイル(rfile.conf)に、 サムネイル表示のため `sxiv -t` を追加した。 これで、いつ頃どの方面に出掛けたのか分かり易くなった。かも。
ext png, has sxiv, X, flag f = sxiv -t *.png
以下がpythonスクリプト(前のやつ)。
#!~/.pyenv/shims/python """Make grid pattern image of gpx log file.""" import sys import math import logging from pathlib import Path from statistics import mean import gpxpy import logzero from PIL import Image, ImageDraw, ImageFont from pytz import timezone from dateutil import parser a_div = math.pi / 4.0 ang_lst = [[-0.5 * a_div, 0.5 * a_div, "NN"], [0.5 * a_div, 1.5 * a_div, "NE"], [1.5 * a_div, 2.5 * a_div, "EE"], [2.5 * a_div, 3.5 * a_div, "SE"], [3.5 * a_div, 4.5 * a_div, "SS"], [-4.5 * a_div, -3.5 * a_div, "SS"], [-3.5 * a_div, -2.5 * a_div, "SW"], [-2.5 * a_div, -1.5 * a_div, "WW"], [-1.5 * a_div, -0.5 * a_div, "NW"]] def set_direction(s_p, e_p): """Calculate distance and direction.""" dist_m = gpxpy.geo.distance(s_p[0], s_p[1], None, e_p[0], e_p[1], None) ang_ra = math.atan2(e_p[1] - s_p[1], e_p[0] - s_p[0]) dir_char = "--" if dist_m != 0: for a_lst in ang_lst: if a_lst[0] <= ang_ra <= a_lst[1]: dir_char = a_lst[2] return dist_m, dir_char def show_info(g_f, info_lst, op_lst, img_file): """Show gpx info.""" jt_st, jt_en, mv_dist, stp_dist, max_speed, mv_time, stp_time, up_hill, down_hill = info_lst hr_mean, hr_max, lat_min, lat_max, lon_min, lon_max, dis_far, dir_st = op_lst logzero.logger.info("# {0:s}".format(str(g_f.name))) mes_str = "- time ({0:%Y-%m-%d %H:%M:%S %Z})".format(jt_st) mes_str += " - ({0:%Y-%m-%d %H:%M:%S %Z})".format(jt_en) logzero.logger.info(mes_str) mes_st = '- trip {0:.1f}km'.format(mv_dist) mes_st += '({0:d}({1:d})min)'.format(int(mv_time), int(stp_time)) mes_st += ' spd.{0:5.1f}({1:.1f})km'.format(mv_dist / mv_time * 60.0, max_speed * 3.6) mes_st += ' u/d({0:.1f}/{1:.1f})'.format(up_hill, down_hill) logzero.logger.info(mes_st) mes_st += ' {0:6.1f} m'.format(stp_dist / 1.0) mes_st = "- HR mean {0:d} max {1:d}".format(hr_mean, hr_max) mes_st += " (rat {0:6.3f}-{1:6.3f} lon {2:7.3f}-{3:7.3f})".format(lat_min, lat_max, lon_min, lon_max) logzero.logger.info(mes_st) mes_st = "- farthest {0:.1f}km {1:s}".format(dis_far, dir_st) logzero.logger.info(mes_st) logzero.logger.info("- Img {0:s}".format(img_file.name)) def parse_track(gpx_obj): """Parse track data.""" hr_val, lat_val, lon_val = [], [], [] for track in gpx_obj.tracks: for segment in track.segments: for point in segment.points: if point.extensions: ext_obj = int(point.extensions[0].getchildren()[0].text.strip()) else: ext_obj = 0 hr_val.append(ext_obj) lat_val.append(point.latitude) lon_val.append(point.longitude) return hr_val, lat_val, lon_val def to_mapcode(lat_v, lon_v): """Calculate geo code by geographic code system.""" code_1st_a, mod_a = divmod(lat_v * 60.0, 40.0) code_1st_b, mod_b = int(lon_v - 100.0), lon_v - int(lon_v) code_2nd_a, mod_c = divmod(mod_a, 5.0) code_2nd_b, mod_d = divmod(mod_b * 60.0, 7.5) code_3rd_a, mod_e = divmod(mod_c * 60.0, 30.0) code_3rd_b, mod_f = divmod(mod_d * 60.0, 45.0) mod_e += mod_f code_1st = int(code_1st_a) * 100 + int(code_1st_b) code_2nd = int(code_2nd_a) * 10 + int(code_2nd_b) code_3rd = int(code_3rd_a) * 10 + int(code_3rd_b) mesh_code = code_1st * 10000 + code_2nd * 100 + code_3rd return mesh_code def diff_mapcode(pre_c, now_c): """Diff mapcode.""" c_str = "{0:08d}".format(pre_c) pre_a = int(c_str[0:2]) * 100 + int(c_str[4:5]) * 10 + int(c_str[6:7]) pre_l = int(c_str[2:4]) * 100 + int(c_str[5:6]) * 10 + int(c_str[7:8]) c_str = "{0:08d}".format(now_c) now_a = int(c_str[0:2]) * 100 + int(c_str[4:5]) * 10 + int(c_str[6:7]) now_l = int(c_str[2:4]) * 100 + int(c_str[5:6]) * 10 + int(c_str[7:8]) return pre_a - now_a, pre_l - now_l, now_a, now_l def calc_geocode(lat_val, lon_val): """Calculate geo code.""" code_pre, code_now = 0, 0 code_lst, inc_lst = [], [] diff_a, diff_l = 0, 0 for v_lat, v_lon in zip(lat_val, lon_val): mesh_code = to_mapcode(v_lat, v_lon) code_lst.append(mesh_code) code_pre = code_now code_now = mesh_code if mesh_code != code_pre: diff_a, diff_l, m_a, m_l = diff_mapcode(code_now, code_pre) inc_lst.append([diff_a, diff_l, m_a, m_l]) return inc_lst def gen_grid(inc_lst, img_f, mv_dist, g_mes): """Generate grid path image.""" image = Image.new(mode='L', size=(600, 600), color=255) skip_div = 5 n_div = int(max((mv_dist + 3.0 * skip_div) * 2.5, 2) / 2.0) n_step = int(image.width / n_div) draw = ImageDraw.Draw(image) y_start, y_end = 0, image.height x_start, x_end = 0, image.width for x in range(0, image.width, n_step * skip_div): line = ((x, y_start), (x, y_end)) draw.line(line, fill=140) for y in range(0, image.height, n_step * skip_div): line = ((x_start, y), (x_end, y)) draw.line(line, fill=140) m_xo, m_yo = inc_lst[1][3], inc_lst[1][2] for m_po in inc_lst[1:-1]: p_x = int(image.width / 2 + (m_po[3] - m_xo - 0.5) * n_step) p_y = int(image.height / 2 - (m_po[2] - m_yo - 0.5) * n_step) c_col = 32 if (m_po[2] == m_xo and m_po[3] == m_yo) else 120 draw.rectangle((p_x, p_y, p_x + n_step, p_y + n_step), fill=(c_col), outline=(128)) dd_cell = int(image.width / 15) p_x = int(image.width / 2 - 0.5 * dd_cell) p_y = int(image.height / 2 + 0.5 * dd_cell) draw.rectangle((p_x, p_y, p_x + dd_cell, p_y - dd_cell), fill=(2), outline=(2)) d_font = ImageFont.truetype("arial.ttf", 82) draw.text((0, image.height / 22), g_mes, font=d_font) del draw image.save(img_f, 'png', quality=70, optimize=True) def parse_gpx(g_f, g_obj): """Parse gpx file.""" st_time, en_time = g_obj.get_time_bounds() jst_st = timezone('UTC').localize(st_time).astimezone(timezone('Asia/Tokyo')) jst_en = timezone('UTC').localize(en_time).astimezone(timezone('Asia/Tokyo')) mv_time, stp_time, mv_dist, stp_dist, max_speed = g_obj.get_moving_data(stopped_speed_threshold=0.4) up_hill, down_hill = g_obj.get_uphill_downhill() info_lst = [jst_st, jst_en, mv_dist / 1000.0, stp_dist / 1000.0, max_speed, mv_time / 60.0, stp_time / 60.0, up_hill, down_hill] hr_val, lat_val, lon_val = parse_track(g_obj) st_point = [lat_val[1], lon_val[1]] farthest = 0.0 for lat_p, lon_p in zip(lat_val, lon_val): distance, dr_car = set_direction(st_point, [lat_p, lon_p]) if distance > farthest: farthest = distance dir_char = dr_car op_lst = [int(mean(hr_val)), int(max(hr_val)), min(lat_val), max(lat_val), min(lon_val), max(lon_val), farthest / 1000., dir_char] inc_lst = calc_geocode(lat_val, lon_val) g_mes = " {0:s}{1:d}".format(op_lst[7], int(op_lst[6])) g_mes += "({0:d}k)".format(int(info_lst[2])) img_file_name = g_f.with_suffix('.png') gen_grid(inc_lst, img_file_name, int(farthest / 1000), g_mes) return info_lst, op_lst, img_file_name def main(): """Do main prcess.""" args = sys.argv gpx_log_path = Path.cwd() / Path(args[1]) with open(gpx_log_path, 'r') as gpx_file: gpx_obj = gpxpy.parse(gpx_file) gspc_lst, op_lst, img_file = parse_gpx(gpx_log_path, gpx_obj) show_info(gpx_log_path, gspc_lst, op_lst, img_file) if __name__ == '__main__': LOG_FORMAT = '%(color)s[%(module)s:%(lineno)d]%(end_color)s %(message)s' FORMATTER = logzero.LogFormatter(fmt=LOG_FORMAT) logzero.setup_default_logger(formatter=FORMATTER) logzero.loglevel(logging.INFO) logzero.logfile("/home/hogehoge/.logs/log.log", maxBytes=3e5, backupCount=3) main()
0 件のコメント:
コメントを投稿