BODIK APIの使い方(Javascript編)機能追加1
「BODIK APIの使い方(Javascript編)」で、Javascriptを使ったWebアプリケーションで、BODIK APIをどのように使うのかをご説明しています。課題2では、BODIK APIを呼び出した結果を地図に表示するプログラムを記述しました。
課題2のプログラムに機能を追加することを考えてみましょう。
地図をクリックしたとき、古いマーカーを削除する
課題2のプログラムでは、地図をクリックすると、そのクリックされた地点を中心に一定の半径内にある「AED」をBODIK APIで検索し、その結果を地図上にマーカーとして表示しました。クリックするたびに、マーカーが追加されてしまいます。
これはかっこ悪いので、新しくクリックしたときは、古いマーカーを消すようにしてみましょう。
対策1
基本的な考え方として、「マーカーを覚えておいて、1つずつ削除する」でやってみましょう。
「マーカーを覚えておく」は、配列に入れておけばいいですね。
marker_list = []; marker_list.push(marker);
「マーカーを削除する」は、Leafletの「removeFrom」メソッドを使えます。
marker.removeFrom(map);
プログラムは次のようになります。
配列「marker_list」を関数(initやshowMarker)の外側(グローバル)で定義していることが重要です。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>BODIK API</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin="" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<script type="text/javascript">
let map = null;
let marker_list = null; // マーカーを覚えておく配列
function init() {
map = L.map('map', { zoomControl: false });
// 指定された緯度経度を中心とした地図
let lat = 33.59334325082392;
let lon = 130.35598920962553;
let center = [lat, lon];
map.setView(center, 14);
//地理院地図の標準地図タイル
let gsi = L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
{ attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>" });
gsi.addTo(map);
//showMarker(lat, lon);
map.on('click', onMapClick);
}
function onMapClick(e) {
let latlng = e.latlng;
let lat = latlng.lat;
let lon = latlng.lng;
showMarker(lat, lon);
}
function showMarker(lat, lon) {
let api_server = 'https://wapi.bodik.jp';
let api = 'aed';
let distance = 20000;
let maxResults = 100;
let api_url = `${api_server}/${api}?select_type=geometry&lat=${lat}&lon=${lon}&distance=${distance}&maxResults=${maxResults}`;
fetch(api_url)
.then(response => response.json())
.then(data => {
if (marker_list) { // 古いマーカーがあったら
for (let marker of marker_list) { // 配列から順次取り出し
marker.removeFrom(map); // マップから削除する
}
marker_list = null;
}
let features = data['resultsets']['features'];
marker_list = [];
for (let feature of features) {
let properties = feature['properties'];
let popup = L.popup().setContent(properties['name']);
let geometry = feature['geometry'];
let location = geometry['coordinates'];
let marker = L.marker([location[1], location[0]]).addTo(map);
marker.bindPopup(popup);
marker_list.push(marker); // 配列に記録する
}
});
}
</script>
</head>
<body onload="init()">
<h2>BODIK API program #2</h2>
<div class="map" id="map" style="width:800px;height:600px"></div>
</body>
</html>
これは、わかりやすいのですが、、、削除するときに、「for」で回すところをもうすこしスマートにしたいですね。
対策2
Leafletに「オブジェクトをまとめて扱う」機能はないのかな?と探していたら、「layerGroup」というのを見つけました。
https://leafletjs.com/reference.html#layergroup
説明にも、「まとめて地図に表示したり、削除したりできる」とあるので、これを使ってみましょう。
今回の場合、layerGroupを作るときに、マーカーの配列をパラメータに与えればいいみたいです。
また、マーカーを作成した時点では地図に表示せず、mapの「addLayer」メソッドを使って、作成したLayerを地図に表示するようです。
marker_list = []; let marker = L.marker([location[1], location[0]]); // マーカーを作成するだけ。この時点で地図に表示しない。 marker_list.push(marker); ・・・・ marker_layer = L.layerGroup(marker_list); map.addLayer(marker_layer); // 地図に表示する。
プログラムにすると、次のようになります。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>BODIK API</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin="" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<script type="text/javascript">
let map = null;
let marker_list = null; // マーカーの配列
let marker_layer = null; // マーカーのレイヤー
function init() {
map = L.map('map', { zoomControl: false });
// 指定された緯度経度を中心とした地図
let lat = 33.59334325082392;
let lon = 130.35598920962553;
let center = [lat, lon];
map.setView(center, 14);
//地理院地図の標準地図タイル
let gsi = L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
{ attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>" });
gsi.addTo(map);
//showMarker(lat, lon);
map.on('click', onMapClick);
}
function onMapClick(e) {
let latlng = e.latlng;
let lat = latlng.lat;
let lon = latlng.lng;
showMarker(lat, lon);
}
function showMarker(lat, lon) {
let api_server = 'https://wapi.bodik.jp';
let api = 'aed';
let distance = 20000;
let maxResults = 100;
let api_url = `${api_server}/${api}?select_type=geometry&lat=${lat}&lon=${lon}&distance=${distance}&maxResults=${maxResults}`;
fetch(api_url)
.then(response => response.json())
.then(data => {
if (marker_layer) { // 古いレイヤがあったら
map.removeLayer(marker_layer); // レイヤーを削除する
marker_layer = null;
}
let features = data['resultsets']['features'];
marker_list = [];
for (let feature of features) {
let properties = feature['properties'];
let popup = L.popup().setContent(properties['name']);
let geometry = feature['geometry'];
let location = geometry['coordinates'];
let marker = L.marker([location[1], location[0]]); // 地図に表示しない
marker.bindPopup(popup);
marker_list.push(marker); // 配列に記録する
}
if (marker_list.length > 0) {
marker_layer = L.layerGroup(marker_list); // レイヤーを作成
map.addLayer(marker_layer); // 地図に表示する
}
});
}
</script>
</head>
<body onload="init()">
<h2>BODIK API program #2</h2>
<div class="map" id="map" style="width:800px;height:600px"></div>
</body>
</html>
こんなやり方もありますね。
ひらの

