Chuyển đổi tệp CSV sang KML

Mano Marks, Nhóm API Địa lý của Google
Tháng 3 năm 2008

Mục tiêu

Phần hướng dẫn này trình bày những thông tin cơ bản về cách tạo tệp KML từ dữ liệu Giá trị được phân tách bằng dấu phẩy (CSV) bằng Python. Dữ liệu CSV là một trong những định dạng tệp phổ biến nhất được sử dụng hiện nay. Hầu hết bảng tính và cơ sở dữ liệu đều có thể đọc và ghi tệp CSV. Bạn có thể chỉnh sửa định dạng đơn giản trong trình chỉnh sửa văn bản. Nhiều ngôn ngữ lập trình, chẳng hạn như Python, có các thư viện đặc biệt để đọc và ghi tệp CSV. Do đó, đây là một phương tiện tuyệt vời để trao đổi lượng lớn dữ liệu.

Mặc dù mã mẫu trong hướng dẫn này được viết bằng Python, nhưng bạn có thể điều chỉnh các mã đó cho hầu hết ngôn ngữ lập trình khác. Hướng dẫn này dùng mã từ Mã hoá địa lý để sử dụng trong KML để biến địa chỉ thành kinh độ/vĩ độ. Nó cũng sử dụng phần tử <ExtendedData> mới của KML 2.2 và tận dụng tính năng tạo mẫu bóng trong phần Thêm dữ liệu tùy chỉnh. Do đó, KML được tạo hiện không được hỗ trợ trong Google Maps hoặc các ứng dụng tiêu thụ KML khác, nhưng bạn có thể điều chỉnh mã này để tạo KML tương thích với Maps.

Dữ liệu mẫu

Để xem hướng dẫn này, hãy sử dụng tệp google-Address.csv dưới dạng tệp CSV mẫu. Tệp này có tất cả địa chỉ, số điện thoại và số fax của nhiều văn phòng Google tại Hoa Kỳ. Dưới đây là nội dung văn bản của tệp:

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,

Lưu ý cách mỗi dòng là một chuỗi văn bản được phân tách bằng dấu phẩy. Mỗi dấu phẩy phân tách một trường; mỗi dòng có cùng số dấu phẩy. Dòng đầu tiên chứa tên của các trường theo thứ tự. Chẳng hạn, khối văn bản đầu tiên trong mỗi hàng là trường "Office", văn bản thứ hai "Address1", v.v. Python có thể biến trường đó thành một tập hợp dicts được gọi là DictReader, cho phép bạn đi qua từng hàng. Mã mẫu này dựa trên việc bạn biết trước cấu trúc dữ liệu của mình, nhưng bạn có thể thêm một số trình xử lý cơ bản để tự động truyền cấu trúc trường.

Phân tích cú pháp tệp CSV

Mô-đun xml.dom.minidom của Python cung cấp các công cụ tuyệt vời để tạo tài liệu XML và vì KML là XML nên bạn sẽ sử dụng nó khá nhiều trong hướng dẫn này. Bạn tạo một phần tử có createElement hoặc createElementNS và thêm vào một phần tử khác có appendChild. Sau đây là các bước để phân tích cú pháp tệp CSV và tạo tệp KML.

  1. Nhập Geocodes_for_{8/}.py vào mô-đun của bạn.
  2. Tạo DictReader cho các tệp CSV. DictReader là một tập hợp dicts, mỗi tập hợp mỗi hàng.
  3. Tạo tài liệu bằng cách sử dụng xml.dom.minidom.Document() của Python.
  4. Tạo phần tử <kml> gốc bằng cách sử dụng createElementNS.
  5. Thêm vào tài liệu.
  6. Tạo phần tử <Document> bằng cách sử dụng createElement.
  7. Thêm nó vào phần tử <kml> bằng cách sử dụng appendChild.
  8. Đối với mỗi hàng, hãy tạo một phần tử <Placemark> và thêm phần tử đó vào phần tử <Document>.
  9. Đối với mỗi cột trong mỗi hàng, hãy tạo một phần tử <ExtendedData> và thêm phần tử đó vào phần tử <Placemark> đã tạo ở bước 8.
  10. Tạo một phần tử <Data> rồi thêm phần tử đó vào phần tử <ExtendedData>. Đặt phần tử tên cho phần tử <Data> và gán giá trị của tên cột đó bằng setAttribute.
  11. Tạo một phần tử <value> và thêm phần tử đó vào phần tử <Data>. Tạo một nút văn bản và chỉ định giá trị của cột bằng cách sử dụng createTextNode. Thêm nút văn bản vào phần tử <value>.
  12. Tạo một phần tử <Point> rồi thêm phần tử đó vào phần tử <Placemark>. Tạo một phần tử <coordinates> rồi thêm phần tử đó vào phần tử <Point>.
  13. Trích xuất địa chỉ từ hàng để trở thành một chuỗi duy nhất ở định dạng này: Address1,Address2,City,State,Zip. Vì vậy, hàng đầu tiên sẽ là 1600 Amphitheater Parkway,,Mountain View,CA,94043. Chấp nhận có dấu phẩy bên cạnh nhau. Lưu ý: Để làm được điều này, bạn cần phải có kiến thức trước về cấu trúc của tệp CSV và những cột nào cấu thành địa chỉ.
  14. Mã hóa địa lý địa chỉ bằng cách sử dụng mã geo quá_mã_cho_mã_web_của_chúng tôi. được giải thích trong phần Địa chỉ mã hoá địa lý để sử dụng trong KML. Phương thức này trả về một chuỗi là kinh độ và vĩ độ của vị trí.
  15. Tạo một nút văn bản và chỉ định giá trị của toạ độ trong bước 14, sau đó thêm nút đó vào phần tử <coordinates>.
  16. Ghi tài liệu KML vào tệp.
  17. Nếu bạn truyền một danh sách tên cột làm đối số cho tập lệnh, tập lệnh sẽ thêm các phần tử theo thứ tự đó. Nếu không quan tâm đến thứ tự của các phần tử, chúng ta có thể sử dụng dict.keys() để tạo một list. Tuy nhiên, dict.keys() không giữ nguyên thứ tự ban đầu trong tài liệu. Để sử dụng đối số này, hãy truyền vào danh sách tên trường dưới dạng danh sách được phân tách bằng dấu phẩy, như trong ví dụ sau:
    python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax

Mã Python mẫu

Mã mẫu để tạo tệp KML từ tệp CSV bằng Python 2.2 được trình bày dưới đây. Bạn cũng có thể tải xuống tại đây.


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

Đã tạo KML mẫu

Dưới đây là ví dụ về tệp KML mà tập lệnh này tạo ra. Hãy lưu ý cách một số<value> phần tử chỉ có khoảng trắng trong đó. Nguyên nhân là do trường này không có dữ liệu nào. Bạn cũng có thể tải toàn bộ mẫu xuống tại đây.

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

Ảnh chụp màn hình

Dưới đây là ảnh chụp màn hình của tệp KML trong Google Earth. Do mỗi phần tử<Placemark> không có phần tử <BalloonStyle><text> và không có phần tử <description>, nên hộp chú giải mặc định là kiểu bảng, vẽ trên các phần tử <Data>.

Ảnh chụp màn hình của KML do tập lệnh này tạo

Cân nhắc về việc mã hóa địa lý

Điều này đã được đề cập trong "Địa chỉ mã hóa địa lý để sử dụng trong KML", nhưng nó lặp lại. Các yêu cầu mã hóa địa lý của bạn sẽ phải tuân theo tỷ lệ truy vấn tối đa của bộ mã hóa và 15.000 truy vấn mỗi ngày dựa trên IP của bạn. Ngoài ra, bộ mã hoá sẽ trả về mã trạng thái 620 nếu bạn truy vấn mã này nhanh hơn tốc độ xử lý. (Bạn có thể xem danh sách đầy đủ các mã trạng thái tại đây.) Để đảm bảo bạn không gửi truy vấn quá nhanh đến bộ mã hoá địa lý, bạn có thể chỉ định độ trễ giữa mỗi yêu cầu mã hoá địa lý. Bạn có thể tăng độ trễ này mỗi khi nhận được trạng thái 620 và sử dụng vòng lặp while để đảm bảo bạn đã mã hóa thành công một địa chỉ trước khi lặp lại vào địa chỉ tiếp theo. Điều này có nghĩa là nếu tệp CSV của bạn rất lớn, bạn có thể phải sửa đổi mã mã hoá địa lý hoặc theo dõi tốc độ tạo dấu vị trí và giảm tốc độ đó nếu chạy quá nhanh.

Kết luận

Giờ đây, bạn có thể sử dụng Python để tạo tệp KML từ tệp CSV. Sử dụng mã được cung cấp, tệp KML sẽ chỉ hoạt động trong Google Earth. Bạn có thể sửa đổi để làm việc trong cả Maps và Earth bằng cách sử dụng <description> thay vì <ExtendedData>. Bạn cũng có thể dễ dàng chuyển đổi mã mẫu này sang ngôn ngữ lập trình khác có hỗ trợ XML.

Sau khi chuyển đổi xong tất cả các tệp CSV sang KML, bạn có thể xem các bài viết KML khác, chẳng hạn như Sử dụng PHP và MySQL để tạo KML và bài viết Hướng dẫn cho nhà phát triển trên ExtendedData, Thêm dữ liệu tuỳ chỉnh.

Quay lại đầu trang