All docsHelpTutorialsFind elevations with the Tilequery API

Find elevations with the Tilequery API

In this tutorial, you will use the Mapbox Tilequery API and the Mapbox Terrain vector tileset to create an app that, when a user clicks a point on the map, returns the elevation at that point.

Getting started

To complete this tutorial, you will need:

  • A Mapbox access token. Your Mapbox access tokens are on your Account page.
  • Mapbox GL JS. Mapbox GL JS is a JavaScript API for building web maps.
  • Mapbox Tilequery API. The Tilequery API allows you to retrieve data about specific features from a vector tileset, based on a given latitude and longitude.
  • Mapbox Terrain vector tileset. The Mapbox Terrain vector tileset provides terrain data, including elevation contours.
  • A text editor. Use the text editor of your choice for writing HTML, CSS, and JavaScript.

Create a map

To get started, you will create a map using Mapbox GL JS.

  1. Open your text editor and create a new file named index.html.
  2. Copy the following code and paste it into the new file:
<!DOCTYPE html>
<html lang='en'>

<head>
  <meta charset='utf-8' />
  <title>Find elevations with the Tilequery API</title>
  <meta name='viewport' content='width=device-width, initial-scale=1' />
  <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js'></script>
  <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css' rel='stylesheet' />
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }
  </style>
</head>

<body>

  <div id='map'></div>

  <script>
    mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
    const map = new mapboxgl.Map({
      container: 'map', // Specify the container ID
      style: 'mapbox://styles/mapbox/outdoors-v11', // Specify which map style to use
      center: [-122.43877, 37.75152], // Specify the starting position [lng, lat]
      zoom: 11.5 // Specify the starting zoom
    });
  </script>

</body>

</html>    

This code creates the structure of the page. This code also imports Mapbox GL JS in the <head> of the page. The Mapbox GL JS JavaScript and CSS files allow you to use Mapbox GL JS functionality and style.

This Mapbox GL JS code sets the style for the map to Mapbox Outdoors, gives it coordinates on which to center, and sets a zoom level.

The <div> element with the ID map in the <body> of the page is the container in which the map will be displayed on the page.

  1. If the value of mapboxgl.accessToken is YOUR_MAPBOX_ACCESS_TOKEN, replace that string with your Mapbox token, as described in Getting started.

  2. Save your changes. Open the HTML file in your browser to see the rendered map, which is centered on San Francisco.

Get coordinates when a user clicks

In this app, the coordinates used in the Tilequery API request will be generated when a user clicks on the map. To get these coordinates, you will create a map.on('click') function that gets the longitude and the latitude at the clicked location.

  1. Add the following JavaScript above the closing </script> tag in your HTML file:
// Create constants for the latitude and longitude.
let lng;
let lat;

map.on('click', (event) => {
  // When the map is clicked, set the lng and lat constants
  // equal to the lng and lat properties in the returned lngLat object.
  lng = event.lngLat.lng;
  lat = event.lngLat.lat;
  console.log(`${lng}, ${lat}`);
});

When the map is clicked, the returned event includes a Mapbox GL JS lngLat object, which includes the longitude and latitude of the clicked point. The map.on('click') function sets the lng and lat constants equal to these properties.

  1. Save your work, then refresh your browser page and open the browser's developer tools. When you click on the map, your app will print the clicked point's longitude and latitude to the JavaScript console.

Add the sidebar

  1. In the <body> of your HTML, directly below your <div id='map'></div> element, add a new <div>. This <div> will be used to show the clicked point's longitude, latitude, and elevation:
<div class="ele-info">
  <div>Longitude: <span id='lng'></span></div>
  <div>Latitude: <span id='lat'></span></div>
  <div>Elevation: <span id='ele'></span></div>
</div>
  1. To style the new <div>, add the following CSS to the <style> section of your HTML:
.ele-info {
  position: absolute;
  font-family: sans-serif;
  margin-top: 5px;
  margin-left: 5px;
  padding: 5px;
  width: 200px;
  border: 2px solid black;
  font-size: 20px;
  color: #222;
  background-color: #fff;
}
  1. Finally, create three new constants that you will use to target the new <span> IDs. Add the following code in your JavaScript, below the lng and lat constants:
const lngDisplay = document.getElementById('lng');
const latDisplay = document.getElementById('lat');
const eleDisplay = document.getElementById('ele');
  1. Save your work and refresh the page. The new sidebar will be on the left side of the page. In an upcoming step, you will use the <span> IDs that you created to display the longitude, latitude, and elevation in the sidebar.

