> ## Documentation Index
> Fetch the complete documentation index at: https://data.avert.ldeo.columbia.edu/llms.txt
> Use this file to discover all available pages before exploring further.

# RUKA

export const MultigasCO2SO2RatioChart = ({volcano, station, availableYears = [], defaultYear}) => {
  const volcanoName = volcano || "villarrica";
  const stationName = station || "ruka";
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [selectedYear, setSelectedYear] = useState(defaultYear || (availableYears.length > 0 ? availableYears[0] : new Date().getFullYear()));
  const years = useMemo(() => availableYears, [availableYears]);
  const chartId = `multigas-co2-so2-ratio-chart-${volcanoName}-${stationName}`;
  const triggerBlobDownload = async (url, fallbackFilename) => {
    try {
      const response = await fetch(url, {
        method: "GET",
        mode: "cors",
        headers: {
          Accept: "application/octet-stream, application/zip, */*"
        }
      });
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
      const blob = await response.blob();
      let filename = fallbackFilename;
      const cd = response.headers.get("Content-Disposition");
      if (cd) {
        const match = cd.match(/filename="?([^";]+)"?/i);
        if (match && match[1]) filename = match[1].trim();
      }
      const href = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = href;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(href);
    } catch (e) {
      console.error("Download failed:", e);
      alert("Failed to download file. Please try again.");
    }
  };
  const renderChart = data => {
    if (typeof window !== "undefined" && window.Plotly) {
      const chartDiv = document.getElementById(chartId);
      if (chartDiv) {
        const config = {
          ...data.chart_config.config,
          modeBarButtonsToRemove: [...data.chart_config.config?.modeBarButtonsToRemove || [], "zoom2d", "autoScale2d"]
        };
        const layout = {
          ...data.chart_config.layout,
          height: 600,
          paper_bgcolor: "rgba(0,0,0,0)",
          plot_bgcolor: "rgba(0,0,0,0)"
        };
        try {
          window.Plotly.newPlot(chartDiv, data.chart_config.data, layout, config);
        } catch (err) {
          console.error("Error rendering chart:", err);
        }
      }
    }
  };
  useEffect(() => {
    const loadPlotlyAndFetchData = async () => {
      setLoading(true);
      setError(null);
      if (typeof window !== "undefined" && !window.Plotly) {
        await new Promise((resolve, reject) => {
          const script = document.createElement("script");
          script.src = "https://cdn.plot.ly/plotly-2.35.2.min.js";
          script.onload = () => resolve();
          script.onerror = () => reject(new Error("Failed to load Plotly"));
          document.head.appendChild(script);
        });
      }
      try {
        const url = `https://avert.ldeo.columbia.edu/api/generate-interactive-chart/multigas-co2-so2-ratio/${volcanoName}/${stationName}?year=${selectedYear}`;
        const response = await fetch(url, {
          method: "POST",
          mode: "cors",
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json"
          }
        });
        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
        const data = await response.json();
        if (data.success) {
          setTimeout(() => renderChart(data), 200);
        } else {
          throw new Error("Failed to generate chart");
        }
      } catch (err) {
        setError(err.message);
        console.error("Error:", err);
      } finally {
        setLoading(false);
      }
    };
    loadPlotlyAndFetchData();
  }, [selectedYear, volcanoName, stationName]);
  if (loading) {
    return <div className="flex items-center justify-center h-64">
        <div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-[#0B1D26]"></div>
        <p className="ml-4 text-gray-600 dark:text-gray-300">Loading chart data...</p>
      </div>;
  }
  if (error) {
    return <div className="text-red-500 p-4 border border-red-300 rounded-md bg-red-50 dark:bg-red-900 dark:border-red-700">
        <p>{error}</p>
      </div>;
  }
  return <div className="border border-zinc-950/20 dark:border-white/20 rounded-xl overflow-hidden relative">
      <div className="bg-zinc-50 dark:bg-zinc-900 px-4 py-3 border-b border-zinc-950/20 dark:border-white/20">
        <div className="flex items-center justify-between">
          <div className="text-sm font-medium text-zinc-950 dark:text-white">
            [{selectedYear}] CO₂/SO₂ Ratio — {stationName.toUpperCase()} ({volcanoName.toUpperCase()})
          </div>
          <select value={selectedYear} onChange={e => setSelectedYear(parseInt(e.target.value))} className="text-xs px-2 py-1 border border-zinc-300 dark:border-zinc-600 rounded bg-white dark:bg-zinc-800">
            {years.map(yr => <option key={yr} value={yr}>{yr}</option>)}
          </select>
        </div>
      </div>
      <div id={chartId} style={{
    width: "100%",
    height: "600px"
  }} className="relative"></div>
      <div className="px-4 py-3 bg-zinc-50 dark:bg-zinc-900 border-t border-zinc-950/20 dark:border-white/20 flex items-center justify-between">
        <div className="text-xs text-zinc-600 dark:text-zinc-300">
          Download raw .dat files for the selected year (ZIP)
        </div>
        <button onClick={() => triggerBlobDownload(`https://avert.ldeo.columbia.edu/api/download/multigas/${selectedYear}/${stationName}`, `${stationName.toUpperCase()}_multigas_${selectedYear}.zip`)} className="text-xs px-3 py-1 rounded bg-[#0B1D26] text-white hover:bg-[#0B1D26]/80">
          Download {selectedYear} (.zip)
        </button>
      </div>
      <style>{`
        #${chartId} .modebar-group {
          display: flex !important;
          flex-direction: column !important;
        }
      `}</style>
    </div>;
};

