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, oLink, en cuyo interior a una etiquetahref, 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.
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:
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
- Aplicación AppleScript (exportada según las instrucciones anteriores, y comprimida en formato ZIP; almacenado en Cloud.ly): Parse and download functional KML.app.zip
- Workflow de Automator (comprimido en formato ZIP, almacenado en Cloud.ly): Download proper KML file.workflow.zip
- Gist en GitHub con el script: Download referenced KML.scpt; incluye una versión ligeramente distinta para usar con Automator.
- Archivo KML de ejemplo: CafeBarTapasZonaIAA.kml

