Conversione di foto con tag geografici in overlay di foto KML

Mano Marks, team delle API di Google Geo
gennaio 2009

Obiettivo

Questo tutorial ti insegna come utilizzare le foto con etichetta geografica per creare il file KML PhotoOverlays. Anche se il codice campione è scritto in Python, molte librerie simili esistono in altri linguaggi di programmazione, quindi non dovrebbe essere un problema per tradurre questo codice in un altro linguaggio. Il codice in questo articolo si basa su una libreria Python open source, EXIF.py.

Introduzione

Le fotocamere digitali sono dispositivi straordinari. Molti utenti non se ne rendono conto, ma fanno molto di più che limitarsi a scattare foto e registrare video. Inoltre, taggano i video e le foto con metadati relativi alla fotocamera e alle sue impostazioni. Negli ultimi anni, le persone hanno trovato dei modi per aggiungere dati geografici a queste informazioni, incorporate dai produttori di fotocamere, come alcune fotocamere Ricoh e Nikon, oppure tramite dispositivi come i logger GPS e EyeFi Explore. I telefoni con fotocamera come l'iPhone e i telefoni con sistema operativo Android, ad esempio G1 di T-Mobile, incorporano automaticamente questi dati. Alcuni siti per il caricamento di foto, come Panorama, Picasa Web Album e Flickr, analizzano automaticamente i dati GPS e li usano per geolocalizzare una foto. Puoi quindi recuperarli nei feed. Ma c'è qualcosa di divertente? In questo articolo viene spiegato come ottenere questi dati.

Intestazioni Exif

Il modo più comune per incorporare i dati in un file immagine è utilizzare il formato file immagine scambiabile o EXIF. I dati vengono archiviati in forma binaria nelle intestazioni EXIF in modo standard. Se conosci la specifica delle intestazioni EXIF, puoi analizzarle autonomamente. Fortunatamente, qualcuno ha già lavorato e realizzato un modulo Python per te. La libreria open source EXIF.py è un ottimo strumento per leggere le intestazioni di un file JPEG.

Il codice

Il codice campione per questo articolo è disponibile in questo file: exif2kb.py. Se vuoi passare direttamente all'utilizzo, scarica il modulo, oltre a EXIF.py, quindi inseriscili nella stessa directory. Esegui python exif2kml.py foo.jpg sostituendo foo.jpg con il percorso di una foto con tag geografico. Verrà generato un file denominato test.kml.

Analisi delle intestazioni Exif

EXIF.py offre un'interfaccia semplice per l'estrazione delle intestazioni Exif. Esegui la funzione process_file() e restituirà le intestazioni come oggetto 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

Dopo aver ottenuto le intestazioni Exif, devi estrarre le coordinate GPS. EXIF.py considera questi oggetti come oggetti Ratio per l'archiviazione del numeratore e del denominatore dei valori. che determina un rapporto preciso anziché affidarsi a un numero in virgola mobile. Tuttavia, KML prevede numeri decimali, non proporzioni. Per estrarre ciascuna delle coordinate e convertire il numeratore e il denominatore in un singolo numero con virgola mobile per i gradi decimali:

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

Una volta ottenute le coordinate, è facile creare una PhotoOverlay per ogni 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)

Puoi vedere che utilizziamo solo metodi W3C DOM standard, perché sono quelli disponibili nella maggior parte dei linguaggi di programmazione. Per scoprire come funziona tutto insieme, scarica il codice da qui.

Questo campione non sfrutta tutta la potenza di PhotoOverlays, che ti consente di creare esplorazioni approfondite delle foto ad alta risoluzione. Tuttavia, dimostra come appendere una foto in stile cartellone su Google Earth. Ecco un esempio di file KML creato con questo codice:

<?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>

Ecco come si presenta in Google Earth:


Attenzione

Il tagging geografico delle foto è ancora agli inizi.

Ecco alcuni aspetti da tenere presenti:

  • I dispositivi GPS non sono sempre accurati al 100%, in particolare quelli forniti con la fotocamera, pertanto devi controllare la posizione delle foto.
  • Molti dispositivi non tracciano l'altitudine, ma la impostano su 0. Se l'altitudine è importante per te, dovresti trovare un altro modo per acquisire questi dati.
  • La posizione GPS è la posizione della fotocamera, non del soggetto della foto. Questo è il motivo per cui questo campione posiziona l'elemento Fotocamera nella posizione GPS e la foto effettiva lontano da quella posizione.
  • Exif non acquisisce informazioni sulla direzione della tua fotocamera, pertanto potrebbe essere necessario regolare PhotoOverlays. La buona notizia è che alcuni dispositivi, come i telefoni con sistema operativo Android, ti consentono di acquisire dati quali la direzione della bussola e l'inclinazione diretta, ma non nelle intestazioni Exif.

Detto questo, si tratta comunque di un modo efficace di visualizzare le tue foto. Ci auguriamo che nel prossimo futuro assisteremo a un'ulteriore geocodifica delle foto.

Come procedere

Ora che hai iniziato a utilizzare le intestazioni EXIF, puoi esplorare le specifiche EXIF. Ci sono molti altri dati archiviati al suo interno e potrebbe interessarti acquisirli, inserendoli in un fumetto descrittivo. Puoi anche creare PhotoOverlays più completi utilizzando ImagePyramids. Nella Guida per gli sviluppatori (PhotoOverlays) è disponibile una buona panoramica d'uso.