DEV Community

climintell
climintell

Posted on

Building Volcano Monitoring Applications with Satellite-Based Degassing Data

When Hayli Gubbi volcano in Ethiopia erupted on November 23, 2025 after 12,000 years of dormancy, satellite SO₂ sensors detected precursor signals 5-6 days before the explosion. For developers building disaster monitoring applications, this raises an important question: how do you integrate real-time volcanic degassing data into your applications?

This article walks through the architecture, data formats, and integration patterns for building volcano monitoring apps using satellite-based SO₂ data.

The Problem Space

Traditional volcano monitoring relies on ground-based seismic networks and gas sensors. This works well for well-studied volcanoes like Kilauea or Mount St. Helens, but fails for:

  • Remote volcanoes (Hayli Gubbi had zero ground infrastructure)

  • Understudied volcanoes (no historical eruption data)

  • Developing regions (limited resources for monitoring networks)

  • Global-scale monitoring (thousands of active/dormant volcanoes worldwide)

Satellite-based monitoring fills this gap. The Copernicus Sentinel-5P satellite measures atmospheric SO₂ concentrations daily at 7×3.5 km spatial resolution, covering every volcano on Earth.

The challenge for developers: raw satellite data requires significant processing before it's useful for applications.

Why not use raw Sentinel-5P data directly?

  • Data volume: Daily global coverage = petabytes
  • Processing complexity: Atmospheric corrections, cloud masking, quality filtering
  • Domain expertise: Interpreting mol/m² values requires volcanology knowledge
  • Infrastructure: Requires geospatial data processing pipeline

APIs like VolcanoWatch handle the processing layer, returning actionable volcanic intelligence.

API Request Pattern

Basic Degassing Analysis

POST /api/volcano/degassing-analysis
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY

{
  "volcano_name": "Hayli Gubbi (Erta Ale)",
  "country": "Ethiopia",
  "analysis_period_months": 6,
  "buffer_km": 10
}
Enter fullscreen mode Exit fullscreen mode

Parameters explained:

  • volcano_name: Target volcano (use standardized names from Global Volcanism Program)
  • country: Geographic filter (handles volcanoes with multiple names)
  • analysis_period_months: Historical lookback window
  • buffer_km: Spatial radius around volcano coordinates (accounts for plume drift)

Response Structure

{
  "volcano": {
    "name": "Hayli Gubbi (Erta Ale)",
    "location": {
      "latitude": 13.33,
      "longitude": 40.72
    },
    "elevation_m": 500
  },
  "analysis": {
    "period": {
      "start_date": "2025-07-01",
      "end_date": "2026-01-01",
      "total_days": 184
    },
    "anomalies": {
      "total_count": 12,
      "high_alerts": 1,
      "moderate_alerts": 2,
      "weak_alerts": 9
    },
    "events": [
      {
        "date": "2025-11-23",
        "so2_column_density": 24.035,
        "alert_level": "HIGH",
        "confidence": "high",
        "interpretation": "Active eruption in progress"
      },
      {
        "date": "2025-11-18",
        "so2_column_density": 1.510,
        "alert_level": "WEAK",
        "confidence": "medium",
        "interpretation": "Precursor degassing detected"
      }
      // ... more events
    ]
  },
  "thresholds": {
    "weak": 0.0015,
    "moderate": 0.005,
    "high": 0.01,
    "units": "mol/m²"
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Integration Patterns

1. Real-Time Alert Systems

Use case: Send notifications when volcanic unrest is detected

import requests
from datetime import datetime, timedelta

def check_volcano_status(volcano_name):
    """
    Check for recent degassing anomalies
    Returns alert level if activity detected in last 48 hours
    """
    response = requests.post(
        'https://api.climintell.com/volcano/degassing-analysis',
        headers={'Authorization': f'Bearer {API_KEY}'},
        json={
            'volcano_name': volcano_name,
            'analysis_period_months': 1,  # Recent activity only
            'buffer_km': 10
        }
    )

    data = response.json()

    # Check for recent anomalies
    cutoff_date = datetime.now() - timedelta(days=2)
    recent_events = [
        event for event in data['analysis']['events']
        if datetime.fromisoformat(event['date']) > cutoff_date
    ]

    if not recent_events:
        return None

    # Return highest alert level detected
    alert_levels = {'HIGH': 3, 'MODERATE': 2, 'WEAK': 1}
    max_alert = max(recent_events, key=lambda x: alert_levels.get(x['alert_level'], 0))

    return {
        'volcano': volcano_name,
        'alert_level': max_alert['alert_level'],
        'so2_value': max_alert['so2_column_density'],
        'date': max_alert['date'],
        'interpretation': max_alert['interpretation']
    }

# Example usage
alert = check_volcano_status('Hayli Gubbi (Erta Ale)')
if alert and alert['alert_level'] in ['HIGH', 'MODERATE']:
    send_notification(alert)  # Your notification function
Enter fullscreen mode Exit fullscreen mode

2. Precursor Detection Dashboard

Use case: Visualize degassing trends to identify precursor signals

// Fetch historical data
async function getVolcanoDegassingTrend(volcanoName, months = 6) {
  const response = await fetch('https://api.climintell.com/volcano/degassing-analysis', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      volcano_name: volcanoName,
      analysis_period_months: months,
      buffer_km: 10
    })
  });

  const data = await response.json();

  // Transform for charting library (e.g., Chart.js, Recharts)
  return data.analysis.events.map(event => ({
    date: new Date(event.date),
    so2: event.so2_column_density,
    alert: event.alert_level,
    label: event.interpretation
  }));
}

