> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/mapbox-tiling-service/ja/llms.txt)

# Create a temperature map

In this example, you'll process GFS temperature data from NOAA using [Raster MTS](https://docs.mapbox.com/mapbox-tiling-service/ja/raster/#what-is-raster-mts). This is a multi-band dataset where each band is a single timestamp.

The goal is to create a `raster-array` [source](https://docs.mapbox.com/style-spec/reference/sources/#raster-array) and add it on a map to visualize temperature.

## Prerequisites

To follow along, you'll need:

-   [GFS](https://www.ncei.noaa.gov/products/weather-climate-models/global-forecast) data (sample download below)

[Sample GFS temperature data](https://mts-recipe-example-data.s3.amazonaws.com/gfs-temperature.nc)

### 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](https://docs.mapbox.com/mapbox-tiling-service/ja/guides/tileset-sources/). 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](https://docs.mapbox.com/help/glossary/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:

```js
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`](https://gdal.org/programs/gdalinfo.html) is a useful tool for understanding the structure of your data.

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

```bash
$ 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**

```shell
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"
```

**tilesets-cli**

```shell
tilesets upload-raster-source $ACCOUNT $SOURCE_NAME $SOURCE_LOCATION
```

## 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.

```json
{
  "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 (warning): 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.

```json
"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:

```json
"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:

```json
"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**

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

**tilesets-cli**

```shell
tilesets create $TILESET_ID \
    --recipe $RECIPE_LOCATION \
    --name "tilesets example tileset" 
```

  

> **Note (warning): 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.
> 
> ```json
> {
>   "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**

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

**tilesets-cli**

```shell
tilesets publish $TILESET_ID
```

## 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`.

```bash
$ 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](https://docs.mapbox.com/help/glossary/tilejson/) for this tileset, which stores all the useful metadata that can be used for map visualizations.

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

Below is the (truncated) output

```json
"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](https://docs.mapbox.com/style-spec/reference/sources/#raster-array), 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](https://docs.mapbox.com/help/glossary/layout-paint-property/) to assign color using [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/guides/).

```html
<!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.25.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.25.0/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](#create-a-recipe) step.