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.
- Open your text editor and create a new file named
index.html
. - 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/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/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-v12', // 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.
-
If the value of
mapboxgl.accessToken
isYOUR_MAPBOX_ACCESS_TOKEN
, replace that string with your Mapbox token, as described in Getting started. -
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.
- 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.
- 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
- 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>
- 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;
}
- Finally, create three new constants that you will use to target the new
<span>
IDs. Add the following code in your JavaScript, below thelng
andlat
constants:
const lngDisplay = document.getElementById('lng');
const latDisplay = document.getElementById('lat');
const eleDisplay = document.getElementById('ele');
- 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, contour
s 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.
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.
- Write a function
getElevation()
that uses thelng
andlat
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.
- Now, update the
map.on('click')
function to callgetElevation()
when a user clicks on the map:
map.on('click', (event) => {
lng = event.lngLat.lng;
lat = event.lngLat.lat;
getElevation();
});
- 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 seeproperties.ele
for that feature.elevations
contains everyproperties.ele
value.highestElevation
contains the largest value inelevations
, and so the highest point returned by the Tilequery API for the requested coordinates.
Display the returned values
- 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
.
- 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.
- 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);
- 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/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/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: <span id="lng"></span></div>
<div>Latitude: <span id="lat"></span></div>
<div>Elevation: <span id="ele"></span></div>
</div>
<script>
mapboxgl.accessToken = '{{MAPBOX_ACCESS_TOKEN}}';
const map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/outdoors-v12', // 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:
- Explore the Tilequery API documentation for more information on how to use the optional parameters.
- Learn how to use the Tilequery API outside of a map with the Create a timezone finder with the Tilequery API tutorial.
- Learn how to use the Tilequery API in conjunction with the Mapbox Geocoding API to query results from a custom tileset with the Make a healthy food finder with the Tilequery API tutorial.