はじめに
こんにちは、AWS DevTools Hero の桐本です。
本日は、AWS Cloud Development Kit (AWS CDK) と AWS CloudFormation を用いて、AWSの環境下で位置情報アプリケーションを構築可能な、Amazon Location Service の構築方法を紹介します。そして、最近話題の CDK Migrate や AWS CloudFormation IaCジェネレーター の例も紹介します。
AWS CDK と AWS CloudFormation を利用することで、インフラストラクチャをコードとして管理し、これにより開発プロセスを自動化し効率化することが可能です。AWS CDK はプログラミング言語を用いて、開発者に親しみやすく、コンポーネントの再利用を容易にします。AWS CloudFormation は、安定したリソース管理を実現し、JSON や YAML でテンプレートを作成できます。これらのツールを利用することで、コード化され再現可能なインフラストラクチャを構築するというメリットが得られます。
Amazon Location Service については、昨年リリースされた Amazon Location SDK と API キー機能 を利用し、マップ・ジオコーディング・ルーティングの3機能を構築します。また、今年発表された「API キーとリソース管理の CloudFormation サポートを開始」についても、AWS CDK と AWS CloudFormation での実装を検証しました。
下記の流れで例を紹介します。リソースの構築には、AWS CDK か AWS CloudFormation のどちらかを選んでください。
1. 事前準備
2. Amazon Location Service のリソース構築 : AWS CloudFormation
3. Amazon Location Service のリソース構築 : AWS CDK
4. Amazon Location Service のリソース確認
5. Amazon Location Service のアプリケーション構築
6. CDK Migrate の紹介
7. AWS CloudFormation IaC ジェネレーターの紹介
8. 各リソースの削除
9. まとめ

builders.flash メールメンバー登録
1. 事前準備
事前準備AWS CDK の環境をインストールします。今回、AWS CloudFormation を利用する方はインストール不要です。
執筆時の検証バージョン
node v20.0.0
npm v9.6.4
パッケージをインストール
パッケージをインストールします。
npm install -g aws-cdk
バージョンを確認
バージョンを確認します。
cdk --version
----------------------------------------
2.127.0 (build 6c90efc)
2. Amazon Location Service のリソース構築 : AWS CloudFormation
はじめに、AWS CloudFormation で Amazon Location Service のリソースを構築します。
GitHub で作成した環境を公開しています。ご自身の環境に fork またはダウンロードしご利用ください。
aws-cloudformation-templates-showcase - location-service
create.yml
リソースを構築します。
AWSTemplateFormatVersion: 2010-09-09
Description: Amazon Location Service Creation
Parameters:
MapName:
Description: Map Name
Type: String
PlaceIndexName:
Description: PlaceIndex Name
Type: String
RouteCalculatorName:
Description: RouteCalculator Name
Type: String
APIKeyName:
Description: APIKey Name
Type: String
Resources:
LocationServiceMap:
Type: AWS::Location::Map
Properties:
Configuration:
Style: VectorHereExplore
Description: Amazon Location Service Map
MapName: !Sub ${MapName}
PricingPlan: RequestBasedUsage
LocationServicePlaceIndex:
Type: AWS::Location::PlaceIndex
Properties:
DataSource: Here
DataSourceConfiguration:
IntendedUse: SingleUse
Description: Amazon Location Service PlaceIndex
IndexName: !Sub ${PlaceIndexName}
PricingPlan: RequestBasedUsage
LocationServiceRouteCalculator:
Type: AWS::Location::RouteCalculator
Properties:
DataSource: Here
Description: Amazon Location Service eRouteCalculator
CalculatorName: !Sub ${RouteCalculatorName}
PricingPlan: RequestBasedUsage
LocationServiceAPIKey:
Type: AWS::Location::APIKey
Properties:
Description: Amazon Location Service APIKey
KeyName: !Sub ${APIKeyName}
NoExpiry: true
Restrictions:
AllowActions: [geo:GetMap*, geo:SearchPlaceIndexForPosition, geo:CalculateRoute]
AllowResources:
- !Sub arn:aws:geo:${AWS::Region}:${AWS::AccountId}:map/${MapName}
- !Sub arn:aws:geo:${AWS::Region}:${AWS::AccountId}:place-index/${PlaceIndexName}
- !Sub arn:aws:geo:${AWS::Region}:${AWS::AccountId}:route-calculator/${RouteCalculatorName}
Outputs:
RegionName:
Description: Region Name
Value: !Sub ${AWS::Region}
MapName:
Description: Map Name
Value: !Ref LocationServiceMap
PlaceIndexName:
Description: PlaceIndex Name
Value: !Ref LocationServicePlaceIndex
RouteCalculatorName:
Description: RouteCalculator Name
Value: !Ref LocationServiceRouteCalculator
AWS CloudFormationでデプロイ
作成したテンプレートを使って AWS CloudFormationでAmazon Location Service のリソースをデプロイします。
スタックの作成

