leafletで構築するの全国公示地価地図

全国公示地価地図

公示地価とは

地価公示は、土地鑑定委員会が毎年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の内容そのまま使ってもよいが、必要な内容を切り取って使って頂いてもよいです。
参考になって頂ければ幸いです。

地図上距離測定できるページ作成について

地図上距離測定ページ

目的

GIS勉強の一環として、簡易な距離測定WEBページを作成して見たいと思います。

ツール選択

web系GISライブラリとして、Google Map, Mapbox, OpenLayersが有名ですが、ライセンスがうるさいため、ここは軽量且つパワフルなleafletを使います。

ベース地図

地理院タイル及びGoogleの地図タイルを利用します。
無料で利用できる地図タイルはありがたいですね。地理院の方はいい仕事してくれました。
初めて税金が正しいところに使われたと実感します。
地理院の地図タイルはたくさんあります。ここで使うものをリストします。

  • 地理院標準地図
    https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png

  • 地理院淡色地図
    https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png

  • 地理院航空写真
    https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg

  • Google地図のみ
    https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}

  • Google写真+地図
    https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}

  • Google写真のみ
    https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}

CSS

全部手書きでもよいがbootstrapを利用します。

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
var map = L.map('map', {
zoomAnimation: false,
zoomControl: false,
inertia: false,
maxZoom: 18,
minZoom: 6,
maxBounds: [[80, 0], [-80, 360]],
maxBoundsViscosity: 1,
worldCopyJump: false,
preferCanvas: false,
zoomSnap: 1,
zoomDelta: 1,
wheelPxPerZoomLevel: 240,
padding: 0,
doubleClickZoom: false,
renderer: L.canvas()
}).setView(new L.LatLng(35.681236, 139.767125), 12);


L.control.polylineMeasure({
showMeasurementsClearControl: false,
showUnitControl: true,
showBearings: false
}).addTo(map);

new MousePositionControl().addTo(map);

var tile = L.tileLayer('https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}', {
attribution: "<a href='https://developers.google.com/maps/documentation' target='_blank'>Google Map</a> | <a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>"
});
map.addLayer(tile);



$('.ctl-tile-url').on('click', function(e){
e.preventDefault();
var tile_url = $(e.target).data('url');
tile.setUrl(tile_url);
});

成果物

地図上距離測定

地図上距離測定して見たい方はぜひ使ってみてください。

ソースコードもコンパイルしてないので、興味のある方はぜひ見てください。

感想

leafletは他のライブラリを依存しないし、ソースコードも少ないため、読んでみるとjavascriptの勉強にもなります。
大変素晴らしいライブラリです。今後もっと深堀して行きたいと思います。