كانون الثاني (يناير) 2009
الهدف
تعلّمك هذه المقالة التعليمية كيفية استخدام الصور التي تمّت إضافة معلومات الموقع الجغرافي إليها لإنشاء ملف KML PhotoOverlays
. على الرغم من أنّ الرمز النموذجي مكتوب بلغة Python، تتوفّر العديد من المكتبات المشابهة في لغات برمجة أخرى، لذا لن يكون من الصعب ترجمة هذا الرمز إلى لغة أخرى. تعتمد التعليمات البرمجية الواردة في هذه المقالة على مكتبة Python مفتوحة المصدر، وهي EXIF.py.
مقدمة
الكاميرات الرقمية هي أجهزة مذهلة. لا يدرك العديد من المستخدمين أنّهم يفعلون أكثر من مجرد التقاط الصور والفيديوهات. ويضيفون أيضًا إلى هذه الفيديوهات والصور بيانات وصفية حول الكاميرا وإعداداتها. في السنوات القليلة الماضية، وجد المستخدمون طرقًا لإضافة بيانات جغرافية إلى هذه المعلومات، سواء كانت مضمّنة من قِبل الشركات المصنّعة للكاميرات، مثل بعض كاميرات Ricoh وNikon، أو من خلال أجهزة مثل أجهزة تسجيل نظام تحديد المواقع العالمي (GPS) وEyeFi Explore. تضمّن هواتف الكاميرا، مثل iPhone والهواتف التي تستخدم نظام التشغيل Android، مثل هاتف G1 من T-Mobile، هذه البيانات تلقائيًا. ستقوم بعض المواقع الإلكترونية التي تتيح تحميل الصور، مثل Panoramio وألبومات الويب في Picasa وFlickr، بتحليل بيانات نظام تحديد المواقع العالمي (GPS) تلقائيًا واستخدامها لتحديد الموقع الجغرافي للصورة. يمكنك بعد ذلك استعادة هذه البيانات في الخلاصات. ولكن أين المتعة في ذلك؟ تستكشف هذه المقالة كيفية الوصول إلى هذه البيانات بنفسك.
عناوين Exif
الطريقة الأكثر شيوعًا لتضمين البيانات في ملف صورة هي استخدام تنسيق ملف الصور القابل للتبادل أو EXIF. يتم تخزين البيانات في شكل ثنائي في عناوين EXIF بطريقة عادية. إذا كنت تعرف مواصفات عناوين EXIF، يمكنك تحليلها بنفسك. لحسن الحظ، سبق أن أنجز أحد المستخدمين الجزء الأصعب وكتب وحدة Python لك. مكتبة EXIF.py المفتوحة المصدر هي أداة رائعة لقراءة عناوين ملفات JPEG.
الرمز
يمكنك العثور على عينة التعليمات البرمجية لهذه المقالة في هذا الملف: exif2kml.py. إذا أردت الانتقال مباشرةً إلى استخدامها، نزِّل هذه الوحدة، بالإضافة إلى EXIF.py، وضَعها في الدليل نفسه. نفِّذ الأمر python exif2kml.py foo.jpg
مع استبدال foo.jpg بالمسار المؤدي إلى صورة تم وضع علامة جغرافية عليها. سيؤدي ذلك إلى إنشاء ملف باسم test.kml
.
تحليل عناوين Exif
توفّر EXIF.py واجهة سهلة لاستخراج عناوين Exif. ما عليك سوى تشغيل الدالة process_file()
وستعرض العناوين ككائن dict
.
def GetHeaders(the_file): """Handles getting the Exif headers and returns them as a dict. Args: the_file: A file object Returns: a dict mapping keys corresponding to the Exif headers of a file. """ data = EXIF.process_file(the_file, 'UNDEF', False, False, False) return data
بعد الحصول على عناوين Exif، عليك استخراج إحداثيات نظام تحديد المواقع العالمي (GPS). تعامل EXIF.py مع هذه القيم على أنّها كائنات Ratio
، وهي كائنات لتخزين بسط ومقام القيم. يؤدي ذلك إلى إعداد نسبة دقيقة بدلاً من الاعتماد على عدد عشري. ومع ذلك، يتوقّع KML أرقامًا عشرية، وليس نسبًا. لذلك، عليك استخراج كل إحداثية وتحويل البسط والمقام إلى رقم واحد ذي فاصلة عشرية بالدرجات العشرية:
def DmsToDecimal(degree_num, degree_den, minute_num, minute_den, second_num, second_den): """Converts the Degree/Minute/Second formatted GPS data to decimal degrees. Args: degree_num: The numerator of the degree object. degree_den: The denominator of the degree object. minute_num: The numerator of the minute object. minute_den: The denominator of the minute object. second_num: The numerator of the second object. second_den: The denominator of the second object. Returns: A deciminal degree. """ degree = float(degree_num)/float(degree_den) minute = float(minute_num)/float(minute_den)/60 second = float(second_num)/float(second_den)/3600 return degree + minute + second def GetGps(data): """Parses out the GPS coordinates from the file. Args: data: A dict object representing the Exif headers of the photo. Returns: A tuple representing the latitude, longitude, and altitude of the photo. """ lat_dms = data['GPS GPSLatitude'].values long_dms = data['GPS GPSLongitude'].values latitude = DmsToDecimal(lat_dms[0].num, lat_dms[0].den, lat_dms[1].num, lat_dms[1].den, lat_dms[2].num, lat_dms[2].den) longitude = DmsToDecimal(long_dms[0].num, long_dms[0].den, long_dms[1].num, long_dms[1].den, long_dms[2].num, long_dms[2].den) if data['GPS GPSLatitudeRef'].printable == 'S': latitude *= -1 if data['GPS GPSLongitudeRef'].printable == 'W': longitude *= -1 altitude = None try: alt = data['GPS GPSAltitude'].values[0] altitude = alt.num/alt.den if data['GPS GPSAltitudeRef'] == 1: altitude *= -1 except KeyError: altitude = 0 return latitude, longitude, altitude
بعد الحصول على الإحداثيات، يمكنك بسهولة إنشاء PhotoOverlay
بسيط لكل صورة:
def CreatePhotoOverlay(kml_doc, file_name, the_file, file_iterator): """Creates a PhotoOverlay element in the kml_doc element. Args: kml_doc: An XML document object. file_name: The name of the file. the_file: The file object. file_iterator: The file iterator, used to create the id. Returns: An XML element representing the PhotoOverlay. """ photo_id = 'photo%s' % file_iterator data = GetHeaders(the_file) coords = GetGps(data) po = kml_doc.createElement('PhotoOverlay') po.setAttribute('id', photo_id) name = kml_doc.createElement('name') name.appendChild(kml_doc.createTextNode(file_name)) description = kml_doc.createElement('description') description.appendChild(kml_doc.createCDATASection('<a href="#%s">' 'Click here to fly into ' 'photo</a>' % photo_id)) po.appendChild(name) po.appendChild(description) icon = kml_doc.createElement('icon') href = kml_doc.createElement('href') href.appendChild(kml_doc.createTextNode(file_name)) camera = kml_doc.createElement('Camera') longitude = kml_doc.createElement('longitude') latitude = kml_doc.createElement('latitude') altitude = kml_doc.createElement('altitude') tilt = kml_doc.createElement('tilt') # Determines the proportions of the image and uses them to set FOV. width = float(data['EXIF ExifImageWidth'].printable) length = float(data['EXIF ExifImageLength'].printable) lf = str(width/length * -20.0) rf = str(width/length * 20.0) longitude.appendChild(kml_doc.createTextNode(str(coords[1]))) latitude.appendChild(kml_doc.createTextNode(str(coords[0]))) altitude.appendChild(kml_doc.createTextNode('10')) tilt.appendChild(kml_doc.createTextNode('90')) camera.appendChild(longitude) camera.appendChild(latitude) camera.appendChild(altitude) camera.appendChild(tilt) icon.appendChild(href) viewvolume = kml_doc.createElement('ViewVolume') leftfov = kml_doc.createElement('leftFov') rightfov = kml_doc.createElement('rightFov') bottomfov = kml_doc.createElement('bottomFov') topfov = kml_doc.createElement('topFov') near = kml_doc.createElement('near') leftfov.appendChild(kml_doc.createTextNode(lf)) rightfov.appendChild(kml_doc.createTextNode(rf)) bottomfov.appendChild(kml_doc.createTextNode('-20')) topfov.appendChild(kml_doc.createTextNode('20')) near.appendChild(kml_doc.createTextNode('10')) viewvolume.appendChild(leftfov) viewvolume.appendChild(rightfov) viewvolume.appendChild(bottomfov) viewvolume.appendChild(topfov) viewvolume.appendChild(near) po.appendChild(camera) po.appendChild(icon) po.appendChild(viewvolume) point = kml_doc.createElement('point') coordinates = kml_doc.createElement('coordinates') coordinates.appendChild(kml_doc.createTextNode('%s,%s,%s' %(coords[1], coords[0], coords[2]))) point.appendChild(coordinates) po.appendChild(point) document = kml_doc.getElementsByTagName('Document')[0] document.appendChild(po)
كما تلاحظ، نحن نستخدم طرق W3C DOM العادية فقط، لأنّها متاحة في معظم لغات البرمجة. لمعرفة كيفية عمل كل ذلك معًا، يمكنك تنزيل الرمز من هنا.
لا تستفيد هذه العيّنة من الإمكانات الكاملة لـ PhotoOverlays
، والتي تتيح لك إنشاء استكشافات مفصّلة لصور عالية الدقة. ومع ذلك، يوضّح هذا الفيديو كيفية تعليق صورة بأسلوب اللوحات الإعلانية على Google Earth. في ما يلي نموذج لملف KML تم إنشاؤه باستخدام هذا الرمز:
<?xml version="1.0" encoding="utf-8"?> <kml xmlns="http://www.opengis.net/kml/2.2"> <Document> <PhotoOverlay id="photo0"> <name> 1228258523134.jpg </name> <description> <![CDATA[<a href="#photo0">Click here to fly into photo</a>]]> </description> <Camera> <longitude> -122.3902159196034 </longitude> <latitude> 37.78961266330473 </latitude> <altitude> 10 </altitude> <tilt> 90 </tilt> </Camera> <Icon> <href> 1228258523134.jpg </href> </Icon> <ViewVolume> <leftFov> -26.6666666667 </leftFov> <rightFov> 26.6666666667 </rightFov> <bottomFov> -20 </bottomFov> <topFov> 20 </topFov> <near> 10 </near> </ViewVolume> <Point> <coordinates> -122.3902159196034,37.78961266330473,0 </coordinates> </Point> </PhotoOverlay> </Document> </kml>
وفي ما يلي الشكل الذي يظهر به في Google Earth:

