leaflet.js
を業務で使う事になったので調査。
埼玉大学谷謙二研究室さんが公開してくれている日本語のチュートリアル( http://ktgis.net/service/leafletlearn/index.html )が分かりやすかったので、やってみた。
なお、公式チュートリアル( https://leafletjs.com/examples.html )も存在するが、今回は触れてない。
learn_leaflet.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>埼玉大学谷謙二研究室さんが公開してくれているleaflet.jsの日本語チュートリアルをやってみた。</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.0/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.3.0/dist/leaflet.js"></script>
<style>
/*紫色のアイコンのCSS */
.icon1 {
width: 20px !important;
height: 20px !important;
border-radius: 10px;
border: 3px solid #fdfdfd;
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.8);
background-color: rgb(204, 51, 166);
}
/*茶色のアイコンのCSS */
.icon2 {
width: 20px !important;
height: 20px !important;
border-radius: 10px;
border: 3px solid #fdfdfd;
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.8);
background-color: rgb(204, 125, 51);
}
/* 作成するdivのCSS */
.infostyle {
border: solid 1px;
background-color: azure;
border-radius: 10px;
opacity: 0.8;
padding: 5px;
font-size: 15px;
color: black;
}
</style>
<script>
// 定数:地図URL
const MAP_URL_STD = 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png'; // 地理院地図・標準地図タイル
const MAP_URL_PALE = 'http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'; // 地理院地図・淡色地図タイル
const MAP_URL_OPEN = 'http://tile.openstreetmap.jp/{z}/{x}/{y}.png'; // オープンストリートマップのタイル
const MAP_URL_RELIEF = 'http://cyberjapandata.gsi.go.jp/xyz/relief/{z}/{x}/{y}.png'; // 地理院地図・色別標高図タイル
const MAP_URL_HILL = 'http://cyberjapandata.gsi.go.jp/xyz/hillshademap/{z}/{x}/{y}.png'; // 地理院地図・陰影起伏図タイル
// 定数:地図URLに紐づくaタグの属性
const ATTR_URL_STD = "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>"; // 地理院地図
const ATTR_URL_PALE = "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>"; // 地理院地図
const ATTR_URL_OPEN = "<a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors"; // オープンストリートマップ
// 定数:座標
const COORD_BASE = [35.40 , 136]; // 初期位置
const COORD_SAITAMA_UNIV = [35.8627 , 139.6072]; // 埼玉大学
const COORD_SAKURA_KUYAKUSHO = [35.8561 , 139.6098]; // 桜区役所
const COORD_KITAURAWA_STA = [35.871986 , 139.645908]; // 北浦和駅
const COORD_ROUTE = [[35.865465, 139.60734], [35.870404, 139.6249], [35.870195, 139.6320], [35.871047, 139.6447], COORD_KITAURAWA_STA]; // 北浦和駅~埼玉大学の道案内
const COORD_SAITAMA_UNIV_SITE = [[35.864891, 139.605503], [35.865969, 139.6088], [35.865378, 139.6097], [35.863309, 139.609559], [35.858996, 139.609709], [35.858248, 139.608722], [35.859813, 139.6053], [35.864248, 139.6056], [35.864891, 139.605503]]; // 埼玉大学の敷地
const COORD_STOREs = [ // コンビニ一覧
{ pos: [35.8645472, 139.6048663], name: "セブンイレブン浦和埼玉大学店" },
{ pos: [35.8689857, 139.6086909], name: "セブンイレブンさいたま大久保店" },
{ pos: [35.871305, 139.6128431], name: "ファミリーマート浦和上大久保店" },
{ pos: [35.8665389, 139.6133905], name: "ミニストップさいたま上大久保店" },
{ pos: [35.8650306, 139.6070633], name: "ローソン埼玉大学店" }
];
// クラス:オーバレイするグリッド線(タイル座標(z,x,y))をGridLayerで表示するためのクラス
var GridLayerClass = L.GridLayer.extend({
createTile: function (coords) {
//div要素でタイルを作成
var tileDiv = L.DomUtil.create('div', '');
tileDiv.setAttribute("style", "border: solid 1px");
//タイル要素の中にzxyを表示するdiv要素を作成
var coordsDiv = L.DomUtil.create('div', '', tileDiv);
coordsDiv.setAttribute("style", "position:absolute; background-color:white; padding:5px; border:solid 1px; left:10px; top:10px; font-size:15px;");
coordsDiv.innerHTML = "z / x / y = " + coords.z + " / " + coords.x + " / " + coords.y;
return tileDiv;
},
});
// 変数:地図オブジェクトと線オブジェクトは各関数で使うためグローバル変数にしておく
var map;
var pline;
//
// 画面表示時の初期処理
// - 各チュートリアルの内容を本関数に集約したよ
//
function init() {
// -------------------- 基本オブジェクトの作成 --------------------
map = L.map('mapcontainer', {
zoomControl: false
});
// -------------------- 初期状態(地図の中心・ズームレベル) --------------------
map.setView(COORD_BASE, 5);
// -------------------- レイヤー(基本地図・切替地図・オーバレイ地図) --------------------
//基本地図:標準地図タイル
L.tileLayer(MAP_URL_STD, { attribution: ATTR_URL_STD }).addTo(map);
//切替地図:標準地図タイル、淡色地図タイル、オープンストリートマップ)
var gsi = L.tileLayer(MAP_URL_STD , { attribution: ATTR_URL_STD });
var gsipale = L.tileLayer(MAP_URL_PALE, { attribution: ATTR_URL_PALE });
var osm = L.tileLayer(MAP_URL_OPEN, { attribution: ATTR_URL_OPEN });
var baseMaps = {
"地理院地図": gsi,
"淡色地図": gsipale,
"オープンストリートマップ": osm
};
//オーバレイ地図:色別標高図タイル、陰影起伏図タイル、グリッド線
var gsirelief = L.tileLayer(MAP_URL_RELIEF, { opacity: 0.7, maxNativeZoom: 15, attribution: ATTR_URL_STD });
var gsirehillshademap = L.tileLayer(MAP_URL_HILL , { opacity: 0.5, maxNativeZoom: 16, attribution: ATTR_URL_STD });
var zxyLayer = new GridLayerClass();
var overlayMaps = {
"色別標高図": gsirelief,
"陰影起伏図": gsirehillshademap,
"XYZ": zxyLayer
};
// 画面にセット
L.control.layers(baseMaps, overlayMaps).addTo(map);
gsi.addTo(map); // ラジオボタンを選択状態にするために必要
// -------------------- スケール(右下) --------------------
L.control.scale({
maxWidth: 200,
position: 'bottomright',
imperial: false
}).addTo(map);
// -------------------- ズーム(左下、基本オブジェクト作成時にzoomControl:falseとする必要あり) --------------------
L.control.zoom({
position: 'bottomleft'
}).addTo(map);
// -------------------- マーカー・ポップアップ・アイコン --------------------
// アイコンオブジェクト
var myIcon1 = L.divIcon({ className: 'icon1', iconAnchor: [13, 13] }); // アンカー:中央
var myIcon2 = L.divIcon({ className: 'icon2', iconAnchor: [0, 0] }); // アンカー:左上
// ポップアップあり、アイコンあり
var popup1 = L.popup({ maxWidth: 550 }).setContent("埼玉大学です<br><img src='su.jpg' width='500' height='375'>");
var popup2 = L.popup().setContent("桜区役所です");
L.marker(COORD_SAITAMA_UNIV, { icon: myIcon1 }).bindPopup(popup1).bindTooltip("埼玉大学").addTo(map);
L.marker(COORD_SAKURA_KUYAKUSHO, { icon: myIcon2 }).bindPopup(popup2).bindTooltip("桜区役所").addTo(map);
// ポップアップなし、アイコンなし
//L.marker(COORD_SAITAMA_UNIV, {title:"埼玉大学", draggable:true}).addTo(map);
//L.marker(COORD_SAKURA_KUYAKUSHO, {title:"桜区役所"}).addTo(map);
// 事前作成した複数のマーカーを表示
var bound = L.latLngBounds(COORD_STOREs[0].pos, COORD_STOREs[0].pos);
for (var num in COORD_STOREs) {
var mk = COORD_STOREs[num];
var popup = L.popup().setContent(mk.name);
L.marker(mk.pos, { title: mk.name }).bindPopup(popup).addTo(map);
bound.extend(mk.pos);
}
// map.fitBounds(bound); // このマーカー群を初期表示位置にしたい場合はfitBoundsを使う(使わない場合はsetViewで設定した座標とズームが適用される)
// -------------------- ベクタレイヤー(円・線・多角形、表示非表示) --------------------
// 円(北浦和駅を中心に1000mごとの円を4つ作成する)
var circleGroup = L.layerGroup();
for (var i = 0; i < 4; i++) {
var r = i * 1000 + 1000;
circleGroup.addLayer(L.circle(COORD_KITAURAWA_STA, {
radius: r,
color: "#FF5555",
fill: false,
weight: 3
}).addTo(map)
);
}
// 円(地図の大きさに関係なく北浦和駅を円で囲む)
var circleMarker = L.circleMarker(COORD_KITAURAWA_STA, {
radius: 20,
color: "#5555ff",
weight: 2,
fill: true,
fillColor: "#ffffff",
opacity: 0.5
}).addTo(map);
// 線(北浦和駅から埼玉大学までの道路)
polyline = L.polyline(COORD_ROUTE, {
color: 'blue',
weight: 5,
bubblingMouseEvents: false // イベントがmapオブジェクトに連鎖するのを防ぐ
}).addTo(map);
// 線(onLineClick関数で使うので空座標で一旦登録しておく)
pline = L.polyline([], {
color: 'blue',
weight: 5,
bubblingMouseEvents: false // イベントがmapオブジェクトに連鎖するのを防ぐ
}).addTo(map)
// 多角形(埼玉大学の敷地)
var polygon = L.polygon(COORD_SAITAMA_UNIV_SITE, {
color: 'green',
weight: 2,
fill: true,
fillColor: 'green',
opacity: 0.5
}).addTo(map);
// 各ベクタレイヤをオブジェクトにまとめて、画面にセット
var overlay = {
"polyline": polyline,
"Circle": circleGroup,
"CircleMarker": circleMarker,
"polygon": polygon
}
L.control.layers(null, overlay).addTo(map);
// -------------------- 地図画面上にDiv要素の追加(左上、この例ではマウス移動した時の座標を表記) --------------------
var latloninfo = L.control({ position: "topleft" });
latloninfo.onAdd = function (map) {
//divを作成(最初は非表示、div上のとmousemoveイベントがmapに連鎖しないように設定)
this.ele = L.DomUtil.create('div', "infostyle");
this.ele.id = "latlondiv";
this.ele.style.visibility = "hidden";
this.ele.onmousemove = function (e) { e.stopPropagation() };
return this.ele;
};
latloninfo.addTo(map);
// -------------------- イベント(click・mousemove) --------------------
map.on('mousemove', onMapMousemove);
map.on('click', onMapClick);
pline.on('click', onLineClick);
}
//
// 地図上のクリック
// - クリック地点の座標にマーカーを追加
// - マーカーがクリックされた場合は、onMarkerClick()を呼び出し
// - クリックを繰り返すと線を引き続ける
//
function onMapClick(e) {
L.marker(e.latlng).on('click', onMarkerClick).addTo(map);
pline.addLatLng(e.latlng);
}
//
// マーカーのクリック
// - クリックされたマーカーを地図のレイヤから削除する
//
function onMarkerClick(e) {
map.removeLayer(e.target);
}
//
// 線のクリック
// - クリックされた線を削除
function onLineClick(e) {
pline.setLatLngs([]);
}
//地図上を移動した際にdiv中に緯度経度を表示
function onMapMousemove(e) {
var box = document.getElementById("latlondiv");
var html = "緯度:" + e.latlng.lat.toFixed(6) + "<br>" +
"経度:" + e.latlng.lng.toFixed(6);
box.innerHTML = html;
box.style.visibility = "visible";
}
</script>
</head>
<body onload="init()">
<!-- 全画面表示するためにstyle設定 -->
<div id="mapcontainer" style="position:absolute; top:0; left:0; right:0; bottom:0;"></div>
</body>
</html>