気象庁のjsonをGitHubPagesで表示してみた


## 概要

- 気象庁のAPIのようなjsonを、GitHubPagesで表示してみました。
- スマートフォン向けの天気予報アプリです。

## デモ

https://kubo-4ka.github.io/chk-weather-app/


## ソース

https://github.com/kubo-4ka/chk-weather-app

## 主な機能
- 特徴
  - 都道府県、地方をプルダウンから簡単に選択できます。
  - 気象庁公式API？から最新の天気情報を取得し、信頼できる予報を提供します。
  - 見やすいダークテーマデザインを採用し、夜間や暗い環境でも快適に利用できます。
  - スマホの縦画面表示に特化しており、最小限の操作で天気を確認できます。
  - 「予報取得」ボタンは5秒間のインターバルを設けており、APIへの負担を軽減します。
  - 3/21追加　取得したjsonを参照できるようにしました。
  様々な情報が入っているので、気になる情報があれば、可視化できるよう改変してみてください。

- 使い方
  - 初期表示は東京都についての天気が表示されます。
  - プルダウンから、見たい都道府県・地方を選択します。
  - 「予報取得」ボタンを押し、選択したエリアの最新の天気予報を取得します。

- 技術仕様
  - HTML / CSS / JavaScript（Vanilla JS）を用いたフロントエンドのみの構成
  - GitHub Pagesでホスト

## 注意事項
4年ほど前に、気象庁の機能追加というか、APIのようなものが発見された、ということです。

https://forest.watch.impress.co.jp/docs/serial/yajiuma/1309318.html

提供が中断される可能性はある、ということですが、
一応4年間は提供が続いているようで、
今回はapi？利用、可視化のツールとして作成してみました。
モチベーションとしては、
jsonをJavaScriptで取得、表示すること、
GitHubPagesで公開すること、
スマホの見栄えが良い感じになること、
辺りを試してみたかった感じです。

## ソースについて

<details><summary>htmlについて</summary>

🔹 1. !DOCTYPE html

```html
<!DOCTYPE html>
```
- HTML5 を使用する宣言
ブラウザに「このページは HTML5 で記述されている」と伝えるために必須。

🔹 2. html lang="ja"
```html
<html lang="ja">
```
- HTML 文書の開始タグ
lang="ja" → ページの言語を「日本語」と明示。

🔹 3. head セクション
```html
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>天気予報</title>
    <link rel="stylesheet" href="style.css">
</head>
```
- `<meta charset="UTF-8">`
文字エンコーディングをUTF-8に設定
日本語の文字化けを防ぐために必須。
- `<meta name="viewport" content="width=device-width, initial-scale=1.0">`
スマホ対応のレスポンシブデザインを有効化
width=device-width → 画面サイズに合わせる
initial-scale=1.0 → 初期倍率を1倍にする
- `<title>天気予報</title>`
ブラウザのタブに表示されるページタイトル
- `<link rel="stylesheet" href="style.css">`
CSS ファイル (style.css) を適用
ページの見た目を整えるために使用

🔹 4. body セクション
```html
<body>
    <div class="container">
        <h1>天気予報</h1>
```
- ページの内容を表示するエリア
container クラスで全体を囲むことで CSS でレイアウトを統一。

🔹 5. タイトル（見出し）
```html
<h1>天気予報</h1>
```
- 大きな見出し（タイトル）
h1 は ページの最も重要な見出し。

🔹 6. エリア選択 & 予報取得ボタン
```html
<div class="controls">
    <label for="area">エリア選択:</label>
    <select id="area"></select>
    <button id="fetchWeather">予報取得</button>
</div>
```
- `<label for="area">エリア選択:</label>`
`<for="area" → select id="area">` と関連付け。
クリックすると、select> を自動的にフォーカス。
- `<select id="area"></select>`
エリア選択用のドロップダウンリスト
JavaScript (script.js) で動的にエリア情報を設定。
- `<button id="fetchWeather">予報取得</button>`
クリックすると、天気予報を取得
script.js 内のイベントで処理。

