Azure Digital Twins データを Azure Maps 屋内マップに統合する

この記事では、Azure Digital Twins データを使用して、Azure Maps の "屋内マップ" に表示される情報を更新する方法について説明します。 Azure Digital Twins は IoT デバイスの関係のグラフを格納し、デバイス データを異なるエンドポイントにルーティングするため、マップ上の情報オーバーレイを更新するための優れたサービスです。

このガイドの内容は次のとおりです。

  1. Azure Functions の関数にツイン更新イベントを送信するように、Azure Digital Twins インスタンスを構成する。
  2. Azure Maps 屋内マップの地物状態セットを更新するために関数を作成する。
  3. マップ ID と地物状態セット ID を Azure Digital Twins のグラフに格納する。

作業の開始

このセクションでは、この記事で紹介する情報の背景を説明します。

前提条件

この記事を読み進める前に、Azure Digital Twins と Azure Maps の各リソースを設定することから始めてください。

  • Azure Digital Twins の場合: エンド ツー エンド ソリューションの接続に関するページの手順に従って、サンプル ツイン グラフとシミュレーションされたデータ フローを使用して Azure Digital Twins インスタンスを設定します。
    • この記事では、そのソリューションを別のエンドポイントとルートで拡張します。 また、そのチュートリアルの関数アプリに別の関数を追加します。
  • Azure Mapsの場合: 「Creator を使用して屋内マップを作成する」と「地物状態セットを作成する」の手順に従って、機能の状態を含む Azure Maps 屋内マップを作成します。
    • 地物状態セットは、部屋や機器といったデータセットの地物に割り当てられた動的なプロパティ (状態) のコレクションです。 上記の Azure Maps の手順では、地物状態セットには、マップに表示される部屋の状態が格納されています。
    • Azure Maps のサブスクリプション キー、地物状態セット ID、および mapConfiguration が必要です。

トポロジ

次の図は、このチュートリアルの屋内マップの統合要素が、より大きなエンドツーエンドの Azure Digital Twins シナリオに該当する場所を示しています。

Diagram of Azure services in an end-to-end scenario, highlighting the Indoor Maps Integration piece.

Azure Digital Twins からツインの更新通知をルーティングする

ツインの状態が更新されるたびに、Azure Digital Twins インスタンスでツイン更新イベントを生成できます。 Azure Digital Twins のエンドツーエンド ソリューションの接続に関するページ (上記のリンク) では、温度計を使用して、部屋のツインに関連付けられた温度属性を更新するシナリオについて説明しています。 このチュートリアルでは、このソリューションを拡張して、ツインからの更新通知を Azure 関数にサブスクライブし、その関数を使ってマップを更新する方法を説明します。

このパターンでは、IoT デバイスではなく、部屋のツインから直接読み取りを行います。これにより、マッピング ロジックを更新しなくても、基になるデータ ソースを温度に合わせて柔軟に変更することができます。 たとえば、複数の温度計を追加したり、別の部屋と温度計を共有するようにこの部屋を設定したりすることができます。これらはすべて、マップ ロジックを更新せずに行うことができます。

まず、すべてのツイン更新イベントを Event Grid トピックに転送するために、Azure Digital Twins でルートを作成します。

  1. 以下の CLI コマンドを使用して、Azure Digital Twins インスタンスからイベントを受信する、Event Grid トピックを作成します。

    az eventgrid topic create --resource-group <your-resource-group-name> --name <your-topic-name> --location <region>
    
  2. 以下の CLI コマンドを使用して、Event Grid トピックを Azure Digital Twins にリンクするエンドポイントを作成します。

    az dt endpoint create eventgrid --endpoint-name <Event-Grid-endpoint-name> --eventgrid-resource-group <Event-Grid-resource-group-name> --eventgrid-topic <your-Event-Grid-topic-name> --dt-name <your-Azure-Digital-Twins-instance-name>
    
  3. 以下の CLI コマンドを使用して、ツイン更新イベントをお使いのエンドポイントに送信するためのルートを Azure Digital Twins に作成します。 このコマンドの Azure Digital Twins インスタンス名のプレースホルダーには、フレンドリ名またはホスト名を使用すると、パフォーマンスを向上させることができます。

    Note

    Cloud Shell には現在、az dt routeaz dt modelaz dt twin の各コマンド グループに影響する既知の問題があります。

    解決するには、コマンドを実行する前に Cloud Shell で az login を実行するか、Cloud Shell ではなくローカル CLI を使用します。 この件の詳細については、「Azure Digital Twins の既知の問題」を参照してください。

    az dt route create --dt-name <your-Azure-Digital-Twins-instance-hostname-or-name> --endpoint-name <Event-Grid-endpoint-name> --route-name <my-route> --filter "type = 'Microsoft.DigitalTwins.Twin.Update'"
    

イベントを取得してマップを更新する Azure 関数を作成する

このセクションでは、Event Grid トピックに送信されたイベントをリッスンする関数を作成します。 この関数は、これらの更新通知を読み取り、対応する更新を Azure Maps 地物状態セットに送信し、1 つの部屋の温度を更新します。

