跳至主要内容

地理圍欄

本課程概述的手繪筆記

手繪筆記由 Nitya Narasimhan 提供。點擊圖片查看大圖。

這段影片概述了地理圍欄及如何在 Azure Maps 中使用它們,這些主題將在本課程中介紹:

來自 Microsoft Developer IoT 節目的 Azure Maps 地理圍欄

🎥 點擊上方圖片觀看影片

課前測驗

課前測驗

介紹

在過去的三節課中,你已經使用物聯網來定位從農場運送農產品到加工中心的卡車。你已經捕獲了 GPS 數據,將其發送到雲端存儲,並在地圖上進行了可視化。提高供應鏈效率的下一步是當卡車即將到達加工中心時獲得警報,這樣卸貨所需的工作人員可以在車輛到達時立即準備好叉車和其他設備。這樣他們可以快速卸貨,你不必支付卡車和司機的等待費用。

在本課程中,你將學習地理圍欄 - 定義的地理空間區域,例如距離加工中心 2 公里內的區域,以及如何測試 GPS 坐標是否在地理圍欄內或外,以便你可以查看你的 GPS 傳感器是否已到達或離開某個區域。

在本課程中,我們將介紹:

🗑 這是本項目中的最後一課,因此在完成本課程和作業後,別忘了清理你的雲端服務。你將需要這些服務來完成作業,因此請確保先完成作業。

如有需要,請參閱清理項目指南以獲取清理指示。

什麼是地理圍欄

地理圍欄是現實世界地理區域的虛擬邊界。地理圍欄可以是定義為點和半徑的圓(例如圍繞建築物的 100 米寬的圓),或覆蓋區域的多邊形,例如學校區域、城市邊界或大學或辦公園區。

一些地理圍欄示例,顯示圍繞 Microsoft 公司商店的圓形地理圍欄,以及圍繞 Microsoft 西校區的多邊形地理圍欄

💁 你可能已經在不知不覺中使用了地理圍欄。如果你使用 iOS 提醒應用或 Google Keep 設置了基於位置的提醒,你就已經使用了地理圍欄。這些應用會根據給定的位置設置地理圍欄,並在你的手機進入地理圍欄時提醒你。

有很多原因讓你想知道車輛是否在地理圍欄內或外:

  • 卸貨準備 - 獲得車輛到達現場的通知可以讓工作人員準備卸貨,減少車輛等待時間。這可以讓司機在一天內進行更多次的交付,減少等待時間。
  • 稅務合規 - 一些國家(如新西蘭)對柴油車輛在公共道路上行駛時根據車輛重量徵收道路稅。使用地理圍欄可以跟踪在公共道路上行駛的里程,而不是在農場或伐木區等私人道路上行駛的里程。
  • 監控盜竊 - 如果車輛應該只留在某個區域(例如農場),而它離開了地理圍欄,則可能被盜。
  • 位置合規 - 工作場所、農場或工廠的某些部分可能對某些車輛禁區,例如將運載人工肥料和農藥的車輛遠離種植有機農產品的田地。如果進入地理圍欄,則車輛不符合規定,司機可以收到通知。

✅ 你能想到其他地理圍欄的用途嗎?

Azure Maps,你在上一課中用來可視化 GPS 數據的服務,允許你定義地理圍欄,然後測試某個點是否在地理圍欄內或外。

定義地理圍欄

地理圍欄是使用 GeoJSON 定義的,與上一課中添加到地圖上的點相同。在這種情況下,它不是 Point 值的 FeatureCollection,而是包含 PolygonFeatureCollection

{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-122.13393688201903,
47.63829579223815
],
[
-122.13389128446579,
47.63782047131512
],
[
-122.13240802288054,
47.63783312249837
],
[
-122.13238388299942,
47.63829037035086
],
[
-122.13393688201903,
47.63829579223815
]
]
]
},
"properties": {
"geometryId": "1"
}
}
]
}

每個多邊形上的點都定義為一個經度、緯度對的數組,這些點在一個數組中被設置為 coordinates。在上一課中的 Point 中,coordinates 是一個包含 2 個值(緯度和經度)的數組,而對於 Polygon,它是一個包含 2 個值(經度、緯度)的數組的數組。

💁 記住,GeoJSON 使用 經度、緯度 來表示點,而不是 緯度、經度

多邊形坐標數組總是比多邊形上的點數多 1 個,最後一個條目與第一個條目相同,閉合多邊形。例如,對於一個矩形,會有 5 個點。

一個帶有坐標的矩形

