مشاركة عبر


البرنامج التعليمي: توجيه المركبات الكهربائية باستخدام Jupyter Notebooks (Python)

خرائط Azure عبارة عن مجموعة من واجهات برمجة تطبيقات الخدمة الجغرافية المكانية المدمجة في Azure، ما يتيح للمطورين إنشاء تطبيقات مدركة للموقع لسيناريوهات مختلفة مثل IoT والتنقل وتعقب الأصول.

خرائط Azure تدعم واجهات برمجة تطبيقات REST لغات مثل Python وR لتحليل البيانات الجغرافية المكانية والتعلم الآلي، ما يوفر واجهات برمجة تطبيقات توجيه قوية لحساب المسارات استنادا إلى شروط مثل نوع السيارة أو المنطقة التي يمكن الوصول إليها.

يرشد هذا البرنامج التعليمي المستخدمين من خلال توجيه المركبات الكهربائية باستخدام واجهات برمجة التطبيقات خرائط Azure جنبا إلى جنب مع Jupyter Notebooks في VS Code وPython للعثور على أقرب محطة شحن عندما تكون البطارية منخفضة.

في هذا البرنامج التعليمي، سوف نتعلم:

  • إنشاء وتشغيل Jupyter Notebook في VS Code.
  • اتصل بخرائط Azure واجهات REST APIs في Python.
  • ابحث عن نطاق يمكن الوصول إليه استنادًا إلى طراز استهلاك السيارة الكهربائية.
  • ابحث عن محطات شحن المركبات الكهربائية ضمن النطاق الذي يمكن الوصول إليه، أو isochrone.
  • قم بعرض حدود النطاق القابلة للوصول، ومحطات الشحن على الخريطة.
  • ابحث واعرض مسار إلى أقرب محطة شحن للسيارات الكهربائية استنادًا إلى وقت القيادة.

المتطلبات الأساسية

إشعار

ولمزيد من المعلومات حول مصادقة Azure Maps، راجع إدارة المصادقة في Azure Maps.

تثبيت حزم مستوى المشروع

يحتوي مشروع توجيه EV والنطاق القابل للوصول على تبعيات على مكتبات aiohttp وIPython python. يمكنك تثبيت هذه في محطة Visual Studio الطرفية باستخدام pip:

pip install aiohttp
pip install ipython

افتح Jupyter Notebook في Visual Studio Code

قم بالتنزيل ثم افتح دفتر الملاحظات المستخدم في هذا البرنامج التعليمي:

  1. افتح الملف EVrouting.ipynb في مستودع AzureMapsJupyterSamples في GitHub.

  2. حدد الزر Download raw file في الزاوية العلوية اليسرى من الشاشة لحفظ الملف محليا.

    لقطة شاشة توضح كيفية تنزيل ملف دفتر الملاحظات المسمى EVrouting.ipynb من مستودع GitHub.

  3. افتح دفتر الملاحظات الذي تم تنزيله في Visual Studio Code بالنقر بزر الماوس الأيمن فوق الملف ثم تحديد Open with > Visual Studio Code، أو من خلال VS Code مستكشف الملفات.

تحميل الوحدات النمطية، والأطر المطلوبة

بمجرد إضافة التعليمات البرمجية الخاصة بك، يمكنك تشغيل خلية باستخدام أيقونة Run إلى يسار الخلية ويتم عرض الإخراج أسفل خلية التعليمات البرمجية.

قم بتشغيل البرنامج النصي التالي لتحميل جميع الوحدات النمطية وأطر العمل المطلوبة.

import time
import aiohttp
import urllib.parse
from IPython.display import Image, display

لقطة شاشة توضح كيفية تنزيل الخلية الأولى في دفتر الملاحظات التي تحتوي على عبارات الاستيراد المطلوبة مع تمييز زر التشغيل.

قم بطلب حدود النطاق القابلة للوصول

تدير شركة توصيل الطرود أسطولا يتضمن بعض المركبات الكهربائية. ويجب إعادة شحن هذه المركبات خلال النهار دون العودة إلى المستودع. عندما تنخفض الشحنة المتبقية إلى أقل من ساعة، يتم إجراء بحث للعثور على محطات الشحن ضمن نطاق يمكن الوصول إليه. ثم يتم الحصول على معلومات الحدود الخاصة بمجموعة محطات الشحن هذه.

والطلب routeType هو البيئة لتحقيق التوازن بين الاقتصاد والسرعة. يستدعي البرنامج النصي التالي واجهة برمجة تطبيقات Get Route Range لخدمة التوجيه خرائط Azure، باستخدام المعلمات المتعلقة بنموذج استهلاك السيارة. ثم يوزع البرنامج النصي الاستجابة لإنشاء كائن مضلع بتنسيق GeoJSON، يمثل الحد الأقصى لنطاق السيارة الذي يمكن الوصول إليه.

subscriptionKey = "Your Azure Maps key"
currentLocation = [34.028115,-118.5184279]
session = aiohttp.ClientSession()

# Parameters for the vehicle consumption model 
travelMode = "car"
vehicleEngineType = "electric"
currentChargeInkWh=45
maxChargeInkWh=80
timeBudgetInSec=550
routeType="eco"
constantSpeedConsumptionInkWhPerHundredkm="50,8.2:130,21.3"

# Get boundaries for the electric vehicle's reachable range.
routeRangeResponse = await (await session.get("https://atlas.microsoft.com/route/range/json?subscription-key={}&api-version=1.0&query={}&travelMode={}&vehicleEngineType={}&currentChargeInkWh={}&maxChargeInkWh={}&timeBudgetInSec={}&routeType={}&constantSpeedConsumptionInkWhPerHundredkm={}"
                                              .format(subscriptionKey,str(currentLocation[0])+","+str(currentLocation[1]),travelMode, vehicleEngineType, currentChargeInkWh, maxChargeInkWh, timeBudgetInSec, routeType, constantSpeedConsumptionInkWhPerHundredkm))).json()

polyBounds = routeRangeResponse["reachableRange"]["boundary"]

for i in range(len(polyBounds)):
    coordList = list(polyBounds[i].values())
    coordList[0], coordList[1] = coordList[1], coordList[0]
    polyBounds[i] = coordList

polyBounds.pop()
polyBounds.append(polyBounds[0])

boundsData = {
               "geometry": {
                 "type": "Polygon",
                 "coordinates": 
                   [
                      polyBounds
                   ]
                }
             }

البحث عن محطات شحن السيارات الكهربائية ضمن النطاق الذي يمكن الوصول إليه

بعد تحديد نطاق السيارة الكهربائية الذي يمكن الوصول إليه (isochrone)، يمكنك البحث عن محطات الشحن داخل تلك المنطقة.

يستخدم البرنامج النصي التالي خرائط Azure Post Search Inside Geometry API للعثور على محطات الشحن ضمن النطاق الأقصى الذي يمكن الوصول إليه في السيارة. ثم يوزع الاستجابة في صفيف من المواقع التي يمكن الوصول إليها.

# Search for electric vehicle stations within reachable range.
searchPolyResponse = await (await session.post(url = "https://atlas.microsoft.com/search/geometry/json?subscription-key={}&api-version=1.0&query=electric vehicle station&idxSet=POI&limit=50".format(subscriptionKey), json = boundsData)).json() 

reachableLocations = []
for loc in range(len(searchPolyResponse["results"])):
                location = list(searchPolyResponse["results"][loc]["position"].values())
                location[0], location[1] = location[1], location[0]
                reachableLocations.append(location)

قم بعرض محطات الشحن والنطاق القابل للوصول على الخريطة

اتصل بخدمة خرائط Azure Get Map Image لعرض نقاط الشحن والحد الأقصى الذي يمكن الوصول إليه على صورة الخريطة الثابتة عن طريق تشغيل البرنامج النصي التالي:

# Get boundaries for the bounding box.
def getBounds(polyBounds):
    maxLon = max(map(lambda x: x[0], polyBounds))
    minLon = min(map(lambda x: x[0], polyBounds))

    maxLat = max(map(lambda x: x[1], polyBounds))
    minLat = min(map(lambda x: x[1], polyBounds))
    
    # Buffer the bounding box by 10 percent to account for the pixel size of pins at the ends of the route.
    lonBuffer = (maxLon-minLon)*0.1
    minLon -= lonBuffer
    maxLon += lonBuffer

    latBuffer = (maxLat-minLat)*0.1
    minLat -= latBuffer
    maxLat += latBuffer
    
    return [minLon, maxLon, minLat, maxLat]

minLon, maxLon, minLat, maxLat = getBounds(polyBounds)
polyBoundsFormatted = ('|'.join(map(str, polyBounds))).replace('[','').replace(']','').replace(',','')
reachableLocationsFormatted = ('|'.join(map(str, reachableLocations))).replace('[','').replace(']','').replace(',','')

path = "lcff3333|lw3|la0.80|fa0.35||{}".format(polyBoundsFormatted)
pins = "custom|an15 53||{}||https://raw.githubusercontent.com/Azure-Samples/AzureMapsCodeSamples/e3a684e7423075129a0857c63011e7cfdda213b7/Static/images/icons/ev_pin.png".format(reachableLocationsFormatted)

encodedPins = urllib.parse.quote(pins, safe='')

# Render the range and electric vehicle charging points on the map.
staticMapResponse =  await session.get("https://atlas.microsoft.com/map/static/png?api-version=2022-08-01&subscription-key={}&pins={}&path={}&bbox={}&zoom=12".format(subscriptionKey,encodedPins,path,str(minLon)+", "+str(minLat)+", "+str(maxLon)+", "+str(maxLat)))

poiRangeMap = await staticMapResponse.content.read()

display(Image(poiRangeMap))

