Mapbox 勉強日記(3)基本概念

Mapbox 勉強日記(3)基本概念

Mapbox基本概念

Mapboxでは、Layer(StyleLayer)とSourceはとても重要な部分になります。

Layer(StyleLayer)

mapboxの描画方法になります。
現時点下記
circle,
heatmap,
hillshade,
fill,
fill-extrusion,
line,
symbol,
background,
raster,
custom
10種類あります。

それぞれStyleLayerを継承し、定義されています。

Source

layerを描画する時に使われるデータです。
vector,
raster,
raster-dem,
geojson,
video,
image,
canvas
7種類あります。

SourceCacheはSourceのコンテナーであり、Sourceをタイル形式に変換し、そのタイルをCacheします。

Bucket

BucketはSourceがタイルに分割されたgeoデータをGLのvertexとして使えるBufferなどに変換し、キープしています。

GLSL

実際webglで描画に使うShaderスクリプト。

Draw

具体的に描画コードの入口はpainter.jsファイルの下記の部分です。

painter.js

1
2
3
4
5
6
7
renderLayer(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array<OverscaledTileID>) {
if (layer.isHidden(this.transform.zoom)) return;
if (layer.type !== 'background' && layer.type !== 'custom' && !coords.length) return;
this.id = layer.id;

draw[layer.type](painter, sourceCache, layer, coords, this.style.placement.variableOffsets);
}

まとめ

MapboxはEvent Drivenの形で、データ処理、描画しています。
最新のjavascript, webGL, webWorker, GISの技術を多用しています。
そのため、完全に理解するには多少時間がかかります。
今後できるだけ詳しく書いて行きたいと思います。

Mapbox 勉強日記(2)使い方

Mapbox 勉強日記(2)使い方

Mapboxは基本Style, Source, Layerを設定しながら使います。

Mapboxを使うにはまずmapboxgl.Mapのインスタンスを作成します。
この時、いろいろと設定出来ます。

その中styleの設定がとりわけ重要になります。

下記の図のように、styleにはsourceとlayersの設定が含まれます。

sourceは地図で使うリソースを記入します。geojson, 画像tile, vector tileなどなど。
layersにはsourceを使った描画方法を記入します。backgroundなど、sourceを使わないレイヤも
ありますが、基本sourceに登録された、データを
circle,
heatmap,
hillshade,
fill,
fill-extrusion,
line,
symbol,
background,
raster,
custom
のいずれかの方法で描画することを設定します。

具体的には下記のサンプルを参照してください。

トークン無しで使えるMapboxデモ。

当サイトでは以前Mapboxを利用にはTokenが必須と書きましたが
厳密にはmapbox APIを利用するならTokenが必須であり、mapbox-gl-jsを使うだけであれば、
Token無しでできます。

そのため、今回はトークン無しmapbox-gl-jsを使えるデモを作成します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var map = new mapboxgl.Map({
container: 'map',
zoom: 10,
center: [139.767125, 35.681236],
style: {
"version": 8,
"sources": {
"tilemap": {
"type": "raster",
"tiles": ["https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png"],
"tileSize": 256,
}
},
"layers": [{
"id": "background",
"type": "background",
"paint": {
"background-color": "rgb(125,125,125)"
}
}, {
"id": "tilemap",
"type": "raster",
"source": "tilemap",
"minzoom": 0,
"maxzoom": 18
}]
},
});

デモサイトはこちらです。

トークン無しで使えるMapboxデモ

もちろんmapbox-gl-jsでは、map.addLayerとmap.addSourceの動的に追加する方法も用意されています。

必要に応じて使えます。

Mapbox 勉強日記(1)環境構築

Mapbox 勉強日記(1)環境構築

Mapboxについて

前から気になっていましたが、今回は本気で勉強することになりした。

Mapboxはwebglを活用して、vectortileなどのデータをブラウザで高速に描画するライブラリです。
使うには事前に登録とトークンが必要です。

環境構築

やり方はlinuxのVMにmapbox-gl-jsをインストールし、vscodeのRemote Developmentを使て
コードを読んだり、修正したり、テスト実行したりるす。

VMはvirtualboxでUbuntu64_18.04を入れました。

