Amazon Location Service のマップをOpenLayersを利用して表示してみた
こんにちは!DA(データアナリティクス)事業本部 インテグレーション部の大高です。
先日Amazon Location Serviceのマップを、地図表示ライブラリの「Mapbox GL JS」を利用しているサンプルコードで表示してみました。
本エントリでは、このサンプルコードを参考に、オープンソースの地図表示ライブラリであるOpenLayersを利用してマップを表示してみたいと思います。
前提と事前準備
前提条件として下記のドキュメントに記載のとおり「map」リソースが作成されている必要があります。今回は検証環境に既にexplore.map
という名前の「map」が存在していたので、これを利用しています。
また、リソースへのリクエストについてはCognitoを利用した認証設定が必要になります。今回は地図表示をするために必要となるので、下記のドキュメントに従ってIDプールを作成します。
ドキュメントの通りですが、作成時のポイントとしては以下になります。
- 「認証されていない ID に対してアクセスを有効にする」にチェックを入れて作成
- 「Identify the IAM roles to use with your new identity pool」の画面が表示される
- 「詳細を表示」をクリックする
- 「ロールの概要」の「IAM ロール」プルダウンで「新しい IAM ロールの作成」を選択
- 下の方の「Your unauthenticated identities would like access to Cognito.」であることに注意
- 「ポリシードキュメントを表示」をクリックして「編集」リンクをクリック
- ブラウザの高さがある程度ないと隠れて見えないので注意
- ポリシードキュメントは、サンプルと同様に以下のように記載する(Resourceの箇所は自分のMapリソースに合わせる)
{ "Version": "2012-10-17", "Statement": [ { "Sid": "MapsReadOnly", "Effect": "Allow", "Action": [ "geo:GetMapStyleDescriptor", "geo:GetMapGlyphs", "geo:GetMapSprites", "geo:GetMapTile" ], "Resource": "arn:aws:geo:ap-northeast-1:XXXXXXXXXXXX:map/explore.map" } ] }
なお、ドキュメントにも記載されているとおり、セキュリティを考慮して不必要なリソースへのアクセスは許可しないように十分留意してください。
今回は簡単な検証のために認証されていないIDに対してもアクセスを有効化していますが、本番利用の際には用途にあわせて設定を行う必要があります。
プロジェクトの準備
今回はOpenLayersのサイトで公開されている以下のサンプル「Mapbox-gl Layer」が参考になりそうなので、こちらをベースに作っていきます。先日試してみた公式サンプルに「mapbox-gl-js」のサンプルコードがありますので、こちらが一番親和性が高いと判断しました。
なお、サンプルコードからもわかるように、最近のOpenLayersはモジュール形式になっているのでnpm
かyarn
を利用する必要があります。
サンプルコードのmain.js
、index.html
、package.json
をフォルダ内に配置したら、下記のようにして必要なパッケージのインストールをして簡単に動作確認しておきます。
$ yarn install $ yarn start
なお、この時点ではサンプルコードの実行に必要なAPIキーは設定していないので認証エラーで地図画像はでません。
サンプルコードの修正
では、このサンプルコードをベースとして修正を加えていきます。基本的な方針としては、下記で公開されているサンプルの「mapbox-gl-js」の処理を組み込んでいく方針です。
まず、index.html
には、aws-sdkとamplifyのライブラリを読み込ませるようにします。また、地図の表示範囲もちょっと広げておきましょう。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Mapbox-gl Layer</title> <script src="https://sdk.amazonaws.com/js/aws-sdk-2.784.0.min.js"></script> <script src="https://unpkg.com/@aws-amplify/[email protected]/dist/aws-amplify-core.min.js"></script> <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer --> <script src="https://unpkg.com/elm-pep"></script> <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x --> <script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder"></script> <script src="https://unpkg.com/[email protected]/dist/mapbox-gl.js"></script> <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/mapbox-gl.css"> <style> .map { width: 100%; height: 95vh; } /* Reset font size changed by Mapbox CSS */ .map { font-size: medium; font-family: 'Quattrocento Sans', sans-serif; } </style> </head> <body> <div id="map" class="map"></div> <script src="main.js"></script> </body> </html>
次に、main.js
です。identityPoolId
とmapName
はそれぞれ利用しているものに合わせて修正します。ちょっと気をつけるポイントとしては、リクエストのパラメータに署名が必要なので、initializeMap
で async/await を利用してcredentials.getPromise()
を呼んでいるあたりになります。また、今回は地図の中心を東京にしてみました。
import "ol/ol.css"; import Layer from "ol/layer/Layer"; import Map from "ol/Map"; import View from "ol/View"; import { fromLonLat, toLonLat } from "ol/proj"; // use Signer from @aws-amplify/core const { Signer } = window.aws_amplify_core; // configuration // Cognito Identity Pool ID const identityPoolId = "<Identity Pool ID>"; // Amazon Location Service Map Name const mapName = "<Map name>"; // extract the region from the Identity Pool ID AWS.config.region = identityPoolId.split(":")[0]; // instantiate a credential provider const credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: identityPoolId, }); var center = [139.7696, 35.6796]; /** * Sign requests made by Mapbox GL 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")) { // only 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, }), }; } // don't sign return { url }; } /** * Initialize a map. */ async function initializeMap() { // load credentials and set them up to refresh await credentials.getPromise(); const mbMap = new mapboxgl.Map({ container: "map", center: center, // initial map centerpoint zoom: 10, // initial map zoom style: mapName, transformRequest, }); var mbLayer = new Layer({ render: function (frameState) { var canvas = mbMap.getCanvas(); var viewState = frameState.viewState; var visible = mbLayer.getVisible(); canvas.style.display = visible ? "block" : "none"; var opacity = mbLayer.getOpacity(); canvas.style.opacity = opacity; // adjust view parameters in mapbox var rotation = viewState.rotation; mbMap.jumpTo({ center: toLonLat(viewState.center), zoom: viewState.zoom - 1, bearing: (-rotation * 180) / Math.PI, animate: false, }); // cancel the scheduled update & trigger synchronous redraw // see https://github.com/mapbox/mapbox-gl-js/issues/7893#issue-408992184 // NOTE: THIS MIGHT BREAK IF UPDATING THE MAPBOX VERSION if (mbMap._frame) { mbMap._frame.cancel(); mbMap._frame = null; } mbMap._render(); return canvas; }, }); var map = new Map({ target: "map", view: new View({ center: fromLonLat(center), zoom: 4, }), layers: [mbLayer], }); } initializeMap();
地図を表示してみる
では、実際に地図を表示させてみましょう。
$ yarn start
ローカルサーバが立ち上がるのでhttp://localhost:1234
にアクセスして確認します。
想定どおりに表示されました!
まとめ
以上、Amazon Location Service のマップをOpenLayersを利用して表示してみました。
当初は、OpenLayersの他のサンプルをベースに試していましたが、なかなかうまくいかずに困っていました。ですが、途中で「Mapbox-gl Layer」のサンプルを見つけ、このサンプルがあったおかげで簡単に表示することができました。今回は単純に1レイヤを表示していますが、OpenLayersを利用することで任意のレイヤーを重ねることもできそうですね。
どなたかのお役に立てば幸いです。それでは!