Exifから画像がGPS情報を含むか判定【Vue.js】

# はじめに
初めてのアドベントカレンダーの4日目を担当します，[ぽら](https://twitter.com/pola5pola5)です！
自分は，画像に含まれるGPS情報を取得する方法，GPS情報がない場合のエラーハンドリングについて記載します．

Vue.jsに慣れていない初心者の方や，フロントで画像のGPS情報のバリデーション方法を探している方などの参考になれば幸いです！

# 画像に含まれるGPS情報について
最近は，スマートフォンで写真を撮ることが多くあると思います．その際，位置情報をONにしていると画像のExifの中にGPS情報を含めることができます．

## Exifとは
Exifは，カメラで撮影された際に様々なメタデータを格納し，画像データに埋め込まれています．
[Exchangeable image file format - Wikipedia - ウィキペディア](https://ja.wikipedia.org/wiki/Exchangeable_image_file_format)
この情報を取得することで，画像が撮影された日時や，GPS機能による位置情報が確認できます．
# 下準備
## Vueプロジェクトの作成
本記事ではプロジェクト作成は割愛させていただきました．[こちら](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)の記事通りに進めれば作成できると思います．
`http://localhost:8080/`にサンプルが表示されれば成功です．

今回はVueのバージョン`2.6.11`の環境下で実装していますが，Vue3系での実装も実際に確認しました．

## ページ作成
作成したVueプロジェクト内の`src/components`配下に`pages`というディレクトリを作成し，`pages`内に`Input.vue`というファイルを作成します．ここに画像をアップロードできるようにしていきます．
## ルーティング設定
トップ画面を使って手軽に試せるようにルーティング設定を行います．今回は最低限の設定で行っていきます．

```js:src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Input from './components/pages/Input.vue';

Vue.use(Router)
const router = new Router({
    mode: 'history',
    routes: [
      {
        path: '/',
        name: 'input',
        component: Input
      },
    ]
})
export default router
```

```js:src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'; // ++

Vue.config.productionTip = false

new Vue({
  router, //++
  render: h => h(App),
}).$mount('#app')
```

```vue:App.vue
<template>
  <router-view />
</template>
```

Vue Routerについてより詳しく知りたい方は[こちら](https://router.vuejs.org/ja/)

## 画像のアップロード
画像の入力ができるようにします．今回は`jpeg`画像を対象に実装しています．

```vue:Input.vue
<template>
  <div class="Input">
    <h1>画像のGPS情報取得</h1>
    <div>
      <input type="file" accept="image/jpeg"/>
    </div>
  </div>
</template>
```

![スクリーンショット 2021-12-03 16.15.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1624987/92c98c9a-2ac2-3e48-9faf-c247b8d1e364.png)


ファイルを選択してアップロードできるようになりました．

## 画像の情報を見てみる
次に，ファイルの情報を取得できるようにします．
`onImageChange`というメソッドを追加しました．

```vue:App.vue
<template>
  <div class="Input">
    <h1>画像のGPS情報取得</h1>
    <div>
      <input type="file" accept="image/jpeg" @change="onImageChange" />
    </div>
  </div>
</template>

<script>
  export default {
    methods: {
      onImageChange(e) {
        const files = e.target.files;
        console.log(files[0]);
      },
    },
  };
</script>
```

実際に画像を入力します.
![スクリーンショット 2021-12-03 16.20.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1624987/19865824-c52a-b46c-6924-4691c9adc255.png)

このように画像が撮影された日時などは取得できます．
ただ，このままではExifにはアクセスできていません．
そこで，今回は[blueimp/JavaScript-Load-Image](https://github.com/blueimp/JavaScript-Load-Image)というライブラリを使用します．

# Exif情報を取得
##Exif情報を出力してみる
npmでライブラリのインストールを行います．

```
npm install blueimp-load-image
```

インストールした`blueimp-load-imag`から`loadImage`をインポートして，Exif情報を出力します．

```vue:App.vue
<template>
~~
</template>

<script>
  import loadImage from "blueimp-load-image";

  export default {
    methods: {
      checkGPS(file) {
        loadImage.parseMetaData(file, function (data) {
          console.log("Exif data: ", data.exif);
        });
      },
      onImageChange(e) {
        const files = e.target.files;
        this.checkGPS(files[0]);
      },
    },
  };
</script>

```

![スクリーンショット 2021-12-03 16.44.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1624987/472076ef-f5d4-1e22-af12-a54f77618ca3.png)

機種情報などが出力できているのがわかります．このようにExifには様々な情報が含まれ，`TAGID`に情報が紐づいています．
TAGIDの一覧は[こちら](http://cachu.xrea.jp/perl/ExifTAG.html)．

実際に各項目には`parseMetaData`内で，

```
data.exif.get("GPSLongitude");
```
のように`get("欲しい情報")`でアクセスできます．

## 位置情報を含むか判定
GPS情報を取得することはできました．
ただ，このままではGPSを含まない場合には空の値にアクセスしてしまうのでGPS情報を含まない場合にはエラー表示できるようにします．

```vue:App.vue
<script>
 ~~
      checkGPS(file) {
        loadImage.parseMetaData(file, function (data) {
          if (data.exif && data.exif.get("GPSInfo")) {
            console.log(data.exif.get("GPSInfo"));
          } else {
            alert(
              "エラー: " + file.name + "\n位置情報を含む画像を選択してください"
            );
          }
        });
      },
~~
</script>
```

今回は，試しにGPS情報を含まない自分のアイコン`pola_.jpg`を入力しました．
![スクリーンショット 2021-12-03 17.23.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1624987/794d7dab-75fb-ff17-1e79-3b6d714f441b.png)

ちゃんとエラーハンドリングできました！

念のため実装した`Input.vue`を載せておきます．

```vue:Input.vue
<template>
  <div class="Input">
    <h1>画像のGPS情報取得</h1>
    <div>
      <input type="file" accept="image/*" @change="onImageChange" />
    </div>
  </div>
</template>

<script>
  import loadImage from "blueimp-load-image";

  export default {
    methods: {
      checkGPS(file) {
        loadImage.parseMetaData(file, function (data) {
          if (data.exif && data.exif.get("GPSInfo")) {
            console.log(data.exif.get("GPSInfo"));
          } else {
            alert(
              "エラー: " + file.name + "\n位置情報を含む画像を選択してください"
            );
          }
        });
      },
      onImageChange(e) {
        const files = e.target.files;
        this.checkGPS(files[0]);
      },
    },
  };
</script>
```


# おわりに
この度，ハッカソンに参加していた他の学生さんの呼びかけのおかげで，勇気を出してアドベントカレンダーに初挑戦できました！

記事を書くこともほぼ初めてなので，至らない点，ご指摘等ありましたらコメントいただけると幸いです．