Scenario
The Challenge
Event planners managing multiple outdoor venues (weddings, concerts, festivals, corporate events) need to make critical decisions about event scheduling, venue selection, and backup plans based on weather conditions. Traditional weather apps don't provide the location-specific precision needed for multi-venue event management.
The Solution
This implementation demonstrates how to build an interactive dashboard that displays weather data for multiple event venues on an interactive map, provides venue-specific weather suitability scores, and helps planners make informed decisions about event logistics and contingency planning.
Implementation Benefits
Risk Mitigation
Weather-based alerts prevent costly event cancellations
Significant reduction in weather-related event issuesVenue Optimization
Data-driven venue selection based on weather patterns
Improved guest satisfaction and event success ratesEquipment Planning
Precise weather data for equipment and logistics decisions
Optimized setup costs and resource allocationGuest Experience
Proactive communication about weather conditions
Enhanced attendee preparation and comfortAPI Request
API Request
Request Parameters
Parameter | Value | Type |
---|---|---|
location | "21.3099,-157.8581" | string |
apikey | "YOUR_API_KEY" | string |
Request URL
https://api.tomorrow.io/v4/weather/forecast?location=21.3099%2C-157.8581&apikey=YOUR_API_KEY
cURL
curl -X GET "https://api.tomorrow.io/v4/weather/forecast?location=21.3099%2C-157.8581&apikey=YOUR_API_KEY"
JavaScript
const response = await fetch('https://api.tomorrow.io/v4/weather/forecast?location=21.3099%2C-157.8581&apikey=YOUR_API_KEY', {
method: 'GET',
});
const data = await response.json();
API Response
🎪 Event Planning Significance of Key Fields
API Response
Key Response Fields
Complete Response
{
"timelines": [
{
"timestep": "1h",
"intervals": [
{
"time": "2025-01-06T14:00:00Z",
"values": {
"temperature": 16,
"humidity": 86,
"precipitationProbability": 0,
"rainIntensity": 0,
"windSpeed": 4.1,
"windDirection": 83,
"windGust": 6.8,
"weatherCode": 1001,
"visibility": 13.31,
"uvIndex": 0,
"cloudCover": 100
}
},
{
"time": "2025-01-06T15:00:00Z",
"values": {
"temperature": 17,
"humidity": 69,
"precipitationProbability": 0,
"rainIntensity": 0,
"windSpeed": 3.5,
"windDirection": 68,
"windGust": 6.9,
"weatherCode": 1001,
"visibility": 13.14,
"uvIndex": 1,
"cloudCover": 100
}
},
{
"time": "2025-01-06T16:00:00Z",
"values": {
"temperature": 19,
"humidity": 68,
"precipitationProbability": 0,
"rainIntensity": 0,
"windSpeed": 3.4,
"windDirection": 76,
"windGust": 7,
"weatherCode": 1001,
"visibility": 12.97,
"uvIndex": 2,
"cloudCover": 100
}
},
{
"time": "2025-01-06T17:00:00Z",
"values": {
"temperature": 20,
"humidity": 65,
"precipitationProbability": 15,
"rainIntensity": 0.2,
"windSpeed": 4.2,
"windDirection": 78,
"windGust": 8.1,
"weatherCode": 4000,
"visibility": 11.5,
"uvIndex": 1,
"cloudCover": 95
}
},
{
"time": "2025-01-06T18:00:00Z",
"values": {
"temperature": 21,
"humidity": 62,
"precipitationProbability": 75,
"rainIntensity": 1.8,
"windSpeed": 5.1,
"windDirection": 82,
"windGust": 9.2,
"weatherCode": 4001,
"visibility": 8.3,
"uvIndex": 0,
"cloudCover": 98
}
}
]
}
],
"location": {
"lat": 21.3099,
"lon": -157.8581
}
}
Response Structure
Live Implementation
This interactive dashboard demonstrates how to combine Tomorrow.io weather data with Leaflet.js mapping for multi-venue event planning and risk assessment.
Event Planning Workflow
Real Weather Data from Hawaii's Microclimates: Each venue shows actual weather conditions from different locations across Hawaii, demonstrating how dramatic climate variations can occur within a small geographic area. Click markers to compare real-world suitability scores across diverse Hawaiian microclimates.
🗺️ Zoom, pan, and click markers for detailed weather information • No API keys required!
Real weather data from June 9, 2025 (captured from Tomorrow.io API across Hawaiian microclimates)
Event Suitability Legend
🗺️ Leaflet.js Implementation
This implementation uses Leaflet.js with OpenStreetMap for zero-cost mapping. Features include:
- • Interactive Leaflet map with custom weather-based venue markers
- • OpenStreetMap tiles (completely free, no API keys required)
- • Real-time weather data integration from Tomorrow.io API
- • Custom popup windows with detailed weather information
- • Microclimate simulation for realistic venue variations
- • Color-coded risk assessment and suitability scoring
Implementation Code
// Initialize Leaflet map with OpenStreetMap tiles
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
const map = L.map('map-container', {
center: [20.5, -156.5], // Hawaii center coordinates [lat, lng]
zoom: 8,
zoomControl: true,
scrollWheelZoom: true
});
// Add OpenStreetMap tile layer (no API key required!)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19
}).addTo(map);
// Event venues across Hawaii's diverse microclimates
const venues = [
{
id: 'honolulu-beach',
name: 'Honolulu Beach Resort',
lat: 21.3099,
lon: -157.8581,
eventType: 'Wedding Reception',
capacity: 200
},
{
id: 'hilo-gardens',
name: 'Hilo Tropical Gardens',
lat: 19.7297,
lon: -155.0900,
eventType: 'Corporate Retreat',
capacity: 300
},
{
id: 'kona-coast',
name: 'Kona Coast Venue',
lat: 19.8968,
lon: -155.5828,
eventType: 'Music Festival',
capacity: 1000
},
{
id: 'mauna-kea',
name: 'Mauna Kea Observatory',
lat: 19.8207,
lon: -155.4680,
eventType: 'Stargazing Event',
capacity: 150
}
];
// Fetch weather data for all venues
async function updateVenueWeather(apiKey) {
console.log('Fetching weather data for', venues.length, 'venues...');
const weatherPromises = venues.map(async (venue) => {
try {
const url = `https://api.tomorrow.io/v4/weather/forecast?location=${venue.lat},${venue.lon}&apikey=${apiKey}×teps=1h&fields=temperature,humidity,precipitationProbability,windSpeed,visibility`;
console.log(`Fetching weather for ${venue.name} at ${venue.lat},${venue.lon}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
const currentWeather = data.timelines[0].intervals[0].values;
return {
...venue,
weather: currentWeather,
score: calculateEventScore(currentWeather)
};
} catch (error) {
console.error(`Failed to fetch weather for ${venue.name}:`, error);
return {
...venue,
weather: null,
score: 0
};
}
});
const venuesWithWeather = await Promise.all(weatherPromises);
return venuesWithWeather;
}
// Calculate event suitability score based on weather conditions
function calculateEventScore(weather) {
if (!weather) return 0;
let score = 100;
// Temperature comfort (optimal: 18-24°C)
if (weather.temperature < 10 || weather.temperature > 30) score -= 30;
else if (weather.temperature < 15 || weather.temperature > 27) score -= 15;
// Rain probability impact
if (weather.precipitationProbability > 70) score -= 40;
else if (weather.precipitationProbability > 30) score -= 20;
// Wind conditions (high winds problematic for tents, stages)
if (weather.windSpeed > 10) score -= 25;
else if (weather.windSpeed > 6) score -= 10;
// Visibility for photography/videography
if (weather.visibility < 5) score -= 15;
else if (weather.visibility < 10) score -= 5;
return Math.max(0, Math.round(score));
}
// Add weather-based markers to map
async function displayVenuesOnMap(apiKey) {
const venuesWithWeather = await updateVenueWeather(apiKey);
venuesWithWeather.forEach(venue => {
const score = venue.score;
const color = score >= 80 ? '#10b981' : score >= 60 ? '#f59e0b' : '#ef4444';
// Create custom marker element
const markerElement = document.createElement('div');
markerElement.style.cssText = `
width: 30px; height: 30px;
background: ${color};
border: 3px solid white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.4);
`;
markerElement.textContent = score.toString();
// Create custom Leaflet icon
const customIcon = L.divIcon({
html: markerElement.outerHTML,
className: 'custom-marker',
iconSize: [30, 30],
iconAnchor: [15, 15]
});
// Create detailed popup content
const popupContent = `
<div style="padding: 12px; font-family: sans-serif; max-width: 250px;">
<h3 style="margin: 0 0 8px 0; color: #1f2937;">${venue.name}</h3>
<div style="margin-bottom: 8px; padding: 6px; background: ${score >= 80 ? '#ecfdf5' : score >= 60 ? '#fefce8' : '#fef2f2'}; border-radius: 4px;">
<strong>Suitability Score: ${score}/100</strong>
</div>
${venue.weather ? `
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 12px;">
<div><strong>🌡️ Temperature:</strong><br/>${venue.weather.temperature}°C</div>
<div><strong>🌧️ Rain Chance:</strong><br/>${venue.weather.precipitationProbability}%</div>
<div><strong>💨 Wind Speed:</strong><br/>${venue.weather.windSpeed} m/s</div>
<div><strong>👁️ Visibility:</strong><br/>${venue.weather.visibility} km</div>
</div>
` : '<div style="color: red;">Weather data unavailable</div>'}
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #e5e7eb; font-size: 11px; color: #6b7280;">
<strong>Event Type:</strong> ${venue.eventType}<br/>
<strong>Capacity:</strong> ${venue.capacity} guests
</div>
</div>
`;
// Add marker to map
L.marker([venue.lat, venue.lon], { icon: customIcon })
.bindPopup(popupContent)
.addTo(map);
});
console.log('All venue markers added to map');
return venuesWithWeather;
}
// Initialize the venue weather dashboard
displayVenuesOnMap('YOUR_API_KEY').then(venues => {
console.log('Venue weather dashboard initialized with', venues.length, 'locations');
});
Leaflet.js Integration Steps
// 1. Install Leaflet.js (no API keys required!)
npm install leaflet @types/leaflet
// 2. Import Leaflet and CSS
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
// 3. Initialize map with OpenStreetMap
const map = L.map('map-container', {
center: [40.7589, -73.9851], // NYC coordinates [lat, lng]
zoom: 12,
zoomControl: true,
scrollWheelZoom: true
});
// 4. Add OpenStreetMap tile layer (completely free!)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19
}).addTo(map);
// 5. Create custom markers with weather data
function addWeatherMarker(venue, weatherData) {
const score = calculateEventScore(weatherData);
const color = score >= 80 ? '#10b981' : score >= 60 ? '#f59e0b' : '#ef4444';
// Custom marker element
const markerElement = document.createElement('div');
markerElement.style.cssText = `
width: 30px; height: 30px;
background: ${color};
border: 3px solid white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
`;
markerElement.textContent = score.toString();
// Create Leaflet marker
const customIcon = L.divIcon({
html: markerElement.outerHTML,
className: 'custom-marker',
iconSize: [30, 30],
iconAnchor: [15, 15]
});
L.marker([venue.lat, venue.lon], { icon: customIcon })
.bindPopup(`<div>
<h3>${venue.name}</h3>
<p>Score: ${score}/100</p>
<p>🌡️ ${weatherData.temperature}°C</p>
</div>`)
.addTo(map);
}
// 6. Error handling for API requests
async function fetchVenueWeather(venues, apiKey) {
try {
const promises = venues.map(async (venue) => {
const response = await fetch(
`https://api.tomorrow.io/v4/weather/forecast?location=${venue.lat},${venue.lon}&apikey=${apiKey}`
);
if (!response.ok) {
throw new Error(`Weather API error: ${response.status}`);
}
const data = await response.json();
return { ...venue, weather: data.timelines[0].intervals[0].values };
});
return await Promise.all(promises);
} catch (error) {
console.error('Failed to fetch venue weather:', error);
return venues; // Return venues without weather data
}
}