本家マニュアル
https://github.com/mapbox/mapbox-gl-js/blob/master/CONTRIBUTING.md

本家githubにも書いてありますが、こちらのやり方は下記の通りです。

rootユーザで実行しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#Install lib
apt-get update
apt-get install build-essential git nodejs libglew-dev libxi-dev


#Install yarn
curl -o- -L https://yarnpkg.com/install.sh | bash


#Install clone
git clone git@github.com:mapbox/mapbox-gl-js.git

cd mapbox-gl-js
yarn install

vscodeの設定

Remote Development

Remote DevelopmentのExtensionをインストールします。
手順はネットでいっぱいあるため割愛します。

Remote Developmentを使って、linuxにつなぐため
C:/Users/{user}/.ssh\configファイルに
下記のような設定を追加する。

1
2
3
4
5
6
Host vbox.ubuntu64_18_14
User root
HostName xxx.xxx.xxx.xxx
Port 22
IdentityFile C:/Users/{user}/.ssh/key.file
BatchMode yes

key.fileやIPを自分の環境と合わせて変更してください。

flowtype

Remote Developmentよりubuntu64_18_14に接続出来たら、cloneしたmapbox-gl-jsフォルダを開きます。

maxbox はflowtypeを使って開発されています。
そのため、リモートワークスペースにvscodeのExtension
「Flow Language Support」と「vscode-flow-ide」をインストールします。

ディフォルトのvscodeではエラーいっぱい出ますのでjavascript.validate.enableの設定をfaseにする。

setting.json

"javascript.validate.enable": false

debug

debug起動

1
2
3
4
5
6
7
8
9
10
#token作成
MAPBOX_ACCESS_TOKEN=yourkey yarn run build-token

#コード修正後または起動前にはビルドコマンドでコンパイルする。
yarn run build-dev

#サーバーを起動する。
yarn run start-server

#起動後はhttp://xxx.xxx.xxx.xxx:9966/debug にアクセスして確認できる。

終り

ひとまず、環境が整えました。これからはコードを分析したり、カスタマイズ
したりして、理解を深めていきたいと思います。

ピュアなwebglで描画する3D日本の地形地図

3D日本の地形地図

今回はwebglについて勉強していた時の成果物
日本の地形地図
に関するの内容を書きます。
実際2年も前に作ったものであるため詳細は忘れている部分もありますが、
思い出せるところまで書きます。

また私の勉強不足により、間違っている内容もあるかもしれないので、
間違っているところをTwitterで教えて頂けると嬉しいです。

webglについて

webglはブラウザの上でグラフィックカードを使った3D描画のためのインターフェースになります。
近年モダンなブラウズがほとんど対応しています。

webglをベースにした有名なライブラリはthree.jsになります。しかし本当にwebglを勉強したい方
はthree.jsを介さずに、直javascriptでwebglを基本から練習することをおすすめします。

その理由は、グラフィックカードを動かしているのはGLSL (OpenGL Shading Language) だからです
グラフィックカードで実行されるプログラムはCPUで実行される一般的(C, java, javascript)な言語
と違って、vertex shaderとfragment shaderに書かれて通りに動作します。

OpenGLを触れていないプログラマーにはこの部分はかなりハードルがあると思います。実際私もそうだった。

日本の地形の元データ

地形データは国土地理院の標高タイルタイルを元に作成します。
標高タイルの詳細は下記のサイトを参照してください。

https://maps.gsi.go.jp/development/demtile.html

写真地図は下記のタイルを使います。
https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg

日本の地形描画の基本的な考え方

3Dの描画はcanvas 2dと違って、円や、四角、ポリゴンを直接描画できません。
webglは基本すべてが三角の頂点データになります。

そのため、下記の手順で描画します。

  1. まず地理院の標高タイルを取得してから、格子状に三角の頂点データを作成します。

  2. その後、各三角頂点データの間のピクセルを写真地図タイルのピースで貼り付けます。

この2つが出来たら、後はデータを渡して、webglで描画させるだけです。

実際描画のコードの抜粋を貼ります。

