Take a journey with your own map service by AWS Amplify and Amazon Location Service
Tags: aws, amplify, vue, geolocation
Published: 2021-06-08 13:19:29

{% youtube G1BclFCVMmw %}

Amazon Location Service is Generally Available on June 1st, 2021.
https://aws.amazon.com/jp/blogs/aws/amazon-location-service-is-now-generally-available-with-new-routing-and-satellite-imagery-capabilities/

So, we can make our own map service which used Amazon Location Service.

This post shows how to make your own map service by AWS Amplify and Amazon Location Service.
We can search the location and display it on the map.

## 1. Setup

* Vue CLI (+ Vuetify)
* Amplify CLI

### 1.1. Vue CLI

Install "Vue CLI."
[Vue CLI](https://cli.vuejs.org/guide/prototyping.html)

``` bash
$ vue -V
@vue/cli 4.5.13
```

### 1.2. Amplify CLI
Install "Amplify CLI."
[AWS Amplify](https://docs.amplify.aws/start/getting-started/installation/q/integration/vue)

``` bash
$ amplify -v
Scanning for plugins...
Plugin scan successful
4.52.0
```

### 1.3. Create Vue project

Create a Vue project by `Vue CLI`.

{% details vue create amplify-amazon-location %} 
``` bash
$ vue create amplify-amazon-location


Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated confi
g files
? Save this as a preset for future projects? No

(snip)

$ cd amplify-amazon-location/
$ vue add vuetify
$
```
{% enddetails %}

### 1.4. Setup Amplify project

Setup Amplify project.

{% details amplify init %} 
``` bash
$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project amplifyamazonlocatio
The following configuration will be applied:

Project information
| Name: amplifyamazonlocatio
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: vue
| Source Directory Path: src
| Distribution Directory Path: dist
| Build Command: npm run-script build
| Start Command: npm run-script serve

? Initialize the project with the above configuration? No
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  yarn build
? Start Command: yarn serve
Using default provider  awscloudformation
? Select the authentication method you want to use: AWS profile

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html

? Please choose the profile you want to use amplify-dev
Adding backend environment dev to AWS Amplify Console app: d2qfsvhqbazu5s
⠦ Initializing project in the cloud...

(snip)

$
```
{% enddetails %}


And don't forget to add UI Components.

``` bash
$ yarn add aws-amplify @aws-amplify/ui-vue
$
```

### 1.5. Add other packages

This time we use "MapLibre GL JS," so we need to add these packages.

* [aws-sdk](https://github.com/aws/aws-sdk-js)
* [@benchmark-urbanism/vue-mapbox-map](https://github.com/benchmark-urbanism/vue-mapbox-map)
* [maplibre-gl](https://github.com/maplibre/maplibre-gl-js)

See [the official document](https://docs.aws.amazon.com/location/latest/developerguide/tutorial-maplibre-gl-js.html) for the detail.

``` bash
$ yarn add aws-sdk @benchmark-urbanism/vue-mapbox-map maplibre-gl
```

### 1.6. Edit main.js

Then, we edit `src/main.js` like below.

``` javascript
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'
import '@aws-amplify/ui-vue'
import Amplify from 'aws-amplify'
import awsconfig from './aws-exports'

Amplify.configure(awsconfig)

import 'maplibre-gl/dist/maplibre-gl.css'

Vue.config.productionTip = false

new Vue({
  vuetify,
  render: (h) => h(App),
}).$mount('#app')
```

## 2. Add Auth

Add Authentication.

{% details amplify add auth %} 
``` bash
$ amplify add auth
Using service: Cognito, provided by: awscloudformation
 
 The current configured provider is Amazon Cognito. 
 
 Do you want to use the default authentication and security configuration? Defau
lt configuration
 Warning: you will not be able to edit these selections. 
 How do you want users to be able to sign in? Username
 Do you want to configure advanced settings? No, I am done.
Successfully added auth resource amplifyamazonlocatio3e211eea locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

$
```
{% enddetails %}

Then, Push project.

{% details amplify push %} 
``` bash
$ amplify push
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name                | Operation | Provider plugin   |
| -------- | ---------------------------- | --------- | ----------------- |
| Auth     | amplifyamazonlocatio3e211eea | Create    | awscloudformation |
? Are you sure you want to continue? Yes
⠧ Updating resources in the cloud. This may take a few minutes...

(snip)

$
```
{% enddetails %}

## 3. Create a Map and a Search index

### 3.1.　Create a Map
Next, we open the AWS console by the browser and move to "Amazon Location Service."
1. Click the "Maps" menu on the left panel.
2. And click "Create map" in the My maps area.
3. Input the map name "SampleMap" in the Name field.
4. Select the map in the Maps area. In this case, we select "Esri Light."
5. Select "Pricing plan use-case." In this case, we select "Yes" to the first question.
6. Push "Create map" at the bottom of the page.

![Create a Map](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vc5r70fou1f60vvrlna6.png)

### 3.2.　Create a Search index
Continuously, we create a Place index.

1. Click the "Search indexes" menu on the left panel.
2. And click "Create place index" in the My maps area.
3. Input the map name "SamplePlaceIndex" in the Name field.
4. Select the data provider in the Data provider area. In this case, we select "Esri."
5. Select the data storage option in the Data storage options area. In this case, we select "No, single use only."
6. Select "Pricing plan use-case." In this case, we select "Yes" to the first question.
7. Push "Create place index" at the bottom of the page.

![Create　a Search index](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qoje6x7d1uac9qcv03n8.png)

## 4. Set IAM Role
We have already added the authentication function by Amplify.
However, the IAM role is incomplete. Unable to access previously created location services resources.
Let's add it.

1. Go to IAM Services and click the Roles menu on the left panel.
2. Input "amplify-amplifyamazonlocatio" in the search field.
3. Click the role that name ends with "-authRole".
4. Click "Add inline policy".
5. Select the JSON tab and input below JSON data. (Set your ARN in [MAP_ARN] and [PLACE_INDEX_ARN].)

``` JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "geo:GetMapGlyphs",
                "geo:GetMapSprites",
                "geo:GetMapStyleDescriptor",
                "geo:GetMapTile"
            ],
            "Resource": [MAP_ARN]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "geo:SearchPlaceIndexForText",
            "Resource": [PLACE_INDEX_ARN]
        }
    ]
}
```

## 5. Implement

OK, we can implement a search place and show it on the map.
Please see the repository for code details.

{% github tacck/amplify-amazon-location no-readme %}

### 5.1. Search the location

"Search the location" implementation is like below.
Only use `searchPlaceIndexForText`, we can get the place information.

[`src/components/SearchMap.vue`](https://github.com/tacck/amplify-amazon-location/blob/main/src/components/SearchMap.vue)

``` javascript
    search: function () {
      if (!this.place || this.place.length === 0) {
        return
      }

      const params = {
        IndexName: 'SamplePlaceIndex',
        Text: this.place,
      }
      this.client.searchPlaceIndexForText(params, (err, data) => {
        if (err) {
          console.error(err)
          return
        }
        if (data) {
          console.log(data)

          if (data.Results && data.Results.length > 0) {
            this.lng = data.Results[0].Place.Geometry.Point[0]
            this.lat = data.Results[0].Place.Geometry.Point[1]
          }
        }
      })
    },
```

### 5.2. Display the location on the map

"Display the location on the map" implementation is like below.
`mapInstance` is the object rendering the map.
`transformRequest` make a Signed URL for access to the Map resource previously created.

[`src/components/Map.vue`](https://github.com/tacck/amplify-amazon-location/blob/main/src/components/Map.vue)

``` javascript
  mounted: async function () {
    this.credentials = await Auth.currentCredentials()

    this.mapInstance = new maplibregl.Map({
      container: 'map',
      style: this.mapName,
      center: [this.scene.lng, this.scene.lat],
      zoom: this.scene.zoom,
      transformRequest: this.transformRequest,
    })

    this.mapInstance.addControl(new maplibregl.NavigationControl(), 'top-left')
    this.mapInstance.on('load', function () {
      this.resize()
    })
  },
(snip)
    transformRequest: function (url, resourceType) {
      if (resourceType === 'Style' && !url.includes('://')) {
        url = `https://maps.geo.${awsconfig.aws_project_region}.amazonaws.com/maps/v0/maps/${url}/style-descriptor`
      }

      if (url.includes('amazonaws.com')) {
        return {
          url: Signer.signUrl(url, {
            access_key: this.credentials.accessKeyId,
            secret_key: this.credentials.secretAccessKey,
            session_token: this.credentials.sessionToken,
          }),
        }
      }

      return { url }
    },
```

## 6. Take a journey

We arrive at the final section.
Let's get to the journey!

{% youtube G1BclFCVMmw %}