Konwertuję pliki CSV na format KML

Mano Marks, Google Geo APIs Team
Marzec 2008

Cel

Ten samouczek opisuje podstawy tworzenia pliku KML na podstawie danych wartości rozdzielanych przecinkami (CSV) przy użyciu języka Python. Pliki CSV to jeden z najpopularniejszych obecnie formatów plików. Większość arkuszy kalkulacyjnych i baz danych może odczytywać i zapisywać pliki CSV. Prosty format można edytować w edytorze tekstu. Wiele języków programowania, takich jak Python, ma specjalne biblioteki do odczytu i zapisu plików CSV. To świetna okazja do wymiany dużych ilości danych.

Przykładowy kod w tym samouczku jest w Pythonie, ale można go dostosować do większości języków programowania. W tym samouczku używamy kodu z adresów geograficznych do użycia w plikach KML, aby przekształcić adres we współrzędne długości i szerokości geograficznej. Wykorzystuje też nowy element <ExtendedData> języka KML 2.2 i używa szablonów dymków opisanych w artykule Dodawanie danych niestandardowych. W związku z tym wygenerowany plik KML nie jest obecnie obsługiwany w Mapach Google ani w innych aplikacjach obsługujących KML, ale można dostosować kod, aby utworzyć plik KML zgodny z Mapami.

Przykładowe dane

W tym samouczku użyj pliku google-addresses.csv jako przykładowego pliku CSV. Ten plik zawiera wszystkie adresy, numery telefonów i numery faksów różnych biur Google w Stanach Zjednoczonych. Oto tekst pliku:

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,

Zwróć uwagę, że każdy wiersz jest ciągiem ciągów tekstowych rozdzielonych przecinkami. Każdy przecinek oddziela pole, a wiersz zawiera taką samą liczbę przecinków. Pierwszy wiersz zawiera nazwy pól w kolejności. Na przykład pierwszy blok tekstu w każdym wierszu to pole „Office”, drugi „Adres1” itp. Python może przekształcić ten ciąg w zbiór dicts nazywany DictReader, który umożliwia przejście przez każdy wiersz. Ten przykładowy kod opiera się na znajomości wcześniej struktury danych, ale można też dodać kilka podstawowych modułów obsługi, które dynamicznie przekazują strukturę pola.

Analizowanie pliku CSV

Moduł Pythona xml.dom.minidom udostępnia doskonałe narzędzia do tworzenia dokumentów XML, a ponieważ KML jest w formacie XML, będziesz go używać w tym samouczku bardzo często. Tworzysz element za pomocą createElement lub createElementNS i dołączasz go do innego elementu za pomocą appendChild. Poniżej znajdziesz instrukcje analizowania pliku CSV i tworzenia pliku KML.

  1. Zaimportuj plik Geocoding_for_json.py do modułu.
  2. Utwórz DictReader w plikach CSV. DictReader to zbiór danych dicts, po jednym w każdym wierszu.
  3. Utwórz dokument za pomocą xml.dom.minidom.Document() w Pythonie.
  4. Utwórz główny element <kml> za pomocą createElementNS.
  5. Dodaj go do dokumentu.
  6. Utwórz element <Document> za pomocą createElement.
  7. Dołącz go do elementu <kml> przy użyciu appendChild.
  8. Dla każdego wiersza utwórz element <Placemark> i dołącz go do elementu <Document>.
  9. Dla każdej kolumny w każdym wierszu utwórz element <ExtendedData> i dołącz go do elementu <Placemark> utworzonego w kroku 8.
  10. Utwórz element <Data> i dołącz go do elementu <ExtendedData>. Nadaj elementowi <Data> atrybut nazwy i przypisz mu wartość nazwy kolumny za pomocą atrybutu setAttribute.
  11. Utwórz element <value> i dołącz go do elementu <Data>. Utwórz węzeł tekstowy i przypisz mu wartość kolumny za pomocą createTextNode. Dołącz węzeł tekstowy do elementu <value>.
  12. Utwórz element <Point> i dołącz go do elementu <Placemark>. Utwórz element <coordinates> i dołącz go do elementu <Point>.
  13. Wyodrębnij adres z wiersza, tak aby zawierał on 1 ciąg w tym formacie: Address1,Address2,City,State,Zip. Pierwszy wiersz to 1600 Amphitheater Parkway,,Mountain View,CA,94043. Nie ma problemu, jeśli znajdują się obok nich przecinki. Aby to zrobić, musisz znać strukturę pliku CSV i dowiedzieć się, które kolumny zawierają jego adres.
  14. Dodaj geokod z użyciem kodu geocoding_for_json.py wyjaśnionego w artykule Adresy geograficzne w pliku KML. Zwraca ciąg znaków, który zawiera długość i szerokość geograficzną lokalizacji.
  15. Utwórz węzeł tekstowy, przypisz mu wartość współrzędnych w kroku 14, a potem dołącz go do elementu <coordinates>.
  16. Zapisz dokument KML w pliku.
  17. Jeśli przekażesz do skryptu listę nazw kolumn jako argumenty, skrypt doda elementy w tej kolejności. Gdybyśmy nie zajmowali się kolejnością elementów, moglibyśmy użyć właściwości dict.keys(), aby wygenerować list. Jednak dict.keys() nie zachowuje oryginalnego zamówienia z dokumentu. Aby użyć tego argumentu, przekaż listę nazw pól w formie listy rozdzielonej przecinkami:
    python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax

