2019年2月28日木曜日

Emacsのsmartparen,expandregionを見直し?

私はDebian上のエディタは、emacs(26.1)をメインにしている。 まあ、vimが周辺をサポートしているのようなのだが。

用途は、org-mode(9.2.1)をメモに使っているのと、少し長めのpythonスクリプト。

Emacsを使っていると、smartparens が邪魔に感じる事がある。 parensがらみだ。細かく設定した対応する方法があるのかもしれないが、 私には無理っぽいので、hydraを使って逃げてみた。

emacs24.4以降、autopairs の機能が含められたらしく、 私にsmartparensは不要かも思うが、まあ頑張ってみている。 expand-region と組み合わせると、もしかしたら 使い易いくなるのかも、とか。 objedのようなパッケージを使った方がよかったのかもしれない。

smartparen (201908up)

smartparensは、show-smartparens-mode does not highlight the contained expression らしいので、 見た目の設定は、 rainbow-delimiters にやらせる。

(use-package smartparens :ensure t
 :diminish smartparens-mode
 :hook (
  ((css-mode html-mode web-mode mhtml-mode) . smartparens-mode)
  ((rst-mode markdown-mode text-mode) . smartparens-mode)
  (org-mode . smartparens-mode)
  (prog-mode . smartparens-mode)
  )
 :init
 (progn
  (use-package smartparens-config)
  (smartparens-global-mode 1)
  (show-smartparens-global-mode 1))
 :commands smartparens-config
 :config
 (electric-pair-mode nil)
 ;; (show-smartparens-mode t) ; global-modeにすべきか
 (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil)
 (sp-local-pair 'rst-mode "`" "`") ;; adds local pair
 (sp-local-pair 'emacs-lisp-mode "`" nil :when '(sp-in-string-p))
 ;;
 (defun my-sp-wrap-backquote ()
   "Wrap following sexp in square brackets."
   (interactive)
   (sp-wrap-with-pair "`"))
 (defun my-sp-wrap-squote ()
   "Wrap following sexp in square brackets."
   (interactive)
   (sp-wrap-with-pair "'"))
)
;;
;;
(use-package rainbow-delimiters :ensure t
  :diminish
  :hook (
    ((css-mode html-mode web-mode) . rainbow-delimiters-mode)
    ((org-mode rst-mode markdown-mode text-mode) . rainbow-delimiters-mode)
    ((python-mode rust-mode) . rainbow-delimiters-mode)
  )
  :config
  (setq rainbow-delimiters-outermost-only-face-count 1)
  (show-paren-mode 1)
  )

expand-region

expand-regionは、 modeによって使えるコマンドが異なったりするようで注意。 過信せず、少しづつ確認してゆくつもり。

(use-package expand-region :ensure t
:bind (
  ("C-M-:" . er/expand-region)
  ("C-M-;" . er/contract-region)
  )
:config
  (require 'expand-region)
 )

hydra

hydra を使うことで、キーバインドの見直し改善。 やってみると、which-keyより素人向けかもしれないと思った。 グローバルに使うキーを設定するのがよいのだろう。

沢山メニュー項目を用意しても使えない。 シンプルにすべきだったと反省。

expand-regionでは、body-preで一旦markさせる。 どこで見付けたのか、メモ忘れ。

(use-package hydra :ensure t)

(defhydra hyd-sparen (:hint nil)
 "SmartParens"
   ("u" sp-up-sexp "Up" :column "Move")
   ("d" sp-down-sexp "Down")
   ("f" sp-forward-sexp "forward")
   ("b" sp-backward-sexp "backward")
   ("a" sp-beginning-of-sexp "begin")
   ("e" sp-end-of-sexp "end")
   ("N" sp-backward-down-sexp "back and down")
   ("P" sp-backward-up-sexp "back and up")

   ("h" sp-backward-slurp-sexp "back slurp" :column "Slurping barfing")
   ("H" sp-backward-barf-sexp  "back barf")
   ("l" sp-forward-slurp-sexp  "forward slurp")
   ("L" sp-forward-barf-sexp  "forward slurp")

   ("m" sp-mark-sexp "Mark" :column "Edit")
   ("w" sp-copy-sexp "Copy")
   ("t" sp-transpose-sexp "transpose")
   ("c" sp-change-inner "change inner")
   ("C" sp-change-enclosing "change enclose")
   ("k" sp-kill-sexp "kill")
   ("K" sp-backward-kill-sexp "back kill")

   ("0" sp-unwrap-sexp "Unwrap" :column "Wrap")
   ("R" sp-rewrap-sexp "rewrap")
   ("U" sp-backward-unwrap-sexp "bk unweap")
   ("[" sp-wrap-square "wrap[]")
   ("(" sp-wrap-round "wrap()")
   ("{" sp-wrap-curly "wrap{}")
   ("C" sp-wrap-cancel "Cancel")

   ("S" sp-split-sexp "Split" :column "Juggling")
   ("s" sp-splice-sexp "splice")
   ("r" sp-raise-sexp "raise")
   ("j" sp-join-sexp "join")
   ("t" sp-transpose-sexp "transpose")
   ("A" sp-absorb-sexp "absorb")
   ("E" sp-emit-sexp "emit")
   ("o" sp-convolute-sexp "convolute")

   ("q" nil "Exit" :exit t :column "Quit")
   ;("q" nil "cancel" :color blue)
)


(defhydra hyd-mark (:body-pre (er/expand-region 1)
                    :color red :hint nil)
 "ExpandRegion"
  ("w" er/mark-word "Word" :column "Object1")
  ("c" er/mark-comment "Comment")
  ("s" er/mark-symbol "Symbol")
  ("S" er/mark-symbol-with-prefix "Prefixed symbol")
  ("d" er/mark-defun "def/func")

  ("q" er/mark-inside-quotes "In quotes" :column "Pairs")
  ("Q" er/mark-outside-quotes "Out quotes")
  ("(" er/mark-inside-pairs "In paren")
  (")" er/mark-outside-pairs "Out paren")

  ("p" er/mark-text-paragraph "Paragraph" :column "Object2")
  ("t" er/mark-inner-tag "In tag")
  ("T" er/mark-outer-tag "Ou tag")
  ("." er/expand-region "Expand region")
  ("," er/contract-region "Contract region")

  ("u" er/mark-url "Url" :column "others")
  ("E" er/mark-email "Email")
  ("a" er/mark-html-attribute "HTML attribute")

  ("q" nil "Quit" :exit t :column "Quit")
)

(global-set-key (kbd "M-p") 'hyd-sparen/body)
(global-set-key (kbd "M-m") 'hyd-mark/body)

smartparensとexpand-regionのメニューをひとまとめにしたいが、 しばらく使ってみて、機能を絞り込んでから。

2019年2月26日火曜日

Emacsの補完で使ってるyasnippet見直し?[0708up]

Debian上のエディタは、emacs(26.1)をメインにしている。 最近、expandregion, smartparens, yasnippet, companyが うまく機能してないように感じ始めた。

companyに続いて yasnippet を見直してみた。

ただ無知なだけかもしれないが、 smartparensと、特にyasnippetの `<` で始まるやつとかが困る(追記:解決かも)。
[201904] 間違ってた。‘org-insert-structure-template’ というやつだった。

知らないうちに後側の括弧が追加されるとかも。

今回は、 ivy-yasnippets を使って ivy-mode配下でsnippetの一覧リストから絞り込むようにしてみた。逃げた設定かも。

previewを無効にした方がいい感じ、と思うが、続けてみないと。

companyからyasnippetを除外するかは、今後判断。

(use-package yasnippet :ensure t
 :diminish
 ;; :after (counsel)
 :after ivy ;; [201904]
 :bind (
  :map yas-minor-mode-map
    ("C-c s i" . yas-insert-snippet)  ; 既存へ挿入
    ("C-c s n" . yas-new-snippet)     ; 新規作成
    ("C-c s v" . yas-visit-snippet-file)  ; 既存の閲覧編集
    ("C-c s l" . yas-describe-tables) ; 選択可能なスニペットの一覧表示
    ("C-c s g" . yas-reload-all))
 :hook ((prog-mode . yas-minor-mode)
        (markdown-mode . yas-minor-mode)
        (rst-mode . yas-minor-mode)
        (org-mode . yas-minor-mode))
 :config
 (yas-reload-all) ;;[201904]
 (yas-minor-mode) ;;[201904]
 ;; (setq yas-snippet-dirs '("~/.emacs.d/mysnippets")) ;;[201907]
 ;;  Original value ("/home/hogehoge/.emacs.d/snippets")
 ;;  に戻す。別ディレクトだと、うまくいかない。
 (use-package yasnippet-snippets :ensure t) ;; mainのdirsは自動設定

 ;; ivy-yasnippets : call ivy-yasnippet in yas-minor-mode.
 (use-package ivy-yasnippet :ensure t
  :bind ("C-c y s" . ivy-yasnippet)
  :config
   (setq ivy-yasnippet-expand-keys "smart") ; nil "always" , "smart"
   ; https://github.com/seagle0128/.emacs.d/blob/master/lisp/init-ivy.el
   (advice-add #'ivy-yasnippet--preview :override #'ignore)
 )
; こいつをdataに入れれば、indentが反映される。
; # expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))
)

[20190310] org-modeで、source blocks(babel)を挿入する場合は、yasnippetを使わずに、 Template expansion for Org structure を使えば良いようだ。 ここを 見付けられたのがラッキーだった。 defaultでは読み込みされないというような記事も見掛けたような気がする。

(use-package org-tempo :ensure nil
  ;; C-c C-, (org-insert-structure-template)
  :defer t
  :after org
  :config
  (add-to-list 'org-structure-template-alist '("R" . "src R"))
)

yasnippet-snippetsのorg-modeのキーの設定を書き直せば良いのだが、 updateされる毎に作業するのは??なので、こちらのキーバインドを覚えることにする。

[201904] org-modeのブロックは、hydraでメニュー化した方がよさそう。 ここをコピペ修正すればすぐ使えるかも。

2019年2月23日土曜日

Emacsの補完で使ってるcompany見直し?[201907]

Debian上のエディタに、emacs(26.1)をメインに使用している。 まあ、vimが周辺をサポートしているのだが。

org-modeでメモが主用途。それと、少し長めのpythonスクリプト。 素人なので、コピペ基本の設定。

最近、expandregion, smartparens, yasnippet, companyが うまく機能してないように感じ始めている。

なので、まず補完のcompanyを見直してみた。のだったが。

package managerは、use-package。ivy-mode + company 環境。

まず、eclipse,c,c++はやらないので、以下のようにbackendを変更しようとした。

;; https://github.com/company-mode/company-mode/issues/407 + others
(setq company-backends
  '(company-ispell
    company-yasnippet
    (company-dabbrev-code company-gtags company-keywords) 
    ;   grouping分らん、 withとかなんとか
    ;   company-keywords   ; keywords by company default
    ;   company-bbdb       ; database for mail address......
    ;   company-gtags      ; あまり使用感がない?
    company-files          ; files & directory
    company-capf           ; completion-at-point-functions
    ;company-dabbrev-code  ; dabbrev for code
    ;company-oddmuse   ; WikiWiki engine Oddmuse such as emacswiki.org
    ;company-abbrev    ; 静的略称展開
    company-dabbrev    ; 動的略称展開
))

だが、この設定はglobal-modeで有効になるようだが、 company-webでは反映されなかった。ようだ。 company-safe-backendsを使っているようで、 こいつは触らない方が良いみたい。

This variable may be risky if used as a file-local variable.

ただ既に、ispellが含まれてたりする。 標準に含まれてないbackendを使おうとする場合、 その作法というか、色々あるのかもしれない。あくまで想像だが。

なので、 パッケージに含まれる company-ispellconpany-yasnippetcompany-backend に 追加して、company-other-backend を指定する方法にしてみた。

;; C-h v company-backends から
;; ‘company-begin-backend’ can be used to start a specific backend,
;; ‘company-other-backend’ will skip to the next matching backend in the list.

現状、以下。

(use-package company :ensure t
 :diminish company-mode 
 :bind (
   ("C-c y /" . company-yasnippet) ;; [201907]
   ;; backend 切り替えできないので
  ; :map company-mode-map ; 通常 On なので注意 
  :map company-active-map ; 選択している最中
   ("M-p" . nil)  ; ミスタッチ予防
   ("M-n" . nil)
   ([tab] . company-complete-common-or-cycle)
   ("C-n" . company-select-next)
   ("C-p" . company-select-previous)
   ("C-o" . company-other-backend)
   ;; 他のbackend候補を表示させる。firsrがispellだから。
   ;; firstを変更するキーバインドが欲しいかも 
   ("C-v" . company-next-page) 
   ("M-v" . company-previous-page)
   ("C-s" . company-filter-candidates) ;; flx みたいに絞りこめる。
  :map company-search-map  ; company-filter-candidatesでの設定かと。
   ("C-p" . company-select-previous) 
   ("C-n" . company-select-next)
   ;("C-o" . company-other-backend) ; ダメ、機能しない。
   ; これが使えると嬉しいかも。
 )        
 :init (global-company-mode) 
 :config     
  ; optionの設定  
  (setq company-show-numbers nil
        ;; company-auto-expand t (20190410)
        company-auto-complete nil ;; (20190410)
        ;; company-transformers '(
        ;;    company-sort-by-occurrence
        ;;    company-sort-by-backend-importance
        ;; );; Original value was nil  
        company-transformers nil ;; set nil for speed up
        company-selection-wrap-around t 
        company-idle-delay 0.1
        completion-ignore-case t
        company-dabbrev-downcase nil
        company-dabbrev-other-buffers t
        ; use only buffers with same major-mode for company-dabbrev
        ; company-minimum-prefix-length 3 ; 2文字ならyasnippetを直接
        company-minimum-prefix-length 1   ; C-o で他の候補になるから。
        company-selection-weap-around t
        company-tooltip-align-annotations t  ; align to the right border
        ;; company-sort-by-occurrence t (20190410)
        company-tooltip-limit 10)

  (add-to-list 'company-backends 'company-yasnippet) 
  (add-to-list 'company-backends 'company-ispell)

  ; start backend
  (setq company-begin-backend 'company-ispell) 
  ; 普段はメモとかだからispellにしておく。
  ;   elpy-mode : firstはispellではない。companyを使ってない?
  ;   web-mode  : firstはispellのまま。

  ; skip to the next matching backend in the list.
  (setq company-other-backend '(company-etags company-eclim company-bbdd
                                company-xcode company-cmake company-oddmuse))

  ; たまにhtmlを直接編集するからcompany-webを追加。
  ; company-safe-backendsに追加されるが、company-web-htmlはリストにない。
  ; [201907] うまく動いてないので、一旦削除。
  ;
  ;(use-package company-web :ensure t
  ; :config 
  ; (add-hook 'web-mode-hook 
  ;   (lambda ()
  ;      (add-to-list (make-local-variable 'company-backends) 
  ;                   'company-nxml) 
  ;      (add-to-list (make-local-variable 'company-backends)
  ;                   'company-web-html)
  ;      (add-to-list (make-local-variable 'company-backends)
  ;                   'company-css)
  ;      )) 
  ; )

  ; company-prescient レスポンスがかなり低下するので止め 20190414
  ; company-flx       うまく動かない。company-filter-candidatesでよしとする。

  ; custom face  ;; T510の貧相な発色にあわせてる。
  (set-face-attribute 'company-tooltip nil
          :foreground "black" :background "gray75")
  (set-face-attribute 'company-tooltip-common nil
          :foreground "black" :background "gray55")
  (set-face-attribute 'company-tooltip-common-selection nil
          :foreground "white" :background "DodgerBlue4") 
  (set-face-attribute 'company-tooltip-selection nil                                       
          :foreground "black" :background "DodgerBlue3")
  (set-face-attribute 'company-preview nil 
          :foreground "gray90" :background "RoyalBlue4" :underline t)
  (set-face-attribute 'company-preview-common nil
          :foreground "white" :background "DodgerBlue4" :underline t) 
  (set-face-attribute 'company-scrollbar-fg nil
          :background "orange") 
  (set-face-attribute 'company-scrollbar-bg nil
          :background "gray40")
)

pythonには、elpyを使っている。elpy-modeでは elpy-module-companyが勝手に設定してくれるようで (https://github.com/jorgenschaefer/elpy/issues/1397)、 jedi-coreをemacs側にインストして、必要なpythonのモジュールをインストして環境を整えれば、まあ動いているようだ。

とりあえず、少しは良くなったみたい。

しかし、私には難しすぎるし時間がかかった。 smartparenとか、他にも調べたいのがあるが、ハードル高い。 しばらくこれで様子見か。

[201907] : 上記の設定は、デフォの設定であって、 modeに応じて変更させる時は、localな設定にするのかも、と試してみる。

(use-package company-web :ensure t
 :after company
 :hook (
    (web-mode . (lambda () (add-to-list (make-local-variable 'company-safe-backends) '(company-web-html . "WEB"))))
    (css-mode . (lambda () (add-to-list (make-local-variable 'company-safe-backends) '(company-css . "CSS"))))
  )
)

ompany-safe-backends には追加されるようなので、一応継続。
調べながら使うのが基本なので、 問題にならなかったのだろう。ちゃんと調べないと、と反省。
GTAGS が無いとエラーになるので、面倒でも作成しないといけない。
きっと、設定方法があるのだろうけど。

2019年2月17日日曜日

オフセットが選べるボトルケージ

ダウンチューブ下に付けるボトルケージを新調した。

私は小柄でフレームが小さいので、 ダウンチューブ下のダボにケージを付けると、 ツール缶はごく小さいもの限定されてしまう。

小柄なライダーのアルアルだと思う。

オフセットするためのアタッチメントがshimanoから販売されているのが、 非常に高価。 自分で加工して穴位置をずらしても良いが、 ケージに余分なスペースが無いのが普通だ。

しかし幸運なことに、昨年末、 タイヤ購入のため海外通販のサイトを探していた時、 穴が4つあるボトルケージを見付けてしまった。 で、タイヤに加えてこのケージもポチってしまった。

Syncrosだけど、安すぎて逆に不安。実物の写真が下。

ミノウラの定番と、Syncrosを比べてみるとこうなる。

ミノウラの方はタイヤと干渉するが、Syncrosでは、泥除けがなんとか設置できる。

もう少しオフセットできればとか、 デザインが好みでないとか、 耐久性が不安とか、欲をいえばきいりがない。 取り敢えず悩みがひとつ解消したことにしておこう。

今時期2ボトルは不要なので出動はないが、 そのうち試してみたい。


実はその後、国内でも同様の商品を見付けた。 giosの こいつ
でもやはり高価で、外通の手数料以上の差がある。 海外通販を利用してもよい場合には、色々探しまわると良いことがあるかもしれない。

2019年2月14日木曜日

シャープミュージアム

先月はパナだったけど、 今月は シャープミュージーアム に行ってみた。 もち、自転車で。

基本は事前予約のようだが、ホームページのQAにあるように、 フラッと立ち寄るのもアリ。

入館料が大人1000円と高価。 施設廃止に向けて設定された価格なのかもしれない。

シャープ総合開発センターの敷地内なので、 ゲートの警備員さんに見学にきたことを伝えて、 担当の方に連絡してもらう。

ゲートから歩いて30秒でミュージアムに到着。 そこで担当者の方に入館料を支払い、レシートを受け取る。 この日は館員による案内はできないとのことで自由見学。 写真撮影は自由。

見学には1時間ほど必要とあったが、確かに1時間ほど必要だった。 館員による案内がないのにだ。じじいの郷愁感にリンクしたようだ。

  • シャーペン(やはりコレ)

  • MZ(懐しいかぎり)。X68000もあった。

  • ゼンマイ式カミソリ(実用的とは思えないけど)

  • ポータブルラジオ(on 自転車のハンドル)

  • Vista (ビデオを流してたPC。頑張ってる。)

他にもいっぱい。私としては、NetWalker PC-Z1がみつからなかったのが残念。 商品としては失敗だったのかもしれないが、欲しかった一品。

製品とかのファクトを淡々と展示していて解説が非常に乏しい。 事前にインプットしておいた方が楽しいだろう。 館員の案内がどのようなものか興味あり。

あと半額だったらいいのに。


飲料の自販機、トイレ、喫煙コーナーあり。ロビーは広くソファーで休息できる。 サイクルスタンド、駐輪場無し。ロッカー無し。

床はワックスがのったPタイル。よく滑るので注意。

トイレは案内表示で自販機奥へ行くと、 滑りやすい階段を降りることになるので、受付横奥を使うのがベター。 そこは展示スペース出口になるので、案内を避けてるのだろうが、 安全には替えられない。


白梅の香りで小休止が心地よい。 風はすごかったけど。

2019年2月10日日曜日

PythonでExifをsqliteのdbにしてみた

写真のExifをsqlite3でデータベース化して、 指定した時刻近傍のファイルリストを作成してみた。

データベースを一度使ってみたかったからというか、 写真を閲覧して探しだす作業がすごくメモリを食って私の環境には厳しい。 なので、可能性を試そうかと考えた。

  • linux(debian)のpyenv環境で、3.7.2。
  • Exifはpyexifinfoを使う。exiftoolのラッパー。
  • sqliteはpython標準で。
  • dbの中身は、小さくすべきと思って、画像ファイル名(パス含む)、ユリウス年、緯度、経度。
  • ユリウス年は julianを使って処理。

スクリプト

header and oters

#!~/.pyenv/shims/python
"""Generate exif(Exchangeable Image File Format) database."""

import logging
import sqlite3
from pathlib import Path
from datetime import datetime
from contextlib import closing

import exifread
import julian
import logzero
import pyexifinfo as pex
from pytz import timezone

TABLE_EXIF = '''create table if not exists exifdata (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name VARCHAR(128), juda REAL, bla REAL, blo REAL,
                time_stamp DEFAULT CURRENT_TIMESTAMP
                )'''
SQL_E = '''insert into exifdata (name, juda, bla, blo) values (?,?,?,?)'''
TFORM = "{0:%Y-%m-%d %H:%M:%S %Z}"

文字列からdatetimeとjulian年を作成する。

私が保管している写真のExifは、フォーマットなど色々だったので、 いくつか対応するようにした。't_zone'は、文字列がUTCかどうかの指定。

def tstr2dtime(t_str, zo, _type):
    """Convert string to datetime."""
    if _type == 0:
        # 2018:06:22 11:20:42
        t_t = datetime.strptime(t_str, '%Y:%m:%d %H:%M:%S')
    elif _type == 1:
        # 2018-06-22 11:20:42
        t_t = datetime.strptime(t_str, '%Y-%m-%d %H:%M:%S')
    elif _type == 2:
        # 2019-01-04T07:06:23Z
        t_t = datetime.strptime(t_str, '%Y-%m-%dT%H:%M:%SZ')
    elif _type == 3:
        # 2019-01-04 07:06:23.000011
        t_t = datetime.strptime(t_str, '%Y-%m-%d %H:%M:%S.%f')
    elif _type == 4:
        # 2019:01:04 07:06:23.000011
        t_t = datetime.strptime(t_str, '%Y:%m:%d %H:%M:%S.%f')
    elif _type == 5:
        # 2005:01:30 09:59:48+09:00
        t_t = datetime.strptime(t_str, '%Y:%m:%d %H:%M:%S%z')

    if zo == 'UTC':
        st_utc = t_t
        st_loc = st_utc.astimezone(timezone('Asia/Tokyo'))
    else:
        st_loc = t_t
        st_utc = st_loc.astimezone(timezone('UTC'))

    ju_val = julian.to_jd(st_utc)
    mju_val = ju_val - 2400001

    return st_utc, st_loc, ju_val, mju_val

緯度経度をdegreeに変換

def conv2deg(lat_v):
    """Convert to degree."""
    a_deg, a_min, a_sec = int(lat_v[0]), int(lat_v[2][:-1]), float(lat_v[3][:-1])
    return a_deg + a_min / 60. + a_sec / 3600.

ファイルを指定して、Exifのリストを作成

デジカメによってExifの中身が異るので、 時刻に関連する項目はほぼ全てリスト化してみた。中身が無いのは'None'。

pyexifinfoは、jsonとかcsvで返してくれるので、素人にやさしい。 速度がどうかはわからなし。exiftoolのラッパー。

def get_exifs(p_file):                                                                                     
    """Get EXIF of an image if exists."""                                                                  
    p_data = pex.get_json(p_file)[0]                                                                       
    # logzero.logger.info(p_data)  # show all meta data                                                    
    c_exver = p_data.get('EXIF:ExifVersion', 'None')                                                       
    c_model = p_data.get('EXIF:Model', 'None')                                                             
    c_time = [p_data.get('EXIF:DateTimeOriginal', 'None'),                                                 
              p_data.get('EXIF:CreateDate', 'None'),                                                       
              p_data.get('EXIF:GPSDateStamp', 'None'),                                                     
              p_data.get('EXIF:GPSTimeStamp', 'None'),                                                     
              p_data.get('EXIF:DateTime', 'None'),                                                         
              p_data.get('EXIF:DateTimeDigitized', 'None'),                                                
              p_data.get('File:FileModifyDate', 'None'),                                                   
              p_data.get('EXIF:TimeZoneOffset', 'None')]                                                   
    c_geos = [p_data.get('EXIF:GPSLatitude', 'None'),                                                      
              p_data.get('EXIF:GPSLongitude', 'None'),                                                     
              p_data.get('EXIF:GPSImgDirection', 'None'),                                                  
              p_data.get('EXIF:GPSAltitude', 'None')]                                                      
                                                                                                           
    if 'None' in c_time[2:4]:                                                                              
        # camera の時間はローカルタイムとして扱う。TimeZoneOffset が無いから。                             
        if c_time[0] != 'None':                                                                            
            st_utc, st_loc, ju_val, mju_val = tstr2dtime(c_time[0], 'Asia/Tokyo', 0)                       
        elif c_time[1] != 'None':                                                                          
            st_utc, st_loc, ju_val, mju_val = tstr2dtime(c_time[1], 'Asia/Tokyo', 0)                       
        elif c_time[4] != 'None':                                                                          
            st_utc, st_loc, ju_val, mju_val = tstr2dtime(c_time[4], 'Asia/Tokyo', 0)                       
        else:                                                                                              
            st_utc, st_loc, ju_val, mju_val = tstr2dtime(c_time[6], 'Asia/Tokyo', 5)
            # これしかないデータがあった。                       
    else:                                                                                                  
        # gps時があれば、これをUTCとして使う。                                                             
        st_utc, st_loc, ju_val, mju_val = tstr2dtime(c_time[2] + ' ' + c_time[3], 'UTC', 4)                
                                                                                                           
    if 'None' not in c_geos[0:2]:                                                                          
        # gps の座標情報があるときは、変換する。                                                           
        g_locate = [conv2deg(c_geos[0].split()), conv2deg(c_geos[1].split())]                              
    else:                                                                                                  
        g_locate = [None, None]                                                                            
                                                                                                           
    return [str(p_file), c_exver, c_model, st_loc, ju_val, mju_val, g_locate, c_time, st_utc]              

ユリウス年の区間を指定して抽出

def search_byjud(s_jud, e_jud, sqdb_file):                                             
    """Search picture file in ju_date."""                                              
    logzero.logger.info("- Results:")                                                  
    with closing(sqlite3.connect(sqdb_file)) as conn:                                  
        db_curs = conn.cursor()                                                        
        db_curs.execute("SELECT * FROM exifdata")                                      
        sel_sql = '''select * from exifdata where juda > ? and juda < ?'''             
        for i, row in enumerate(db_curs.execute(sel_sql, (s_jud, e_jud, ))):           
            mes_st = " {0:3d}, {1:s}".format(i, Path(row[1]).name)                     
            p_time = timezone('UTC').localize(julian.from_jd(row[2], fmt='jd'))        
            mes_st += ", " + TFORM.format(p_time.astimezone(timezone('Asia/Tokyo')))   
            mes_st += ", MJD {0:.9f}".format(row[2] - 2400001)                         
            mes_st += ", {0:9.6f},{1:10.6f}".format(row[3], row[4])                    
            logzero.logger.info(mes_st)                                                

メイン

def main():                                                                                                  
    """Do main prcess."""                                                                                    
    # 画像ファイル                                                                                           
    p_path = Path('/home/hogehoge/Pictures/CAMERA')                                                          
    p_files = sorted(list(p_path.glob("NIKON/**/*.JPG")))                                                    
                                                                                                             
    # データベースファイル                                                                                   
    sqldb_file = 'pic_exif.sqlite'                                                                           
                                                                                                             
    with closing(sqlite3.connect(sqldb_file)) as conn:                                                       
        db_curs = conn.cursor()                                                                              
                                                                                                             
        # tableがあれば削除させたい時                                                                        
        # db_curs.execute("DROP TABLE IF EXISTS exifdata")                                                   
                                                                                                             
        db_curs.execute(MAKE_EXID_TABLE)                                                                     
        db_curs.execute("SELECT * FROM exifdata")                                                            
                                                                                                             
        for p_f in p_files:                                                                                  
            # 登録済か確認                                                                                   
            db_curs.execute("SELECT name FROM exifdata WHERE name = ?", (str(p_f),))                         
            ck_row = db_curs.fetchall()                                                                      
            db_curs.execute("SELECT * FROM exifdata")                                                        
            f_nam = "{0:s}:".format(str(p_f.relative_to(p_path)))                                            
                                                                                                             
            if ck_row:                                                                                       
                # 登録あればパス                                                                             
                mes_st = "Has " + f_nam                                                                      
            else:                                                                                            
                # なければ、Exifを読んで登録                                                                 
                p_table = get_exifs(p_f)                                                                     
                p_jdate = p_table[4]                                                                         
                p_lat = p_table[6][0] if p_table[6][0] else 0.0                                              
                p_lon = p_table[6][1] if p_table[6][1] else 0.0                                              
                db_curs.execute(SQL_IN, (str(p_f), p_jdate, p_lat, p_lon))                                   
                                                                                                             
                # メッセージ作成                                                                             
                mes_st = "New " + f_nam + TFORM.format(p_table[3])                                           
                mes_st += ", JD {0:.9f}".format(p_table[4])                                                  
                mes_st += ", PO({0:},{1:})".format(p_table[6][0], p_table[6][1])                             
                                                                                                             
            logzero.logger.info(mes_st)                                                                      
                                                                                                             
        # 終了したら、保存、圧縮。                                                                           
        conn.commit()                                                                                        
        db_curs.execute('VACUUM')                                                                            
                                                                                                             
    # ユリウス年を指定して検索してみる                                                                       
    search_byjud(2457970.53, 2457970.54, sqldb_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("./_logs/log.log", maxBytes=3e5, backupCount=3)                                          
                                                                                                             
    main()                                                                                                   

出力例

$ python exifdb.py                                                                      
 Has zenpad_Z380M/20161219/P_20161219_103516_1_p.jpg:                                   
 Has zenpad_Z380M/20161219/P_20161219_103530_1_p.jpg:                                   
      ;;                                                                                
      ;;                                                                                
 - Results:                                                                             
    0, RIMG1141.JPG, 2017-08-05 09:47:31 JST, MJD 57969.533007292, 35.312625,136.015147 
    1, RIMG1142.JPG, 2017-08-05 09:47:31 JST, MJD 57969.533007292, 35.312625,136.015147 
                                                                                        

このスクリプトに、gpxのログから'gpxpy'の'get_time_bounds'を使って ユリウス年を求めて使うようにすると、以下のような出力が得られる。

- Read log [hoge.gpx]:                                                                                       
  - (2017-08-04 21:01:36 UTC) - (2017-08-05 08:50:11 UTC)                                                    
  - (2017-08-05 06:01:36 JST) - (2017-08-05 17:50:11 JST)                                                    
- Results:                                                                                                   
   0, CAMERA/WG-4T/140_0805/RIMG1139.JPG, 2017-08-05 06:14:17 JST, 2457970.384930555,  0.000000,  0.000000   
   1, CAMERA/WG-4T/140_0805/RIMG1140.JPG, 2017-08-05 09:30:39 JST, 2457970.521286458, 35.274528,136.010953   
   2, CAMERA/WG-4T/140_0805/RIMG1141.JPG, 2017-08-05 09:47:31 JST, 2457970.533007292, 35.312625,136.015147   
   3, CAMERA/WG-4T/140_0805/RIMG1142.JPG, 2017-08-05 09:47:31 JST, 2457970.533007292, 35.312625,136.015147   
       ;                                                                                                     
       ;                                                                                                     

使ってみた感想なんだが、 デジカメの時刻設定がデタラメな時期があったことを知った。だけだった。
次はトラックポイントとの紐付けだと思っていたのに、萎えた。

まあ初心者でも、sqliteが少しは使えたのかも、ということで良しとした。

2019年2月7日木曜日

琵琶湖、夢風車は解体中。

久し振りに琵琶湖大橋を渡ってきたのだが、 ランドマークとして格好の目印だった風車がなくなっていた。

草津グリーンプラザや琵琶湖博物館とかのすぐ脇にあった 風力発電施設である夢風車だ。で、そう解体中だった。

帰宅して調べると、 ここそこ とかに記事があった。 もう少し早めにいけば、もっと感動できたかも。

で、風車もだがクレーンに少し感動。 450tmだそうだ(鳶のおじさん、教えていただきありがとう)。 これまで、港で200tmを見たのが最大だったような気がする。 内陸で450tmはちと嬉しいかも。

携帯ポンプが壊れたので新調

長年使っていた携帯ポンプが壊れた。 下り坂で、後方確認のために、大きく後ろを振り向いた時、 タイツをひっかけて落した時だ。

何度か落したことはあったが、今回ついに、 持ち手の部分が壊れてしまった。

BeamのXOPミニフロアーポンプ、長年ご苦労様でした。 2014以前の購入みたいです。 プラスチッキーだけど、実用的で良いポンプでした。 現行モデルはありません。

新しいやつは此れ。 airboneのZT510 .

XOPミニフロアーポンプより細身で少し長めを探して、これになった。 レビューが見当らなかったが、こいつのコンパクト版は良い評価のようだ。

試用では大きな不満は感じない。8ぐらいはいける。以下、感想。

  • 一応、地面に押し付けてポンピングすることは可能だが、 セッティングに時間がかかる。
  • 口金は確実に装着できるが、手軽さは感じない。
  • ホースと本体を接続するネジ山の数が少ないと思う。 あと2山あれば安心感があるのだが。強く締めるのはNGかも。出先の作業だと忘れるんだよな。
  • スペック以上に「長い」と感じる。

幸運?にも、実践投入。[201903]
細長いやつを地面に押し付けての作業はかなり楽(以前に比べて)。
抜け方が遅いのでスローパンクかと思ったが、バルブコア部分のリークだった。

しかし、バルブコアから漏れはじめたら、どう対処するのが適切なのだろう。 今回は、接着剤を買ってキャップ内側に少量入れてシール付き?蓋にしてみたが、 泡がいくつも出来てきちんと止まらなかったが、状況の改善は体感できた。

やはり、定期的に交換しろということか。

Emacs の lsp の設定、なう(202310)

前回さらしてから、さらに1年。そう、3年めになる。 が、今回は一段と自信がない。 環境は、 Debian GNU/Linux 12 (bookworm) + emacs(29.1)。consult + company。 embark は未だに使ってない。 用途は、メモ と ...