[C#]GeoTag(GPS情報)をjpgファイルに設定/取得するときのハマったメモ


もくじ
https://qiita.com/tera1707/items/4fda73d86eded283ec4f

GeoTag関連
- GeoTag(GPS情報)をjpgファイルに設定/取得する
- GeoTag(GPS情報)をjpgファイルに設定/取得するときのハマったメモ

やりたいこと

UWPのAPI(GeotagHelper.SetGeotagAsync())を使って、jpegファイルにGeoTagを付与したいのだが、あるjpegファイルだけ、なぜかGeotagHelper.SetGeotagAsync()を実行した際に例外が発生してしまう。

jpegファイルをちゃんとSetGeotagAsyncにセットしてやっているはずが、なんで例外になるのかさっぱりわからない。そのときに調べた(というかいろいろ試した)事のメモ。

結論(例外の原因)

原因は、GeoTag付与しようとしていたjpegファイルが、実はjpegファイルではなかったこと。
具体的には、そのjpegファイルをSystem.Drawing.BitmapSave()メソッドを使って作成していたが、Saveの引数にSystem.Drawing.Imaging.ImageFormat.Jpegを指定してJpeg作成すべきが、System.Drawing.Imaging.ImageFormat.Bmpを指定して、ファイルの拡張子だけ.jpegにしてjpegファイルにしていたことが原因。

Windowsでは、拡張子が.bmpのビットマップファイルの拡張子だけを.jpegに変えてやると普通にjpegとしてペイントやその他アプリで開けるようになるが、GeotagHelper.SetGeotagAsync()的にはそれはダメで、拡張子だけではなく生粋の.jpgファイルでないと、ためした限り受け付けてくれないらしい。
(EXIFのデータを乗っける領域がbmpファイルの先頭部分には用意されてないからかも。)

サンプル

var cur = Directory.GetCurrentDirectory();      // exeのあるディレクトリ
var filepath = cur + @"\ginga.bmp";             // 元の画像
var filepath_out_jpg = cur + @"\ginga_out.jpg"; // jpgとして保存する画像
var filepath_out_bmp = cur + @"\ginga_out_fake.jpg"; // 実はbmpだけどjpgとして保存する画像

using (var fs = new FileStream(filepath, FileMode.Open, FileAccess.ReadWrite))
using (var bmp = new System.Drawing.Bitmap(fs))
{
    // 元の画像を、jpgとbmpで保存し分ける
    bmp.Save(filepath_out_jpg, System.Drawing.Imaging.ImageFormat.Jpeg);
    bmp.Save(filepath_out_bmp, System.Drawing.Imaging.ImageFormat.Bmp);
}

// GPS値作成
BasicGeoposition bgps = new BasicGeoposition() { Latitude = 3.0, Longitude = 2.0, Altitude = 1.0 };
// GPS値をGeopointにセット
Geopoint gps = new Geopoint(bgps);

try
{
    // GPS値をjpgファイルに書き込み
    var stjpg = await Windows.Storage.StorageFile.GetFileFromPathAsync(filepath_out_jpg);
    await GeotagHelper.SetGeotagAsync(stjpg, gps);// →こっちは問題なくgeotag付与できる
    var stbmp = await Windows.Storage.StorageFile.GetFileFromPathAsync(filepath_out_bmp);
    await GeotagHelper.SetGeotagAsync(stbmp, gps);// →こっちは、拡張子はjpgだが実はbmpでjpgではないので、geotag付与時に例外発生
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}