新しいリソースを使用

テンプレートファイルのアップロード

スタックの詳細を指定

スタックオプションの設定

スタック設定の確認と作成

スタック作成完了

3. Amazon Location Service のリソース構築 : AWS CDK
次に、AWS CDK で Amazon Location Service のリソースを構築します。
AWS CDKをはじめて利⽤するかたは、ワークショップ もぜひご確認ください。
GitHubで 作成した環境を公開しています。ご自身の環境に fork またはダウンロードしご利用ください。
aws-cdk-templates-showcase - location-service
ファイル構成
ファイル構成
.
├── README.md
├── bin
│ └── location-service.ts
├── cdk.json
├── jest.config.js
├── lib
│ └── location-service-stack.ts
├── package-lock.json
├── package.json
├── test
└── tsconfig.json
package.json
リソースを構築します。
{
"name": "location-service",
"version": "0.1.0",
"bin": {
"location-service": "bin/location-service.js"
},
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"test": "jest",
"cdk": "cdk"
},
"keywords": [],
"author": "Yasunori Kirimoto",
"license": "ISC",
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/node": "20.11.16",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"aws-cdk": "2.127.0",
"ts-node": "^10.9.2",
"typescript": "~5.3.3"
},
"dependencies": {
"aws-cdk-lib": "2.127.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.21"
}
}
(lib) location-service-stack.ts
リソースを構築します。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as location from 'aws-cdk-lib/aws-location';
export interface LocationServiceStackProps extends cdk.StackProps {
readonly mapName: string;
readonly placeIndexName: string;
readonly routeCalculatorName: string;
readonly apiKeyName: string;
}
/**
* Amazon Location Service Creation
*/
export class LocationServiceStack extends cdk.Stack {
public constructor(scope: Construct, id: string, props: LocationServiceStackProps) {
super(scope, id, props);
// Amazon Location Service APIKey
const locationServiceApiKey = new location.CfnAPIKey(this, 'LocationServiceAPIKey', {
description: 'Amazon Location Service APIKey',
keyName: `${props.apiKeyName!}`,
noExpiry: true,
restrictions: {
allowActions: [
'geo:GetMap*',
'geo:SearchPlaceIndexForPosition',
'geo:CalculateRoute',
],
allowResources: [
`arn:aws:geo:${this.region}:${this.account}:map/${props.mapName}`,
`arn:aws:geo:${this.region}:${this.account}:place-index/${props.placeIndexName}`,
`arn:aws:geo:${this.region}:${this.account}:route-calculator/${props.routeCalculatorName}`,
],
},
});
// Amazon Location Service Map
const locationServiceMap = new location.CfnMap(this, 'LocationServiceMap', {
configuration: {
style: 'VectorHereExplore',
},
description: 'Amazon Location Service Map',
mapName: props.mapName,
pricingPlan: 'RequestBasedUsage',
});
// Amazon Location Service Place Index
const locationServicePlaceIndex = new location.CfnPlaceIndex(this, 'LocationServicePlaceIndex', {
dataSource: 'Here',
dataSourceConfiguration: {
intendedUse: 'SingleUse',
},
description: 'Amazon Location Service PlaceIndex',
indexName: props.placeIndexName,
pricingPlan: 'RequestBasedUsage',
});
// Amazon Location Service Route Calculator
const locationServiceRouteCalculator = new location.CfnRouteCalculator(this, 'LocationServiceRouteCalculator', {
dataSource: 'Here',
description: 'Amazon Location Service eRouteCalculator',
calculatorName: props.routeCalculatorName,
pricingPlan: 'RequestBasedUsage',
});
// Outputs
new cdk.CfnOutput(this, 'CfnOutputRegionName', {
description: 'Region Name',
value: this.region,
});
new cdk.CfnOutput(this, 'CfnOutputMapName', {
description: 'Map Name',
value: locationServiceMap.ref,
});
new cdk.CfnOutput(this, 'CfnOutputPlaceIndexName', {
description: 'PlaceIndex Name',
value: locationServicePlaceIndex.ref,
});
new cdk.CfnOutput(this, 'CfnOutputRouteCalculatorName', {
description: 'RouteCalculator Name',
value: locationServiceRouteCalculator.ref,
});
}
}
(bin) location-service.ts
リソースを構築します。
AWS CDK でデプロイ
作成したプロジェクトを使って AWS CDK で Amazon Location Service のリソースをデプロイします。
ディレクトリを移動
ディレクトリを移動します。
cd aws-cdk-templates-showcase/location-service
パッケージをインストール
パッケージをインストールします。
npm install
ブートストラップ
デプロイ前に初回のみ下記コマンドを実行します。リージョンを変更した時にも実行します。
cdk bootstrap