Przykładowy kod w Pythonie

Przykładowy kod tworzenia pliku KML przy użyciu Pythona 2.2 znajdziesz poniżej. Możesz ją też pobrać stąd.


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

Utworzono przykładowy plik KML

Poniżej znajdziesz przykładowy plik KML utworzony przez ten skrypt. Zwróć uwagę, że niektóre<value>elementy zawierają tylko spacje. Dzieje się tak, ponieważ pole nie zawiera żadnych danych. Możesz też pobrać pełną przykład tutaj.

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

Zrzut ekranu

Poniżej znajduje się zrzut ekranu z plikiem KML. Każdy element <Placemark> nie ma elementu <BalloonStyle><text> ani elementu <description>, dlatego dymek jest domyślnie ustawiany na styl tabeli i rysuje na elementach <Data>.

Zrzut ekranu pliku KML utworzonego przez ten skrypt

Uwagi na temat geokodowania

Została ona wymieniona w sekcji „Adresy geograficzne do użycia w plikach KML”, ale została powtórzona. Twoje żądania geokodowania będą podlegać maksymalnej liczbie zapytań geokodera oraz 15 000 zapytań dziennie na podstawie Twojego adresu IP. Poza tym kod stanu 620 zwróci kod geokodera, jeśli podasz go szybciej niż może go obsłużyć. (Pełną listę kodów stanu znajdziesz tutaj). Aby mieć pewność, że zapytania nie będą wysyłane zbyt szybko do geokodera, możesz określić opóźnienie między poszczególnymi żądaniami geokodowania. Możesz opóźnić to opóźnienie za każdym razem, gdy otrzymasz stan 620, i użyć pętli while, aby mieć pewność, że kod został prawidłowo przetworzony przed przejściem do kolejnego. Oznacza to, że jeśli plik CSV jest bardzo duży, konieczna może być zmiana kodu geograficznego lub śledzenie szybkości tworzenia oznaczeń miejsc albo spowolnienie pracy, jeśli pliki są zbyt szybkie.

Podsumowanie

Teraz możesz użyć Pythona, aby utworzyć plik KML na podstawie pliku CSV. Przy użyciu podanego kodu plik KML będzie działać tylko w Google Earth. Możesz go zmodyfikować, aby działał zarówno w Mapach, jak i w Google Earth, za pomocą <description> zamiast <ExtendedData>. Możesz też łatwo przekonwertować ten przykładowy kod na dowolny inny język programowania, który obsługuje XML.

Po przekonwertowaniu wszystkich plików CSV na format KML możesz zapoznać się z innymi artykułami KML, takimi jak Używanie języka PHP i MySQL do tworzenia plików KML oraz artykułem Przewodnik Google dla programistów na temat ExtendedData, Dodawanie danych niestandardowych.

Powrót do góry