Mengonversi Foto yang Geotagged menjadi PhotoOverlay KML

Mano Marks, Tim Google Geo API
Januari 2009

Tujuan

Tutorial ini mengajarkan cara menggunakan foto yang diberi geotag untuk membuat KML PhotoOverlays. Meskipun kode contoh ditulis dalam Python, ada banyak library serupa dalam bahasa pemrograman lain, jadi tidak akan menjadi masalah untuk menerjemahkan kode ini ke bahasa lain. Kode dalam artikel ini mengandalkan library Python open source, EXIF.py.

Pengantar

Kamera digital adalah perangkat yang cukup luar biasa. Banyak pengguna tidak menyadarinya, tetapi mereka melakukan lebih dari sekadar mengambil foto dan video. Mereka juga memberi tag pada video dan foto tersebut dengan metadata tentang kamera dan setelannya. Dalam beberapa tahun terakhir, orang-orang telah menemukan cara untuk menambahkan data geografis ke informasi tersebut, baik yang disematkan oleh produsen kamera, seperti beberapa kamera Ricoh dan Nikon, atau melalui perangkat seperti pencatat GPS dan EyeFi Explore. Ponsel kamera seperti iPhone dan ponsel yang menggunakan sistem operasi Android, seperti G1 dari T-Mobile, menyematkan data tersebut secara otomatis. Beberapa situs upload foto, seperti Panoramio, Album Web Picasa, dan Flickr, akan mengurai data GPS secara otomatis dan menggunakannya untuk memberi geotag pada foto. Kemudian, Anda dapat memasukkan kembali data tersebut ke feed. Tapi di mana serunya? Artikel ini membahas cara mendapatkan data tersebut sendiri.

Header EXIF

Cara paling umum untuk menyematkan data ke dalam file gambar adalah menggunakan Exchangeable Image File Format, atau EXIF. Data disimpan dalam bentuk biner di header EXIF dengan cara standar. Jika mengetahui spesifikasi untuk header EXIF, Anda dapat menguraikannya sendiri. Untungnya, seseorang telah melakukan pekerjaan berat dan menulis modul Python untuk Anda. Library open source EXIF.py adalah alat yang sangat baik untuk membaca header file JPEG.

Kode

Contoh kode untuk artikel ini ada di file ini: exif2kml.py. Jika Anda ingin langsung menggunakannya, download modul tersebut, serta EXIF.py, dan tempatkan keduanya di direktori yang sama. Jalankan python exif2kml.py foo.jpg dengan mengganti foo.jpg dengan jalur ke foto yang diberi geotag. Tindakan ini akan menghasilkan file bernama test.kml.

Mengurai header Exif

EXIF.py menyediakan antarmuka yang mudah untuk menarik header Exif. Cukup jalankan fungsi process_file() dan fungsi tersebut akan menampilkan header sebagai objek dict.

def GetHeaders(the_file):
  """Handles getting the Exif headers and returns them as a dict.

  Args:
    the_file: A file object

  Returns:
    a dict mapping keys corresponding to the Exif headers of a file.
  """

  data = EXIF.process_file(the_file, 'UNDEF', False, False, False)
  return data

Setelah memiliki header Exif, Anda perlu mengekstrak koordinat GPS. EXIF.py memperlakukan ini sebagai objek Ratio, objek untuk menyimpan pembilang dan penyebut nilai. Hal ini menyiapkan rasio yang tepat, bukan mengandalkan bilangan floating point. Namun, KML mengharapkan angka desimal, bukan rasio. Jadi, Anda mengekstrak setiap koordinat, dan mengonversi pembilang dan penyebut menjadi satu bilangan floating point untuk derajat desimal:

def DmsToDecimal(degree_num, degree_den, minute_num, minute_den,
                 second_num, second_den):
  """Converts the Degree/Minute/Second formatted GPS data to decimal degrees.

  Args:
    degree_num: The numerator of the degree object.
    degree_den: The denominator of the degree object.
    minute_num: The numerator of the minute object.
    minute_den: The denominator of the minute object.
    second_num: The numerator of the second object.
    second_den: The denominator of the second object.

  Returns:
    A deciminal degree.
  """

  degree = float(degree_num)/float(degree_den)
  minute = float(minute_num)/float(minute_den)/60
  second = float(second_num)/float(second_den)/3600
  return degree + minute + second


