Android の Camera2 API と LocationManager を使って 位置情報付きの写真を撮る

[Android の Camera2 API を使って カメラのプレビュー画面を表示する](https://qiita.com/ohwada/items/7bb65a566114066c8ced)
の続きです。

下記を参考にした。

- [Android開発 | 画像のExif属性に位置情報や撮影日時を保存する方法](https://mussyu1204.myhome.cx/wordpress/it/?p=161)

### LocationManager

GPS を使って位置情報を取得する Android のクラス

[reference : LocationManager](https://developer.android.com/reference/kotlin/android/location/LocationManager)

参考 : [Androidでの簡単な位置情報取得](https://qiita.com/gksdyd88/items/feeaccdef401ce5644c7)

### EXIF

画像及び画像にカメラの機種や撮影時の条件情報や位置情報を埋め込むための規格

Exchangeable Image file Format (交換可能な画像ファイルフォーマット) の略

参考 : [wikipedia : Exchangeable image file format](https://ja.wikipedia.org/wiki/Exchangeable_image_file_format)


参考 : [デジタルスチルカメラ用画像ファイルフォーマット規格: Exif 2.3](http://www.cipa.jp/std/documents/j/DC-008-2012_J.pdf)

参考 : [Exifファイルフォーマット](http://www2.airnet.ne.jp/~kenshi/exif.html#EXIF)

### 位置情報を取得する権限の設定

```AndroidManifest.xml
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```
### 位置情報を取得する権限を取得する

ユーザが必要とするときに設定できるように
PreferenceFragment を使って設定画面を用意する

詳細は省略する


### PreferenceFragment

Preference を設定するためのクラス

[preference : PreferenceFragment](https://developer.android.com/reference/android/preference/PreferenceFragment)

例 : カメラアプリの設定画面
![camera_settibg_location.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/8ef025da-662c-4660-ebf1-64efb230460b.png)

### 位置情報を取得する

```
    // LocationManage を取得する
    LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

    // リスナーを登録して開始する
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 3, locationListener);

    // リスナー
    LocationListener mLocationListener = new LocationListener() {
        public void onLocationChanged(Location location) {
            //変数に格納して、後で使用する
            mLocation = location;
        }
    };
```

### 位置情報付きの写真を撮る

まず、写真を撮ってJPEG形式でファイル保存する。
次に、ファイルに EXIF を追加する。

### 位置情報を EXIF 形式に変換する

これが一番の難物だった。
何をしなければならないのか理解するのに時間を要した。

位置情報の表記にはいろいろな形式がある。
一般的に使われているのは、10進度数(decimal degrees) と呼ばれるものです。
例えば経度は、イギリスのグリニッジを0度とし東西に180度ずつに分割した−180から180の実数です。
Android　もこれに従っている。

参考：[中学校社会 地理/緯度と経度](https://ja.wikibooks.org/wiki/%E4%B8%AD%E5%AD%A6%E6%A0%A1%E7%A4%BE%E4%BC%9A_%E5%9C%B0%E7%90%86/%E7%B7%AF%E5%BA%A6%E3%81%A8%E7%B5%8C%E5%BA%A6)


規格 EXIF 2.3 では、下記のように定義されている。

> 緯度経度は、度,分,秒 を表す3 つの 有理数 RATIONAL によって表現する

また　Androidのreferenceでは、下記のようになっている。


> Type is rational.
> Format is "num1/denom1,num2/denom2,num3/denom3".

これを最初に読んだときは、??? となった。

[デジタルスチルカメラ用画像ファイルフォーマット規格: Exif 2.3](http://www.cipa.jp/std/documents/j/DC-008-2012_J.pdf)

[reference : TAG_GPS_LATITUDE](https://developer.android.com/reference/android/media/ExifInterface.html#TAG_GPS_LATITUDE)

EXIFでは、緯度経度を 度分秒 で表記する60進度数(Sexagesimal Degrees) が採用されている。
有理数 RATIONAL とは、分子/分母 という文字列での表記
例えば、123 なら 123/1と、3.14 なら 314/100 と表記する。

北緯南緯と東経西経は数値とは別のEXIFタグがある。
文字列で "N" "S" "W "E" と表記する。
これは東西南北(East, West, South, North)の英略語なので、
すんなり理解できた。

横浜の緯度 139.64720　経度3535.4515374は、下記のように表記される。

> E 139/1,38/1,49932/1000
> N  35/1,27/1,5534/1000


プログラムの例

```
public String convert(double latitude) {
        latitude = Math.abs(latitude);
        int degree = (int) latitude;
        latitude *= 60;
        latitude -= (degree * 60.0d);
        int minute = (int) latitude;
        latitude *= 60;
        latitude -= (minute * 60.0d);
        int second = (int) (latitude*1000.0d);

        StringBuilder sb = new StringBuilder(20);
        sb.setLength(0);
        sb.append(degree);
        sb.append("/1,");
        sb.append(minute);
        sb.append("/1,");
        sb.append(second);
        sb.append("/1000");
        return sb.toString();
}

public static String latitudeRef(double latitude) {
        String str = (latitude<0.0d)?"S":"N";
        return str;
}
```

参考 : [stackoverflow : how-to-save-gps-coordinates-in-exif-data-on-android](https://stackoverflow.com/questions/5280479/how-to-save-gps-coordinates-in-exif-data-on-android)

### ExifInterface

EXIFタグを読み書きするための Android のクラス

[reference : ExifInterface](https://developer.android.com/reference/android/media/ExifInterface#summary)

参考 : [Y.A.M の 雑記帳 : Exif](http://y-anz-m.blogspot.com/2011/03/exif.html)


```
    // EXIF 情報を読み込む
    File file;
    ExifInterface exif = new ExifInterface( file.toString() );

    // EXIF 情報に緯度経度情報を追加する
    exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, ref);
    exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, lat);

    // EXIF 情報を保存する
    exif.saveAttributes();
```


### 位置情報を確認する

位置情報付きの写真ファイルを フォトアプリで閲覧する。
情報を見ると地図が表示される。

注意：
フォトアプリで位置情報を確認する時は、
ファイルの保存先は、
外部SDカードの領域(ExternalStorage)であること。

公式サンプル Camera2Basic の流れから
ExternalFilesDir に保存することが多い。
[Camera2Basic](https://github.com/googlesamples/android-Camera2Basic/tree/master/Application/src/main/java/com/example/android/camera2basic)

ExternalFilesDirでは、写真は表示されるが
位置情報は表示されない。

### スクリーンショット

フォトアプリで位置情報付きの写真の情報を見たとき。
![camera216_location.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/4aa2799e-eaa2-bd9e-c32a-82be7e84f70b.png)

サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/Camera216