Cómo convertir archivos CSV a KML

Mano Marks, equipo de las APIs de Geo de Google
Marzo de 2008

Objetivo

En este instructivo, se describen los conceptos básicos para crear KML a partir de datos de valores separados por comas (CSV) con Python. Los datos CSV son uno de los formatos de archivo más utilizados 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 excelente medio para intercambiar grandes cantidades de datos.

Si bien las muestras de código de este instructivo están en Python, se pueden adaptar a la mayoría de los otros lenguajes de programación. En este instructivo, se usa código de Geocoding Addresses for Use in KML para convertir una dirección en coordenadas de longitud y latitud. También usa el nuevo elemento <ExtendedData> de KML 2.2 y aprovecha las plantillas de globo descritas en Cómo agregar datos personalizados. Por lo tanto, el KML producido no es compatible actualmente con Google Maps ni con otras aplicaciones que consumen KML, pero el código se puede adaptar para producir KML compatible con Maps.

Datos de muestra

Para este instructivo, usa el archivo google-addresses.csv como archivo CSV de muestra. Este archivo contiene todas las direcciones, los números de teléfono y los números de fax de las distintas oficinas de Google en 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 cómo cada línea es una serie de cadenas 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 de cada fila es el campo "Office", el segundo "Address1", etcétera. Python puede convertirlo en una colección de dicts llamada DictReader, que te permite recorrer cada fila. Este ejemplo de código se basa en que conoces de antemano la estructura de tus datos, pero podrías agregar algunos controladores básicos para pasar la estructura de los campos de forma dinámica.

Cómo analizar el archivo CSV

El módulo xml.dom.minidom de Python proporciona excelentes herramientas para crear documentos XML y, dado que KML es XML, lo usarás bastante en este instructivo. Creas un elemento con createElement o createElementNS, y lo agregas 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 en tu módulo.
  2. Crea un DictReader para los archivos CSV. El DictReader es una colección de dicts, uno para cada fila.
  3. Crea el documento con 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. Anéxalo al elemento <kml> con appendChild.
  8. Para cada fila, crea un elemento <Placemark> y agrégalo al elemento <Document>.
  9. Para cada columna de cada fila, crea un elemento <ExtendedData> y agrégalo al elemento <Placemark> que creaste en el paso 8.
  10. Crea un elemento <Data> y agrégalo 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 agrégalo al elemento <Data>. Crea un nodo de texto y asígnale el valor de la columna con createTextNode. Anexa el nodo de texto al elemento <value>.
  12. Crea un elemento <Point> y agrégalo al elemento <Placemark>. Crea un elemento <coordinates> y agrégalo al elemento <Point>.
  13. Extrae la dirección de la fila para que sea una sola cadena con este formato: Dirección1,Dirección2,Ciudad,Estado,Código postal. Por lo tanto, la primera fila sería 1600 Amphitheater Parkway,,Mountain View,CA,94043. Está bien si hay comas una al lado de la otra. Ten en cuenta que, para ello, se requiere conocimiento previo de la estructura del archivo CSV y de qué columnas constituyen la dirección.
  14. Geocodifica la dirección con el código geocoding_for_kml.py que se explica en Geocodificación de direcciones para usar en KML. Devuelve una cadena que contiene la longitud y la latitud de la ubicación.
  15. Crea un nodo de texto y asígnale el valor de las coordenadas del paso 14. Luego, agrégalo al elemento <coordinates>.
  16. Escribe el documento KML en un archivo.
  17. Si pasas una lista de nombres de columnas como argumentos a la secuencia de comandos, esta agregará elementos en ese orden. Si no nos importara el orden de los elementos, podríamos usar dict.keys() para producir un 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

Código de Python de muestra

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()

Se creó un archivo KML de muestra

A continuación, se muestra una muestra del KML que crea esta secuencia de comandos. Observa cómo algunos elementos<value>solo tienen espacios en blanco. Esto se debe a que el campo no contení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 incluye una captura de pantalla de cómo se ve ese archivo KML en Google Earth. Como cada elemento <Placemark> no tiene un elemento <BalloonStyle><text> ni un elemento <description>, el globo usa de forma predeterminada un diseño de tabla, que se basa en los elementos <Data>.

Captura de pantalla del archivo KML creado por este script

Consideraciones sobre la geocodificación

Esto se mencionó en "Cómo geocodificar direcciones para usarlas en KML", pero vale la pena repetirlo. Tus solicitudes de geocodificación estarán sujetas a la frecuencia máxima de consultas del geocodificador y a 15,000 consultas por día según tu IP. Además, el geocodificador devolverá un código de estado 620 si lo consultas más rápido de lo que puede procesar. (Aquí puedes consultar la lista completa de códigos de estado). Para asegurarte de no enviar consultas demasiado rápido al geocodificador, puedes especificar una demora entre cada solicitud de geocodificación. Puedes aumentar este retraso cada vez que recibas un estado 620 y usar un bucle while para asegurarte de haber geocodificado correctamente una dirección antes de iterar a la siguiente. Esto significa que, si tu archivo CSV es muy grande, es posible que debas modificar el código de geocodificación o hacer un seguimiento de la velocidad con la que creas marcas de posición y reducirla si vas demasiado rápido.

Conclusión

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

Ahora que terminaste de convertir todos tus archivos CSV a KML, te recomendamos que consultes otros artículos sobre 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