def GetGps(data):
  """Parses out the GPS coordinates from the file.

  Args:
    data: A dict object representing the Exif headers of the photo.

  Returns:
    A tuple representing the latitude, longitude, and altitude of the photo.
  """

  lat_dms = data['GPS GPSLatitude'].values
  long_dms = data['GPS GPSLongitude'].values
  latitude = DmsToDecimal(lat_dms[0].num, lat_dms[0].den,
                          lat_dms[1].num, lat_dms[1].den,
                          lat_dms[2].num, lat_dms[2].den)
  longitude = DmsToDecimal(long_dms[0].num, long_dms[0].den,
                           long_dms[1].num, long_dms[1].den,
                           long_dms[2].num, long_dms[2].den)
  if data['GPS GPSLatitudeRef'].printable == 'S': latitude *= -1
  if data['GPS GPSLongitudeRef'].printable == 'W': longitude *= -1
  altitude = None

  try:
    alt = data['GPS GPSAltitude'].values[0]
    altitude = alt.num/alt.den
    if data['GPS GPSAltitudeRef'] == 1: altitude *= -1

  except KeyError:
    altitude = 0

  return latitude, longitude, altitude

Setelah Anda memiliki koordinat, Anda dapat dengan mudah membuat PhotoOverlay sederhana untuk setiap foto:

def CreatePhotoOverlay(kml_doc, file_name, the_file, file_iterator):
  """Creates a PhotoOverlay element in the kml_doc element.

  Args:
    kml_doc: An XML document object.
    file_name: The name of the file.
    the_file: The file object.
    file_iterator: The file iterator, used to create the id.

  Returns:
    An XML element representing the PhotoOverlay.
  """

  photo_id = 'photo%s' % file_iterator
  data = GetHeaders(the_file)
  coords = GetGps(data)

  po = kml_doc.createElement('PhotoOverlay')
  po.setAttribute('id', photo_id)
  name = kml_doc.createElement('name')
  name.appendChild(kml_doc.createTextNode(file_name))
  description = kml_doc.createElement('description')
  description.appendChild(kml_doc.createCDATASection('<a href="#%s">'
                                                     'Click here to fly into '
                                                     'photo</a>' % photo_id))
  po.appendChild(name)
  po.appendChild(description)

  icon = kml_doc.createElement('icon')
  href = kml_doc.createElement('href')
  href.appendChild(kml_doc.createTextNode(file_name))

  camera = kml_doc.createElement('Camera')
  longitude = kml_doc.createElement('longitude')
  latitude = kml_doc.createElement('latitude')
  altitude = kml_doc.createElement('altitude')
  tilt = kml_doc.createElement('tilt')

  # Determines the proportions of the image and uses them to set FOV.
  width = float(data['EXIF ExifImageWidth'].printable)
  length = float(data['EXIF ExifImageLength'].printable)
  lf = str(width/length * -20.0)
  rf = str(width/length * 20.0)

  longitude.appendChild(kml_doc.createTextNode(str(coords[1])))
  latitude.appendChild(kml_doc.createTextNode(str(coords[0])))
  altitude.appendChild(kml_doc.createTextNode('10'))
  tilt.appendChild(kml_doc.createTextNode('90'))
  camera.appendChild(longitude)
  camera.appendChild(latitude)
  camera.appendChild(altitude)
  camera.appendChild(tilt)

  icon.appendChild(href)

  viewvolume = kml_doc.createElement('ViewVolume')
  leftfov = kml_doc.createElement('leftFov')
  rightfov = kml_doc.createElement('rightFov')
  bottomfov = kml_doc.createElement('bottomFov')
  topfov = kml_doc.createElement('topFov')
  near = kml_doc.createElement('near')
  leftfov.appendChild(kml_doc.createTextNode(lf))
  rightfov.appendChild(kml_doc.createTextNode(rf))
  bottomfov.appendChild(kml_doc.createTextNode('-20'))
  topfov.appendChild(kml_doc.createTextNode('20'))
  near.appendChild(kml_doc.createTextNode('10'))
  viewvolume.appendChild(leftfov)
  viewvolume.appendChild(rightfov)
  viewvolume.appendChild(bottomfov)
  viewvolume.appendChild(topfov)
  viewvolume.appendChild(near)

  po.appendChild(camera)
  po.appendChild(icon)
  po.appendChild(viewvolume)
  point = kml_doc.createElement('point')
  coordinates = kml_doc.createElement('coordinates')
  coordinates.appendChild(kml_doc.createTextNode('%s,%s,%s' %(coords[1],
                                                              coords[0],
                                                              coords[2])))
  point.appendChild(coordinates)

  po.appendChild(point)

  document = kml_doc.getElementsByTagName('Document')[0]
  document.appendChild(po)

