気象庁予報の JSON ファイルを PHP で RSS 形式に変換してみる

## 動機
先日[記事](https://qiita.com/harycurl/items/8bb100d914c97fe4bb68)で紹介した、サイネージソフトの Slideshow は、 RSS 形式以外の方法で外部のテキストソースを取得することができない。
そこで、大昔に借りたレンタル Web サーバーの PHP 機能を用いて、気象庁予報の JSON ファイルを RSS 形式に変換することにした。

## PHP で JSON ファイルを取得、要素を取り出す
PHP での JSON ファイルの取得・要素抽出は以下の記事を参考にした。

https://qiita.com/Tatsuki_Yo/items/e60d8cf15978e975f0d2

サンプル以外の要素も取り出したいので、 JSON ファイルの構造を確認したい。

以下の記事を見ると、 `var_dump` で全要素の構造が見られるようだ。

https://your-school.jp/php-json-to-associative-array/212/

実際にやってみたものの一部を抜粋するとこんな感じに。
<details><summary>200000.jsonの構造(一部)</summary>

```JSON: 200000.jsonの構造(一部)
array(2) {
	[0]=> 
	array(3) { 
		["publishingOffice"]=> 
		string(21) "長野地方気象台" 
		["reportDatetime"]=> 
		string(25) "2024-12-02T17:00:00+09:00" 
		["timeSeries"]=> 
		array(3) { 
			[0]=> 
			array(2) { 
				["timeDefines"]=> 
				array(3) { 
					[0]=> 
					string(25) "2024-12-02T17:00:00+09:00" 
					[1]=> 
					string(25) "2024-12-03T00:00:00+09:00" 
					[2]=> 
					string(25) "2024-12-04T00:00:00+09:00" 
				} 
				["areas"]=> 
				array(3) { 
					[0]=> 
					array(4) { 
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "北部" 
							["code"]=> 
							string(6) "200010" 
						} 
						["weatherCodes"]=> 
						array(3) { 
							[0]=> 
							string(3) "100" 
							[1]=> 
							string(3) "111" 
							[2]=> 
							string(3) "201" 
						} 
						["weathers"]=> 
						array(3) { 
							[0]=> 
							string(6) "晴れ" 
							[1]=> 
							string(96) "晴れ　昼前　から　くもり　所により　昼前　から　夜のはじめ頃　雨" 
							[2]=> 
							string(27) "くもり　時々　晴れ" 
						} 
						["winds"]=> 
						array(3) { 
							[0]=> 
							string(9) "南の風" 
							[1]=> 
							string(27) "南の風　後　北の風" 
							[2]=> 
							string(30) "東の風　後　北東の風" 
						} 
					}
					[1]=> 
					array(4) { 
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "中部" 
							["code"]=> 
							string(6) "200020" 
						} 
						["weatherCodes"]=> 
						array(3) { 
							[0]=> 
							string(3) "100" 
							[1]=> 
							string(3) "110" 
							[2]=> 
							string(3) "101" 
						} 
						["weathers"]=> 
						array(3) { 
							[0]=> 
							string(6) "晴れ" 
							[1]=> 
							string(48) "晴れ　昼過ぎ　から　時々　くもり" 
							[2]=> 
							string(27) "晴れ　時々　くもり" 
						} 
						["winds"]=> 
						array(3) { 
							[0]=> 
							string(9) "南の風" 
							[1]=> 
							string(9) "南の風" 
							[2]=> 
							string(9) "南の風" 
						} 
					} 
					[2]=> 
					array(4) {
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "南部" 
							["code"]=> 
							string(6) "200030" 
						}
						["weatherCodes"]=> 
						array(3) { 
							[0]=> 
							string(3) "100" 
							[1]=> 
							string(3) "110" 
							[2]=> 
							string(3) "101" 
						} 
						["weathers"]=> 
						array(3) { 
							[0]=> 
							string(6) "晴れ" 
							[1]=> 
							string(48) "晴れ　昼過ぎ　から　時々　くもり" 
							[2]=> 
							string(27) "晴れ　時々　くもり" 
						} 
						["winds"]=> 
						array(3) { 
							[0]=> 
							string(9) "南の風" 
							[1]=> 
							string(9) "南の風" 
							[2]=> 
							string(9) "南の風" 
						} 
					} 
				} 
			} 
			[1]=> 
			array(2) { 
				["timeDefines"]=> 
				array(5) { 
					[0]=> 
					string(25) "2024-12-02T18:00:00+09:00" 
					[1]=> 
					string(25) "2024-12-03T00:00:00+09:00" 
					[2]=> 
					string(25) "2024-12-03T06:00:00+09:00" 
					[3]=> 
					string(25) "2024-12-03T12:00:00+09:00" 
					[4]=> 
					string(25) "2024-12-03T18:00:00+09:00" 
				} 
				["areas"]=> 
				array(3) { 
					[0]=> 
					array(2) { 
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "北部" 
							["code"]=> 
							string(6) "200010" 
						} 
						["pops"]=> 
						array(5) { 
							[0]=> 
							string(1) "0" 
							[1]=> 
							string(1) "0" 
							[2]=> 
							string(2) "20" 
							[3]=> 
							string(2) "40" 
							[4]=> 
							string(2) "30" 
						} 
					} 
					[1]=> 
					array(2) { 
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "中部" 
							["code"]=> 
							string(6) "200020" 
						} 
						["pops"]=> 
						array(5) { 
							[0]=> 
							string(1) "0" 
							[1]=> 
							string(1) "0" 
							[2]=> 
							string(2) "10" 
							[3]=> 
							string(2) "20" 
							[4]=> 
							string(2) "20" 
						} 
					} 
					[2]=> 
					array(2) { 
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "南部" 
							["code"]=> 
							string(6) "200030" 
						} 
						["pops"]=> 
						array(5) { 
							[0]=> 
							string(1) "0" 
							[1]=> 
							string(1) "0" 
							[2]=> 
							string(2) "10" 
							[3]=> 
							string(2) "20" 
							[4]=> 
							string(2) "20" 
						} 
					} 
				} 
			} 
			[2]=> 
			array(2) { 
				["timeDefines"]=>
				array(2) { 
					[0]=> 
					string(25) "2024-12-03T00:00:00+09:00" 
					[1]=> 
					string(25) "2024-12-03T09:00:00+09:00" 
				} 
				["areas"]=> 
				array(5) { 
					[0]=> 
					array(2) { 
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "長野" 
							["code"]=> 
							string(5) "48156" 
						} 
						["temps"]=> 
						array(2) { 
							[0]=> 
							string(1) "3" 
							[1]=> 
							string(2) "13" 
						} 
					} 
					[1]=> 
					array(2) { 
						["area"]=> 
						array(2) { 
							["name"]=> 
							string(6) "松本" 
							["code"]=> 
							string(5) "48361" 
						} 
						["temps"]=> 
						array(2) { 
							[0]=> 
							string(1) "6" 
							[1]=> 
							string(2) "15" 
						} 
```
</details>


1階層目から追っていくと
- 北部の今日の天気は `["0"]["timeSeries"]["0"]["areas"]["0"]["weathers"]["0"]`
- 北部の明日の天気は `["0"]["timeSeries"]["0"]["areas"]["0"]["weathers"]["1"]`
- 中部の今日の天気は `["0"]["timeSeries"]["0"]["areas"]["1"]["weathers"]["0"]`
- 北部の降水確率の最初の時間帯の数字は `["0"]["timeSeries"]["1"]["areas"]["0"]["pops"]["0"]`
- 長野の気温の最初の数字は `["0"]["timeSeries"]["2"]["areas"]["0"]["temps"]["0"]`

などとなる。

ちなみに、降水確率は最大8つ（当日0-6[^1], 当日6-12, 当日12-18, 当日18-24, 翌日0-6,翌日6-12,翌日12-18,翌日18-24）、気温は最大4つ（~~当日最低,~~ 当日日中最高, 当日最高, 翌日最低, 翌日最高）入るが、予報の発表時刻によって記載されない要素があり、その部分は空データとならず、詰めて配列に入る。厳密に表現したい場合は発表時間または要素数で条件を分けて処理した方がよいが、今回はそこまでは行わない。
[^1]: 実際は出現しないかも

## RSS 形式で出力する
RSS の書式はこちらの記事を参考にした。

https://kamenokoki.com/article/20210111

RSS の最低限必要な要素に絞り、表示に特化した形で title 等を設定の上、 RSS を出力する。
```PHP: jma_weather_rss.php
<?php
$url = "https://www.jma.go.jp/bosai/forecast/data/forecast/200000.json";

$weather_json = file_get_contents($url);
$weather_array = json_decode($weather_json, true);

$date = $weather_array["0"]["timeSeries"]["0"]["timeDefines"]["0"];
$jma_weather_1_1 = $weather_array["0"]["timeSeries"]["0"]["areas"]["0"]["weathers"]["0"];
$jma_weather_1_2 = $weather_array["0"]["timeSeries"]["0"]["areas"]["0"]["weathers"]["1"];
$jma_weather_2_1 = $weather_array["0"]["timeSeries"]["0"]["areas"]["1"]["weathers"]["0"];
$jma_weather_2_2 = $weather_array["0"]["timeSeries"]["0"]["areas"]["1"]["weathers"]["1"];

$jma_pop_1 = $weather_array["0"]["timeSeries"]["1"]["areas"]["0"]["pops"];
$jma_pop_2 = $weather_array["0"]["timeSeries"]["1"]["areas"]["1"]["pops"];

$jma_temp_1 = $weather_array["0"]["timeSeries"]["2"]["areas"]["0"]["temps"];
$jma_temp_2 = $weather_array["0"]["timeSeries"]["2"]["areas"]["1"]["temps"];

echo '<?xml version="1.0" encoding="UTF-8"?>';
echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">';
echo "<channel><title>気象庁</title><link>https://www.jma.go.jp/</link><description>長野県の天気</description>";
echo "<lastBuildDate>".gmdate("D, d M Y H:i:s T")."</lastBuildDate>";
echo "<item><title>[長野県の予報]発表日時</title><description>" . $date . "</description></item>";
echo "<item><title>今日の天気(北部)</title><description>" . $jma_weather_1_1. "</description></item>";
echo "<item><title>明日の天気(北部)</title><description>" . $jma_weather_1_2. "</description></item>";
echo "<item><title>今日の天気(中部)</title><description>" . $jma_weather_2_1. "</description></item>";
echo "<item><title>明日の天気(中部)</title><description>" . $jma_weather_2_2. "</description></item>";
echo "<item><title>降水確率(長野)</title><description>";
foreach ($jma_pop_1 as $value){
    echo $value. '% ';
}
echo "</description></item>";
echo "<item><title>降水確率(松本)</title><description>";
foreach ($jma_pop_2 as $value){
    echo $value. '% ';
}
echo "</description></item>";
echo "<item><title>気温(長野)</title><description>";
foreach ($jma_temp_1 as $value){
    echo $value. '℃ ';
}
echo "</description></item>";
echo "<item><title>気温(松本)</title><description>";
foreach ($jma_temp_2 as $value){
    echo $value. '℃ ';
}
echo "</description></item>";
echo "</channel>";
echo "</rss>";
```
すると、このような RSS が出力される。
```
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>気象庁</title><link>https://www.jma.go.jp/</link><description>長野県の天気</description><lastBuildDate>Mon, 02 Dec 2024 16:58:39 GMT</lastBuildDate><item><title>[長野県の予報]発表日時</title><description>2024-12-02T17:00:00+09:00</description></item><item><title>今日の天気(北部)</title><description>晴れ</description></item><item><title>明日の天気(北部)</title><description>晴れ　昼前　から　くもり　所により　昼前　から　夜のはじめ頃　雨</description></item><item><title>今日の天気(中部)</title><description>晴れ</description></item><item><title>明日の天気(中部)</title><description>晴れ　昼過ぎ　から　時々　くもり</description></item><item><title>降水確率(長野)</title><description>0% 0% 20% 40% 30% </description></item><item><title>降水確率(松本)</title><description>0% 0% 10% 20% 20% </description></item><item><title>気温(長野)</title><description>3℃ 13℃ </description></item><item><title>気温(松本)</title><description>6℃ 15℃ </description></item></channel></rss>
```

あとは Slideshow の RSS として読みこませると、このように天気予報を表示させることができる（下部のティッカー。右上の天気は Slideshow の現在天気機能で今回の内容とは関係がない）。

![slideshow_rss_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/a8f04976-bf51-4809-54d5-81bdcd2a6402.jpeg)

## 若干の改良
絵文字が表示できる環境であれば、晴れ・くもり・雨・雪を☀・☁・☂・☃に置き換える手がある。
間に挟まった全角スペースも取り除くとさらに見た目がよくなる。
```php: 絵文字置換(一部)
$weather_phrase = array("晴れ", "くもり", "雨", "雪");
$weather_mark = array("☀","☁","☂","☃");
$jma_weather_1_1 = str_replace("　", "", $jma_weather_1_1);
$jma_weather_1_1 = str_replace($weather_phrase, $weather_mark, $jma_weather_1_1);
```
![slideshow_rss_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/af1e6571-9b9a-44bd-5cae-2bf105a8288a.jpeg)