2019年1月31日木曜日

パナソニックミュージーアム

鶴見緑地の帰りにパナソニックミュージーアムに寄ってみた。

「松下幸之助歴史館」は幸之助大好きにはたまらないだろう。 「ものづくりイズム館」は、スタッフが付き添わないと、奥まで観ることができない。でも、 結構楽しい。

無料なんで、近くにいったら訪ずれてみたいという気持はあったのだが。

駐輪場がおまけ扱い。

ままチャリ数台分だけ。サイクルスタンドは当然無い。 イズム館の方は立看だけ。 パナソニックは高価なPOS自転車も扱ってるのに。

それと、 些細な事 だが、

歴史館で、 ホールから出てきたスーツ姿のネームプレートを首から下げた若者たちが、 「キンタ○○○○○○○」と大声で雑談していた。

また、図面を広げて話し合っている数人は、 来館者が歩いてきても、展示物の前を動こうとしない。 閉館後やれじゃなく気持。

企業イズム?の一端を見せようとする場かと思うのだが。

内容は良いのに、後味が良くない。

手作り?サイクルスタンドがある蕎麦屋

手作りだよね。サイクルスタンド。

あずま というお店。

蕎麦は、しっかりと蕎麦。 汁は甘め。蕎麦湯はトロリ。 辛味大根はきちんと辛い。

ご主人はビッグバイクを所有。でも、 ロードバイクを半年前に始めたそうだ。

メインの道路から直接アクセスできないので、 分りにくい。あと、少々割高かも。

2019年1月21日月曜日

gpxのログをgrid patternで表示してみる

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()

2019年1月20日日曜日

「はとむぎの杜」でお茶。でポータブルスピーカーが寿命。

奈良盆地へは自転車で時々でかける。 天理方面の時は、 中西ピーナッツ で小袋を購入する。 種類が多いので、数回で飽きることはない。 平日でも駐車場がかなり埋まる人気店のようだ。

で、その帰り道で立ち寄ってみたのが はとむぎの杜 。 たまたまだったのだが、あたりだった。パンが旨い。

値段はかなり高めだが、 イートインの鳩麦茶(レモングラス入りとか)が無料なので、 そのような利用なら、ペイする感じ。

健康指向の店で女性客が多く、ジジイには似合わない。 まあ時間をずらせば許してくれるだろう。

この時は「おやき」を頂いた。 ちなみにサイクルスタンドは無い。 販売量が少なめなのか、 13時なのに売り切れがあった。

あと、 携行しているポータブルスピーカーの電池が、4時間もたなくなった。 カタログ上は8時間。2016年11月購入だから、2年とちょっと。 さすがに、3時間ほどでは辛い。

結構、気に入ってたので、バッテリーだけ交換できないかとチャレンジしたが、 バッチリ接着剤で固定されてるようで、私には無理だった。 しかし、2000円を切る安物だけど、バスレフしている。裏のダクトはまだ不明だけどパッシブラジエータが付いている。

次を物色する必要あり。 シングルスピーカーでよいので、 10時間使えて、SD対応、簡易防水で小振りなのが良い。 あるかな?


今まで使っていたやつと同じ面相のスピーカーを見付けた。 こいつ だ。 外形寸法とか、オレンジのシリコンっぽいやつとか、 今回ダメになったやつとほぼ同じに見えてしまう。 写真では随分大きいみたいだが、商品パッケージの寸法が13x3.8x6.4cmとなっていて、これが正解なら、ほぼ同じサイズ。

スピーカーのサイズも同じみたいだけど、パッシブが大きくなっているようだ。 音は期待できるかも。 画像ではFMの文字があるが、文字には無い。こんなボタンで同調はできそうにない。 そして価格は6倍。reviewに、Excelent priceとかAmazingとかあるけど、ムムムだな。


追記

電池は18650で、保護回路を別途付けている。 電池とスピーカーの固定方法は、スポンジで押えているだけ。

ちゃちい造りなのに、お気に入りだったのは、 パッシブラジエータがあることだけだったかもしれない。

あと、電池は劣化していたのではなく、 電池を繋ぐ半田が片側はずれていたことが原因のようだ。 破壊しなければ自分で修理可能だったかも。 改造して遊ぶ要素もあったかも。軽率だったと反省。

2019年1月6日日曜日

エディタ(emacs, neovim)の行番号をリラティブにしてみる

使っているemacsが26.1になったので、 何が変ったのか調べてみた。

素人なので、エディタを使うのに影響が無いことは想像できるが、 興味本位で一応読んでみた。 私の設定では、<F1> n で読むことができた。

自分でも直感的に判る内容がいくつかあったので、試してみた。

  1. Emacs now uses double buffering to reduce flicker.

    もともと、フリッカーが気になるような状況ではないので、違いが判らなかった。

  2. New user option 'mouse-drag-and-drop-region.

    一応、設定してみたが、使わないだろう。ノートパソコンの画面が狭いので、 キーボードの方を使うのが良い。

  3. Emacs now supports optional display of line numbers in the buffer.

    これには結構興味が湧いた。 行番号を表示させると、emacsの反応が著しく遅くなるので、 非表示にしていたからだ。 そこで、今回以下の設定を追加してみた。 どこかで見つけたやつのコピペなんだが、メモ忘れです。

    (setq-default display-line-numbers-type 'visual
            display-line-numbers-current-absolute t
            display-line-numbers-width 4
            display-line-numbers-widen t)
    (add-hook 'text-mode-hook #'display-line-numbers-mode)
    (add-hook 'prog-mode-hook #'display-line-numbers-mode)
    

    使っているうちに反応が鈍く感じるかもしれないが、 それまでは試してみる。

    この表示方法は、vimの方が嬉しいかもと探してみたら、あった。設定は以下。

    " set line number
    " Since Vim 7.4, enabling number and relativenumber
    Plug 'jeffkreeftmeijer/vim-numbertoggle'
    

    随分以前から設定出来たんだと知った。

    こちらは、`jk` で便利そうなんで、そのままになりそうだ。

他にも沢山記述があるが、自分には縁が無いように思えた。 今迄動かないと思っていたものが動いたり、その逆があるかもしれない。

今年の初ライドは、穏やかで風がない日に、芦屋浜から甲子園へ。

麻のボディタオル

2018年の秋(まだ、自転車を封印してない)、 近江上布伝統産業会館 で、興味からボディタオルを購入した。 お、よかった。: 自然派パン工房 ふるさとの道 ほぼ毎日風呂で使ってきて、ついに寿命がきたようだ。 お店の方に、「糸が痩せて破れてくる」まで使える、と...