
Ambee's new heatmap tile overlay allows developers to instantly visualize real-time and historical Air Quality and Pollen data. The overlay integrates seamlessly with popular base maps like Google Maps and updates data hourly.Ambee provides access to historical data for the past 50 hours and supports modular data types, including NO2 and CO. Developers must obtain an API key from Ambee to utilize the feature for applications like health apps and urban planning.
{{key-insights}}
We are thrilled to announce a major update at Ambee: the launch of our heatmap tile overlay for Air Quality and Pollen data (tree, grass, and weed). This innovative feature integrates seamlessly with popular base maps like Google Maps and OpenStreetMap, providing you with a powerful tool to visualize both real-time and historical climate data.


Our heatmaps are refreshed hourly, ensuring you have the latest data available. Tiles are generated at the last full hour in UTC, providing you with timely and accurate information.
Our heatmap overlay is already making an impact across various industries. Here are some of the key use cases:
In addition to our new heatmap tile overlay, we also offer Ambee Climate Maps, a comprehensive platform to explore a wide range of climate data. Visit Ambee climate maps to visualize and interact with data on air quality, pollen, and more, providing insights into the environmental conditions around the globe. This platform enhances your ability to monitor and analyze climate data for various applications, from health and wellness to urban planning and environmental research.
To start using Ambee's heatmap tile overlay, you will need an API key. Contact us to request your API key and integrate our climate data solutions into your applications.


