Convertir archivos CSV a KML

Mano Marks, equipo de API de Google Geo
Marzo de 2008

Objetivo

En este instructivo, se describen los conceptos básicos para crear datos KML a partir de datos de valores separados por comas (CSV) mediante Python. Los datos CSV son uno de los formatos de archivo más comunes que se usan en la actualidad. La mayoría de las hojas de cálculo y bases de datos pueden leer y escribir archivos CSV. Su formato simple se puede editar en un editor de texto. Muchos lenguajes de programación, como Python, tienen bibliotecas especiales para leer y escribir archivos CSV. Por lo tanto, es un medio excelente para intercambiar grandes cantidades de datos.

Si bien las muestras de código de este instructivo están en Python, se pueden adaptar para la mayoría de los demás lenguajes de programación. En este instructivo, se usa código de Direcciones de geocodificación para usar en KML a fin de convertir una dirección en coordenadas de longitud y latitud. También usa el nuevo elemento <ExtendedData> de KML 2.2 y aprovecha la plantilla de globos que se describe en Cómo agregar datos personalizados. Por lo tanto, el formato KML que se produce actualmente no es compatible con Google Maps ni otras aplicaciones que utilicen KML, pero el código se puede adaptar para producir un archivo KML compatible con Maps.

Datos de muestra

Para este instructivo, usa el archivo google-addresses.csv como un archivo CSV de muestra. Este archivo tiene todas las direcciones, números de teléfono y números de fax de las distintas oficinas de Google de EE.UU. Este es el texto del archivo:

Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax
Headquarters,1600 Amphitheatre Parkway,,,Mountain View,CA,94043,650-253-0000,650-253-0001
New York Sales & Engineering Office,76 Ninth Avenue,,,New York,NY,10011,212-565-0000,212-565-0001
Ann Arbor Sales Office,201 South Division Street,,,Ann Arbor,MI,48104,734-332-6500,734-332-6501
Atlanta Sales & Engineering Office,10 10th Street NE,,,Atlanta,GA,30309,404-487-9000,404-487-9001
Boulder Sales & Engineering Office,2590 Pearl St.,,,Boulder,CO,80302,303-245-0086,303-535-5592
Cambridge Sales & Engineering Office,5 Cambridge Center,,,Cambridge,MA,02142,617-682-3635,617-249-0199
Chicago Sales & Engineering Office,20 West Kinzie St.,,,Chicago,IL,60610,312-840-4100,312-840-4101
Coppell Sales Office,701 Canyon Drive,,,Coppell,TX,75019,214-451-4000,214-451-4001
Detroit Sales Office,114 Willits Street,,,Birmingham,MI,48009,248-351-6220,248-351-6227
Irvine Sales & Engineering Office,19540 Jamboree Road,,,Irvine,CA,92612,949-794-1600,949-794-1601
Pittsburgh Engineering Office,4720 Forbes Avenue,,,Pittsburgh,PA,15213,,
Santa Monica Sales & Engineering Office,604 Arizona Avenue,,,Santa Monica,CA,90401,310-460-4000,310-309-6840
Seattle Engineering Office,720 4th Avenue,,,Kirkland,WA,98033,425-739-5600,425-739-5601
Seattle Sales Office,501 N. 34th Street,,,Seattle,WA,98103,206-876-1500,206-876-1501
Washington D.C. Public Policy Office,1001 Pennsylvania Avenue NW,,,Washington,DC,20004,202-742-6520,

Observa que cada línea es una serie de strings de texto separadas por comas. Cada coma delimita un campo y cada línea tiene la misma cantidad de comas. La primera línea contiene los nombres de los campos en orden. Por ejemplo, el primer bloque de texto en cada fila es el campo “Office”, el segundo “Address1”, etc. Python puede convertir eso en una colección de dicts llamada DictReader, que te permite revisar cada fila. Esta muestra de código se basa en que sabes de antemano la estructura de tus datos, pero puedes agregar algunos controladores básicos para pasar la estructura de campo de forma dinámica.

Cómo analizar el archivo CSV

