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 사무실의 모든 주소, 전화번호, 팩스 번호가 포함되어 있습니다. 파일의 텍스트는 다음과 같습니다.

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' 필드, 두 번째 '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 행이며 행마다 하나씩 생성됩니다.
  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과 같은 형식의 단일 문자열이 되도록 행에서 주소를 추출합니다. 따라서 첫 번째 행은 1600 Amphitheater Parkway,,Mountain View,CA,94043입니다. 쉼표를 나란히 붙여도 괜찮습니다. 이를 위해서는 CSV 파일의 구조와 주소를 구성하는 열이 무엇인지 미리 알고 있어야 합니다.
  14. KML에서 사용할 주소 지오코딩에 설명된 지오코딩_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 어스에서 KML 파일이 어떻게 표시되는지 보여주는 스크린샷입니다. 각 <Placemark> 요소에는 <BalloonStyle><text><description> 요소가 없으므로 풍선에서 기본적으로 <Data> 스타일을 그리는 표 스타일로 설정됩니다.

이 스크립트로 만든 KML 스크린샷

지오코딩 고려사항

이는 'KML에서 사용할 주소 지오코딩'에서 언급되었지만 반복됩니다. 지오코딩 요청에는 지오코더의 최대 쿼리 비율과 IP를 기준으로 매일 15,000개의 쿼리가 적용됩니다. 또한 620가 처리할 수 있는 것보다 빠른 속도로 쿼리하는 경우 지오코더가 상태 코드를 반환합니다. 상태 코드의 전체 목록은 여기에서 확인할 수 있습니다. 쿼리를 지오코더로 너무 빨리 전송하지 않도록 하려면 각 지오코드 요청 간의 지연 시간을 지정하면 됩니다. 620 상태를 수신할 때마다 이 지연을 늘리고 while 루프를 사용하여 다음 주소로 반복하기 전에 주소를 성공적으로 지오코딩했는지 확인할 수 있습니다. 즉, CSV 파일이 매우 크면 지오코딩 코드를 수정하거나 위치표시를 만드는 속도를 추적하고 너무 빨리 속도를 높이면 속도가 느려질 수 있습니다.

마무리

이제 Python을 사용하여 CSV 파일에서 KML 파일을 만들 수 있습니다. 제공된 코드를 사용하면 KML 파일이 Google 어스에서만 작동합니다. <ExtendedData> 대신 <description>를 사용하여 지도와 어스 모두에서 작동하도록 수정할 수 있습니다. 또한 이 코드 샘플을 XML을 지원하는 다른 프로그래밍 언어로 쉽게 변환할 수 있습니다.

모든 CSV 파일을 KML로 변환했으므로 PHP 및 MySQL을 사용하여 KML 만들기 및 ExtendedData에 관한 Google 개발자 가이드 문서(커스텀 데이터 추가)와 같은 다른 KML 문서를 확인하는 것이 좋습니다.

맨 위로