geopicker – Leafletで表示される地図上の点をクリック(orタップ)すると緯度・経度を表示するWebアプリを作ってみた。

By | 2020年9月28日

ここまでのあらすじ

Leatletには地図を表示する機能だけではなく、その地図を操作した時にイベントを発生させる機能や、イベント発生時の処理を記述するためのイベントハンドラを定義することができます。

そのイベントの一つ(かつもっとも直感的にわかりやすいものの一つ)に…

「地図上のある点がクリック(orタップ)されたら、その緯度・経度を通知する。」

というイベント(clickイベント)があります。

そこで、

「これをWordpressのダッシュボードに組み込めば、Leafletの地図上の好きな場所にポップアップがサクサク表示できるようになるんじゃね?」

と思い、さっそく組み込んでみました↓

↑で作成したページで、ポップアップの表示のために必要な情報(緯度、経度及びポップアップ文字列)を上記のページより指定し、画面上の「変更を保存」ボタンを押すとデータベースの然るべきテーブルに指定されたデータを保管することができます(保存したデータを読み出してLeafletの地図上に表示させるためのWordPressのショートコードについてはこちら参照)。

ただ、データベースに保管したデータの編集・削除の機能までは作成しておらず、この機能自体の公開は難しいかなと思っていたところ、地図上の点をクリック(orタップ)した時に、その地点の緯度・経度が画面上に表示されると見た目的には面白そうだったので( ^ω^)、その部分だけ切り出して公開可能なWebアプリ化することにしました。

スポンサーリンク

まず、名前を考えます。


スポンサーリンク

昔、24bitカラーのディスプレイ端末がまだ高価だった頃、RGB値の16進数と実際の色の対応関係がよくわからず、

「それなら、RGB値が指定されたら、それに対応する色見本を作るJavaのアプレットを作ればいいじゃん❤️」

というわけで、Javaのアプレットを作成したことを思い出しました。

そして、そのようなニーズは他にもあったらしく、色のRGB値を指定するとそれに対応する色見本を表示ツールを総称して英語では「colorpicker」と呼ぶそうです。

最近はGoogle先生の検索フィールドに単に”#rrggbb”と入力して「検索」ボタンをクリックすると、colorpickerが表示されたりします(この記事を最初に書いた時点(2020年9月)の情報です。)↓

そこで、作成するWebアプリの名前は「geopicker」と命名することとしました。

次に、コードを書きます。

基本方針


スポンサーリンク

コードを書くと言っても、元ネタはvsseのHTMLファイル及びCSSファイルをコピーして、Leafletのコードを入れ込んで、ちょっと修正するだけで完成する…

そんなふうに考えていた時期がありましたが、いくつか考慮が必要なところがありました。

LeafletとVue.jsを組み合わせて使用する

地図上の点をクリック(orタップ)された際に発生するイベントのハンドリングはこの記事のコードの場合にはWordPressのscript-loader.phpでjQueryが読み込まれることから、jQueryを用いてDOMツリーから操作の対象となるノードを選択し、処理(=緯度・経度データの挿入)を行っています。

一方、Leafletのコードの入れ込み先となるvsseではVue.jsを使っています。jQueryを使うとなるとNode.js+jQueryの組み合わせになりますが、この組み合わせにはどことなく敗北感が漂いますので、Node.js+Vue.jsの組み合わせでLeafletのイベントのハンドリングができないか考えます。

ぐぐったり試行錯誤してみたりした結果、以下のようにコードを書くとVueインスタンスのデータオブジェクトにデータが渡るようです。

  1. Vueインスタンスを作成する際にはインスタンスを格納するための変数を定義します。以下の例ではappにVueインスタンスが格納されます。
     let app = new Vue({
       el: ‘#app’,
       data: {
         latlng: ‘Click the map!!’
       }
     });

     

  2. Leafletのイベントハンドラ(setLonLat関数)はMapオブジェクト(map)を作成した後に以下のように書くことで追加します。
    map.on(‘click’,setLonlat);

     

  3. Vueインスタンス内部のデータオブジェクト内の変数には変数名を指定することでアクセスできるので、手順2で定義したイベントハンドラ内で以下のように書くことで緯度・経度の値を渡します。なお、app.latlngはapp.$data.latlngとしても良いです(app.$dataでデータオブジェクトが取得できます)。
    function setLonlat(e) {
     app.latlng = “Latitude: “+e.latlng.lat+”, Longitude: “+e.latlng.lng;
    }

    スポンサーリンク

     

Vueインスタンスのデータオブジェクトにデータが渡れば、mustache記法等でWebページ上における表示位置を指定することによって、ページ上にそのデータを表示できますね。👍

経度の数値の絶対値が180を超えてしまうことがある問題への対応

Leafletの地図をものすごくズームアウトすると、地球全体(北極や南極を除きます。)が表示されます↓

メルカトル図法の地図を横(東西方向)に貼り合わせた感じの地図になっていて、本初子午線が地図上に複数存在することになります。

この図における経度の表現ですが、最初にロードされた時点において描画される地図のうち、描画エリアの中心にもっとも近い本初子午線の経度を0度とし、そこから東向きを正の向きとして表現されているようです。また、東向きに向かう限り経度の値は単調増加するため、日付変更線を越えると180度を超えますし、別の本初子午線に到達するとその経度は360度になります。

地球上のある位置を表現する際には、「東経(または西経)〇度、北緯(または南緯)〇度」と表すことが一般的で、「地球を(東西方向に)リアルに何周回ったか」ということを気にすることはあまりないと思いますので、経度については-180から180までの範囲の値を出力するように前節のコードを変更します。

具体的には以下のように変更します。

function setLonlat(e) {
 var lon = e.latlng.lng-Math.floor(e.latlng.lng/360)*360;
 if (lon > 180) {
  lon -= 360;
 }
 app.latlng = “Latitude: “+e.latlng.lat+”, Longitude: “+lon;
}

 

Math.floor関数は「引数を超えない整数で最大の整数」を返すので、2行目の処理により変数lonには0以上360未満の値が代入されます。よって、変数lonが180を超える場合には360を引くことで、$(-180,180]$となる値を得ることができます。

レスポンシブ対応

コピー元のvsseのレスポンシブ対応が少々甘かったので、レスポンシブ対応のためのmetaタグ(↓)を追加しています。コピー元のvsseにも追加しています。

<meta name=”viewport” content=”width=device-width,initial-scale=1″>

 

動作確認をやります。

出来上がったらさっそくデプロイして、https://vsse.pandanote.info/geopicker.html にアクセスしてみます。

すると…

横浜駅の周辺が表示されていることが確認できます。

そこで、横浜駅のあたりをクリックします。

すると…

のように、クリックした点に対応する緯度及び経度が表示されます(上図の赤矢印(a))。

なお、この記事を最初に書いた時点(2020年9月)では、クリックした点に対応する緯度及び経度のJSON的な表現もついでに表示できます(上図の赤矢印(b))。

スマホからでも、地図上の点をタップすると緯度・経度が表示できます。

スポンサーリンク

まとめ

クリック(orタップ)した点の緯度・経度を表示するだけという超ベーシックなツールですが、何かの動作確認用や暇つぶしに使えると思いますので、使ってみていただけると幸いです。

実は、ちょっとした機能拡張の実装を始めています。panda先生の次回作にご期待ください。🐼

この記事は以上です。