export const MultigasSO2MaxChart = ({volcano, station, availableYears = [], defaultYear}) => {
  const volcanoName = volcano || "villarrica";
  const stationName = station || "ruka";
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [selectedYear, setSelectedYear] = useState(defaultYear || (availableYears.length > 0 ? availableYears[0] : new Date().getFullYear()));
  const years = useMemo(() => availableYears, [availableYears]);
  const chartId = `multigas-so2-max-chart-${volcanoName}-${stationName}`;
  const triggerBlobDownload = async (url, fallbackFilename) => {
    try {
      const response = await fetch(url, {
        method: "GET",
        mode: "cors",
        headers: {
          Accept: "application/octet-stream, application/zip, */*"
        }
      });
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
      const blob = await response.blob();
      let filename = fallbackFilename;
      const cd = response.headers.get("Content-Disposition");
      if (cd) {
        const match = cd.match(/filename="?([^";]+)"?/i);
        if (match && match[1]) filename = match[1].trim();
      }
      const href = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = href;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(href);
    } catch (e) {
      console.error("Download failed:", e);
      alert("Failed to download file. Please try again.");
    }
  };
  const renderChart = data => {
    if (typeof window !== "undefined" && window.Plotly) {
      const chartDiv = document.getElementById(chartId);
      if (chartDiv) {
        const config = {
          ...data.chart_config.config,
          modeBarButtonsToRemove: [...data.chart_config.config?.modeBarButtonsToRemove || [], "zoom2d", "autoScale2d"]
        };
        const layout = {
          ...data.chart_config.layout,
          height: 600,
          paper_bgcolor: "rgba(0,0,0,0)",
          plot_bgcolor: "rgba(0,0,0,0)"
        };
        try {
          window.Plotly.newPlot(chartDiv, data.chart_config.data, layout, config);
        } catch (err) {
          console.error("Error rendering chart:", err);
        }
      }
    }
  };
  useEffect(() => {
    const loadPlotlyAndFetchData = async () => {
      setLoading(true);
      setError(null);
      if (typeof window !== "undefined" && !window.Plotly) {
        await new Promise((resolve, reject) => {
          const script = document.createElement("script");
          script.src = "https://cdn.plot.ly/plotly-2.35.2.min.js";
          script.onload = () => resolve();
          script.onerror = () => reject(new Error("Failed to load Plotly"));
          document.head.appendChild(script);
        });
      }
      try {
        const url = `https://avert.ldeo.columbia.edu/api/generate-interactive-chart/multigas-so2-max/${volcanoName}/${stationName}?year=${selectedYear}`;
        const response = await fetch(url, {
          method: "POST",
          mode: "cors",
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json"
          }
        });
        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
        const data = await response.json();
        if (data.success) {
          setTimeout(() => renderChart(data), 200);
        } else {
          throw new Error("Failed to generate chart");
        }
      } catch (err) {
        setError(err.message);
        console.error("Error:", err);
      } finally {
        setLoading(false);
      }
    };
    loadPlotlyAndFetchData();
  }, [selectedYear, volcanoName, stationName]);
  if (loading) {
    return <div className="flex items-center justify-center h-64">
        <div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-[#0B1D26]"></div>
        <p className="ml-4 text-gray-600 dark:text-gray-300">Loading chart data...</p>
      </div>;
  }
  if (error) {
    return <div className="text-red-500 p-4 border border-red-300 rounded-md bg-red-50 dark:bg-red-900 dark:border-red-700">
        <p>{error}</p>
      </div>;
  }
  return <div className="border border-zinc-950/20 dark:border-white/20 rounded-xl overflow-hidden relative">
      <div className="bg-zinc-50 dark:bg-zinc-900 px-4 py-3 border-b border-zinc-950/20 dark:border-white/20">
        <div className="flex items-center justify-between">
          <div className="text-sm font-medium text-zinc-950 dark:text-white">
            [{selectedYear}] SO₂ Max (ppm) — {stationName.toUpperCase()} ({volcanoName.toUpperCase()})
          </div>
          <select value={selectedYear} onChange={e => setSelectedYear(parseInt(e.target.value))} className="text-xs px-2 py-1 border border-zinc-300 dark:border-zinc-600 rounded bg-white dark:bg-zinc-800">
            {years.map(yr => <option key={yr} value={yr}>{yr}</option>)}
          </select>
        </div>
      </div>
      <div id={chartId} style={{
    width: "100%",
    height: "600px"
  }} className="relative"></div>
      <div className="px-4 py-3 bg-zinc-50 dark:bg-zinc-900 border-t border-zinc-950/20 dark:border-white/20 flex items-center justify-between">
        <div className="text-xs text-zinc-600 dark:text-zinc-300">
          Download raw .dat files for the selected year (ZIP)
        </div>
        <button onClick={() => triggerBlobDownload(`https://avert.ldeo.columbia.edu/api/download/multigas/${selectedYear}/${stationName}`, `${stationName.toUpperCase()}_multigas_${selectedYear}.zip`)} className="text-xs px-3 py-1 rounded bg-[#0B1D26] text-white hover:bg-[#0B1D26]/80">
          Download {selectedYear} (.zip)
        </button>
      </div>
      <style>{`
        #${chartId} .modebar-group {
          display: flex !important;
          flex-direction: column !important;
        }
      `}</style>
    </div>;
};

