緯度、経度を画像のExifから取得する - 画像に埋め込まれた緯度、経度の取得 - C#

画像に埋め込まれたExif情報にある緯度、経度を取得するコードを紹介します。

UI

下図のUIを準備します。画面にあるコントロールに加えて、OpenFileDialogを配置します。

コード

以下のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GetExif
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
        textBox_FileName.Text = openFileDialog1.FileName;
      }
    }

    private void button_GetExif_Click(object sender, EventArgs e)
    {
      string filename = textBox_FileName.Text;
      Bitmap bmp = new Bitmap(filename);

      for (int i = 0; i < bmp.PropertyItems.Length; i++) {
        if (bmp.PropertyItems[i].Id == 0x0001) {
          string value = System.Text.Encoding.ASCII.GetString(bmp.PropertyItems[i].Value);
          value = value.Trim(new char[] { '\0' });

          if (value == "N") {
            textBox_Output2.Text += "北緯";
          }
          else if (value == "S") {
            textBox_Output2.Text += "南緯";
          }
        }
        if (bmp.PropertyItems[i].Id == 0x0002) {
          UInt32 deg_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 0);
          UInt32 deg_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 4);
          double deg = (double)deg_numerator / (double)deg_denominator;

          UInt32 min_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 8);
          UInt32 min_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 12);
          double min = (double)min_numerator / (double)min_denominator;

          UInt32 sec_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 16);
          UInt32 sec_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 20);
          double sec;
          if (sec_denominator == 0) {
            sec = 0;
          }
          else {
            sec = (double)sec_numerator / (double)sec_denominator;
          }

          textBox_Output2.Text += string.Format("{0:f}度{1:f}分{2:f}秒\r\n",deg,min,sec);
        }

        if (bmp.PropertyItems[i].Id == 0x0003) {
          string value = System.Text.Encoding.ASCII.GetString(bmp.PropertyItems[i].Value);
          value = value.Trim(new char[] { '\0' });

          if (value == "E") {
            textBox_Output2.Text += "東経";
          }
          else if (value == "W") {
            textBox_Output2.Text += "西経";
          }
        }

        if (bmp.PropertyItems[i].Id == 0x0004) {
          UInt32 deg_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 0);
          UInt32 deg_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 4);
          double deg = (double)deg_numerator / (double)deg_denominator;

          UInt32 min_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 8);
          UInt32 min_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 12);
          double min = (double)min_numerator / (double)min_denominator;

          UInt32 sec_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 16);
          UInt32 sec_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 20);
          double sec;
          if (sec_denominator == 0) {
            sec = 0;
          }
          else {
            sec = (double)sec_numerator / (double)sec_denominator;
          }
          textBox_Output2.Text += string.Format("{0:f}度{1:f}分{2:f}秒\r\n", deg, min, sec);
        }
      }
    }
  }
}

緯度、経度の数値の形式について

緯度、経度の数値は、24バイトで表現されています。最初の8バイトが"度"、9バイトから16バイトまでが"分"、17バイト以降が"秒"を表しています。度、分、秒、の8バイトはさらに前半の4バイトが分子、後半の4バイトが分母を表しています。

解説

北緯、南緯の取得

北緯、南緯を示す情報はPropertyItemのIDが"0x0001"の要素に格納されています。
if (bmp.PropertyItems[i].Id == 0x0001) {
  string value = System.Text.Encoding.ASCII.GetString(bmp.PropertyItems[i].Value);
  value = value.Trim(new char[] { '\0' });

  if (value == "N") {
    textBox_Output2.Text += "北緯";
  }
  else if (value == "S") {
    textBox_Output2.Text += "南緯";
  }
}
PropertyItemのIDが"0x0001"の要素の値を文字列として取り出し、末尾の\0を除去し、文字が"N"であれば北緯を、"S"であれば南緯を示しています。

緯度の取得

緯度はPropertyItemのIDが"0x0002"の要素に格納されています。
if (bmp.PropertyItems[i].Id == 0x0002) {
  UInt32 deg_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 0);
  UInt32 deg_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 4);
  double deg = (double)deg_numerator / (double)deg_denominator;

  UInt32 min_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 8);
  UInt32 min_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 12);
  double min = (double)min_numerator / (double)min_denominator;

  UInt32 sec_numerator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 16);
  UInt32 sec_denominator = BitConverter.ToUInt32(bmp.PropertyItems[i].Value, 20);
  double sec;
  if (sec_denominator == 0) {
    sec = 0;
  }
  else {
    sec = (double)sec_numerator / (double)sec_denominator;
  }

  textBox_Output2.Text += string.Format("{0:f}度{1:f}分{2:f}秒\r\n",deg,min,sec);
}
先に紹介した緯度の形式に沿って数値を取り出し、度、分、秒の値を取り出します。データの形式によっては度と分のみで記述されている場合があり、その場合は秒の分母の値が0になっています。バイト配列から数値への変換はBitConverterクラスを用います。

東経、西経の取得

東経、西経を示す情報はPropertyItemのIDが"0x0003"の要素に格納されています。
if (bmp.PropertyItems[i].Id == 0x0003) {
  string value = System.Text.Encoding.ASCII.GetString(bmp.PropertyItems[i].Value);
  value = value.Trim(new char[] { '\0' });

  if (value == "E") {
    textBox_Output2.Text += "東経";
  }
  else if (value == "W") {
    textBox_Output2.Text += "西経";
  }
}
PropertyItemのIDが"0x0003"の要素の値を文字列として取り出し、末尾の\0を除去し、文字が"E"であれば東経を、"W"であれば西経を示しています。

経度の取得

経度はPropertyItemのIDが"0x0004"の要素に格納されています。
値の取得方法は緯度と同じです。

実行結果

アプリケーションを起動し、[参照]ボタンを押します。ファイルオープンダイアログが表示されますので、緯度、経度を取得したい画像を選択します。画像を選択するとテキストボックスにファイルパスが入力されるので、その後[Exif取得]ボタンを押します。
Exif情報に緯度経度が埋め込まれていれば、テキストボックスに緯度、経度が表示されます。(下図参照)


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用

関連するページ

掲載日: 2012-04-25
iPentec all rights reserverd.