在上圖中,有一個矩形。多邊形坐標從左上角的 47,-122 開始,然後向右移動到 47,-121,然後向下移動到 46,-121,然後向右移動到 46, -122,然後回到起點 47, -122。這給了多邊形 5 個點 - 左上角、右上角、右下角、左下角,然後是左上角以閉合它。

✅ 嘗試在你的家或學校周圍創建一個 GeoJSON 多邊形。使用像 GeoJSON.io 這樣的工具。

任務 - 定義地理圍欄

要在 Azure 地圖中使用地理圍欄,首先必須將其上傳到你的 Azure 地圖帳戶。一旦上傳,你將獲得一個唯一的 ID,你可以用它來測試某個點是否在地理圍欄內。要將地理圍欄上傳到 Azure 地圖,你需要使用地圖 Web API。你可以使用名為 curl 的工具來調用 Azure 地圖 Web API。

🎓 Curl 是一個命令行工具,用於對 Web 端點發出請求

  1. 如果你使用的是 Linux、macOS 或 Windows 10 的最新版本,你可能已經安裝了 curl。從你的終端或命令行運行以下命令來檢查:

    curl --version

    如果你沒有看到 curl 的版本信息,你需要從 curl 下載頁面 安裝它。

    💁 如果你熟悉 Postman,那麼你可以選擇使用它。

  2. 創建一個包含多邊形的 GeoJSON 文件。你將使用你的 GPS 傳感器來測試它,因此請在你當前位置周圍創建一個多邊形。你可以通過編輯上面給出的 GeoJSON 示例手動創建一個,或者使用像 GeoJSON.io 這樣的工具。

    GeoJSON 需要包含一個 FeatureCollection,其中包含一個 Feature,其 geometry 類型為 Polygon

    必須 還要在與 geometry 元素相同的級別添加一個 properties 元素,並且這必須包含一個 geometryId

    "properties": {
    "geometryId": "1"
    }

    如果你使用 GeoJSON.io,那麼你需要手動將此項目添加到空的 properties 元素中,無論是在下載 JSON 文件後,還是在應用程序中的 JSON 編輯器中。

    geometryId 必須在此文件中是唯一的。你可以將多個地理圍欄作為多個 Features 上傳到同一個 GeoJSON 文件中的 FeatureCollection,只要每個地理圍欄都有不同的 geometryId。如果從不同的文件在不同的時間上傳,多邊形可以具有相同的 geometryId

  3. 將此文件保存為 geofence.json,並在終端或控制台中導航到保存它的位置。

  4. 運行以下 curl 命令來創建地理圍欄:

    curl --request POST 'https://atlas.microsoft.com/mapData/upload?api-version=1.0&dataFormat=geojson&subscription-key=<subscription_key>' \
    --header 'Content-Type: application/json' \
    --include \
    --data @geofence.json

    將 URL 中的 <subscription_key> 替換為你的 Azure 地圖帳戶的 API 密鑰。

    該 URL 用於通過 https://atlas.microsoft.com/mapData/upload API 上傳地圖數據。調用包括一個 api-version 參數來指定要使用的 Azure 地圖 API,這是為了允許 API 隨時間變化但保持向後兼容性。上傳的數據格式設置為 geojson

    這將運行 POST 請求到上傳 API 並返回一個響應標頭列表,其中包括一個名為 location 的標頭

    content-type: application/json
    location: https://us.atlas.microsoft.com/mapData/operations/1560ced6-3a80-46f2-84b2-5b1531820eab?api-version=1.0
    x-ms-azuremaps-region: West US 2
    x-content-type-options: nosniff
    strict-transport-security: max-age=31536000; includeSubDomains
    x-cache: CONFIG_NOCACHE
    date: Sat, 22 May 2021 21:34:57 GMT
    content-length: 0

    🎓 當調用 Web 端點時,你可以通過在調用後添加 ?,然後是鍵值對作為 key=value,並用 & 分隔鍵值對來傳遞參數。

  5. Azure 地圖不會立即處理此請求,因此你需要使用 location 標頭中給出的 URL 檢查上傳請求是否已完成。對此位置發出 GET 請求以查看狀態。你需要將你的訂閱密鑰添加到 location URL 的末尾,方法是添加 &subscription-key=<subscription_key> 到末尾,將 <subscription_key> 替換為你的 Azure 地圖帳戶的 API 密鑰。運行以下命令:

    curl --request GET '<location>&subscription-key=<subscription_key>'

    <location> 替換為 location 標頭的值,將 <subscription_key> 替換為你的 Azure 地圖帳戶的 API 密鑰。

  6. 檢查響應中的 status 值。如果不是 Succeeded,則等待一分鐘再試一次。

  7. 一旦狀態返回為 Succeeded,查看響應中的 resourceLocation。這包含有關 GeoJSON 對象的唯一 ID(稱為 UDID)的詳細信息。UDID 是 metadata/ 之後的值,不包括 api-version。例如,如果 resourceLocation 是:

    {
    "resourceLocation": "https://us.atlas.microsoft.com/mapData/metadata/7c3776eb-da87-4c52-ae83-caadf980323a?api-version=1.0"
    }

    那麼 UDID 將是 7c3776eb-da87-4c52-ae83-caadf980323a

    保留此 UDID 的副本,因為你將需要它來測試地理圍欄。

