「東所沢の『光るマンホール』探勝マップ」を作った

東所沢の『光るマンホール』探勝マップ

これは何?

埼玉県所沢市には、アニメ作品をモチーフにした、『光るマンホール』があるという。
JR武蔵野線 東所沢駅から北へ向かい、東所沢公園付近に至るまでの間に、全28基が設置されている。

それらを鑑賞して楽しむためのガイドブックとして、また、撮影した画像を地図上に重ね合わせて表示し、見て楽しむためのコンテンツとして、マップを作成しました。

今回、私としては初めての試みとして、マップを表示するためのライブラリとして、Mapboxを利用することにしました。

基本的には公式ドキュメントに記載されているサンプルほぼそのままに、SDKの機能を呼び出している(組み合わせた)のですが、一部、オリジナルの機能を実現するために、独自に実装した機能もあります。

説明スライド

特徴

  • マップには、Mapbox GL JSを使用。3D風の、斜め見下ろし視点
  • 28基のマンホールがある位置には、マーカーを配置。番号と作品イラストで分かりやすく
  • マーカーをクリック(タップ)すると、ポップアップを表示。撮影したマンホールの画像を閲覧できる
  • ユーザーの現在位置を表示できる(端末の位置情報を有効にした場合)
  • ユーザーの移動に合わせて、軌跡を表示(グレーの線)
  • 現在位置から各マンホールまでの、直線距離を表示(ポップアップの下部)

独自に実装した機能

  • ユーザーの軌跡を線で表示する
    • 位置情報の履歴を保持して、それをgeojson形式に整形、ラインとしてマップ上にレイヤーを描画
  • 特定の座標(東所沢)へマップの表示範囲を移動するボタン
    • 独自のコントロールを追加できる機能を使い、実装。クリックイベントに、マップの移動をするコールバック関数を登録した
  • ヘルプを表示するボタン
    • 上記同様、独自のコントロール。予めヘルプを書いておいたHTML要素を、表示/非表示切り替え、CSSでオーバーレイ表示している

Hexo 5.0 実装予定のInjectorを試す

まずは、GitHubにある最新のHexoをインストールします。
今回は、4.2.1が可動していた環境に、上書きでインストールしました。

$ cd <my-blog-dir>
$ npm i git+ssh://git@github.com/hexojs/hexo.git#master

$ ls -l node_modules/hexo/lib/extend/injector.js
-rw-rw-r-- 1 naoto naoto 2341 10月 26 1985 node_modules/hexo/lib/extend/injector.js

そして拙作プラグインをInjector対応にしたものを、GitHub上の私の開発ブランチから導入します。
hexo-tag-google-photos-album
参考:修正内容

$ npm i git+ssh://git@github.com:isnot/hexo-tag-google-photos-album.git#new_injector

さらに、hexo-light-galleryもHexo 5.0 w/ Injector対応したので、これも導入します。
参考:修正内容

$ npm i git+ssh://git@github.com/isnot/hexo-light-gallery.git#isnot-hexo5

動作確認。

インデックスの場合。同一画面内に複数記事があり、各々にギャラリーがあったりなかったりするパターン。
https://pages.isnot.jp/

post個別ページで、画面内にギャラリーが単一のみ存在するパターン。
https://pages.isnot.jp/2019-05/30-TMG-Observatories/

post個別ページで、画面内にギャラリーが複数あったり、直接画像タグを使って画像を貼るなど混在しているパターン。
https://pages.isnot.jp/2019-04/13-image-test/

postではない、個別のpageのパターン。
https://pages.isnot.jp/about/

ひとまず、これはマズイなぁという感じの動作が改善されていることを確認できました。
仕様レベルで調整をする余地はあるかとはおもいますが、OKということにしましょう。

Hexo 5.0がリリースになった暁には、hexo-tag-google-photos-albumの修正をnpmにパブリッシュしたい。
4系、3系の場合を考慮したバージョンチェック的なコードを付け加えたほうがいいかも。

※補足
lightgallery.jsは、jQueryに依存しない、lightbox風ギャラリーです。
Hexo界隈でよく見られるfancyboxは、jQuery依存なので、かわりにこちらを使っています。

【HowTo】Hexoの新しいInjectorについて調べた【5.0】

予てより、Hexoに新しいInjector機構を導入する動きがありました。
それが、実際のコードとして試せる、かもしれない段階まで進展しているようです。
先月の16日に、マスターブランチにマージされたからです。
リリースは、次のメジャーバージョンアップとなる(筈の)、5.0になるようです。

PR: feat(extend/injector): bring up new extend Injector #4049

これはなに?

