
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、最近は少しでかけにくくなったものの、その昔はよく「電車 + 折りたたみ自転車」で各地を巡ってそれをブログ記事にしていました。
しかし、毎回ブログを書くたびに「うーん・・・」となることがありました。
それが・・・・・・
地図の埋め込み&リンクをつくるのがメンドウ…
というものです。
「ゆるり旅」に出ると、いくつかの場所に行くのですが、その全ての地図を用意するのはなかなか大変なんですね。
そして、何か対策は無いかと考えたら、ひとつアイデアが浮かびました。
写真の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データを操作する方法(取得、書き込み、削除)
ぜひ皆さんもやってみてくださいね。
ではでは〜
「各地ではその土地の
おいしいものをいただきます」