One of the most challenging elements of utilizing large volumes of weather data for business can be performance. With weather as an input to nearly every industry in some regard, the importance of consistent, fast data retrieval and rendering is paramount. Loading high-resolution and layered imagery inefficiently can dramatically interrupt the smoothness of utilizing interactive and animated map elements within any application, online or otherwise. Fortunately for AerisWeather users, one can call upon multiple tile servers working in unison to dramatically increase render speed.
Many customers already have integrations with third-party mapping platforms like Google, Mapbox, or Leaflet, so in this example we’ll walk through how to enable multi-tile server map animation in conjunction with a Google Maps instance. The below sections explain the code line by line, but if you are looking to jump ahead, you’ll find the full length of code at the end of this tutorial.
The first step to getting started is to ensure you have a Google Maps API key or create a new one. To create your own, sign up for a Google Cloud account and verify your email. Be sure to restrict your API key’s usage via the Google Cloud Console before sending your project live for added security.
The next step is to add CSS to style your map into the <head>. Below is a basic snippet that creates a full-width map within its container:
1 2 3 4 5 6 7 8 9 10 11 |
<style> html, body { height: 100%; margin: 0; padding: 0; } #map { width: 100%; height: 100%; } </style> |
Now place the container, ensuring you are working in the <body> tags of your HTML file:
1 |
<div id="map"></div> |
In this step, we will render the AerisWeather mapping layers we’d like to animate to display the movement and change within weather patterns and events. You’ll need to create an AerisWeather account if you have not already and gather or generate new keys under the “Apps” tab of your account.
Next, we need to set the parameters of our animation. Use frameCount to define the number of intervals in your animation and startMinutes to define the number of minutes back in time for your animation to start from, using negative values to denote the past. endMinutes allows you to define the time offset of the final interval, with 0 being the current hour and minute. Please note that the default number of intervals is 10 and animation playback speed is 2 seconds. If you simply define a start and end time, the mapping platform will automatically calculate the frames between those times and as closely match those to the tiles and times we have available in our database.
1 2 3 4 5 |
let map; const frameCount = 10; // total intervals const startMinutes = -60; // start time offset relative to now, where negative means past const endMinutes = 0; |
In the below snippet we now declare our AerisWeather credentials and the layers we want to animate on our map:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const AERIS_ID = 'CLIENT_ID'; const AERIS_KEY = 'CLIENT_SECRET'; const NUM_COLORS = '256'; // set to empty string for true color png const TILE_SIZE = 256; // layer to include on the map // uncomment more layers or add more! const layers = [ // 'alerts', // 'satellite', 'radar', 'stormcells' ]; |
Adding a little direction for the construction of our URL string. No inputs or further code needed below; we’ve done the concatenation work for you.
1 2 3 4 5 6 7 8 9 |
function getTileServer(stepNumber, layers, opacity = 0) { const interval = (endMinutes - startMinutes)/frameCount; const timeOffset = startMinutes + interval * stepNumber; const layerStr = layers.join(','); return { getTileUrl: (coord, zoom) => { const server = Math.abs(coord.x + coord.y) % 4 + 1; return <pre class="inline:true decode:1 " >https://maps${server}.aerisapi.com/${AERIS_ID}_${AERIS_KEY}/${layerStr}/${zoom}/${coord.x}/${coord.y}/${timeOffset}min.png${NUM_COLORS} |
;
},
tileSize: new google.maps.Size(TILE_SIZE, TILE_SIZE),
opacity: opacity
}
}
We are now ready to begin rendering our Google Map! We initialize our map and layers within an event handler once everything on the page has loaded, including the Google Maps library we’ll be referencing. First, we set up the map instance and then grab our map tiles for each interval and giving them time to load before starting the animation. Once more, just breaking down the functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
window.addEventListener('load', () => { // initialize map map = new google.maps.Map(document.getElementById('map'), { center: { lat: 43.0, lng: -93.0 }, zoom: 5 }); // set up the animation frames and layers const frames = []; for (let i = 0; i < frameCount; i += 1) { const opacity = (i === 0) ? 1 : 0; frames.push(new google.maps.ImageMapType(getTileServer(i, layers, opacity))); map.overlayMapTypes.push(frames[i]); } // wait time determines how long to wait and allow frames to load before // beginning animation playback const waitTime = 5000; // step time determines the time in milliseconds each frame holds before advancing const stepTime = 1000; let currentOffset = 0; let previousOffset = currentOffset; setTimeout(() => { setInterval(() => { previousOffset = currentOffset; currentOffset += 1; if (currentOffset === frames.length - 1) { currentOffset = 0; } frames[previousOffset].setOpacity(0); frames[currentOffset].setOpacity(1); }, stepTime); }, waitTime); }); |
After this, you’re safe to close your script tags. The final step is to let Google know you’re legitimate by inserting your Google Maps API key as a parameter in the query string in <script> tags directly below. Voila, you’re done!
1 |
<script src="https://maps.googleapis.com/maps/api/js?key=GOOGLE_API_KEY" async defer></script> |
As promised, below is the full code block with a few additional comments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"/> <style> html, body { height: 100%; margin: 0; padding: 0; } #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> <script> let map; const frameCount = 10; // total intervals const startMinutes = -60; // start time offset relative to now, where negative means past const endMinutes = 0; const AERIS_ID = 'CLIENT_ID'; const AERIS_KEY = 'CLIENT_SECRET'; const NUM_COLORS = '256'; // set to empty string for true color png const TILE_SIZE = 256; // layer to include on the map // uncomment more layers or add more! const layers = [ // 'alerts', // 'satellite', 'radar', 'stormcells' ]; function getTileServer(stepNumber, layers, opacity = 0) { const interval = (endMinutes - startMinutes)/frameCount; const timeOffset = startMinutes + interval * stepNumber; const layerStr = layers.join(','); return { getTileUrl: (coord, zoom) => { const server = Math.abs(coord.x + coord.y) % 4 + 1; return <pre class="inline:true decode:1 " >https://maps${server}.aerisapi.com/${AERIS_ID}_${AERIS_KEY}/${layerStr}/${zoom}/${coord.x}/${coord.y}/${timeOffset}min.png${NUM_COLORS} |
;
},
tileSize: new google.maps.Size(TILE_SIZE, TILE_SIZE),
opacity: opacity
}
}
window.addEventListener(‘load’, () => {
// initialize map
map = new google.maps.Map(document.getElementById(‘map’), {
center: {
lat: 43.0,
lng: -93.0
},
zoom: 5
});
// set up the animation frames and layers
const frames = [];
for (let i = 0; i < frameCount; i += 1) {
const opacity = (i === 0) ? 1 : 0;
frames.push(new google.maps.ImageMapType(getTileServer(i, layers, opacity)));
map.overlayMapTypes.push(frames[i]);
}
// wait time determines how long to wait and allow frames to load before
// beginning animation playback
const waitTime = 5000;
// step time determines the time in milliseconds each frame holds before advancing
const stepTime = 1000;
let currentOffset = 0;
let previousOffset = currentOffset;
setTimeout(() => {
setInterval(() => {
previousOffset = currentOffset;
currentOffset += 1;
if (currentOffset === frames.length – 1) {
currentOffset = 0;
}
frames[previousOffset].setOpacity(0);
frames[currentOffset].setOpacity(1);
}, stepTime);
}, waitTime);
});
</script>
<script src=”https://maps.googleapis.com/maps/api/js?key=GOOGLE_API_KEY” async defer></script>
</body>
</html>
There you have it – faster weather maps, animatable in any direction throughout time. Have further questions or interested in other tutorials? Please reach out to our team to provide feedback as we’re always excited to share better and interesting ways of using our weather API and Weather Mapping Platform.
2 Comments
Is this going to be possible with mapbox GL JS?
Hi Josh, that process is outlined here: https://www.aerisweather.com/blog/2020/07/14/animating-amp-layers-with-mapbox-gl/
Thanks for writing in!