20190521のNode.jsに関する記事は4件です。

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() を呼び出すのもよいアイディアだと思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IT dashboard の行政界データをベクトルタイルにしたい

IT dashboard の行政界データをベクトルタイルにしたい

2019-05-25: ベクトルタイルが完成したので、記事を更新しました。

 _   _ _   _  __     __        _             
| | | | \ | | \ \   / /__  ___| |_ ___  _ __ 
| | | |  \| |  \ \ / / _ \/ __| __/ _ \| '__|
| |_| | |\  |   \ V /  __/ (__| || (_) | |   
 \___/|_| \_|    \_/ \___|\___|\__\___/|_|   

 _____ _ _        _____           _ _    _ _   
|_   _(_) | ___  |_   _|__   ___ | | | _(_) |_ 
  | | | | |/ _ \   | |/ _ \ / _ \| | |/ / | __|
  | | | | |  __/   | | (_) | (_) | |   <| | |_ 
  |_| |_|_|\___|   |_|\___/ \___/|_|_|\_\_|\__|

       Make the technology the easy part.

動機

情報源は地域におけるデータの活用促進に向けて~政府のオープンデータ政策~なのですが、 IT Dashboard では次の GeoJSON データが使われているようですね。

  1. prefectures.geojson
  2. municipality.geojson
  3. municipality_name.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

個人的なこだわりとしては、次のようなことを考えています。

  1. レイヤ名は単数形で行きます。

munilabel という命名には、polylabelというプロダクト名が影響していると思います。

(開発) コードに落とす

次の方針でコードに落とします。

  1. ogr2ogr で生成した3つのストリームを立て続けに Tippecanoe に与えて一体のベクトルタイルにする。
  2. 上記 1. の三連ストリームを出す部分をまず先に作って標準出力で確認する。
  3. そのストリームを 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
        ]
      ]
    ]
  }
}
...

(調整) データを鑑賞し、属性を調整方針を考える

データを見るに、レイヤがまとめられた形でオンラインで提供されるベクトルタイルとしてはどのように属性を調整するべきか考えてみると、私は次のように感じました。

  • ベクトルタイルでは、属性にレイヤ名を冠するメリットがありません。属性名はシンプルにしましょう。つまり、codename のようなシンプルなものにします。
  • 地方公共団体コードを数値型で持っていることについては、私は好感が持てます。なぜなら、コードを整数値で表現する場合には正規の形が定義しやすいですが、文字列型の場合には正規の形をわざわざ定義しなければならないからです。数値型なら「ゼロフィルをするかしないか」のようなことで悩む必要がありません。

(文書化) ベクトルタイルスキーマを整理して文書化しておく

上記の調整方針に従って、ベクトルタイルを次のようなスキーマに調整することにします。

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

付録

感想

  • 実際の現場では、このドキュメントほどはお行儀よくやりません。とりあえずいい加減にものを作ってしまって、何度でもやりなおすようなアプローチを取り、最後に苦しみながらドキュメントを書く(あるいは、ドキュメントを書き忘れる)ことが多いです。
  • 自分で似たミニプロジェクトを重ねながら流していて思ったのですが、部品の共通化というのはプロダクトの信頼性を上げるために重要だと、工学の教科書通りに、改めて思いました。ただ、よい共通部品は抽象的思考の中から上流で設計されるときよりも、並列で流れるミニプロジェクトたちが、周囲の成果を盗むあうときにできるかもしれないと思います。抽象的思考の中から生まれる共通部品は、悪いものではありませんが、費用対効果が説明しづらい場合が多いです。
  • 何よりも手を動かすこと、それから、言語にはそれほどこだわらずにドキュメントすることが重要ですね。ドキュメント作業も、プロダクトも重要ですがプロセスも重要なのですね。

本稿のドラフト置き場

https://hackmd.io/k_CpPVFPRpiMi16rO4x9Cg?both

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IT dashboard の行政界データをベクトルタイルにした

2019-05-25: ベクトルタイルが完成したので、記事を更新しました。

 _   _ _   _  __     __        _             
| | | | \ | | \ \   / /__  ___| |_ ___  _ __ 
| | | |  \| |  \ \ / / _ \/ __| __/ _ \| '__|
| |_| | |\  |   \ V /  __/ (__| || (_) | |   
 \___/|_| \_|    \_/ \___|\___|\__\___/|_|   

 _____ _ _        _____           _ _    _ _   
|_   _(_) | ___  |_   _|__   ___ | | | _(_) |_ 
  | | | | |/ _ \   | |/ _ \ / _ \| | |/ / | __|
  | | | | |  __/   | | (_) | (_) | |   <| | |_ 
  |_| |_|_|\___|   |_|\___/ \___/|_|_|\_\_|\__|

       Make the technology the easy part.

動機

情報源は地域におけるデータの活用促進に向けて~政府のオープンデータ政策~なのですが、 IT Dashboard では次の GeoJSON データが使われているようですね。

  1. prefectures.geojson
  2. municipality.geojson
  3. municipality_name.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

個人的なこだわりとしては、次のようなことを考えています。

  1. レイヤ名は単数形で行きます。

munilabel という命名には、polylabelというプロダクト名が影響していると思います。

(開発) コードに落とす

次の方針でコードに落とします。

  1. ogr2ogr で生成した3つのストリームを立て続けに Tippecanoe に与えて一体のベクトルタイルにする。
  2. 上記 1. の三連ストリームを出す部分をまず先に作って標準出力で確認する。
  3. そのストリームを 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
        ]
      ]
    ]
  }
}
...

(調整) データを鑑賞し、属性を調整方針を考える

データを見るに、レイヤがまとめられた形でオンラインで提供されるベクトルタイルとしてはどのように属性を調整するべきか考えてみると、私は次のように感じました。

  • ベクトルタイルでは、属性にレイヤ名を冠するメリットがありません。属性名はシンプルにしましょう。つまり、codename のようなシンプルなものにします。
  • 地方公共団体コードを数値型で持っていることについては、私は好感が持てます。なぜなら、コードを整数値で表現する場合には正規の形が定義しやすいですが、文字列型の場合には正規の形をわざわざ定義しなければならないからです。数値型なら「ゼロフィルをするかしないか」のようなことで悩む必要がありません。

(文書化) ベクトルタイルスキーマを整理して文書化しておく

上記の調整方針に従って、ベクトルタイルを次のようなスキーマに調整することにします。

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 を開いてください。

付録

感想

  • 実際の現場では、このドキュメントほどはお行儀よくやりません。とりあえずいい加減にものを作ってしまって、何度でもやりなおすようなアプローチを取り、最後に苦しみながらドキュメントを書く(あるいは、ドキュメントを書き忘れる)ことが多いです。
  • 自分で似たミニプロジェクトを重ねながら流していて思ったのですが、部品の共通化というのはプロダクトの信頼性を上げるために重要だと、工学の教科書通りに、改めて思いました。ただ、よい共通部品は抽象的思考の中から上流で設計されるときよりも、並列で流れるミニプロジェクトたちが、周囲の成果を盗むあうときにできるかもしれないと思います。抽象的思考の中から生まれる共通部品は、悪いものではありませんが、費用対効果が説明しづらい場合が多いです。
  • 何よりも手を動かすこと、それから、言語にはそれほどこだわらずにドキュメントすることが重要ですね。ドキュメント作業も、プロダクトも重要ですがプロセスも重要なのですね。

本稿のドラフト置き場

https://hackmd.io/k_CpPVFPRpiMi16rO4x9Cg?both

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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())\""
    }

}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む