プロジェクトをデプロイ
プロジェクトをデプロイします。
cdk deploy

4. Amazon Location Service のリソース確認
AWS CDK または AWS CloudFormation のデプロイが反映されているかを確認します。
各リソースを確認

マップ名・ジオコーディング名・ルーティング名をコピー

API キー値をコピー

環境構築完了
5. Amazon Location Service のアプリケーション構築
スターターのインストール
既存のスターターを利用し、Amazon Location Service のフロントエンド環境を構築します。このスターターは、マップライブラリをシンプルに利用できる構成になっています。ご自身の環境に fork またはダウンロードしインストールをしてください。
MapLibre GL JS & Amazon Location Service スターター

ファイル構成
ファイル構成
.
├── LICENSE
├── README.md
├── dist
│ ├── assets
│ └── index.html
├── docs
├── img
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── main.ts
│ ├── style.css
│ └── vite-env.d.ts
├── tsconfig.json
└── vite.config.ts
.env
デプロイした環境のリージョン・API キー・マップ名を env ファイルに設定します。
VITE_REGION = xxxxx
VITE_MAP_API_KEY = v1.public.xxxxx
VITE_MAP_NAME = xxxxx
ディレクトリを移動
ディレクトリを移動します。
cd maplibregljs-amazon-location-service-starter
パッケージをインストール
パッケージをインストールします。
npm install
ローカルサーバーで確認
ローカルサーバーで確認します。
npm run dev
マップ表示
マップが表示されます。

