lab-charts

Chart.jsギャラリー

気象庁・e-Statの実データをChart.js(Canvas描画の定番ライブラリ)で可視化した6点。各カードの「コードを見る」で実装をそのまま確認できます。

東京の月別平均気温の変化

収録データの1991-2020年平均と2016-2025年平均の比較。夏を中心に近年が上回る(2014年の観測地点移転の影響を含む)

コードを見る
"use client";

import { Line } from "react-chartjs-2";
import { baseOptions } from "./chartjsSetup";
import { useReducedMotion } from "@/lib/useReducedMotion";
import { MONTH_LABELS } from "@/lib/format";
import { SERIES, withAlpha } from "@/lib/palette";
import type { TempLineData } from "@/lib/selectors";

export default function TokyoTempLine({ data }: { data: TempLineData }) {
  const reducedMotion = useReducedMotion();
  const base = baseOptions<"line">(reducedMotion);

  return (
    <div
      className="h-80"
      role="img"
      aria-label="東京の月別平均気温を収録データの1991-2020年平均と直近10年平均で比較した折れ線グラフ。夏を中心に多くの月で直近10年が上回る"
    >
      <Line
        data={{
          labels: [...MONTH_LABELS],
          datasets: [
            {
              label: data.recentLabel,
              data: data.recent,
              borderColor: SERIES[5],
              backgroundColor: withAlpha(SERIES[5], 0.15),
              borderWidth: 2,
              pointRadius: 3,
              fill: false,
              tension: 0.2,
            },
            {
              label: data.normalLabel,
              data: data.normal,
              borderColor: SERIES[0],
              backgroundColor: withAlpha(SERIES[0], 0.15),
              borderWidth: 2,
              borderDash: [6, 3],
              pointRadius: 3,
              fill: false,
              tension: 0.2,
            },
          ],
        }}
        options={{
          ...base,
          interaction: { mode: "index", intersect: false },
          plugins: {
            ...base.plugins,
            tooltip: {
              callbacks: {
                label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y}度`,
              },
            },
          },
          scales: {
            y: {
              title: { display: true, text: "月平均気温(度)" },
              ticks: { callback: (v) => `${v}度` },
            },
          },
        }}
      />
    </div>
  );
}

東京の猛暑日・真夏日・冬日の日数

年別の階級日数。夏の高温日は積み上げ棒、冬日は折れ線で重ねる

コードを見る
"use client";

import { Chart } from "react-chartjs-2";
import { baseOptions } from "./chartjsSetup";
import { useReducedMotion } from "@/lib/useReducedMotion";
import { SERIES, withAlpha } from "@/lib/palette";
import type { ExtremeDaysData } from "@/lib/selectors";

export default function ExtremeDaysBar({ data }: { data: ExtremeDaysData }) {
  const reducedMotion = useReducedMotion();
  // 棒+折れ線の混合チャートのためユニオン型で解決する
  const base = baseOptions<"bar" | "line">(reducedMotion);

  return (
    <div
      className="h-80"
      role="img"
      aria-label="東京の年別の猛暑日・真夏日日数の積み上げ棒グラフと冬日日数の折れ線グラフ。猛暑日は増加、冬日は減少傾向"
    >
      <Chart<"bar" | "line">
        type="bar"
        data={{
          labels: data.years,
          datasets: [
            {
              type: "bar" as const,
              label: "猛暑日(最高35度以上)",
              data: data.mousho,
              backgroundColor: SERIES[5],
              stack: "summer",
              borderRadius: 2,
            },
            {
              type: "bar" as const,
              label: "真夏日(最高30度以上35度未満)",
              data: data.manatsuExcl,
              backgroundColor: withAlpha(SERIES[7], 0.85),
              stack: "summer",
              borderRadius: 2,
            },
            {
              type: "line" as const,
              label: "冬日(最低0度未満)",
              data: data.fuyubi,
              borderColor: SERIES[0],
              backgroundColor: SERIES[0],
              borderWidth: 2,
              pointRadius: 2,
              tension: 0.2,
            },
          ],
        }}
        options={{
          ...base,
          interaction: { mode: "index", intersect: false },
          plugins: {
            ...base.plugins,
            tooltip: {
              callbacks: {
                label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y}日`,
              },
            },
          },
          scales: {
            x: { stacked: true, ticks: { maxTicksLimit: 12 } },
            y: {
              stacked: true,
              title: { display: true, text: "日数(日)" },
            },
          },
        }}
      />
    </div>
  );
}

