Weather Map API Integration Samples

This guide uses the Weather Maps API to provide standalone HTML examples for some of the most popular web mapping libraries: Leaflet, MapLibre GL JS, OpenLayers, Google Maps and ESRI.

Each example uses the standard XYZ tile format. Replace YOUR_API_KEY with your actual Visual Crossing API key to see the data.

Before attempting to use the samples, ensure that you have a valid Visual Crossing Weather account, if not click here to sign up. After signing up, ensure that you have access to the Weather Maps API. As the API is currently in beta, you can request access here. You will find you API key on the account page.

1. Leaflet (The Lightweight Standard)

Leaflet is the most common choice for simple 2D weather maps.

<!DOCTYPE html>
<html>
<head>
    <title>Visual Crossing - Leaflet</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <style>#map { height: 100vh; }</style>
</head>
<body>
    <div id="map"></div>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <script>
        const map = L.map('map').setView([40, -74], 5);

        // Base Layer
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

        // Weather Layer
        const apiKey = 'YOUR_API_KEY';
        const weatherUrl = `https://maps.visualcrossing.com/VisualCrossingWebServices/rest/api/v1/map/tile/precipcomposite/{z}/{x}/{y}.webp?key=${apiKey}&time=latest&unitGroup=metric&strict=false`;
        
        L.tileLayer(weatherUrl, {
            attribution: 'Visual Crossing',
            opacity: 0.7
        }).addTo(map);
    </script>
</body>
</html>

2. Leaflet – Simple animated map

The example uses the Leaflet Maps to create a simple animated precipitation map by pre-loading maps at 10 minute intervals and then switching between them.

