メインコンテンツまでスキップ

Create a temperature map

In this example, you'll process GFS temperature data from NOAA using Raster MTS. This is a multi-band dataset where each band is a single timestamp.

The goal is to create a raster-array source and add it on a map to visualize temperature.

Prerequisites

To follow along, you'll need:

  • GFS data (sample download below)
arrow-downSample GFS temperature data

Environment variables

Throughout this example, you'll use the following required environment variables:

  • ACCOUNT - Set this to your account username.
  • ENDPOINT - The path to the Mapbox tilesets API.
  • RECIPE_LOCATION - The local file location of your Raster MTS recipe.
  • SOURCE_NAME - The name used to create your tileset source. Once your tileset source has been created, you must reference that returned tileset source URI in your recipe.
  • SOURCE_LOCATION - The local file location of your raster source.
  • TILESET_ID - A unique identifier given to your tileset. A tileset ID always starts with your Mapbox username, followed by the tileset's unique alphanumeric identifier: username.identifier.
  • YOUR_SECRET_MAPBOX_ACCESS_TOKEN - A secret token for your Mapbox account with tilesets:write (for uploading source, creating tileset and publish job) and tilesets:read scope (for getting job status).

And when replacing these variables in code snippets or commands, refer to this list:

ACCOUNT=your-account-name
ENDPOINT="https://api.mapbox.com/tilesets/v1"
RECIPE_LOCATION="/path/to/file/gfs-temperature.json"
SOURCE_NAME=gfs-temperature-source
SOURCE_LOCATION="/path/to/file/gfs-temperature.nc"
TILESET_ID=your-account-name.gfs-temperature
YOUR_SECRET_MAPBOX_ACCESS_TOKEN="<Insert your own sk.* token here>"

Review raster source metadata

The GDAL tool gdalinfo is a useful tool for understanding the structure of your data.

Running gdalinfo on this sample source, you'll see the (truncated) output:

$gdalinfo gfs-temperature.nc
...
Band 1 Block=1440x45 Type=Float32, ColorInterp=Undefined
NoData Value=-9999
Unit Type: K
Metadata:
amip=ta
coordinates=reftime time height_above_ground3 latitude longitude
description=Air temperature is the bulk temperature of the air, not the surface (skin) temperature.
grib=11 E130
Grib2_Generating_Process_Type=Forecast
Grib2_Level_Desc=Specified height level above ground
Grib2_Level_Type=103
Grib2_Parameter={0,0,0}
Grib2_Parameter_Category=Temperature
Grib2_Parameter_Discipline=Meteorological products
Grib2_Parameter_Name=Temperature
Grib2_Statistical_Process_Type=UnknownStatType--1
Grib_Variable_Id=VAR_0-0-0_L103
grid_mapping=LatLon_721X1440-0p13S-180p00E
long_name=Temperature @ Specified height level above ground
NETCDF_DIM_height_above_ground3=2
NETCDF_DIM_time=0
NETCDF_VARNAME=Temperature_height_above_ground
standard_name=air_temperature
units=K
_FillValue=-9999

Create a tileset source

First, you'll create a tileset source by uploading your GFS data to MTS. If you're using the API directly, you'll need to make a request for each individual source file that you'll be referencing in your recipe. The example GFS data you're processing has only a single source file, so you'll need to make the following POST request once.

curl \
-X POST "${ENDPOINT}/sources/${ACCOUNT}/${SOURCE_NAME}?access_token=${YOUR_SECRET_MAPBOX_ACCESS_TOKEN}" \
-F file=@${SOURCE_LOCATION} \
--header "Content-Type: multipart/form-data"

Create a recipe

A recipe is a set of instructions used for processing raster data. To use your recipe you will need to create a file and add your own information:

  1. Create a .json file named gfs-temperature.json.
  2. Paste in the recipe below.
  3. Replace {ACCOUNT} and {SOURCE_NAME} with your own values.
{
"version": 1,
"type": "rasterarray",
"sources": [
{
"uri": "mapbox://tileset-source/{ACCOUNT}/{SOURCE_NAME}"
}
],
"minzoom": 0,
"maxzoom": 3,
"layers": {
// Layer name "2t" is based on naming conventions from https://cfconventions.org/
// 2t represents air temperature at 2 meters above ground.
"2t": {
"tilesize": 256,
"offset": -100,
"scale": 0.1,
"resampling": "bilinear",
"buffer": 1,
"units": "K",
"source_rules": {
"filter":[
"all",
["==",["get","NETCDF_VARNAME"],"Temperature_height_above_ground"],
["==",["get","NETCDF_DIM_height_above_ground3"],"2"]
],
"name":["to-number",["get","NETCDF_DIM_time"]],
"order":"asc"
}
}
}
}