El módulo xml.dom.minidom de Python proporciona herramientas excelentes para crear documentos XML. Además, como KML es XML, lo usarás bastante en este instructivo. Debes crear un elemento con createElement o createElementNS y adjuntarlo a otro elemento con appendChild. Estos son los pasos para analizar el archivo CSV y crear un archivo KML.

  1. Importa geocoding_for_kml.py a tu módulo.
  2. Crea un DictReader para los archivos CSV. El DictReader es una colección de dicts, una para cada fila.
  3. Crea el documento mediante xml.dom.minidom.Document() de Python.
  4. Crea el elemento raíz <kml> con createElementNS.
  5. Agrégalo al documento.
  6. Crea un elemento <Document> con createElement.
  7. Agrégalo al elemento <kml> con appendChild.
  8. Para cada fila, crea un elemento <Placemark> y adjúntalo al elemento <Document>.
  9. Para cada columna en cada fila, crea un elemento <ExtendedData> y adjúntalo al elemento <Placemark> que creaste en el paso 8.
  10. Crea un elemento <Data> y adjúntalo al elemento <ExtendedData>. Asigna al elemento <Data> un atributo de nombre y asígnale el valor del nombre de la columna con setAttribute.
  11. Crea un elemento <value> y adjúntalo al elemento <Data>. Crea un nodo de texto y asígnale el valor de la columna con createTextNode. Agrega el nodo de texto al elemento <value>.
  12. Crea un elemento <Point> y adjúntalo al elemento <Placemark>. Crea un elemento <coordinates> y adjúntalo al elemento <Point>.
  13. Extrae la dirección de la fila para que sea una sola string con este formato: Address1,Address2,City,State,Zip. La primera fila sería 1600 Amphitheater Parkway,,Mountain View,CA,94043. No hay problema si hay comas una al lado de la otra. Ten en cuenta que, para hacer esto, se requiere conocimiento previo de la estructura del archivo CSV y qué columnas constituyen la dirección.
  14. Geocodificar la dirección con el código geocoding_for_kml.py, que se explica en Codificación geográfica de direcciones para uso en KML Esto muestra una string que es la longitud y latitud de la ubicación.
  15. Crea un nodo de texto, asígnale el valor de las coordenadas en el paso 14 y, luego, agrégalo al elemento <coordinates>.
  16. Escribe el documento KML en un archivo.
  17. Si pasas una lista de nombres de columna como argumentos a la secuencia de comandos, la secuencia de comandos agregará elementos en ese orden. Si no nos importara el orden de los elementos, podríamos usar dict.keys() para producir una list. Sin embargo, dict.keys() no conserva el orden original del documento. Para usar este argumento, pasa la lista de nombres de campos como una lista separada por comas, de la siguiente manera:
    python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax

Ejemplo de código de Python

A continuación, se muestra un ejemplo de código para crear un archivo KML a partir de un archivo CSV con Python 2.2. También puedes descargarlo aquí.


import geocoding_for_kml
import csv
import xml.dom.minidom
import sys


def extractAddress(row):
  # This extracts an address from a row and returns it as a string. This requires knowing
  # ahead of time what the columns are that hold the address information.
  return '%s,%s,%s,%s,%s' % (row['Address1'], row['Address2'], row['City'], row['State'], row['Zip'])

def createPlacemark(kmlDoc, row, order):
  # This creates a  element for a row of data.
  # A row is a dict.
  placemarkElement = kmlDoc.createElement('Placemark')
  extElement = kmlDoc.createElement('ExtendedData')
  placemarkElement.appendChild(extElement)
  
  # Loop through the columns and create a  element for every field that has a value.
  for key in order:
    if row[key]:
      dataElement = kmlDoc.createElement('Data')
      dataElement.setAttribute('name', key)
      valueElement = kmlDoc.createElement('value')
      dataElement.appendChild(valueElement)
      valueText = kmlDoc.createTextNode(row[key])
      valueElement.appendChild(valueText)
      extElement.appendChild(dataElement)
  
  pointElement = kmlDoc.createElement('Point')
  placemarkElement.appendChild(pointElement)
  coordinates = geocoding_for_kml.geocode(extractAddress(row))
  coorElement = kmlDoc.createElement('coordinates')
  coorElement.appendChild(kmlDoc.createTextNode(coordinates))
  pointElement.appendChild(coorElement)
  return placemarkElement