التحذيرات
لا تزال عملية وضع علامات جغرافية على الصور في بدايتها.
في ما يلي بعض الأمور التي يجب الانتباه إليها:
- لا تكون أجهزة نظام تحديد المواقع العالمي (GPS) دقيقة بنسبة% 100 في بعض الأحيان، خاصةً تلك المضمّنة في الكاميرات، لذا عليك التحقّق من المواضع الجغرافية لصورك.
- لا تتتبّع العديد من الأجهزة الارتفاع عن سطح البحر، بل تضبطه على 0. إذا كان الارتفاع مهمًا بالنسبة إليك، عليك البحث عن طريقة أخرى لتسجيل هذه البيانات.
- موقع نظام تحديد المواقع العالمي (GPS) هو موقع الكاميرا، وليس موقع الجسم المُراد تصويره. لهذا السبب، يضع هذا النموذج عنصر "الكاميرا" في موضع نظام تحديد المواقع العالمي (GPS)، ويضع الصورة الفعلية بعيدًا عن هذا الموضع.
- لا تسجّل بيانات Exif معلومات حول الاتجاه الذي تشير إليه الكاميرا، لذا قد تحتاج إلى تعديل
PhotoOverlays
بسبب ذلك. الخبر السار هو أنّ بعض الأجهزة، مثل الهواتف التي تعمل بنظام التشغيل Android، تتيح لك تسجيل بيانات مثل اتجاه البوصلة والميل مباشرةً، ولكن ليس في عناوين Exif.
مع ذلك، تظل هذه الطريقة فعّالة لعرض صورك. نأمل أن نرى المزيد من الصور التي تم تحديد الموقع الجغرافي لها بدقة في المستقبل القريب.
الخطوات التالية
بعد أن بدأت في استخدام عناوين EXIF، يمكنك استكشاف مواصفات EXIF. هناك الكثير من البيانات الأخرى المخزّنة هناك، وقد يهمّك تسجيلها ووضعها في بالون الوصف. يمكنك أيضًا إنشاء PhotoOverlays
أكثر تفصيلاً باستخدام ImagePyramids
. تقدّم المقالة في "دليل المطوّرين" على PhotoOverlays
نظرة عامة جيدة حول كيفية استخدامها.