🔹 7. 天気予報の表示エリア
```html
<div id="weatherDisplay" class="weather-box">
    <p>天気予報を取得中...</p>
</div>
```
- 天気予報の情報を表示するボックス
- script.js で内容が更新される。

🔹 8. フッター
- 「気象庁のjsonを利用している」ことを明記

🔹 9. script.js の読み込み
```html
<script src="script.js"></script>
```
- JavaScript (script.js) を読み込む
- ページの機能（エリア選択や予報取得）を動かすために必要

</details>

---

<details><summary>jsについて</summary>

🔹 1. DOMContentLoaded イベントのリスナー
```javascript
document.addEventListener("DOMContentLoaded", function () {
```
- ページの読み込みが完了したら JavaScript を実行。
- まだ select や button が読み込まれていない段階では実行されない。

🔹 2. API の URL を定義
```javascript
const apiBaseUrl = "https://www.jma.go.jp/bosai/forecast/data/forecast/";
const areaJsonUrl = "https://www.jma.go.jp/bosai/common/const/area.json";
```
- apiBaseUrl → 気象庁の 天気予報 API の基本 URL
- areaJsonUrl → 気象庁の エリア情報 API の URL

🔹 3. HTML 要素を取得
```javascript
const areaSelect = document.getElementById("area");
const fetchWeatherBtn = document.getElementById("fetchWeather");
const weatherDisplay = document.getElementById("weatherDisplay");
```
- エリア選択 (`<select>`)
- 予報取得ボタン (`<button>`)
- 天気予報表示エリア (`<div>`)

🔹 4. エリア情報を取得
```javascript
function fetchAreaData() {
    fetch(areaJsonUrl)
        .then(response => response.json())
        .then(data => {
            areaData = data;
            updateAreaDropdown();
            fetchWeather(currentAreaCode);
        })
        .catch(error => console.error("エリア情報の取得に失敗しました:", error));
}
```
- fetch() を使って area.json を取得
- エリアデータ (areaData) を保存
- プルダウンを更新 (updateAreaDropdown())
- 天気を最初に取得 (fetchWeather)

🔹 5. エリア選択リストを更新
```javascript
function updateAreaDropdown() {
    areaSelect.innerHTML = "";
    let options = [];
    Object.keys(areaData.offices).forEach(code => {
        options.push({ code: code, name: areaData.offices[code].name });
    });
    options.sort((a, b) => a.code.localeCompare(b.code));
    options.forEach(opt => {
        let optionElement = document.createElement("option");
        optionElement.value = opt.code;
        optionElement.textContent = opt.name;
        areaSelect.appendChild(optionElement);
    });
    areaSelect.value = "130000";
}
```
- areaData.offices からエリア一覧を作成
- プルダウン (`<select>`) にエリアをセット
- 東京都 (130000) を初期選択

🔹 6. 天気予報を取得
```javascript
function fetchWeather(areaCode) {
    fetch(`${apiBaseUrl}${areaCode}.json`)
        .then(response => response.json())
        .then(data => {
            displayWeather(data);
        })
        .catch(error => {
            console.error("天気予報の取得に失敗しました:", error);
            weatherDisplay.innerHTML = "<p>天気予報の取得に失敗しました。</p>";
        });
}
```
- エリアごとの forecast/XXXXXX.json を取得
- 成功したら displayWeather() を実行

🔹 7. 予報を画面に表示

```javascript
function displayWeather(data) {
```

- 取得した天気データを div id="weatherDisplay" に表示
- 複数のエリアの天気を一覧で表示

</details>

---

<details><summary>cssについて</summary>