def createKML(csvReader, fileName, order):
  # This constructs the KML document from the CSV file.
  kmlDoc = xml.dom.minidom.Document()
  
  kmlElement = kmlDoc.createElementNS('http://earth.google.com/kml/2.2', 'kml')
  kmlElement.setAttribute('xmlns','http://earth.google.com/kml/2.2')
  kmlElement = kmlDoc.appendChild(kmlElement)
  documentElement = kmlDoc.createElement('Document')
  documentElement = kmlElement.appendChild(documentElement)

  # Skip the header line.
  csvReader.next()
  
  for row in csvReader:
    placemarkElement = createPlacemark(kmlDoc, row, order)
    documentElement.appendChild(placemarkElement)
  kmlFile = open(fileName, 'w')
  kmlFile.write(kmlDoc.toprettyxml('  ', newl = '\n', encoding = 'utf-8'))

def main():
  # This reader opens up 'google-addresses.csv', which should be replaced with your own.
  # It creates a KML file called 'google.kml'.
  
  # If an argument was passed to the script, it splits the argument on a comma
  # and uses the resulting list to specify an order for when columns get added.
  # Otherwise, it defaults to the order used in the sample.
  
  if len(sys.argv) >1: order = sys.argv[1].split(',')
  else: order = ['Office','Address1','Address2','Address3','City','State','Zip','Phone','Fax']
  csvreader = csv.DictReader(open('google-addresses.csv'),order)
  kml = createKML(csvreader, 'google-addresses.kml', order)
if __name__ == '__main__':
  main()

Ejemplo de creación de KML

A continuación, se muestra un ejemplo del archivo KML que crea esta secuencia de comandos. Observa que algunos elementos<value> solo tienen espacios en blanco. Eso se debe a que el campo no tenía datos. También puedes descargar la muestra completa aquí.

<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
  <Document>
    <Placemark>
      <ExtendedData>
        <Data name="Office">
          <value>
            Headquarters
          </value>
        </Data>
        <Data name="Address1">
          <value>
            1600 Amphitheater Parkway
          </value>
        </Data>
        <Data name="City">
          <value>
            Mountain View
          </value>
        </Data>
        <Data name="State">
          <value>
            CA
          </value>
        </Data>
        <Data name="Zip">
          <value>
            94043
          </value>
        </Data>
        <Data name="Phone">
          <value>
            650-253-0000
          </value>
        </Data>
        <Data name="Fax">
          <value>
            650-253-0001
          </value>
        </Data>
      </ExtendedData>
      <Point>
        <coordinates>
          -122.081783,37.423111
        </coordinates>
      </Point>
    </Placemark>
    ...

Captura de pantalla

A continuación, se muestra una captura de pantalla de ese archivo KML en Google Earth. Debido a que cada elemento <Placemark> no tiene <BalloonStyle><text> ni <description>, el globo usa un estilo de tabla de forma predeterminada, y usa los elementos <Data>.

Captura de pantalla de KML creada por esta secuencia de comandos

Consideración de la geocodificación

Esto se mencionó en "Codificación geográfica de direcciones para uso en KML", pero indica la repetición. Tus solicitudes de geocodificación están sujetas a la tasa de consultas máxima del geocodificador y a 15,000 consultas por día según tu IP. Además, el geocodificador mostrará un código de estado 620 si lo consultas más rápido de lo que puede controlarlo. (puedes encontrar una lista completa de los códigos de estado aquí). Para asegurarte de no enviar consultas demasiado rápido al geocodificador, puedes especificar una demora entre cada solicitud de codificación geográfica. Puedes aumentar este retraso cada vez que recibas un estado 620 y usar un bucle while para asegurarte de geocodificar con éxito una dirección antes de iterar en la siguiente. Esto significa que si tu archivo CSV es muy grande, es posible que tengas que modificar el código de codificación geográfica o realizar un seguimiento de la velocidad con la que creas marcas de posición y disminuirla si vas demasiado rápido.

Conclusión

Ahora puedes usar Python para crear un archivo KML a partir de un archivo CSV. Al utilizar el código proporcionado, el archivo KML solo funcionará en Google Earth. Puedes modificarlo para que funcione en Maps y Earth mediante <description> en lugar de <ExtendedData>. También es fácil convertir esta muestra de código en cualquier otro lenguaje de programación que admita XML.

Ahora que terminaste de convertir todos tus archivos CSV en KML, te recomendamos que consultes otros artículos KML, como Cómo usar PHP y MySQL para crear KML y el artículo de la Guía para desarrolladores de Google sobre ExtendedData, Cómo agregar datos personalizados.

Volver al principio