測試點是否在地理圍欄內

一旦多邊形上傳到 Azure 地圖,你可以測試某個點是否在地理圍欄內或外。你可以通過發出 Web API 請求來做到這一點,傳遞地理圍欄的 UDID 以及要測試的點的緯度和經度。

當你發出此請求時,你還可以傳遞一個稱為 searchBuffer 的值。這告訴地圖 API 返回結果時的準確度。原因是 GPS 並不完全準確,有時位置可能會偏差幾米甚至更多。默認的搜索緩衝區為 50 米,但你可以設置從 0 米到 500 米的值。

當從 API 調用返回結果時,結果的一部分是測量到地理圍欄邊緣最近點的 distance,如果點在地理圍欄外則為正值,如果在地理圍欄內則為負值。如果此距離小於搜索緩衝區,則返回實際距離(以米為單位),否則值為 999 或 -999。999 表示該點在地理圍欄外超過搜索緩衝區,-999 表示該點在地理圍欄內超過搜索緩衝區。

一個帶有 50 米搜索緩衝區的地理圍欄

在上圖中,地理圍欄有一個 50 米的搜索緩衝區。

  • 地理圍欄中心的一個點,遠在搜索緩衝區內,距離為 -999
  • 搜索緩衝區外的一個點,距離為 999
  • 地理圍欄內且在搜索緩衝區內的一個點,距離地理圍欄 6 米,距離為 6 米
  • 地理圍欄外且在搜索緩衝區內的一個點,距離地理圍欄 39 米,距離為 39 米

了解到地理圍欄邊緣的距離,並將其與其他信息(如其他 GPS 讀數、速度和道路數據)結合起來,在基於車輛位置做出決策時非常重要。

例如,假設 GPS 讀數顯示一輛車沿著一條最終靠近地理圍欄的道路行駛。如果單個 GPS 值不準確並將車輛放置在地理圍欄內,儘管沒有車輛通行權,那麼可以忽略它。

顯示一輛車沿著 520 經過微軟校園的 GPS 路徑,沿著道路的 GPS 讀數,除了在地理圍欄內的一個讀數

在上圖中,有一個地理圍欄覆蓋了微軟校園的一部分。紅線顯示了一輛卡車沿著 520 行駛,圓圈顯示了 GPS 讀數。大多數這些讀數是準確的,沿著 520 行駛,只有一個不準確的讀數在地理圍欄內。這個讀數不可能是正確的 - 沒有道路可以讓卡車突然從 520 轉到校園,然後再回到 520。檢查此地理圍欄的代碼需要考慮之前的讀數,然後再根據地理圍欄測試結果採取行動。

✅ 你需要檢查哪些額外數據來確定 GPS 讀數是否可以被認為是正確的?

任務 - 測試點是否在地理圍欄內

  1. 首先構建 Web API 查詢的 URL。格式為:

    https://atlas.microsoft.com/spatial/geofence/json?api-version=1.0&deviceId=gps-sensor&subscription-key=<subscription-key>&udid=<UDID>&lat=<lat>&lon=<lon>

    <subscription_key> 替換為你的 Azure 地圖帳戶的 API 密鑰。

    <UDID> 替換為上一個任務中地理圍欄的 UDID。

    <lat><lon> 替換為你要測試的緯度和經度。

    此 URL 使用 https://atlas.microsoft.com/spatial/geofence/json API 查詢使用 GeoJSON 定義的地理圍欄。它針對 1.0 API 版本。deviceId 參數是必需的,應該是緯度和經度來自的設備的名稱。

    默認搜索緩衝區為 50 米,你可以通過傳遞額外的 searchBuffer=<distance> 參數來更改此值,將 <distance> 設置為以米為單位的搜索緩衝區距離,範圍從 0 到 500。

  2. 使用 curl 對此 URL 發出 GET 請求:

    curl --request GET '<URL>'

    💁 如果你收到 BadRequest 的響應代碼,並出現以下錯誤:

    Invalid GeoJSON: All feature properties should contain a geometryId, which is used for identifying the geofence.

    那麼你的 GeoJSON 缺少帶有 geometryIdproperties 部分。你需要修復你的 GeoJSON,然後重複上述步驟重新上傳並獲取新的 UDID。

  3. 響應將包含一個 geometries 列表,每個多邊形在用於創建地理圍欄的 GeoJSON 中定義。每個幾何體都有 3 個感興趣的字段,distancenearestLatnearestLon

    {
    "geometries": [
    {
    "deviceId": "gps-sensor",
    "udId": "7c3776eb-da87-4c52-ae83-caadf980323a",
    "geometryId": "1",
    "distance": 999.0,
    "nearestLat": 47.645875,
    "nearestLon": -122.142713
    }
    ],
    "expiredGeofenceGeometryId": [],
    "invalidPeriodGeofenceGeometryId": []
    }
    • nearestLatnearestLon 是地理圍欄邊緣上最接近測試位置的點的緯度和經度。

    • distance 是測試位置到地理圍欄邊緣最近點的距離。負數表示在地理圍欄內,正數表示在地理圍欄外。此值將小於 50(默認搜索緩衝區),或 999。

  4. 使用地理圍欄內外的位置多次重複此操作。

