Handling Mapbox Style Changes
Render a basic interactive map using Mapbox with the ability to change the base map style.
When switching the base map style on a Mapbox map, existing layers and sources are often removed from the map (opens in a new tab) and not re-added with the new style. The following example demonstrates how to re-add existing Aeris-related weather layers back onto a Mapbox map when changing the base map to a different style.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Xweather JavaScript SDK - Handling Mapbox Style Changes</title>
<script defer src="https://cdn.aerisapi.com/sdk/js/latest/aerisweather.min.js"></script>
<link rel="stylesheet" href="https://cdn.aerisapi.com/sdk/js/latest/aerisweather.css">
<style>
body {
font-family: 'Helvetica','Arial',sans-serif;
}
#map {
height: 800px;
margin: 10px auto 30px;
width: 1000px;
}
.map-styles {
font-size: 12px;
text-align: center;
}
.map-styles > a {
border: 2px solid #ddd;
color: #333;
display: inline-block;
font-weight: bold;
margin: 0 2px;
padding: 4px 8px;
text-decoration: none;
}
.map-styles > a:hover {
background: #ddd;
color: #333;
}
.map-styles > a.selected {
background: #333;
border-color: #333;
color: #fff;
}
</style>
</head>
<body>
<p class="map-styles">
<a href="./#" data-style="streets-v11">Streets</a>
<a href="./#" data-style="satellite-v9">Satellite</a>
<a href="./#" data-style="outdoors-v11">Outdoors</a>
<a href="./#" data-style="light-v10">Light</a>
<a href="./#" data-style="dark-v10">Dark</a>
</p>
<div id="map"></div>
<script>
window.onload = () => {
const aeris = new Xweather('CLIENT_ID', 'CLIENT_SECRET');
const utils = aeris.utils;
const $ = aeris.utils.$;
let currentStyle = 'streets-v11';
const setMapStyle = (map, type) => {
if (type === currentStyle) return;
currentStyle = type;
// grab the existing style and cache all Aeris-related sources and layers
// so they can be re-added after changing the map style
const mapStyle = map.getStyle();
const aerisLayers = mapStyle.layers.filter((layer) => /^aeris-/.test(layer.id));
const aerisSources = Object.keys(mapStyle.sources).filter((key) => {
return /^aeris-/.test(key);
}).reduce((prev, result) => {
prev[result] = mapStyle.sources[result];
return prev;
}, {});
// need to wait for the new style to load before re-adding the previous map data
map.on('style.load', () => {
Object.keys(aerisSources).forEach((key) => {
const existing = map.getSource(key);
if (!existing) {
map.addSource(key, aerisSources[key]);
}
});
aerisLayers.forEach((layer) => {
const existing = map.getLayer(layer.id);
if (!existing) {
map.addLayer(layer)
}
});
});
// update the map style
map.setStyle(`mapbox://styles/mapbox/${type}`, { diff: true });
};
aeris.views().then(views => {
const interactive = new views.InteractiveMap('#map', {
strategy: 'mapbox',
accessToken: 'MAPBOX_TOKEN',
center: {
lat: 44.977,
lon: -88.265
},
zoom: 5,
layers: 'satellite,alerts,radar,stormreports',
timeline: {
from: -12 * 3600,
to: 0
}
});
interactive.on('ready', () => {
$('.map-styles > a').on('click', (e) => {
e.preventDefault();
const $el = $(e.target);
$('.map-styles > a').removeClass('selected');
$el.addClass('selected');
const type = $el.attr('data-style');
if (type) {
setMapStyle(interactive.map, type);
}
});
$('.map-styles > a:nth-child(1)').click();
});
});
};
</script>
</body>
</html>