九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、最近は少しでかけにくくなったものの、その昔はよく「電車 + 折りたたみ自転車」で各地を巡ってそれをブログ記事にしていました。
しかし、毎回ブログを書くたびに「うーん・・・」となることがありました。
それが・・・・・・
地図の埋め込み&リンクをつくるのがメンドウ…😫
というものです。
「ゆるり旅」に出ると、いくつかの場所に行くのですが、その全ての地図を用意するのはなかなか大変なんですね。
そして、何か対策は無いかと考えたら、ひとつアイデアが浮かびました。
写真のGPSデータから自動的にマップを用意すればいいじゃないか
と。
そこで❗
今回はJavaScript
を使って、選択された写真のExif
データから「緯度経度」を取得し、そのデータで該当する場所のマップ・HTMLタグを用意できるようにしてみます。
ぜひ何かの参考になりましたら嬉しいです😊✨
「この前、消費カロリー 4,500 kcal でした。
普段 1,000 もいかないのに😂」
開発環境: Bootstrap 5、Vue 3、Google Map
目次
Google Embed Map の APIキーを取得する
今回使う地図は、Google Map
(埋め込み版)です。
そのため、まずはGoogle Embed Map API
が使えるように「APIキー」を取得しましょう。
※ ちなみにGoogle Embed Map API
は、2021.8.17
現在、完全無料で使えます。
では、Google Cloud にログインし、ページ上部からプロジェクトを選びます(もしない場合はつくってください)
そして、ページ左上の「ハンバーガーボタン」をクリックしてメニューを表示し、「APIとサービス」をクリックします。
ページが移動したら、「APIとサービスの有効化」をクリック。
すると、APIの検索ページが表示されるので、「maps embed api」で検索し「Maps Embed API」をクリックします。
そして、「有効にする」ボタンをクリックすればOKです。
では次に、Maps Embed API
にアクセスするための「APIキー」を取得します。
ページ左側にあるメニューから「認証情報」をクリック。
ページが移動したら、「Map Embed API」を選択し、「認証情報を作成」をクリックしてください。
すると、ポップアップが表示されるので「APIキー」をクリックします。
するとポップアップが表示され、その中にAPIキーがあるのでこれをコピーして控えておいてください。
※ ちなみにこのAPIキーにはドメインなどの制限が入っていないため、どのサイトでも&誰でも使えるようになっています。そのため、制限をかけたいかたは、「ちなみに 1: Google MapのAPIキーに制限をかける」を参考にしてみてください。
Exifデータを取得し、地図表示するコードをつくる
では、メインになるHTML
&JavaScript
のコードです。
<html> <head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div id="app" class="p-5"> <strong class="h3">JavaScript でGPSデータを取得し、地図を表示するサンプル</strong> <hr class="mb-4"> <div class="row"> <div class="col-12"> <label class="btn btn-outline-primary"> ファイルを選択する <input type="file" class="form-control d-none" accept="image/jpeg" @change="onFileChange"> </label> </div> <div class="col-4"> <img class="img-fluid mt-3" :src="image"> </div> <div class="col-4" v-if="hasExif"> <label>緯度</label> <div class="border border-2 p-2 rounded mb-2" v-text="latitude"></div> <label>経度</label> <div class="border border-2 p-2 rounded" v-text="longitude"></div> <div class="mt-4 overflow-auto p-3 bg-light" style="height:300px;" v-if="hasExif"> おまけ: 抽出された Exif データ <hr> <div v-for="(value,key) in exif"> <strong v-text="key"></strong>: <span v-text="value"></span> </div> </div> </div> <div class="col-4" v-if="hasExif"> <iframe class="w-100" height="450" :src="googleMapEmbedUrl" allowfullscreen> </iframe> <textarea class="form-control mt-2" rows="5" v-text="googleMapEmbedTag"></textarea> <a :href="googleMapLinkUrl" class="btn btn-outline-primary mt-3" target="_blank">Googleマップで確認する</a> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.min.js"></script> <script src="https://unpkg.com/vue@3.1.1/dist/vue.global.prod.js"></script> <script src="https://cdn.jsdelivr.net/npm/exif-js"></script> <script> Vue.createApp({ data() { return { exif: {}, googleMapApiKey: '(ここにあなたのAPIキーを書き込む)' } }, methods: { getExif(key) { try { return this.exif[key]; } catch(e) {} return ''; }, getGps(key) { // `Latitude` or `Longitude` const gps = this.getExif('GPS'+ key); const ref = this.getExif('GPS'+ key +'Ref'); if(gps instanceof Array) { const degrees = gps[0]; const minutes = gps[1]; const seconds = gps[2]; let dd = degrees + minutes/60 + seconds/(60*60); if (['S', 'W'].includes(ref)) { // 南半球、西経の場合はマイナス dd *= -1; } return dd } return ''; }, onFileChange(e) { const files = e.target.files; if(files.length > 0) { this.exif = {}; const file = files[0]; const reader = new FileReader(); reader.addEventListener('load', () => { this.image = reader.result; EXIF.getData(file, () => { // Exif を取り出す this.exif = EXIF.getAllTags(file); }); }); reader.readAsDataURL(file); } } }, computed: { hasExif() { return (Object.keys(this.exif).length > 0); }, latitude() { // 緯度 return this.getGps('Latitude'); }, longitude() { // 経度 return this.getGps('Longitude'); }, googleMapEmbedUrl() { const key = this.googleMapApiKey; const latitude = this.latitude; const longitude = this.longitude; return `https://www.google.com/maps/embed/v1/place?key=${key}&q=${latitude},${longitude}`; }, googleMapEmbedTag() { return `<iframe width="450" height="450" src="${this.googleMapEmbedUrl}" allowfullscreen></iframe>`; }, googleMapLinkUrl() { const latitude = this.latitude; const longitude = this.longitude; return `https://www.google.com/maps/search/?api=1&query=${latitude},${longitude}`; } } }).mount('#app'); </script> </body> </html>
この中で実際にExif
データを取得しているのは、onFileChange()
の中ですが、「exif-js」というパッケージを使っています。
(こういったパッケージは本当に助かりますね。感謝です❗)
なお、先ほど取得したAPIキーは「(ここにあなたのAPIキーを書き込む)」の部分と入れ替えてください。
また、getGps()
は少し複雑なのですが、写真のExif
データから取得できるGPS情報は「DMS」とよばれる{度、分、秒}形式になっているので、「DD(Decimal degrees)」と呼ばれる形式に変換しています。
ちなみに 1: Google MapのAPIキーに制限をかける
今のままでは「どのサイトでも&どのAPIにも」使えるようになっているので、勝手にAPIキーを使われてしまう可能性もあります。
そこで、APIキーに制限をかける方法をご紹介します。
制限をかけるのは、次の2つです。
- 他のドメインでは使えないようにする
- 今回のAPIキーを Google マップ以外には使えないようにする
では、さきほど作ったAPIキーが「認証情報ページ」に表示されているので編集ボタンをクリックしてください。
ページ移動したら、「アプリケーションの制限」の中から「HTTP リファラー」を選択して「項目を追加」をクリックしてください。
すると、リファラーを登録するポップアップが表示されるので、以下のようにドメインを入力します。
(*
はワイルドカードなので、「なんでもOK」という意味になります。)
次に、利用できるAPIの制限を設定します。
「APIの制限」から「キーを制限」を選択し、すぐ下にあるセレクトボックスをクリックします。
すると、APIの一覧が表示されるので、この中から「Maps Embed API」にチェックをいれて「OK」をクリックします。
あとは、「保存」ボタンをクリックすれば完了です。
ちなみに 2: スマホの写真にGPSが埋め込まれていない場合
スマホの設定でGPS埋め込みを変更することができます。
やり方は、以下を参考にしてみてください。
📝 参考ページ:
デモを用意しました
今回の機能を試すことができるデモページをご用意しました。
ぜひお試しください。(データは一切送信しません)
📝 デモページ
企業様へのご提案
今回の機能を使うと以下のようなことに使えます。
- お店の外観写真にGPSデータを埋め込んでおけば、後で地図の位置を指定する手間を省くことができます
- 写真を送ってもらうだけで、その人のいる場所や時間を知ることができるので、作業完了報告などに応用することができる
- 連続した写真を撮影することで、1日の動きを地図上に表示することができる
- いわゆる「置き配」の証拠として撮影した写真から、どの場所かをすぐ特定できる
もしこういった機能をご希望でしたらいつでもお気軽に「お問い合わせ」からご連絡ください。m(_ _)m
おわりに
ということで、今回はJavaScript
でExif
データを抜き出し、地図表示をしてみました。
Exif
データは、自宅の場所がバレたりしてしまうので、一時期問題になりましたが、ビジネスにからめてみると、とても便利なので有効活用しやすいんじゃないでしょうか。
なお、上記の理由からアップロードされたExif
データは基本的に削除することが望ましいですが、その方法は以下の記事を参考にしてみてください。
📝 参考ページ: PHPでEXIFデータを操作する方法(取得、書き込み、削除)
ぜひ皆さんもやってみてくださいね。
ではでは〜❗
「各地ではその土地の
おいしいものをいただきます✨」