從無伺服器代碼中使用地理圍欄

你現在可以為你的 Functions 應用添加一個新觸發器,以測試 IoT 中心 GPS 事件數據是否在地理圍欄內。

消費者組

正如你從前面的課程中記得的那樣,IoT 中心允許你重播已接收但未處理的事件。但是,如果多個觸發器連接會發生什麼?它如何知道哪一個已處理了哪些事件。

答案是它不能!相反,你可以定義多個單獨的連接來讀取事件,每個連接都可以管理未讀消息的重播。這些稱為 消費者組。當你連接到端點時,你可以指定要連接的消費者組。應用程序的每個組件將連接到不同的消費者組

一個 IoT 中心有 3 個消費者組將相同的消息分發給 3 個不同的 Functions 應用

理論上,每個消費者組最多可以連接 5 個應用程序,並且它們都會在消息到達時接收消息。最佳實踐是每個消費者組只有一個應用程序訪問,以避免重複消息處理,並確保在重新啟動時所有排隊的消息都能正確處理。例如,如果你在本地啟動了 Functions 應用並在雲中運行它,它們都會處理消息,導致存儲帳戶中存儲的 blob 重複。

如果你查看在前面的課程中創建的 IoT 中心觸發器的 function.json 文件,你會在事件中心觸發器綁定部分看到消費者組:

"consumerGroup": "$Default"

當你創建一個 IoT 中心時,系統會預設創建 $Default 消費者組。如果你想添加額外的觸發器,你可以使用新的消費者組來添加。

💁 在本課程中,你將使用不同的函數來測試地理圍欄,而不是用來存儲 GPS 數據的函數。這是為了展示如何使用消費者組並分離代碼,使其更易於閱讀和理解。在生產應用中,你可以有多種架構方式——將兩者放在一個函數中,使用存儲帳戶上的觸發器來運行函數以檢查地理圍欄,或使用多個函數。沒有“正確的方式”,這取決於你的應用程序的其餘部分和你的需求。

任務 - 創建一個新的消費者組

  1. 運行以下命令為你的 IoT 中心創建一個名為 geofence 的新消費者組:

    az iot hub consumer-group create --name geofence \
    --hub-name <hub_name>

    <hub_name> 替換為你用於 IoT 中心的名稱。

  2. 如果你想查看 IoT 中心的所有消費者組,運行以下命令:

    az iot hub consumer-group list --output table \
    --hub-name <hub_name>

    <hub_name> 替換為你用於 IoT 中心的名稱。這將列出所有消費者組。

    Name      ResourceGroup
    -------- ---------------
    $Default gps-sensor
    geofence gps-sensor

💁 當你在前面的課程中運行 IoT 中心事件監視器時,它連接到了 $Default 消費者組。這就是為什麼你不能同時運行事件監視器和事件觸發器。如果你想同時運行兩者,那麼你可以為所有函數應用使用其他消費者組,並保留 $Default 給事件監視器。