Tilequery API request format

The Mapbox Tilequery API allows you to retrieve data about specific features from a vector tileset, based on a given latitude and longitude.

A Tilequery request has two required two parameters:

  • tileset_id: The identifier of the tileset being queried.
  • {lon, lat}: The coordinates of the query point.

The Tilequery API also accepts several optional parameters. In this tutorial, you will use two optional parameters:

  • limit: This parameter allows you to specify the maximum number of results that a query can return. The default number of results is five, and the maximum number is 50. The specified number of features are returned in order of their proximity to the queried {lon},{lat}.
  • layers: This parameter allows you to only return results from specific layers in the queried tileset.

An example call to the Tilequery API that has a limit of 50 results and only returns results from the contour layer looks like:

https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2/tilequery/-105.01109,39.75953.json?layers=contour&limit=50&access_token=YOUR_MAPBOX_ACCESS_TOKEN

A Tilequery API request returns a GeoJSON FeatureCollection of features at or near the geographic point described by {lon},{lat}. To learn more about the Tilequery API, its other optional parameters, and its response object, explore the Tilequery API documentation.

Mapbox Terrain tileset

In this tutorial, you will use the Tilequery API to query the Mapbox Terrain vector tileset, which includes features like topography, hillshades, and landcover. Specifically, you will use the layers parameter to return features in the contour source layer, which contains a property called ele. This property is the elevation value in meters, and is mapped to 10 meter height increments.

In the Mapbox Terrain tileset, contours are comprised of stacked polygons. This means that most Tilequery API requests that query the Mapbox Terrain tileset will return multiple features from the contour layer. Because the elevation data you want is included in the contour layer, you will need to parse the returned GeoJSON to isolate the features from the contour layer and find the highest elevation value.

Elevation data limitations in the Mapbox Terrain tileset

The Tilequery API's limit parameter allows 50 or fewer features in a response. Since this tutorial uses the Mapbox Terrain vector tileset, which includes elevation data at 10 meter increments, 50 returned features may not be enough to return the largest value for some locations at high elevations. The API returns the specified number of features in order of their proximity to the query point. It is not possible to request features in any other order (for example, returning the features with the highest elevations first).

Also, since the elevations are returned in 10 meter increments, the results may lose some nuance. For example, a hill that is 264 meters high would return a highest elevation of 260 meters. Mapbox does not have any other vector data sources with more precise elevation data than is available in the Terrain tileset.

If you are using the Tilequery API for areas with steep elevation changes, or if you require precise elevations, we recommend that you use the Tilequery API to query a custom tileset that contains this information.

Add the Tilequery API call

You have already created constants that capture the longitude and latitude when a user clicks on the map. In this step, you will use these lng and lat constants in a call to the Tilequery API.

  1. Write a function getElevation() that uses the lng and lat values to construct a Tilequery API request, then uses fetch to send the request and retrieve the results. Add the following code to your JavaScript, before the closing </script> tag:
async function getElevation() {
  // Construct the API request.
  const query = await fetch(
    `https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2/tilequery/${lng},${lat}.json?layers=contour&limit=50&access_token=${mapboxgl.accessToken}`,
    { method: 'GET' }
  );
  if (query.status !== 200) return;
  const data = await query.json();
  // Get all the returned features.
  const allFeatures = data.features;
  console.log(allFeatures);
  // For each returned feature, add elevation data to the elevations array.
  const elevations = allFeatures.map((feature) => feature.properties.ele);
  console.log(elevations);
  // In the elevations array, find the largest value.
  const highestElevation = Math.max(...elevations);
  console.log(highestElevation);
}

The Tilequery API returns feature objects. Since the feature objects in this query come from the contour layer, each of them contains a properties.ele value.

For each item in allFeatures, this code snippet takes the properties.ele value and pushes it to the elevations array. Then, using Math.max and spread syntax (...elevations), it sets the highestElevation constant to the largest value in the elevations array.

  1. Now, update the map.on('click') function to call getElevation() when a user clicks on the map:
map.on('click', (event) => {
  lng = event.lngLat.lng;
  lat = event.lngLat.lat;

  getElevation();
});
  1. Save your changes and open your developer tools. Refresh the page in your browser and click on the map. On click, three items will print to the console:
  • allFeatures contains all the feature objects returned by the call to the Tilequery API. You can explore each feature object to see properties.ele for that feature.
  • elevations contains every properties.ele value.
  • highestElevation contains the largest value in elevations, and therefore the highest point returned by the Tilequery API for the requested coordinates.