Note

The source_rules field allows you to select a single band within a raster file. For example, in this recipe, the temperature data exists in a band labeled with "Temperature_height_above_ground".

The following construct indicates that Raster MTS should only process bands that have a NETCDF_VARNAME that includes Temperature_height_above_ground where the NETCDF_DIM_height_above_ground3 dimension is 2.

"filter":[
"all",
["==",["get","NETCDF_VARNAME"],"Temperature_height_above_ground"],
["==",["get","NETCDF_DIM_height_above_ground3"],"2"]
],

The recipe then names the constructed slice of data with:

"name":["to-number",["get","NETCDF_DIM_time"]],

These value will be later available as "band" used to add on the map.

The recipe then organizes those distinct slices of data with:

"sort_key":["to-number",["get","NETCDF_DIM_time"]],
"order":"asc"

Create an empty tileset

Now you will create an empty tileset and provide the recipe to be used for processing.

curl \
-X POST "${ENDPOINT}/${TILESET_ID}?access_token=${YOUR_SECRET_MAPBOX_ACCESS_TOKEN}" \
-d @"${RECIPE_LOCATION}" \
--header "Content-Type:application/json"

Note

If you're using the API directly, you'll need to wrap your recipe in an additional JSON structure to include a recipe and name property.

{
"recipe": {...},
"name": "example_tileset_name"
}

Publish a tileset

At this point you have uploaded a source, created an empty tileset, and provided a recipe to be used for processing the source to this tileset. Now you can start processing your tileset by publishing it.

curl -X POST "${ENDPOINT}/${TILESET_ID}/publish?access_token=${YOUR_SECRET_MAPBOX_ACCESS_TOKEN}"

Review tileset job information

At this point, Raster MTS will begin to process your job with a job_id. You can retrieve your job's information by calling the API endpoint below with your job_id.

$curl "\${ENDPOINT}/\${TILESET_ID}/jobs/{job_id}?access_token=\${YOUR_SECRET_MAPBOX_ACCESS_TOKEN}"

Review tileset TileJSON

Once the tileset is finished processing, you can retrieve TileJSON for this tileset, which stores all the useful metadata that can be used for map visualizations.

$curl "\${ENDPOINT}/\${TILESET_ID}.json?access_token=\${YOUR_SECRET_MAPBOX_ACCESS_TOKEN}"

Below is the (truncated) output

"raster_layers": [
{
"fields": {
// Values extracted from NetCDF's "NETCDF_DIM_time" variables become bands
// Used in "raster-array-band" paint property
"bands": ["0", "3", "6"...],
"buffer": 1,
"name": "2t",
"offset": 0,
// Used in "raster-color-range" paint property
"range": [
204.5353240966797,
323.64263916015625
],
"scale": 1,
"tilesize": 256,
"units": ""
},
// Used in "raster-array-layer" layout property
"id": "2t",
"maxzoom": 3,
"minzoom": 0
}
],

The id, band and range keys in the TileJSON identify which layer and band to display and how to assign colors to the values.

Add tileset to map

Using this tileset as a raster-array source, you will add it as a raster layer on the map.

You will use raster-array-band to allot value 3, which represents the band named 3 in the source NetCDF file. You will use paint properties to assign color using Mapbox GL JS.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Create a temperature map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.5.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.5.1/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new mapboxgl.Map({
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
container: 'map',
style: 'mapbox://styles/mapbox/light-v11',
zoom: 0,
center: [0, 0],
projection: 'mercator'
});

map.on('load', () => {
map.addSource('gfs-temperature', {
type: 'raster-array',
// Replace this URL with a 'mapbox://TILESET_ID'
url: 'mapbox://mapbox.gfs-temperature',
tileSize: 256
});
map.addLayer({
id: 'gfs-temperature-layer',
type: 'raster',
source: 'gfs-temperature',
// If your tileset has multiple layers, you can specify which one to use with the 'source-layer'.
// In this example we're specifying the '2t' temperature layer defined in the recipe above.
'source-layer': '2t',
paint: {
'raster-color-range': [204, 323],
'raster-array-band': '3',
'raster-color': [
'interpolate',
['linear'],
['raster-value'],
204,
'#50509B',
266,
'#FAFAA0',
323,
'#96053C'
],
'raster-resampling': 'nearest'
}
});
});
</script>

</body>
</html>

Troubleshooting

Here are solutions to some common integration problems:

Empty Tileset

  • If you receive an empty tileset error when trying to publish, check that you updated the recipe with your credentials in the Create A Recipe step.
このexampleは役に立ちましたか?