Conversione di foto georeferenziate in PhotoOverlay KML

Mano Marks, team delle API Google Geo
Gennaio 2009

Obiettivo

Questo tutorial ti insegna a utilizzare le foto georeferenziate per creare file KML PhotoOverlays. Sebbene il codice di esempio sia scritto in Python, esistono molte librerie simili in altri linguaggi di programmazione, quindi non dovrebbe essere un problema tradurlo in un'altra lingua. Il codice di 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 scattare foto e girare video. Inoltre, taggano questi video e queste foto con metadati sulla videocamera e sulle relative impostazioni. Negli ultimi anni, le persone hanno trovato il modo di aggiungere dati geografici a queste informazioni, incorporati dai produttori di fotocamere, come alcune fotocamere Ricoh e Nikon, o tramite dispositivi come i registratori GPS e EyeFi Explore. I fotocamere smartphone come l'iPhone e gli smartphone con sistema operativo Android, come il G1 di T-Mobile, incorporano automaticamente questi dati. Alcuni siti di caricamento di foto, come Panoramio, Picasa Web Albums e Flickr, analizzano automaticamente i dati GPS e li utilizzano per georeferenziare una foto. Potrai quindi recuperare questi dati nei feed. Ma dov'è il divertimento? Questo articolo spiega come accedere autonomamente a questi dati.

Intestazioni EXIF

Il modo più comune per incorporare i dati in un file immagine è utilizzare il formato EXIF (Exchangeable Image File Format). I dati vengono archiviati in formato binario nelle intestazioni EXIF in modo standard. Se conosci la specifica per le intestazioni EXIF, puoi analizzarle autonomamente. Fortunatamente, qualcuno ha già fatto il lavoro più difficile e ha scritto un modulo Python per te. La libreria open source EXIF.py è un ottimo strumento per leggere le intestazioni di un file JPEG.

The Code

Il codice di esempio per questo articolo si trova in questo file: exif2kml.py. Se vuoi passare direttamente all'utilizzo, scarica il modulo e EXIF.py e inseriscili nella stessa directory. Esegui python exif2kml.py foo.jpg sostituendo foo.jpg con il percorso di una foto georeferenziata. Verrà generato un file denominato test.kml.

Analisi delle intestazioni Exif

EXIF.py fornisce un'interfaccia semplice per estrarre le intestazioni Exif. Basta eseguire la funzione process_file() per restituire 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

Una volta ottenute le intestazioni Exif, devi estrarre le coordinate GPS. EXIF.py li considera oggetti Ratio, oggetti per memorizzare il numeratore e il denominatore dei valori. In questo modo viene impostato un rapporto preciso anziché basarsi su un numero in virgola mobile. Tuttavia, KML prevede numeri decimali, non rapporti. Estrai quindi ciascuna delle coordinate e converti il numeratore e il denominatore in un unico numero in 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 un semplice 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)

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

Questo esempio non sfrutta tutta la potenza di PhotoOverlays, che ti consente di creare esplorazioni approfondite di foto ad alta risoluzione. Tuttavia, mostra come appendere una foto in stile cartellone pubblicitario su Google Earth. Ecco un esempio di file KML creato utilizzando 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 appare in Google Earth:


Avvertenze

Il geotagging delle foto è ancora agli inizi.

Ecco alcuni aspetti da tenere presente:

  • I dispositivi GPS non sono sempre precisi al 100%, in particolare quelli integrati nelle fotocamere, quindi devi controllare le posizioni delle tue foto.
  • Molti dispositivi non monitorano l'altitudine, impostandola invece su 0. Se l'altitudine è importante per te, devi trovare un altro modo per acquisire questi dati.
  • La posizione GPS è quella della videocamera, non del soggetto della foto. Per questo motivo, questo esempio posiziona l'elemento Fotocamera nella posizione GPS e la foto effettiva lontano da questa posizione.
  • Exif non acquisisce informazioni sulla direzione in cui è puntata la videocamera, quindi potresti dover regolare PhotoOverlays per questo motivo. La buona notizia è che alcuni dispositivi, come gli smartphone basati sul sistema operativo Android, consentono di acquisire direttamente dati come la direzione della bussola e l'inclinazione, ma non nelle intestazioni Exif.

Detto questo, è comunque un modo efficace per visualizzare le tue foto. Ci auguriamo di vedere un numero sempre maggiore di foto geotaggate con precisione nel prossimo futuro.

Passaggi successivi

Ora che hai iniziato a utilizzare le intestazioni EXIF, puoi esplorare la specifica EXIF. Sono memorizzati molti altri dati che potrebbero interessarti e che potresti inserire in un fumetto descrittivo. Potresti anche prendere in considerazione la creazione di PhotoOverlays più ricchi utilizzando ImagePyramids. L'articolo della Guida per gli sviluppatori su PhotoOverlays offre una buona panoramica del loro utilizzo.