🔹 1. ダークテーマの設定
```css
body {
    background-color: #121212;
    color: #ffffff;
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100vh;
}
```
- 背景色 (background-color: #121212;)
  - 黒に近いダークグレー（深い黒すぎると目が疲れるため）
- 文字色 (color: #ffffff;)
  - 白で視認性を確保
- フォント (font-family: Arial, sans-serif;)
  - 汎用的なフォントを指定
- ページ全体の配置 (display: flex; flex-direction: column; align-items: center;)
  - 中央揃え
- 高さを 100% に (height: 100vh;)
  - スマホでのスクロールを最小限に

🔹 2. コンテンツの中央揃え
```css
.container {
    width: 90%;
    max-width: 400px;
    text-align: center;
    margin-top: 20px;
}
```
- 横幅は 90% で、最大 400px に制限
  - スマホの画面幅に合わせて適切なサイズ
- 中央揃え (text-align: center;)
- 上部の余白 (margin-top: 20px;)

🔹 3. タイトル（h1）のデザイン
```css
h1 {
    font-size: 24px;
}
```
- フォントサイズを 24px にして見やすく

🔹 4. エリア選択とボタンのデザイン
```css
.controls {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 20px;
}
```
- 要素を縦に並べる (flex-direction: column;)
- 要素間の間隔 (gap: 10px;)
- 下部に余白 (margin-bottom: 20px;)

🔹 5. プルダウン（`<select>`）とボタン（`<button>`）のデザイン
```css
select, button {
    padding: 10px;
    font-size: 16px;
    width: 100%;
    border-radius: 5px;
    border: none;
}
```
- 大きく押しやすいデザイン
- 角を丸めて (border-radius: 5px;) 柔らかい印象
- 横幅いっぱい (width: 100%;)

🔹 6. ボタンのデザイン
```css
button {
    background-color: #6200ea;
    color: white;
    cursor: pointer;
}

button:disabled {
    background-color: #555;
    cursor: not-allowed;
}
```
- 通常時の背景色 (background-color: #6200ea;)
  - 鮮やかな紫色（Material Design のボタンカラーに近い）
- 文字色 (color: white;)
- マウスオーバー時に pointer を表示
- ボタン無効時のデザイン (background-color: #555;)
  - グレーにして「無効感」を出す
- カーソルを「禁止マーク」に (cursor: not-allowed;)

🔹 7. 天気予報の表示エリア
```css
.weather-box {
    background-color: #333;
    padding: 15px;
    border-radius: 5px;
    text-align: left;
}
```
- 背景を少し明るいグレー (#333) に
- 余白 (padding: 15px;) を追加
- 角を丸める (border-radius: 5px;)
- 左揃え (text-align: left;) にして読みやすく

🔹 8. フッターのデザイン
```css
footer {
    margin-top: 20px;
    font-size: 12px;
}
```
- 上部に余白 (margin-top: 20px;)
- フォントサイズを小さく (font-size: 12px;)

🔹 9. フッターのリンクデザイン
```css
footer a {
    color: #bb86fc;
    text-decoration: none;
}

footer a:hover {
    text-decoration: underline;
}
```
- リンクの色 (color: #bb86fc;)
- 薄紫でダークテーマに馴染ませる
- マウスオーバー時に下線 (text-decoration: underline;)

</details>

---

## フロー図

```mermaid
sequenceDiagram
    participant User as 利用者（スマホ）
    participant GitHub as GitHub Pages（ホスティング）
    participant JMA as 気象庁APIサーバ

    User->>GitHub: スマホでアクセス
    GitHub-->>User: HTML, CSS, JavaScript を取得
    User->>User: JavaScriptを実行（スマホ上）

    User->>JMA: 気象庁APIにJSONリクエスト
    JMA-->>User: JSONレスポンス（天気データ）

    User->>User: 画面作成 & 天気情報を表示

```
---

:::note info
気温も知りたいなと思い、Open-Meteoのjsonを利用した天気予報もやってみたので、良ければご確認ください。

プルダウン形式
https://qiita.com/kubo4ka/items/1dcc35bf350a9e600e3c

地図形式
https://qiita.com/kubo4ka/items/7128a8040ac257a6c039

:::

以上