この記事は「PowerAppsでBingMapsを活用する時のお決まり計算をコンポーネントに」「BingMapsScaleコンポーネントの改善」の続きになります(大変遅くなり申し訳ありませんでした)。
はじめに
次はいよいよ地図をタップするコンポーネントを作成します、と言いながら、もう一度だけBingMapsScaleコンポーネントの改善記事を書くことにしました。
というのも、前回の改善で、地図表示範囲内のデータ抽出はFilter文一発で可能となったものの、残念ながらBingMapsのGetMethodではPushPinの最大数が18に制限されており、対象のデータ数が多くなると、何等かの基準で絞り込む必要があります。多くの場合は中心座標から近い順に選ぶのですが、この距離の計算が結構面倒でしたので、お決まり計算としてコンポーネントに入れてしまいました(ただし、コンポーネントとしては正式対応していないCollectionを使っています)。
また、前回記事の「気になる点3」で挙げた「タイミングによってはアウトプットが初期値で渡されてしまう」という点についても、OnReset を使うことで回避しています。
なお、サンプルプログラムのデータとして、東京都オープンデータカタログサイトに掲載されている、文京区のAED設置箇所一覧(aedsettikasyoitiran.xlsx)を使わせていただきました。ありがとうございます。
まずは出来上がりから
コンポーネントの出来上がりですが、見た目の変化はありません。以下のカスタムプロパティを追加するだけです。
表示名 | 名前 | プロパティの型 | データ型 | コメント |
---|---|---|---|---|
PointListInput | PointListInput | 入力 | テーブル | 対象とするポイントのリスト |
PointListOutput | PointListOutput | 出力 | テーブル | 処理結果のポイントのリスト |
MaxPoint | MaxPoint | 入力 | 数値 | 絞り込むポイントの数。初期値は18としておきます |
PointListInputは地図に表示させたいポイントの集合体です。これを地図に表示可能でMaxPoint以内に絞り込んで、中心からの距離が近い順にPointListOutputにセットするのが処理内容となります。
入力テーブル(PointListInput)の構造
入力テーブルに必須な項目は以下の3項目です。これらさえあれば、引き渡すときに他の項目が付いていても問題ありません。
項目名 | データ型 | コメント |
---|---|---|
UniqueKey | テキスト | テーブル内で一意のキーです |
PointLatitude | 数値 | 緯度 |
PointLongitude | 数値 | 経度 |
参考までに、サンプルプログラムではaedsettikasyoitiran.xlsxの「NO」をUniqueKeyとして設定しています。
Table(
{UniqueKey:"0000000004", PointLatitude:35.708014, PointLongitude:139.756669},
{UniqueKey:"0000000032", PointLatitude:35.708093, PointLongitude:139.758136},
{UniqueKey:"0000000050", PointLatitude:35.708818, PointLongitude:139.754156},
{UniqueKey:"0000000091", PointLatitude:35.707769, PointLongitude:139.758134},
{UniqueKey:"0000000092", PointLatitude:35.708093, PointLongitude:139.758136},
{UniqueKey:"0000000121", PointLatitude:35.708578, PointLongitude:139.757479},
{UniqueKey:"0000000158", PointLatitude:35.708554, PointLongitude:139.756996}
)
出力テーブル(PointListOutput)の構造
出力テーブルは、入力テーブルに以下の4項目を追加します。
項目名 | データ型 | コメント |
---|---|---|
ID | 数値 | 中心からの距離が近い順に1から番号が振られます。 |
GapX | 数値 | 中心からの経度方向のピクセル差 |
GapY | 数値 | 中心からの緯度方向のピクセル差 |
Distance | 数値 | 中心からの距離 |
処理の設定
地図の中心位置からの距離計算と絞り込みは、OnResetに記述します。
最初に地図の表示範囲にあるポイントに絞り込んだ上で、中心からの距離が小さい順に並べ替え、MaxPointで指定した行数に限定しています。
さらに付加情報として距離順のIDを振り、PointListOutputに引き渡します。
ClearCollect(
colWork_cmpBMS,
FirstN( //レコード数をMaxPointに限定する
SortByColumns( //地図の中心からの距離が小さい順に並べ替える
AddColumns( //地図の中心からのピクセル差と距離の列を付加する
Filter( //地図の表示範囲のポイントだけに絞り込む
cmpBingMapsScale.PointListInput,
PointLatitude > cmpBingMapsScale.MapLatitudeFrom,
PointLatitude < cmpBingMapsScale.MapLatitudeTo,
PointLongitude > cmpBingMapsScale.MapLongitudeFrom,
PointLongitude < cmpBingMapsScale.MapLongitudeTo
),
"GapX",
((PointLongitude - cmpBingMapsScale.Longitude) / cmpBingMapsScale.LongitudePer1000Pixels) * 1000,
"GapY",
((PointLatitude - cmpBingMapsScale.Latitude) / cmpBingMapsScale.LatitudePer1000Pixels) * 1000,
"Distance",
Sqrt(
Power(
((PointLatitude - cmpBingMapsScale.Latitude) / cmpBingMapsScale.LatitudePer1000Pixels) * 1000,
2
) + Power(
((PointLongitude - cmpBingMapsScale.Longitude) / cmpBingMapsScale.LongitudePer1000Pixels) * 1000,
2
)
) * cmpBingMapsScale.MeterPerPixel
),
"Distance",
Ascending
),
cmpBingMapsScale.MaxPoint
)
);
Clear(colWork2_cmpBMS);
ForAll(
colWork_cmpBMS,
Collect(
colWork2_cmpBMS,
{
ID: Max( //地図の中心からの距離が小さい順にIDを振る
colWork2_cmpBMS,
ID
) + 1,
UK: UniqueKey
}
)
);
ClearCollect(
colPointListOutput_cmpBMS, //出力用のコレクションを作成する
AddColumns( //絞り込んだレコードに先程計算したIDをLOOKUPする
colWork_cmpBMS,
"ID",
LookUp(
colWork2_cmpBMS,
UK = UniqueKey,
ID
)
)
)
最後に作成したコレクションを出力項目に設定します。
以上でコンポーネントの改善は完了です。
試してみよう
では、コンポーネントが上手く動いているか確認しましょう。丁寧に書くと長くなってしまうため、ポイントだけ記述していきます。
出来上がりイメージ
サンプルデータとして接続したAED設置箇所一覧(aedsettikasyoitiran.xlsx)に必須項目を追加し、中心にしたいポイントとズームレベルを指定すると、右下に絞られたリスト、右上に地図が表示されるというものです。
事前準備
事前準備としてAED設置箇所一覧(aedsettikasyoitiran.xlsx)のエクセルファイルをダウンロードし、「エクセルからインポート」を使ってデータソースとして接続しておきます(デフォルトでは「テーブル1」として接続)。また、上記コンポーネントをインポートし「cmpBingMapsScale_1」として挿入しておきます。
コントロールと変数、コレクション
コントロール名 | 種類 | 用途 |
---|---|---|
cmpBingMapsScale_1 | コンポーネント | 表示されていませんが、コンポーネントコントロールです。 |
DataTable1 | データテーブル | 入力テーブルを表示します。(テーブル1に「UniqueKey」「PointLatitude」「PointLongitude」を付加したコレクション) |
DataTable2 | データテーブル | 出力テーブルを表示します。(地図表示範囲とMaxPointに絞られ「ID」「Distance」「GapX」「GapY」を付加したコレクション) |
drpLocation | ドロップダウン | 地図の中心にするポイントを選択します。 |
drpZoomLevel | ドロップダウン | 地図のズームレベルを指定します。 |
imgMapSpace | 画像 | 地図を表示します。 |
変数名 | 種類 | 用途 |
---|---|---|
gblLatitude | 数値 | drpLocationで選択したポイントの緯度を保持します。 |
gblLongitude | 数値 | drpLocationで選択したポイントの経度を保持します。 |
コレクション名 | 用途 |
---|---|
colPointListInput | 入力テーブルとして成形したリストを保持します。 |
colPointListOutput_cmpBMS | コンポーネント内で使うコレクションで、出力テーブルを保持します。 |
colWork_cmpBMS,colWork2_cmpBMS | コンポーネント内で使う作業用のコレクションです。 |
OnStartの設定
入力テーブルを成形するために、OnStartに以下を設定します。
ClearCollect(colPointListInput,AddColumns(テーブル1,"PointLatitude",Round(Value(緯度),8),"PointLongitude",Round(Value(経度),8),"UniqueKey",NO))
コントロールの設定
cmpBingMapScale_1
プロパティ名 | 設定値 | コメント |
---|---|---|
Latitude | gblLatitude | |
Longitude | gblLongitude | |
MaxXPixels | imgMapSpace.Width | |
MaxYPixels | imgMapSpace.Height | |
MaxPoint | 16 | 初期値を18としているので、少し小さめの16にしました。 |
PointListInput | colPointListInput | |
Visible | false | |
ZoomLevel | drpZoomLevel.Selected.Value |
DataTable1
プロパティ名 | 設定値 | コメント |
---|---|---|
Items | colPointListInput | |
フィールド | 名称、UniqueKey、PointLatitude、PointLongitude | 何でも良いです |
DataTable2
プロパティ名 | 設定値 | コメント |
---|---|---|
Items | cmpBingMapsScale_1.PointListOutput | |
フィールド | ID、Distance、名称、UniqueKey | 何でも良いです |
DrpLocation
プロパティ名 | 設定値 | コメント |
---|---|---|
Items | colPointListInput | |
OnChange | Set(gblLatitude,drpLocation.Selected.PointLatitude);Set(gblLongitude,drpLocation.Selected.PointLongitude);Reset(cmpBingMapsScale_1) | 選択されたポイントの緯度経度を変数に格納し、コンポーネントをリセットします。ここで再計算が行われます。 |
DrpZoomLevel
プロパティ名 | 設定値 | コメント |
---|---|---|
Items | [15,16,17,18,19] | 使いそうなズームレベルを並べます。 |
Default | 17 | とりあえず真ん中を指定しました。 |
OnChange | Reset(cmpBingMapsScale_1) | 地図表示範囲が変わったらコンポーネントを再計算します。 |
imgMapSpace
プロパティ名 | 設定値 | コメント |
---|---|---|
Image | "https://dev.virtualearth.net/REST/v1/Imagery/Map/Road/" & gblLatitude & "," & gblLongitude & "/" & drpZoomLevel.Selected.Value & "?" & Concat(cmpBingMapsScale_1.PointListOutput,"&pp=" & PointLatitude & "," & PointLongitude & ";9;" & ID&名称) & "&ms=" & imgMapSpace.Width & "," & imgMapSpace.Height & "&c=ja-JP&Key="&YourAPIKey | 複数ポイントのPushpinを立てるのにConcat関数を使っています。";9;"はPushpinの形の指定です。詳しくはこちらを参照してください。YourAPIKeyの部分には、各自で取得したAPIキーを当てはめてください。 |
最後に
当初は一気に書くつもりだった一連の記事も(Oculus Questにハマってしまい、)半年がかりとなっています。その間にPowerAppsも進化し、地図も静止画像オンリーから、PowerApps Component Frameworkを使えば動的なものも組み込めるようになったようですが、ここまで来たらあと一回、地図のタップ部分を作るだけなので、なんとか頑張ってみようと思います。