vertex shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
attribute float      gridIndex;
uniform mat4 mvpMatrix;
uniform vec4 gridInfo;
uniform sampler2D demTex;
varying vec2 colorCoord;
void main(void) {
int idx = int(gridIndex - 1.0);
int j = idx / int(gridInfo.z);
int i = idx - j * int(gridInfo.z);
vec2 XY = vec2(float(i) * gridInfo.x - gridInfo.x * gridInfo.z / 2.0, gridInfo.y * gridInfo.w / 2.0 - float(j) * gridInfo.y);
vec2 tp = vec2(float(i)/gridInfo.z + 0.5/gridInfo.z, 1.0 - float(j)/gridInfo.w - 0.5/gridInfo.w);
float Z = getZ(texture2D(demTex, tp));
gl_Position = mvpMatrix * vec4(XY, Z, 1.0);
colorCoord = tp;
}

fragment shader

1
2
3
4
5
6
precision mediump    float;
varying vec2 colorCoord;
uniform sampler2D mapTex;
void main(void) {
gl_FragColor = texture2D(mapTex, colorCoord) * vec4(1.0, 1.0, 1.0, 1.0);
}

座標の計算

webglによる3D描画は座標変換がとても重要です。
しかも3次元の座標変換はても複雑になりがちです。
そこで数学の行列の基礎が問われます。
行き詰まった時は、原点に戻り、座標変換のステップを一つずつ追って見るがいいでしょう。

最後に

ある程度shaderを書けて、座標の計算も慣れてから、three.jsを使うとwebglがとても
楽しくなって来ると思います。

webglに関しては、書きたい内容はは沢山ありますので、また整理出来てから書きたい
と思います。

サーバーレスで構築するの全国公示地価地図

全国公示地価地図

公示地価とは

地価公示は、土地鑑定委員会が毎年1回標準地の正常な価格を公示し、一般の土地の取引価格に対して指標を与えるとともに、公共事業用地の取得価格算定の規準とされ、また、国土利用計画法に基づく土地取引の規制における土地価格算定の規準とされる等により、適正な地価の形成に寄与することを目的としている。

地域の代表となる地点の土地価格を評価し公開されるため、土地価格の参考になります。

公示地価データについて

公示地価のデータは毎年、国土交通省から公開されています。今回使うデータはこちらからダウンロードしたものです。

http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-L01-v2_5.html

地図サイト構築について

まず成果物はこちらです。

全国公示地価地図

典型的にやり方は、

  1. 生データを解析して、データベースに登録する。
  2. APPサーバを立て、APPサーバがクライアントのリクエストを受け、SQLまたはNoSQLでデータベースからデータを取得し、クライアントへ送る。
  3. クライアントブラウザがデータを受け取り、javascriptによりレンダリングする。

ですが、今回は、サーバーサイドの不可を軽減するため、生データを小さいjsonファイルに分割し、静的ファイルとして直Nginxの元に置きます。
いわゆるサーバーレスのサイトになります。

データの配置

メイン地図画面全国の、公示地価データを一気に表示するため、
2019年分のデータをgeobufで圧縮し、一つのファイルにまとめます。

やり方は前回の記事人口集中地区(DID)地図を作成しました
を参照してください。

各地点の過去データを描画するため、
年次フォルダ分けして、地点毎のデータを小さいjsonファイルに保存する。

実装