まずHexoですが、いわゆる「静的サイトジェネレーター」とよばれる方式の、ブログ作成ツールです。
本ブログでも使っているものです。

そして本件で言うInjectorとは、プログラムが生成するHTMLファイルに対して、cssやjavascriptなどの、code snippet(断片)を、後付で挿入する仕組みのことを指します。

主なユースケースは、プラグインによって、テーマ(HTMLのテンプレート)を直に編集することなく、cssやjavascriptを挿入することになるでしょう。

テーマ等が固定であれば、直接テンプレートに追加することで、本件要件は満たせます。
しかしHexo本体とは別に、各種テーマ、各種プラグインを好みによって導入できる仕組みであるために、このような機構が必要になってきます。

これまでの流れ

以前より同じ目的のために、hexo-injectプラグインが存在しました。
これはだいたい2016年3月頃までは開発が続けられていた模様で、その後にメンテナンスがされなくなり、またHexo本体のバージョンが上がると共に、動作しない状態になっていました。

それが昨年の8月に、再び修正する機運が持ち上がっていたようなのですが、結局のところそれも中途で実用化されないままになっていたようです。

私にもその理由は、なんとなく想像ができます。
Injector機構をプラグインによって実装することは、アドホック感ありありで、それならばHexo本体でサポートする方が、ずっとシンプルかつスマートに実現できると考えられるからです。
(修正しようとしていた開発者様が同じ考えだったかは、わかりません。)

Issues: Deprecation #90

私とInjector

私がHexoを使い始めると同時に作り始めたプラグインがあります。
hexo-tag-google-photos-album 公開しました (npm)

このプラグインの中でも、cssやjavascriptを挿入する機能が必要になって、開発の過程でhexo-injectプラグインのことを知り、そして挫折しました。

それで他作者様のプラグイン等を参考にして、独自にInjector機能を実装したもの、実は満足する動作を実現できていません。
ひとまず動く、かな、位の状況になっていて、後で必ず、作り直そうと決めていたのです。

が、いかんせん、上記のように、プラグインからそれを実装するには原理的な困難があり、同時に他の開発者によって改善に動いている様子があることも知ったために、その進捗を待つこととして、私の作成するプラグインは、不完全なままで、時期が来るのを待つことにしていたのでした。

ドキュメント 私家版和訳

新しいAPIは、本番リリース前なので、現時点で公式サイトを探してもドキュメントがありません。

そして公式サイトの更新も、プルリクが既に準備されており、開発ブランチを通してその中身を読むことが可能です。

docs | API/Injector | Hexo

今回、これを和訳してみようかと思います。

↓↓↓和訳ここから


Injector


「Injector」は、静的なコードの断片(スニペット)を、生成されたHTMLファイル内の、head要素やbody要素に付け加えます。
Hexoは、after_render:htmlフィルターが処理される前に、Injectorを実行します。

Synopsis 使い方

hexo.extend.injector.register(entry, value, to)

entry <string>

HTML内部において、目的のコードを挿入する場所を指定します。<文字列>

以下の値を指定できます。

  • head_begin: <head>要素開始タグの直後に、コードを挿入します。(デフォルト)
  • head_end: </head>要素閉じタグの直前 〃
  • body_begin: <body>要素開始タグの直後 〃
  • body_end: </body>要素閉じタグの直前 〃

value <string> | <Function>

挿入したい、コードの断片(スニペット)。<文字列、もしくは関数>

文字列を返却する関数を指定することも可能です。

to <string>

どのページに対して、コードの断片を挿入するか。<文字列>

  • default: 全てのページ(デフォルト)
  • home: インデックスのみ (ヘルパーis_home()true のとき)
  • post: ブログ記事のみ (ヘルパーis_post()true のとき)
  • page:(postではない)静的ページのみ (ヘルパーis_page()true のとき)
  • archive: アーカイブのみ (ヘルパーis_archive()true のとき)
  • category: カテゴリーのみ (ヘルパーis_category()true のとき)
  • tag: タグのみ (ヘルパーis_tag()true のとき)
  • カスタムレイアウト(Layout)の名前を指定することが可能です。参照:Writing - Layout

他にも内部的な機能がありますが、詳細は hexojs/hexo#4049 を参照してください。

Example サンプル

const css = hexo.extend.helper.get('css');
const js = hexo.extend.helper.get('js');

hexo.extend.injector.register('head_end', () => {
return css('https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.css');
}, 'music');

hexo.extend.injector.register('body_end', '<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js">', 'music');

hexo.extend.injector.register('body_end', () => {
return js('/js/jquery.js');
});

