趣味で撮る写真をどうにか整理したくて。
ただリンク張るだけだと面白くないので、Google Map APIで地図の上にMarkerとリンクを描画してみました。
ついでにWordPressの上でJSファイルを別に管理しようとしたらちょっと面倒くさかったです。
目次
完成形
現在は少し実装が変わっています。
-
Google Map APIでCustom Popupsを描画する
前回はMarkerというパーツを使って地図上にリンクを乗っけてたんですが。 このMarker、テキストを表示しようとしたらツールチップになってしまい、うまく表示ができなかったんですよね。。。 そこでA ...
続きを見る
実際に動くものはこちら。
-
写真Top
更新履歴 2024/03/02 アカゲラ&オニオオハシの動画を追加 2024/02/26 多々良沼公園(Tatara Marsh Park)を追加 2024/02/25 桐生自然観察の森に行っ ...
続きを見る
で、Markerでの実装で完成した地図がこれ。
更新ボタンを押すとチェックボックスがONになってる項目だけ表示するよう地図を再描画します。
ちなみにこの画像だとペグマンが表示されず空欄になってますが、WordPress側のCSSが干渉してたので修正しました。
-
ペグマンが行方不明になるので修正
Google Map APIを使って地図を作っていたんですが。 ペグマン表示されてないじゃん!! 幸い同じ現象について書いてる人がいたので無事解決。 ペグマン帰ってきました。 目次1 ペグマンとは2 ...
続きを見る
事前準備
Google Map APIは名前の通りGoogleが提供する地図のAPIです。
カスタマイズした地図の作成だけでなく、施設の情報やルート検索ができるAPIも用意されています。
毎月$200の無料枠が用意されていて、例えばJava Script APIであれば1000リクエストで$7。
登録にはクレジットカードが必要ですが、個人利用であれば大抵の場合は無課金で使えるんじゃないでしょうか。
公式ガイドを見ながら登録を済ませておきましょう。
地図にMarkerを描く
そんなに難しいことはしてないのでサクッと。
必要なものはこれだけ。
- Marker用.png
- 地図表示.html
- スタイル.css
- データ.js
- 描画処理.js
公式のチュートリアルがあるので、先に読んだ方がわかりやすいかもしれません。
Marker用.png
画面に描画するためのアイコンを用意しておきます。
アイコン用の画像を指定しない場合はGoogle Mapでおなじみのあの赤いMarkerになるので、こだわりがなければなくても大丈夫です。
地図表示.html
表示を切り替えるためのチェックボックス&ボタンは適当に。
後で件数を表示したいのでspanで箱を作っています。
重要なのは<!--map Objectを描画する箱 -->以下のところです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- checkboxと更新ボタン --> <div> <div><input id="cb_park" type="checkbox" /><img class="alignnone wp-image-3049" src="https://nekodeki.com/wp-content/uploads/2022/05/grn-blank.png" alt="" width="24" height="24" /> 公園(Park)<span id="cnt_park"></span></div> <div><input id="cb_zoo" type="checkbox" /><img class="alignnone wp-image-3049" src="https://nekodeki.com/wp-content/uploads/2022/05/red-blank.png" alt="" width="24" height="24" /> 動物園(Zoo)<span id="cnt_zoo"></span></div> <div><input id="cb_aquarium" type="checkbox" /><img class="alignnone wp-image-3049" src="https://nekodeki.com/wp-content/uploads/2022/05/blu-blank.png" alt="" width="24" height="24" /> 水族館(Aquarium)<span id="cnt_aquarium"></span></div> <div><input id="btn_refresh" type="button" value="更新" /></div> </div> <!--map Objectを描画する箱 --> <div id="map"></div> <!-- Goole Map APIを呼び出すためのリクエスト --> <script src="https://maps.googleapis.com/maps/api/js?key=【自分のAPI Key】&callback=initMap&libraries=&v=weekly" async></script> |
まず、地図自体はJavaScriptで用意するので箱だけ作っておきます。
そしてAPIを呼び出すためのリクエストをasyncで書きます。
この時、作成した地図をWEBで公開する場合はAPI Keyの制限は必ずかけておきましょう。
API Keyを不正に使用されると請求が大変なことになってしまうかもしれませんので。。。
スタイル.css
CSSはとりあえず地図のサイズだけ指定しておきます。
1 2 3 4 |
#map { height: 500px; width: 100%; } |
データ.js
描画するMarkerの数を可変にしたいので連想配列にしておきます。
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 |
// 地図の中心になる位置情報 const default_latlon = { lat: 35.9156984, lng: 139.5788846 }; // アイコンのURL const icon_green = "https://nekodeki.com/wp-content/uploads/2022/05/grn-blank.png"; const icon_blue = "https://nekodeki.com/wp-content/uploads/2022/05/blu-blank.png"; const icon_red = "https://nekodeki.com/wp-content/uploads/2022/05/red-blank.png"; // データの連想配列 const spot_list = { park : [ { view : true, name_jp: "羽生水郷公園", lat: 36.1715691, lng: 139.5950263, url: "https://nekodeki.com/photo-top/spot-list/hanyu-suigo-park/" } ], aquarium : [ { view : false, name_jp: "サンシャイン水族館", lat: 35.7289254, lng: 139.7201573, url: "https://sunshinecity.jp/aquarium/" }, { view : true, name_jp: "さいたま水族館", lat: 36.1730556, lng: 139.5977778, url: "https://nekodeki.com/photo-top/spot-list/saitama-aquarium/" } ], zoo : [ { view : false, name_jp: "上野動物園", lat: 35.7164535, lng: 139.7713177, url: "https://www.tokyo-zoo.net/zoo/ueno/" } ] }; |
位置情報の取得
Google Map APIで使う位置情報は世界測地系というやつです。
位置情報を取得する方法はいろいろありますが、Google Mapから取るのが簡単だと思います。
同じGoogle Mapからなら間違えて日本測地系の位置情報を取ることもないですしね。
- Google Mapで場所を検索
- 表示されたMarkerを右クリック
で位置情報がコピーできます。
描画処理.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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
// html要素 let btn_refresh; let cb_park; let cb_zoo; let cb_aquarium; let cnt_park; let cnt_zoo; let cnt_aquarium; let cntNums = {"park": 0, "zoo": 0, "aquarium": 0}; let gMap; let markers = []; let cbVals = {"park": true, "zoo": true, "aquarium": true}; /** * DOMツリーの構築が完了した時点で発火するイベント * 各要素を変数にセット * 各スポットの件数を取得してHTMLにセット * 更新ボタンのイベントをセット */ document.addEventListener("DOMContentLoaded", function() { // チェックボックス初期設定 cb_park = document.getElementById("cb_park"); cb_zoo = document.getElementById("cb_zoo"); cb_aquarium = document.getElementById("cb_aquarium"); cb_zoo.checked = true; cb_park.checked = true; cb_aquarium.checked = true; // 件数 cnt_park = document.getElementById("cnt_park"); cnt_zoo = document.getElementById("cnt_zoo"); cnt_aquarium = document.getElementById("cnt_aquarium"); rewriteCnt(); // 更新ボタン btn_refresh = document.getElementById("btn_refresh"); btn_refresh.addEventListener('click', event => {refreshMarker()}); }); /** * 画像なども含めてすべてのリソースを読み込んだ後に発火するイベント * 各スポットの件数を取得してHTMLにセット */ window.onload = function() { // DOMContentLoaded時にinitMapが終わっていなかった場合の保険 rewriteCnt(); }; /** * Google APIが読み込まれたら自動で実行される * マップの初期化を行う */ function initMap() { // Mapオブジェクト作成 const firstViewPoint = default_latlon; gMap = new google.maps.Map(document.getElementById("map"), { zoom: 9, center: firstViewPoint, }); // マップの読み込みが終わったら要素を追加 google.maps.event.addListenerOnce(gMap, "idle", function(){ addMarker(gMap, cbVals); }); }; /** * マップにマーカーを追加する * @param {google.maps.Map} map - Google Map オブジェクト * @param {object} cbValues - checkboxの値を格納した連想配列 */ function addMarker(map, cbValues) { // 既存のmarkerがあればすべて削除する markers.forEach((oldMarker) => { oldMarker.setMap(null); }); markers = []; // 施設情報読み込み let icon_url; Object.keys(spot_list).forEach(function(key) { switch (key){ case "park": icon_url = icon_green; break; case "aquarium": icon_url = icon_blue; break; case "zoo": icon_url = icon_red; break; default: icon_url = icon_green; } // checkboxがONなら描画 if (cbValues[key] === true) { // 新しいマーカーを追加する let cnt = 0; spot_list[key].forEach((spot) => { if (spot["view"]) { cnt = cnt + 1; let marker = new google.maps.Marker({ position: {lat: spot["lat"], lng: spot["lng"]}, title: spot["name_jp"], icon: {url: icon_url} }); marker.addListener("click", () => { window.location.href = spot["url"]; }); marker.setMap(map); markers.push(marker); } cntNums[key] = cnt; }); } }); } /** * Markerを更新する */ function refreshMarker() { // チェックボックスの状態を反映する cbVals["park"] = cb_park.checked; cbVals["zoo"] = cb_zoo.checked; cbVals["aquarium"] = cb_aquarium.checked; // チェックボックスの状態を渡してMarkerを再描画する addMarker(gMap, cbVals); // 件数を再描画する rewriteCnt(); } /** * 件数を更新する */ function rewriteCnt() { cnt_park.innerText = cntNums["park"] + "件"; cnt_zoo.innerText = cntNums["zoo"] + "件"; cnt_aquarium.innerText = cntNums["aquarium"] + "件"; } |
function initMap()
こいつがAPIを使用するうえで一番重要なfunctionです。
HTMLに書いたAPIのリクエストが終わると自動で発火します。
ここで作成したMap ObjectにMarkerを追加していきますが、そのまま書くとObjectの用意が終わる前に追加処理が走ってエラーになってしまう可能性があります。
なのでaddListenerOnceで処理が終わるのを待ってから追加するようにします。
1 2 3 4 |
// マップの読み込みが終わったら要素を追加 google.maps.event.addListenerOnce(gMap, "idle", function(){ addMarker(gMap, cbVals); }); |
function addMarker(map, cbValues)
Markerの追加処理はいろいろやりたいので別functionにしました。
基本はここでMarkerを追加しますが、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// checkboxがONなら描画 if (cbValues[key] === true) { // 新しいマーカーを追加する let cnt = 0; spot_list[key].forEach((spot) => { if (spot["view"]) { cnt = cnt + 1; let marker = new google.maps.Marker({ position: {lat: spot["lat"], lng: spot["lng"]}, title: spot["name_jp"], icon: {url: icon_url} }); marker.addListener("click", () => { window.location.href = spot["url"]; }); marker.setMap(map); markers.push(marker); } cntNums[key] = cnt; }); } |
更新ボタンを押したときに一旦リセットしてから描画したいので、既存のMarkerがある場合はすべて削除してます。
Markerを削除するにはMarker Objectに対してsetMap(null)を実行します。
1 2 3 4 |
// 既存のmarkerがあればすべて削除する markers.forEach((oldMarker) => { oldMarker.setMap(null); }); |
WordPress上で地図を表示する
地図ができたら、後はWordPressにのせるだけ。
だと思ったら結構大変でした。。。
JSファイルはWordPressにUPできない
普通にJSファイルをUPしようとしたらセキュリティの問題があるからダメだと。
まぁわからんでもないけど。。。
プラグインでJSとCSSをWordPress上で管理できるようにする
さてどうするかと思ったらちょうどいいプラグインがありました。
無料版は機能制限がありますが、小規模の処理ならまぁ大丈夫でしょう。
場合によっては有料版の検討を
このプラグイン、無料版だとすべてのページで登録されたソースを降らせる仕組みになっているようです。
つまり本来はJSやCSSが必要がないはずのページを表示した場合でもファイルがダウンロードされてしまうと。
例えばロードが終わったときに何かを描画する処理があったとして、IDやクラスがたまたま被っていたら想定外のページで発火してレイアウトが崩れてしまうかもしれません。。。
まぁある程度は運用でカバーできますが、複雑なものを作る場合は有料版にした方がいいかもしれません。
複雑な処理はクラウドにしたほうがいいかも
AWSならLambda、GCPならGoogle Cloud Functionsですかね。
今回ならデータ.jsに書いたspot_listをクラウドから取得するイメージです。
その方が
- 処理をクラウド側に任せられるのでブラウザ(端末)側のリソースを食わない
- データリソースが大きくなっても対応可能
というメリットがあるかと。
LambdaもGoogle Cloud Functionsも無料枠があるので個人利用だと課金されないでしょうしね。
WordPressなんだから自分のサーバー上にPHPで書けと思わないでもないですが、Lambdaのほうがお手軽なので。。。
今は写真を整理する作業が多すぎて余裕がないですが、そのうちクラウド化したらまた書くと思います。
関連
-
Google Map APIでCustom Popupsを描画する
前回はMarkerというパーツを使って地図上にリンクを乗っけてたんですが。 このMarker、テキストを表示しようとしたらツールチップになってしまい、うまく表示ができなかったんですよね。。。 そこでA ...
続きを見る
-
ペグマンが行方不明になるので修正
Google Map APIを使って地図を作っていたんですが。 ペグマン表示されてないじゃん!! 幸い同じ現象について書いてる人がいたので無事解決。 ペグマン帰ってきました。 目次1 ペグマンとは2 ...
続きを見る
-
Google Map APIのデフォルトボタンを非表示にする
前回自分で作ったGoogole Mapからペグマンが行方不明になってた件。 表示自体は直ったけど、やっぱりストリートビューボタンいらないよね? ということで非表示にしちゃいました。 Google Ma ...
続きを見る