Nuxtstop

For all things nuxt.js

Build a Map Application on AWS in 10 Steps

Build a Map Application on AWS in 10 Steps
175 6

Last week, the team I work on at AWS Amplify launched new map UI components. I wanted to take this opportunity to show what you can build using Amplify Geo and Amplify Studio together!

I'm an avid hiker, and AllTrails is one of my favorite sites, I duplicated a very, very small portion of their UI for this tutorial!

1. Create a new Studio Instance

Head to the Amplify Console and select build a new app.

Once your Studio instance is deployed, click "Launch Studio".

2. Create your data model

Once you're in Studio, click "Data" under "Set up" on the lefthand navigation. Add one model, Hike. Add the fields name, difficulty, location, lat, long, length, time, and coverImage. lat, long, and length should be floats, all other fields should be strings.

Image description

Once you've implemented your data model, click "Save and Deploy"!

Now, go to the "Content" tab under "Manage" and add a few hikes! You can find the latitude and longitude by Googling "trail name lat and long".

3. Enable auth

Once your data model is done deploying, go to "Authentication" under "Set up". Then deploy the default authentication mode. We won't implement a full sign in flow, though you can learn about how to here, we will just need auth enabled to enable mapping.

4. Change UI components in Figma

Head to the AWS Amplify UI Kit. Duplicate the Figma file to your account, and then head to the "My Components" page. You'll see a "CardB" card which we will modify for our app.

  1. Round the card corners by adding a border-radius to the card and the top corners of the image.
  2. Copy a small button from the Primitives page and paste it onto the card.
  3. Change what's currently an address into three separate text fields, the middle one being a dot character.

Image description

When you're done, your card should look similar to the above.

I also deleted the components I'm not using.

5. Figma to Code

Then, back in Studio, head to the "UI Library" page. Click "Get Started" and paste in the link to your Figma file. Then accept all of your components.

Then, click on the CardB component on the "UI Library" page. Then click "Configure". Once here, we'll bind data to our component!

Click the image, then set the src to the hike.coverImage. Then, click the $99 USD and set the label to hike.name. Set the information about the beds/baths/sqft to the name of the hike.location.

Set the button label to hike.difficulty. I also want this element to be a div instead of a button - so I'll set the as prop to "div".

Image description

I'll set the first text field under that to a concatenated property: hike.length + miles. I set the second one to the hike.time.

My final card looks like this:

Image description

I want to display all my hikes in a list, so I'll create a collection. Click the "Create a collection" button on the right side. I called my collection Hikes.

Add margin as needed on the right side, and I added search and pagination. I kept the dataset as is, though you can add sorts or filters as desired.

Image description

6. Setup Local Code

Now we're ready to integrate this list into our app! First, create a React app:

npx create-react-app amplify-maps

Install the Amplify libraries and React components:

npm i aws-amplify @aws-amplify/ui-react

Run Amplify pull for your project. In Studio on the top right corner, you'll see a "Local setup instructions" link with a unique command for your app. Run this in your command line.

amplify pull --appId your-app-id --envName staging

Then, configure Amplify within your index.js file:

// Add needed imports
import { Amplify } from 'aws-amplify'
import config from './aws-exports'
import { AmplifyProvider } from '@aws-amplify/ui-react'

import '@aws-amplify/ui-react/styles.css'

// Configure Amplify
Amplify.configure(config)

// use the Amplify Provider component to intialize CSS
ReactDOM.render(
  <AmplifyProvider>
    <App />
  </AmplifyProvider>,
  document.getElementById('root')
)
Enter fullscreen mode Exit fullscreen mode

Now, go to your App.js file and import the Hikes component.

import { Hikes } from './ui-components'
Enter fullscreen mode Exit fullscreen mode

Replace the body of your App.js file with the following:

