Today, I will show you how to build Amazon Location Service, which allows you to build location-based applications within your AWS environment using AWS Cloud Development Kit (AWS CDK) and AWS CloudFormation. I will also show examples of the recently popular CDK Migrate and AWS CloudFormation IaC generator.
AWS CDK and AWS CloudFormation allow you to code and manage your infrastructure and automate and streamline the development process. AWS CDK uses a developer-friendly programming language to facilitate component reuse. AWS CloudFormation, on the other hand, offers stable resource management and the ability to create templates using JSON and YAML. You can enjoy the benefits of building a coded and reproducible infrastructure by leveraging these tools.
For Amazon Location Service, I will use the Amazon Location SDK and API key functionality released last year to build three functions: mapping, geocoding, and routing. Also, for the "Launch of CloudFormation Support for API Keys and Resource Management」" announced this year, I verified the implementation with AWS CDK and AWS CloudFormation.
An example is presented in the following flow. You can choose either AWS CDK or AWS CloudFormation to build your resources.
- Advance Preparation
- Building an Amazon Location Service Resource: AWS CloudFormation
- Building an Amazon Location Service Resource: AWS CDK
- Checking an Amazon Location Service Resource
- Building an Amazon Location Service Application
- Introduction to CDK Migrate
- Introduction to AWS CloudFormation IaC Generator
- Summary
Advance Preparation
Install the AWS CDK environment. Installation is not required if you are using AWS CloudFormation.
Verified version at the time of writing
- node v20.0.0
- npm v9.6.4
Install the package
npm install -g aws-cdk
Check the version
cdk --version
2.127.0 (build 6c90efc)
Building an Amazon Location Service Resource: AWS CloudFormation
First, build Amazon Location Service resources with AWS CloudFormation.
The environment we created is available on GitHub. Please fork or download and use it in your environment.
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
Deploy with AWS CloudFormation
Deploy the Amazon Location Service resource with AWS CloudFormation using the created template.
AWS Management Console → AWS CloudFormation → "Create Stack".
Click on "With New Resources."
For the prerequisite, select "Template is ready." To specify a template, select "Upload a template file" and upload the file → Click "Next".
Set an arbitrary stack name, API key name, map name, geocoding name, and routing name → Click "Next".
Set the stack options as default this time → Click "Next."
Confirm settings → Click "Submit".
After a few moments, you will see that the stack has been created.
Building an Amazon Location Service Resource: AWS CDK
Next, build Amazon Location Service resources with AWS CDK.
The environment we created is available on GitHub. Please fork or download and use it in your environment.
aws-cdk-templates-showcase - location-service
File Configuration
.
├── 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
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { LocationServiceStack } from '../lib/location-service-stack';
const app = new cdk.App();
new LocationServiceStack(app, 'location-service', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
apiKeyName: 'LocationServiceApiKey',
mapName: 'LocationServiceMap',
placeIndexName: 'LocationServicePlace',
routeCalculatorName: 'LocationServiceRoute',
});
Deployment with AWS CDK
Deploy Amazon Location Service resources with AWS CDK using the project you have created.
Move the directory
cd aws-cdk-templates-showcase/location-service
Install the package
npm install
Run the following command only the first time before deploying. Run it again when you change the region.
cdk bootstrap
Deploy the project
cdk deploy
Checking an Amazon Location Service Resource
Check if the AWS CDK or AWS CloudFormation deployment is reflected.
AWS Management Console → Amazon Location Service → each resource.
Map, geocoding, and routing settings are reflected. Copy the map name, geocoding name, and routing name for use in the next application build.
The API key settings are reflected. Copy the region name and API key value for the next application build. When publishing externally, the API key referrer setting is also required.
At this point, you have completed building the Amazon Location Service environment with AWS CDK or AWS CloudFormation. Next, we will build the Amazon Location Service application.
Building an Amazon Location Service Application
Installing the Starter
Use an existing starter to build the Amazon Location Service front-end environment. This starter is configured to make simple use of the map library. You can fork, download, and install it in your environment.
MapLibre GL JS and Amazon Location Service Starter
maplibregljs-amazon-location-service-starter
Validated version at the time of writing
- node v20.0.0
- npm v9.6.4
File Configuration
.
├── 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
Set the region, API key, and map name of your deployed environment in the env file.
VITE_REGION = xxxxx
VITE_MAP_API_KEY = v1.public.xxxxx
VITE_MAP_NAME = xxxxx
Move the directory
cd maplibregljs-amazon-location-service-starter
Install the package
npm install
Start local server
npm run dev
Install Amazon Location SDK
Next, install the necessary libraries for the Amazon Location SDK. The installation will make it easier to authenticate the API and combine it with MapLibre GL JS.
client-location
Install the AWS SDK. "client-location" is an SDK that allows you to manipulate the Amazon Location Service.
npm install @aws-sdk/client-location
amazon-location-utilities-auth-helper
Install "amazon-location-utilities-auth-helper," a library that facilitates authentication with Amazon Location Service API keys and Cognito.
npm install @aws/amazon-location-utilities-auth-helper
amazon-location-utilities-datatypes
Install "amazon-location-utilities-datatypes," a library that converts Amazon Location Service responses to GeoJSON format.
npm install @aws/amazon-location-utilities-datatypes
I contributed to "amazon-location-utilities-datatypes" to add an optional feature because it was sometimes difficult to use in combination with MapLibre GL JS!
Building an Application
Finally, I will show how to build the mapping, geocoding, and routing functionality of the Amazon Location Service using API keys.
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
Set the region, API key, map name, geocoding name, and routing name of the deployed environment in an env file.
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';
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() {
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);
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);
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();
Start local server
npm run dev
You will see the map, geocoding, and routing functions of Amazon Location Service.
At this point, we have completed building the Amazon Location Service application. Next, I will introduce CDK Migrate and IaC Generator.
Introduction to CDK Migrate
CDK Migrate can be used to automatically convert AWS CloudFormation templates into AWS CDK projects. This project was also created using CDK Migrate, and most of the code was available. However, this project is a simple configuration, so there may be issues with more complex configurations.
The CDK migrate command converts the AWS CloudFormation template into an AWS CDK project.
cdk migrate --stack-name location-service --from-path ./create.yml --language typescript
Introduction to AWS CloudFormation IaC Generator
The AWS CloudFormation IaC generator allows you to create AWS CloudFormation templates and import existing resources. This comes in handy when creating and templating resources in the AWS Management Console. The CloudFormation template we created this time was helpful in some respects but had to be created anew.
AWS Management Console → AWS CloudFormation → Click "IaC Generator" → Click "Start a new scan" → After the scan is complete, click "Create template".
Select "Start from a new template" → Set a template name → Click "Next".
Select the resource you want to make into a template from the scanned resources. In this case, select a resource related to Amazon Location Service → Click "Next".
Confirm the settings → Click "Create template".
A template for the specified resource will be created.
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"
Summary
CloudFormation support for API keys and resource management in Amazon Location Service is now available, giving you more options for automating and templating your environment builds. I look forward to future updates!
Also, the AWS CDK only provides the Place Index for Amazon Location Service L2 constructs in alpha as of March 2024. So, you will need to define it in the L1 construct.
I hope this example will be helpful to those building location-based applications on AWS!
Unofficially, I distribute monthly updates of Amazon Location Service.
Monthly Amazon Location Service Updates (JPN)
Monthly Amazon Location Service Updates (ENG)
Related Articles
Use 3D map library with API key function of Amazon Location Service
Yasunori Kirimoto for AWS Heroes ・ Aug 2 '23
Trying to display an Amazon Location Service map in QGIS
Yasunori Kirimoto for AWS Heroes ・ Sep 4 '23
Amazon Location Service and AWS Amplify to Use Various Map Library
Yasunori Kirimoto for AWS Heroes ・ Jan 5 '23
References
Amazon Location Service
AWS Cloud Development Kit (AWS CDK)
AWS CloudFormation
MapLibre
Top comments (1)
Cool example - thanks for sharing!