// Render timeline chart
async function renderDegassingChart(volcanoName) {
  const trendData = await getVolcanoDegassingTrend(volcanoName);

  // Color-code by alert level
  const colorMap = {
    'HIGH': '#dc2626',
    'MODERATE': '#ea580c',
    'WEAK': '#facc15',
    'NORMAL': '#22c55e'
  };

  // Chart.js example
  new Chart(ctx, {
    type: 'scatter',
    data: {
      datasets: [{
        label: 'SO₂ Emissions',
        data: trendData.map(d => ({ x: d.date, y: d.so2 })),
        backgroundColor: trendData.map(d => colorMap[d.alert])
      }]
    },
    options: {
      scales: {
        y: { 
          title: { text: 'SO₂ Column Density (mol/m²)' },
          type: 'logarithmic'  // Better for wide range of values
        }
      }
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

3. Multi-Volcano Monitoring

Use case: Aviation safety, regional monitoring

def monitor_volcano_region(volcanoes_list, alert_threshold='MODERATE'):
    """
    Monitor multiple volcanoes in a region
    Returns list of volcanoes with alerts above threshold
    """
    active_volcanoes = []

    for volcano in volcanoes_list:
        status = check_volcano_status(volcano)

        if status and meets_threshold(status['alert_level'], alert_threshold):
            active_volcanoes.append(status)

    return active_volcanoes

# Example: Monitor Ethiopian Rift Valley volcanoes
ethiopian_volcanoes = [
    'Hayli Gubbi (Erta Ale)',
    'Erta Ale',
    'Dabbahu',
    'Manda Hararo',
    'Alu-Dalafilla'
]

alerts = monitor_volcano_region(ethiopian_volcanoes, alert_threshold='WEAK')

# Generate aviation advisory
if alerts:
    generate_vaac_style_report(alerts)
Enter fullscreen mode Exit fullscreen mode

Data Interpretation Best Practices

Understanding SO₂ Thresholds

The API returns threshold-classified alerts, but understanding the science helps with application logic:

Weak Precursor (>0.0015 mol/m²):

  • Typical warning window: 5-7 days
  • Interpretation: Magma degassing at depth
  • Action: Increased monitoring, preparedness

Moderate (>0.005 mol/m²):

  • Magma approaching surface or significant pressure buildup
  • Action: Enhanced surveillance, evacuation planning

High (>0.01 mol/m²):

  • Co-eruptive or actively erupting
  • Action: Emergency response, aviation alerts

Critical consideration: These thresholds are statistically derived from historical eruptions. They provide guidance, not certainty.

Confidence Levels

The API returns confidence levels based on multi-parameter analysis:

{
  "confidence": "high",
  "factors": {
    "so2_elevation": true,
    "temporal_consistency": true,
    "spatial_pattern": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Integration logic:

def should_trigger_alert(event):
    """
    Decision logic for alert triggering
    """
    if event['alert_level'] == 'HIGH':
        return True  # Always alert on high

    if event['alert_level'] == 'MODERATE' and event['confidence'] == 'high':
        return True

    if event['alert_level'] == 'WEAK':
        # Only alert if part of temporal pattern
        return check_temporal_pattern(event)

    return False
Enter fullscreen mode Exit fullscreen mode

Temporal Pattern Recognition

Single anomalies can be atmospheric noise. Look for patterns:

def check_temporal_pattern(events, window_days=7):
    """
    Check if anomalies cluster temporally (precursor pattern)
    """
    from collections import Counter
    from datetime import datetime, timedelta

    dates = [datetime.fromisoformat(e['date']) for e in events]
    dates.sort()

    # Check for multiple anomalies within window
    clusters = []
    for i, date in enumerate(dates):
        cluster = [date]
        for other_date in dates[i+1:]:
            if (other_date - date).days <= window_days:
                cluster.append(other_date)
        if len(cluster) >= 2:
            clusters.append(cluster)

    return len(clusters) > 0  # Pattern detected
Enter fullscreen mode Exit fullscreen mode

Use Cases by Industry

Aviation Safety

  • Integration point: VAAC (Volcanic Ash Advisory Centers)
  • Key feature: SO₂ cloud tracking + ash plume correlation
  • Alert threshold: Any detected anomaly (aviation is zero-tolerance)

Disaster Management

  • Integration point: Civil defense early warning systems
  • Key feature: Temporal pattern detection for evacuation timing
  • Alert threshold: MODERATE with high confidence

Research & Academia

  • Integration point: Volcano observatories, research databases
  • Key feature: Historical analysis, eruption correlation studies
  • Alert threshold: All events logged for analysis

Insurance & Risk Assessment

  • Integration point: Catastrophe modeling systems
  • Key feature: Long-term degassing trends, eruption probability
  • Alert threshold: Sustained elevated activity

Rate Limits & Caching Strategies

Satellite data updates daily, so aggressive polling isn't necessary:

import time
from functools import lru_cache

@lru_cache(maxsize=128)
def get_volcano_status_cached(volcano_name, date_key):
    """
    Cache results by volcano + date
    date_key ensures daily refresh
    """
    return check_volcano_status(volcano_name)

# Usage
date_key = datetime.now().strftime('%Y-%m-%d')
status = get_volcano_status_cached('Erta Ale', date_key)
Enter fullscreen mode Exit fullscreen mode

Recommended polling frequency:

  • Real-time monitoring: Every 6-12 hours
  • Dashboard updates: Daily
  • Historical analysis: On-demand

Error Handling

import requests
from requests.exceptions import RequestException

def safe_api_call(volcano_name, max_retries=3):
    """
    Robust API calling with exponential backoff
    """
    for attempt in range(max_retries):
        try:
            response = requests.post(
                API_ENDPOINT,
                headers={'Authorization': f'Bearer {API_KEY}'},
                json={'volcano_name': volcano_name},
                timeout=30
            )

            if response.status_code == 200:
                return response.json()
            elif response.status_code == 404:
                # Volcano not found - check name spelling
                raise ValueError(f"Volcano '{volcano_name}' not found")
            elif response.status_code == 429:
                # Rate limit - backoff
                wait_time = 2 ** attempt
                time.sleep(wait_time)
                continue
            else:
                response.raise_for_status()

        except RequestException as e:
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)

    return None
Enter fullscreen mode Exit fullscreen mode

Going Further

This article covered core integration patterns. Additional capabilities to explore:

  • Spatial analysis: Plume drift modeling, downwind impact zones
  • Multi-gas correlation: SO₂ + CO + CH₄ for higher confidence
  • Seismic data fusion: Combine satellite degassing with ground seismic data
  • Machine learning: Train models on historical precursor patterns

Key Takeaways

  1. Satellite data democratizes volcano monitoring - works for remote/understudied volcanoes
  2. APIs abstract complexity - no need to process petabytes of raw satellite data
  3. Thresholds are guidance - use temporal patterns and confidence levels in logic
  4. Cache aggressively - daily satellite updates don't require real-time polling
  5. Context matters - interpretation differs by volcano type and tectonic setting

The Hayli Gubbi case proved satellite degassing data can detect precursors 5-6 days before explosive eruptions. The challenge for developers is building systems that act on these signals.

VolcanoWatch API provides processed Sentinel-5P SO₂ data for global volcano monitoring. For API access and documentation: www.climintell.com

Technologies mentioned: Sentinel-5P TROPOMI, Python, JavaScript, Chart.js

Related reading:

  • Carn et al. (2017) - "Multi-decadal satellite measurements of global volcanic degassing" - Scientific Reports
  • NASA Earth Observatory - Volcano monitoring from space
  • Copernicus Sentinel-5P documentation

What volcano monitoring use cases are you building? Drop your questions in the comments!

Top comments (0)