- 投稿日:2019-05-21T19:06:22+09:00
Nodeで対話型プログラム(readlineモジュールのasync iterator対応について)
NodeでCLIなソフトを作るとき、標準モジュールの
readline
を使うのだけれど「ユーザからの入力を同期的に待ち、それに応じた処理する」という一見楽勝な操作でさえ、煩雑な形の実装になってしまう。JavaScript/Nodeが非同期なAPIを提供することに起因する。そのため、これまでは
readline-sync
というサードパーティ製のライブラリを使うことが多かったのだけれど Promise, await, async iterator を手に入れた今、(単純な要件なら)readline-sync を使わなくても 簡単に実現できる。この辺は以前 考えた けどもう一度必要になったので再度整理したところ (Node v11 から?)
readline.createInterface
が async iterable を返すようになっていたという公式ドキュメントの記載を見つけたので、スッキリ書けるようになっていることに気づいた。以下 Node v12で動作確認しており、たぶんv11以降じゃないと動作しない
レシピ1: 2数を行ごとに受け取って和を表示
イテレータを手動管理するパターン
const readline = require('readline'); async function main () { const rl = readline.createInterface({ input: process.stdin }); const ait = rl[Symbol.asyncIterator](); const n1 = parseInt((await ait.next()).value, 10); const n2 = parseInt((await ait.next()).value, 10); rl.close(); console.log(`answer is ${n1 + n2}`); } main();ユーザからの入力が2行であるとわかっている場合、
rl[Symbol.asyncIterator]()
のように明示的にイテレータを取得してnext
を呼び出すことで値を取得する。rl.close()
は明示的に呼び出さないといつまでも入力を待ち続けることになる(Ctrl+Dを押さないとプログラムが終了しなくなる)。レシピ2: 可変個の数を行ごとに受け取り総和を表示
イテレータ管理は
for-await-of
に任せるパターンconst readline = require('readline'); async function main () { const rl = readline.createInterface({ input: process.stdin }); let sum = 0; for await (const val of rl) { sum += parseInt(val, 10); console.log(`accumulated: ${sum}`); } console.log(`answer is ${sum}`); } main();入力の終わりを知らせるために Ctrl+D を押下する必要がある。その代わりイテレータの管理をしなくてもよい。forループの内部で何らかの脱出条件の判定をして明示的に
rl.close()
を呼び出すのもよいアイディアだと思う。
- 投稿日:2019-05-21T19:04:04+09:00
IT dashboard の行政界データをベクトルタイルにしたい
IT dashboard の行政界データをベクトルタイルにしたい
2019-05-25: ベクトルタイルが完成したので、記事を更新しました。
_ _ _ _ __ __ _ | | | | \ | | \ \ / /__ ___| |_ ___ _ __ | | | | \| | \ \ / / _ \/ __| __/ _ \| '__| | |_| | |\ | \ V / __/ (__| || (_) | | \___/|_| \_| \_/ \___|\___|\__\___/|_| _____ _ _ _____ _ _ _ _ |_ _(_) | ___ |_ _|__ ___ | | | _(_) |_ | | | | |/ _ \ | |/ _ \ / _ \| | |/ / | __| | | | | | __/ | | (_) | (_) | | <| | |_ |_| |_|_|\___| |_|\___/ \___/|_|_|\_\_|\__| Make the technology the easy part.動機
情報源は地域におけるデータの活用促進に向けて~政府のオープンデータ政策~なのですが、 IT Dashboard では次の GeoJSON データが使われているようですね。
ここが出典となるサイトであり、ここが利用規約です。「出典:IT Dashboard (https://www.itdashboard.go.jp) 」で自由に使えるはず。
これ、サイズが随分大きいので、ベクトルタイルにしてみたいと思っています。
(実験1) ogr2ogr (2.4.0 以降) で GeoJSON Text Sequence に変換
国連ベクトルタイルツールキット方式で変換する場合、ソースデータは GeoJSON Text Sequence にまず変換します。データをストリームに変換することで、データをマージするとかしないとかった悩みから解放されます。2.4.0 以降の ogr2ogr を使えば、こういったコマンド一発で変換することができます。
ogr2ogr prefecture.geojsons https://www.itdashboard.go.jp/js/data/prefectures.geojson
(実験2) 標準出力にリダイレクト
国連ベクトルタイルツールキット方式のオンザフライスキーマ変換をするには、modify.js を噛ませなければなりません。Tippecanoe の --prefilter はレイヤ名の変更ができませんし、ogr2ogr にはそのようなフィルタ機能はないようですから、フィルタは Node.js で噛ませることにして、ogr2ogr と Tippecanoe は Node.js から spawn して pipe でつなぐことにします。
手始めに、実験1の結果をファイルではなくて標準出力にリダイレクトできることを確かめます。
ogr2ogr -f GeoJSONSeq -lco RS=YES /vsistdout/ https://www.itdashboard.go.jp/js/data/prefectures.geojson
(検討) 成果物の形式
成果物はプレインな未圧縮のベクトルタイルとし、gh-pages でホストすることにしましょう。
Tippecanoe の README.md によると、ベクトルタイルをファイルシステムに書き出すためには
--output-to-directory=directory
オプションを使います。また、書き出したベクトルタイルを未圧縮とするためには、--no-tile-compression
オプションを指定します。(検討) ズームレベル等の割当・レイヤ名の命名
国連ベクトルタイルツールキット方式では、ベクトルタイル生産後にもベクトルタイルサイズの最適化を回しますが、その前に概ねのズームレベル割り当てとレイヤ名の命名を済ませておいた方が楽です。
サイトを見て、ざっくり次のような感じかなと思います。
ソース GeoJSON レイヤ名 ジオメトリ型 minzoom maxzoom prefectures.geojson prefecture Polygon + MultiPolygon 2 7 municipality.geojson municipality Polygon + MultiPolygon 8 10 municipality_name.geojson munilabel Point 10 10 個人的なこだわりとしては、次のようなことを考えています。
- レイヤ名は単数形で行きます。
munilabel
という命名には、polylabelというプロダクト名が影響していると思います。(開発) コードに落とす
次の方針でコードに落とします。
- ogr2ogr で生成した3つのストリームを立て続けに Tippecanoe に与えて一体のベクトルタイルにする。
- 上記 1. の三連ストリームを出す部分をまず先に作って標準出力で確認する。
- そのストリームを Tippecanoe にパイプする。
作成したコードは https://github.com/hfu/autonomy においていきます。
(FIXME: ここの README.md に書いてあるインストールおよび実行の方法を、ここにも書き出しておく。)
(開発中途) 地物ストリームを確認する
地物ストリームを出せるようになった段階でのスクリプトと設定ファイルをコピーしておくと、次の通りでした。
index.js
const config = require('config') const Parser = require('json-text-sequence').parser const { spawn } = require('child_process') const minzoom = config.get('minzoom') const maxzoom = config.get('maxzoom') const srcs = config.get('srcs') const ogr2ogrPath = config.get('ogr2ogrPath') const downstream = process.stdout for (const src of srcs) { const parser = new Parser() .on('data', f => { console.log(JSON.stringify(f, null, 2)) }) const ogr2ogr = spawn(ogr2ogrPath, [ '-f', 'GeoJSONSeq', '-lco', 'RS=YES', '/vsistdout/', src.url ]) ogr2ogr.stdout.pipe(parser) }config/default.json
{ minzoom: 2 maxzoom: 10 srcs: [ { url: https://www.itdashboard.go.jp/js/data/prefectures.geojson layer: prefecture minzoom: 2 maxzoom: 7 } { url: https://www.itdashboard.go.jp/js/data/municipality.geojson layer: munisipality minzoom: 8 maxzoom: 10 } { url: https://www.itdashboard.go.jp/js/data/municipality_name.geojson layer: munilabel minzoom: 10 maxzoom: 10 } ] ogr2ogrPath: /usr/local/bin/ogr2ogr }地物ストリーム
出てきた三連の GeoJSON Text Sequences は、次のようになります。(出典: IT Dashboard)
{ "type": "Feature", "properties": { "municipality_code": 15217 }, "geometry": { "type": "Point", "coordinates": [ 138.1892722, 36.9073484 ] } } ... { "type": "Feature", "properties": { "prefectures_code": 32, "prefectures_name": "島根県" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 133.0038269, 36.0329253 ], ... [ 133.1517075, 35.2161611 ] ] ] ] } } ... { "type": "Feature", "properties": { "municipality_code": 15217, "submap": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 138.3786956, 36.9804372 ], ... [ 138.3786956, 36.9804372 ] ] ] } } ...(調整) データを鑑賞し、属性を調整方針を考える
データを見るに、レイヤがまとめられた形でオンラインで提供されるベクトルタイルとしてはどのように属性を調整するべきか考えてみると、私は次のように感じました。
- ベクトルタイルでは、属性にレイヤ名を冠するメリットがありません。属性名はシンプルにしましょう。つまり、
code
やname
のようなシンプルなものにします。- 地方公共団体コードを数値型で持っていることについては、私は好感が持てます。なぜなら、コードを整数値で表現する場合には正規の形が定義しやすいですが、文字列型の場合には正規の形をわざわざ定義しなければならないからです。数値型なら「ゼロフィルをするかしないか」のようなことで悩む必要がありません。
(文書化) ベクトルタイルスキーマを整理して文書化しておく
上記の調整方針に従って、ベクトルタイルを次のようなスキーマに調整することにします。
prefecture
属性名 説明 code 整数として表現された市区町村コード municipality
属性名 説明 code 整数として表現された都道府県コード name 都道府県の名称 munilabel
属性名 説明 code 整数として表現された市区町村コード submap あとで調べるべき、謎の整数値 (開発) オンザフライベクトルスキーマ調整の部分を作り込む
次のような形で作りこみました。やや面倒な作りこみですが、データがかなり分かりやすくなると私は思っています。
const renameProperties = (f) => { for (let pair of [ ['municipality_code', 'code'], ['prefectures_code', 'code'], ['prefectures_name', 'name'], ]) { if (f.properties[pair[0]]) { f.properties[pair[1]] = f.properties[pair[0]] delete f.properties[pair[0]] } } return f } //... .on('data', f => { f = renameProperties(f) f.tippecanoe = { layer: src.layer, minzoom: src.minzoom, maxzoom: src.maxzoom } downstream.write(`\x1e${JSON.stringify(f)}\n`) }) //...
node index.js
の結果を標準出力で確認して、これで大丈夫かな、と思ったら、Tippecanoe
にパイプでつなぎましょう。(開発) Tippecanoe にパイプして実際のベクトルタイルを得る
ここは、 process.stdout に設定していた downstream を tippecanoe.stdin に書き換えるだけです。Tippecanoe プロセスを作る部分は、次の通りです。
const tippecanoe = spawn(tippecanoePath, [ `--output-to-directory=${dstDir}`, `--no-tile-compression`, `--minimum-zoom=${minzoom}`, `--maximum-zoom=${maxzoom}` ], { stdio: ['pipe', 'inherit', 'inherit'] }) const downstream = tippecanoe.stdinこの形で
node index.js
を実行することで、zxy
フォルダにベクトルタイルが作られることになります。git add .; git commit -m update; git push origin master
で GitHub に送り込み、gh-pages
でホストしてもらいます。(開発) サイトを作る
style.hjson を経由して style.json を書く
Mapbox Style のドキュメントを見ながら style.json を書きますが、JSON を書くストレスを軽減するために、Hjson で書いて、hjson というツールで変換します。変換コマンドは Rakefile に書いています。style.hjson は次の内容です。
{ version: 8 center: [ 139.754 35.746 ] zoom: 8.22 sources: { v: { type: vector tiles: [ https://hfu.github.io/autonomy/zxy/{z}/{x}/{y}.pbf ] attribution: IT Dashboard (source) minzoom: 2 maxzoom: 10 } } sprite: https://hfu.github.io/unite-sprite/sprite glyphs: https://vectortiles.xyz/fonts/{fontstack}/{range}.pbf layers: [ { id: background type: background paint: { background-color: [ rgb 187 222 251 ] } } { id: prefecture type: fill source: v source-layer: prefecture paint: { fill-color: [ rgb 245 245 245 ] fill-outline-color: [ rgb 92 99 102 ] } } { id: municipality type: fill source: v source-layer: municipality paint: { fill-color: [ rgb 245 245 245 ] fill-outline-color: [ rgb 92 99 102 ] } } { id: munilabel type: symbol source: v source-layer: munilabel layout: { text-field: [ get code ] text-font: [ sans ] } } ] }index.html を書く
index.html は、普通に次のような感じです。Mapbox GL JS の v1.0.0 突破、おめでとうございます。
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> <link rel="stylesheet" type="text/css" href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.0.0/mapbox-gl.css"/> <style> body { margin: 0; top: 0; bottom: 0; width: 100%; } #map { position: absolute; top: 0; bottom: 0; width: 100%; } </style> <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.0.0/mapbox-gl.js"></script> </head> <body> <div id="map"></div> <script src="bundle.js"></script> </body> </html>サイトの開発補助
これまでは、頻繁に git push して gh-pages からサイトを確認していたのですが、最近は budo を使って手元でサイトの確認をするようにしたりしています。こちらも、詳細は Rakefile を参照してください。
完成したサイト
https://hfu.github.io/autonomy
付録
感想
- 実際の現場では、このドキュメントほどはお行儀よくやりません。とりあえずいい加減にものを作ってしまって、何度でもやりなおすようなアプローチを取り、最後に苦しみながらドキュメントを書く(あるいは、ドキュメントを書き忘れる)ことが多いです。
- 自分で似たミニプロジェクトを重ねながら流していて思ったのですが、部品の共通化というのはプロダクトの信頼性を上げるために重要だと、工学の教科書通りに、改めて思いました。ただ、よい共通部品は抽象的思考の中から上流で設計されるときよりも、並列で流れるミニプロジェクトたちが、周囲の成果を盗むあうときにできるかもしれないと思います。抽象的思考の中から生まれる共通部品は、悪いものではありませんが、費用対効果が説明しづらい場合が多いです。
- 何よりも手を動かすこと、それから、言語にはそれほどこだわらずにドキュメントすることが重要ですね。ドキュメント作業も、プロダクトも重要ですがプロセスも重要なのですね。
本稿のドラフト置き場
- 投稿日:2019-05-21T19:04:04+09:00
IT dashboard の行政界データをベクトルタイルにした
2019-05-25: ベクトルタイルが完成したので、記事を更新しました。
_ _ _ _ __ __ _ | | | | \ | | \ \ / /__ ___| |_ ___ _ __ | | | | \| | \ \ / / _ \/ __| __/ _ \| '__| | |_| | |\ | \ V / __/ (__| || (_) | | \___/|_| \_| \_/ \___|\___|\__\___/|_| _____ _ _ _____ _ _ _ _ |_ _(_) | ___ |_ _|__ ___ | | | _(_) |_ | | | | |/ _ \ | |/ _ \ / _ \| | |/ / | __| | | | | | __/ | | (_) | (_) | | <| | |_ |_| |_|_|\___| |_|\___/ \___/|_|_|\_\_|\__| Make the technology the easy part.動機
情報源は地域におけるデータの活用促進に向けて~政府のオープンデータ政策~なのですが、 IT Dashboard では次の GeoJSON データが使われているようですね。
ここが出典となるサイトであり、ここが利用規約です。「出典:IT Dashboard (https://www.itdashboard.go.jp) 」で自由に使えるはず。
これ、サイズが随分大きいので、ベクトルタイルにしてみたいと思っています。
(実験1) ogr2ogr (2.4.0 以降) で GeoJSON Text Sequence に変換
国連ベクトルタイルツールキット方式で変換する場合、ソースデータは GeoJSON Text Sequence にまず変換します。データをストリームに変換することで、データをマージするとかしないとかった悩みから解放されます。2.4.0 以降の ogr2ogr を使えば、こういったコマンド一発で変換することができます。
ogr2ogr prefecture.geojsons https://www.itdashboard.go.jp/js/data/prefectures.geojson
(実験2) 標準出力にリダイレクト
国連ベクトルタイルツールキット方式のオンザフライスキーマ変換をするには、modify.js を噛ませなければなりません。Tippecanoe の --prefilter はレイヤ名の変更ができませんし、ogr2ogr にはそのようなフィルタ機能はないようですから、フィルタは Node.js で噛ませることにして、ogr2ogr と Tippecanoe は Node.js から spawn して pipe でつなぐことにします。
手始めに、実験1の結果をファイルではなくて標準出力にリダイレクトできることを確かめます。
ogr2ogr -f GeoJSONSeq -lco RS=YES /vsistdout/ https://www.itdashboard.go.jp/js/data/prefectures.geojson
(検討) 成果物の形式
成果物はプレインな未圧縮のベクトルタイルとし、gh-pages でホストすることにしましょう。
Tippecanoe の README.md によると、ベクトルタイルをファイルシステムに書き出すためには
--output-to-directory=directory
オプションを使います。また、書き出したベクトルタイルを未圧縮とするためには、--no-tile-compression
オプションを指定します。(検討) ズームレベル等の割当・レイヤ名の命名
国連ベクトルタイルツールキット方式では、ベクトルタイル生産後にもベクトルタイルサイズの最適化を回しますが、その前に概ねのズームレベル割り当てとレイヤ名の命名を済ませておいた方が楽です。
サイトを見て、ざっくり次のような感じかなと思います。
ソース GeoJSON レイヤ名 ジオメトリ型 minzoom maxzoom prefectures.geojson prefecture Polygon + MultiPolygon 2 7 municipality.geojson municipality Polygon + MultiPolygon 8 10 municipality_name.geojson munilabel Point 10 10 個人的なこだわりとしては、次のようなことを考えています。
- レイヤ名は単数形で行きます。
munilabel
という命名には、polylabelというプロダクト名が影響していると思います。(開発) コードに落とす
次の方針でコードに落とします。
- ogr2ogr で生成した3つのストリームを立て続けに Tippecanoe に与えて一体のベクトルタイルにする。
- 上記 1. の三連ストリームを出す部分をまず先に作って標準出力で確認する。
- そのストリームを Tippecanoe にパイプする。
作成したコードは https://github.com/hfu/autonomy においていきます。
(FIXME: ここの README.md に書いてあるインストールおよび実行の方法を、ここにも書き出しておく。)
(開発中途) 地物ストリームを確認する
地物ストリームを出せるようになった段階でのスクリプトと設定ファイルをコピーしておくと、次の通りでした。
index.js
const config = require('config') const Parser = require('json-text-sequence').parser const { spawn } = require('child_process') const minzoom = config.get('minzoom') const maxzoom = config.get('maxzoom') const srcs = config.get('srcs') const ogr2ogrPath = config.get('ogr2ogrPath') const downstream = process.stdout for (const src of srcs) { const parser = new Parser() .on('data', f => { console.log(JSON.stringify(f, null, 2)) }) const ogr2ogr = spawn(ogr2ogrPath, [ '-f', 'GeoJSONSeq', '-lco', 'RS=YES', '/vsistdout/', src.url ]) ogr2ogr.stdout.pipe(parser) }config/default.json
{ minzoom: 2 maxzoom: 10 srcs: [ { url: https://www.itdashboard.go.jp/js/data/prefectures.geojson layer: prefecture minzoom: 2 maxzoom: 7 } { url: https://www.itdashboard.go.jp/js/data/municipality.geojson layer: munisipality minzoom: 8 maxzoom: 10 } { url: https://www.itdashboard.go.jp/js/data/municipality_name.geojson layer: munilabel minzoom: 10 maxzoom: 10 } ] ogr2ogrPath: /usr/local/bin/ogr2ogr }地物ストリーム
出てきた三連の GeoJSON Text Sequences は、次のようになります。(出典: IT Dashboard)
{ "type": "Feature", "properties": { "municipality_code": 15217 }, "geometry": { "type": "Point", "coordinates": [ 138.1892722, 36.9073484 ] } } ... { "type": "Feature", "properties": { "prefectures_code": 32, "prefectures_name": "島根県" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 133.0038269, 36.0329253 ], ... [ 133.1517075, 35.2161611 ] ] ] ] } } ... { "type": "Feature", "properties": { "municipality_code": 15217, "submap": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 138.3786956, 36.9804372 ], ... [ 138.3786956, 36.9804372 ] ] ] } } ...(調整) データを鑑賞し、属性を調整方針を考える
データを見るに、レイヤがまとめられた形でオンラインで提供されるベクトルタイルとしてはどのように属性を調整するべきか考えてみると、私は次のように感じました。
- ベクトルタイルでは、属性にレイヤ名を冠するメリットがありません。属性名はシンプルにしましょう。つまり、
code
やname
のようなシンプルなものにします。- 地方公共団体コードを数値型で持っていることについては、私は好感が持てます。なぜなら、コードを整数値で表現する場合には正規の形が定義しやすいですが、文字列型の場合には正規の形をわざわざ定義しなければならないからです。数値型なら「ゼロフィルをするかしないか」のようなことで悩む必要がありません。
(文書化) ベクトルタイルスキーマを整理して文書化しておく
上記の調整方針に従って、ベクトルタイルを次のようなスキーマに調整することにします。
prefecture
属性名 説明 code 整数として表現された市区町村コード municipality
属性名 説明 code 整数として表現された都道府県コード name 都道府県の名称 munilabel
属性名 説明 code 整数として表現された市区町村コード submap あとで調べるべき、謎の整数値 (開発) オンザフライベクトルスキーマ調整の部分を作り込む
次のような形で作りこみました。やや面倒な作りこみですが、データがかなり分かりやすくなると私は思っています。
const renameProperties = (f) => { for (let pair of [ ['municipality_code', 'code'], ['prefectures_code', 'code'], ['prefectures_name', 'name'], ]) { if (f.properties[pair[0]]) { f.properties[pair[1]] = f.properties[pair[0]] delete f.properties[pair[0]] } } return f } //... .on('data', f => { f = renameProperties(f) f.tippecanoe = { layer: src.layer, minzoom: src.minzoom, maxzoom: src.maxzoom } downstream.write(`\x1e${JSON.stringify(f)}\n`) }) //...
node index.js
の結果を標準出力で確認して、これで大丈夫かな、と思ったら、Tippecanoe
にパイプでつなぎましょう。(開発) Tippecanoe にパイプして実際のベクトルタイルを得る
ここは、 process.stdout に設定していた downstream を tippecanoe.stdin に書き換えるだけです。Tippecanoe プロセスを作る部分は、次の通りです。
const tippecanoe = spawn(tippecanoePath, [ `--output-to-directory=${dstDir}`, `--no-tile-compression`, `--minimum-zoom=${minzoom}`, `--maximum-zoom=${maxzoom}` ], { stdio: ['pipe', 'inherit', 'inherit'] }) const downstream = tippecanoe.stdinこの形で
node index.js
を実行することで、zxy
フォルダにベクトルタイルが作られることになります。git add .; git commit -m update; git push origin master
で GitHub に送り込み、gh-pages
でホストしてもらいます。(開発) サイトを作る
style.hjson を経由して style.json を書く
Mapbox Style のドキュメントを見ながら style.json を書きますが、JSON を書くストレスを軽減するために、Hjson で書いて、hjson というツールで変換します。変換コマンドは Rakefile に書いています。style.hjson は次の内容です。
{ version: 8 center: [ 139.754 35.746 ] zoom: 8.22 sources: { v: { type: vector tiles: [ https://hfu.github.io/autonomy/zxy/{z}/{x}/{y}.pbf ] attribution: IT Dashboard (source) minzoom: 2 maxzoom: 10 } } sprite: https://hfu.github.io/unite-sprite/sprite glyphs: https://vectortiles.xyz/fonts/{fontstack}/{range}.pbf layers: [ { id: background type: background paint: { background-color: [ rgb 187 222 251 ] } } { id: prefecture type: fill source: v source-layer: prefecture paint: { fill-color: [ rgb 245 245 245 ] fill-outline-color: [ rgb 92 99 102 ] } } { id: municipality type: fill source: v source-layer: municipality paint: { fill-color: [ rgb 245 245 245 ] fill-outline-color: [ rgb 92 99 102 ] } } { id: munilabel type: symbol source: v source-layer: munilabel layout: { text-field: [ get code ] text-font: [ sans ] } } ] }index.html を書く
index.html は、普通に次のような感じです。Mapbox GL JS の v1.0.0 突破、おめでとうございます。
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> <link rel="stylesheet" type="text/css" href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.0.0/mapbox-gl.css"/> <style> body { margin: 0; top: 0; bottom: 0; width: 100%; } #map { position: absolute; top: 0; bottom: 0; width: 100%; } </style> <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.0.0/mapbox-gl.js"></script> </head> <body> <div id="map"></div> <script src="bundle.js"></script> </body> </html>サイトの開発補助
これまでは、頻繁に git push して gh-pages からサイトを確認していたのですが、最近は budo を使って手元でサイトの確認をするようにしたりしています。こちらも、詳細は Rakefile を参照してください。
完成したサイト
https://hfu.github.io/autonomy
Maputnik で見るには、https://maputnik.github.io/editor/?style=https://hfu.github.io/autonomy/style.json を開いてください。
付録
感想
- 実際の現場では、このドキュメントほどはお行儀よくやりません。とりあえずいい加減にものを作ってしまって、何度でもやりなおすようなアプローチを取り、最後に苦しみながらドキュメントを書く(あるいは、ドキュメントを書き忘れる)ことが多いです。
- 自分で似たミニプロジェクトを重ねながら流していて思ったのですが、部品の共通化というのはプロダクトの信頼性を上げるために重要だと、工学の教科書通りに、改めて思いました。ただ、よい共通部品は抽象的思考の中から上流で設計されるときよりも、並列で流れるミニプロジェクトたちが、周囲の成果を盗むあうときにできるかもしれないと思います。抽象的思考の中から生まれる共通部品は、悪いものではありませんが、費用対効果が説明しづらい場合が多いです。
- 何よりも手を動かすこと、それから、言語にはそれほどこだわらずにドキュメントすることが重要ですね。ドキュメント作業も、プロダクトも重要ですがプロセスも重要なのですね。
本稿のドラフト置き場
- 投稿日:2019-05-21T14:07:28+09:00
Node.jsのpakage.jsonで追加モジュールに頼らず、他のpakage.jsonのscriptを実行する方法
Node.jsのpakage.jsonで、追加モジュールに頼らず他のpakage.jsonのscriptを実行する方法
- カレントディレクトリを変更してからコマンドを実行しなければならないのですが、意外に簡単な方法がありません
- 今回の内容はNode.js自体の機能を使うだけなので、追加のモジュールのインストールは不要です
- 欠点は記述が冗長なことと、コマンドの実行が終わらないと、コンソールに状況が表示されないことです
- カレントディレクトリの変更が必要な、その他のコマンドにも対処可能です
package.json{ … "scripts": { "コマンド名": "node -e \"process.chdir('ターゲットディレクトリ');console.log(require('child_process').execSync('npm run コマンド名').toString())\"" } … }