---
title: "Weather wind and temperature layer | Weather SDK | JavaScript maps SDK"
description: "Visualize wind and temperature layers with cursor data."
---

> For AI agents: a documentation index is available at [/llms.txt](https://docs.maptiler.com/llms.txt). Markdown versions of all pages are available by appending .md to any URL.

# Weather wind and temperature layer

Experience the power of visualizing weather conditions, including wind speed and temperature, with the help of MapTiler’s Weather Wind dataset. By accessing this dataset, you can easily retrieve information about wind speed and temperature at any specific location.

To enhance the user experience, we have implemented a unique feature in this example. Utilizing the `ColorRamp.builtin.NULL` on the wind layer, we have ensured that only the wind direction particles are displayed above the temperature layer. Additionally, we have incorporated a convenient time control that allows you to select a specific date and time to view the corresponding data. The time control is generated using the `getAnimationStartDate()` and `getAnimationEndDate()` functions of the layer, which define the minimum and maximum dates available for exploration.

### NPM module setup

```bash
npm install --save @maptiler/sdk @maptiler/weather
```

#### main.js

```js
import { Map, MapStyle, config } from '@maptiler/sdk';
import '@maptiler/sdk/dist/maptiler-sdk.css';
import { WindLayer, TemperatureLayer, ColorRamp } from '@maptiler/weather';

const timeInfoContainer = document.getElementById("time-info");
const timeTextDiv = document.getElementById("time-text");
const timeSlider = document.getElementById("time-slider");
const playPauseButton = document.getElementById("play-pause-bt");
const pointerDataDiv = document.getElementById("pointer-data");
let pointerLngLat = null;

config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

const map = new Map({
  container: document.getElementById('map'),
  hash: true,
  zoom: 2,
  center: [0, 40],
  style: MapStyle.BACKDROP,
  projectionControl: true,
  projection: 'globe'
});

const layerBg = new TemperatureLayer({
  opacity: 0.8,
});

const layer = new WindLayer({
  id: "Wind Particles",
  colorramp: ColorRamp.builtin.NULL,
  speed: 0.001,
  fadeFactor: 0.03,
  maxAmount: 256,
  density: 200,
  color: [0, 0, 0, 30],
  fastColor: [0, 0, 0, 100],
});

map.on('load', function () {
  // Darkening the water layer to make the land stand out
  map.setPaintProperty("Water", 'fill-color', "rgba(0, 0, 0, 0.6)");
  map.addLayer(layer);
  map.addLayer(layerBg, "Water");
  
});

timeSlider.addEventListener("input", (evt) => {
  layer.setAnimationTime(parseInt(timeSlider.value / 1000))
  layerBg.setAnimationTime(parseInt(timeSlider.value / 1000))
})

// Event called when all the datasource for the next days are added and ready.
// From now on, the layer nows the start and end dates.
layer.on("sourceReady", event => {
  const startDate = layer.getAnimationStartDate();
  const endDate = layer.getAnimationEndDate();
  const currentDate = layer.getAnimationTimeDate();
  refreshTime()

  timeSlider.min = +startDate;
  timeSlider.max = +endDate;
  timeSlider.value = +currentDate;
})

// Called when the animation is progressing
layer.on("tick", event => {
  refreshTime();
  updatePointerValue(pointerLngLat);
})

// Called when the time is manually set
layer.on("animationTimeSet", event => {
  refreshTime()
})

// When clicking on the play/pause
let isPlaying = false;
playPauseButton.addEventListener("click", () => {
  if (isPlaying) {
    layer.animateByFactor(0);
    layerBg.animateByFactor(0);
    playPauseButton.innerText = "Play 3600x";
  } else {
    layer.animateByFactor(3600);
    layerBg.animateByFactor(3600);
    playPauseButton.innerText = "Pause";
  }

  isPlaying = !isPlaying;
})

// Update the date time display
function refreshTime() {
  const d = layer.getAnimationTimeDate();
  timeTextDiv.innerText = d.toString();
  timeSlider.value = +d;
}

function updatePointerValue(lngLat) {
  if (!lngLat) return;
  pointerLngLat = lngLat;
  const valueWind = layer.pickAt(lngLat.lng, lngLat.lat);
  const valueTemp = layerBg.pickAt(lngLat.lng, lngLat.lat);
  if (!valueWind) {
    pointerDataDiv.innerText = "";
    return;
  }
  pointerDataDiv.innerText = `${valueTemp.value.toFixed(1)}°C \n ${valueWind.speedKilometersPerHour.toFixed(1)} km/h`
}

timeInfoContainer.addEventListener("mouseenter", () => {
  pointerDataDiv.innerText = "";
})

map.on('mousemove', (e) => {
  updatePointerValue(e.lngLat);
});
```

#### index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="robots" content="noindex" />
    <title>Weather wind and temperature layer | NPM example</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="time-info">
      <span id="time-text"></span>
      <button id="play-pause-bt" class="button">Play 3600x</button>
      <input type="range" id="time-slider" min="0" max="11" step="1" />
    </div>

    <div id="variable-name">Temperature + Wind</div>
    <div id="pointer-data"></div>
    <div id="map"></div>
    <script type="module" src="./main.js"></script>
  </body>
</html>
```

#### style.css

```css
body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
}

