Automatización: Descargar KMLs autocontenidos, o de cómo leer archivos XML con AppleScript

Si utilizáis Google Earth, o Google Maps, para crear vuestros propios mapas, es posible que os encontréis con que los archivos KML guardados desde esas aplicaciones no pueden ser utilizados por algunas aplicaciones. Por ejemplo, es imposible utilizar los archivos resultantes en aplicaciones como Galileo Offline Maps, o en un conversor online entre formatos KML y GPX, como por ejemplo http://gpx2kml.com.

¿Qué es lo que ocurre? ¿Cómo podemos solucionarlo? Y, ¿qué podemos aprender con ello?

Si abrimos el contenido del archivo KML con un editor de texto, veremos que el contenido no es muy grande (típicamente, son archivos de menos de 1KB), y que lo que en realidad contienen es un enlace a un archivo KML en el que sí que se describe el mapa de nuestro interés. El siguiente ejemplo viene de un mapa de cafeterías y restaurantes cercanos al IAA-CSIC que preparamos para un congreso, y contiene todo el archivo XML:



  Café/bar/tapas zona IAA
  
  
    
      Café/bar/tapas zona IAA
      http://maps.google.es/maps/ms?ie=UTF8&hl=es&msa=0&msid=110626053481957456401.00000112e19e9b30ff17a&output=kml
    
  


Así que, si queremos descargar los datos reales, que podamos utilizar en otros programas, deberíamos obtener el enlace dentro de la etiqueta Url, y descargarlo.

Una solución rápida de implementar es usar AppleScript: dado que AppleScript tiene soporte nativo (aunque un poco rudimentario) para interpretar archivos XML, sin depender de que instalemos nada en nuestro equipo, podemos crear el siguiente script, que realiza las siguientes funciones:

  • Recibe una lista de uno o mas archivos que se hayan podido lanzar sobre la aplicación
  • Para cada archivo recibido, si el archivo es un archivo KML, y contiene etiquetas Url, o Link, en cuyo interior a una etiqueta href, pregunta dónde guardar la versión completa del archivo, y procede. Descarta los archivos que no sean XML, que no sean KML, o que sean KML pero no contengan enlaces a un KML remoto.

Éste es el script correspondiente:

<!– –>


Al usar el método on open, podemos crear una aplicación, abriendo el archivo anterior y exportándolo como aplicación, dejando también marcada la casilla de Sólo ejecutar.

ExportAppleScriptAsApp

AppleScript a tu servicio, a través de Automator

Por supuesto, también podemos utilizar Automator, creando un nuevo servicio —accesible a través del menú contextual Servicios—, que podemos configurar para que sólo se pueda utilizar sobre archivos en Finder.app, como indica la imagen:

Workflow descarga de KMLs

En ese caso, hay que cambiar ligeramente el script, que queda de la siguiente forma:


La principal diferencia es que en una aplicación AppleScript, el método que se llama es on open, mientras que en Automator se llama a la función on run. Por lo demás, son idénticos.

Más sobre AppleScript y XML

Una de las partes más interesantes del script es la forma en la que se pueden obtener los valores de un XML Element. Se usan bloques tell jerárquicamente para navegar la estructura:

set kmlDataContent to the contents of XML file kmlFileString

tell kmlDataContent
  tell XML element "kml" -- the klm level
    tell XML element "Document"
      tell XML element "NetworkLink"
        try -- we try first with "Link"
          tell XML element "Link"
            set theUrl to (value of XML element "href")
          end tell
        on error -- if there was no "Link"...
          -- On ocassions, it can have a Url insted of a Link
	  try -- ...with "Url"
            tell XML element "Url"
              set theUrl to (value of XML element "href")
            end tell
          end try
        end try
      end tell
    end tell
  end tell
end tell

Es posible hacer el código un poco más compacto haciendo explícita la jerarquía de otra forma:

tell kmlDataContent to set theUrl to the value of XML element "href"  ¬
  of XML element "Link" of XML element "NetworkLink" of ¬
    XML element "Document" of XML element "kml"

Hay posibilidad, también, de obtener listas de elementos, utilizando propiedades como whose. Por ejemplo, si consideramos un archivo XML:



  
    Text for first title
    Fun for all the family
  
  
    Text for our second title
    Fun for XML nerds
  

Podríamos leer el título del documento con identificador 2 de la siguiente forma:

set xmlFile to (POSIX file "~/Downloads/xml_example.Documents.xml") as string

tell application "System Events"
  set xmlData to the contents of XML file xmlFile

  tell xmlData
    tell XML element "Documents"
      set documentTags to the XML elements whose id is "2"
      -- incluso con sólo un elemento, documentTags es ahora una lista;
      -- por eso necesitamos usar *first item of*;
      get the value of XML element "Title" of the first item of documentTags
    end tell
  end tell
end tell

Lamentablemente, AppleScript sólo puede aplicar condiciones a los elementos visibles en el nivel actual. Por ejemplo, el siguiente script devuelve una lista vacía:

set xmlFile to (POSIX file "~/Downloads/xml_example.Documents.xml") as string

tell application "System Events"
  set xmlData to the contents of XML file xmlFile

  tell xmlData
    set documentTags to the XML elements whose id is "2"
    -- incluso con sólo un elemento, documentTags es ahora una lista;
    -- por eso necesitamos usar *first item of*;
    get the value of XML element "Title" of the first item of documentTags
  end tell
end tell

La razón es que justo por debajo del nivel 0, no hay tags con id igual a 2 (sólo está el tag “Documents”, sin atributos).

Un último ejemplo: obtener el contenido de títulos y subtítulos:

set xmlFile to (POSIX file "~/Downloads/xml_example.Documents.xml") as string

set titleList to {}

tell application "System Events"
  set xmlData to the contents of XML file xmlFile

  tell xmlData
    tell XML element "Documents"
      set documentList to every XML element whose name = "Document"
      repeat with doc in documentList
        tell doc
          set titleValue to (the value of every XML element ¬
            whose (name = "Title" or name = "Subtitle"))
          set titleList to titleList & titleValue
        end tell
      end repeat
    end tell
  end tell
end tell

Para saber más sobre el soporte directo de XML en AppleScript, podéis consultar la XML Suite en el diccionario de System Events.app, leer la sección AppleScript the la web RossetaCode, o este artículo de MacScripter sobre AppleScript y XML. Y por supuesto, Google y StackOverflow son también tus amigos.

Descargas

Descubre más desde Memoria de Acceso Aleatorio

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo