Convertir des fichiers CSV en KML

Mano Marks, équipe des API Google Geo
Mars 2008

Objectif

Ce tutoriel explique les bases de la création de fichiers KML à partir de données CSV (Comma Separated Value) à l'aide de Python. Les données CSV sont l'un des formats de fichier les plus répandus aujourd'hui. La plupart des feuilles de calcul et des bases de données peuvent lire et écrire des fichiers CSV. Son format simple peut être modifié dans un éditeur de texte. De nombreux langages de programmation, comme Python, disposent de bibliothèques spéciales pour lire et écrire des fichiers CSV. Il constitue donc un excellent moyen d'échanger de grandes quantités de données.

Bien que les exemples de code de ce tutoriel soient en Python, ils peuvent être adaptés à la plupart des autres langages de programmation. Ce tutoriel utilise le code de Géocoder des adresses pour les utiliser dans KML afin de convertir une adresse en coordonnées de longitude/latitude. Il utilise également le nouvel élément <ExtendedData> de KML 2.2 et tire parti des modèles de ballon décrits dans Ajouter des données personnalisées. Par conséquent, le fichier KML produit n'est actuellement pas compatible avec Google Maps ni avec d'autres applications utilisant le format KML. Toutefois, le code peut être adapté pour produire un fichier KML compatible avec Maps.

Exemples de données

Pour ce tutoriel, utilisez le fichier google-addresses.csv comme exemple de fichier CSV. Ce fichier contient toutes les adresses, numéros de téléphone et numéros de fax des différents bureaux Google aux États-Unis. Voici le texte du fichier :

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,

Remarquez que chaque ligne est une série de chaînes de texte séparées par des virgules. Chaque virgule délimite un champ. Chaque ligne comporte le même nombre de virgules. La première ligne contient les noms des champs dans l'ordre. Par exemple, le premier bloc de texte de chaque ligne correspond au champ "Office", le deuxième à "Address1", etc. Python peut transformer cela en une collection de dicts appelée DictReader, qui vous permet de parcourir chaque ligne. Cet exemple de code repose sur votre connaissance préalable de la structure de vos données, mais vous pouvez ajouter des gestionnaires de base pour transmettre la structure des champs de manière dynamique.

Analyser le fichier CSV

Le module xml.dom.minidom de Python fournit d'excellents outils pour créer des documents XML. Comme KML est un langage XML, vous l'utiliserez beaucoup dans ce tutoriel. Vous créez un élément avec createElement ou createElementNS, et l'ajoutez à un autre élément avec appendChild. Voici les étapes à suivre pour analyser le fichier CSV et créer un fichier KML.

  1. Importez geocoding_for_kml.py dans votre module.
  2. Créez un DictReader pour les fichiers CSV. DictReader est une collection de dicts, une pour chaque ligne.
  3. Créez le document à l'aide de xml.dom.minidom.Document() de Python.
  4. Créez l'élément <kml> racine à l'aide de createElementNS..
  5. Ajoutez-le au document.
  6. Créez un élément <Document> à l'aide de createElement.
  7. Ajoutez-le à l'élément <kml> à l'aide de appendChild.
  8. Pour chaque ligne, créez un élément <Placemark> et ajoutez-le à l'élément <Document>.
  9. Pour chaque colonne de chaque ligne, créez un élément <ExtendedData> et ajoutez-le à l'élément <Placemark> que vous avez créé à l'étape 8.
  10. Créez un élément <Data> et ajoutez-le à l'élément <ExtendedData>. Donnez à l'élément <Data> un attribut de nom et attribuez-lui la valeur du nom de la colonne à l'aide de setAttribute.
  11. Créez un élément <value> et ajoutez-le à l'élément <Data>. Créez un nœud de texte et attribuez-lui la valeur de la colonne à l'aide de createTextNode. Ajoutez le nœud de texte à l'élément <value>.
  12. Créez un élément <Point> et ajoutez-le à l'élément <Placemark>. Créez un élément <coordinates> et ajoutez-le à l'élément <Point>.
  13. Extrayez l'adresse de la ligne pour qu'elle soit une seule chaîne dans ce format : Address1,Address2,City,State,Zip. La première ligne serait donc 1600 Amphitheater Parkway,,Mountain View,CA,94043. Ce n'est pas grave si des virgules sont côte à côte. Notez que pour ce faire, vous devez connaître la structure du fichier CSV et les colonnes qui constituent l'adresse.
  14. Géocodez l'adresse à l'aide du code geocoding_for_kml.py expliqué dans Géocoder des adresses à utiliser dans KML. Cela renvoie une chaîne correspondant à la longitude et à la latitude de l'emplacement.
  15. Créez un nœud de texte et attribuez-lui la valeur des coordonnées de l'étape 14, puis ajoutez-le à l'élément <coordinates>.
  16. Écrivez le document KML dans un fichier.
  17. Si vous transmettez une liste de noms de colonnes en tant qu'arguments au script, celui-ci ajoutera les éléments dans cet ordre. Si l'ordre des éléments ne nous intéressait pas, nous pourrions utiliser dict.keys() pour produire un list. Toutefois, dict.keys() ne conserve pas l'ordre d'origine du document. Pour utiliser cet argument, transmettez la liste des noms de champs sous forme de liste séparée par des virgules, comme suit :
    python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax

Exemple de code Python

Vous trouverez ci-dessous un exemple de code permettant de créer un fichier KML à partir d'un fichier CSV à l'aide de Python 2.2. Vous pouvez également le télécharger ici.


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

Exemple de fichier KML créé

Vous trouverez ci-dessous un exemple du fichier KML créé par ce script. Notez que certains éléments<value>ne contiennent que des espaces. En effet, le champ ne contenait aucune donnée. Vous pouvez également télécharger l'exemple complet sur cette page.

<?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>
    ...

Capture d'écran

Vous trouverez ci-dessous une capture d'écran montrant à quoi ressemble ce fichier KML dans Google Earth. Comme chaque élément<Placemark>ne comporte aucun élément<BalloonStyle><text>ni<description>, la bulle adopte par défaut un style de tableau, en s'appuyant sur les éléments<Data>.

Capture d&#39;écran du fichier KML créé par ce script

Éléments à prendre en compte pour le geocoding

Cela a été mentionné dans "Géocoder des adresses à utiliser dans KML", mais il est important de le répéter. Vos demandes de géocodage seront soumises au taux de requêtes maximal du géocodeur et à 15 000 requêtes par jour en fonction de votre adresse IP. De plus, le geocoder renverra un code d'état 620 si vous l'interrogez plus rapidement qu'il ne peut le gérer. (Une liste complète des codes d'état est disponible ici.) Pour vous assurer de ne pas envoyer de requêtes trop rapidement au géocodeur, vous pouvez spécifier un délai entre chaque requête de géocodage. Vous pouvez augmenter ce délai chaque fois que vous recevez un état 620 et utiliser une boucle while pour vous assurer d'avoir correctement géocodé une adresse avant de passer à la suivante. Cela signifie que si votre fichier CSV est très volumineux, vous devrez peut-être modifier le code de géocodage ou suivre la vitesse à laquelle vous créez des repères et la ralentir si vous allez trop vite.

Conclusion

Vous pouvez désormais utiliser Python pour créer un fichier KML à partir d'un fichier CSV. Le fichier KML ne fonctionnera que dans Google Earth avec le code fourni. Vous pouvez le modifier pour qu'il fonctionne à la fois dans Maps et Earth en utilisant <description> au lieu de <ExtendedData>. Il est également facile de convertir cet exemple de code dans n'importe quel autre langage de programmation compatible avec XML.

Maintenant que vous avez terminé de convertir tous vos fichiers CSV au format KML, vous pouvez consulter d'autres articles sur le format KML, comme Utiliser PHP et MySQL pour créer des fichiers KML et l'article du guide du développeur Google sur ExtendedData, Ajouter des données personnalisées.

Haut de page