<Tabs>
  <Tab title="SO₂ Max">
    <MultigasSO2MaxChart volcano="villarrica" station="ruka" availableYears={[2026]} defaultYear={2026} />
  </Tab>

  <Tab title="CO₂/SO₂ Ratio">
    <MultigasCO2SO2RatioChart volcano="villarrica" station="ruka" availableYears={[2026]} defaultYear={2026} />
  </Tab>
</Tabs>

The interactive charts above show multigas measurements from the RUKA monitoring station at Villarrica volcano, Chile. This data provides crucial insights into volcanic degassing processes and potential changes in volcanic activity.

## SO₂ Max (ppm) chart

Each point on the line represents the peak SO₂ concentration (in parts per million) measured
during a 30-minute analytical session. SO₂\_max is a direct measure of plume signal strength and
serves as a rough proxy for the total amount of gas being emitted by the volcano — though keep
in mind it is also sensitive to wind direction: if the plume is not blowing toward the sensor,
SO₂ will appear low even during active degassing. Elevated or rising SO₂\_max values may indicate
increased volcanic activity and warrant close monitoring.

## CO₂/SO₂ Ratio chart

Each dot represents the average CO₂/SO₂ ratio calculated over a 30-minute session.
A value is only shown when two quality-control conditions are both satisfied:

1. SO₂\_max exceeded 1 ppm (the plume was strong enough to measure reliably), AND
2. The R² of the linear regression between CO₂ and SO₂ concentrations was ≥ 0.5
   (the two gases co-varied tightly, confirming a volcanic source rather than noise).
   Sessions that did not meet these thresholds are omitted — that is why the ratio chart may appear
   sparse. Changes in the CO₂/SO₂ ratio over time can reflect shifts in magma degassing depth or
   supply rate: a rising ratio may indicate deeper or more primitive magma reaching the surface,
   while a falling ratio can suggest shallower, more degassed material.

<Note>The H2S sensor at this station is currently not operational, so H2S-related ratios are not shown.</Note>