4都市の月別降水量(平年値)

札幌・東京・大阪・那覇の降水パターンの違い

コードを見る
"use client";

import { Bar } from "react-chartjs-2";
import { baseOptions } from "./chartjsSetup";
import { useReducedMotion } from "@/lib/useReducedMotion";
import { MONTH_LABELS } from "@/lib/format";
import { CITY_COLORS } from "@/lib/palette";
import type { CityPrecipData } from "@/lib/selectors";

export default function CityPrecipBar({ data }: { data: CityPrecipData }) {
  const reducedMotion = useReducedMotion();
  const base = baseOptions<"bar">(reducedMotion);

  return (
    <div
      className="h-80"
      role="img"
      aria-label="札幌・東京・大阪・那覇の月別降水量平年値のグループ棒グラフ。那覇は梅雨期と台風期に多く、太平洋側は夏に多い"
    >
      <Bar
        data={{
          labels: [...MONTH_LABELS],
          datasets: data.cities.map((c) => ({
            label: c.name,
            data: c.precip,
            backgroundColor: CITY_COLORS[c.name],
            borderRadius: 2,
          })),
        }}
        options={{
          ...base,
          plugins: {
            ...base.plugins,
            tooltip: {
              callbacks: {
                label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y}mm`,
              },
            },
          },
          scales: {
            y: {
              title: { display: true, text: `月降水量(mm、${data.period})` },
            },
          },
        }}
      />
    </div>
  );
}

4都市の気候レーダー

1991-2020年平年値の5指標を指標ごとに正規化して比較(ツールチップは実測値)

コードを見る
"use client";

import { Radar } from "react-chartjs-2";
import { baseOptions } from "./chartjsSetup";
import { useReducedMotion } from "@/lib/useReducedMotion";
import { CITY_COLORS, withAlpha } from "@/lib/palette";
import type { CityRadarData } from "@/lib/selectors";

const UNITS = ["度", "mm", "時間", "度", "度"];

export default function CityRadar({ data }: { data: CityRadarData }) {
  const reducedMotion = useReducedMotion();
  const base = baseOptions<"radar">(reducedMotion);

  return (
    <div
      className="h-80"
      role="img"
      aria-label="札幌・東京・大阪・那覇の気候指標(年平均気温・年降水量・年日照時間・8月と1月の平均気温)を正規化して比較したレーダーチャート"
    >
      <Radar
        data={{
          labels: data.indicators,
          datasets: data.cities.map((c) => ({
            label: c.name,
            data: c.values,
            borderColor: CITY_COLORS[c.name],
            backgroundColor: withAlpha(CITY_COLORS[c.name], 0.08),
            borderWidth: 2,
            pointRadius: 2,
          })),
        }}
        options={{
          ...base,
          plugins: {
            ...base.plugins,
            tooltip: {
              callbacks: {
                // 正規化値ではなく実測の平年値を単位付きで表示する
                label: (ctx) => {
                  const city = data.cities[ctx.datasetIndex];
                  const raw = city.raw[ctx.dataIndex];
                  return `${city.name}: ${raw}${UNITS[ctx.dataIndex]}`;
                },
              },
            },
          },
          scales: {
            r: {
              min: 0,
              max: 100,
              ticks: { display: false },
              pointLabels: { font: { size: 11 } },
            },
          },
        }}
      />
    </div>
  );
}

都道府県別人口増減率(上位・下位)

2015年 -> 2020年(国勢調査)

コードを見る
"use client";

import { Bar } from "react-chartjs-2";
import { baseOptions } from "./chartjsSetup";
import { useReducedMotion } from "@/lib/useReducedMotion";
import { DIVERGING } from "@/lib/palette";
import type { PrefGrowthData } from "@/lib/selectors";

export default function PrefGrowthBar({ data }: { data: PrefGrowthData }) {
  const reducedMotion = useReducedMotion();
  const base = baseOptions<"bar">(reducedMotion);

  return (
    <div
      className="h-96"
      role="img"
      aria-label="都道府県別の人口増減率の横棒グラフ(増加上位と減少下位)。東京圏と沖縄で増加、東北で減少が大きい"
    >
      <Bar
        data={{
          labels: data.items.map((i) => i.name),
          datasets: [
            {
              label: `人口増減率 ${data.periodLabel}`,
              data: data.items.map((i) => i.rate),
              // 増加は赤系・減少は青系の発散配色
              backgroundColor: data.items.map((i) =>
                i.rate >= 0 ? DIVERGING.positive : DIVERGING.negative,
              ),
              borderRadius: 2,
            },
          ],
        }}
        options={{
          ...base,
          indexAxis: "y" as const,
          plugins: {
            ...base.plugins,
            legend: { display: false },
            tooltip: {
              callbacks: {
                label: (ctx) => {
                  const v = ctx.parsed.x ?? 0;
                  return `増減率: ${v >= 0 ? "+" : ""}${v}%`;
                },
              },
            },
          },
          scales: {
            x: {
              title: { display: true, text: `増減率(%、${data.periodLabel})` },
              ticks: { callback: (v) => `${v}%` },
            },
            y: { ticks: { autoSkip: false, font: { size: 11 } } },
          },
        }}
      />
    </div>
  );
}

東京の月平均気温と月降水量の関係

1991年以降の全月をプロット。夏期(6〜9月)に多雨月が集中する

コードを見る
"use client";

import { Scatter } from "react-chartjs-2";
import { baseOptions } from "./chartjsSetup";
import { useReducedMotion } from "@/lib/useReducedMotion";
import { SERIES, withAlpha } from "@/lib/palette";
import type { ScatterPoint } from "@/lib/selectors";

export default function TempPrecipScatter({ data }: { data: ScatterPoint[] }) {
  const reducedMotion = useReducedMotion();
  const base = baseOptions<"scatter">(reducedMotion);

  // 夏(6-9月)とそれ以外で色分けし、梅雨・台風期の降水集中を見せる
  const summer = data.filter((p) => p.month >= 6 && p.month <= 9);
  const other = data.filter((p) => p.month < 6 || p.month > 9);

  return (
    <div
      className="h-80"
      role="img"
      aria-label="東京の月平均気温と月降水量の散布図(1991年以降の全月)。夏期に降水量の多い月が集中する"
    >
      <Scatter
        data={{
          datasets: [
            {
              label: "6〜9月",
              data: summer,
              backgroundColor: withAlpha(SERIES[5], 0.55),
              pointRadius: 3,
            },
            {
              label: "その他の月",
              data: other,
              backgroundColor: withAlpha(SERIES[0], 0.45),
              pointRadius: 3,
            },
          ],
        }}
        options={{
          ...base,
          plugins: {
            ...base.plugins,
            tooltip: {
              callbacks: {
                label: (ctx) => {
                  const p = ctx.raw as ScatterPoint;
                  return `${p.year}年${p.month}月: ${p.x}度 / ${p.y}mm`;
                },
              },
            },
          },
          scales: {
            x: {
              title: { display: true, text: "月平均気温(度)" },
              ticks: { callback: (v) => `${v}度` },
            },
            y: {
              title: { display: true, text: "月降水量(mm)" },
            },
          },
        }}
      />
    </div>
  );
}