上記の例では、musicレイアウトを指定した全てのページに対して、APlayer.min.css (<link> 要素)を、</head>要素閉じタグの前に挿入します。
同時に、APlayer.min.js (<script> 要素)を、それらのページの</body>要素閉じタグの前に挿入します。

そして最後の段落では、全ての生成されたページで、jquery.js (<script> 要素)を、</body>要素閉じタグの前に挿入します。


↑↑↑和訳ここまで

この和訳は、 API/Injector を、私(isnot)が独自に翻訳したものです。原文はMIT Licenseです。

私の和訳について、MIT Licenseもしくは、クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。
クリエイティブ・コモンズ・ライセンス

チャット・ボットのためのフレームワークを開発してみた。序章

GASでChat Botを動かすためのFrameworkを作成する。

背景

ここで言うチャットとは、LINEやTelegramのような、グループチャットのことです。
また興味の対象としては、アプリとしてよりも、プラットフォーム(サービスの基盤)として、見ています。

チャット・ボット(Chat Bot、単にBotと呼ぶ場合が多い)とは、そのようなチャットの中で、機能的、あるいは非機能的な振る舞いをする、自動的なプログラムのことを指します。

各チャットのサービス(プラットフォーム)を提供する事業者は、そういったBotを作成・動作させるための仕組みを、一般の第三者に対して提供しています。
つまり利用者(私、Botの開発者)がプログラムを作成し、動かすことによって、チャットの中に独自の機能や価値を、組み入れていくことができます。

課題

独自のBotを動かそうとしたとき、Botのプログラムを動作させる場所(いわゆるサーバー)が必要になってきます。
この点が比較的大きめの障壁であると感じられます。

簡単なテストの目的で動かすとか、あるいは開発をする段階では、普段使っている自分の(自宅の)パソコンだけで十分です。
しかし本格的に、継続的・安定的に動かし続けるのであれば、それでは足りず、サーバーで動かしたくなってきます。

でも、公開のサーバーを管理するのは、何かと手間やリスクがつきもので、できる限り避けたいものです。
そこで昨今の情勢としては、いわゆるパブリック・クラウドと呼ばれる、フルマネージド型(管理を代行してくれる、利用者は手間が少ない)のサーバー運用サービスが主流です。
その中でも、サーバーレスと言われるタイプのヤツでは、「サーバーの管理」という概念が、利用者(Bot開発者)側にまったくありません。

多くの場合で、サーバーレスと言えば、AmazonのAWSや、GoogleのGCP、MicrosoftのAzureといったプラットフォームの中で提供されているモノを指していることが多いと思われますが、さらにお手軽なモノがあります。
それが、Google Apps Script、通称GASです。

GASでは、利用者が用意したプログラムを24時間動かすことができます。
それは、チャットのサービスや、それ以外の外部のサービスと連携させる事も可能で、さらにGoogleが持つ多くの製品との連携がかなり簡単にできるようになっています。
具体的には、スプレッドシートの内容を読み書きし、またチャットのサービスから送信されるリクエストを待ち受けて、また逆にメッセージ等を送り返す(チャットのメンバーに届ける)ことができるわけです。

GASの特徴と利点、そして制約

Googleアカウントを持っているならば、それを使ってすぐに始められます。
GmailやGoogleドライブ、Google Play等を利用したことがあるならば、Googleアカウントは既に利用可能な状態です。
また、普段使っているものとは別に、Googleアカウントを作成しても良いです。

利用は基本的に無料です。これは大きいです。
例えば同じGoogleでも、GCPやFirebaseでも無料枠の中でそれなりのことが可能だろうと思いますが、クレジット・カードの登録が必要であり、その部分の違い(GASなら請求のおそれがない)は大であります。

何よりGoogleブランドの安心感。サーバーの運用面は一切おまかせです。

しかし何でも可能というわけではなく、一定の制約があります。
コンピューター資源の使用量や回数に上限が定められています。
また、普通はありませんが、極端に負荷がかかる状態を継続していると、前記の無料という話に関わらず、上位の有料プランにアップグレードする必要が発生する可能性があるようです。

また、個人的に使う、あるいは一定の人数のグループで使うためのチャット・ボットという程度であればさしたる問題になりませんが、処理の速度、パフォーマンスという面では、「まぁまぁ、そこそこ」程度です。

シビアな反応速度(レイテンシ)が求められる用途には向いていないです。
また、個人的なざっくりとした感覚ですが、チャットの参加人数は延べ100人程度までにしておくと良いかと思われます。
それを超える規模で必要であれば、別のGoogleアカウントを併用するなど、工夫が必要です。が、そうなると管理の手間が増えてしまいますし、GAS以外の選択肢が視野に入ってくるかと思われます。

