OpenStreetMap – بارگذاری دادهها به کمک پایتون و Overpass API


آیا تاکنون کنجکاو شدهاید که بدانید بیشترین تعداد باغ-رستورانهای آلمان کجا هستند و یا این که در سوئیس چند بانک وجود دارد؟ OpenStreetMap یک نقشه اوپنسورس عالی از دنیا است که بینشی در مورد این سؤالات و همچنین سؤالهای مشابه به ما عرضه میکند. مقادیر عظیمی از اطلاعات در برچسبهای مفید و اطلاعات جغرافیایی نهفته است؛ اما چگونه میتوانیم از این دادهها بهره بگیریم؟

چند روش برای دانلود دادههای نقشه از (OpenStreetMap (OSM وجود دارد که در ویکی این پروژه ذکر شدهاند. البته شما میتوانید کل وب سایت Planet.osm را دانلود کنید؛ اما به این منظور باید 800 گیگابایت فضای خالی روی رایانه خود داشته باشید. این عددی است که در زمان نگارش این مقاله وجود داشته است و با گذشت زمان احتمالاً افزایش خواهد یافت. اگر صرفاً میخواهید بر روی ناحیه خاصی کار کنید، میتوانید نقشه را در قالبهای مختلفی مانند OSM. (ذخیره شده به صورت XML)، PBF. (نسخه فشرده OSM.)، Shapefile یا GeoJSON مورد استفاده قرار دهید. همچنین API های مختلفی مانند API بومی OSM یا API Nominatim بدین منظور ارائه شدهاند. در این مقاله ما تنها بر روی Overpass API متمرکز میشویم که به ما امکان کوئری دادههای خاص از مجموعه داده OSM را میدهد.
مروری سریع بر مدل داده OSM
پیش از آن که کار خود را آغاز کنیم، میبایست به نوع ساختار OSM نگاهی داشته باشیم. در مدل داده OSM سه نوع مؤلفه اساسی داریم که گرهها، راهها و روابط هستند و همه آنها یک id دارند. بسیاری از اجزا دارای تگ هستند که ویژگیهای خاص آن عنصر را به صورت جفتهای کلید-مقدار توصیف میکند. به بیان سادهتر گرهها نقاطی روی نقشه (بر حسب طول و عرض جغرافیایی) هستند.
برای مثال یک گره در تصویر زیر برای نیمکتی در شهر لندن نمایش یافته است.
در سوی دیگر «راه» فهرستی مرتب از گرهها است که میتوان آن را متناظر با خیابان یا محدوده یک منزل تصور کرد. در ادامه نمونهای از خانه McSorley’s Old Ale در شهر نیویورک ارائه شده است که در OSM به صورت یک راه نمایش مییابد.
عنصر آخر دادهای در OSM رابطه است که آن را نیز میتوان فهرستی مرتب از گرهها و یا راهها و یا حتی رابطههای دیگر دانست. از این عنصر برای مدلسازی روابط منطقی یا جغرافیایی بین اشیا استفاده میشود. برای نمونه از آن میتوان برای ساختارهای بزرگی مانند کاخ ورسای استفاده کرد که شامل چندین چندضلعی برای توصیف بنا است.
استفاده از Overpass API
اینک نگاهی به نحوه بارگذاری دادهها از OSM خواهیم داشت. Overpass API از یک زبان کوئری سفارشی برای تعریف کوئریها بهره میگیرد. گرچه آشنا شدن با این زبان به اندکی زمان نیاز دارد؛ اما خوشبختانه بسته Overpass Turbo ارائه شده که باعث میشود بتوانیم به صورت تعاملی و مستقیم کوئریها را در مرورگر ارزیابی کنیم. برای مثال اگر بخواهید گرههای کافه را بررسی کنید، در این صورت کوئری شما چیزی شبیه زیر خواهد بود:
node["amenity"="cafe"]({{bbox}}); out;
در کوئری فوق میبینیم که هر عبارت با نقطهویرگول بسته میشود. این کوئری با تعیین جزئی که میخواهیم کوئری کنیم، آغاز میشود که در این مورد یک گره است. یک فیلتر به وسیله تگ روی کوئری اعمال شده که از آن میخواهد به دنبال همه گرههایی بگردد که جفت کلید-مقدار "amenity"="cafe" دارند. گزینههای مختلفی برای فیلتر کردن از طریق تگ وجود دارند که میتوانید در مستندات مشاهده کنید. انواع مختلفی از تگها وجود دارند و یکی از رایجترین آنها amenity است که ساختمانهای عمومی مختلفی از جمله کافه، رستوران یا حتی یک نیمکت را شامل میشود. برای این که مروری بر تگهای ممکن دیگر در OSM داشته باشید نگاهی به OSM Map Features یا taginfo بیندازید.
فیلتر دیگری که باید اشاره کنیم فیلتر کادر محاط {{bbox}} است که متناظر با محدودهای است که میخواهیم در آن جستجو کنیم و تنها در Overpass Turbo عمل میکند. در غیر این صورت میتوانید کادر محاط خود را با استفاده از مقادیر (south, west, north, east) به صورت عرض و طول جغرافیایی به صورت زیر تعیین نمایید:
node["amenity"="pub"] (53.2987342,-6.3870259,53.4105416,-6.1148829); out;
این وضعیت را در Overpass Turbo نیز میتوانید مورد استفاده قرار دهید. همان طور که قبلاً در مدل داده OSM دیدیم، همواره راهها و روابطی وجود دارند که ممکن است خصوصیات مشترکی داشته باشند. با استفاده از عبارت بلوک union میتوانیم آنها را نیز به دست آوریم. این عبارت همه خروجیها را از توالی عبارتهای درون یک جفت پرانتز به صورت زیر استخراج میکند:
(node["amenity"="cafe"]({{bbox}}); way["amenity"="cafe"]({{bbox}}); relation["amenity"="cafe"]({{bbox}}); ); out;
روش دیگر برای فیلتر کردن کوئریها از طریق id اجزا است. برای مثال کوئری node(1); out; نصفالنهار مبدأ دنیا را با طول جغرافیایی نزدیک به صفر به ما ارائه میکند:
روش دیگر برای فیلتر کردن کوئریها از طریق ناحیه است که به صورت area["ISO3166-1"="GB"][admin_level=2]; تعیین میشود و مساحت بریتانیای کبیر را به ما ارائه میکند. ما میتوانیم از این فیلتر از طریق افزودن (area) به عیارت خود به صورت زیر برای کوئری استفاده کنیم:
area["ISO3166-1"="GB"][admin_level=2]; node["place"="city"](area); out;
کوئری فوق همه شهرهای بریتانیا را بازمی گرداند. همچنین امکان استفاده از رابطه یا راه به عنوان یک ناحیه وجود دارد. در این حالت id های ناحیه باید از یک راه OSM موجود با افزودن 2400000000 به آیدی OSM آن استخراج شوند. همچنین در مورد رابطهها با افزودن 3600000000 این وضعیت حاصل میشود. توجه کنید که همه راهها/رابطهها یک ناحیه متناظر دارند (یعنی آنهایی که دارای تگ area=no هستند و اغلب چند-چندضلعی ها و آنهایی که name=* تعریف شده ندارند بخشی از ناحیهها نیستند.) اگر رابطه Great Britain را به مثال قبلی اعمال کنیم، نتیجه زیر حاصل میشود:
area(3600062149); node["place"="city"](area); out;
در نهایت میتوانیم خروجی دادههای کوئری شده را که به وسیله out action پیکربندی شده است، تعیین نماییم. تا به این جا خروجی را به صوت out; تعیین کردهایم؛ اما مقادیر مختلف دیگری را نیز میتوان به انتهای دستور الحاق کرد. نخستین مجموعه از مقادیر میتوانند سطح جزییات اطلاعات خروجی را تعیین کنند مانند ids، skel، body (مقدار پیشفرض)tags، meta و count که در مستندات به تفصیل توضیح داده شدهاند.
به علاوه ما میتوانیم اصلاحاتی به اطلاعات دارای ژئوکد اضافه کنیم. geom هندسه کامل را به هر شیء اضافه میکند. این تگ در مواردی که مختصاتی به شیء الصاق نشده است و شما به مختصات جغرافیایی گرهها و راهها نیاز دارید حائز اهمیت است. برای نمونه کوئری rel["ISO3166-1"="GB"][admin_level=2]; out geom; در غیر این حالت هیچ مختصاتی بازنمیگرداند. مقدار bb تنها کادر محاط را به هر راه و رابطه اضافه میکند و center تنها مرکز آن کادر محاط را اضافه میکند (و نه مرکز هندسی).
ترتیب مرتبسازی نیز میتواند از طریق asc و qt تعیین شود که به ترتیب بر اساس id یا اندیس quadtile مرتبسازی صورت میگیرد. مرتبسازی حالت دوم به مقدار زیادی سریعتر است. در نهایت با افزودن یک مقدار صحیح میتوانید بیشینه تعداد اجزایی که بازگردانده خواهند شد را تعیین کنید.
پس از ترکیب همه آن چه تاکنون آموختیم در نهایت میتوانیم موقعیت همه باغ-رستوران ها آلمان را شناسایی کنیم.
area["ISO3166-1"="DE"][admin_level=2]; (node["amenity"="biergarten"](area); way["amenity"="biergarten"](area); rel["amenity"="biergarten"](area); ); out center;
پایتون و Overpass API
اینک بینش خوبی در مورد شیوه کوئری کردن دادههای OSM با Overpass API در اختیار داریم؛ اما سؤال این است که چطور میتوانیم از دادههایی که به دست آورده این استفاده کنیم؟ یک روش برای دانلود دادهها استفاده از ابزارهای خط فرمان مانند curl یا wget است. به این منظور باید به یکی از نقاط انتهایی Overpass API دسترسی داشته باشید که قالبی به شکل http://overpass-api.de/api/interpreter?data=query دارد. زمانی که از curl استفاده میکنیم میتوانیم OSM XML کوئری خود را با اجرای دستور زیر دانلود کنیم:
curl --globoff -o output.xml http://overpass-api.de/api/interpreter?data=node(1);out;
در این مورد کوئری که قبلاً ساختیم پس از data= میآید و این کوئری باید به صورت url انکود شده باشد. پارامتر –globeoff برای استفاده از براکت های مربعی و منحنی بدون این که از سوی url تفسیر شدند حائز اهمیت است. این کوئری نتیجه XML زیر را بازمیگرداند.
<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="Overpass API 0.7.54.13 ff15392f"> <note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note> <meta osm_base="2018-02-24T21:09:02Z"/> <node id="1" lat="51.4779481" lon="-0.0014863"> <tag k="historic" v="memorial"/> <tag k="memorial" v="stone"/> <tag k="name" v="Prime Meridian of the World"/> </node> </osm>
قالبهای خروجی مختلفی وجود دارند که در مستندات در مورد آنها بیشتر میتوانید بخوانید. برای دانلود نتایج کوئری به صورت JSON باید عبارت [out:json]; را به صورت زیر به ابتدای کوئری خود اضافه کنیم:
curl --globoff - o output.json http://overpass-api.de/api/interpreter?data=[out:json];node(1);out;
کوئری فوق همان نتیجه XML فوق را این بار در قالب JSON ارائه میکند. همچنین میتوانید این کوئری را با دسترسی به آدرس http://overpass-api.de/api/interpreter?data=[out:json];node(1);out; در مرورگر تست کنید.
اما یکی از بهترین روشها برای دسترسی به نتایج استفاده از پایتون است. ما میتوانیم کوئری باغ-رستورانهای خودمان را با پایتون و از طریق بسته requests جهت دسترسی به Overpass API و بسته json جهت خواندن قالببندی جیسون از نتایج کوئری اجرا کنیم.
import requests import json overpass_url = "http://overpass-api.de/api/interpreter" overpass_query = """ [out:json]; area["ISO3166-1"="DE"][admin_level=2]; (node["amenity"="biergarten"](area); way["amenity"="biergarten"](area); rel["amenity"="biergarten"](area); ); out center; """ response = requests.get(overpass_url, params={'data': overpass_query}) data = response.json()
در این مورد نیازی به استفاده از انکودینگ url برای کوئری وجود ندارد، زیرا این مسئله در بسته requests.get لحاظ شده است و اینک میتوانید دادهها را به طور مستقیم ذخیره کنید و یا در جای دیگری مورد استفاده قرار دهید. دادههایی که برای ما مهم هستند زیر کلید elements قرار دارند. هر عنصر شامل یک کلید type است که تعیین میکند این عنصر گره، راه یا رابطه است و همچنین یک کلید id دارد. از آنجا که ما از عیارت out center; در کوئری خود استفاده کردهایم، برای هر راه و رابطه یک مختصات مرکز نیز دریافت میکنیم که در کلید center ذخیره شده است. در مورد عناصر گره، مختصات به سادگی در کلیدهای lat, lon ذخیره شدهاند.
import numpy as np import matplotlib.pyplot as plt # Collect coords into list coords = [] for element in data['elements']: if element['type'] == 'node': lon = element['lon'] lat = element['lat'] coords.append((lon, lat)) elif 'center' in element: lon = element['center']['lon'] lat = element['center']['lat'] coords.append((lon, lat)) # Convert coordinates into numpy array X = np.array(coords) plt.plot(X[:, 0], X[:, 1], 'o') plt.title('Biergarten in Germany') plt.xlabel('Longitude') plt.ylabel('Latitude') plt.axis('equal') plt.show()
روش دیگر برای دسترسی به Overpass API در پایتون از طریق استفاده از بسته overpy به عنوان یک پوشش است. در ادامه میبینیم که چگونه میتوانیم مثال قبلی را با بسته overpy اجرا کنیم.
import overpy api = overpy.Overpass() r = api.query(""" area["ISO3166-1"="DE"][admin_level=2]; (node["amenity"="biergarten"](area); way["amenity"="biergarten"](area); rel["amenity"="biergarten"](area); ); out center; """) coords = [] coords += [(float(node.lon), float(node.lat)) for node in r.nodes] coords += [(float(way.center_lon), float(way.center_lat)) for way in r.ways] coords += [(float(rel.center_lon), float(rel.center_lat)) for rel in r.relations]
یک نکته جالب در مورد overpy این است که این بسته نوع محتوا (یعنی XML یا JSON) را از روی پاسخ تشخیص میدهد. برای اطلاعات بیشتر به مستندات آن نگاه کنید. شما میتوانید در ادامه از این دادههای گردآوری شده برای مقاصد دیگر و با صرفاً جهت بصریسازی آن با بلندر چنان که در پروژه openstreetmap-heatmap توضیح داده شده است استفاده کنید. در تصویر زیر میتوانید توزیع باغ-رستوران ها را در کشور آلمان مشاهده کنید.
نتیجهگیری
پروژه اوپنسورس OSM امکانات بسیار زیادی را در اختیار شما قرار میدهد که با بررسی بیشتر به مرزهای نامحدود آن پی میبرید. تصور این که چه مقدار اطلاعات در OSM وجود دارند که آماده کاوش هستند هیجانانگیز است. حتی میتوان نقشههای سهبعدی ساختمانها را در این پروژه مشاهده کرد. از آنجا که OSM بر اساس مشارکت کاربرانش عمل میکند، میتوانید مشاهده کنید که در طی زمان چه مقدار پیشرفت کرده است و چه تعداد از کاربران به آن ملحق شدهاند. حتی یک پروژه به نام pyosmium وجود دارد که آمار کاربران OSM را برای مناطق مختلف استخراج میکند. امیدواریم در این نوشته انگیزه لازم را به شما داده باشیم تا به کاوش یافتههای هیجانانگیز و جذاب از اعماق OSM به کمک ابزارهایی که آموزش داده شد بپردازید.
اگر به این نوشته علاقهمند بودید، پیشنهاد میکنیم موارد زیر را نیز ملاحظه کنید:
- آموزش ویرایش و کارتوگرافی نقشه ها درArcGIS
- آموزش نرم افزار (Generic Mapping Tools (GMT برای رسم نقشه های زمین شناسی و نمودارهای آماری
- آموزش های سیستم اطلاعات جغرافیایی (GIS)
- آموزش هندسه دیفرانسیل برای عمران و نقشه برداری
- آموزش اتوکد ۲ بعدی (AutoCAD) با حل مثال های عملی
- آموزش پروژه محور ArcScene – طراحی نقشه شهری سه بعدی
- آموزش اتوکد (AutoCAD)
==