Building a 3D React Map Component Using Mapbox

tylerben

Ben Tyler

Posted on January 20, 2021

Building a 3D React Map Component Using Mapbox

This post is part of my Building Interactive Maps with React course - a course for anyone wanting to learn how to build interactive maps and integrate them into their React applications. If you enjoy this guide, then chances are you will enjoy the course too!

Mapbox released GL JS V2 recently which has a whole slew of awesome features, but the one I am most excited is the addition of 3D terrain rendering. I have been waiting for this feature for a looooooooong time. A lot of the applications I build are focused on the outdoors and feature an interactive map. Just about every one of these apps would benefit greatly from the ability to render things in 3D. That is a now a reality!

The best part about the new release is just how easy it is to render 3D terrain. This post will walk you through how to create a React Map component with 3D terrain rendering. The process is more or less exactly what you would follow for creating any other Mapbox Gl JS map in React.

Before You Get Started

This guide assumes the following:

  • you are adding this component to an existing React app. If you do not already have an app to add this too or are unsure of how to setup a React app, check out the React docs.
  • you already have a Mapbox account and access token. You can sign up here

Installing Mapbox

To get started, let's install Mapbox.


# yarn
yarn add mapbox-gl

# npm
npm install mapbox-gl --save

Enter fullscreen mode Exit fullscreen mode

Then make sure you include the GL JS CSS file in the <head> of your html document. If you are using Create React App or a similarly structured app, add it to the <head> of the index.html file in the public directory.

<!-- index.html -->
<link
  href="https://api.mapbox.com/mapbox-gl-js/v2.0.0/mapbox-gl.css"
  rel="stylesheet"
/>
Enter fullscreen mode Exit fullscreen mode

Developing the Map Component

The next several steps will walk you through how to create a dead simple Map component with 3D rendering enabled. Create a new component called Map and then copy and paste the snippet below. This will render a simple interactive map.

// Map.js
import React, { useRef, useEffect } from "react"
import mapboxgl from "mapbox-gl"

// Grab the access token from your Mapbox account
// I typically like to store sensitive things like this
// in a .env file
mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_TOKEN

export const Map = () => {
  const mapContainer = useRef()

  // this is where all of our map logic is going to live
  // adding the empty dependency array ensures that the map
  // is only created once
  useEffect(() => {
    // create the map and configure it
    // check out the API reference for more options
    // https://docs.mapbox.com/mapbox-gl-js/api/map/
    const map = new mapboxgl.Map({
      container: "map",
      style: "mapbox://styles/mapbox/satellite-streets-v11",
      center: [-119.99959421984575, 38.619551620333496],
      zoom: 14,
    })
  }, [])

  return (
    <div
      id="map"
      ref={mapContainer}
      style={{ width: "100%", height: "100vh" }}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

Taking It All 3D

At this point you should have a basic 2D satellite streets map rendering successfully. Converting this rendering to 3D is a surprisingly small amount of work. We need to do the following:

  • adjust the map pitch (aka the camera angle) so that we are not looking straight down at the map
  • add the Mapbox DEM (digital elevation model) source to our map

First, we add the pitch property to the map configuration. This value can be between 0 and 85. For this example, I personally prefer 60. And then lastly, we need to add a load event listener and define the logic for adding Mapbox's DEM tiles, generating the 3D terrain, and adding a sky layer for a nice touch.

That's it! If you revisit your app, you should not have a 3D rendering that resembles what you would see in Google Earth.

// Map.js
import React, { useRef, useEffect } from "react"
import mapboxgl from "mapbox-gl"

// Grab the access token from your Mapbox account
// I typically like to store sensitive things like this
// in a .env file
mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_TOKEN

export const Map = () => {
  const mapContainer = useRef()

  // this is where all of our map logic is going to live
  // adding the empty dependency array ensures that the map
  // is only created once
  useEffect(() => {
    // create the map and configure it
    // check out the API reference for more options
    // https://docs.mapbox.com/mapbox-gl-js/api/map/
    const map = new mapboxgl.Map({
      container: "map",
      style: "mapbox://styles/mapbox/satellite-streets-v11",
      center: [-119.99959421984575, 38.619551620333496],
      zoom: 14,
      pitch: 60,
    })

    map.on("load", () => {
      map.addSource("mapbox-dem", {
        type: "raster-dem",
        url: "mapbox://mapbox.mapbox-terrain-dem-v1",
        tileSize: 512,
        maxZoom: 16,
      })
      map.setTerrain({ source: "mapbox-dem", exaggeration: 1.5 })
      map.addLayer({
        id: "sky",
        type: "sky",
        paint: {
          "sky-type": "atmosphere",
          "sky-atmosphere-sun": [0.0, 90.0],
          "sky-atmosphere-sun-intensity": 15,
        },
      })
    })
  }, [])

  return (
    <div
      id="map"
      ref={mapContainer}
      style={{ width: "100%", height: "100vh" }}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

If you found this post useful, please retweet, share, or pick up a copy of The Mapbox Developer's Handbook!

💖 💪 🙅 🚩
tylerben
Ben Tyler

Posted on January 20, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related