それと可用性の面ですが、GASはときどき止まります。これも感覚ですが、半年に1回とか。
トラブル(Googleが攻撃を受ける)等で停止することもありますし、全体的に問題なく動いている場合でも、不意に処理が抜け落ちる(応答がない、タイムアウトする)といった現象が起こりえます。
そういう「不意落ち」は、数ヶ月に1回位は見る気がします。これはプログラム側の問題や、その他の外部要因もあるだろうと思われ、一概にGASのサービス・レベルの問題とは言えないのかもしれませんが、それでも他のより本格的なクラウドサービスと比べた場合、割り引いて見ておくと良いかと思います。

目的

前提条件

必要な要素

現時点までの状況

今後の目標

apt-btrfs-snapshotによるディスク容量の消費

パソコンのディス容量がアップアップになっていたところを、今まで知らなかった方法で大きく改善した話。
一時的にディスフル(100%)になり、従来手法を使っても95%にしか減らず、それでは困るので別の手法を取り入れ、結果的に60%利用まで落ち着いた。

Ubuntuのリリースサイクルは半年毎であり、最新の版は先月でた19.10 eoanであるということは、先刻ご承知であろうかと思う。
そして、私のパソコン(のひとつ)では、約1年と少し前にSSDを交換した後も、何かとディスク容量の空きに余裕がなく、苦慮していた。

今回まずきっかけになったのは、VirtualBoxで動かしているWindows10のシステム更新をしたところ、問題発生、途中で停止したことによる。
ディスク容量が問題になっていることは、すぐに察した。今までに同様の件は経験済みである。

このパソコンで最もディスクを消費しているものは、そのWindows10のHDDイメージで、約51GiBであった。
これを、まだディスクには余裕がるもう一つのパソコンに移動した。
つまり今後はVirtualBoxはそちらのほうで使うことにした。

これでディスクには大きく余裕ができるハズだろうと思ったのに…。なんと100%→95%になっただけ。残り約10GiB。
これは何かがおかしいと思って、イロイロ調べることにした。

dfや「btrfs filesystem usage /」で見ると、ディスクの割当て済みがほぼ100%近く、空きは上記のように約10GiBと表示される。
いっぽう、duやbaobabで調べる、つまり個々のファイルの大きさを積算していく方式で調べると、それは約53GiBであると判った。
これは、驚きを持って受け止められれるべき事柄でありつつ、たしかに、冷静に考えると(ある点を除いて)妥当だと納得いく数字。

つまり、妥当であるのは、パソコンの利用状況などを考慮すれば、使用済みが53GiBであるという事実。200GiB超も使っているというほうが変だ。
そして納得できない、分からないのが、ディスク容量の使用済み(実数)と空きの合計が、ディスク(というよりパーティション)として確保している大きさに一致しないこと。
170GiB以上も、謎の差分があることになる。

これは、古き伝統的(そしてごく単純な)ファイルシステムを管理していた頃の知識からは、ありえない、異常事態としか言いようがない。
しかしここはもはや、Btrfsの世界なのである。
なので、何を調べるべきかは、だいたい想像できることではあるのだ。ただ、私の経験と知識では、まだ知らない何かがあるのだろう、ということ。

調べた。
その結果に辿り着いた答えが、aptのスナップショット機能によるものだったというわけ。
apt-btrfs-snapshotを使うと、それを確認でき、削除を含む管理ができる。
一番あたらしいものを一つだけ残して、あとは削除した。

Ubuntuをインストールしているシステムのファイルシステムとして、Btrfsを使っている場合、アップグレードのタイミング等で、自動的にaptがスナップショットを保存する。
そして冒頭のリリースサイクルの話に繋がるが、当パソコンでは、cosmic、disco、eoanという3回のアップグレードを経ており、この通り蓄積が大きくなっていたわけ。
これは全て自動的に行われており、今までまったく意識していなかった。
ファイルシステムのスナップショット機能があることは理解していたし、それを使えば相応にディスク容量が消費されるのはまったく道理なのだが、自分で能動的にスナップショット機能を利用したことはないため、当初は思いに至らなかった。
また同時に、スナップショットの保存によって消費された分が、システム的に、具体的にはdf等にどのように反映される(見える)のかは、知らないでいた。
まぁ、「見えない数字」になるということが、今回判った。

そして私は、この貴重な経験と共に、十分に余裕があるディスクスペースを確保することに成功した。
今後も、半年毎のリリースのたびに、このことを思い起こして不要なスナップショットを手動で削除する必要があるだろう…。