実装は、例のleaflet + bootstrapで実装しましたが、
今回は例年地価の変動をグラフで表現したいため、d3.jsを導入しました。
d3.jsによるグラフ描画は非常にシンプルできれいがグラフが作成されます。
実際描画の関数のコードを貼ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
var makeChart = function(priceData){
var dataset = [
{year: '2010', price: priceData['2010']/10000},
{year: '2011', price: priceData['2011']/10000},
{year: '2012', price: priceData['2012']/10000},
{year: '2013', price: priceData['2013']/10000},
{year: '2014', price: priceData['2014']/10000},
{year: '2015', price: priceData['2015']/10000},
{year: '2016', price: priceData['2016']/10000},
{year: '2017', price: priceData['2017']/10000},
{year: '2018', price: priceData['2018']/10000},
{year: '2019', price: priceData['2019']/10000}
];
var margin = {top: 20, right: 20, bottom: 70, left: 30};
var width = $('.modal-body').width() - margin.left - margin.right;
var height = 300 - margin.top - margin.bottom;
var x = d3.scaleBand().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
x.domain(dataset.map(function(d) { return d.year; }));
y.domain([Math.max(d3.min(dataset, function(d) { return d.price; }) - 10, 0), d3.max(dataset, function(d) { return d.price; })]);
var svg = d3.select('.chart-area').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var barGrp = svg.selectAll()
.data(dataset)
.enter()
.append('g');

barGrp .append('rect')
.attr('class', 'chart_bar')
.attr('x', function(d) { return x(d.year) + 10; })
.attr('width', x.bandwidth() - 20)
.attr('y', function(d) { return y(d.price); })
.attr('height', function(d) { return height - y(d.price); });

barGrp .append('text')
.attr('class', 'chart_bar_txt')
.attr('x', function(d){ return x(d.year) + x.bandwidth() / 2; })
.attr('y', function(d){ return y(d.price) - 5; })
.attr('text-anchor', 'middle')
.text(function(d){return d.price ? d.price+'万' : ''});

svg .append('g')
.attr('transform', 'translate(0,' + height + ')')
.call(d3.axisBottom(x));

svg .append("g")
.call(d3.axisLeft(y));

svg.append('text')
.attr('x', -(height / 2) - margin.top)
.attr('y', margin.left)
.attr('text-anchor', 'middle')
.text('価格 (万円)');
};

描画される結果はこんな感じになります。

おわりに

令和元年の公示地価の公開は来年1月以後になるようです。
またその時にデータを更新したいと思います。

d3.jsビジュアル系では大変人気のライブラリです。さらに理解を深めて行きたいですね。

コピペだけで作る完全無料なWEB地図

完全に無料なWEB地図はありますでしょうか。

WEB地図と言えばGoogleMapが圧倒的なシェアと知名度があります。一昔はGoogleMap
は完全無料であったが、今はクレジット登録が必須となっています。
一応毎月200ドルの無料枠があるため、一般的なホームページは無料枠を使い切って課金されること
は少ないでしょう。

しかしアクセスの多いサイトではGoogleMapは運営コストになります。
GoogleMap以外にMapboxやYahoo!地図などありますがどれもユーザ登録やトークン発行が必要です。
そこで、ユーザ登録やトーコンなど一切不要で、簡単にWEB地図は作れないでしょう。
その解答は勿論あります。

完全に無料なWEB地図を作る方法

それはとても簡単です。

  1. 地図描画にはleafle.jsを使うこと。
  2. そして、ベースとなるタイル地図はOpenStreetMapまたは地理院タイルを使うことです。
  • leafle.jsについて

leafle.jsはオープンソフトの地図描画
用のjavascriptライブラリです。完全にフリーで使えます。

  • 地図タイルについて

地図タイルとは決まったルールで格子状に分割された地図画像の断片です。詳細は
地理院サイトで確認できます。
leafle.jsはそのルールをもとに、画像の断片をきれいに並べて一枚の地図のように表示します。

この地図タイルは膨大なデータになるため、個人サーバで用意するのは難しいです。
無料で使える選択肢としてOpenStreetMapと地理院タイルになります。

OpenStreetMapのライセンスについて、こちらです。

“© OpenStreetMap contributors”を表示し、本家リンクを貼れば、基本無料で使えます。

地理院のタイルも、「国土地理院」と出所を明示し、リンクを貼ることで自由に使える地図が沢山あります。
詳細はこちらです。

日本では地理院タイルのほうが表示速度が速いです。

コピペだけでWEB地図を作る

下記の地図を利用して、とても簡単に作れます。

  • 地図の下のラジオボタンより、ベースとなる地図を選びます。
  • 地図をズームや移動して表示範囲を決めます。
  • 地図の上のマーカーを移動して、目的地点を決めます。

その後「コードをコピーする」ボタンを押して、コードをにコピーします。

コピー下コードを自分のサイトやブログに貼り付けるだけで地図が作成されます。

取り合えず確認したい場合は、JSFiddleのHTMLの部分にコードを貼り付けて見てください。

     

地図をさらに変更するには

  • 地図のサイズを変更するにはコードの下記部分の数字を変更してください。
    自分のサイトのコード環境に合わせた変更が必要です。