Amazon Location SDK のインストール
次に、Amazon Location SDK の必要なライブラリをインストールします。インストールすることで、API の認証や MapLibre GL JS との組み合わせが手軽になります。
client-location
AWS SDK をインストールします。"client-location" は Amazon Location Service を操作できる SDK です。
npm install @aws-sdk/client-location
amazon-location-utilities-auth-helper
"amazon-location-utilities-auth-helper" をインストールします。Amazon Location Service の API キーと Amazon Cognito の認証が手軽になるライブラリです。
npm install @aws/amazon-location-utilities-auth-helper
amazon-location-utilities-datatypes
"amazon-location-utilities-datatypes" をインストールします。Amazon Location Service のレスポンスを GeoJSON 形式に変換してくれるライブラリです。
npm install @aws/amazon-location-utilities-datatypes
amazon-location-utilities-datatypes のオプション機能追加について
"amazon-location-utilities-datatypes" について、MapLibre GL JS と組み合わせると一部利用しにくかったため、先日オプション機能を追加するコントリビュートをしました。
アプリケーションの構築
最後に、実際に Amazon Location Service のマップ・ジオコーディング・ルーティング機能を API キーを用いて構築する方法を紹介します。
package.json
ファイル内容
{
"name": "maplibregljs-amazon-location-service-starter",
"version": "4.0.0",
"description": "",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"keywords": [],
"author": "MapLibre User Group Japan",
"license": "ISC",
"devDependencies": {
"typescript": "^5.3.3",
"vite": "^5.1.1"
},
"dependencies": {
"@aws-sdk/client-location": "^3.511.0",
"@aws/amazon-location-utilities-auth-helper": "^1.0.3",
"@aws/amazon-location-utilities-datatypes": "^1.0.5",
"maplibre-gl": "^4.0.0"
}
}
.env
デプロイした環境のリージョン・API キー・マップ名・ジオコーディング名・ルーティング名を env ファイルに設定します。
VITE_REGION = xxxxx
VITE_API_KEY = v1.public.xxxxx
VITE_MAP_NAME = xxxxx
VITE_PLACE_NAME = xxxxx
VITE_ROUTE_NAME = xxxxx
main.ts
内容
import './style.css'
import 'maplibre-gl/dist/maplibre-gl.css';
import maplibregl from 'maplibre-gl';
// Amazon Location SDKを設定
import { LocationClient, SearchPlaceIndexForPositionCommand, CalculateRouteCommand } from "@aws-sdk/client-location";
import { placeToFeatureCollection, routeToFeatureCollection } from '@aws/amazon-location-utilities-datatypes';
import { withAPIKey } from '@aws/amazon-location-utilities-auth-helper';
const region = import.meta.env.VITE_REGION;
const apiKey = import.meta.env.VITE_API_KEY;
const mapName = import.meta.env.VITE_MAP_NAME;
const placeName = import.meta.env.VITE_PLACE_NAME;
const routeName = import.meta.env.VITE_ROUTE_NAME;
async function initialize() {
// APIキーの認証設定
const authHelper = await withAPIKey(apiKey);
const client = new LocationClient({
region: region,
...authHelper.getLocationClientConfig()
});
// 指定位置からリバースジオコーディング
const inputPlace = {
IndexName: placeName,
Position: [139.767, 35.681],
};
const commandPlace = new SearchPlaceIndexForPositionCommand(inputPlace);
const responsePlace = await client.send(commandPlace);
// ジオコーディングのレスポンスをGeoJSONに変換
const featureCollectionPlace = placeToFeatureCollection(responsePlace, {
flattenProperties: true
});
// 指定位置でルート検索
const inputRoute = {
CalculatorName: routeName,
DeparturePosition: [139.7558, 35.6767],
DestinationPosition: [139.8160, 35.6830],
IncludeLegGeometry: true,
};
const commandRoute = new CalculateRouteCommand(inputRoute);
const responseRoute = await client.send(commandRoute);
// ルーティングのレスポンスをGeoJSONに変換
const featureCollectionRoute = routeToFeatureCollection(responseRoute, {
flattenProperties: true
});
// マップを設定
const map = new maplibregl.Map({
container: 'map',
style: `https://maps.geo.${region}.amazonaws.com/maps/v0/maps/${mapName}/style-descriptor?key=${apiKey}`,
center: [139.767, 35.681],
zoom: 11,
});
map.addControl(
new maplibregl.NavigationControl({
visualizePitch: true,
})
);
map.on('load', function () {
// ジオコーディング結果のスタイル設定
map.addSource("search-result", {
type: "geojson",
data: featureCollectionPlace
});
map.addLayer({
'id': "search-result",
'type': 'circle',
'source': 'search-result',
'layout': {},
'paint': {
'circle-color': '#007cbf',
'circle-radius': 10
}
});
// ジオコーディング結果の情報表示
map.on('click', 'search-result', (e) => {
const coordinates = e.lngLat;
const description = e.features![0].properties['Place.Label'];
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(description)
.addTo(map);
});
map.on('mouseenter', 'search-result', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'search-result', () => {
map.getCanvas().style.cursor = '';
});
// ルーティング結果のスタイル設定
map.addSource("route-result", {
type: "geojson",
data: featureCollectionRoute
});
map.addLayer({
'id': "route-result",
'type': 'line',
'source': 'route-result',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#FF0000',
'line-width': 10,
'line-opacity': 0.5
}
});
// ルーティング結果の情報表示
map.on('click', 'route-result', (e) => {
const coordinates = e.lngLat;
const description = `${e.features?.[0]?.properties['Distance'] ?? ''}km`;
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(description)
.addTo(map);
});
map.on('mouseenter', 'route-result', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'route-result', () => {
map.getCanvas().style.cursor = '';
});
});
}
initialize();
ローカルサーバーで確認
ローカルサーバーで確認します。
アプリケーションのデモ
Amazon Location Service のマップ・ジオコーディング・ルーティング機能が表示されます。
ここまでで、Amazon Location Service のアプリケーション構築が完了しました。次に、CDK Migrate と IaC ジェネレーターについて紹介します。
6. CDK Migrate の紹介
CDK Migrate を利用することで、AWS CloudFormation のテンプレートを AWS CDK のプロジェクトに自動で変換できます。今回のプロジェクトも CDK Migrate を利用して作成し、ほとんどのコードをそのまま利用できました。ただ、このプロジェクトはシンプルな構成のため、複雑な構成になると課題はでるかもしれません。
CDK Migrate コマンドでプロジェクトを変換
cdk migrate --stack-name location-service --from-path ./create.yml --language typescript