多摩湖自転車歩行者道〜六道山〜文明堂壹番館

今日、自転車に乗って出かけた。
多摩湖自転車歩行者道の始点から入り、西へ。
すぐに落ち葉焚きの匂いが。さらに合わせて金木犀の匂いも被さる。
金木犀は、この先の道々方々で強烈な芳香を放っていた。

萩山駅〜八坂駅の間のストレート区間で、ギアをアウター・トップに入れて、思いっきりペダルを踏み込んでみた。
しかし息が上がってしまって最後まで踏み切れず。心肺と脚力が大幅に弱まっている。むむぅ。

狭山公園の入口を素通り、多摩湖南岸側を進み、中堤防を渡る。
中堤防は、正しくは村山上貯水池堰堤と言い、堤体強化工事が進行中。下流側の、一段低い広場がある部分を削り、「抑え盛り土」を堤頂と同じ高さまで盛り立てるようだ。
どうやらあと4年かけて完工予定のようだ。
http://akutamako.g2.xrea.com/8-reinforce2/8-reinforce2.html

武蔵村山市学校給食センターの横からゲートを抜けて、六道山公園方面へ進む。
都立野山北・六道山公園の北端をなぞるように、未舗装路の尾根道を走ると、爽快な気分になってきた。

瑞穂町立文化の森・六道山公園の、展望塔へ登る。
南側の眺望はそこそこ楽しめた。しかし東側の地平線あたりはモヤで見通しが悪く、かろうじて新宿の高層ビル群がシルエットで見える程度だった。
西側は、残念ながら富士山はまったく見えず。雲が覆っていた。
http://www.town.mizuho.tokyo.jp/shisetsu/006/p004205.html

瑞穂町スカイタワーの展望広場にも立ち寄る。横田基地の北側が見渡せる。まぁ六道山のほうが遠くまで見えるな…。

ここで、雨が降り出す。日暮れまで降らない予報だったので、何の支度もしていなかったので、以降濡れることに。
迷ったが、まぁいいかということで、そのまま文明堂壹番館(工場直売所)へ。
バームクーヘンと、生南部サブレ、五三カステラ切り落としを購入。
http://www.1ban-can.com/ichbankan/accsess/index.html

その頃には、本降り。
帰路につく。自転車のブレーキが濡れたおかげで、効きがかなり弱くて、マイッタ。
そして寒い。寒い。寒い。

帰宅したら、ざっと洗車してからシャワー浴びて一息ついた。

Avenir Shards Bookmarklet

Ingressでは、目下のところ公式のイベント「Avenir Shards」が開催中であることは、ご存知かと思います。
Nianticの公式シャード情報ページでは、マップ上に軌跡を表示するなど、情報提供がされています。
https://ingress.com/shards/avenir_shards (以下、公式シャード情報)

一方で、私は各シャードの移動履歴をリスト形式で一覧表示したものが欲しかったので、以下のBookmarkletを作成しました。
Bookmarkletとは?ということについては、説明を省きますので、Google検索などで調べてください。

利用方法

  • Bookmarkletをブラウザに登録する。これ→ PrintShardsHistory
  • 公式シャード情報を開き、そこでBookmarkletを実行する。

内容

javascript:
(function(){
const d=document;
const h=d.createElement('div');
h.innerHTML=points.map(e=>{return e.title}).join('&lt;br&gt;\n');
d.getElementById('targets').appendChild(h)
})();

上記PrintShardsHistoryに改行を加えて読みやすくしたものです。

GoogleSpreadsheetAPIv4

Node.jsからスプシを操作したいわけじゃないですか。
https://developers.google.com/sheets/api/

なので、npmの中から適当なライブラリがないか、調べたのです。
すると、結構な数が検索で挙げられてきて、ひとつひとつ見ていくわけです。

結局の所、殆どのものは、API v3を前提にしたもので、そのAPI v3は過去の存在なわけですね。

つまるところ理解したのは、API v4を使うにあたって、いま、選択するべきライブラリといえば、Google自身によるgoogleapis。
https://www.npmjs.com/package/googleapis

そうなってくると、サンプルが欲しいわけじゃないですか。

スプレッドシートをWebAPI化するサービスの作り方 から辿り、
スプレッドシートを読み込んでWebAPI化するCloudFunctionsのソース に行き着く。

これはvalues.getを包むラッパーということですが、あとはリファレンスその他を見ながら、updateやappendも容易に実装できるわけです。

googleapisは、ライブラリとしての使い心地も、まぁしっくりクル。
ただし、やはり記述が冗長になるので、ラッパーがあると良いよね、という。
このサンプル、ありがたいことです。