地図上でポイントの緯度経度を補正する(PowerApps & BingMaps)


この記事は、先に投稿した「PowerAppsでBingMapsを活用する時のお決まり計算をコンポーネントに」で作ったコンポーネントの活用サンプルです。

まずは出来上がりから

実業務では、外回りで新規活動先を見つけた時に現在位置をベースに登録するのと、登録済みの位置情報が間違っていた時に修正する機能として作っていますが、ここでは現在位置をベースに地図上の+マークをスライダーで動かして、指定された位置の緯度経度を計算で取得し、新たにその緯度経度を中心とした地図を表示する形にしました。取得した緯度経度が間違っているとズレるので、正しく動いているかどうかわかる、という仕組みです(ちょっと無理があるか)。
長い記述になりましたが、ポイントは地図上ではピクセル数で計算して、距離や度数を出したい時にコンポーネントの出力を掛けて使う、というところです。


なお、このアプリではBingMapsの画像を取得するのにAPIキーが必要になります。お持ちでない方は、以下のページから登録・取得を行っておいてください。
Bing Maps Dev Center(https://www.bingmapsportal.com/)

作ってみよう

コンポーネントの読み込み

コンポーネントを読み込むには、新規アプリを開いた後で「ファイル」-「アプリの設定」-「詳細設定」-「コンポーネント」をオンにする必要があります。
設定したら、「挿入」メニューで「コンポーネント」というタブが現れるので、そこから「コンポーネントのインポート」を選び、前回の記事で保存したコンポーネントを選択します。
コンポーネントが読み込まれたら、「挿入」-「コンポーネント」から読み込んだコンポーネントを選び画面に挿入します。

ここではコンポーネントの名前が「Component1」だったので挿入されたコンポーネントは「Component1_1」になりました。

コントロールと変数

コントロール一覧

番号 コントロール名 種類 用途
0 Component1_1 コンポーネント 画面上表示されていませんが、コンポーネントコントロールです。
1 icoSetLocation アイコン 現在位置を使って表示をリセットします。
2 lblBaseLatitude ラベル 地図中央の緯度が表示されます
3 lblBaseLongitude ラベル 地図中央の経度が表示されます
4 MapSizeX テキスト入力 地図の幅(ピクセル)。あちこちから参照します。
5 MapSizeY テキスト入力 地図の高さ(ピクセル)。あちこちから参照します。
6 drpZoom ドロップダウン 地図のズームレベル。地図表示とコンポーネントに使います。
7 Image1 画像 地図を表示します。
8 SliderX スライダー 地図の座標と連動し、+マーカーを動かします。
9 SliderY スライダー 地図の座標と連動し、+マーカーを動かします。
10 icoMarker アイコン 移動先を示します。
11 btnChangeBaseLocation ボタン +マーカーの位置を新たな中央座標として表示をリセットします。
12 lblMovedPixelsLat ラベル +マーカーが中央から緯度方向に何ピクセル動いたか表示します。
13 lblMovedPixelsLon ラベル +マーカーが中央から経度方向に何ピクセル動いたか表示します。
14 lblMovedMeterLat ラベル +マーカーが中央から緯度方向に何m動いたか表示します。
15 lblMovedMeterLon ラベル +マーカーが中央から経度方向に何m動いたか表示します。
16 lblMovedDegreeLat ラベル +マーカーが中央から緯度方向に何度動いたか表示します。
17 lblMovedDegreeLon ラベル +マーカーが中央から経度方向に何度動いたか表示します。

変数一覧

変数名 変数の種類 用途
baseLatitude ローカル変数 地図中央の緯度を保持します。
baseLongitude ローカル変数 地図中央の経度を保持します。

コントロールの設定

0. Component1_1

プロパティ名 設定値 コメント
Latitude baseLatitude 変数に設定した緯度を基準にします。
ZoomLevel drpZoom.Selected.Value 変数に設定した緯度を基準にします。
Visible false 計算結果だけを使うので非表示にします。

1. icoSetLocation

icoSetLocation.OnSelect
//現在位置を変数にセット
UpdateContext({baseLatitude: Location.Latitude, baseLongitude: Location.Longitude});
//スライダーーをリセット
Reset(SliderX);Reset(SliderY);
//移動を反映するボタンを選択
Select(btnChangeBaseLocation)

2. lblBaseLatitude - 6. DrpZoom

番号 コントロール名 プロパティ名 設定値 コメント
2 lblBaseLatitude Text baseLatitude 表示のみ
3 lblBaseLongitude Text baseLongitude 表示のみ
4 MapSizeX Default 560 任意。地図表示、マーカー表示で使います。右側にスライダーの幅を残す必要があります。
5 MapSizeY Default 350 任意。地図表示、マーカー表示で使います。
6 DrpZoom Items [15,16,17,18,19] ある程度拡大したズームレベルに限定します。
Default 17 初期値は任意ですが、ここでは17にします。

7. 画像(Image1)

ImagesにBing Mapsの地図画像を取得します。
画像取得で使うURLの作成方法は、以下のページを参照してください。
Key=の後ろの部分には、別途取得したAPIキーを設定してください。
Microsoft Docs: Get a Static Map

Image1.Image
"https://dev.virtualearth.net/REST/v1/Imagery/Map/Road/" & baseLatitude & "," & baseLongitude & "/" & drpZoom.Selected.Value & "?mapSize=" & MapSizeX & "," & MapSizeY & "&Key=YourAPIKey"
プロパティ名 設定値 コメント
ImagePosition Center 画像を拡大縮小しないで表示します。
Height MapSizeY テキスト入力コントロールに設定した値を参照します。
Width MapSizeX

8. SliderX

地図で表示されている位置情報を経度方向に移動するためのスライダーです。地図の中央を0と置きます。

プロパティ名 設定値 コメント
Default 0 現在の経度の位置を0にします。
Max RoundDown(MapSizeX/2,0) 地図の横幅の半分を、プラスの目盛りに振ります。
Min (SliderX.Max+1)-MapSizeX 地図の横幅からプラスの目盛りと0目盛りを除いた分をマイナスに振ります。
Width MapSizeX スライダーの横幅を地図に合わせます。
X Image1.X 地図に合わせます。
Y Image1.Y+Image1.Height 地図の下に合わせます。

*注意点としては、Image1の位置を変更するときは、Image1のみ動かしてください。スライダーコントロールを動かしてしまうと、せっかく設定したX,Yの数式が無くなってしまいます。

9. SliderY

地図で表示されている位置情報を緯度方向に移動するためのスライダーです。地図の中央を0と置きます。

プロパティ名 設定値 コメント
Layout Vertical スライダーを縦にします。
Default 0 現在の経度の位置を0にします。
Max RoundDown(MapSizeY/2,0) 地図の高さの半分を、プラスの目盛りに振ります。
Min (SliderY.Max+1)-MapSizeY 地図の高さからプラスの目盛りと0目盛りを除いた分をマイナスに振ります。
Height MapSizeY スライダーの縦の長さを地図に合わせます。
Width SliderX.Height スライダーの幅をSliderXの高さに合わせます。
X Image1.X+Image1.Width 地図の右に合わせます。
Y Image1.Y 地図に合わせます。

10. icoMarker

移動先の位置を表示します。

プロパティ名 設定値 コメント
Height 61 上下各30ピクセルの大きさ。
Width 61 左右も各30ピクセルの大きさ。
X Image1.X+(SliderX.Value-SliderX.Min+1)-RoundDown(icoMarker.Width/2,0) スライダーの位置にアイコンの中央を合わせます。
Y Image1.Y+(SliderY.Max-SliderY.Value+1)-RoundDown(icoMarker.Height/2,0) スライダーの位置にアイコンの中央を合わせます。

11. btnChangeBaseLocation

現在の地図中央の位置情報を移動先の位置情報に置き換えます。地図は再描画されます。

btnChangeBaseLocation.OnSelect
//位置情報を保持する変数を移動先にアップデートします。ここではラベルで計算した移動量(度数)を参照して加減しています。
UpdateContext({baseLatitude: baseLatitude+lblMovedDegreeLat, baseLongitude: baseLongitude+lblMovedDegreeLon});
//スライダーをリセットします。
Reset(SliderX);Reset(SliderY)

12. lblMovedPixelsLat - 17. lblMovedDegreeLon

番号 コントロール名 プロパティ名 設定値 コメント
12 lblMovedPixelsLat Text SliderY.Value 表示のみ
13 lblMovedPixelsLon Text SliderX.Value 表示のみ
14 lblMovedMeterLat Text SliderY*Component1_1.MeterPerPixel コンポーネントの出力を使用して、緯度方向の移動距離を表示します。
15 lblMovedMeterLon Text SliderX.Value*Component1_1.MeterPerPixel コンポーネントの出力を使用して、経度方向の移動距離を表示します。
16 lblMovedDegreeLat Text SliderY.Value*Component1_1.LatitudePer1000Pixels/1000 コンポーネントの出力を使用して、緯度の移動分を計算します。1000ピクセルあたりの度数なので、1000で割っています。
17 lblMovedDegreeLon Text SliderX.Value*Component1_1.LongitudePer1000Pixels/1000 コンポーネントの出力を使用して、経度の移動分を計算します。1000ピクセルあたりの度数なので、1000で割っています。

以上でアプリの設定は終わりです。

動かしてみよう

全て設定できたら動かしてみましょう。スライダーの移動に従って、緯度方向か経度方向の変化が、メートルや度数で表されましたでしょうか。
「移動を反映」ボタンを押すと、+マークで示されていた部分が新たに地図中央になるはずです。

最後に

以上が簡単なコンポーネントの使用例です。
スライダーを2つ動かすのは面倒ですね。
@tomikiya さんの「PowerAppsでドラッグ&ドロップを実装する(https://qiita.com/tomikiya/items/591a7b3d69c70d56c70f)」という素晴らしいアイディアをうまく使いたかったのですが、PCでは問題なかったもののiPhoneでは上手く指を認識させられず、見送りました。

次はコンポーネント使用例の第二弾として、地図に複数ポイントを表示してタッチで選択する方法とコンポーネント化をご紹介したいと思うのですが、今回以上に複雑になるので書き切れるか自信がありません(すでに今回も破綻寸前ですし)。こちらも結局は各ポイントをピクセル位置に置き換えて、そこに透明なコントロールを重ねる、というのが基本コンセプトですので、待ちきれないから自分で作るよ、という方はお試しください。