Display the returned values

  1. Now that you have seen the results of the Tilequery API request printed to the console, your next step is to display them in the sidebar that you created earlier. Update the getElevation() function:
async function getElevation() {
  // Construct the API request.
  const query = await fetch(
    `https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2/tilequery/${lng},${lat}.json?layers=contour&limit=50&access_token=${mapboxgl.accessToken}`,
    { method: 'GET' }
  );
  if (query.status !== 200) return;
  const data = await query.json();
  // Display the longitude and latitude values.
  lngDisplay.textContent = lng.toFixed(2);
  latDisplay.textContent = lat.toFixed(2);
  // Get all the returned features.
  const allFeatures = data.features;
  // For each returned feature, add elevation data to the elevations array.
  const elevations = allFeatures.map((feature) => feature.properties.ele);
  // In the elevations array, find the largest value.
  const highestElevation = Math.max(...elevations);
  // Display the largest elevation value.
  eleDisplay.textContent = `${highestElevation} meters`;
}

This updated function sets the value of lngDisplay and latDisplay to the clicked longitude and latitude respectively, and uses toFixed(2) to limit the displayed value to two decimal points. It also sets the value of eleDisplay to highestElevation.

  1. Save your changes and refresh the page in your browser. Now, when you click a location on the map, its longitude, latitude, and elevation will display in the sidebar.

Add a marker to the clicked location

The final step is to add a marker to the map at the coordinates where the user clicks using the Mapbox GL JS Marker class. This will help the user better visualize the location that they are getting coordinates and elevation data for.

  1. Add the following code to your JavaScript, before the map.on('click') function:
const marker = new mapboxgl.Marker({
  color: '#314ccd'
});

You can use the Mapbox GL JS lngLat object that is returned when the map is clicked to set the marker's location. To draw the marker on the map at the clicked coordinates, add the following code inside of your map.on('click') function:

marker.setLngLat(lngLat).addTo(map);
  1. Save your work and refresh the page. Now when you click on the map, you will see a blue marker at the clicked coordinates.

Final product

You have created an app that returns a clicked point's elevation and displays it on the map.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Demo: Find elevations with the Tilequery API</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css"
rel="stylesheet"
/>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.ele-info {
position: absolute;
font-family: sans-serif;
margin-top: 5px;
margin-left: 5px;
padding: 5px;
width: 200px;
border: 2px solid black;
font-size: 20px;
color: #222;
background-color: #fff;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="ele-info">
<div>Longitude:&nbsp;<span id="lng"></span></div>
<div>Latitude:&nbsp;<span id="lat"></span></div>
<div>Elevation:&nbsp;<span id="ele"></span></div>
</div>
<script>
mapboxgl.accessToken = '<your access token here>';
const map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/outdoors-v11', // stylesheet location
center: [-122.4387738485193, 37.75152511125579], // starting position [lng, lat]
zoom: 11.5 // starting zoom
});
let lng;
let lat;
const lngDisplay = document.getElementById('lng');
const latDisplay = document.getElementById('lat');
const eleDisplay = document.getElementById('ele');
const marker = new mapboxgl.Marker({
'color': '#314ccd'
});
map.on('click', (event) => {
// Use the returned LngLat object to set the marker location
// https://docs.mapbox.com/mapbox-gl-js/api/#lnglat
marker.setLngLat(event.lngLat).addTo(map);
lng = event.lngLat.lng;
lat = event.lngLat.lat;
getElevation();
});
async function getElevation() {
// Construct the API request
const query = await fetch(
`https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2/tilequery/${lng},${lat}.json?layers=contour&limit=50&access_token=${mapboxgl.accessToken}`,
{ method: 'GET' }
);
if (query.status !== 200) return;
const data = await query.json();
// Display the longitude and latitude values
lngDisplay.textContent = lng.toFixed(2);
latDisplay.textContent = lat.toFixed(2);
// Get all the returned features
const allFeatures = data.features;
// For each returned feature, add elevation data to the elevations array
const elevations = allFeatures.map((feature) => feature.properties.ele);
// In the elevations array, find the largest value
const highestElevation = Math.max(...elevations);
// Display the largest elevation value
eleDisplay.textContent = `${highestElevation} meters`;
}
</script>
</body>
</html>

Next steps

To build on top of the tools and techniques you used in this tutorial, explore the following resources: