Preload animated weather data

Preload animated weather data

This example demonstrates how to preload animation data for active weather layers when the map is initialized so that playback can begin immediately without having to wait for animation data to load.

In this example, we're adding a temperatures layer to the map and preloading the animation data for the layer once it's initialized. The animation data is loaded by calling the loadData() method on the layer's animator instance. This method will load the animation data for the layer's current time range and any additional data needed for playback.

Once the data is loaded, the layer will be ready to animate when playback is started.

preload-animation-data.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>MapsGL SDK - Preload animated weather data</title>
    <meta name="description" content="Preload animation data for active weather layers when the map is initialized so that playback can begin immediately without having to wait for animation data to load." />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
 
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.8.0/mapbox-gl.css" rel="stylesheet" />
    <script defer src="https://api.mapbox.com/mapbox-gl-js/v2.8.0/mapbox-gl.js"></script>
 
    <link href="https://cdn.aerisapi.com/sdk/js/mapsgl/latest/aerisweather.mapsgl.css" rel="stylesheet" />
    <script defer src="https://cdn.aerisapi.com/sdk/js/mapsgl/latest/aerisweather.mapsgl.js"></script>
 
    <style>
    body, html {
        margin: 0;
        padding: 0;
    }
    #map {
        height: 100vh;
        width: 100%;
    }
    #controls {
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
        gap: 8px;
        position: absolute;
        top: 10px;
        left: 10px;
        z-index: 1;
        background: rgba(255, 255, 255, 0.8);
        padding: 10px;
        border-radius: 5px;
        font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
        font-size: 12px;
    }
    #controls h3 {
        margin: 0 0 6px;
        font-size: 14px;
    }
    #controls {
        min-height: 20px;
    }
    #controls .loader {
        display: none;
        height: 20px;
    }
    .loader .ring {
        display: inline-block;
        position: relative;
        width: 20px;
        height: 20px;
    }
    .loader .ring div {
        box-sizing: border-box;
        display: block;
        position: absolute;
        width: 18px;
        height: 18px;
        margin: 1px;
        border: 2px solid #333;
        border-radius: 50%;
        animation: loader-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
        border-color: #333 transparent transparent transparent;
    }
    .loader .ring div:nth-child(1) {
        animation-delay: -0.45s;
    }
    .loader .ring div:nth-child(2) {
        animation-delay: -0.3s;
    }
    .loader .ring div:nth-child(3) {
        animation-delay: -0.15s;
    }
    @keyframes loader-ring {
        0% {
            transform: rotate(0deg);
        }
        100% {
            transform: rotate(360deg);
        }
    }
    </style>
 
</head>
<body>
    <div id="map"></div>
    <div id="controls">
        <button class="btn-play">Play</button>
        <button class="btn-pause">Pause</button>
        <div class="loader">
            <div class="ring">
                <div></div><div></div><div></div><div></div>
            </div>
        </div>
    </div>
 
    <script>
        window.addEventListener('load', () => {
 
            // Create the Mapbox map instance
            mapboxgl.accessToken = 'MAPBOX_TOKEN';
            const map = new mapboxgl.Map({
                container: 'map',
                style: 'mapbox://styles/mapbox/light-v11',
                center: [-85.5, 40],
                zoom: 3,
                projection: 'mercator'
            });
 
            const account = new aerisweather.mapsgl.Account('CLIENT_ID', 'CLIENT_SECRET');
            const controller = new aerisweather.mapsgl.MapboxMapController(map, { 
                account,
                animation: {
                    // Disable auto-resuming playback when the map is moved, which means that user will need to
                    // manually resume playback after moving the map through a pan or zoom action. This allows new
                    // data to be loaded for the new map view before playback is resumed since the animation is in
                    // a paused state.
                    resumeOnMoveEnd: false
                }
            });
 
            // Set up animation controls
            const controls = {
                play: document.querySelector('#controls .btn-play'),
                pause: document.querySelector('#controls .btn-pause'),
                loader: document.querySelector('#controls .loader')
            };
 
            // Hide/show the loading indicator when any map layer is loading data
            controller.on('load:start', () => {
                controls.loader.style.display = 'block';
            });
            controller.on('load:complete', () => {
                controls.loader.style.display = 'none';
            });
 
            // Set up timeline controls
            controls.pause.disabled = true;
            controller.timeline.on('play', () => {
                controls.play.textContent = 'Stop';
                controls.pause.textContent = 'Pause';
                controls.pause.disabled = false;
            }).on('stop', () => {
                controls.play.textContent = 'Play';
                controls.pause.textContent = 'Pause';
                controls.pause.disabled = true;
            }).on('pause', () => {
                controls.pause.textContent = 'Resume';
            }).on('resume', () => {
                controls.pause.textContent = 'Pause';
            });
            controls.play.addEventListener('click', (e) => {
                e.preventDefault();
                if (controller.timeline.isActive) {
                    controller.timeline.stop();
                } else {
                    controller.timeline.play();
                }
            });
            controls.pause.addEventListener('click', (e) => {
                e.preventDefault();
                if (controller.timeline.isPaused) {
                    controller.timeline.resume();
                } else {
                    controller.timeline.pause();
                }
            });
 
            controller.on('load', () => {
                let beforeLayerId;
 
                // Find the Mapbox layer id to insert the weather layer before by matching against a
                // particular Mapbox layer id value and type. Here we look for the first admin boundary
                // layer to insert weather layers below.
                const mapboxLayers = controller.map.getStyle().layers;
                mapboxLayers.forEach((layer) => {
                    if (!beforeLayerId && layer.type === 'line') {
                        if (/^admin-1/.test(layer.id)) {
                            beforeLayerId = layer.id;
                        }
                    }
                });
 
                const tempsLayer = controller.addWeatherLayer('wind-speeds', null, beforeLayerId);
 
                // Preload animation data for the temperatures layer once it's initialized
                tempsLayer.on('ready', () => {
                    tempsLayer.animator.loadData();
                });
            });
 
        });
    </script>
 
</body>
</html>