MapsGL - Compare forecast model temperatures
This example adds forecast model temperature layers over the United States. The timeline is set two days into the future so the model output is valid. Use the checkboxes to show one or more models — GFS, ECMWF HRES, and GEFS are global; NBM CONUS and NDFD CONUS are clipped to their native grids.
When more than one model is visible, fills use the same temperature colorscale and opacity is reduced so overlapping coverage is easier to see. The data inspector qualifies colliding titles by model family (for example GFS Temperature next to NBM Temperature).
forecast-model-layer.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MapsGL SDK - Compare forecast model temperatures</title>
<meta name="description" content="Toggle one or more forecast models for temperatures over the United States." />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://api.mapbox.com/mapbox-gl-js/v3.12.0/mapbox-gl.css" rel="stylesheet" />
<script defer src="https://api.mapbox.com/mapbox-gl-js/v3.12.0/mapbox-gl.js"></script>
<link href="https://cdn.aerisapi.com/sdk/js/mapsgl/1.10.1/aerisweather.mapsgl.css" rel="stylesheet" />
<script defer src="https://cdn.aerisapi.com/sdk/js/mapsgl/1.10.1/aerisweather.mapsgl.js"></script>
<style>
body, html {
margin: 0;
padding: 0;
}
#map {
height: 100vh;
width: 100%;
}
#legend {
position: absolute;
bottom: 10px;
right: 10px;
}
#controls {
position: absolute;
top: 10px;
left: 10px;
z-index: 1;
background: rgba(255, 255, 255, 0.9);
padding: 10px 12px;
border-radius: 5px;
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 12px;
line-height: 1.4;
max-width: 260px;
}
#controls h3 {
margin: 0 0 8px;
font-size: 14px;
}
#controls label {
display: flex;
align-items: center;
gap: 8px;
margin: 6px 0;
cursor: pointer;
}
#controls label.unavailable {
color: #999;
cursor: not-allowed;
}
#controls p {
margin: 8px 0 0;
color: #555;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="legend"></div>
<div id="controls">
<h3>Forecast temperatures</h3>
<label>
<input id="model-gfs" type="checkbox" data-code="gfs-temperatures" checked />
GFS
</label>
<label>
<input id="model-ecmwf" type="checkbox" data-code="ecmwf-hres-temperatures" />
ECMWF HRES
</label>
<label>
<input id="model-gefs" type="checkbox" data-code="gefs-temperatures" />
GEFS
</label>
<label>
<input id="model-nbm" type="checkbox" data-code="nbm-conus-temperatures" />
NBM CONUS
</label>
<label>
<input id="model-ndfd" type="checkbox" data-code="ndfd-conus-temperatures" />
NDFD CONUS
</label>
<p>Regional models are clipped to their native grid. Enable more than one to compare coverage; overlapping fills share the temperature colorscale.</p>
</div>
<script>
window.addEventListener('load', () => {
mapboxgl.accessToken = 'MAPBOX_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v11',
center: [-98, 39],
zoom: 4,
projection: 'mercator'
});
const account = new aerisweather.mapsgl.Account('CLIENT_ID', 'CLIENT_SECRET');
const controller = new aerisweather.mapsgl.MapboxMapController(map, { account });
controller.on('load', () => {
controller.timeline.setStartDateUsingRelativeTime('now');
controller.timeline.setEndDateUsingRelativeTime('+2 days');
controller.addLegendControl('#legend');
controller.addDataInspectorControl();
const inputs = Array.from(document.querySelectorAll('#controls input[data-code]'));
const ids = typeof controller.weatherProvider.getSupportedLayerIds === 'function'
? controller.weatherProvider.getSupportedLayerIds()
: [];
const supported = new Set(ids);
inputs.forEach((input) => {
if (ids.length > 0 && !supported.has(input.dataset.code)) {
input.checked = false;
input.disabled = true;
input.parentElement.classList.add('unavailable');
}
});
const syncModels = () => {
const active = inputs.filter((input) => input.checked);
const opacity = active.length > 1 ? 0.55 : 1;
inputs.forEach((input) => {
const code = input.dataset.code;
const exists = controller.hasWeatherLayer(code);
if (input.checked && !exists) {
controller.addWeatherLayer(code, {
paint: {
sample: {
opacity
}
}
});
} else if (!input.checked && exists) {
controller.removeWeatherLayer(code);
} else if (exists) {
const layer = controller.getWeatherLayer(code);
const target = Array.isArray(layer) ? layer[0] : layer;
target?.setPaintProperty('sample.opacity', opacity);
}
});
};
inputs.forEach((input) => {
input.addEventListener('change', syncModels);
});
syncModels();
});
});
</script>
</body>
</html>