تحويل ملفات CSV إلى KML

مانو ماركس، فريق Google Geo APIs
آذار (مارس) 2008

الهدف

يوضّح هذا البرنامج التعليمي الأساسيات المتعلقة بكيفية إنشاء ملف KML من بيانات القيم المفصولة بفواصل (CSV) باستخدام Python. تُعد بيانات CSV من أكثر تنسيقات الملفات شيوعًا المستخدمة اليوم. يمكن لمعظم جداول البيانات وقواعد البيانات قراءة ملفات CSV وكتابتها. ويمكن تعديل تنسيقه البسيط في محرِّر نصوص. تتضمّن العديد من لغات البرمجة، مثل Python، مكتبات خاصة لقراءة ملفات CSV وكتابتها. لذلك، تُعدّ وسيلة رائعة لتبادل كميات كبيرة من البيانات.

مع أنّ عيّنات الرموز البرمجية في هذا البرنامج التعليمي مكتوبة بلغة Python، يمكن تكييفها لتناسب معظم لغات البرمجة الأخرى. يستخدم هذا البرنامج التعليمي الرمز من الترميز الجغرافي للعناوين لاستخدامها في KML لتحويل عنوان إلى إحداثيات خطوط الطول والعرض. يستخدم هذا المثال أيضًا عنصر <ExtendedData> الجديد في KML 2.2، ويستفيد من نماذج البالونات الموضّحة في إضافة بيانات مخصّصة. وبالتالي، لا تتوافق ملفات KML التي يتم إنتاجها حاليًا مع &quot;خرائط Google&quot; أو التطبيقات الأخرى التي تستخدم ملفات KML، ولكن يمكن تعديل الرمز البرمجي لإنتاج ملفات KML متوافقة مع &quot;خرائط Google&quot;.

عيّنات البيانات

في هذا البرنامج التعليمي، استخدِم الملف 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,

لاحظ كيف أنّ كل سطر هو سلسلة من سلاسل النصوص مفصولة بفواصل. يفصل كل فاصلة حقلًا، ويحتوي كل سطر على العدد نفسه من الفواصل. يحتوي السطر الأول على أسماء الحقول بالترتيب. على سبيل المثال، تمثّل كتلة النص الأولى في كل صف حقل "المكتب"، والثانية حقل "العنوان1"، وما إلى ذلك. يمكن أن يحوّل Python ذلك إلى مجموعة من dicts تُسمى DictReader تتيح لك التنقّل بين الصفوف. يعتمد نموذج الرمز هذا على معرفتك مسبقًا ببنية بياناتك، ولكن يمكنك إضافة بعض المعالجات الأساسية لتمرير بنية الحقل بشكل ديناميكي.

تحليل ملف CSV

يوفّر نموذج xml.dom.minidom في Python أدوات رائعة لإنشاء مستندات XML، وبما أنّ KML هي XML، ستستخدمها بشكل كبير في هذا البرنامج التعليمي. يمكنك إنشاء عنصر باستخدام createElement أو createElementNS، وإلحاقه بعنصر آخر باستخدام appendChild. في ما يلي خطوات تحليل ملف CSV وإنشاء ملف KML.

  1. استورِد geocoding_for_kml.py إلى الوحدة.
  2. أنشئ DictReader لملفات CSV. DictReader هي مجموعة من dicts، واحدة لكل صف.
  3. أنشئ المستند باستخدام xml.dom.minidom.Document() في Python.
  4. إنشاء عنصر الجذر <kml> باستخدام createElementNS.
  5. إلحاقها بالمستند
  6. أنشئ عنصر <Document> باستخدام createElement.
  7. أضِفها إلى عنصر <kml> باستخدام appendChild.
  8. لكل صف، أنشئ عنصر <Placemark> وأضِفه إلى عنصر <Document>.
  9. لكل عمود في كل صف، أنشئ عنصر <ExtendedData> وأضِفه إلى عنصر <Placemark> الذي أنشأته في الخطوة 8.
  10. أنشئ عنصر <Data> وأضِفه إلى عنصر <ExtendedData>. امنح العنصر <Data> سمة اسم، وعيِّن له قيمة اسم العمود باستخدام 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. ترميز العنوان جغرافيًا باستخدام الرمز geocoding_for_kml.py الموضّح في الترميز الجغرافي للعناوين لاستخدامها في KML تعرض هذه السمة سلسلة تمثّل خط الطول وخط العرض للموقع الجغرافي.
  15. أنشئ عقدة نصية وأسنِد إليها قيمة الإحداثيات في الخطوة 14، ثم ألحِقها بالعنصر <coordinates>.
  16. اكتب مستند KML في ملف.
  17. إذا مرّرت قائمة بأسماء الأعمدة كمعلمات إلى البرنامج النصي، سيضيف البرنامج النصي العناصر بهذا الترتيب. إذا لم يكن ترتيب العناصر مهمًا، يمكننا استخدام dict.keys() لإنشاء list. ومع ذلك، لا تحتفظ dict.keys() بالترتيب الأصلي من المستند. لاستخدام هذه الوسيطة، مرِّر أسماء الحقول في قائمة مفصولة بفواصل، على النحو التالي:
    python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax

نموذج لرمز Python البرمجي

في ما يلي نموذج رمز لإنشاء ملف KML من ملف CSV باستخدام Python 2.2. يمكنك أيضًا تنزيله من هنا.


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

لقطة شاشة

في ما يلي لقطة شاشة توضّح شكل ملف KML هذا في Google Earth. بما أنّ كل عنصر <Placemark> لا يحتوي على <BalloonStyle><text> ولا على عنصر <description>، يتم تلقائيًا عرض البالون بنمط جدول، مع الاستفادة من عناصر <Data>.

لقطة شاشة لملف KML تم إنشاؤه بواسطة هذا النص البرمجي

اعتبارات الترميز الجغرافي

وقد تم ذكر ذلك في "ترميز العناوين الجغرافية لاستخدامها في ملفات KML"، ولكن من المهم تكرار هذه المعلومة. ستخضع طلبات الترميز الجغرافي للحد الأقصى لمعدّل طلبات البحث في أداة الترميز الجغرافي و15,000 طلب بحث في اليوم استنادًا إلى عنوان IP. بالإضافة إلى ذلك، سيعرض برنامج الترميز الجغرافي رمز الحالة 620 إذا أرسلت إليه طلبات بحث أسرع من قدرته على معالجتها. (يمكنك الاطّلاع على قائمة كاملة برموز الحالة هنا). لضمان عدم إرسال طلبات بحث بسرعة كبيرة إلى أداة الترميز الجغرافي، يمكنك تحديد فترة تأخير بين كل طلب ترميز جغرافي. يمكنك زيادة هذا التأخير في كل مرة تتلقّى فيها حالة 620، واستخدام حلقة while لضمان ترميز عنوان جغرافيًا بنجاح قبل الانتقال إلى العنوان التالي. هذا يعني أنّه إذا كان ملف CSV كبيرًا جدًا، قد تحتاج إلى تعديل رمز الترميز الجغرافي أو تتبُّع سرعة إنشاء العلامات المكانية وإبطائها إذا كانت السرعة كبيرة جدًا.

الخاتمة

يمكنك الآن استخدام Python لإنشاء ملف KML من ملف CSV. باستخدام الرمز البرمجي المقدَّم، لن يعمل ملف KML إلا في Google Earth. يمكنك تعديلها لتتمكّن من استخدامها في كل من &quot;خرائط Google&quot; وGoogle Earth من خلال استخدام <description> بدلاً من <ExtendedData>. من السهل أيضًا تحويل عيّنة الرمز هذه إلى أي لغة برمجة أخرى تتيح استخدام XML.

بعد الانتهاء من تحويل جميع ملفات CSV إلى KML، يمكنك الاطّلاع على مقالات أخرى حول KML، مثل استخدام PHP وMySQL لإنشاء KML ومقالة &quot;دليل مطوّري Google&quot; حول ExtendedData، إضافة بيانات مخصّصة.

الرجوع إلى الأعلى