1
<div id="sengine_map" style="width: 600px; height: 400px;"></div>
  • アイコンを変更したい場合はコードの中iconUrl、iconRetinaUrlの部分を自分のアイコンのURLに書き換えてください。
    iconSize、iconAnchorも自分のアイコンの大きさに合わせて変更してください。
1
2
3
4
5
6
7
icon: L.icon({
iconUrl: "",
iconRetinaUrl: "",
shadowUrl: "",
iconSize: [24,36],
iconAnchor: [12,36]'
})
  • さらにいろいろやりたい場合はleaflet本家のドキュメントを参考に修正してください。

以上、コピペだけで作るWEB地図でした。

人口集中地区(DID)地図を作成しました

人口集中地区(DID)地図

人口集中地区について

人口集中地区とは、国勢調査の統計データに基づいて定められています。

原則下記の条件を満たすエリアになります。

  1. 1平方キロメートル当たり4,000人以上の基本単位区等が市区町村の境域内で互いに隣接する。
  2. 隣接した地域の人口が国勢調査時に5,000人以上を有する。

国土交通省 航空局の「無人航空機(ドローン、ラジコン機等)び安全な飛行のためのガイドライン」 には
下記A~Cの場所を飛行の禁止空域としています。

  • (A)地表又は水面から 150m 以上の高さの空域
  • (B)空港周辺の空域
  • (C)人口集中地区の上空

そのため、(C)の人口集中地区はドローン飛行の場合に重要な参考要素になります。

人口集中地区をweb上で簡単に確認できる人口集中地区(DID)地図サイトを構築しました。

データの前処理

メタデータは国土数値情報 人口集中地区データです。

ダウンロードされるGeoJSONファイルは50Mぐらいです。webで直転送にはロード時間が長くなる。

そこで、geobufによるProtocol Buffersファイルへの圧縮を行います。
結果 5Mぐらいまで圧縮されました。
普通のzip圧縮は10Mぐらいでしたので、なかなかいい圧縮率です。

コードを貼ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
var fs        = require('fs');
var geobuf = require('geobuf');
var Pbf = require('pbf');


var txt = fs.readFileSync('./data/A16-15_00_DID.geojson', 'utf8');
var geojson = JSON.parse(txt);

var pbf = geobuf.encode(geojson, new Pbf());

var buff = Buffer.allocUnsafe ? Buffer.from(pbf) : new Buffer(pbf);

fs.writeFileSync('./data/did.pbf', buff);

leaflet によるの描画

データを準備できたら、描画は簡単です。
leafletはGeoJSONをnativeでサポートしています。

pbfを展開してL.geoJSONでレイヤを追加するだけです。

コードの抜粋を貼ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//データ取得と展開
var loadGeobuf = function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function(oEvent) {
var pbf = new Pbf(new Uint8Array(xhr.response));
var geo = geobuf.decode(pbf);
callback(geo);
};
xhr.send();
};

//描画の部分
loadGeobuf('data/did.pbf', function(geojson){
L.geoJSON(geojson, {
smoothFactor: 1.2,
noClip: true,
weight: 1
}).bindPopup(function (layer) {
var props = layer.feature.properties;
var html = [
'<table>',
'<tr><td>人口: </td><td>' + props['人口'] + '</td></tr>',
'<tr><td>人口割合: </td><td>' + props['人口割合'] + '</td></tr>',
'<tr><td>面積: </td><td>' + props['面積'] + '</td></tr>',
'<tr><td>面積割合: </td><td>' + props['面積割合'] + '</td></tr>',
'<tr><td>市町村名称: </td><td>' + props['市町村名称'] + '</td></tr>',
'<tr><td>調査年度: </td><td>' + props['調査年度'] + '</td></tr>',
'</table>'
];
return html.join('\n');
}).addTo(map);
});

詳細は人口集中地区(DID)地図にアクセスしてコードを見てください。
とてもシンプルです。

感想

  • GISのデータは巨大なデータが多いがある程度小さいデータであればgeobufで十分対応できます。

  • データが大きい場合、静止画のタイルに落とすか、vextor tile にして描画する工夫が必要です。

  • leafletの数の少ないGeoJSONを非常にシンプルに描画できます。個人の間隔としては、polygonの場合数千件
    が限界と思います。それ超えると描画が遅くなり、ブラウザが固まることがあります。

  • 基本的にSVGよりCanvasで描画したほうが速いような気がする。

