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>
こんなやり方もありますね。
ひらの