🗺️

Amazon Location ServiceのMapとplace Index+CoginitoでWebAppに地図を埋め込む。

2022/05/13に公開

はじめに

前回記事からの流れで、
Amazon Location Serviceを触りはじめます。
開発者ガイドを見ながら進めていきます。

※少し簡略化はしていますが、オリジナルな部分はほぼないです。
※あくまでもハンズオンではなく「やってみた。」ですので、暖かい目でお願いします。
※↑の手順通りやった方が理解できます。
※リソースの削除については記載していません。

開始。

Amazon Location Serviceを開きます。

左端のハンバーガーメニューを押下し、左ペインを表示させます。

以下ブロックごとにアコーディオンに纏めています。↓↓↓

Maps

Maps

左ペインからMapsを選択。

create mapボタンを押下。

↓↓
※分かり易いように「日本語に翻訳」して入力していきます。

マップの1つを選択します。



今回は立体のこれ(HERE Berlin)を選んでみます。

Tagはつけず、チェックボックスにチェックを入れボタンを押下。

次は Place indexesを触ります↓↓↓

Place indexes

Place indexes

左ペインからPlace indexesを選択。

create Place indexボタンを押下。

”test_index”としておきます。

EsriHEREを選択できます。
Mapのタイプで「HERE Berlin」を選択しているのでHEREを選択します。

※ドキュメントにこのように書いてあります。↓


データストレージオプションは一旦「No, single use only」を選んでおきます。
(本音はDBまで手を伸ばしたいですが、あれもこれもはよくないので一旦我慢します。)

Tagはつけず、チェックボックスにチェックを入れボタンを押下。

次は手順通りアプリの認証部分を作ります↓↓↓

Cognitoでアプリケーションの認証を設定

Cognito

※以下こちらのページの中段の手順です。
AmazonCognitoコンソールに移動し、「IDプールの管理」ボタンを押下。

[新しいIDプールの作成]を選択してから、IDプールの名前を入力。
※作成するプールは、前セクションで作成したALSリソースと同じアカウントおよびリージョンである必要有。

[認証されていないIDの折りたたみ可能]セクションから、[認証されていないIDへのアクセスを有効にする]にチェックを入れます。

認証フロー・認証プロバイダはスルーし、「プールの作成」ボタンを押下。

ボタンを押したらIamRoleを作成するページに遷移します。
[詳細の表示]で展開します
[ポリシードキュメントの表示]で展開。

[編集]クリック。
中身を全消しして以下に挿げ替え。
※<日本語の指示>の部分は書いてある通りに変更。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LocationReadOnly",
      "Effect": "Allow",
      "Action": [
        "geo:GetMapStyleDescriptor",
        "geo:GetMapGlyphs",
        "geo:GetMapSprites",
        "geo:GetMapTile",
        "geo:SearchPlaceIndex*"
      ],
      "Resource": [
        "<ここにさっき作ったMapのARNをコピーして貼る>",
        "<ここにさっき作ったPlace IndexのARNをコピーして貼る>"
      ]
    }
  ]
}

許可を押して完了。

↓プラットフォームから”Javascript”を選択。

※ここで表示される「AWS 認証情報の取得」から情報をコピーしてローカルに保存しておきます。

ここまで来たら、”次のステップ”ですが、
今回は自分の中では「Webアプリの作成」一択です。

Webアプリの作成

Webアプリの作成

※以下私はVSCODEで作業します。
※これから3つのファイルを作りますが全部同じディレクトリ(わからなければ同じフォルダ内)に保存します。
※これで動作問題ないと思いますが、万一以下を踏襲いただいて「うまく動作しない」場合本来の手順通りされるのをお勧めします。

テキストエディタで
①”quickstart.html”を作成します。

中身↓↓(本来の手順纏めてます。)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Quick start tutorial</title>

    <!-- Styles -->
    <link href="https://unpkg.com/maplibre-gl@1.14.0/dist/maplibre-gl.css" rel="stylesheet" />
    <link href="main.css" rel="stylesheet" />
  </head>
  
  <body>
    <header>
      <h1>Quick start tutorial</h1>
    </header>
    <main>
      <div id="map"></div>
      <aside>
        <h2>JSON Response</h2>
        <pre id="response"></pre>
      </aside>
    </main>
    <footer>This is a simple Amazon Location Service app. Pan and zoom. Click to see details about entities close to a point.</footer>
    <!-- JavaScript dependencies -->
    <script src="https://unpkg.com/maplibre-gl@1.14.0/dist/maplibre-gl.js"></script>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1030.0.min.js"></script>
    <script src="https://unpkg.com/@aws-amplify/core@3.7.0/dist/aws-amplify-core.min.js"></script>
    
    <!-- JavaScript for the app -->
    <script src="main.js"></script>
  </body>
</html>

②続いて”main.css”を作成します。

中身↓↓

* {
  box-sizing: border-box;
  font-family: Arial, Helvetica, sans-serif;
}

body {
  margin: 0;
}

header {
  background: #000000;
  padding: 0.5rem;
}