地図レンダリングエンジンharp.glについて

地図レンダリングエンジンharp.gl

harp.glとは

数年前はweb上に地図を描画するには、単に静的タイル画像(png, jpg)をブラウザ上に位置を合わせて貼り付けること
だけでした。主にOpenLayers,leafletがその代表的にフレームワークでした。実際昔のGoogleMapもそうでした。

最近は主流のブラウザがWebGLを対応するようになってから、WebGLを利用した3Dベースの描画方法が広がっています。
GoogleMapは特にそうですが、Mapbox GL JSもその典型になります。

harp.glもMapbox GL JSと非常に似ていて、three.jsベースにオーペンソースで開発された地図レンダリングエンジンです。
今日はharp.glについて、調べた内容を書きます。

ソースコード

ソースコードはgithubに公開されています。
TypeScript言語で書かれています。個人的にはピュアなJavaScriptであってほしかったな。。。

速度

厳密はベンチマークはしていませんが、geojson-viewer
サンプルサイトに50Mのgeojsonファイルを投げたところ、サクサク描画されます。流石のGL powerと思います。

注意事項

Open sourceではありますが、完全にフリーではありません。
実際開発で利用したい場合は登録して、TOKEN発行が必要です。
現時点でフリーで利用できるリミットは下記通りです。

  • Location Services

250K Transactions per month
5K SDK Monthly Active Users
250 Assets per month
Pay per additional Transactions

  • XYZ

2.5GB Data transfer per month
5GB Database storage per month
Pay per additional Data transfer or Database storage

詳細はhere開発者サイトをご確認ください。

感想

フリーのデータソース(例え、地理院地図タイル)をTOKENなしで、描画したかったが、どうやらそれができないようです。残念でした。

実際コーディングして使うまで至りませんでしたが、描画のパフォーマンスはよさそうです。必要な時にまた研究します。

地図の上で用途地域を確認できる用途地域地図サイトの構築について

用途地域地図

用途地域とは

用途地域は、住居、商業、工業など市街地の大枠としての土地利用を定めるもので、

  • 第1種低層住居専用地域
  • 第2種低層住居専用地域
  • 第1種中高層住居専用地域
  • 第2種中高層住居専用地域
  • 第1種住居地域
  • 第2種住居地域
  • 準住居地域
  • 近隣商業地域
  • 商業地域
  • 準工業地域
  • 工業地域
  • 工業専用地域

13種類あります。用途地域が指定されると、それぞれの目的に応じて、建てられる建物の種類が決められます。
国都交通省ではそのデータが無料で提供されております。
http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-A29.html

建築などプランを作成する上でも大変重要な情報の一つになります。

今回は用途地域を地図上からに簡単に確認できるサイトを構築したいと思います。

まず、成果物はこちらです。

用途地域地図

leafletをベースにした、サーバーレスサイトの実装

ベースとなる地図は恒例のleafletです。

用途地域データの準備

国都交通省のデータはshapeファイルであるため、qgisなど特定のソフトでしか表示できない。
webで表示させるためにgeojsonフォーマットに変換する。shapeからgeojsonの変換は一昔は面倒だったが、
今はqgisを使えば今は簡単にできます。

単純にgeojsonデータをwebで表示することもできるが、用途地域データの場合、データ量が大きいため
エリア分けて表示することになる。今回日本全国シームレスで表示したいため、geojsonをmapboxの
ベクトルタイルフォーマットに変換します。

ベクトルタイル仕様書

変換ツールはtippecanoeです。

用途地域描画の実装

用途地域のラベリング

用途地域デーはポリゴンデータであるため
まず、ポリゴンの中心を計算しラベリングのポイントにする。

地図のズームや移動を対応しながら、重ならないラベリングを実装するため、こちらの内容を参考に実装しました。

ベクトルタイルの描画

mapboxなら、ディフォルトでベクトルタイルの描画が内装されているが、leafletでは独自の実装が必要です。実装方法についてはベクトルタイルの使用をわかっているかたなら難しくはないと思います。後日コードを整理して公開したいと思います。

感想