<!DOCTYPE html>
<html>
<head>
    <title>Visual Crossing Animation</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <style>
        #map { height: 500px; width: 100%; }
        .controls { padding: 10px; background: #f8f9fa; }
    </style>
</head>
<body>

<div class="controls">
    <button onclick="toggleAnimation()">Start/Stop Animation</button>
    <span id="time-display">Loading...</span>
</div>
<div id="map"></div>

<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
    const API_KEY = 'YOUR_API_KEY';
    const map = L.map('map').setView([37, -95], 4); // Center of US
    
    // Add a base map (OpenStreetMap)
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

    let currentIndex = 0;
    let animationInterval = null;
    let layers = [];

    // 1. Generate a list of times (e.g., last 60 minutes in 10-min steps)
    function generateTimeSteps() {
        const steps = [];
        const now = new Date();
        for (let i = 6; i >= 0; i--) {
            const d = new Date(now.getTime() - (i * 10 * 60000));
            // Format to ISO string: YYYY-MM-DDTHH:mm:ss
            steps.push(d.toISOString().split('.')[0]);
        }
        return steps;
    }

    const timeSteps = generateTimeSteps();

    // 2. Create a layer for each time step
    timeSteps.forEach((time, index) => {
        const url = `https://maps.visualcrossing.com/VisualCrossingWebServices/rest/api/v1/map/tile/precipcomposite/{z}/{x}/{y}.webp?key=${API_KEY}&time=${time}&unitGroup=metric&source=radar`;
        
        const layer = L.tileLayer(url, {
            opacity: index === 0 ? 0.7 : 0, // Only show first layer initially
            zIndex: 100
        }).addTo(map);
        
        layers.push(layer);
    });

    // 3. Animation Logic
    function nextFrame() {
        // Hide current layer
        layers[currentIndex].setOpacity(0);
        
        // Move to next
        currentIndex = (currentIndex + 1) % layers.length;
        
        // Show next layer
        layers[currentIndex].setOpacity(0.7);
        document.getElementById('time-display').innerText = "Time: " + timeSteps[currentIndex];
    }

    function toggleAnimation() {
        if (animationInterval) {
            clearInterval(animationInterval);
            animationInterval = null;
        } else {
            animationInterval = setInterval(nextFrame, 800); // 800ms per frame
        }
    }

    document.getElementById('time-display').innerText = "Ready. Click Start.";
</script>
</body>
</html>

3. MapLibre GL JS (Vector / GPU Accelerated)

MapLibre is the open-source fork of Mapbox GL JS. It handles rotation and tilting very smoothly.

<!DOCTYPE html>
<html>
<head>
    <title>Visual Crossing - MapLibre</title>
    <link href="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.css" rel="stylesheet" />
    <style>#map { height: 100vh; }</style>
</head>
<body>
    <div id="map"></div>
    <script src="https://unpkg.com/maplibre-gl@3.x/dist/maplibre-gl.js"></script>
    <script>
        const apiKey = 'YOUR_API_KEY';
        const map = new maplibregl.Map({
            container: 'map',
            style: 'https://demotiles.maplibre.org/style.json', // Basic style
            center: [-74, 40],
            zoom: 4
        });

        map.on('load', () => {
            map.addSource('weather-source', {
                'type': 'raster',
                'tiles': [`https://maps.visualcrossing.com/VisualCrossingWebServices/rest/api/v1/map/tile/precipcomposite/{z}/{x}/{y}.webp?key=${apiKey}&time=latest&unitGroup=metric&strict=false`],
                'tileSize': 256
            });

            map.addLayer({
                'id': 'weather-layer',
                'type': 'raster',
                'source': 'weather-source',
                'paint': { 'raster-opacity': 0.7 }
            });
        });
    </script>
</body>
</html>

4. OpenLayers (The Powerful Heavyweight)

OpenLayers is preferred for complex GIS applications and handles projection systems very well.

<!DOCTYPE html>
<html>
<head>
    <title>Visual Crossing - OpenLayers</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v8.2.0/ol.css">
    <style>#map { width: 100%; height: 100vh; }</style>
</head>
<body>
    <div id="map"></div>
    <script src="https://cdn.jsdelivr.net/npm/ol@v8.2.0/dist/ol.js"></script>
    <script>
        const apiKey = 'YOUR_API_KEY';

        const map = new ol.Map({
            target: 'map',
            layers: [
                new ol.layer.Tile({ source: new ol.source.OSM() }), // Base
                new ol.layer.Tile({
                    opacity: 0.7,
                    source: new ol.source.XYZ({
                        url: `https://maps.visualcrossing.com/VisualCrossingWebServices/rest/api/v1/map/tile/precipcomposite/{z}/{x}/{y}.webp?key=${apiKey}&time=latest&unitGroup=metric&strict=false`
                    })
                })
            ],
            view: new ol.View({
                center: ol.proj.fromLonLat([-74, 40]),
                zoom: 5
            })
        });
    </script>
</body>
</html>

5. Google Maps JS API

Note: You will need a Google Maps API Key in addition to your Visual Crossing key.

<!DOCTYPE html>
<html>
<head>
    <title>Visual Crossing - Google Maps</title>
    <style>#map { height: 100vh; }</style>
</head>
<body>
    <div id="map"></div>
    <script>
        function initMap() {
            const map = new google.maps.Map(document.getElementById("map"), {
                zoom: 5,
                center: { lat: 40, lng: -74 },
            });

            const apiKey = 'YOUR_VC_API_KEY';
            
            const weatherLayer = new google.maps.ImageMapType({
                getTileUrl: function(coord, zoom) {
                    return `https://maps.visualcrossing.com/VisualCrossingWebServices/rest/api/v1/map/tile/precipcomposite/${zoom}/${coord.x}/${coord.y}.webp?key=${apiKey}&time=latest&unitGroup=metric&strict=false`;
                },
                tileSize: new google.maps.Size(256, 256),
                name: "Weather",
                opacity: 0.7
            });

            map.overlayMapTypes.insertAt(0, weatherLayer);
        }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_KEY&callback=initMap" async defer></script>
</body>
</html>

To add an ESRI example, you can use the ArcGIS Maps SDK for JavaScript.

For XYZ tile services like Visual Crossing, the appropriate class to use is WebTileLayer. This class allows you to define a URL template using standard placeholders. Note that while many libraries use {z}/{x}/{y}, ArcGIS uses {level}/{col}/{row} (though it often handles the standard format as well, the level/col/row syntax is the native SDK standard).

6. ESRI ArcGIS Maps SDK for JavaScript

This example uses the modern 4.x SDK. You’ll need an ArcGIS API Key for the basemap.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Visual Crossing - ArcGIS JS</title>
  <link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css" />
  <script src="https://js.arcgis.com/4.28/"></script>

  <style>
    html, body, #viewDiv { padding: 0; margin: 0; height: 100%; width: 100%; }
  </style>

  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/WebTileLayer"
    ], function(Map, MapView, WebTileLayer) {

      const apiKey = "YOUR_VC_API_KEY";
      
      // Define the Visual Crossing Weather Layer
      const weatherLayer = new WebTileLayer({
        urlTemplate: `https://maps.visualcrossing.com/VisualCrossingWebServices/rest/api/v1/map/tile/precipcomposite/{level}/{col}/{row}.webp?key=${apiKey}&time=latest&unitGroup=metric&strict=false`,
        opacity: 0.7,
        copyright: "Visual Crossing"
      });

      const map = new Map({
        basemap: "streets-vector", // Requires ArcGIS API Key or login
        layers: [weatherLayer]
      });

      const view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-74, 40], 
        zoom: 4
      });
    });
  </script>
</head>
<body>
  <div id="viewDiv"></div>
</body>
</html>

Important Tips for Weather Tiles:

  1. Tile Size: Most weather providers use 256x256 tiles. If you use MapLibre or Mapbox, ensure you specify this, as they often default to 512x512.
  2. Order: Always ensure the weather layer is added after the base map layer, or it will be hidden underneath.
  3. Opacity: Weather tiles often have solid backgrounds or intense colors; setting opacity to 0.6 or 0.7 usually makes the map more readable.