لقطة شاشة تعرض نطاق الموقع.

العثور على محطة الشحن الأمثل

أولا، تحديد جميع محطات الشحن المحتملة ضمن نطاق السيارة الذي يمكن الوصول إليه. بعد ذلك، حدد أي من هذه المحطات يمكن الوصول إليها في أقصر وقت ممكن.

يستدعي البرنامج النصي التالي واجهة برمجة تطبيقات توجيه مصفوفة خرائط Azure. تقوم بإرجاع موقع السيارة ووقت السفر والمسافة إلى كل محطة شحن. يوزع البرنامج النصي اللاحق هذه الاستجابة لتحديد أقرب محطة شحن يمكن الوصول إليها في أقل قدر من الوقت.

locationData = {
            "origins": {
              "type": "MultiPoint",
              "coordinates": [[currentLocation[1],currentLocation[0]]]
            },
            "destinations": {
              "type": "MultiPoint",
              "coordinates": reachableLocations
            }
         }

# Get the travel time and distance to each specified charging station.
searchPolyRes = await (await session.post(url = "https://atlas.microsoft.com/route/matrix/json?subscription-key={}&api-version=1.0&routeType=shortest&waitForResults=true".format(subscriptionKey), json = locationData)).json()

distances = []
for dist in range(len(reachableLocations)):
    distances.append(searchPolyRes["matrix"][0][dist]["response"]["routeSummary"]["travelTimeInSeconds"])

minDistLoc = []
minDistIndex = distances.index(min(distances))
minDistLoc.extend([reachableLocations[minDistIndex][1], reachableLocations[minDistIndex][0]])
closestChargeLoc = ",".join(str(i) for i in minDistLoc)

حساب المسار إلى أقرب محطة شحن

بعد تحديد موقع أقرب محطة شحن، استخدم واجهة برمجة تطبيقات Get Route Directions للحصول على توجيهات مفصلة من الموقع الحالي للمركبات. قم بتشغيل البرنامج النصي في الخلية التالية لإنشاء كائن GeoJSON يمثل المسار وتحليله.

# Get the route from the electric vehicle's current location to the closest charging station. 
routeResponse = await (await session.get("https://atlas.microsoft.com/route/directions/json?subscription-key={}&api-version=1.0&query={}:{}".format(subscriptionKey, str(currentLocation[0])+","+str(currentLocation[1]), closestChargeLoc))).json()

route = []
for loc in range(len(routeResponse["routes"][0]["legs"][0]["points"])):
                location = list(routeResponse["routes"][0]["legs"][0]["points"][loc].values())
                location[0], location[1] = location[1], location[0]
                route.append(location)

routeData = {
         "type": "LineString",
         "coordinates": route
     }

عرض المسار

لتصور المسار، استخدم واجهة برمجة تطبيقات Get Map Image لعرضها على الخريطة.

destination = route[-1]

#destination[1], destination[0] = destination[0], destination[1]

routeFormatted = ('|'.join(map(str, route))).replace('[','').replace(']','').replace(',','')
path = "lc0f6dd9|lw6||{}".format(routeFormatted)
pins = "default|codb1818||{} {}|{} {}".format(str(currentLocation[1]),str(currentLocation[0]),destination[0],destination[1])


# Get boundaries for the bounding box.
minLon, maxLon = (float(destination[0]),currentLocation[1]) if float(destination[0])<currentLocation[1] else (currentLocation[1], float(destination[0]))
minLat, maxLat = (float(destination[1]),currentLocation[0]) if float(destination[1])<currentLocation[0] else (currentLocation[0], float(destination[1]))

# Buffer the bounding box by 10 percent to account for the pixel size of pins at the ends of the route.
lonBuffer = (maxLon-minLon)*0.1
minLon -= lonBuffer
maxLon += lonBuffer

latBuffer = (maxLat-minLat)*0.1
minLat -= latBuffer
maxLat += latBuffer

# Render the route on the map.
staticMapResponse = await session.get("https://atlas.microsoft.com/map/static/png?api-version=2022-08-01&subscription-key={}&&path={}&pins={}&bbox={}&zoom=16".format(subscriptionKey,path,pins,str(minLon)+", "+str(minLat)+", "+str(maxLon)+", "+str(maxLat)))

staticMapImage = await staticMapResponse.content.read()

await session.close()
display(Image(staticMapImage))

لقطة شاشة تعرض خريطة تعرض المسار.

في هذا البرنامج التعليمي، تعلمتم كيفية استدعاء واجهات برمجة التطبيقات REST APIs في خرائط Azure مباشرة، وعرض بيانات Azure الخرائط باستخدام Python.

لمزيد من المعلومات حول واجهات برمجة التطبيقات خرائط Azure المستخدمة في هذا البرنامج التعليمي، راجع:

للحصول على قائمة كاملة بـAzure Maps REST APIs، قم بالاطلاع على Azure Maps REST APIs.

الخطوات التالية