GISデータをすぐ巨大になりがちです。ここ数年IT業界でビッグデータという言葉が流行っていますが、地図データは昔ながらのビッグデータです。
一方、サービスとしてweb提供するには、ネットワーク、端末メモリやcpuの制限があります。そこで、いかにデータを捌き、いかにUIデザインを工夫するのが醍醐味ですね。

まだまだ勉強不足ではありますが、日々精神していきたいと思います。

国土地理院標高APIを利用したleafletマウスポイント標高プラグインの実装

標高表示プラグイン

背景

国土地理院標高APIが公開され、任意地点の
高精度な標高値が簡単に取得できるようになっています。
一方leafletでまだそれを利用した標高表示プラグインが見当たらい為作ってみました。

まず、デモサイトはこちらです。

実装

leaflet は軽量でありながら、大変強力なライブラリです。実際地理院地図2Dはleafletがベースになっています。
今回jQueryとunderscore.jsを依存します。

データ取得時の$.ajaxと、eventを抑えるための.debounce、と描画じに.templateを使っています。

コード抜粋を貼ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
var ElevitionJP = L.Control.extend({
options: {
position: 'bottomright'
},
demApiURL: '//cyberjapandata2.gsi.go.jp/general/dem/scripts/getelevation.php',
onAdd: function(map){
this._container = L.DomUtil.create('div', 'l_elevitionjp');
L.DomEvent.disableClickPropagation(this._container);
map.on('mousemove', this._onMouseMove, this);

var me = this;
me._lazyDem = _.debounce(function(){
var requestData = {
lat: me.currentLatLng.lat,
lon: me.currentLatLng.lng,
outtype: 'JSON'
};
$.ajax({
url: me.demApiURL,
dataType: 'json',
data: requestData,
beforeSend: function(jqXHR, settings){
jqXHR.requestData = requestData;
}
}).done(function(data, textStatus, jqXHR) {
if(jqXHR.requestData.lat == me.currentLatLng.lat && jqXHR.requestData.lon == me.currentLatLng.lng) {
me.currentDem = data;
me.resetHtml();
}
});
}, 300);

me.currentLatLng = {lon: null, lng: null};

return me._container;
},

onRemove: function(map){
var me = this;
map.off('mousemove', me._onMouseMove, me);
},

_onMouseMove: function(e){
var me = this;
me.currentLatLng = e.latlng.clone();
me.currentDem = {elevation: '-----', hsrc: '-----'};
me._lazyDem();
me.resetHtml();
},

resetHtml: function(){
var me = this;
var zoom = me.currentZoom;
var lat = me.currentLatLng.lat;
var lng = me.currentLatLng.lng;
var html = _.template([
'<table>',
' <tr><td colspan="2" style="text-align: left;"><%=elevation=="-----" ? "標高: -----" : "標高: " + Math.round(elevation*3.28084) + " ft " + elevation + " m" %></td></tr>',
' <tr><td><%=lat10%></td><td><%=lng10%></td></tr>',
' <tr><td><%=lat60%></td><td><%=lng60%></td></tr>',
'</table>'
].join('\n'))(_.extend({
lat10: lat ? me.convertLat2NS(lat, 5) : '',
lng10: lng ? me.convertLng2EW(lng, 5) : '',
lat60: lat ? me.convertLat2DMS(lat) : '',
lng60: lng ? me.convertLng2DMS(lng) : '',
zoom: zoom,
elevation: '-----',
hsrc: '-----'
}, me.currentDem));
me._container.innerHTML = html;
},
//...
});
  • onAdd onRemove
    L.Control にて初期化や削除時の必須実装。

  • _lazyDem
    ajax を抑えながらデータを取得する実装。

  • resetHtml
    htmlレンダリングの部分です。

CSS

見栄えは .l_elevitionjp で制御できます。
デモサイトでは下記通りです。

1
2
3
4
5
.l_elevitionjp {
border-radius: 5px;
background-color: rgba(256,256,256,0.9);
padding: 5px;
}

ソースコード

コースコードはgithub https://github.com/sengine-xyz/leaflet.elevationJP
にUPしました。

ElevitionJP.jsの内容そのまま使ってもよいが、必要な内容を切り取って使って頂いてもよいです。
参考になって頂ければ幸いです。