CSV ファイルを KML に変換する

Mano Marks、Google Geo API チーム
2008 年 3 月

目的

このチュートリアルでは、Python を使用してカンマ区切り値(CSV)データから KML を作成する基本的な方法を紹介します。CSV データは、今日最もよく使用されているファイル形式です。ほとんどのスプレッドシートとデータベースは、CSV ファイルの読み取りと書き込みを行うことができます。シンプルなフォーマットはテキスト エディタで編集できます。Python などの多くのプログラミング言語には、CSV ファイルの読み取りと書き込みを行うための特別なライブラリがあります。そのため、大量のデータを交換する際の優れたメディアになります。

このチュートリアルのコードサンプルは Python ですが、他のほとんどのプログラミング言語にも適用できます。このチュートリアルでは、KML で使用する住所のジオコーディングのコードを使用して、住所を経度/緯度の座標に変換します。また、KML 2.2 の新しい <ExtendedData> 要素を使用し、カスタムデータの追加に記載されているバルーン テンプレートを活用しています。そのため、生成された KML は現在のところ Google マップやその他の KML 消費アプリケーションではサポートされていませんが、このコードはマップ互換の KML を生成するように適用できます。

サンプルデータ

このチュートリアルでは、google-addresses.csv ファイルをサンプル CSV ファイルとして使用します。このファイルには米国のさまざまな Google オフィスの住所、電話番号、FAX 番号がすべて含まれています。ファイルのテキストは次のとおりです。

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,

各行がカンマで区切られた一連のテキスト文字列であることに注目してください。各フィールドがカンマで区切られ、各行のカンマの数は同じになります。 最初の行には、フィールドの名前が順番に含まれています。たとえば、各行のテキストの最初のブロックは「Office」フィールド、2 番目の「Address1」などです。Python はそれを DictReader という dicts のコレクションに変換して、各行を順を追って実行できます。このコードサンプルでは、データ構造を事前に把握している必要がありますが、フィールド構造を動的に渡す基本ハンドラを追加することもできます。

CSV ファイルの解析

Python の xml.dom.minidom モジュールは、XML ドキュメントを作成するための優れたツールを提供します。KML は XML なので、このチュートリアルでは頻繁に使用します。createElement または createElementNS で要素を作成し、appendChild で別の要素に追加します。CSV ファイルを解析して KML ファイルを作成する手順を以下に示します。

  1. Geocoding_for_kml.py をモジュールにインポートします。
  2. CSV ファイル用に DictReader を作成します。DictReaderdicts のコレクションです(各行に 1 つ)。
  3. Python の xml.dom.minidom.Document() を使用してドキュメントを作成します。
  4. createElementNS. を使用してルート <kml> 要素を作成する
  5. これをドキュメントに追加します
  6. createElement を使用して <Document> 要素を作成します。
  7. appendChild を使用して <kml> 要素に追加します。
  8. 各行に対して <Placemark> 要素を作成し、<Document> 要素に追加します。
  9. 各行の列ごとに <ExtendedData> 要素を作成し、ステップ 8 で作成した <Placemark> 要素に追加します。
  10. <Data> 要素を作成し、<ExtendedData> 要素に追加します。<Data> 要素に名前の属性を指定し、setAttribute を使用して列名の値を代入します。
  11. <value> 要素を作成し、<Data> 要素に追加します。テキストノードを作成し、createTextNode を使用して列の値を割り当てます。テキストノードを <value> 要素に追加します。
  12. <Point> 要素を作成し、<Placemark> 要素に追加します。<coordinates> 要素を作成し、<Point> 要素に追加します。
  13. 行から住所を抽出して、それが Address1、Address2、City、State、Zip という 1 つの文字列になるようにします。したがって、最初の行は 1600 Amphitheater Parkway,,Mountain View,CA,94043 になります。 カンマが隣にあっても問題ありません。なお、この操作を行うには、CSV ファイルの構造とアドレスを構成する列を事前に把握している必要があります。
  14. KML で使用する住所のジオコーディングに記載されている Geocoding_for_kml.py コードを使用して、住所のジオコードを行います。その場所の経度と緯度である文字列を返します。
  15. テキストノードを作成して、手順 14 で座標の値を代入し、<coordinates> 要素に追加します。
  16. KML ドキュメントをファイルに書き込みます。
  17. 列名のリストをスクリプトに引数として渡すと、スクリプトはその順序で要素を追加します。要素の順序が重要でなければ、dict.keys() を使用して list を生成できます。ただし、dict.keys() はドキュメントの元の順序を保持しません。この引数を使用するには、次のようにフィールド名のリストをカンマ区切りのリストとして渡します。
    python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax

Python サンプルコード

Python 2.2 を使用して CSV ファイルから KML ファイルを作成するためのサンプルコードを以下に示します。こちらからダウンロードできます。


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

KML サンプルを作成しました

このスクリプトで作成する KML のサンプルを以下に示します。 一部の <value> 要素には空白文字しかありません。これは、フィールドにデータが含まれていませんでした。サンプル全体はこちらからダウンロードできます。

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

スクリーンショット

以下のスクリーンショットは、Google Earth での KML ファイルの例です。 各 <Placemark> 要素には <BalloonStyle><text> 要素も <description> 要素もないため、バルーンはデフォルトで <Data> 要素に基づいて描画されるテーブル スタイルに設定されます。

このスクリプトで作成した KML のスクリーンショット

ジオコーディングの考慮事項

これは「KML で使用する住所のジオコーディング」で言及されていますが、繰り返されます。ジオコーディング リクエストには、ジオコーダの最大クエリレートと、IP に基づいて 1 日あたり 15,000 件のクエリが適用されます。また、クエリが処理可能な速度を超えてクエリすると、ジオコーダから 620 のステータス コードが返されます。(すべてのステータス コードはこちらで確認できます)。ジオコーダにクエリを速く送信しすぎないように、ジオコーダ リクエスト間の遅延を指定できます。620 ステータスを受け取るたびに、この遅延を増やすことができます。また、while ループを使用して、住所をジオコーディングしたことを確認したうえで、次の住所までの反復処理を行うことができます。つまり、CSV ファイルが非常に大きい場合、ジオコーディング コードを変更するか、マークの作成速度を追跡し、動作が遅い場合は速度を落とす必要があります。

まとめ

Python を使用して CSV ファイルから KML ファイルを作成できるようになりました。指定されたコードを使用して KML ファイルは Google Earth でのみ動作します。マップと Earth の両方で機能するように変更する場合は、<ExtendedData> ではなく <description> を使用します。また、このコードサンプルは、XML をサポートする他のプログラミング言語に簡単に変換できます。

すべての CSV ファイルの KML への変換が完了したら、PHP と MySQL を使用した KML の作成や、ExtendedData に関する Google デベロッパー ガイドのカスタムデータの追加など、その他の KML の記事をご覧ください。

トップへ戻る