---
title: "Weather layer switcher | Weather SDK | JavaScript maps SDK"
description: "Switch weather layers and view detailed data under cursors."
---

> 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 layer switcher

Switch the display of various weather layers within MapTiler Weather dataset and access the data details at the location indicated by the cursor.

By utilizing the `animateByFactor(3600)` function, we can create a time-based animation that spans the next four days. Each second of the animation represents an hour.

Utilize the provided time animation bar to explore the weather forecast for the upcoming four-day period.

### 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, PressureIsolineLayer, PrecipitationLayer, TemperatureLayer, PressureLayer, RadarLayer, WindArrowLayer, ColorRamp } from '@maptiler/weather';

config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

const weatherLayers = {
  "precipitation": {
    "layer": null,
    "value": "value",
    "units": " mm"
  },
  "pressure": {
    "layer": null,
    "value": "value",
    "units": " hPa"
  },
  "radar": {
    "layer": null,
    "value": "value",
    "units": " dBZ"
  },
  "temperature": {
    "layer": null,
    "value": "value",
    "units": "°"
  },
  "wind": {
    "layer": null,
    "value": "speedMetersPerSecond",
    "units": " m/s"
  }
};

const map = (window.map = new Map({
  container: 'map', // container's id or the HTML element to render the map
  style: MapStyle.BACKDROP,  // stylesheet location
  zoom: 2,
  center: [-42.66, 37.63],
  hash: true,
  projectionControl: true,
  projection: 'globe'
}));

const initWeatherLayer = "wind";
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;
let activeLayer = null;
let isPlaying = false;
let currentTime = null;

timeSlider.addEventListener("input", (evt) => {
  const weatherLayer = weatherLayers[activeLayer]?.layer;
  if (weatherLayer) {
    weatherLayer.setAnimationTime(parseInt(timeSlider.value / 1000));
  }
});

// When clicking on the play/pause
playPauseButton.addEventListener("click", () => {
  const weatherLayer = weatherLayers[activeLayer]?.layer;
  if (weatherLayer) {
    if (isPlaying) {
      pauseAnimation(weatherLayer);
    } else {
      playAnimation(weatherLayer);
    }
  }
});

function pauseAnimation(weatherLayer) {
  weatherLayer.animateByFactor(0);
  playPauseButton.innerText = "Play 3600x";
  isPlaying = false;
}

function playAnimation(weatherLayer) {
  weatherLayer.animateByFactor(3600);
  playPauseButton.innerText = "Pause";
  isPlaying = true;
}

map.on('load', function () {
  map.setPaintProperty("Water", 'fill-color', "rgba(0, 0, 0, 0.4)");
  initWeatherMap(initWeatherLayer);
});

map.on('mouseout', function(evt) {
  if (!evt.originalEvent.relatedTarget) {
    pointerDataDiv.innerText = "";
    pointerLngLat = null;
  }
});

function updatePointerValue(lngLat) {
  if (!lngLat) return;
  pointerLngLat = lngLat;
  const weatherLayer = weatherLayers[activeLayer]?.layer;
  const weatherLayerValue = weatherLayers[activeLayer]?.value;
  const weatherLayerUnits = weatherLayers[activeLayer]?.units;
  if (weatherLayer) {
    const value = weatherLayer.pickAt(lngLat.lng, lngLat.lat);
    if (!value) {
      pointerDataDiv.innerText = "";
      return;
    }
    pointerDataDiv.innerText = `${value[weatherLayerValue].toFixed(1)}${weatherLayerUnits}`
  }
}

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

document.getElementById('buttons').addEventListener('click', function (event) {
  // Change layer based on button id
  changeWeatherLayer(event.target.id);
});

function changeWeatherLayer(type) {
  if (type !== activeLayer) {
    if (map.getLayer(activeLayer)) {
      const activeWeatherLayer = weatherLayers[activeLayer]?.layer;
      if (activeWeatherLayer) {
        currentTime = activeWeatherLayer.getAnimationTime();
        map.setLayoutProperty(activeLayer, 'visibility', 'none');
      }
    }
    activeLayer = type;
    const weatherLayer = weatherLayers[activeLayer].layer || createWeatherLayer(activeLayer);
    if (map.getLayer(activeLayer)) {
      map.setLayoutProperty(activeLayer, 'visibility', 'visible');
    } else {
      map.addLayer(weatherLayer, 'Water');
    }
    changeLayerLabel(activeLayer);
    activateButton(activeLayer);
    changeLayerAnimation(weatherLayer);
    return weatherLayer;
  }
}