#map {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
  background-color: #444952;
}

#time-info {
  position: fixed;
  width: 60vw;
  bottom: 0;
  z-index: 1;
  margin: 10px;
  text-shadow: 0px 0px 5px black;
  color: white;
  font-size: 20px;
  font-weight: 500;
  text-align: center;
  left: 0;
  right: 0;
  margin: auto;
  padding: 20px;
}

#time-slider {
  width: 100%;
  height: fit-content;
  left: 0;
  right: 0;
  z-index: 1;
  filter: drop-shadow(0 0 7px #000a);
  margin-top: 10px;
}

#pointer-data {
  z-index: 1;
  position: fixed;
  font-size: 50px;
  font-weight: 900;
  margin: 27px 0px 0px 10px;
  color: #fff;
  text-shadow: 0px 0px 10px #0007;
}

#variable-name {
  z-index: 1;
  position: fixed;
  font-size: 20px;
  font-weight: 500;
  margin: 5px 0px 0px 10px;
  color: #fff;
  text-shadow: 0px 0px 10px #0007;
}

.button {
  cursor: pointer;
  width: auto;
  padding: 8px;
  border-radius: 3px;
  margin: 10px 0 0 0;
  font-size: 12px;
  text-align: center;
  color: #fff;
  background: #3174ff;
  font-family: sans-serif;
  font-weight: bold;
}
```

### Basic JavaScript setup

```html
<!DOCTYPE html>
<html>
<head>
  <title>MapTiler Wind + Temperature</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <script src="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.umd.min.js"></script>
  <link href="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.css" rel="stylesheet" />
  <script src="https://cdn.maptiler.com/maptiler-weather/v4.0.1/maptiler-weather.umd.min.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
      font-family: sans-serif;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
      background-color: #444952;
    }

    #time-info {
      position: fixed;
      width: 60vw;
      bottom: 0;
      z-index: 1;
      margin: 10px;
      text-shadow: 0px 0px 5px black;
      color: white;
      font-size: 20px;
      font-weight: 500;
      text-align: center;
      left: 0;
      right: 0;
      margin: auto;
      padding: 20px;
    }

    #time-slider {
      width: 100%;
      height: fit-content;
      left: 0;
      right: 0;
      z-index: 1;
      filter: drop-shadow(0 0 7px #000a);
      margin-top: 10px;
    }

    #pointer-data {
      z-index: 1;
      position: fixed;
      font-size: 50px;
      font-weight: 900;
      margin: 27px 0px 0px 10px;
      color: #fff;
      text-shadow: 0px 0px 10px #0007;
    }

    #variable-name {
      z-index: 1;
      position: fixed;
      font-size: 20px;
      font-weight: 500;
      margin: 5px 0px 0px 10px;
      color: #fff;
      text-shadow: 0px 0px 10px #0007;
    }

    .button {
      cursor: pointer;
      width: auto;
      padding: 8px;
      border-radius: 3px;
      margin: 10px 0 0 0;
      font-size: 12px;
      text-align: center;
      color: #fff;
      background: #3174ff;
      font-family: sans-serif;
      font-weight: bold;
    }
  </style>
</head>