Anda dapat melihat bahwa kami hanya menggunakan metode W3C DOM standar, karena metode tersebut tersedia di sebagian besar bahasa pemrograman. Untuk melihat cara kerja semuanya, download kode dari sini.

Contoh ini tidak memanfaatkan kecanggihan PhotoOverlays sepenuhnya, yang memungkinkan Anda membuat eksplorasi mendalam terhadap foto beresolusi tinggi. Namun, ini menunjukkan cara menggantung foto dalam gaya papan iklan di Google Earth. Berikut adalah contoh file KML yang dibuat menggunakan kode ini:

<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <PhotoOverlay id="photo0">
      <name>
        1228258523134.jpg
      </name>
      <description>
<![CDATA[<a href="#photo0">Click here to fly into photo</a>]]>      </description>
      <Camera>
      	<longitude>
          -122.3902159196034
        </longitude>
        <latitude>
           37.78961266330473
        </latitude>
        <altitude>
          10
        </altitude>
        <tilt>
          90
        </tilt>
      </Camera>
      <Icon>
        <href>
          1228258523134.jpg
        </href>
      </Icon>
      <ViewVolume>
        <leftFov>
          -26.6666666667
        </leftFov>
        <rightFov>
          26.6666666667
        </rightFov>
        <bottomFov>
          -20
        </bottomFov>
        <topFov>
          20
        </topFov>
        <near>
          10
        </near>
      </ViewVolume>
      <Point>
        <coordinates>
          -122.3902159196034,37.78961266330473,0
        </coordinates>
      </Point>
    </PhotoOverlay>
  </Document>
</kml>

Berikut tampilannya di Google Earth:


Perhatian

Pemberian geotag pada foto masih dalam tahap awal.

Berikut beberapa hal yang perlu diperhatikan:

  • Perangkat GPS tidak selalu 100% akurat, terutama yang ada di kamera, jadi Anda harus memeriksa posisi foto Anda.
  • Banyak perangkat tidak melacak ketinggian, tetapi menyetelnya ke 0. Jika ketinggian penting bagi Anda, Anda harus menemukan cara lain untuk merekam data tersebut.
  • Posisi GPS adalah posisi kamera, bukan posisi subjek foto. Itulah sebabnya contoh ini memosisikan elemen Kamera di posisi GPS, dan foto sebenarnya jauh dari posisi tersebut.
  • Exif tidak merekam informasi tentang arah kamera Anda, jadi Anda mungkin perlu menyesuaikan PhotoOverlays karena hal tersebut. Kabar baiknya adalah beberapa perangkat, seperti ponsel yang dibuat di sistem operasi Android, memungkinkan Anda merekam data seperti arah kompas dan kemiringan secara langsung, tetapi tidak di header Exif.

Meski demikian, ini tetap merupakan cara yang efektif untuk memvisualisasikan foto Anda. Semoga, kita akan melihat semakin banyak foto yang diberi geotag secara akurat dalam waktu dekat.

Langkah Selanjutnya

Setelah mulai menggunakan header EXIF, Anda dapat menjelajahi spesifikasi EXIF. Ada banyak data lain yang disimpan di sana, dan Anda mungkin tertarik untuk mengambilnya, lalu menempatkannya dalam balon deskripsi. Anda juga dapat mempertimbangkan untuk membuat PhotoOverlays yang lebih kaya menggunakan ImagePyramids. Artikel Panduan Developer tentang PhotoOverlays memberikan ringkasan yang baik tentang cara menggunakannya.