function activateButton(activeLayer) {
  const buttons = document.getElementsByClassName('button');
  for (let i = 0; i < buttons.length; i++) {
    const btn = buttons[i];
    if (btn.id === activeLayer) {
      btn.classList.add('active');
    } else {
      btn.classList.remove('active');
    }
  }
}

function changeLayerAnimation(weatherLayer) {
  weatherLayer.setAnimationTime(parseInt(timeSlider.value / 1000));
  if (isPlaying) {
    playAnimation(weatherLayer);
  } else {
    pauseAnimation(weatherLayer);
  }
}

function createWeatherLayer(type){
  let weatherLayer = null;
  switch (type) {
    case 'precipitation':
      weatherLayer = new PrecipitationLayer({id: 'precipitation'});
      break;
    case 'pressure':
      weatherLayer = new PressureLayer({
        opacity: 0.8,
        id: 'pressure'
      });
      break;
    case 'radar':
      weatherLayer = new RadarLayer({
        opacity: 0.8,
        id: 'radar'
      });
      break;
    case 'temperature':
      weatherLayer = new TemperatureLayer({
        colorramp: ColorRamp.builtin.TEMPERATURE_3,
        id: 'temperature'
      });
      break;  
    case 'wind':
      weatherLayer = new WindLayer({id: 'wind'});
      break;
  }

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

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

  // 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.
  weatherLayer.on("sourceReady", event => {
    const startDate = weatherLayer.getAnimationStartDate();
    const endDate = weatherLayer.getAnimationEndDate();      
    if (timeSlider.min > 0){
      weatherLayer.setAnimationTime(currentTime);
      changeLayerAnimation(weatherLayer);
    } else {
      const currentDate = weatherLayer.getAnimationTimeDate();
      timeSlider.min = +startDate;
      timeSlider.max = +endDate;
      timeSlider.value = +currentDate;
    }
  });

  weatherLayers[type].layer = weatherLayer;
  return weatherLayer;
}

// Update the date time display
function refreshTime() {
  const weatherLayer = weatherLayers[activeLayer]?.layer;
  if (weatherLayer) {
    const d = weatherLayer.getAnimationTimeDate();
    timeTextDiv.innerText = d.toString();
    timeSlider.value = +d;
  }
}

function changeLayerLabel(type) {
  document.getElementById("variable-name").innerText = type;
}

function initWeatherMap(type) {
  const weatherLayer = changeWeatherLayer(type);
}
```

#### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="robots" content="noindex">
  <title>Weather layer switcher | 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="btn btn-primary btn-sm time-button">Play 3600x</button>
    <input type="range" id="time-slider" min="0" max="11" step="1">
  </div>

  <div id="variable-name">Wind</div>
  <div id="pointer-data"></div>
  <div id="map">
    <ul id="buttons">
      <li id="precipitation" class="btn btn-primary button">Precipitation</li>
      <li id="pressure" class="btn btn-primary button">Pressure</li>
      <li id="radar" class="btn btn-primary button">Radar</li>
      <li id="temperature" class="btn btn-primary button">Temperature</li> 
      <li id="wind" class="btn btn-primary button">Wind</li>
    </ul>
  </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: #3E4048;}
#pointer-data{ z-index: 1; position: fixed; font-size: 20px; 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; text-transform:capitalize;} 
#time-info{ position: fixed; width: 60vw; bottom: 0; z-index: 1; margin: 10px; text-shadow: 0px 0px 5px black; color: white; font-size: 18px; font-weight: 500; text-align: center; left: 0; right: 0; margin: auto; padding: 20px;} 
#time-text{ font-size: 12px; font-weight: 600;} 
#time-slider{ width: 100%; height: fit-content; left: 0; right: 0; z-index: 1; filter: drop-shadow(0 0 7px #000a); margin-top: 10px;} 
#buttons{ width: auto; margin: 0 10px; padding:0; position:absolute; top: 50px; left:0; z-index:99;} 
.button{ display: block; position: relative; margin: 10px 0 0 0; font-size: 0.9em;}
```

