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

Mano Marks، فريق Google Geo APIs
آذار (مارس) 2008

الغرض

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

على الرغم من أن نماذج التعليمات البرمجية في هذا البرنامج التعليمي متوفرة بلغة Python، إلا أنه يمكن تطويعها لتتناسب مع معظم لغات البرمجة الأخرى. يستخدم هذا البرنامج التعليمي شفرة من عناوين الترميز الجغرافي للاستخدام في KML لتحويل العنوان إلى إحداثيات خطوط الطول/العرض. كما يستخدم أيضًا عنصر <ExtendedData> الجديد في KML 2.2، كما يستفيد من نماذج البالون الموضحة في إضافة البيانات المخصصة. وبناءً على ذلك، فإن ملف 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 تحويل هذا النص إلى مجموعة من ملفات dicts تُسمى DictReader، وتسمح لك بالتنقل في كل صف. يعتمد نموذج الشفرة هذا على معرفة بنية بياناتك مسبقًا، ولكن يمكنك إضافة بعض المعالجات الأساسية لتمرير بنية الحقل ديناميكيًا.

تحليل ملف CSV

تقدم وحدة xml.dom.minidom بيثون أدوات رائعة لإنشاء مستندات 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"، لكن الأمر يتطلب تكرارًا. وستخضع طلبات الترميز الجغرافي لمعدل طلب البحث الأقصى في أداة الترميز الجغرافي و15000 طلب بحث في اليوم الواحد استنادًا إلى عنوان IP. بالإضافة إلى ذلك، سيعرض رمز حالة الترميز 620 رمز الحالة إذا تم إجراء البحث عنه بشكل أسرع مما يمكن التعامل معه. (تتوفر قائمة كاملة برموز الحالة هنا ). ولضمان عدم إرسال طلبات البحث بسرعة كبيرة جدًا إلى أداة الترميز الجغرافي، يمكنك تحديد فترة تأخير بين كل طلب جغرافي. يمكنك زيادة هذا التأخير في كل مرة تتلقى فيها حالة 620، واستخدام حلقة while لضمان نجاح ترميز العنوان عنوانًا جغرافيًا قبل التكرار إلى العنوان التالي. وهذا يعني أنه إذا كان ملف CSV كبيرًا جدًا، فقد تضطر إلى تعديل شفرة الترميز الجغرافي، أو تتبع مدى سرعة إنشاء العلامات الموضعية، وإبطاء ذلك إذا كنت تتقدم بسرعة كبيرة.

الخاتمة

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

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

الرجوع إلى أعلى الصفحة