Azure Digital Twins チュートリアルの「前提条件」では、Azure Functions の Azure Digital Twins を格納するための関数アプリを作成しました。 ここでは、関数アプリ内に新しい Event Grid によってトリガーされる Azure 関数を作成します。

関数コードを次のコードに置き換えます。 これにより、空間のツインに対する更新のみが除外され、更新された温度が読み取られ、その情報が Azure Maps に送信されるようになります。

using System;
using System.Threading.Tasks;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Azure.Messaging.EventGrid;

namespace updateMaps
{
    public static class ProcessDTUpdatetoMaps
    {
        // Read maps credentials from application settings on function startup
        private static string statesetID = Environment.GetEnvironmentVariable("statesetID");
        private static string subscriptionKey = Environment.GetEnvironmentVariable("subscription-key");
        private static HttpClient httpClient = new HttpClient();

        [FunctionName("ProcessDTUpdatetoMaps")]
        public static async Task Run([EventGridTrigger]EventGridEvent eventGridEvent, ILogger log)
        {
            JObject message = (JObject)JsonConvert.DeserializeObject(eventGridEvent.Data.ToString());
            log.LogInformation($"Reading event from twinID: {eventGridEvent.Subject}: {eventGridEvent.EventType}: {message["data"]}");

            //Parse updates to "space" twins
            if (message["data"]["modelId"].ToString() == "dtmi:contosocom:DigitalTwins:Space;1")
            {
                // Set the ID of the room to be updated in your map.
                // Replace this line with your logic for retrieving featureID.
                string featureID = "UNIT103";

                // Iterate through the properties that have changed
                foreach (var operation in message["data"]["patch"])
                {
                    if (operation["op"].ToString() == "replace" && operation["path"].ToString() == "/Temperature")
                    {
                        // Update the maps feature stateset
                        var postcontent = new JObject(
                            new JProperty(
                                "States",
                                new JArray(
                                    new JObject(
                                        new JProperty("keyName", "temperature"),
                                        new JProperty("value", operation["value"].ToString()),
                                        new JProperty("eventTimestamp", DateTime.UtcNow.ToString("s"))))));

                        var response = await httpClient.PutAsync(
                            $"https://us.atlas.microsoft.com/featurestatesets/{statesetID}/featureStates/{featureID}?api-version=2.0&subscription-key={subscriptionKey}",
                            new StringContent(postcontent.ToString()));


                        log.LogInformation(await response.Content.ReadAsStringAsync());
                    }
                }
            }
        }
    }
}

関数アプリで 2 つの環境変数を設定する必要があります。 1 つは Azure Maps のプライマリ サブスクリプション キーで、もう 1 つは Azure Maps の状態セット ID です。

az functionapp config appsettings set --name <your-function-app-name> --resource-group <your-resource-group> --settings "subscription-key=<your-Azure-Maps-primary-subscription-key>"
az functionapp config appsettings set --name <your-function-app-name> --resource-group <your-resource-group> --settings "statesetID=<your-Azure-Maps-stateset-ID>"

マップにライブ更新を表示する

ライブ更新温度を表示するには、以下の手順に従います。

  1. DeviceSimulator プロジェクトを実行し、シミュレートされた IoT データの送信を Azure Digital Twins から開始します (エンドツーエンドのソリューションの接続に関するページを参照)。 この手順の説明は、「シミュレーションを構成して実行する」にあります。
  2. Azure Maps Indoor モジュールを使用して、Azure Maps Creator で作成された屋内マップをレンダリングします。
    1. 例: カスタム スタイル: WebSDK でマップ構成を使用する (プレビュー)」にあるサンプルの屋内マップの HTML ファイルをコピーします。
    2. ローカル HTML ファイルのサブスクリプション キーmapConfigurationstatesetIDregion を実際の値で置き換えます。
    3. ブラウザーでそのファイルを開きます。

どちらのサンプルでも、互換性のある範囲で温度が送信されるので、マップには約 30 秒ごとに 121 号室の更新の色が表示されるはずです。

Screenshot of an office map showing room 121 colored orange.

Azure Digital Twins にマップ情報を格納する

これでマップ情報を更新するためのハードコードされたソリューションが完成したので、Azure Digital Twins のグラフを使用して、屋内マップの更新に必要なすべての情報を格納できます。 この情報には、各マップと場所の状態セット ID、マップ サブスクリプション ID、および地物 ID がそれぞれ含まれます。

この特定の例のソリューションには、状態セット ID とマップ サブスクリプション ID に属性を設定するための各最上レベルの空間の更新、および地物 ID を設定するための各部屋の更新が含まれます。 ツイン グラフを初期化するときにこれらの値を 1 回設定し、ツインの更新イベントごとにそれらの値に対してクエリを実行する必要があります。

ご自分のトポロジの構成によっては、これら 3 つの属性を、マップの細分性に関連付けてさまざまなレベルで格納できます。

次のステップ

ツイン グラフの情報の管理、アップグレード、および取得について詳しくは、以下の参照情報をご覧ください。