h1 {
  margin: 0;
  text-align: center;
  font-size: 1.5rem;
  color: #ffffff;
}

main {
  display: flex;
  min-height: calc(100vh - 94px);
}

#map {
  flex: 1;
}

aside {
  overflow-y: auto;
  flex: 0 0 30%;
  max-height: calc(100vh - 94px);
  box-shadow: 0 1px 1px 0 #001c244d, 1px 1px 1px 0 #001c2426, -1px 1px 1px 0 #001c2426;
  background: #f9f9f9;
  padding: 1rem;
}

h2 {
  margin: 0;
}

pre {
  white-space: pre-wrap;
  font-family: monospace;
  color: #16191f;
}

footer {
  background: #000000;
  padding: 1rem;
  color: #ffffff;
}

③最後に”main.js”を作ります。

中身↓↓
※ここも本来の手順纏めてます。
※<日本語の指示>の部分は書いてある通りに変更します。

// Use Signer from @aws-amplify/core
const { Signer } = window.aws_amplify_core;

// AWS Resources
// Cognito:
const identityPoolId = "<※ここにさっきのAWS 認証情報の取得をコピーした時のIdentityPoolIdをコピーして貼る>";

// Amazon Location Service resource names:
const mapName = "<ここにさっき作ったMapの名前をコピーして貼る>";
const placesName = "<ここにさっき作ったPlace Indexの名前をコピーして貼る>";

// Extract the region from the Identity Pool ID
AWS.config.region = identityPoolId.split(":")[0];

// Instantiate a Cognito-backed credential provider
const credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: identityPoolId,
});

// Sign requests made by MapLibre GL JS using AWS SigV4:
function transformRequest(url, resourceType) {
  if (resourceType === "Style" && !url.includes("://")) {
    // Resolve to an AWS URL
    url = `https://maps.geo.${AWS.config.region}.amazonaws.com/maps/v0/maps/${url}/style-descriptor`;
  }

  if (url.includes("amazonaws.com")) {
    // Sign AWS requests (with the signature as part of the query string)
    return {
      url: Signer.signUrl(url, {
        access_key: credentials.accessKeyId,
        secret_key: credentials.secretAccessKey,
        session_token: credentials.sessionToken,
      }),
    };
  }

  // If not amazonaws.com, falls to here without signing
  return { url };
}

// Initialize a map
async function initializeMap() {
  // Load credentials and set them up to refresh
  await credentials.getPromise();
  
  // Initialize the map
  const mlglMap = new maplibregl.Map({
    container: "map", // HTML element ID of map element
    center: [-77.03674, 38.891602], // Initial map centerpoint
    zoom: 16, // Initial map zoom
    style: mapName,
    transformRequest,
  });

  // Add navigation control to the top left of the map
  mlglMap.addControl(new maplibregl.NavigationControl(), "top-left");
  
  return mlglMap;
}

async function main() {
  // Initialize map and AWS SDK for Location Service:
  const map = await initializeMap();
  const location = new AWS.Location({credentials, region: AWS.config.region});
  
  // Variable to hold marker that will be rendered on click
  let marker;

  // On mouse click, get results:
  map.on("click", function(e) {
  // Remove any existing marker
  if(marker) {
    marker.remove();
  }

  // Render a marker on clicked point
  marker = new maplibregl.Marker()
    .setLngLat([e.lngLat.lng, e.lngLat.lat])
    .addTo(map);
    
    // Set up parameters for search call
    let params = {
      IndexName: placesName,
      Position: [e.lngLat.lng, e.lngLat.lat],
      Language: "en",
      MaxResults: "5"
    };

    // Search for results around clicked point
    location.searchPlaceIndexForPosition(params, function(err, data) {
      if (err) {
        // Alert user about an error
        alert("There was an error searching.");

        // Write JSON response error to HTML
        document.querySelector("#response").textContent = JSON.stringify(err, undefined, 2);
      } else {
        // Write JSON response data to HTML
        document.querySelector("#response").textContent = JSON.stringify(data, undefined, 2);

        // Display place label in an alert box
        alert(data.Results[0].Place.Label);      }
    });
  });
}

main();

ここでは端折ってますが、
・MapLibreGLJS
・AWSSDKバージョン2
・AWSAmplify
なんかの依存関係を記述している事でパンやズーム・データ取得・認証がうまくいっているんだそうです。(振り返りで個人的に勉強し直そうと思います。)

④全部保存出来たら”quickstart.html”ファイルをブラウザにドロップします。

⑤無事表示されました。

※ちなみにJSのこの部分↓↓を変更すれば初期の位置・画面のズーム度合いは変えられます。

    center: [-77.03674, 38.891602], // Initial map centerpoint
    zoom: 16, // Initial map zoom

[139.70628210300083, 35.66161975232981]に変更して東京のこの辺りを表示してみました。

⑥クリックするとこんなふうに表示されるようになりました。凄いです。

最後に

楽しいです。
まだ途中ですので、明日以降時間を見つけて続きをやっていきます。
読んでくださった方お時間くださって有難う御座いました。

Discussion