Help us understand the problem. What is going on with this article?

地図表示範囲内のポイントを近い順に絞り込む(BingMapsScaleコンポーネントの改善2)

この記事は「PowerAppsでBingMapsを活用する時のお決まり計算をコンポーネントに」「BingMapsScaleコンポーネントの改善」の続きになります(大変遅くなり申し訳ありませんでした)。

はじめに

 次はいよいよ地図をタップするコンポーネントを作成します、と言いながら、もう一度だけBingMapsScaleコンポーネントの改善記事を書くことにしました。
 というのも、前回の改善で、地図表示範囲内のデータ抽出はFilter文一発で可能となったものの、残念ながらBingMapsのGetMethodではPushPinの最大数が18に制限されており、対象のデータ数が多くなると、何等かの基準で絞り込む必要があります。多くの場合は中心座標から近い順に選ぶのですが、この距離の計算が結構面倒でしたので、お決まり計算としてコンポーネントに入れてしまいました(ただし、コンポーネントとしては正式対応していないCollectionを使っています)。
また、前回記事の「気になる点3」で挙げた「タイミングによってはアウトプットが初期値で渡されてしまう」という点についても、OnReset を使うことで回避しています。

なお、サンプルプログラムのデータとして、東京都オープンデータカタログサイトに掲載されている、文京区のAED設置箇所一覧(aedsettikasyoitiran.xlsx)を使わせていただきました。ありがとうございます。

まずは出来上がりから

 コンポーネントの出来上がりですが、見た目の変化はありません。以下のカスタムプロパティを追加するだけです。
image.png

表示名 名前 プロパティの型 データ型 コメント
PointListInput PointListInput 入力 テーブル 対象とするポイントのリスト
PointListOutput PointListOutput 出力 テーブル 処理結果のポイントのリスト
MaxPoint MaxPoint 入力 数値 絞り込むポイントの数。初期値は18としておきます

 PointListInputは地図に表示させたいポイントの集合体です。これを地図に表示可能でMaxPoint以内に絞り込んで、中心からの距離が近い順にPointListOutputにセットするのが処理内容となります。

入力テーブル(PointListInput)の構造

 入力テーブルに必須な項目は以下の3項目です。これらさえあれば、引き渡すときに他の項目が付いていても問題ありません。

項目名 データ型 コメント
UniqueKey テキスト テーブル内で一意のキーです
PointLatitude 数値 緯度
PointLongitude 数値 経度

参考までに、サンプルプログラムではaedsettikasyoitiran.xlsxの「NO」をUniqueKeyとして設定しています。

cmpBingMapScale.PointListInput初期値
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に引き渡します。

cmpBingMapScale.OnReset
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
        )
    )
)

最後に作成したコレクションを出力項目に設定します。
outputsetting.jpg
以上でコンポーネントの改善は完了です。

試してみよう

 では、コンポーネントが上手く動いているか確認しましょう。丁寧に書くと長くなってしまうため、ポイントだけ記述していきます。

出来上がりイメージ

 サンプルデータとして接続したAED設置箇所一覧(aedsettikasyoitiran.xlsx)に必須項目を追加し、中心にしたいポイントとズームレベルを指定すると、右下に絞られたリスト、右上に地図が表示されるというものです。
BMSListsample1.png

事前準備

 事前準備として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に以下を設定します。

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を使えば動的なものも組み込めるようになったようですが、ここまで来たらあと一回、地図のタップ部分を作るだけなので、なんとか頑張ってみようと思います。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away