<body>
  <div id="time-info">
    <span id="time-text"></span>
    <button id="play-pause-bt" class="button">Play 3600x</button>
    <input type="range" id="time-slider" min="0" max="11" step="1">
  </div>

  <div id="variable-name">Temperature + Wind</div>
  <div id="pointer-data"></div>
  <div id="map"></div>

  <script>
    const timeInfoContainer = document.getElementById("time-info");
    const timeTextDiv = document.getElementById("time-text");
    const timeSlider = document.getElementById("time-slider");
    const playPauseButton = document.getElementById("play-pause-bt");
    const pointerDataDiv = document.getElementById("pointer-data");
    let pointerLngLat = null;

    maptilersdk.config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

    const map = new maptilersdk.Map({
      container: document.getElementById('map'),
      hash: true,
      zoom: 2,
      center: [0, 40],
      style: maptilersdk.MapStyle.BACKDROP,
      projectionControl: true,
      projection: 'globe'
    });

    const layerBg = new maptilerweather.TemperatureLayer({
      opacity: 0.8,
    });

    const layer = new maptilerweather.WindLayer({
      id: "Wind Particles",
      colorramp: maptilerweather.ColorRamp.builtin.NULL,
      speed: 0.001,
      fadeFactor: 0.03,
      maxAmount: 256,
      density: 200,
      color: [0, 0, 0, 30],
      fastColor: [0, 0, 0, 100],
    });
    
    map.on('load', function () {
      // Darkening the water layer to make the land stand out
      map.setPaintProperty("Water", 'fill-color', "rgba(0, 0, 0, 0.6)");
      map.addLayer(layer);
      map.addLayer(layerBg, "Water");
      
    });

    timeSlider.addEventListener("input", (evt) => {
      layer.setAnimationTime(parseInt(timeSlider.value / 1000))
      layerBg.setAnimationTime(parseInt(timeSlider.value / 1000))
    })

    // Event called when all the datasource for the next days are added and ready.
    // From now on, the layer nows the start and end dates.
    layer.on("sourceReady", event => {
      const startDate = layer.getAnimationStartDate();
      const endDate = layer.getAnimationEndDate();
      const currentDate = layer.getAnimationTimeDate();
      refreshTime()

      timeSlider.min = +startDate;
      timeSlider.max = +endDate;
      timeSlider.value = +currentDate;
    })

    // Called when the animation is progressing
    layer.on("tick", event => {
      refreshTime();
      updatePointerValue(pointerLngLat);
    })

    // Called when the time is manually set
    layer.on("animationTimeSet", event => {
      refreshTime()
    })

    // When clicking on the play/pause
    let isPlaying = false;
    playPauseButton.addEventListener("click", () => {
      if (isPlaying) {
        layer.animateByFactor(0);
        layerBg.animateByFactor(0);
        playPauseButton.innerText = "Play 3600x";
      } else {
        layer.animateByFactor(3600);
        layerBg.animateByFactor(3600);
        playPauseButton.innerText = "Pause";
      }

      isPlaying = !isPlaying;
    })

    // Update the date time display
    function refreshTime() {
      const d = layer.getAnimationTimeDate();
      timeTextDiv.innerText = d.toString();
      timeSlider.value = +d;
    }

    function updatePointerValue(lngLat) {
      if (!lngLat) return;
      pointerLngLat = lngLat;
      const valueWind = layer.pickAt(lngLat.lng, lngLat.lat);
      const valueTemp = layerBg.pickAt(lngLat.lng, lngLat.lat);
      if (!valueWind) {
        pointerDataDiv.innerText = "";
        return;
      }
      pointerDataDiv.innerText = `${valueTemp.value.toFixed(1)}°C \n ${valueWind.speedKilometersPerHour.toFixed(1)} km/h`
    }

    timeInfoContainer.addEventListener("mouseenter", () => {
      pointerDataDiv.innerText = "";
    })

    map.on('mousemove', (e) => {
      updatePointerValue(e.lngLat);
    });

  </script>
</body>
</html>
```

## Learn more

Check out the [Weather JS module reference](https://docs.maptiler.com/sdk-js/modules/weather/)

Check out the [Wind layer reference](https://docs.maptiler.com/sdk-js/modules/weather/api/wind/)

Check out the [Temperature layer reference](https://docs.maptiler.com/sdk-js/modules/weather/api/temperature/)

## Related examples

### [Weather custom popup](https://docs.maptiler.com/sdk-js/examples/weather-custom-popup/)

Examples

Create highly customizable popups for weather maps using MarkerLayout.

### [Weather layer switcher](https://docs.maptiler.com/sdk-js/examples/weather-layer-switcher/)

Examples

Switch weather layers and view detailed data under cursors.

### [Weather layer custom color blind ramp](https://docs.maptiler.com/sdk-js/examples/weather-temperature-color-ramp/)

Examples

Visualize temperature layer with custom color blind ramp.

### [Weather+ wind arrow layer](https://docs.maptiler.com/sdk-js/examples/weather-plus-wind-arrow/)

Examples

Visualize wind direction and speed using animated wind arrows.
