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

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

目標

このチュートリアルでは、Python を使用してカンマ区切り値(CSV)データから KML を作成する基本的な方法について説明します。CSV データは、現在最も広く使用されているファイル形式の 1 つです。ほとんどのスプレッドシートとデータベースは、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,

各行は、カンマで区切られた一連のテキスト文字列です。各カンマはフィールドを区切り、各行には同じ数のカンマがあります。1 行目にはフィールド名が順番に並んでいます。たとえば、各行の最初のテキスト ブロックは「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 を作成します。DictReader は、行ごとに 1 つの dicts のコレクションです。
  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> 要素に name 属性を指定し、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 でのみ動作します。<ExtendedData> の代わりに <description> を使用して、マップとアースの両方で動作するように変更できます。このコードサンプルは、XML をサポートする他のプログラミング言語に簡単に変換できます。

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

トップへ戻る