diff --git a/.doc_gen/metadata/geo-places_metadata.yaml b/.doc_gen/metadata/geo-places_metadata.yaml
new file mode 100644
index 00000000000..84ff5859c48
--- /dev/null
+++ b/.doc_gen/metadata/geo-places_metadata.yaml
@@ -0,0 +1,40 @@
+# zexi 0.4.0
+geo-places_ReverseGeocode:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - geoplaces.java2.geocode.main
+ services:
+ geo-places: {ReverseGeocode}
+geo-places_SearchText:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - geoplaces.java2.search.text.main
+ services:
+ geo-places: {SearchText}
+geo-places_SearchNearby:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - geoplaces.java2.search.near.main
+ services:
+ geo-places: {SearchNearby}
diff --git a/.doc_gen/metadata/location_metadata.yaml b/.doc_gen/metadata/location_metadata.yaml
new file mode 100644
index 00000000000..323f4c73037
--- /dev/null
+++ b/.doc_gen/metadata/location_metadata.yaml
@@ -0,0 +1,230 @@
+# zexi 0.4.0
+location_Hello:
+ title: Hello &ALlong;
+ title_abbrev: Hello &ALshort;
+ synopsis: get started using &ALlong;.
+ category: Hello
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.hello.main
+ services:
+ location: {ListGeofencesPaginator}
+location_CreateMap:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.create.map.main
+ services:
+ location: {CreateMap}
+location_CreateKey:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.create.key.main
+ services:
+ location: {CreateKey}
+location_CreateGeofenceCollection:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.create.collection.main
+ services:
+ location: {CreateGeofenceCollection}
+location_PutGeofence:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.put.geo.main
+ services:
+ location: {PutGeofence}
+location_CreateTracker:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.create.tracker.main
+ services:
+ location: {CreateTracker}
+location_BatchUpdateDevicePosition:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.update.device.position.main
+ services:
+ location: {BatchUpdateDevicePosition}
+location_GetDevicePosition:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.get.device.position.main
+ services:
+ location: {GetDevicePosition}
+location_CreateRouteCalculator:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.create.calculator.main
+ services:
+ location: {CreateRouteCalculator}
+location_CalculateRoute:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.calc.distance.main
+ services:
+ location: {CalculateRoute}
+location_DeleteGeofenceCollection:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.delete.collection.main
+ services:
+ location: {DeleteGeofenceCollection}
+location_DeleteKey:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.delete.key.main
+ services:
+ location: {DeleteKey}
+location_DeleteMap:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.delete.map.main
+ services:
+ location: {DeleteMap}
+location_DeleteTracker:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.delete.tracker.main
+ services:
+ location: {DeleteTracker}
+location_DeleteRouteCalculator:
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description:
+ snippet_tags:
+ - location.java2.delete.calculator.main
+ services:
+ location: {DeleteRouteCalculator}
+location_Scenario:
+ synopsis_list:
+ - Create an &ALshort; map.
+ - Create an &ALshort; API key.
+ - Display Map URL.
+ - Create a geofence collection.
+ - Store a geofence geometry.
+ - Create a tracker resource.
+ - Update the position of a device.
+ - Retrieve the most recent position update for a specified device.
+ - Create a route calculator.
+ - Determine the distance between Seattle and Vancouver.
+ - Use &ALshort; higher level APIs.
+ - Delete the &ALshort; Assets.
+ category: Basics
+ languages:
+ Java:
+ versions:
+ - sdk_version: 2
+ github: javav2/example_code/location
+ sdkguide:
+ excerpts:
+ - description: Run an interactive scenario demonstrating &ALlong; features.
+ snippet_tags:
+ - location.java2.scenario.main
+ - description: A wrapper class for &ALlong; SDK methods.
+ snippet_tags:
+ - location.java2.actions.main
+ services:
+ location: {}
diff --git a/.doc_gen/validation.yaml b/.doc_gen/validation.yaml
index 242ab71ebf2..7b750b71102 100644
--- a/.doc_gen/validation.yaml
+++ b/.doc_gen/validation.yaml
@@ -213,6 +213,7 @@ allow_list:
- "src/main/java/com/example/acm/ImportCert"
- "EnablePropagateAdditionalUserContextData"
- "StopQueryWorkloadInsightsTopContributors"
+ - "com/location/latest/APIReference/Welcome"
sample_files:
- "README.md"
- "chat_sfn_state_machine.json"
diff --git a/javav2/example_code/location/.gitignore b/javav2/example_code/location/.gitignore
new file mode 100644
index 00000000000..5ff6309b719
--- /dev/null
+++ b/javav2/example_code/location/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/javav2/example_code/location/README.md b/javav2/example_code/location/README.md
new file mode 100644
index 00000000000..bb207354df9
--- /dev/null
+++ b/javav2/example_code/location/README.md
@@ -0,0 +1,130 @@
+# Amazon Location code examples for the SDK for Java 2.x
+
+## Overview
+
+Shows how to use the AWS SDK for Java 2.x to work with Amazon Location Service.
+
+
+
+
+_Amazon Location lets you easily and securely add maps, places, routes, geofences, and trackers, to your applications._
+
+## ⚠ Important
+
+* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/).
+* Running the tests might result in charges to your AWS account.
+* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
+* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
+
+
+
+
+## Code examples
+
+### Prerequisites
+
+For prerequisites, see the [README](../../README.md#Prerequisites) in the `javav2` folder.
+
+
+
+
+
+### Get started
+
+- [Hello Amazon Location](src/main/java/com/example/location/HelloLocation.java#L18) (`ListGeofencesPaginator`)
+
+
+### Basics
+
+Code examples that show you how to perform the essential operations within a service.
+
+- [Learn the basics](src/main/java/com/example/location/scenario/LocationScenario.java)
+
+
+### Single actions
+
+Code excerpts that show you how to call individual service functions.
+
+- [BatchUpdateDevicePosition](src/main/java/com/example/location/scenario/LocationActions.java#L347)
+- [CalculateRoute](src/main/java/com/example/location/scenario/LocationActions.java#L258)
+- [CreateGeofenceCollection](src/main/java/com/example/location/scenario/LocationActions.java#L456)
+- [CreateKey](src/main/java/com/example/location/scenario/LocationActions.java#L485)
+- [CreateMap](src/main/java/com/example/location/scenario/LocationActions.java#L522)
+- [CreateRouteCalculator](src/main/java/com/example/location/scenario/LocationActions.java#L292)
+- [CreateTracker](src/main/java/com/example/location/scenario/LocationActions.java#L385)
+- [DeleteGeofenceCollection](src/main/java/com/example/location/scenario/LocationActions.java#L557)
+- [DeleteMap](src/main/java/com/example/location/scenario/LocationActions.java#L615)
+- [DeleteRouteCalculator](src/main/java/com/example/location/scenario/LocationActions.java#L674)
+- [DeleteTracker](src/main/java/com/example/location/scenario/LocationActions.java#L643)
+- [GetDevicePosition](src/main/java/com/example/location/scenario/LocationActions.java#L319)
+- [PutGeofence](src/main/java/com/example/location/scenario/LocationActions.java#L415)
+
+
+
+
+
+## Run the examples
+
+### Instructions
+
+
+
+
+
+#### Hello Amazon Location
+
+This example shows you how to get started using Amazon Location.
+
+
+#### Learn the basics
+
+This example shows you how to do the following:
+
+- Create an Amazon Location map.
+- Create an Amazon Location API key.
+- Display Map URL.
+- Create a geofence collection.
+- Store a geofence geometry.
+- Create a tracker resource.
+- Update the position of a device.
+- Retrieve the most recent position update for a specified device.
+- Create a route calculator.
+- Determine the distance between Seattle and Vancouver.
+- Use Amazon Location higher level APIs.
+- Delete the Amazon Location Assets.
+
+
+
+
+
+
+
+
+
+### Tests
+
+⚠ Running tests might result in charges to your AWS account.
+
+
+To find instructions for running these tests, see the [README](../../README.md#Tests)
+in the `javav2` folder.
+
+
+
+
+
+
+## Additional resources
+
+- [Amazon Location Developer Guide](https://docs.aws.amazon.com/location/latest/developerguide/what-is.html)
+- [Amazon Location API Reference](https://docs.aws.amazon.com/location/latest/APIReference/Welcome.html)
+- [SDK for Java 2.x Amazon Location reference](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/entityresolution/package-summary.html)
+
+
+
+
+---
+
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
diff --git a/javav2/example_code/location/pom.xml b/javav2/example_code/location/pom.xml
new file mode 100644
index 00000000000..623bdbd8055
--- /dev/null
+++ b/javav2/example_code/location/pom.xml
@@ -0,0 +1,111 @@
+
+
+ 4.0.0
+
+ org.example
+ Location
+ 1.0-SNAPSHOT
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.2
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+ ${java.version}
+ ${java.version}
+
+
+
+
+
+
+
+ software.amazon.awssdk
+ bom
+ 2.29.45
+ pom
+ import
+
+
+ org.apache.logging.log4j
+ log4j-bom
+ 2.23.1
+ pom
+ import
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.11.4
+ test
+
+
+ software.amazon.awssdk
+ secretsmanager
+
+
+ software.amazon.awssdk
+ netty-nio-client
+
+
+ software.amazon.awssdk
+ location
+
+
+ com.google.code.gson
+ gson
+ 2.10.1
+
+
+ software.amazon.awssdk
+ geoplaces
+
+
+ software.amazon.awssdk
+ sso
+
+
+ software.amazon.awssdk
+ ssooidc
+
+
+ software.amazon.awssdk
+ cloudformation
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.13
+
+
+ org.apache.logging.log4j
+ log4j-slf4j2-impl
+
+
+ org.apache.logging.log4j
+ log4j-1.2-api
+
+
+
\ No newline at end of file
diff --git a/javav2/example_code/location/src/main/java/com/example/location/HelloLocation.java b/javav2/example_code/location/src/main/java/com/example/location/HelloLocation.java
new file mode 100644
index 00000000000..a1c9b7c4adb
--- /dev/null
+++ b/javav2/example_code/location/src/main/java/com/example/location/HelloLocation.java
@@ -0,0 +1,107 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.example.location;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
+import software.amazon.awssdk.core.retry.RetryMode;
+import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
+import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
+import software.amazon.awssdk.services.location.LocationAsyncClient;
+import software.amazon.awssdk.services.location.model.ListGeofencesRequest;
+import software.amazon.awssdk.services.location.paginators.ListGeofencesPublisher;
+import java.time.Duration;
+import java.util.concurrent.CompletableFuture;
+
+// snippet-start:[location.java2.hello.main]
+/**
+ * Before running this Java V2 code example, set up your development
+ * environment, including your credentials.
+ *
+ * For more information, see the following documentation topic:
+ *
+ * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
+ *
+ * In addition, you need to create a collection using the AWS Management
+ * console. For information, see the following documentation.
+ *
+ * https://docs.aws.amazon.com/location/latest/developerguide/geofence-gs.html
+
+ */
+public class HelloLocation {
+
+ private static LocationAsyncClient locationAsyncClient;
+ private static final Logger logger = LoggerFactory.getLogger(HelloLocation.class);
+
+ // This Singleton pattern ensures that only one `LocationClient`
+ // instance.
+ private static LocationAsyncClient getClient() {
+ if (locationAsyncClient == null) {
+ SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
+ .maxConcurrency(100)
+ .connectionTimeout(Duration.ofSeconds(60))
+ .readTimeout(Duration.ofSeconds(60))
+ .writeTimeout(Duration.ofSeconds(60))
+ .build();
+
+ ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
+ .apiCallTimeout(Duration.ofMinutes(2))
+ .apiCallAttemptTimeout(Duration.ofSeconds(90))
+ .retryStrategy(RetryMode.STANDARD)
+ .build();
+
+ locationAsyncClient = LocationAsyncClient.builder()
+ .httpClient(httpClient)
+ .overrideConfiguration(overrideConfig)
+ .build();
+ }
+ return locationAsyncClient;
+ }
+
+ public static void main(String[] args) {
+ final String usage = """
+
+ Usage:
+
+
+ Where:
+ collectionName - The Amazon location collection name.
+ """;
+
+ if (args.length != 1) {
+ System.out.println(usage);
+ System.exit(1);
+ }
+
+ String collectionName = args[0];
+ listGeofences(collectionName);
+ }
+
+ /**
+ * Lists geofences from a specified geofence collection asynchronously.
+ *
+ * @param collectionName The name of the geofence collection to list geofences from.
+ * @return A {@link CompletableFuture} representing the result of the asynchronous operation.
+ * The future completes when all geofences have been processed and logged.
+ */
+ public static CompletableFuture listGeofences(String collectionName) {
+ ListGeofencesRequest geofencesRequest = ListGeofencesRequest.builder()
+ .collectionName(collectionName)
+ .build();
+
+ ListGeofencesPublisher paginator = getClient().listGeofencesPaginator(geofencesRequest);
+ CompletableFuture future = paginator.subscribe(response -> {
+ if (response.entries().isEmpty()) {
+ logger.info("No Geofences were found in the collection.");
+ } else {
+ response.entries().forEach(geofence ->
+ logger.info("Geofence ID: " + geofence.geofenceId())
+ );
+ }
+ });
+ return future;
+ }
+}
+// snippet-end:[location.java2.hello.main]
diff --git a/javav2/example_code/location/src/main/java/com/example/location/scenario/LocationActions.java b/javav2/example_code/location/src/main/java/com/example/location/scenario/LocationActions.java
new file mode 100644
index 00000000000..5c765ee22e3
--- /dev/null
+++ b/javav2/example_code/location/src/main/java/com/example/location/scenario/LocationActions.java
@@ -0,0 +1,696 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.example.location.scenario;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import software.amazon.awssdk.services.geoplaces.GeoPlacesAsyncClient;
+import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
+import software.amazon.awssdk.core.retry.RetryMode;
+import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
+import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
+import software.amazon.awssdk.services.geoplaces.model.GetPlaceRequest;
+import software.amazon.awssdk.services.geoplaces.model.ReverseGeocodeRequest;
+import software.amazon.awssdk.services.geoplaces.model.ReverseGeocodeResponse;
+import software.amazon.awssdk.services.geoplaces.model.SearchNearbyRequest;
+import software.amazon.awssdk.services.geoplaces.model.SearchNearbyResponse;
+import software.amazon.awssdk.services.geoplaces.model.SearchTextRequest;
+import software.amazon.awssdk.services.location.LocationAsyncClient;
+import software.amazon.awssdk.services.location.model.AccessDeniedException;
+import software.amazon.awssdk.services.location.model.ApiKeyRestrictions;
+import software.amazon.awssdk.services.location.model.BatchUpdateDevicePositionRequest;
+import software.amazon.awssdk.services.location.model.BatchUpdateDevicePositionResponse;
+import software.amazon.awssdk.services.location.model.CalculateRouteRequest;
+import software.amazon.awssdk.services.location.model.CalculateRouteResponse;
+import software.amazon.awssdk.services.location.model.ConflictException;
+import software.amazon.awssdk.services.location.model.CreateGeofenceCollectionRequest;
+import software.amazon.awssdk.services.location.model.CreateKeyRequest;
+import software.amazon.awssdk.services.location.model.CreateMapRequest;
+import software.amazon.awssdk.services.location.model.CreateRouteCalculatorRequest;
+import software.amazon.awssdk.services.location.model.CreateRouteCalculatorResponse;
+import software.amazon.awssdk.services.location.model.CreateTrackerRequest;
+import software.amazon.awssdk.services.location.model.DeleteGeofenceCollectionRequest;
+import software.amazon.awssdk.services.location.model.DeleteKeyRequest;
+import software.amazon.awssdk.services.location.model.DeleteMapRequest;
+import software.amazon.awssdk.services.location.model.DeleteRouteCalculatorRequest;
+import software.amazon.awssdk.services.location.model.DeleteTrackerRequest;
+import software.amazon.awssdk.services.location.model.DevicePositionUpdate;
+import software.amazon.awssdk.services.location.model.GeofenceGeometry;
+import software.amazon.awssdk.services.location.model.CreateTrackerResponse;
+import software.amazon.awssdk.services.location.model.GetDevicePositionRequest;
+import software.amazon.awssdk.services.location.model.GetDevicePositionResponse;
+import software.amazon.awssdk.services.location.model.MapConfiguration;
+import software.amazon.awssdk.services.location.model.PutGeofenceRequest;
+import software.amazon.awssdk.services.location.model.PutGeofenceResponse;
+import software.amazon.awssdk.services.location.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.location.model.ServiceQuotaExceededException;
+import software.amazon.awssdk.services.location.model.ValidationException;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+// snippet-start:[location.java2.actions.main]
+public class LocationActions {
+
+ private static LocationAsyncClient locationAsyncClient;
+
+ private static GeoPlacesAsyncClient geoPlacesAsyncClient;
+ private static final Logger logger = LoggerFactory.getLogger(LocationActions.class);
+
+ // This Singleton pattern ensures that only one `LocationClient`
+ // instance is used throughout the application.
+ private LocationAsyncClient getClient() {
+ if (locationAsyncClient == null) {
+ SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
+ .maxConcurrency(100)
+ .connectionTimeout(Duration.ofSeconds(60))
+ .readTimeout(Duration.ofSeconds(60))
+ .writeTimeout(Duration.ofSeconds(60))
+ .build();
+
+ ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
+ .apiCallTimeout(Duration.ofMinutes(2))
+ .apiCallAttemptTimeout(Duration.ofSeconds(90))
+ .retryStrategy(RetryMode.STANDARD)
+ .build();
+
+ locationAsyncClient = LocationAsyncClient.builder()
+ .httpClient(httpClient)
+ .overrideConfiguration(overrideConfig)
+ .build();
+ }
+ return locationAsyncClient;
+ }
+
+ private static GeoPlacesAsyncClient getGeoPlacesClient() {
+ if (geoPlacesAsyncClient == null) {
+ SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
+ .maxConcurrency(100)
+ .connectionTimeout(Duration.ofSeconds(60))
+ .readTimeout(Duration.ofSeconds(60))
+ .writeTimeout(Duration.ofSeconds(60))
+ .build();
+
+ ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
+ .apiCallTimeout(Duration.ofMinutes(2))
+ .apiCallAttemptTimeout(Duration.ofSeconds(90))
+ .retryStrategy(RetryMode.STANDARD)
+ .build();
+
+ geoPlacesAsyncClient = GeoPlacesAsyncClient.builder()
+ .httpClient(httpClient)
+ .overrideConfiguration(overrideConfig)
+ .build();
+ }
+ return geoPlacesAsyncClient;
+ }
+
+ // snippet-start:[geoplaces.java2.search.near.main]
+
+ /**
+ * Performs a nearby places search based on the provided geographic coordinates (latitude and longitude).
+ * The method sends an asynchronous request to search for places within a 1-kilometer radius of the specified location.
+ * The results are processed and printed once the search completes successfully.
+ */
+ public CompletableFuture searchNearBy() {
+ double latitude = 37.7749; // San Francisco
+ double longitude = -122.4194;
+ List queryPosition = List.of(longitude, latitude);
+
+ // Set up the request for searching nearby places.
+ SearchNearbyRequest request = SearchNearbyRequest.builder()
+ .queryPosition(queryPosition) // Set the position
+ .queryRadius(1000L) // Radius in meters (1000 meters = 1 km).
+ .build();
+
+ return getGeoPlacesClient().searchNearby(request)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof software.amazon.awssdk.services.geoplaces.model.ValidationException) {
+ throw new CompletionException("A validation error occurred: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Error performing place search", exception);
+ }
+
+ // Process the response and print the results.
+ response.resultItems().forEach(result -> {
+ logger.info("Place Name: " + result.placeType().name());
+ logger.info("Address: " + result.address().label());
+ logger.info("Distance: " + result.distance() + " meters");
+ logger.info("-------------------------");
+ });
+ });
+ }
+ // snippet-end:[geoplaces.java2.search.near.main]
+
+ // snippet-start:[geoplaces.java2.search.text.main]
+
+ /**
+ * Searches for a place using the provided search query and prints the detailed information of the first result.
+ *
+ * @param searchQuery the search query to be used for the place search (ex, coffee shop)
+ */
+ public CompletableFuture searchText(String searchQuery) {
+ double latitude = 37.7749; // San Francisco
+ double longitude = -122.4194;
+ List queryPosition = List.of(longitude, latitude);
+
+ SearchTextRequest request = SearchTextRequest.builder()
+ .queryText(searchQuery)
+ .biasPosition(queryPosition)
+ .build();
+
+ return getGeoPlacesClient().searchText(request)
+ .thenCompose(response -> {
+ if (response.resultItems().isEmpty()) {
+ logger.info("No places found.");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ // Get the first place ID
+ String placeId = response.resultItems().get(0).placeId();
+ logger.info("Found Place with id: " + placeId);
+
+ // Fetch detailed info using getPlace
+ GetPlaceRequest getPlaceRequest = GetPlaceRequest.builder()
+ .placeId(placeId)
+ .build();
+
+ return getGeoPlacesClient().getPlace(getPlaceRequest)
+ .thenAccept(placeResponse -> {
+ logger.info("Detailed Place Information:");
+ logger.info("Name: " + placeResponse.placeType().name());
+ logger.info("Address: " + placeResponse.address().label());
+
+ if (placeResponse.foodTypes() != null && !placeResponse.foodTypes().isEmpty()) {
+ logger.info("Food Types:");
+ placeResponse.foodTypes().forEach(foodType -> {
+ logger.info(" - " + foodType);
+ });
+ } else {
+ logger.info("No food types available.");
+ }
+ logger.info("-------------------------");
+ });
+ })
+ .exceptionally(exception -> {
+ Throwable cause = exception.getCause();
+ if (cause instanceof software.amazon.awssdk.services.geoplaces.model.ValidationException) {
+ throw new CompletionException("A validation error occurred: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Error performing place search", exception);
+ });
+ }
+
+ // snippet-end:[geoplaces.java2.search.text.main]
+
+ // snippet-start:[geoplaces.java2.geocode.main]
+
+ /**
+ * Performs reverse geocoding using the AWS Geo Places API.
+ * Reverse geocoding is the process of converting geographic coordinates (latitude and longitude) to a human-readable address.
+ * This method uses the latitude and longitude of San Francisco as the input, and prints the resulting address.
+ */
+ public CompletableFuture reverseGeocode() {
+ double latitude = 37.7749; // San Francisco
+ double longitude = -122.4194;
+ logger.info("Use latitude 37.7749 and longitude -122.4194");
+
+ // AWS expects [longitude, latitude].
+ List queryPosition = List.of(longitude, latitude);
+ ReverseGeocodeRequest request = ReverseGeocodeRequest.builder()
+ .queryPosition(queryPosition)
+ .build();
+ CompletableFuture futureResponse =
+ getGeoPlacesClient().reverseGeocode(request);
+
+ return futureResponse.whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof software.amazon.awssdk.services.geoplaces.model.ValidationException) {
+ throw new CompletionException("A validation error occurred: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Error performing reverse geocoding", exception);
+ }
+
+ response.resultItems().forEach(result ->
+ logger.info("The address is: " + result.address().label())
+ );
+ });
+ }
+ // snippet-end:[geoplaces.java2.geocode.main]
+
+ // snippet-start:[location.java2.calc.distance.main]
+
+ /**
+ * Calculates the distance between two locations asynchronously.
+ *
+ * @param routeCalcName the name of the route calculator to use
+ * @return a {@link CompletableFuture} that will complete with a {@link CalculateRouteResponse} containing the distance and estimated duration of the route
+ */
+ public CompletableFuture calcDistanceAsync(String routeCalcName) {
+ // Define coordinates for Seattle, WA and Vancouver, BC.
+ List departurePosition = Arrays.asList(-122.3321, 47.6062);
+ List arrivePosition = Arrays.asList(-123.1216, 49.2827);
+
+ CalculateRouteRequest request = CalculateRouteRequest.builder()
+ .calculatorName(routeCalcName)
+ .departurePosition(departurePosition)
+ .destinationPosition(arrivePosition)
+ .travelMode("Car") // Options: Car, Truck, Walking, Bicycle
+ .distanceUnit("Kilometers") // Options: Meters, Kilometers, Miles
+ .build();
+
+ return getClient().calculateRoute(request)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The AWS resource was not found: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Failed to calculate route: " + exception.getMessage(), exception);
+ }
+ });
+ }
+ // snippet-end:[location.java2.calc.distance.main]
+
+ // snippet-start:[location.java2.create.calculator.main]
+
+ /**
+ * Creates a new route calculator with the specified name and data source.
+ *
+ * @param routeCalcName the name of the route calculator to be created
+ */
+ public CompletableFuture createRouteCalculator(String routeCalcName) {
+ String dataSource = "Esri"; // or "Here"
+ CreateRouteCalculatorRequest request = CreateRouteCalculatorRequest.builder()
+ .calculatorName(routeCalcName)
+ .dataSource(dataSource)
+ .build();
+
+ return getClient().createRouteCalculator(request)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ConflictException) {
+ throw new CompletionException("A conflict error occurred: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Failed to create route calculator: " + exception.getMessage(), exception);
+ }
+ });
+ }
+ // snippet-end:[location.java2.create.calculator.main]
+
+ // snippet-start:[location.java2.get.device.position.main]
+
+ /**
+ * Retrieves the position of a device using the provided LocationClient.
+ *
+ * @param trackerName The name of the tracker associated with the device.
+ * @param deviceId The ID of the device to retrieve the position for.
+ * @throws RuntimeException If there is an error fetching the device position.
+ */
+ public CompletableFuture getDevicePosition(String trackerName, String deviceId) {
+ GetDevicePositionRequest request = GetDevicePositionRequest.builder()
+ .trackerName(trackerName)
+ .deviceId(deviceId)
+ .build();
+
+ return getClient().getDevicePosition(request)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The AWS resource was not found: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Error fetching device position: " + exception.getMessage(), exception);
+ }
+ });
+ }
+ // snippet-end:[location.java2.get.device.position.main]
+
+ // snippet-start:[location.java2.update.device.position.main]
+
+ /**
+ * Updates the position of a device in the location tracking system.
+ *
+ * @param trackerName the name of the tracker associated with the device
+ * @param deviceId the unique identifier of the device
+ * @throws RuntimeException if an error occurs while updating the device position
+ */
+ public CompletableFuture updateDevicePosition(String trackerName, String deviceId) {
+ double latitude = 37.7749; // Example: San Francisco
+ double longitude = -122.4194;
+
+ DevicePositionUpdate positionUpdate = DevicePositionUpdate.builder()
+ .deviceId(deviceId)
+ .sampleTime(Instant.now()) // Timestamp of position update.
+ .position(Arrays.asList(longitude, latitude)) // AWS requires [longitude, latitude]
+ .build();
+
+ BatchUpdateDevicePositionRequest request = BatchUpdateDevicePositionRequest.builder()
+ .trackerName(trackerName)
+ .updates(positionUpdate)
+ .build();
+
+ CompletableFuture futureResponse = getClient().batchUpdateDevicePosition(request);
+ return futureResponse.whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The resource was not found: " + cause.getMessage(), cause);
+ } else {
+ throw new CompletionException("Error updating device position: " + exception.getMessage(), exception);
+ }
+ }
+ });
+ }
+ // snippet-end:[location.java2.update.device.position.main]
+
+ // snippet-start:[location.java2.create.tracker.main]
+
+ /**
+ * Creates a new tracker resource in your AWS account, which you can use to track the location of devices.
+ *
+ * @param trackerName the name of the tracker to be created
+ * @return a {@link CompletableFuture} that, when completed, will contain the Amazon Resource Name (ARN) of the created tracker
+ */
+ public CompletableFuture createTracker(String trackerName) {
+ CreateTrackerRequest trackerRequest = CreateTrackerRequest.builder()
+ .description("Created using the Java V2 SDK")
+ .trackerName(trackerName)
+ .positionFiltering("TimeBased") // Options: TimeBased, DistanceBased, AccuracyBased
+ .build();
+
+ return getClient().createTracker(trackerRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ConflictException) {
+ throw new CompletionException("Conflict occurred while creating tracker: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Error creating tracker: " + exception.getMessage(), exception);
+ }
+ })
+ .thenApply(CreateTrackerResponse::trackerArn); // Return only the tracker ARN
+ }
+
+ // snippet-end:[location.java2.create.tracker.main]
+
+ // snippet-start:[location.java2.put.geo.main]
+
+ /**
+ * Adds a new geofence to the specified collection.
+ *
+ * @param collectionName the name of the geofence collection to add the geofence to
+ * @param geoId the unique identifier for the geofence
+ */
+ public CompletableFuture putGeofence(String collectionName, String geoId) {
+ // Define the geofence geometry (polygon).
+ GeofenceGeometry geofenceGeometry = GeofenceGeometry.builder()
+ .polygon(List.of(
+ List.of(
+ List.of(-122.3381, 47.6101), // First point
+ List.of(-122.3281, 47.6101),
+ List.of(-122.3281, 47.6201),
+ List.of(-122.3381, 47.6201),
+ List.of(-122.3381, 47.6101) // Closing the polygon
+ )
+ ))
+ .build();
+
+ PutGeofenceRequest geofenceRequest = PutGeofenceRequest.builder()
+ .collectionName(collectionName) // Specify the collection.
+ .geofenceId(geoId) // Unique ID for the geofence.
+ .geometry(geofenceGeometry)
+ .build();
+
+ return getClient().putGeofence(geofenceRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ValidationException) {
+ throw new CompletionException("Validation error while creating geofence: " + cause.getMessage(), cause);
+ }
+ throw new CompletionException("Error creating geofence: " + exception.getMessage(), exception);
+ }
+ });
+ }
+ // snippet-end:[location.java2.put.geo.main]
+
+ // snippet-start:[location.java2.create.collection.main]
+
+ /**
+ * Creates a new geofence collection.
+ *
+ * @param collectionName the name of the geofence collection to be created
+ */
+ public CompletableFuture createGeofenceCollection(String collectionName) {
+ CreateGeofenceCollectionRequest collectionRequest = CreateGeofenceCollectionRequest.builder()
+ .collectionName(collectionName)
+ .description("Created by using the AWS SDK for Java")
+ .build();
+
+ return getClient().createGeofenceCollection(collectionRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ConflictException) {
+ throw new CompletionException("The geofence collection was not created due to ConflictException.", cause);
+ }
+ throw new CompletionException("Failed to create geofence collection: " + exception.getMessage(), exception);
+ }
+ })
+ .thenApply(response -> response.collectionArn()); // Return only the ARN
+ }
+
+
+ // snippet-end:[location.java2.create.collection.main]
+
+ // snippet-start:[location.java2.create.key.main]
+
+ /**
+ * Creates a new API key with the specified name and restrictions.
+ *
+ * @param keyName the name of the API key to be created
+ * @param mapArn the Amazon Resource Name (ARN) of the map resource to which the API key will be associated
+ * @return a {@link CompletableFuture} that completes with the Amazon Resource Name (ARN) of the created API key,
+ * or {@code null} if the operation failed
+ */
+ public CompletableFuture createKey(String keyName, String mapArn) {
+ ApiKeyRestrictions keyRestrictions = ApiKeyRestrictions.builder()
+ .allowActions("geo:GetMap*")
+ .allowResources(mapArn)
+ .build();
+
+ CreateKeyRequest request = CreateKeyRequest.builder()
+ .keyName(keyName)
+ .restrictions(keyRestrictions)
+ .noExpiry(true)
+ .build();
+
+ return getClient().createKey(request)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof AccessDeniedException) {
+ throw new CompletionException("The request was denied because of insufficient access or permissions.", cause);
+ }
+ throw new CompletionException("Failed to create API key: " + exception.getMessage(), exception);
+ }
+ })
+ .thenApply(response -> response.keyArn()); // This will never return null if the response reaches here
+ }
+
+ // snippet-end:[location.java2.create.key.main]
+
+ // snippet-start:[location.java2.create.map.main]
+
+ /**
+ * Creates a new map with the specified name and configuration.
+ *
+ * @param mapName the name of the map to be created
+ * @return a {@link CompletableFuture} that, when completed, will contain the Amazon Resource Name (ARN) of the created map
+ * @throws CompletionException if an error occurs while creating the map, such as exceeding the service quota
+ */
+ public CompletableFuture createMap(String mapName) {
+ MapConfiguration configuration = MapConfiguration.builder()
+ .style("VectorEsriNavigation")
+ .build();
+
+ CreateMapRequest mapRequest = CreateMapRequest.builder()
+ .mapName(mapName)
+ .configuration(configuration)
+ .description("A map created using the Java V2 API")
+ .build();
+
+ return getClient().createMap(mapRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ServiceQuotaExceededException) {
+ throw new CompletionException("The operation was denied because the request would exceed the maximum quota.", cause);
+ }
+ throw new CompletionException("Failed to create map: " + exception.getMessage(), exception);
+ }
+ })
+ .thenApply(response -> response.mapArn()); // Return the map ARN
+ }
+
+ // snippet-end:[location.java2.create.map.main]
+
+ // snippet-start:[location.java2.delete.collection.main]
+
+ /**
+ * Deletes a geofence collection asynchronously.
+ *
+ * @param collectionName the name of the geofence collection to be deleted
+ * @return a {@link CompletableFuture} that completes when the geofence collection has been deleted
+ */
+ public CompletableFuture deleteGeofenceCollectionAsync(String collectionName) {
+ DeleteGeofenceCollectionRequest collectionRequest = DeleteGeofenceCollectionRequest.builder()
+ .collectionName(collectionName)
+ .build();
+
+ return getClient().deleteGeofenceCollection(collectionRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The requested geofence collection was not found.", cause);
+ }
+ throw new CompletionException("Failed to delete geofence collection: " + exception.getMessage(), exception);
+ }
+ logger.info("The geofence collection {} was deleted.", collectionName);
+ })
+ .thenApply(response -> null);
+ }
+
+ // snippet-end:[location.java2.delete.collection.main]
+
+ // snippet-start:[location.java2.delete.key.main]
+
+ /**
+ * Deletes the specified key from the key-value store.
+ *
+ * @param keyName the name of the key to be deleted
+ * @return a {@link CompletableFuture} that completes when the key has been deleted
+ * @throws CompletionException if the key was not found or if an error occurred during the deletion process
+ */
+ public CompletableFuture deleteKey(String keyName) {
+ DeleteKeyRequest keyRequest = DeleteKeyRequest.builder()
+ .keyName(keyName)
+ .build();
+
+ return getClient().deleteKey(keyRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The key was not found.", cause);
+ }
+ throw new CompletionException("Failed to delete key: " + exception.getMessage(), exception);
+ }
+ logger.info("The key {} was deleted.", keyName);
+ })
+ .thenApply(response -> null);
+ }
+ // snippet-end:[location.java2.delete.key.main]
+
+ // snippet-start:[location.java2.delete.map.main]
+
+ /**
+ * Deletes a map with the specified name.
+ *
+ * @param mapName the name of the map to be deleted
+ * @return a {@link CompletableFuture} that completes when the map deletion is successful, or throws a {@link CompletionException} if an error occurs
+ */
+ public CompletableFuture deleteMap(String mapName) {
+ DeleteMapRequest mapRequest = DeleteMapRequest.builder()
+ .mapName(mapName)
+ .build();
+
+ return getClient().deleteMap(mapRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The map was not found.", cause);
+ }
+ throw new CompletionException("Failed to delete map: " + exception.getMessage(), exception);
+ }
+ logger.info("The map {} was deleted.", mapName);
+ })
+ .thenApply(response -> null);
+ }
+ // snippet-end:[location.java2.delete.map.main]
+
+ // snippet-start:[location.java2.delete.tracker.main]
+
+ /**
+ * Deletes a tracker with the specified name.
+ *
+ * @param trackerName the name of the tracker to be deleted
+ * @return a {@link CompletableFuture} that completes when the tracker has been deleted
+ * @throws CompletionException if an error occurs while deleting the tracker
+ * - if the tracker was not found, a {@link ResourceNotFoundException} is thrown wrapped in the CompletionException
+ * - if any other error occurs, a generic CompletionException is thrown with the error message
+ */
+ public CompletableFuture deleteTracker(String trackerName) {
+ DeleteTrackerRequest trackerRequest = DeleteTrackerRequest.builder()
+ .trackerName(trackerName)
+ .build();
+
+ return getClient().deleteTracker(trackerRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The tracker was not found.", cause);
+ }
+ throw new CompletionException("Failed to delete the tracker: " + exception.getMessage(), exception);
+ }
+ logger.info("The tracker {} was deleted.", trackerName);
+ })
+ .thenApply(response -> null); // Ensures CompletableFuture
+ }
+ // snippet-end:[location.java2.delete.tracker.main]
+
+ // snippet-start:[location.java2.delete.calculator.main]
+
+ /**
+ * Deletes a route calculator from the system.
+ *
+ * @param calcName the name of the route calculator to delete
+ * @return a {@link CompletableFuture} that completes when the route calculator has been deleted
+ * @throws CompletionException if an error occurs while deleting the route calculator
+ * - If the route calculator was not found, a {@link ResourceNotFoundException} will be thrown
+ * - If any other error occurs, a generic {@link CompletionException} will be thrown
+ */
+ public CompletableFuture deleteRouteCalculator(String calcName) {
+ DeleteRouteCalculatorRequest calculatorRequest = DeleteRouteCalculatorRequest.builder()
+ .calculatorName(calcName)
+ .build();
+
+ return getClient().deleteRouteCalculator(calculatorRequest)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ throw new CompletionException("The route calculator was not found.", cause);
+ }
+ throw new CompletionException("Failed to delete the route calculator: " + exception.getMessage(), exception);
+ }
+ logger.info("The route calculator {} was deleted.", calcName);
+ })
+ .thenApply(response -> null);
+ }
+ // snippet-end:[location.java2.delete.calculator.main]
+}
+// snippet-end:[location.java2.actions.main]
diff --git a/javav2/example_code/location/src/main/java/com/example/location/scenario/LocationScenario.java b/javav2/example_code/location/src/main/java/com/example/location/scenario/LocationScenario.java
new file mode 100644
index 00000000000..1916c902723
--- /dev/null
+++ b/javav2/example_code/location/src/main/java/com/example/location/scenario/LocationScenario.java
@@ -0,0 +1,413 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.example.location.scenario;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import software.amazon.awssdk.services.location.model.AccessDeniedException;
+import software.amazon.awssdk.services.location.model.BatchUpdateDevicePositionResponse;
+import software.amazon.awssdk.services.location.model.CalculateRouteResponse;
+import software.amazon.awssdk.services.location.model.ConflictException;
+import software.amazon.awssdk.services.location.model.CreateRouteCalculatorResponse;
+import software.amazon.awssdk.services.location.model.GetDevicePositionResponse;
+import software.amazon.awssdk.services.location.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.location.model.ServiceQuotaExceededException;
+import software.amazon.awssdk.services.location.model.ValidationException;
+import java.util.Scanner;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+// snippet-start:[location.java2.scenario.main]
+/*
+ * Before running this Java V2 code example, set up your development
+ * environment, including your credentials.
+ *
+ * For more information, see the following documentation topic:
+ *
+ * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
+ *
+ */
+public class LocationScenario {
+
+ public static final String DASHES = new String(new char[80]).replace("\0", "-");
+
+ private static final Logger logger = LoggerFactory.getLogger(LocationScenario.class);
+ static Scanner scanner = new Scanner(System.in);
+
+ static LocationActions locationActions = new LocationActions();
+
+ public static void main(String[] args) {
+ final String usage = """
+
+ Usage:
+
+ Where:
+ mapName - The name of the map to be create (e.g., "AWSMap").
+ keyName - The name of the API key to create (e.g., "AWSApiKey").
+ collectionName - The name of the geofence collection (e.g., "AWSLocationCollection").
+ geoId - The geographic identifier used for the geofence or map (e.g., "geoId").
+ trackerName - The name of the tracker (e.g., "geoTracker").
+ calculatorName - The name of the route calculator (e.g., "AWSRouteCalc").
+ deviceId - The ID of the device (e.g., "iPhone-112356").
+ """;
+
+ if (args.length != 7) {
+ logger.info(usage);
+ return;
+ }
+
+ String mapName = args[0];
+ String keyName = args[1];
+ String collectionName = args[2];
+ String geoId = args[3];
+ String trackerName = args[4];
+ String calculatorName = args[5];
+ String deviceId = args[6];
+
+ logger.info("""
+ AWS Location Service is a fully managed service offered by Amazon Web Services (AWS) that
+ provides location-based services for developers. This service simplifies
+ the integration of location-based features into applications, making it
+ easier to build and deploy location-aware applications.
+
+ The AWS Location Service offers a range of location-based services,
+ including:
+
+ Maps: The service provides access to high-quality maps, satellite imagery,\s
+ and geospatial data from various providers, allowing developers to\s
+ easily embed maps into their applications.
+
+ Tracking: The Location Service enables real-time tracking of mobile devices,\s
+ assets, or other entities, allowing developers to build applications\s
+ that can monitor the location of people, vehicles, or other objects.
+
+ Geocoding: The service provides the ability to convert addresses or\s
+ location names into geographic coordinates (latitude and longitude),\s
+ and vice versa, enabling developers to integrate location-based search\s
+ and routing functionality into their applications.
+ """);
+ waitForInputToContinue(scanner);
+ try {
+ runScenario(mapName, keyName, collectionName, geoId, trackerName, calculatorName, deviceId);
+ } catch (RuntimeException e) {
+ // Clean up AWS Resources.
+ cleanUp(mapName, keyName, collectionName, trackerName, calculatorName);
+ logger.info(e.getMessage());
+ }
+ }
+
+ public static void runScenario(String mapName, String keyName, String collectionName, String geoId, String trackerName, String calculatorName, String deviceId) {
+ logger.info(DASHES);
+ logger.info("1. Create a map");
+ logger.info("""
+ An AWS Location map can enhance the user experience of your
+ application by providing accurate and personalized location-based
+ features. For example, you could use the geocoding capabilities to
+ allow users to search for and locate businesses, landmarks, or
+ other points of interest within a specific region.
+ """);
+
+ waitForInputToContinue(scanner);
+ String mapArn;
+ try {
+ mapArn = locationActions.createMap(mapName).join();
+ logger.info("The Map ARN is: {}", mapArn); // Log success in calling code
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ServiceQuotaExceededException) {
+ logger.error("The request exceeded the maximum quota: {}", cause.getMessage());
+ } else {
+ logger.error("An unexpected error occurred while creating the map.", cause);
+ }
+ return;
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info(DASHES);
+ logger.info("2. Create an AWS Location API key");
+ logger.info("""
+ When you embed a map in a web app or website, the API key is
+ included in the map tile URL to authenticate requests. You can
+ restrict API keys to specific AWS Location operations (e.g., only
+ maps, not geocoding). API keys can expire, ensuring temporary
+ access control.
+ """);
+
+ try {
+ String keyArn = locationActions.createKey(keyName, mapArn).join();
+ logger.info("The API key was successfully created: {}", keyArn);
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof AccessDeniedException) {
+ logger.error("Request was denied: {}", cause.getMessage());
+ } else {
+ logger.error("An unexpected error occurred while creating the API key.", cause);
+ }
+ return;
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info(DASHES);
+ logger.info("3. Display Map URL");
+ logger.info("""
+ In order to get the MAP URL, you need to get the API Key value.
+ You can get the key value using the AWS Management Console under
+ Location Services. This operation cannot be completed using the
+ AWS SDK. For more information about getting the key value, see
+ the AWS Location Documentation.
+ """);
+ String mapUrl = "https://maps.geo.aws.amazon.com/maps/v0/maps/"+mapName+"/tiles/{z}/{x}/{y}?key={KeyValue}";
+ logger.info("Embed this URL in your Web app: " + mapUrl);
+ logger.info("");
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info(DASHES);
+ logger.info("4. Create a geofence collection, which manages and stores geofences.");
+ waitForInputToContinue(scanner);
+ try {
+ String collectionArn = locationActions.createGeofenceCollection(collectionName).join();
+ logger.info("The geofence collection was successfully created: {}", collectionArn);
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ConflictException) {
+ logger.error("A conflict occurred: {}", cause.getMessage());
+ } else {
+ logger.error("An unexpected error occurred while creating the geofence collection.", cause);
+ }
+ return;
+ }
+
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info(DASHES);
+ logger.info("5. Store a geofence geometry in a given geofence collection.");
+ logger.info("""
+ An AWS Location geofence is a virtual boundary that defines a geographic area
+ on a map. It is a useful feature for tracking the location of
+ assets or monitoring the movement of objects within a specific region.
+
+ To define a geofence, you need to specify the coordinates of a
+ polygon that represents the area of interest. The polygon must be
+ defined in a counter-clockwise direction, meaning that the points of
+ the polygon must be listed in a counter-clockwise order.
+
+ This is a requirement for the AWS Location service to correctly
+ interpret the geofence and ensure that the location data is
+ accurately processed within the defined area.
+ """);
+
+ waitForInputToContinue(scanner);
+ try {
+ locationActions.putGeofence(collectionName, geoId).join();
+ logger.info("Successfully created geofence: {}", geoId);
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ValidationException) {
+ logger.error("A validation error occurred while creating geofence: {}", cause.getMessage());
+ } else {
+ logger.error("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info("6. Create a tracker resource which lets you retrieve current and historical location of devices..");
+ waitForInputToContinue(scanner);
+ try {
+ String trackerArn = locationActions.createTracker(trackerName).join();
+ logger.info("Successfully created tracker. ARN: {}", trackerArn); // Log success
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ConflictException) {
+ logger.error("A conflict occurred while creating the tracker: {}", cause.getMessage());
+ } else {
+ logger.error("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info(DASHES);
+ logger.info("7. Update the position of a device in the location tracking system.");
+ logger.info("""
+ The AWS location service does not enforce a strict format for deviceId, but it must:
+ - Be a string (case-sensitive).
+ - Be 1–100 characters long.
+ - Contain only:
+ - Alphanumeric characters (A-Z, a-z, 0-9)
+ - Underscores (_)
+ - Hyphens (-)
+ - Be the same ID used when sending and retrieving positions.
+ """);
+
+ waitForInputToContinue(scanner);
+ try {
+ CompletableFuture future = locationActions.updateDevicePosition(trackerName, deviceId);
+ future.join();
+ logger.info(deviceId + " was successfully updated in the location tracking system.");
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ logger.info("The resource was not found: {}", cause.getMessage(), cause);
+ } else {
+ logger.info("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info("8. Retrieve the most recent position update for a specified device..");
+ waitForInputToContinue(scanner);
+ try {
+ GetDevicePositionResponse response = locationActions.getDevicePosition(trackerName, deviceId).join();
+ logger.info("Successfully fetched device position: {}", response.position());
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ logger.info("The resource was not found: {}", cause.getMessage(), cause);
+ } else {
+ logger.info("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info("9. Create a route calculator.");
+ waitForInputToContinue(scanner);
+ try {
+ CreateRouteCalculatorResponse response = locationActions.createRouteCalculator(calculatorName).join();
+ logger.info("Route calculator created successfully: {}", response.calculatorArn());
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ConflictException) {
+ logger.info("A conflict occurred: {}", cause.getMessage(), cause);
+ } else {
+ logger.info("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info("10. Determine the distance between Seattle and Vancouver using the route calculator.");
+ waitForInputToContinue(scanner);
+ try {
+ CalculateRouteResponse response = locationActions.calcDistanceAsync(calculatorName).join();
+ logger.info("Successfully calculated route. The distance in kilometers is {}", response.summary().distance());
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ logger.info("The resource was not found: {}", cause.getMessage(), cause);
+ } else {
+ logger.info("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info("11. Use the GeoPlacesAsyncClient to perform additional operations.");
+ logger.info("""
+ This scenario will show use of the GeoPlacesClient that enables
+ location search and geocoding capabilities for your applications.\s
+
+ We are going to use this client to perform these AWS Location tasks:
+ - Reverse Geocoding (reverseGeocode): Converts geographic coordinates into addresses.
+ - Place Search (searchText): Finds places based on search queries.
+ - Nearby Search (searchNearby): Finds places near a specific location.
+ """);
+
+ logger.info("First we will perform a Reverse Geocoding operation");
+ waitForInputToContinue(scanner);
+ try {
+ locationActions.reverseGeocode().join();
+ logger.info("Now we are going to perform a text search using coffee shop.");
+ waitForInputToContinue(scanner);
+ locationActions.searchText("coffee shop").join();
+ waitForInputToContinue(scanner);
+
+ logger.info("Now we are going to perform a nearby Search.");
+ //waitForInputToContinue(scanner);
+ locationActions.searchNearBy().join();
+ waitForInputToContinue(scanner);
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof software.amazon.awssdk.services.geoplaces.model.ValidationException) {
+ logger.error("A validation error occurred: {}", cause.getMessage(), cause);
+ } else {
+ logger.error("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+ logger.info(DASHES);
+
+ logger.info("12. Delete the AWS Location Services resources.");
+ logger.info("Would you like to delete the AWS Location Services resources? (y/n)");
+ String delAns = scanner.nextLine().trim();
+ if (delAns.equalsIgnoreCase("y")) {
+ cleanUp(mapName, keyName, collectionName, trackerName, calculatorName);
+ } else {
+ logger.info("The AWS resources will not be deleted.");
+ }
+ waitForInputToContinue(scanner);
+ logger.info(DASHES);
+
+ logger.info(DASHES);
+ logger.info(" This concludes the AWS Location Service scenario.");
+ logger.info(DASHES);
+ }
+
+ /**
+ * Cleans up resources by deleting the specified map, key, geofence collection, tracker, and route calculator.
+ *
+ * @param mapName The name of the map to delete.
+ * @param keyName The name of the key to delete.
+ * @param collectionName The name of the geofence collection to delete.
+ * @param trackerName The name of the tracker to delete.
+ * @param calculatorName The name of the route calculator to delete.
+ */
+ private static void cleanUp(String mapName, String keyName, String collectionName, String trackerName, String calculatorName) {
+ try {
+ locationActions.deleteMap(mapName).join();
+ locationActions.deleteKey(keyName).join();
+ locationActions.deleteGeofenceCollectionAsync(collectionName).join();
+ locationActions.deleteTracker(trackerName).join();
+ locationActions.deleteRouteCalculator(calculatorName).join();
+ } catch (CompletionException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof ResourceNotFoundException) {
+ logger.info("The resource was not found: {}", cause.getMessage(), cause);
+ } else {
+ logger.info("An unexpected error occurred: {}", cause.getMessage(), cause);
+ }
+ return;
+ }
+ }
+
+ private static void waitForInputToContinue(Scanner scanner) {
+ while (true) {
+ logger.info("");
+ logger.info("Enter 'c' followed by to continue:");
+ String input = scanner.nextLine();
+
+ if (input.trim().equalsIgnoreCase("c")) {
+ logger.info("Continuing with the program...");
+ logger.info("");
+ break;
+ } else {
+ logger.info("Invalid input. Please try again.");
+ }
+ }
+ }
+}
+// snippet-end:[location.java2.scenario.main]
\ No newline at end of file
diff --git a/javav2/example_code/location/src/main/resources/log4j2.xml b/javav2/example_code/location/src/main/resources/log4j2.xml
new file mode 100644
index 00000000000..914470047e7
--- /dev/null
+++ b/javav2/example_code/location/src/main/resources/log4j2.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/javav2/example_code/location/src/test/java/LocationTest.java b/javav2/example_code/location/src/test/java/LocationTest.java
new file mode 100644
index 00000000000..28671deebc1
--- /dev/null
+++ b/javav2/example_code/location/src/test/java/LocationTest.java
@@ -0,0 +1,205 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import com.example.location.HelloLocation;
+import com.example.location.scenario.LocationActions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import software.amazon.awssdk.services.location.model.BatchUpdateDevicePositionResponse;
+import software.amazon.awssdk.services.location.model.CalculateRouteResponse;
+import software.amazon.awssdk.services.location.model.CreateGeofenceCollectionResponse;
+import software.amazon.awssdk.services.location.model.CreateRouteCalculatorResponse;
+import software.amazon.awssdk.services.location.model.GetDevicePositionRequest;
+import software.amazon.awssdk.services.location.model.GetDevicePositionResponse;
+import software.amazon.awssdk.services.location.model.PutGeofenceResponse;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@TestInstance(TestInstance.Lifecycle.PER_METHOD)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class LocationTest {
+
+ private static final LocationActions locationActions = new LocationActions();
+
+ private static final Logger logger = LoggerFactory.getLogger(LocationTest.class);
+ private static final String mapName = "TestMap1";
+
+ private static final String keyName = "TestKey1";
+ private static final String collectionName = "TestCollection1";
+ private static final String geoId = "TestGeo1";
+ private static final String trackerName = "TestTracker1";
+
+ private static String mapArn = "";
+ String calculatorName = "TestCalc";
+ String deviceId = "iPhone-112359"; // Use the iPhone's identifier from Swift
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(1)
+ public void testCreateMap() {
+ assertDoesNotThrow(() -> {
+ mapArn = locationActions.createMap(mapName).join();
+ assertNotNull(mapArn);
+ logger.info("Test 1 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(2)
+ public void testCreateKey() {
+ CompletableFuture future = locationActions.createKey(keyName, mapArn);
+ assertDoesNotThrow(() -> {
+ String keyArn = future.join();
+ assertNotNull(keyArn, "Expected key ARN to be non-null");
+ assertFalse(keyArn.isEmpty(), "Expected key ARN to be non-empty");
+ logger.info("Test 2 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(3)
+ public void testCreateGeofenceCollection() {
+ CompletableFuture future = locationActions.createGeofenceCollection(collectionName);
+ assertDoesNotThrow(() -> {
+ String response = future.join();
+ assertNotNull(response, "Expected response to be non-null");
+ logger.info("Test 3 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(4)
+ public void testPutGeoCollection() {
+ CompletableFuture future = locationActions.putGeofence(collectionName, geoId);
+ assertDoesNotThrow(() -> {
+ PutGeofenceResponse response = future.join();
+ assertNotNull(response, "Expected response to be non-null");
+ logger.info("Test 4 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(5)
+ public void testHelloService() {
+ assertDoesNotThrow(() -> {
+ CompletableFuture future = HelloLocation.listGeofences(collectionName);
+ future.join(); // Wait for the asynchronous operation to complete
+ logger.info("Test 5 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(6)
+ public void testCreateTracker() {
+ CompletableFuture future = locationActions.createTracker(trackerName);
+ assertDoesNotThrow(() -> {
+ String trackerArn = future.join();
+ assertNotNull(trackerArn, "Expected tracker ARN to be non-null");
+ assertFalse(trackerArn.isEmpty(), "Expected tracker ARN to be non-empty");
+ logger.info("Test 6 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(7)
+ public void testUpdateDevice() {
+ CompletableFuture future = locationActions.updateDevicePosition(trackerName, deviceId);
+ assertDoesNotThrow(() -> {
+ BatchUpdateDevicePositionResponse response = future.join();
+ assertNotNull(response, "Expected response to be non-null");
+ assertTrue(response.errors().isEmpty(), "Expected no errors while updating device position");
+ logger.info("Test 7 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(8)
+ public void testGetDevicePosition() throws InterruptedException {
+ TimeUnit.SECONDS.sleep(10);
+ CompletableFuture future = locationActions.getDevicePosition(trackerName, deviceId);
+ assertDoesNotThrow(() -> {
+ GetDevicePositionResponse response = future.join();
+ assertNotNull(response, "Expected response to be non-null");
+ // assertNotNull(response.position(), "Expected position data to be non-null");
+ // assertFalse(response.position().isEmpty(), "Expected position data to be non-empty");
+ // assertNotNull(response.receivedTime(), "Expected received time to be non-null");
+ logger.info("Test 8 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(9)
+ public void testCreateRouteCalculator() {
+ CompletableFuture future = locationActions.createRouteCalculator(calculatorName);
+ assertDoesNotThrow(() -> {
+ CreateRouteCalculatorResponse response = future.join();
+ assertNotNull(response, "Expected response to be non-null");
+ assertNotNull(response.calculatorArn(), "Expected calculator ARN to be non-null");
+ assertFalse(response.calculatorArn().isEmpty(), "Expected calculator ARN to be non-empty");
+ logger.info("Test 9 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(10)
+ public void testCalcDistance() {
+ CompletableFuture future = locationActions.calcDistanceAsync(calculatorName);
+ assertDoesNotThrow(() -> {
+ CalculateRouteResponse response = future.join();
+ assertNotNull(response);
+ assertNotNull(response.summary());
+ double distance = response.summary().distance();
+ double duration = response.summary().durationSeconds();
+ assertTrue(distance > 0, "Expected distance to be greater than 0");
+ assertTrue(duration > 0, "Expected duration to be greater than 0");
+ logger.info("Test 10 passed");
+ });
+ }
+
+ @Tag("IntegrationTest")
+ @Order(12)
+ public void testGeoPlaces() {
+
+ assertDoesNotThrow(() -> {
+ locationActions.reverseGeocode();
+ locationActions.searchText("coffee shop");
+ locationActions.searchNearBy();
+ logger.info("Test 11 passed");
+ });
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(12)
+ public void testDeleteLocationResources() {
+ assertDoesNotThrow(() -> {
+ locationActions.deleteMap(mapName).join();
+ locationActions.deleteKey(keyName).join();
+ locationActions.deleteGeofenceCollectionAsync(collectionName).join();
+ locationActions.deleteTracker(trackerName).join();
+ locationActions.deleteRouteCalculator(calculatorName).join();
+ logger.info("Test 12 passed");
+ });
+ }
+}
diff --git a/scenarios/basics/location/README.md b/scenarios/basics/location/README.md
new file mode 100644
index 00000000000..88675916af2
--- /dev/null
+++ b/scenarios/basics/location/README.md
@@ -0,0 +1,47 @@
+# AWS Location Program
+
+## Overview
+
+This AWS Location service basic scenario demonstrates how to interact with the AWS Location service using an AWS SDK. This application demonstrates how to use AWS Location to perform tasks like creating a map, creating a tracker, and how to perform location searches. The program walks through various operations and then how to clean up the created AWS resources.
+
+**Note:** See the [specification document](SPECIFICATION.md) for a complete list of operations.
+
+## Features
+
+1. Create a map
+
+2. Create an AWS Location API key
+
+3. Display Map URL
+
+4. Create a geofence collection
+
+5. Store a geofence geometry in a given geofence collection
+
+6. Create a tracker resource
+
+7. Update the position of a device in the location tracking
+
+8. Retrieve the most recent position
+
+9. Create a route calculator.
+
+10. Determine the distance between Seattle and Vancouver
+
+11. Perform search and geocoding tasks.
+
+12. Clean up AWS resources.
+
+## Implementations
+
+This scenario example will be implemented in the following languages:
+
+- Java
+- Python
+- Kotlin
+
+## Additional Reading
+
+- [AWS Location Documentation](https://docs.aws.amazon.com/location/latest/developerguide/dev-sdks.html)
+
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0
diff --git a/scenarios/basics/location/SPECIFICATION.md b/scenarios/basics/location/SPECIFICATION.md
new file mode 100644
index 00000000000..4440e408a24
--- /dev/null
+++ b/scenarios/basics/location/SPECIFICATION.md
@@ -0,0 +1,447 @@
+# Specification for the AWS Location Service Scenario
+
+## Overview
+
+This SDK Basics scenario showcases how to interact with the AWS Location Service using the AWS SDK. It covers a range of tasks, including creating a map, setting up an API key, configuring a tracker, and more. These examples illustrate fundamental operations for working with the AWS Location Service in a programmatic way using `LocationAsyncClient`.
+
+This scenario uses the higher level `GeoPlacesAsyncClient` to perform additional location search and geocoding capabilities. Finally, it demonstrates how to clean up resources.
+
+## Resources
+
+This Basics scenario requires no additional resources.
+
+## Hello AWS Location Service
+
+This program is intended for users not familiar with the AWS Location Service to easily get up and running. The program uses `listGeofencesPaginator` to demonstrate how you can read through
+Geofences information.
+
+## Basics Scenario Program Flow
+
+The AWS Location Service Basics scenario executes the following operations.
+
+1. **Create a map**:
+ - Description: Creates a map by invoking the `createMap` method.
+ - Exception Handling: Check to see if a `ServiceQuotaExceededException` is thrown, which indicates that the operation was denied because the request would exceed the maximum quota. If the exception is thrown, display the information and end the program.
+
+2. **Create an AWS Location API key**:
+ - Description: Creates an API key required to embed a map in a web app or website by invoking the `createKey` method.
+ - Exception Handling: Handle `AccessDeniedException`, which occurs when there is insufficient access or permissions. If this exception is thrown, display the error message and terminate the program.
+
+3. **Display Map URL**:
+ - Description: Show the syntax of a MAP URL in the console. This uses the map name and key value.
+ - Exception Handling: N/A.
+
+4. **Create a geofence collection**:
+ - Description: Create a geofence collection, which manages and stores geofences by invoking the `createGeofenceCollection` method.
+ - Exception Handling: Check to see if an `ConflictException ` is
+ thrown, which indicates that a conflict occurred. If the
+ exception is thrown, display the message and end the program.
+
+5. **Store a geofence geometry in a given geofence collection**:
+ - Description: Store a geofence geometry in a given geofence collection by invoking the `putGeofence` method. Included in this call is how to constuct a polygon.
+ - Exception Handling: Check to see if a `ValidationException ` is
+ thrown due to an invalid polygon. If so, display the message and end the program.
+
+6. **Create a tracker resource**:
+ - Description: Create a tracker resource which lets you retrieve current and historical location of devices by invoking the `createTracker` method.
+ - Exception Handling: Check to see if an `ConflictException` is thrown. If so, display the message and end the program.
+
+7. **Update the position of a device**:
+ - Description: Update the position of a device in the location tracking system by invoking the`getDevicePosition` method.
+ - Exception Handling: Check to see if a `ResourceNotFoundException` is
+ thrown. If so, display the message and end the program
+
+8. **Retrieve the most recent position**:
+ - Description: Retrieve the most recent position update for a specified device by invoking the
+ `getMatchingJob` method.
+ - Exception Handling: Check to see if an `ResourceNotFoundException` is thrown. If so, display the message and end the program.
+
+9. **Create a route calculator**:
+ - Description: Create a route calculator by invoking the
+ `createRouteCalculator` method.
+ - Exception Handling: Check to see if an `ConflictException` is thrown. If
+ so, display the message and end the program.
+
+10. **Determine the distance between two cities and Vancouver**:
+ - Description: Determine the distance between Seattle and Vancouver by invoking the `calculateRoute` method.
+ - Exception Handling: Check to see if an `ResourceNotFoundException` is thrown. If so, display the message and end the program.
+
+11. **Use AWS Location Services higher level API**
+ - Description: Use the `GeoPlacesAsyncClient`client to perform these tasks:
+ - Reverse Geocoding (reverseGeocode): Converts geographic coordinates into addresses.
+ - Place Search (searchText): Finds places based on search queries.
+ - Nearby Search (searchNearby): Finds places near a specific location.
+ - Exception Handling: Check to see if an `ValidationException` is thrown. If so, display the message and end the program.
+
+12. **Delete AWS resources**:
+ - Description: Delete the various resources by invoking the corresponding delete methods.
+ - Exception Handling: Check to see if an `ResourceNotFoundException` is thrown. If so, display the message and end the program.
+
+### Program execution
+
+The following shows the output of the AWS Location Basics scenario in
+the console.
+
+```
+AWS Location Service is a fully managed service offered by Amazon Web Services (AWS) that
+provides location-based services for developers. This service simplifies
+the integration of location-based features into applications, making it
+easier to build and deploy location-aware applications.
+
+The AWS Location Service offers a range of location-based services,
+including:
+
+Maps: The service provides access to high-quality maps, satellite imagery,
+and geospatial data from various providers, allowing developers to
+easily embed maps into their applications.
+
+Tracking: The Location Service enables real-time tracking of mobile devices,
+assets, or other entities, allowing developers to build applications
+that can monitor the location of people, vehicles, or other objects.
+
+Geocoding: The service provides the ability to convert addresses or
+location names into geographic coordinates (latitude and longitude),
+and vice versa, enabling developers to integrate location-based search
+and routing functionality into their applications.
+
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+1. Create a map
+ An AWS Location map can enhance the user experience of your
+ application by providing accurate and personalized location-based
+ features. For example, you could use the geocoding capabilities to
+ allow users to search for and locate businesses, landmarks, or
+ other points of interest within a specific region.
+
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+The Map ARN is: arn:aws:geo:us-east-1:814548047983:map/AWSMap200
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+2. Create an AWS Location API key
+When you embed a map in a web app or website, the API key is
+included in the map tile URL to authenticate requests. You can
+restrict API keys to specific AWS Location operations (e.g., only
+maps, not geocoding). API keys can expire, ensuring temporary
+access control.
+
+The API key was successfully created: arn:aws:geo:us-east-1:814548047983:api-key/AWSApiKey200
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+3. Display Map URL
+In order to get the MAP URL, you need to get the API Key value.
+You can get the key value using the AWS Management Console under
+Location Services. This operation cannot be completed using the
+AWS SDK. For more information about getting the key value, see
+the AWS Location Documentation.
+
+Embed this URL in your Web app: https://maps.geo.aws.amazon.com/maps/v0/maps/AWSMap200/tiles/{z}/{x}/{y}?key={KeyValue}
+
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+4. Create a geofence collection, which manages and stores geofences.
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+The geofence collection was successfully created: arn:aws:geo:us-east-1:814548047983:geofence-collection/AWSLocationCollection200
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+5. Store a geofence geometry in a given geofence collection.
+An AWS Location geofence is a virtual boundary that defines a geographic area
+on a map. It is a useful feature for tracking the location of
+assets or monitoring the movement of objects within a specific region.
+
+To define a geofence, you need to specify the coordinates of a
+polygon that represents the area of interest. The polygon must be
+defined in a counter-clockwise direction, meaning that the points of
+the polygon must be listed in a counter-clockwise order.
+
+This is a requirement for the AWS Location service to correctly
+interpret the geofence and ensure that the location data is
+accurately processed within the defined area.
+
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Successfully created geofence: geoId200
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+6. Create a tracker resource which lets you retrieve current and historical location of devices..
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Successfully created tracker. ARN: arn:aws:geo:us-east-1:814548047983:tracker/geoTracker200
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+7. Update the position of a device in the location tracking system.
+The AWS location service does not enforce a strict format for deviceId, but it must:
+ - Be a string (case-sensitive).
+ - Be 1–100 characters long.
+ - Contain only:
+ - Alphanumeric characters (A-Z, a-z, 0-9)
+ - Underscores (_)
+ - Hyphens (-)
+ - Be the same ID used when sending and retrieving positions.
+
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+iPhone-112356 was successfully updated in the location tracking system.
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+8. Retrieve the most recent position update for a specified device..
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Successfully fetched device position: [-122.4194, 37.7749]
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+9. Create a route calculator.
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Route calculator created successfully: arn:aws:geo:us-east-1:814548047983:route-calculator/AWSRouteCalc200
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+10. Determine the distance between Seattle and Vancouver using the route calculator.
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Successfully calculated route. The distance in kilometers is 229.4919562976832
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+11. Use the GeoPlacesAsyncClient to perform additional operations.
+This scenario will show use of the GeoPlacesClient that enables
+location search and geocoding capabilities for your applications.
+
+We are going to use this client to perform these AWS Location tasks:
+ - Reverse Geocoding (reverseGeocode): Converts geographic coordinates into addresses.
+ - Place Search (searchText): Finds places based on search queries.
+ - Nearby Search (searchNearby): Finds places near a specific location.
+
+First we will perform a Reverse Geocoding operation
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Use latitude 37.7749 and longitude -122.4194
+The address is: FedEx Office, 1967 Market St, San Francisco, CA 94103, United States
+Now we are going to perform a text search using coffee shop.
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Found Place with id: AQAAAFUARI2OrflhvYzXbmuuOadKpTSJ6ynsZSLcXI2xbU2X2D5PdcHmlQ-jrs-hn-yhF7jOC9hMgrXFP5neo_lK480qGCAO-zdYrsKSBoWVm8DhRNFLF_Eyua-vUTfG9SGbSGJmUfwxSkPKIYRYOnRnocKOT6CsGt_h
+Detailed Place Information:
+Name: POINT_OF_INTEREST
+Address: Cafe Creme, 50 Oak St, San Francisco, CA 94102-6011, United States
+Food Types:
+ - FoodType(LocalizedName=Thai, Id=thai, Primary=true)
+-------------------------
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+Now we are going to perform a nearby Search.
+Place Name: POINT_OF_INTEREST
+Address: Limpar Cleaning Services, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Chuin Phang, Massage Practitioner, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Benjamin Franklin, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Elite Bridal Artists, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Miss Clean Scene, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: New Life Remodeling, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Parking Panda, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Inky Binky Bonky, Market St, San Francisco, CA 94103, United States
+Distance: 2 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Sport-Tec, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Timberwood Tree Service, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Wonderbread5, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Weaving Stories Therapy, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Chef Greg / Personal Chef, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Illuminate Bodywork, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Book My Limo Trip, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: ABC Locksmith Service, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Cardona Landscaping, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: C.H. Burnham Construction, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Edward Dougherty, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+Place Name: POINT_OF_INTEREST
+Address: Ripertonwest Consulting, Market St, San Francisco, CA 94103, United States
+Distance: 3 meters
+-------------------------
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+12. Delete the AWS Location Services resources.
+Would you like to delete the AWS Location Services resources? (y/n)
+y
+The map AWSMap200 was deleted.
+The key AWSApiKey200 was deleted.
+The geofence collection AWSLocationCollection200 was deleted.
+The tracker geoTracker200 was deleted.
+The route calculator AWSRouteCalc200 was deleted.
+
+Enter 'c' followed by to continue:
+c
+Continuing with the program...
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+ This concludes the AWS Location Service scenario.
+--------------------------------------------------------------------------------
+
+```
+
+## SOS Tags
+
+The following table describes the metadata used in this Basics Scenario.
+
+| action | metadata file | metadata key |
+|---------------------------|---------------------------|-------------------------------------|
+|`createKey` | location_metadata.yaml |location_CreateKey |
+|`createMap` | location_metadata.yaml |location_CreateMap |
+|`createCollection` | location_metadata.yaml |location_CreateGeofenceCollection |
+|`putGeofence ` | location_metadata.yaml |location_PutGeofence |
+|`createTracker` | location_metadata.yaml |location_CreateTracker |
+|`batchUpdateDevicePosition`| location_metadata.yaml |location_BatchUpdateDevicePosition |
+|`getDevicePosition` | location_metadata.yaml |location_GetDevicePosition |
+|`createRouteCalculator` | location_metadata.yaml |location_CreateRouteCalculator |
+|`calculateRoute` | location_metadata.yaml |location_CalculateRoute |
+|`deleteMap` | location_metadata.yaml |location_DeleteMap |
+|`deleteKey` | location_metadata.yaml |location_DeleteKey |
+|`deleteGeofenceCollection` | location_metadata.yaml |location_DeleteGeofenceCollection |
+|`deleteTracker` | location_metadata.yaml |location_DeleteTracker |
+|`deleteCalculator` | location_metadata.yaml |location_DeleteRouteCalculator |
+|`scenario` | location_metadata.yaml |location_Scenario |
+|`hello` | location_metadata.yaml |location_Hello |
+|`reverseGeocode` | geo-places_metadata.yaml |geo-places_ReverseGeocode |
+|`searchNearby` | geo-places_metadata.yaml |geo-places_SearchNearby |
+|`searchText` | geo-places_metadata.yaml |geo-places_SearchText |
+
+