### Basic JavaScript setup

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Maptiler weather layers</title>
  <link
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
    rel="stylesheet"
    integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
    crossorigin="anonymous"
  />
  <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: #3E4048;}
    #pointer-data{ z-index: 1; position: fixed; font-size: 20px; 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; text-transform:capitalize;} 
    #time-info{ position: fixed; width: 60vw; bottom: 0; z-index: 1; margin: 10px; text-shadow: 0px 0px 5px black; color: white; font-size: 18px; font-weight: 500; text-align: center; left: 0; right: 0; margin: auto; padding: 20px;} 
    #time-text{ font-size: 12px; font-weight: 600;} 
    #time-slider{ width: 100%; height: fit-content; left: 0; right: 0; z-index: 1; filter: drop-shadow(0 0 7px #000a); margin-top: 10px;} 
    #buttons{ width: auto; margin: 0 10px; padding:0; position:absolute; top: 50px; left:0; z-index:99;} 
    .button{ display: block; position: relative; margin: 10px 0 0 0; font-size: 0.9em;}
  </style>
</head>
<body>
  <div id="time-info">
    <span id="time-text"></span>
    <button id="play-pause-bt" class="btn btn-primary btn-sm time-button">Play 3600x</button>
    <input type="range" id="time-slider" min="0" max="11" step="1">
  </div>

  <div id="variable-name">Wind</div>
  <div id="pointer-data"></div>
  <div id="map">
    <ul id="buttons">
      <li id="precipitation" class="btn btn-primary button">Precipitation</li>
      <li id="pressure" class="btn btn-primary button">Pressure</li>
      <li id="radar" class="btn btn-primary button">Radar</li>
      <li id="temperature" class="btn btn-primary button">Temperature</li> 
      <li id="wind" class="btn btn-primary button">Wind</li>
    </ul>
  </div>
  <script>
      maptilersdk.config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';
      
      const weatherLayers = {
        "precipitation": {
          "layer": null,
          "value": "value",
          "units": " mm"
        },
        "pressure": {
          "layer": null,
          "value": "value",
          "units": " hPa"
        },
        "radar": {
          "layer": null,
          "value": "value",
          "units": " dBZ"
        },
        "temperature": {
          "layer": null,
          "value": "value",
          "units": "°"
        },
        "wind": {
          "layer": null,
          "value": "speedMetersPerSecond",
          "units": " m/s"
        }
      };
      
      const map = (window.map = new maptilersdk.Map({
        container: 'map', // container's id or the HTML element to render the map
        style: maptilersdk.MapStyle.BACKDROP,  // stylesheet location
        zoom: 2,
        center: [-42.66, 37.63],
        hash: true,
        projectionControl: true,
        projection: 'globe'
      }));

      const initWeatherLayer = "wind";
      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;
      let activeLayer = null;
      let isPlaying = false;
      let currentTime = null;

      timeSlider.addEventListener("input", (evt) => {
        const weatherLayer = weatherLayers[activeLayer]?.layer;
        if (weatherLayer) {
          weatherLayer.setAnimationTime(parseInt(timeSlider.value / 1000));
        }
      });

      // When clicking on the play/pause
      playPauseButton.addEventListener("click", () => {
        const weatherLayer = weatherLayers[activeLayer]?.layer;
        if (weatherLayer) {
          if (isPlaying) {
            pauseAnimation(weatherLayer);
          } else {
            playAnimation(weatherLayer);
          }
        }
      });

      function pauseAnimation(weatherLayer) {
        weatherLayer.animateByFactor(0);
        playPauseButton.innerText = "Play 3600x";
        isPlaying = false;
      }

      function playAnimation(weatherLayer) {
        weatherLayer.animateByFactor(3600);
        playPauseButton.innerText = "Pause";
        isPlaying = true;
      }

      map.on('load', function () {
        map.setPaintProperty("Water", 'fill-color', "rgba(0, 0, 0, 0.4)");
        initWeatherMap(initWeatherLayer);
      });

      map.on('mouseout', function(evt) {
        if (!evt.originalEvent.relatedTarget) {
          pointerDataDiv.innerText = "";
          pointerLngLat = null;
        }
      });

      function updatePointerValue(lngLat) {
        if (!lngLat) return;
        pointerLngLat = lngLat;
        const weatherLayer = weatherLayers[activeLayer]?.layer;
        const weatherLayerValue = weatherLayers[activeLayer]?.value;
        const weatherLayerUnits = weatherLayers[activeLayer]?.units;
        if (weatherLayer) {
          const value = weatherLayer.pickAt(lngLat.lng, lngLat.lat);
          if (!value) {
            pointerDataDiv.innerText = "";
            return;
          }
          pointerDataDiv.innerText = `${value[weatherLayerValue].toFixed(1)}${weatherLayerUnits}`
        }
      }

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

      document.getElementById('buttons').addEventListener('click', function (event) {
        // Change layer based on button id
        changeWeatherLayer(event.target.id);
      });

      function changeWeatherLayer(type) {
        if (type !== activeLayer) {
          if (map.getLayer(activeLayer)) {
            const activeWeatherLayer = weatherLayers[activeLayer]?.layer;
            if (activeWeatherLayer) {
              currentTime = activeWeatherLayer.getAnimationTime();
              map.setLayoutProperty(activeLayer, 'visibility', 'none');
            }
          }
          activeLayer = type;
          const weatherLayer = weatherLayers[activeLayer].layer || createWeatherLayer(activeLayer);
          if (map.getLayer(activeLayer)) {
            map.setLayoutProperty(activeLayer, 'visibility', 'visible');
          } else {
            map.addLayer(weatherLayer, 'Water');
          }
          changeLayerLabel(activeLayer);
          activateButton(activeLayer);
          changeLayerAnimation(weatherLayer);
          return weatherLayer;
        }
      }

      function activateButton(activeLayer) {
        const buttons = document.getElementsByClassName('button');
        for (let i = 0; i < buttons.length; i++) {
          const btn = buttons[i];
          if (btn.id === activeLayer) {
            btn.classList.add('active');
          } else {
            btn.classList.remove('active');
          }
        }
      }

      function changeLayerAnimation(weatherLayer) {
        weatherLayer.setAnimationTime(parseInt(timeSlider.value / 1000));
        if (isPlaying) {
          playAnimation(weatherLayer);
        } else {
          pauseAnimation(weatherLayer);
        }
      }

      function createWeatherLayer(type){
        let weatherLayer = null;
        switch (type) {
          case 'precipitation':
            weatherLayer = new maptilerweather.PrecipitationLayer({id: 'precipitation'});
            break;
          case 'pressure':
            weatherLayer = new maptilerweather.PressureLayer({
              opacity: 0.8,
              id: 'pressure'
            });
            break;
          case 'radar':
            weatherLayer = new maptilerweather.RadarLayer({
              opacity: 0.8,
              id: 'radar'
            });
            break;
          case 'temperature':
            weatherLayer = new maptilerweather.TemperatureLayer({
              colorramp: maptilerweather.ColorRamp.builtin.TEMPERATURE_3,
              id: 'temperature'
            });
            break;  
          case 'wind':
            weatherLayer = new maptilerweather.WindLayer({id: 'wind'});
            break;
        }

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

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

        // 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.
        weatherLayer.on("sourceReady", event => {
          const startDate = weatherLayer.getAnimationStartDate();
          const endDate = weatherLayer.getAnimationEndDate();      
          if (timeSlider.min > 0){
            weatherLayer.setAnimationTime(currentTime);
            changeLayerAnimation(weatherLayer);
          } else {
            const currentDate = weatherLayer.getAnimationTimeDate();
            timeSlider.min = +startDate;
            timeSlider.max = +endDate;
            timeSlider.value = +currentDate;
          }
        });

        weatherLayers[type].layer = weatherLayer;
        return weatherLayer;
      }

      // Update the date time display
      function refreshTime() {
        const weatherLayer = weatherLayers[activeLayer]?.layer;
        if (weatherLayer) {
          const d = weatherLayer.getAnimationTimeDate();
          timeTextDiv.innerText = d.toString();
          timeSlider.value = +d;
        }
      }

      function changeLayerLabel(type) {
        document.getElementById("variable-name").innerText = type;
      }

      function initWeatherMap(type) {
        const weatherLayer = changeWeatherLayer(type);
      }
  </script>
</body>
</html>
```

## Learn more

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

## 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+ pressure isolines layer](https://docs.maptiler.com/sdk-js/examples/weather-plus-pressure-isolines/)

Examples

Visualize atmospheric pressure isolines using Weather Plus datasets.

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

Examples

Animate wind arrows and view cursor-specific wind speed data.

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

Examples

Visualize wind and temperature layers with cursor data.