function App () {
  return (
    <div className='container'>
      <Hikes />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Now, if you run your React server, you'll see your list of hikes on the page!

7. Add Geo

Let's add the map to our application. First, we'll need to initialize Amplify Geo.

Run amplify add geo from your command line.

Answer the ensuing questions with the following:

? Select which capability you want to add: Map (visualize the geospatial data)
 Provide a name for the Map: (enter for default)
 Who can access this Map? · Authorized and Guest users
Available advanced settings:
- Map style & Map data provider (default: Streets provided by Esri)

 Do you want to configure advanced settings? (y/N) · no
Enter fullscreen mode Exit fullscreen mode

Now, run amplify push in order to deploy your changes.

8. Add a map to your UI

Now, we'll use the Amplify Geo UI components to add a map to the page. I'll use Denver's latitude and longitude as the center of my map. You also may want to play around with the initial Zoom level.

import { MapView } from '@aws-amplify/ui-react'

...

function App () {
  return (
    <div className='container'>
      <Hikes />
      <MapView
        initialViewState={{
          latitude: 39.113014,
          longitude: -105.358887,
          zoom: 7
        }}
      />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Now, let's add markers to the map for each hike! I'll add a useEffect to query for all of my Hikes.

// Add needed imports
import { useEffect, useState } from 'react'
import { Hike } from './models'
import { DataStore } from 'aws-amplify'


...

// at the top of App.js pull all the hikes and store them in state
const [hikes, setHikes] = useState([])
useEffect(() => {
 const getHikes = async () => {
   const hikes = await DataStore.query(Hike)
   setHikes(hikes)
 }
 getHikes()
}, [])
Enter fullscreen mode Exit fullscreen mode

Now, let's loop through the hikes and add a Marker instance for each with its latitude and longitude:

import { Marker } from 'react-map-gl'

...

function App () {
  return (
    <div className='container'>
      <Hikes />
      <MapView
        initialViewState={{
          latitude: 39.113014,
          longitude: -105.358887,
          zoom: 7
        }}
      >
        {hikes.map(hike => (
          <Marker
            latitude={hike.lat}
            longitude={hike.long}
            key={hike.id}
        />))}
      </MapView>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Now your map should look something like this:

Image description

9. Add popups

Now we have a map with markers on it, let's add popups when a marker is clicked on so you can see more info about the hike! I'll render the CardB component if the popup is open.

// Import the Popup
import { Marker, Popup } from 'react-map-gl'

...

function MarkerWithPopup ({ hike, latitude, longitude }) {
  const [showPopup, setShowPopup] = useState(false)

  // event listener that toggles whether the popup is displayed
  const handleMarkerClick = ({ originalEvent }) => {
    originalEvent.stopPropagation()
    setShowPopup(true)
  }

  // render the marker and the popup, render the CardB again within the popup.
  return (
    <>
      <Marker
        latitude={latitude}
        longitude={longitude}
        onClick={handleMarkerClick}
      />
      {showPopup && (
        <Popup
          latitude={latitude}
          longitude={longitude}
          offset={{ bottom: [0, -40] }}
          onClose={() => setShowPopup(false)}
          maxWidth='95%'
          closeOnMove
        >
          <CardB hike={hike} />
        </Popup>
      )}
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

Change the application to display the MarkerWithPopup instead of just the original marker!

{
  hikes.map(hike => (
    <MarkerWithPopup
      latitude={hike.lat}
      longitude={hike.long}
      hike={hike}
      key={hike.id}
    />)
  )
}
Enter fullscreen mode Exit fullscreen mode

10. Add Styling

I also added the following CSS to my index.css file, please forgive the !importants! They were needed to override the existing styling on the popups!

@import url('https://fonts.googleapis.com/css2?family=Inter:slnt,wght@-10..0,100..900&display=swap');

.container {
  display: flex;
}

.maplibregl-popup-content, .mapboxgl-popup-content {
  background-color: transparent !important;
  box-shadow: none !important;
  border-radius: none !important;
  z-index: 5 !important;
  max-width: 95% !important;
}

.maplibregl-popup-close-button, .mapboxgl-popup-close-button {
  color: white !important;
  font-size: 30px !important;
  font-weight: bold;
}
Enter fullscreen mode Exit fullscreen mode

Image description

Now your app should have working popups when you click on a marker!

Conclusion

In this tutorial, we used Amplify Studio, the Amplify CLI, and Amplify UI components to build out a map interface. If you want to delete the resources used in this tutorial, run amplify delete in your CLI. I would love to hear your feedback as you build with Amplify, especially since Studio is still in developer preview at the time of this writing!