Front-End Web & Mobile
Build a Geospatial Application with Amazon Location Service API Keys
Geospatial applications have become an integral part of our daily lives, from interactive maps to location-based services. With the increasing demand for such applications, developers require powerful and secure tools to build reliable geospatial solutions. Amazon Web Services (AWS) has been at the forefront of providing cutting-edge services, and the recent launch of Amazon Location Service specific API Keys feature and an authentication helper for Amazon Location Service brings exciting opportunities for geospatial app developers. In this blog post, we’ll explore how to leverage the the API Keys feature, the Amazon Location Service Auth Helper Library, and the Amazon Location Service Data Types Converter Library to build a feature-rich geospatial application.
New releases from Amazon Location Service
Before diving into the technical details, let’s briefly explore the latest enhancements introduced by Amazon Location Service. Amazon Location Service recently launched API Keys, which provide additional options for developers to securely access Amazon Location Service APIs. With API Keys, it’s now easier to control access to your geospatial data and services, ensuring that only authorized users interact with your application, and enabling access for a wider range of applications and tools.
Additionally, AWS released the Amazon Location Service Auth Helper Library, a valuable resource to streamline the authentication experience for developers. This library has been designed to streamline Location Service authentication use cases, making it easier to secure your data and ensure a smooth developer experience.
Amazon Location Service API Keys are unique identifiers used to authenticate and control access to the service APIs. You can manage access at a granular level with this feature, including restricting access to specific domains or specific Amazon Location Service resources.
This feature has been designed to enhance security and improve the performance of your application. Amazon Location Service API Keys allow read-only access to specific actions for users of your application. By embedding the keys within your application’s front-end, you can improve latency by removing the multiple calls associated with other authentication methods. This approach also allows you to monitor API usage and—using quotas and key rotation—to manage resource consumption and prevent abuse. The API Key feature has been integrated into the Amazon Location Service Auth Help Library, making it easier to manage access to your Places and Routes.
In this blog post, we will demonstrate the capabilities of the API Key feature and the new Auth Helper Library, and walk through the process of building a geospatial application.
Creating API Keys
Before we create our API Keys, we will create our Amazon Location Service Resources. Follow the guides for maps, places, and routes to create the resources we will be using in this blog post. Note that during creation of Amazon Location Service resources, you will be prompted to configure an API Key for each resource. Each Amazon Location Service API Key can be linked with up to five resources, so we will create one API Key and link each of our resources to the one key, so you can skip that configuration step.
Once you’ve created the Amazon Location Service Resources, we can create our API Key. In this case, we will create a single API Key that can be used with Maps, Places, and Routes.
Navigate to the Amazon Location Service Console and select API Keys.
Now select Create API key.
Give the API Key a name, and select the resources we created in the previous step.
Now we will define our permissions and other API Key configuration options.
We have the ability to define which read-only API actions our Key has access to. In this case we will give it access to specific resources within the Maps, Places and Routes APIs so we can take advantage of key features of Amazon Location Service. Since these resources do come with an associated cost, we recommend setting up billing alerts to monitor spikes in usage, as well as setting an expiration date for our API Key as part of a regular key rotation process.
Finally, if you wish to lock down your API Key for use by a specific domain, you can set a Referer domain that restricts the URLs that the key will be valid for.
Now select Create key to create our API key.
We can now view our API Key by selecting Show API key value
Now that we’ve created our API Key, we can start using it in our applications. Note in these demo code snippets, we have hardcoded the API Key as a variable. When deploying to a production environment, we recommend using AWS Secrets Manager to store and retrieve application credentials in addition to using the security features such as referrer.
Building a map application using API Keys
For this blog, we will build a simple demo that includes a map and a search box. We’ll start by displaying a map using the popular MapLibre GL JS rendering library. Because MapLibre supports providing a style URL, we don’t need to use the Auth Helper Library, and can set the API Key directly in the API endpoint as the style URL value.
We’ll start by creating an index.html file, and adding the following contents, substituting region and apiKey for your region and your API key value, and substituting mapName for the map resource you created in an earlier step.
<!-- index.html -->
<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->
<html>
<head>
<link href="https://unpkg.com/maplibre-gl@3/dist/maplibre-gl.css" rel="stylesheet" />
<style>
body { margin: 0; }
#map { height: 100vh; }
</style>
</head>
<body>
<!-- Map container -->
<div id="map" />
<!-- JavaScript dependencies -->
<script src="https://unpkg.com/maplibre-gl@3"></script>
<script>
const apiKey = "<Your API Key>"; // API key
const region = "<Your Region>"; // Region
const mapName = "<Your Map Resource>"; // Map name
// URL for style descriptor
const style = `https://maps.geo.${region}.amazonaws.com/maps/v0/maps/${mapName}/style-descriptor?key=${apiKey}`;
// Initialize the map
const map = new maplibregl.Map({
container: "map",
style,
center: [-123.1187, 49.2819],
zoom: 11,
});
map.addControl(new maplibregl.NavigationControl(), "top-left");
</script>
</body>
</html>
Save this as index.html
and open it in a browser. You should now see a map of Vancouver, BC.
Now that we’ve displayed a map, we can move onto adding a Location search widget into the application.
Adding a Location Search Box to the map
To power our search box, we will use an Amazon Location Service Place Index. Amazon Location Service Places Index allows us to forward and reverse geocode. In this example, we will use the MapLibre geocoder with Amazon Location Service.
If you would prefer to clone this application instead of copying code snippets, this code is available in the amazon-location-sample-map-with-geocoder GitHub repository.
To create our application, we will create two files in addition to our index.html file.
First, we’ll edit our index.html
to remove the map, and download our dependencies.
<!DOCTYPE html>
<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->
<html>
<head>
<meta charset="utf-8">
<title>Basic Map with Geocoder</title>
<!-- Styles -->
<link href="https://unpkg.com/maplibre-gl@3/dist/maplibre-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
}
#map {
height: 100vh;
}
</style>
</head>
<body>
<main>
<div id="map"></div>
</main>
<!-- JavaScript dependencies -->
<script src="https://unpkg.com/maplibre-gl@3"></script>
<script src="https://unpkg.com/@aws/amazon-location-client@1/dist/amazonLocationClient.js"></script>
<script src="https://unpkg.com/@aws/amazon-location-utilities-auth-helper@1/dist/amazonLocationAuthHelper.js"></script>
<!-- Load the `maplibre-gl-geocoder` plugin. -->
<script src="https://unpkg.com/@maplibre/maplibre-gl-geocoder@1/dist/maplibre-gl-geocoder.min.js"></script>
<link
rel="stylesheet"
href="https://unpkg.com/@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css"
type="text/css"
/>
<!-- JavaScript for the app -->
<script src="main.js"></script>
</body>
</html>
Next, we’ll create a new file called main.js
and paste the following code, replacing the values with your region, map, place index, and API Key you created earlier.
const { GetPlaceCommand, LocationClient, SearchPlaceIndexForSuggestionsCommand, SearchPlaceIndexForTextCommand } = amazonLocationClient;
// Amazon Location Service Resources:
const apiKey = "<Amazon Location API key>";
const mapName = "<Amazon Location Map resource name>";
const placeIndex = "<Amazon Location PlaceIndex resource name>";
const region = "<AWS Region, e.g., eu-central-1>";
// Add Geocoder control to the map via callbacks that are called by maplibre-gl-geocoder.
// forwardGeocode: required for geocoding (Amazon Location SearchPlaceIndexForText API)
// getSuggestions + searchByPlaceId: required for autosugget (Amazon Location SearchPlaceIndexForSuggestions + GetPlace APIs)
async function addGeocoder(map, authHelper, client) {
const amazonLocationGeocoderApi = {
forwardGeocode: async (config) => {
try {
// Set up command to call SearchPlaceIndexForText API
const { Results } = await client.send(new SearchPlaceIndexForTextCommand({
IndexName: placeIndex,
Text: config.query
}));
// Convert the results to Carmen GeoJSON (<link>) to be returned to the MapLibre Geocoder
const features = Results.map((result) => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: result.Place.Geometry.Point,
},
place_name: result.Place.Label,
properties: {
id: result.Place.PlaceId,
},
text: result.Place.Label,
place_type: ['place'],
center: result.Place.Geometry.Point,
}));
return { features };
} catch (error) {
console.error(`Failed to forwardGeocode with error: ${error}`);
}
},
getSuggestions: async (config) => {
try {
// Set up a command to call SearchPlaceIndexForSuggestions API;
const { Results } = await client.send(new SearchPlaceIndexForSuggestionsCommand({
IndexName: placeIndex,
Text: config.query
}));
// Iterate over data.Results and return all suggestions and their place ids
const suggestions = Results.map((result) => ({
text: result.Text,
placeId: result.PlaceId,
}));
return { suggestions };
} catch (error) {
console.error(`Failed to getSuggestions with error: ${error}`);
}
},
searchByPlaceId: async (config) => {
try {
// Set up command to call GetPlace API with a place Id of a selected suggestion
const { Place } = await client.send(new GetPlaceCommand({
IndexName: placeIndex,
PlaceId: config.query,
}));
const place = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: Place.Geometry.Point,
},
place_name: Place.Label,
text: Place.Label,
center: Place.Geometry.Point,
};
return { place };
} catch (error) {
console.error(`Failed to searchByPlaceId with error: ${error}`);
}
},
};
// Add Geocoder control to the map
map.addControl(new MaplibreGeocoder(amazonLocationGeocoderApi, { maplibregl, showResultsWhileTyping: true }));
}
// Initialize a map
async function initializeMap() {
const map = new maplibregl.Map({
container: 'map', // HTML element ID of map element
center: [-123.1187, 49.2819], // Initial map centerpoint
zoom: 16, // Initial map zoom
style: `https://maps.geo.${region}.amazonaws.com/maps/v0/maps/${mapName}/style-descriptor?key=${apiKey}`, // Defines the appearance of the map and authenticates using an API key
});
// Add navigation control to the top left of the map
map.addControl(new maplibregl.NavigationControl(), 'top-left');
return map;
}
async function main() {
// Create an authentication helper instance using an API key
const authHelper = await amazonLocationAuthHelper.withAPIKey(apiKey);
const client = new LocationClient({
region,
...authHelper.getLocationClientConfig(), // Provides configuration required to make requests to Amazon Location
});
// Initialize map and add a geocoder to it.
const map = await initializeMap();
addGeocoder(map, authHelper, client);
}
main();
Once all the files are created, we can now open our index.html
in your browser of choice and see the map with our places search in the top right corner.
We can now test the geocoder. This will demonstrate the auto-complete and forward geocoding capabilities of Amazon Location Service’s Place Index.
Understanding the Auth Library
Now that we’ve created our application. Let’s dive into the code to understand how the Auth Helper Library for Amazon Location Service works.
The first thing we do is define the authentication method. This can be using Amazon Cognito Identity Pools or the Amazon Location API Key feature. Using the API Key is usually the most effective solution for a wider audience and Amazon Cognito Identity Pools are very effective when you want to associate usage and restrict access to specific identities. Cognito Identity Pools also allow for more API actions, such as creating and deleting Amazon Location Service resources, as well as access to additional AWS resources in the same set of credentials.
For API Keys, we would establish the Auth method like this:
const authHelper = await amazonLocationAuthHelper.withAPIKey(apiKey);
If we were using Amazon Cognito Identity Pools, we would establish it like this:
const authHelper = await withIdentityPoolId(identityPoolId);
Next, when we instantiate the Amazon Location Service Client, we include the Auth Helper which provides the client with additional properties based on the type of authentication we configured in the previous step.
const client = new amazonLocationClient.LocationClient({
region,
...authHelper.getLocationClientConfig(), // Provides configuration required to make requests to Amazon Location
});
Finally, we call the Amazon Location Service API. In this very simple example using a Place Index and searching for Seattle, WA, we will use the Auth Helper and the Client to call the SearchPlaceIndexForText API.
<!-- index.html -->
<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->
<html>
<head>
</head>
<body>
<pre id="place_index_results" ></pre>
<script src="https://unpkg.com/@aws/amazon-location-client@1/dist/amazonLocationClient.js"></script>
<script src="https://unpkg.com/@aws/amazon-location-utilities-auth-helper@1/dist/amazonLocationAuthHelper.js"></script>
<script>
const Key = "<Amazon Location API key>"; // API key
const region = "<Amazon Location PlaceIndex resource name>"; // Region
const IndexName = "<AWS Region, e.g., eu-central-1>";
async function placeIndexSearch(){
const authHelper = await amazonLocationAuthHelper.withAPIKey(Key);
const { LocationClient, SearchPlaceIndexForTextCommand } = amazonLocationClient;
// Instantiate the Amazon Location Service Client using the Auth Helper configuration
const client = new LocationClient({
region,
...authHelper.getLocationClientConfig() // Provides configuration required to make requests to Amazon Location using either API Keys or Cognito
});
// Call the SearchPlaceIndexForText API using the Amazon Location client
const data = await client.send(new SearchPlaceIndexForTextCommand({
IndexName,
Text: "Seattle, WA",
MaxResults: 1,
}));
document.getElementById("place_index_results").innerHTML = JSON.stringify(data['Results'], null, 4);
}
placeIndexSearch(Key)
</script>
</body>
</html>
In this example, the result is displayed as JSON in a browser
As you can see, the new Auth Helper makes it much easier to configure authorization of Amazon Location Service resources.
Usage with Python
In addition to building front-end applications with Javascript, API Keys are supported in all languages supported by the Amazon Location SDK. The ability to use API Keys in a backend application reduces the overhead required by setting up IAM Roles or temporary credentials on the underlying infrastructure hosting your application. For example, the following example is a simple python script that takes an address on the command line, and geocodes it using API Keys. To create a simple Python application, create a new Python file and paste the following code:
#Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#SPDX-License-Identifier: MIT-0
import boto3
from botocore import UNSIGNED
from botocore.config import Config
client = boto3.client("location", region_name='<AWS Region, e.g., eu-central-1>', config=Config(signature_version=UNSIGNED))
text = input()
response = client.search_place_index_for_text(
IndexName='<Amazon Location PlaceIndex resource name>',
Key='<Amazon Location API key>',
MaxResults=1,
Text=text
)
print(response['Results'])
When running the code, enter your search term. In this case I’m searching for New York City, NY
After hitting enter, the code will provide the Amazon Location Service Place Index result for your search term.
Data Type Conversion Libraries
In addition to the Auth Helper Library, we have also released the Amazon Location Utilities – Data Types library for JavaScript. These libraries take the output from Amazon Location Service APIs and convert it into the common GeoJSON data format, as well as take input from these formats for geofence creation, place index search, and more. For this example, we’ll build a very simple app that takes input from the user, searches an Amazon Location Service Place Index, and returns a result in GeoJSON format. It then uses this sample application to display the point. To start, open a new HTML file and paste the following, replacing the region, Place Index and API keys with the values you created earlier.
<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->
<html>
<head>
<link
href="https://unpkg.com/maplibre-gl@3/dist/maplibre-gl.css"
rel="stylesheet"
/>
<style>
body {
margin: 0;
}
#map {
height: 100vh;
}
</style>
</head>
<pre id="jsonText" ></pre>
<body>
<div id="map" />
<script src="https://unpkg.com/maplibre-gl@3"></script>
<script src="https://unpkg.com/@aws/amazon-location-utilities-auth-helper@1/dist/amazonLocationAuthHelper.js"></script>
<script src="https://www.unpkg.com/@aws/amazon-location-utilities-datatypes@1/dist/amazonLocationDataConverter.js"></script>
<script src="https://unpkg.com/@aws/amazon-location-client@1/dist/amazonLocationClient.js"></script>
<script>
async function initializeMap() {
const key = "<Amazon Location API key>";
const mapName = "<Amazon Location Map resource name>";
const region = "<AWS Region, e.g., eu-central-1>";
const IndexName = "<Amazon Location PlaceIndex resource name>";
const searchTerm = prompt("Search for a Location");
// Create an authentication helper instance using credentials from Cognito
const authHelper = await amazonLocationAuthHelper.withAPIKey(key);
const client = new amazonLocationClient.LocationClient({
region,
...authHelper.getLocationClientConfig(), // Provides configuration required to make requests to Amazon Location
});
const searchResults = await client.send(
new amazonLocationClient.SearchPlaceIndexForTextCommand({
IndexName,
Text: searchTerm,
MaxResults: 1,
})
);
// Initialize the map
const map = new maplibregl.Map({
container: "map",
// Set the map centerpoint based on the geojson coordinates
center: featureCollection.features[0].geometry.coordinates,
// Initial zoom level
zoom: 14,
style: `https://maps.geo.${region}.amazonaws.com/maps/v0/maps/${mapName}/style-descriptor?key=${key}`,
});
// Add navigation controls
map.addControl(new maplibregl.NavigationControl(), "top-left");
map.on("load", () => {
// Convert search results into a GeoJSON FeatureCollection
const featureCollection = amazonLocationDataConverter.placeToFeatureCollection(searchResults);
// Add a data source containing GeoJSON produced from the Amazon Location Service Place Index output.
map.addSource("place-index-results", {
type: "geojson",
data: featureCollection,
});
// Add a new layer to visualize the points.
map.addLayer({
id: "place-index-results",
type: "circle",
source: "place-index-results",
paint: {
"circle-radius": 8,
"circle-color": "#0080ff",
},
});
map.on('click', 'place-index-results', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const description = JSON.stringify(featureCollection, null, 4);;
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(description)
.addTo(map);
});
});
}
initializeMap();
</script>
</body>
</html>
When you load the HTML page, you will receive a prompt to enter a search. Try out your local city, or a local business you like to frequent. When you click “OK” the page will load with a map and an icon highlighting the location. When you click on the marker, you will receive the GeoJSON output provided by the Data Type Conversion Library.
We can also use the Data Type Conversion Utility for routing, making it easier for developers to draw a route provided by Amazon Location Service on a map. The example is very similar to the above, this time including the Route Calculator we created earlier. To start, open a new HTML file and paste the following, replacing the region, Calculator name and API Keys with the values you created earlier. Set the DeparturePosition and DestinationPosition to draw the route.
<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->
<html>
<head>
<link
href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css"
rel="stylesheet"
/>
<style>
body {
margin: 0;
}
#map {
height: 100vh;
}
</style>
</head>
<body>
<div id="map" />
<script src="https://unpkg.com/maplibre-gl@3"></script>
<script src="https://unpkg.com/@aws/amazon-location-utilities-auth-helper@1/dist/amazonLocationAuthHelper.js"></script>
<script src="https://www.unpkg.com/@aws/amazon-location-utilities-datatypes@1/dist/amazonLocationDataConverter.js"></script>
<script src="https://unpkg.com/@aws/amazon-location-client@1/dist/amazonLocationClient.js"></script>
<script>
async function initializeMap() {
const key = "<Amazon Location API key>";
const mapName = "<Amazon Location Map resource name>";
const region = "<AWS Region, e.g., eu-central-1>";
const IndexName = "<Amazon Location PlaceIndex resource name>";
const CalculatorName = "<Amazon Location Route Calculator resource name>";
const DeparturePosition = "[Departure Longitude, Departure Latitude]"
const DestinationPosition = "[Destination Longitude, Destination Latitude]"
// Create an authentication helper instance using credentials from Cognito
const authHelper = await amazonLocationAuthHelper.withAPIKey(key);
const client = new amazonLocationClient.LocationClient({
region,
...authHelper.getLocationClientConfig(), // Provides configuration required to make requests to Amazon Location
});
const route = await client.send(
new amazonLocationClient.CalculateRouteCommand({
CalculatorName,
DeparturePosition,
DestinationPosition,
IncludeLegGeometry: true,
})
);
// Initialize the map
const map = new maplibregl.Map({
container: "map",
// Set the map centerpoint based on the geojson coordinates
center: DeparturePosition,
// Initial zoom level
zoom: 11,
style: `https://maps.geo.${region}.amazonaws.com/maps/v0/maps/${mapName}/style-descriptor?key=${key}`,
});
// Add navigation controls
map.addControl(new maplibregl.NavigationControl(), "top-left");
map.on("load", () => {
// Convert Amazon Location Service route to GeoJSON
const featureCollection = amazonLocationDataConverter.routeToFeatureCollection(route);
// Add a data source containing GeoJSON produced from the Amazon Location Service Place Index output.
map.addSource("route", {
type: "geojson",
data: featureCollection,
});
// Add a new layer to visualize the points.
map.addLayer({
id: "route",
type: "line",
source: "route",
layout: {
"line-join": "round",
"line-cap": "round",
},
paint: {
"line-color": "#00b0ff",
"line-width": 8,
},
});
});
}
initializeMap();
</script>
</body>
</html>
For more data type conversions, please visit the aws-geospatial/amazon-location-utilities-datatypes-js repository on GitHub for more details on how to use these utilities and other conversions offered.
Cleanup
Use the following links to delete map, place index, and route calculator resources in your AWS Account. To delete the API Key created as part of this blog, follow the instructions located here.
Conclusion
In summary, the new Amazon Location Service Auth helper library simplifies building geospatial applications by providing seamless integration with Amazon Location Service API Keys and Amazon Cognito Identity Pools. Using the Auth Helper library, developers can easily interface with Amazon Location Service Maps, Places, and Routes in their applications.
We’ve also given developers the ability to convert between different datatypes compatible with Amazon Location Service such as GeoJSON. Using these utilities, developers can take GeoJSON and create Geofences, or get GeoJSON outputs from Place Indexes, geofences, and routes. This eases development for common libraries such as MapLibre for geospatial applications.
For more sample applications, please visit the aws-geospatial repository hosted on GitHub and check out the location.aws.com demo site for an interactive look at the capabilities offered by Amazon Location Service.