Ambee's New Heatmap Tile Overlay: Uplifting Climate Maps

October 7, 2024
2 min read
Ambee's New Heatmap Tile Overlay: Uplifting Climate MapsAmbee's New Heatmap Tile Overlay: Uplifting Climate Maps
Product Manager
quotation

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.

What's New?

🌿 Air Quality Heatmap

  • Visual Insight: Instantly understand air quality levels with a color gradient ranging from green (low AQI) to deep red (high AQI).
  • Up-to-date Data: Receive the latest air quality data every hour, ensuring you are always informed.
  • Historical Data: Access heatmaps for the past 50 hours, allowing for comprehensive analysis and trend identification.
Air Quality Heatmap Color Grading
Air Quality Heatmap Color Scale

🌼 Pollen Heatmap

  • Comprehensive Coverage: Access real-time pollen levels for grass, tree, and weed pollen.
  • Detailed Visualization: Utilize color gradients and distinct colors to represent various pollen types and intensity levels, from green (low pollen) to red (very high).
  • Historical Data: Review pollen data for the past 50 hours to better understand trends and fluctuations.
Pollen Heatmap Scale
Pollen Heatmap Scale

🛠 Key Features

  • Interactive Map Overlays: Zoom and pan to explore data at various geographic scales (Zoom Level 0-16).
  • Seamless Integration: Compatible with popular mapping libraries such as Leaflet, OpenLayers, and Mapbox.
  • Customizable Views: Choose your preferred base map and customize the color palette to suit your needs.
  • Modular Tile Server: Our design supports future data type plug-ins, including NO2 and CO, temperature, disaster, or any climate data. It’s completely customizable.

🔄 Data Updates

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.

🚀 How It Works

  • Easy Access: Integrate the heatmap overlay into your applications via our API.
  • Flexible Endpoints: Retrieve specific tiles or metadata using our RESTful API.

📈 Performance Highlights

  • Quick Response: Average tile request response time is under 500ms.
  • High Uptime: Ensured 95.9% uptime for reliable data access.
  • Timely Data: Data is updated within 2 hours of availability.

Use Cases

Our heatmap overlay is already making an impact across various industries. Here are some of the key use cases:

  • Health and Wellness Apps: Leveraging air quality and pollen data to provide users with real-time information, helping those with allergies or respiratory conditions plan their activities accordingly.
  • Environmental Monitoring: Integrating our heatmap data to track pollution levels and their impact on the environment, aiding in research and policy-making.
  • Urban Planning: Using historical and real-time air quality data to inform decisions on urban development and traffic management, enhancing the overall quality of life in cities.
  • Agricultural Management: Utilizing pollen heatmaps to understand and predict the spread of pollen, assisting in crop management and planning for allergy seasons.
  • Travel and Tourism: Providing tourists with real-time and historical data on air quality and pollen levels, helping them make informed travel decisions and enhancing their travel experience.

Ambee Climate Maps

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.

Get Your API Key

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.

Basic Heatmap Using Ambee Tile Overlay

Air Quality Heatmap Tile Overlay
Air Quality Heatmap Tile Overlay
Pollen Heatmaps Tile Overlay
Pollen Heatmaps Tile Overlay

For detailed documentation and to explore more about integrating Ambee's heatmap tile overlay, visit our heatmap tile overlay documentation.

Sample Script

<!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 &copy; <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: '&copy; <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>
Have questions?
Get in touch!
SUBMIT
Request submitted.
Our team will get in touch with you shortly!
Oops! Something went wrong while submitting the form.