任務 - 創建一個新的 IoT 中心觸發器

  1. 為你在前面的課程中創建的 gps-trigger 函數應用添加一個新的 IoT 中心事件觸發器。將此函數命名為 geofence-trigger

    ⚠️ 你可以參考從項目 2,課程 5 中創建 IoT 中心事件觸發器的說明

  2. function.json 文件中配置 IoT 中心連接字符串。local.settings.json 在函數應用中的所有觸發器之間共享。

  3. 更新 function.json 文件中 consumerGroup 的值以引用新的 geofence 消費者組:

    "consumerGroup": "geofence"
  4. 你需要在此觸發器中使用你的 Azure Maps 帳戶的訂閱密鑰,因此在 local.settings.json 文件中添加一個名為 MAPS_KEY 的新條目。

  5. 運行函數應用以確保它正在連接和處理消息。來自前面課程的 iot-hub-trigger 也將運行並將 blob 上傳到存儲。

    為了避免在 blob 存儲中重複 GPS 讀數,你可以停止在雲中運行的函數應用。為此,使用以下命令:

    az functionapp stop --resource-group gps-sensor \
    --name <functions_app_name>

    <functions_app_name> 替換為你用於函數應用的名稱。

    你可以使用以下命令稍後重新啟動它:

    az functionapp start --resource-group gps-sensor \
    --name <functions_app_name>

    <functions_app_name> 替換為你用於函數應用的名稱。

任務 - 從觸發器測試地理圍欄

在本課程的早些時候,你使用 curl 查詢地理圍欄以查看某個點是否位於內部或外部。你可以從觸發器內部發出類似的網絡請求。

  1. 要查詢地理圍欄,你需要它的 UDID。在 local.settings.json 文件中添加一個名為 GEOFENCE_UDID 的新條目,並填入此值。

  2. 打開新的 geofence-trigger 觸發器中的 __init__.py 文件。

  3. 在文件頂部添加以下導入:

    import json
    import os
    import requests

    requests 包允許你進行網絡 API 調用。Azure Maps 沒有 Python SDK,你需要通過網絡 API 調用來從 Python 代碼中使用它。

  4. main 方法的開頭添加以下兩行以獲取 Maps 訂閱密鑰:

    maps_key = os.environ['MAPS_KEY']
    geofence_udid = os.environ['GEOFENCE_UDID']
  5. for event in events 循環內,添加以下內容以從每個事件中獲取緯度和經度:

    event_body = json.loads(event.get_body().decode('utf-8'))
    lat = event_body['gps']['lat']
    lon = event_body['gps']['lon']

    此代碼將事件正文中的 JSON 轉換為字典,然後從 gps 字段中提取 latlon

  6. 使用 requests 時,不需要像使用 curl 那樣構建長 URL,你可以只使用 URL 部分並將參數作為字典傳遞。添加以下代碼以定義要調用的 URL 並配置參數:

    url = 'https://atlas.microsoft.com/spatial/geofence/json'

    params = {
    'api-version': 1.0,
    'deviceId': 'gps-sensor',
    'subscription-key': maps_key,
    'udid' : geofence_udid,
    'lat' : lat,
    'lon' : lon
    }

    params 字典中的項目將匹配你使用 curl 調用網絡 API 時使用的鍵值對。

  7. 添加以下代碼行以調用網絡 API:

    response = requests.get(url, params=params)
    response_body = json.loads(response.text)

    這將使用參數調用 URL,並返回一個響應對象。

  8. 在此之下添加以下代碼:

    distance = response_body['geometries'][0]['distance']

    if distance == 999:
    logging.info('Point is outside geofence')
    elif distance > 0:
    logging.info(f'Point is just outside geofence by a distance of {distance}m')
    elif distance == -999:
    logging.info(f'Point is inside geofence')
    else:
    logging.info(f'Point is just inside geofence by a distance of {distance}m')

    此代碼假設只有一個幾何體,並從該單個幾何體中提取距離。然後根據距離記錄不同的消息。

  9. 運行此代碼。你將在日誌輸出中看到 GPS 坐標是否在地理圍欄內或外,並在點位於 50 米內時顯示距離。根據你的 GPS 傳感器的位置嘗試不同的地理圍欄,嘗試移動傳感器(例如通過手機的 WiFi 連接,或在虛擬 IoT 設備上使用不同的坐標)以查看此變化。

  10. 當你準備好時,將此代碼部署到雲中的函數應用。不要忘記部署新的應用程序設置。

    ⚠️ 你可以參考從項目 2,課程 5 中上傳應用程序設置的說明

    ⚠️ 你可以參考從項目 2,課程 5 中部署函數應用的說明

💁 你可以在 code/functions 文件夾中找到此代碼。


🚀 挑戰

在本課程中,你使用帶有單個多邊形的 GeoJSON 文件添加了一個地理圍欄。只要 properties 部分中有不同的 geometryId 值,你可以同時上傳多個多邊形。

嘗試上傳帶有多個多邊形的 GeoJSON 文件,並調整你的代碼以查找 GPS 坐標最接近或位於哪個多邊形中。

課後測驗

課後測驗

回顧與自學

作業

使用 Twilio 發送通知