7. AWS CloudFormation IaCジェネレーターの紹介
AWS CloudFormation IaCジェネレーター を利用することで、AWS CloudFormation テンプレートを作成し既存のリソースをインポートできます。これは、AWS マネジメントコンソールでリソースを作成してテンプレート化する際に便利になります。今回作成した CloudFormation テンプレートは、一部参考になる点がありましたが、基本的には新たに作成する必要がありました。
IaC ジェネレーターを起動

新しいテンプレートから開始

リソースを選択

「次へ」をクリック

テンプレートを作成

Generated Template
指定したリソースのテンプレートが作成されます。

テンプレート
内容
Metadata:
TemplateId: "arn:aws:cloudformation:ap-northeast-1:xxxxx:generatedTemplate/c686423c-b0d2-4d87-bfe3-ea"
Resources:
LocationRouteCalculator00SampleRouting00Myobr:
UpdateReplacePolicy: "Retain"
Type: "AWS::Location::RouteCalculator"
DeletionPolicy: "Retain"
Properties:
CalculatorName: "SampleRouting"
PricingPlan: "RequestBasedUsage"
Description: ""
Tags: []
DataSource: "Here"
LocationMap00SampleMap00QfNM7:
UpdateReplacePolicy: "Retain"
Type: "AWS::Location::Map"
DeletionPolicy: "Retain"
Properties:
PricingPlan: "RequestBasedUsage"
MapName: "SampleMap"
Configuration:
Style: "VectorHereExplore"
CustomLayers: []
Tags: []
LocationPlaceIndex00SampleGeocoding002C7JQ:
UpdateReplacePolicy: "Retain"
Type: "AWS::Location::PlaceIndex"
DeletionPolicy: "Retain"
Properties:
IndexName: "SampleGeocoding"
PricingPlan: "RequestBasedUsage"
Description: ""
DataSourceConfiguration:
IntendedUse: "SingleUse"
Tags: []
DataSource: "Here"
8. 各リソースの削除
最後に、各リソースの削除方法を紹介します。
AWS CloudFormation でリソース削除
AWS マネジメントコンソール → AWS CloudFormation → location-service を選択 →「削除」をクリックします。

削除をクリック

AWS CDK でリソース削除
cdk destroy コマンドでリソースを削除します。
cdk destroy

まとめ
Amazon Location Service の API キーとリソース管理の CloudFormation サポートが開始されたことで、環境構築の自動化やテンプレート化の選択肢が広がりました。今後のアップデートも楽しみです !
また、AWS CDK では Amazon Location Service の L2 コンストラクトは、2024 年 3 月現在 アルファ版 で Place Index のみが提供されています。そのため、基本的には L1 コンストラクトでの定義が必要になります。
今回の例が、AWS で位置情報アプリケーションを構築しようと考えている方々の参考になれば幸いです !
非公式ではありますが、Amazon Location Service のアップデート情報を毎月配信しています。
Monthly Amazon Location Service Updates (JPN)
Monthly Amazon Location Service Updates (ENG)
筆者プロフィール
桐本 靖規
Co-Founder and COO of MIERUNE
AWS DevTools Hero | Amplify Japan User Group | MapLibre User Group Japan |
MapLibre Voting Member | OSGeo Charter Member | Owner of dayjournal
2004 年から位置情報分野に携わり、2016 年に MIERUNE を共同創業。独自のカルチャーを持つプロフェッショナルなチーム作りや、プロダクト成功のための組織マネジメントに注力し日々模索中。個人活動では、オープンソースへの貢献や、コミュニティの運営メンバーとしてカンファレンスやワークショップを開催。専門は GIS (Geographic Information System) と FOSS4G (Free and OpenSource Software for Geospatial)。AWS と位置情報技術の組み合わせを日々模索中。
好きな AWS サービス: Amazon Location Service / AWS Amplify
Twitter: @dayjournal_nori
GitHub: @dayjournal
LinkedIn: @YasunoriKirimoto

Did you find what you were looking for today?
Let us know so we can improve the quality of the content on our pages