For detailed documentation and to explore more about integrating Ambee's heatmap tile overlay, visit our heatmap tile overlay documentation.
<!doctype html>
<html>
<head>
<title>Ambee Heatmap Historical Data</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="data:;base64,iVBORw0KGgo=" />
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<style>
.layer-switch, .controls, .opacity-control, .time-control {
position: absolute;
background: white;
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 15px rgba(0,0,0,0.2);
z-index: 1000;
}
.layer-switch { top: 10px; left: 10px; }
.controls { bottom: 124px; right: 10px; display: none }
.opacity-control { top: 142px; left: 10px; }
.time-control {
bottom: 28px;
right: 10px;
width: 220px;
text-align: center;
}
.timestamp {
position: absolute;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
background: white;
padding: 5px;
border-radius: 3px;
box-shadow: 0 0 15px rgba(0,0,0,0.2);
z-index: 1000;
}
.layer-switch label, .controls button, .time-control input, .opacity-control input { display: block; margin-bottom: 5px; }
.layer-switch input[type="checkbox"], .controls button, .opacity-control input[type="range"], .time-control input[type="range"] { margin-right: 5px; }
.time-control label { display: block; margin-bottom: 5px; font-weight: bold; }
</style>
</head>
<body style="margin: 0">
<div id="map" style="position: absolute; width: 100%; height: 100%"></div>
<div class="layer-switch" id="layer-switch">
<label><input type="checkbox" id="aqLayer" checked> Air Quality (AQ)</label>
<label><input type="checkbox" id="treePollenLayer"> Tree Pollen</label>
<label><input type="checkbox" id="grassPollenLayer"> Grass Pollen</label>
<label><input type="checkbox" id="weedPollenLayer"> Weed Pollen</label>
</div>
<div class="controls">
<button id="playButton">Play</button>
<button id="pauseButton">Pause</button>
</div>
<div class="opacity-control">
<label for="opacityRange">Heatmap Opacity:</label>
<input type="range" id="opacityRange" min="0" max="1" step="0.1" value="0.6">
</div>
<div class="time-control">
<label for="timeRange">Timeframe</label>
<input type="range" id="timeRange" min="0" max="48" step="1" value="42" style="width: 100%;">
<span id="timeDisplay">Loading...</span>
</div>
<script>
function getFormattedDateHour(offsetHours) {
var date = new Date();
date.setUTCHours(date.getUTCHours() - offsetHours);
var year = date.getUTCFullYear();
var month = String(date.getUTCMonth() + 1).padStart(2, '0');
var day = String(date.getUTCDate()).padStart(2, '0');
var hours = String(date.getUTCHours()).padStart(2, '0');
return `${year}_${month}_${day}_${hours}`;
}
function getFormattedDateHourDisplay(offsetHours) {
var date = new Date();
date.setUTCHours(date.getUTCHours() - offsetHours);
var year = date.getUTCFullYear();
var month = String(date.getUTCMonth() + 1).padStart(2, '0');
var day = String(date.getUTCDate()).padStart(2, '0');
var hours = String(date.getUTCHours()).padStart(2, '0');
var minutes = String(date.getUTCMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes} UTC`;
}
var attribution = 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, Ambee';
var options = {
maxZoom: 16,
attribution: attribution,
errorTileUrl: 'https://via.placeholder.com/256?text=Tile+not+available',
opacity: 0.6
};
var apiKey = 'Your API Key'; //Contact Ambee Team for your API Key
var map = L.map("map", { center: [37.0902, -95.7129], zoom: 4, layers: [] });
var osmLabels = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
maxZoom: 16
}).addTo(map);
var aqLayer, treePollenLayer, grassPollenLayer, weedPollenLayer;
var currentHour = 48;
var intervalId;
function updateLayers() {
var dateHour = getFormattedDateHour(currentHour);
var displayDateHour = getFormattedDateHourDisplay(currentHour);
document.getElementById('timeDisplay').innerText = displayDateHour;
if (aqLayer) map.removeLayer(aqLayer);
if (treePollenLayer) map.removeLayer(treePollenLayer);
if (grassPollenLayer) map.removeLayer(grassPollenLayer);
if (weedPollenLayer) map.removeLayer(weedPollenLayer);
var aqTileUrl = `https://tiles.ambeedata.com/v1/aq/${dateHour}/{z}/{x}/{y}.png?x-api-key=${apiKey}`;
var treePollenTileUrl = `https://tiles.ambeedata.com/v1/tree_pollen/${dateHour}/{z}/{x}/{y}.png?x-api-key=${apiKey}`;
var grassPollenTileUrl = `https://tiles.ambeedata.com/v1/grass_pollen/${dateHour}/{z}/{x}/{y}.png?x-api-key=${apiKey}`;
var weedPollenTileUrl = `https://tiles.ambeedata.com/v1/weed_pollen/${dateHour}/{z}/{x}/{y}.png?x-api-key=${apiKey}`;
var opacity = parseFloat(document.getElementById('opacityRange').value);
aqLayer = L.tileLayer(aqTileUrl, { ...options, opacity });
treePollenLayer = L.tileLayer(treePollenTileUrl, { ...options, opacity });
grassPollenLayer = L.tileLayer(grassPollenTileUrl, { ...options, opacity });
weedPollenLayer = L.tileLayer(weedPollenTileUrl, { ...options, opacity });
if (document.getElementById('aqLayer').checked) map.addLayer(aqLayer);
if (document.getElementById('treePollenLayer').checked) map.addLayer(treePollenLayer);
if (document.getElementById('grassPollenLayer').checked) map.addLayer(grassPollenLayer);
if (document.getElementById('weedPollenLayer').checked) map.addLayer(weedPollenLayer);
}
function play() {
intervalId = setInterval(function() {
currentHour--;
if (currentHour < 0) {
currentHour = 48;
}
updateLayers();
document.getElementById('timeRange').value = currentHour;
}, 1000);
}
function pause() {
clearInterval(intervalId);
}
document.getElementById('playButton').addEventListener('click', play);
document.getElementById('pauseButton').addEventListener('click', pause);
document.getElementById('aqLayer').addEventListener('change', function () {
this.checked ? map.addLayer(aqLayer) : map.removeLayer(aqLayer);
});
document.getElementById('treePollenLayer').addEventListener('change', function () {
this.checked ? map.addLayer(treePollenLayer) : map.removeLayer(treePollenLayer);
});
document.getElementById('grassPollenLayer').addEventListener('change', function () {
this.checked ? map.addLayer(grassPollenLayer) : map.removeLayer(grassPollenLayer);
});
document.getElementById('weedPollenLayer').addEventListener('change', function () {
this.checked ? map.addLayer(weedPollenLayer) : map.removeLayer(weedPollenLayer);
});
document.getElementById('opacityRange').addEventListener('input', function () {
var opacity = parseFloat(this.value);
if (aqLayer) aqLayer.setOpacity(opacity);
if (treePollenLayer) treePollenLayer.setOpacity(opacity);
if (grassPollenLayer) grassPollenLayer.setOpacity(opacity);
if (weedPollenLayer) weedPollenLayer.setOpacity(opacity);
});
document.getElementById('timeRange').addEventListener('input', function () {
currentHour = parseInt(this.value);
updateLayers();
});
updateLayers();
</script>
</body>
</html>