サンプル・プログラムの実行例
グラフ描画する感染症は、インフルエンザを含めて95種類を選択できる(2026年2月現在)。コマンドライン・オプション diseaseCode に0~94の感染症コードを指定する。番号と感染症名の対応は、配列変数 $ListDiseases に定義している。たとえば "influenza.php?d=94" と指定すると、COVID-19 のグラフを表示する。
また、コマンドライン・オプション mode に 1 を指定すると、グラフだけを表示する。2を指定すると、img タグの src 属性に代入する画像データを BASE64エンコード形式で出力する。上図のグラフもこのオプションを使って表示している。
その他のコマンドライン・オプションについては、influenza.php の冒頭のコメントをご覧いただきたい。
目次
サンプル・プログラム
| influenza.php | サンプル・プログラム本体 |
| dataIDWR.json | 感染症報告データのサンプル(JSON形式) |
| dataIDWR.xml | 感染症報告データのサンプル(XML形式) |
| pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
| pahooScraping.php | RSS処理に関わるクラス pahooScraping。 スクレイピング処理に関わるクラスの使い方は「PHPで作るRSSビューア」を参照。include_path が通ったディレクトリに配置すること。 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 3.1.0 | 2026/03/10 | loadXML, saveXML -- メモリ消費抑制 |
| 3.0.0 | 2026/03/06 | IDWR速報 のページ改編に応じて大幅改訂 |
| 2.4.0 | 2023/05/05 | グラフにフッタを追加 |
| 2.3 | 2022/04/03 | PHP8対応,リファラ・チェック改良 |
| 2.21 | 2019/10/06 | http→https変更 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 2.0.1 | 2025/08/11 | getParam() bug-fix |
| 2.0.0 | 2025/08/11 | pahooLoadEnv() 追加 |
| 1.9.0 | 2025/07/26 | getParam() 引数に$trim追加 |
| 1.8.1 | 2025/03/15 | validRegexPattern() debug |
| 1.8.0 | 2024/11/12 | validRegexPattern() 追加 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.2.1 | 2024/10/31 | __construct() 文字化け対策 |
| 1.2.0 | 2024/09/29 | getValueFistrXPath() 属性値でない指定に対応 |
| 1.1.0 | 2023/10/15 | getValueFistrXPath() 追加 |
| 1.0.1 | 2023/09/29 | __construct() bug-fix |
| 1.0.0 | 2023/09/18 | 初版 |
準備:pahooInputData 関数群
また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。
準備:pahooScraping クラス
pahooScraping.php
13: class pahooScraping {
14: private $dom; //DOMDocument
15: private $xpath; //DOMXPath
16: private $language; //使用言語(初期値は日本語)
17: private $error; //エラー・メッセージ
18: private $errorTable; //エラー番号 => エラー・メッセージ 一覧
19:
20: /**
21: * コンストラクタ
22: * @param string $contents 解析対象テキスト(UTF-8)
23: * @return object /NULL:PHP8未満のため実行できない.
24: */
25: function __construct($contents) {
26: //PHPのバージョンをチェックする.
27: if (! $this->isphp8over()) {
28: $this->error = 111;
29: throw new Exception($this->getErrorMessage());
30: return NULL;
31: }
32: //プロパティの初期値を設定する.
33: try {
34: // Unicodeの範囲を指定
35: $map = [ 0x80, 0xFFFF, 0, 0xFFFF ];
36: // DOMの文字化け対策:UTF-8文字を数値エンティティに変換
37: $contents = mb_encode_numericentity($contents, $map, 'UTF-8');
38: // DOMDocument クラス
39: $this->dom = new DOMDocument();
40: libxml_use_internal_errors(TRUE);
41: @$this->dom->loadHTML($contents);
42: libxml_clear_errors();
43: $this->xpath = new DOMXPath($this->dom);
44: } catch (Exception $e) {
45: $this->error = 121;
46: throw new Exception($e->getMessage());
47: return FALSE;
48: }
49:
50: //エラー番号 => エラー・メッセージ 一覧
51: $this->errorTable = array(
52: 111 => array('PHP8以上が必要です', 'PHP8 or higher is required'),
53: 121 => array('DOMエラーです', 'DOM error'),
54: 999 => array('不明のエラー', 'unknown error'),
55: );
56: }
PHPのクラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。
準備:JpGraphライブラリ
influenza.php
66: // JpGraph:include_pathが通ったディレクトリに配置
67: require_once('jpgraph/jpgraph.php');
68: require_once('jpgraph/jpgraph_line.php');
69:
JpGraph は、サーバ側(PHP側)でグラフを画像データとして生成し、クライアントへ配信する。このため、GDライブラリが有効化されていることが前提である。
また、使用する機能によってインクルードする PHPファイルの種類が異なることに留意してほしい。
公式サイトからダウンロードしたファイルを解凍したら、include_path が通ったディレクトリに配置してほしい。
凡例やラベルに日本語を使う場合は、日本語フォント(TrueTypeファイル)を用意しておく。これについても、アシアル社のサイトに詳しい説明がある。
具体的には、フォント・ファイルを格納したフォルダを "jpg-config.inc.php" の定数 TTF_DIR および MBTTF_DIR に設定してやる。
実際に使うフォントは "jpgraph_ttf.inc.php" の定数 MINCHO_TTF_FONT、PMINCHO_TTF_FONT、GOTHIC_TTF_FONT、PGOTHIC_TTF_FONT に設定されている。デフォルトで設定されているのは IPA の無償フォントで、文字情報技術促進協議会の IPAフォントから入手できる。
解説:各種定数
influenza.php
70: // 各種定数(START) ===========================================================
71:
72: // 表示モードのデフォルト値
73: define('DEF_MODE', 0); // 0:パラメータ選択入力, 1:画像のみ出力, 2:画像をBASE64出力
74:
75: // グラフ描画領域のサイズ(デフォルト値)
76: define('DEF_WIDTH', 600); // 幅(ピクセル)
77: define('DEF_HEIGHT', 400); // 高さ(ピクセル)
78:
79: // グラフの背景色(デフォルト値)
80: define('DEF_BGCOLOR', 'FFFFFF');
81:
82: // 感染症コードのデフォルト値
83: define('DEF_DISEASE_CODE', 75);
84:
85: // 感染症報告データ保管用XMLファイル名
86: define('FILENAME_XML', 'dataIDWR.xml');
87:
88: // 感染症報告データ保管用XMLファイル名
89: define('FILENAME_JSON', 'dataIDWR.json');
90:
91: // IDWR速報データ
92: define('URL_IDWR', 'https://id-info.jihs.go.jp/surveillance/idwr/provisional/sokuhou.html');
93:
94: // IDWR速報データのエンコード
95: define('IDWR_ENCODING', 'SJIS');
96:
97: // 1年の最終週
98: define('MAX_WEEK', 53);
99:
100: // 感染症リスト
101: $ListDiseases = array(
102: 0 => 'エボラ出血熱',
103: 1 => 'クリミア・コンゴ出血熱',
104: 2 => '痘そう',
105: 3 => '南米出血熱',
106: 4 => 'ペスト',
107: 5 => 'マールブルグ病',
108: 6 => 'ラッサ熱',
109: 7 => '急性灰白髄炎',
110: 8 => '結核',
111: 9 => 'ジフテリア',
112: 10 => '重症急性呼吸器症候群',
113: 11 => '鳥インフルエンザ(H5N1)',
114: 12 => 'コレラ',
115: 13 => '細菌性赤痢',
116: 14 => '腸管出血性大腸菌感染症',
117: 15 => '腸チフス',
118: 16 => 'パラチフス',
119: 17 => 'E型肝炎',
120: 18 => 'ウエストナイル熱',
121: 19 => 'A型肝炎',
122: 20 => 'エキノコックス症',
123: 21 => '黄熱',
124: 22 => 'オウム病',
125: 23 => 'オムスク出血熱',
126: 24 => '回帰熱',
127: 25 => 'キャサヌル森林病',
128: 26 => 'Q熱',
129: 27 => '狂犬病',
130: 28 => 'コクシジオイデス症',
131: 29 => 'サル痘',
132: 30 => '腎症候性出血熱',
133: 31 => '西部ウマ脳炎',
134: 32 => 'ダニ媒介脳炎',
135: 33 => '炭疽',
136: 34 => 'チクングニア熱',
137: 35 => 'つつが虫病',
138: 36 => 'デング熱',
139: 37 => '東部ウマ脳炎',
140: 38 => '鳥インフルエンザ(H5N1を除く)',
141: 39 => 'ニパウイルス感染症',
142: 40 => '日本紅斑熱',
143: 41 => '日本脳炎',
144: 42 => 'ハンタウイルス肺症候群',
145: 43 => 'Bウイルス病',
146: 44 => '鼻疽',
147: 45 => 'ブルセラ症',
148: 46 => 'ベネズエラウマ脳炎',
149: 47 => 'ヘンドラウイルス感染症',
150: 48 => '発しんチフス',
151: 49 => 'ボツリヌス症',
152: 50 => 'マラリア',
153: 51 => '野兎病',
154: 52 => 'ライム病',
155: 53 => 'リッサウイルス感染症',
156: 54 => 'リフトバレー熱',
157: 55 => '類鼻疽',
158: 56 => 'レジオネラ症',
159: 57 => 'レプトスピラ症',
160: 58 => 'ロッキー山紅斑熱',
161: 59 => 'アメーバ赤痢',
162: 60 => 'ウイルス性肝炎',
163: 61 => '急性脳炎',
164: 62 => 'クリプトスポリジウム症',
165: 63 => 'クロイツフェルト・ヤコブ病',
166: 64 => '劇症型溶血性レンサ球菌感染症',
167: 65 => '後天性免疫不全症候群',
168: 66 => 'ジアルジア症',
169: 67 => '髄膜炎菌性髄膜炎',
170: 68 => '先天性風しん症候群',
171: 69 => '梅毒',
172: 70 => '破傷風',
173: 71 => 'バンコマイシン耐性黄色ブドウ球菌感染症',
174: 72 => 'バンコマイシン耐性腸球菌感染症',
175: 73 => '風しん',
176: 74 => '麻しん',
177: 75 => 'インフルエンザ',
178: 76 => 'RSウイルス感染症',
179: 77 => '咽頭結膜熱',
180: 78 => 'A群溶血性レンサ球菌咽頭炎',
181: 79 => '感染性胃腸炎',
182: 80 => '水痘',
183: 81 => '手足口病',
184: 82 => '伝染性紅斑',
185: 83 => '突発性発しん',
186: 84 => '百日咳',
187: 85 => 'ヘルパンギーナ',
188: 86 => '流行性耳下腺炎',
189: 87 => '急性出血性結膜炎',
190: 88 => '流行性角結膜炎',
191: 89 => '細菌性髄膜炎',
192: 90 => '無菌性髄膜炎',
193: 91 => 'マイコプラズマ肺炎',
194: 92 => 'クラミジア肺炎',
195: 93 => '感染性胃腸炎(ロタウイルス)',
196: 94 => 'COVID-19',
197: );
198:
199: // 都道府県JISコード
200: $PrefList = array(
201: 1 => '北海道',
202: 2 => '青森県',
203: 3 => '岩手県',
204: 4 => '宮城県',
205: 5 => '秋田県',
206: 6 => '山形県',
207: 7 => '福島県',
208: 8 => '茨城県',
209: 9 => '栃木県',
210: 10 => '群馬県',
211: 11 => '埼玉県',
212: 12 => '千葉県',
213: 13 => '東京都',
214: 14 => '神奈川県',
215: 15 => '新潟県',
216: 16 => '富山県',
217: 17 => '石川県',
218: 18 => '福井県',
219: 19 => '山梨県',
220: 20 => '長野県',
221: 21 => '岐阜県',
222: 22 => '静岡県',
223: 23 => '愛知県',
224: 24 => '三重県',
225: 25 => '滋賀県',
226: 26 => '京都府',
227: 27 => '大阪府',
228: 28 => '兵庫県',
229: 29 => '奈良県',
230: 30 => '和歌山県',
231: 31 => '鳥取県',
232: 32 => '島根県',
233: 33 => '岡山県',
234: 34 => '広島県',
235: 35 => '山口県',
236: 36 => '徳島県',
237: 37 => '香川県',
238: 38 => '愛媛県',
239: 39 => '高知県',
240: 40 => '福岡県',
241: 41 => '佐賀県',
242: 42 => '長崎県',
243: 43 => '熊本県',
244: 44 => '大分県',
245: 45 => '宮崎県',
246: 46 => '鹿児島県',
247: 47 => '沖縄県'
248: );
249:
250: // 折れ線グラフの色(年ごとに少しずつ色を変える)
251: $PlotColor = array(
252: 9 => '#0072B2',
253: 8 => '#009E73',
254: 7 => '#E69F00',
255: 6 => '#56B4E9',
256: 5 => '#CC79A7',
257: 4 => '#D55E00',
258: 3 => '#8B4513',
259: 2 => '#6A3D9A',
260: 1 => '#1B9E77',
261: 0 => '#FF0000',
262: );
263:
264: // 折れ線グラフの太さ(直近のグラフだけ太線にする)
265: $PlotWeight = array(
266: 9 => 1,
267: 8 => 1,
268: 7 => 1,
269: 6 => 1,
270: 5 => 1,
271: 4 => 1,
272: 3 => 1,
273: 2 => 1,
274: 1 => 3,
275: 0 => 3,
276: );
277:
278: // グラフの透明処理用の色コード(描画に使っていないものであること)
279: // define('ALPHA_COLOR_CODE', '#FFFFCC');
280: define('ALPHA_COLOR_CODE', '#FF00FF');
281:
282: // 各種定数(END) ===============================================================
色はRGB各8bitで配列変数 $PlotColor に、太さは配列変数 $PlotWeight にあらかじめ代入しておく。
解説:IDWR速報サイトの構造
集計は週単位で、各週のページの中に報告データが CSV形式ファイルとして保存されている。このうち、"\d{4}-\d{2}-zensu.csv" が全数把握疾患、"\d{4}-\d{2}-teiten.csv" が定点把握疾患の報告数を格納しており、この2つのファイルの内容を取得して利用する。
まず、この2つの CSVファイルのURLを求めるユーザー関数を以下に掲げる。
サイト構造の各層の URL を求めるための関数を用意し、最後に getURLreports を使って CSVファイルのURLを求める。
influenza.php
392: /**
393: * IDWR速報がある URLリスト(年次)を取得する.
394: * @param なし
395: * @return array URLリスト [年]=URL / FALSE:取得エラー
396: */
397: function getURLlistYear() {
398: // ページ取得
399: $html = @file_get_contents(URL_IDWR);
400: // エラーチェック
401: if ($html === FALSE) return FALSE;
402:
403: // インスタンスを生成する.
404: $pcr = new pahooScraping($html);
405:
406: // "IDWR速報" を含むリンクを取得する.
407: $nodes = $pcr->queryXPath('//a[contains(normalize-space(.),"IDWR速報")]');
408:
409: // 配列に URLを格納する.
410: $items = [];
411:
412: foreach ($nodes as $node) {
413: $text = trim($node->textContent);
414: $url = $node->getAttribute('href');
415:
416: // 年(4桁)を抽出
417: if (preg_match('/(\d+)年/u', $text, $m)) {
418: $year = $m[1];
419: $items[$year] = resolveUrl(URL_IDWR, $url);
420: }
421: }
422:
423: // インスタンスを解放する.
424: $pcr = NULL;
425:
426: return $items;
427: }
influenza.php
429: /**
430: * IDWR速報がある年次URLを渡し,週次の URLリストを取得する.
431: * @param string $urlYear 年次URL
432: * @return array URLリスト [週番号]=URL / FALSE:取得エラー
433: */
434: function getURLlistWeek($urlYear) {
435: // ページ取得
436: $html = @file_get_contents($urlYear);
437: // エラーチェック
438: if ($html === FALSE) return FALSE;
439:
440: // インスタンスを生成する.
441: $pcr = new pahooScraping($html);
442:
443: // "IDWR速報" を含むリンクを取得する.
444: $nodes = $pcr->queryXPath('//a[contains(normalize-space(.),"IDWR速報")]');
445:
446: // 配列に URLを格納する.
447: $items = [];
448:
449: foreach ($nodes as $node) {
450: $text = trim($node->textContent);
451: $url = $node->getAttribute('href');
452:
453: // 年(4桁)を抽出
454: if (preg_match('/第(\d+)週/ui', $text, $m)) {
455: $week = $m[1];
456: $items[$week] = resolveUrl($urlYear, $url);
457: }
458: }
459:
460: // インスタンスを解放する.
461: $pcr = NULL;
462:
463: return $items;
464: }
influenza.php
466: /**
467: * IDWR速報がある週次URLを渡し,報告数CSVファイルの URLを取得する.
468: * @param string $urlWeek 週次URL
469: * @return array(全数把握疾患, 定点把握疾患)
470: */
471: function getURLreports($urlWeek) {
472: // 取得したいCSVファイル名
473: $targetReps = [
474: '/\d+\-\d+\-zensu.csv/i', // 全数把握疾患
475: '/\d+\-\d+\-teiten.csv/i' // 定点把握疾患
476: ];
477:
478: // ページ取得
479: $html = @file_get_contents($urlWeek);
480: // エラーチェック
481: if ($html === FALSE) return FALSE;
482:
483: // インスタンスを生成する.
484: $pcr = new pahooScraping($html);
485:
486: // CSVリンク取得
487: $nodes = $pcr->queryXPath('//a[contains(@href, ".csv")]');
488:
489: // CSVファイルの URLを取得する.
490: $urls = [];
491: foreach ($nodes as $a) {
492: $href = $a->getAttribute('href');
493: $file = basename($href);
494:
495: foreach ($targetReps as $key=>$rep) {
496: if (preg_match($rep, $href) > 0) {
497: $urls[$key] = resolveUrl($urlWeek, $href);
498: }
499: }
500: }
501:
502: return $urls;
503: }
解説:CSVファイルの構造
$items[年][週][病名][都道府県コード] = 報告数
influenza.php
505: /**
506: * IDWR速報CSVファイルを渡し,報告数を配列に格納する.
507: * @param string $urlCSV CSVファイル名(URL)
508: * @param array $items 報告数を格納する配列
509: * @param int $year 報告年を格納する変数
510: * @param int $week 報告週を格納する変数
511: * @return bool TRUE:取得成功/FALSE:失敗
512: */
513: function getData($urlCSV, &$items, &$year, &$week) {
514: global $PrefList;
515:
516: // ページ取得
517: $contents = @file_get_contents($urlCSV);
518: // エラーチェック
519: if ($contents === FALSE) return FALSE;
520: // エンコード変換
521: $ss = mb_convert_encoding($contents, INTERNAL_ENCODING, IDWR_ENCODING);
522: // 行に分解
523: $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $ss));
524:
525: // 1行ずつ処理する
526: $year = $week = NULL;
527: $disease_row = [];
528: $disease_map = [];
529: foreach ($lines as $line) {
530: // カンマで分割(簡易的なCSV分割)
531: $data = str_getcsv($line, ",", "\"", "\\");
532: $first_cell = trim($data[0] ?? '');
533:
534: // 1.報告年・週がまだ決まっていない場合
535: if ($year === NULL) {
536: if (preg_match('/(\d+)年\s*(\d+)週/iu', $line, $arr) > 0) {
537: $year = (int)$arr[1];
538: $week = (int)$arr[2];
539: }
540:
541: // 2. ヘッダーがまだ決まっていない場合
542: } else if (empty($disease_map)) {
543: if (in_array('エボラ出血熱', $data) || in_array('インフルエンザ', $data)) {
544: $disease_row = $data;
545: continue;
546: }
547: if (in_array('報告', $data)) {
548: $current_disease = "";
549: foreach ($data as $index => $label) {
550: // 病名行から病名を取得
551: if (isset($disease_row[$index]) && trim($disease_row[$index]) !== "") {
552: $current_disease = trim($disease_row[$index]);
553: }
554: // 「報告」列のみマップに登録
555: if (trim($label) === '報告' && $current_disease !== "") {
556: $disease_map[$index] = $current_disease;
557: }
558: }
559: continue;
560: }
561: continue;
562: }
563:
564: // 3. 終了判定:空行、または注釈行でストップ
565: if ($first_cell === "" || $first_cell === "注") {
566: if (! empty($items)) break; // すでにデータがあれば終了
567: continue;
568: }
569:
570: // 4. データ行の処理
571: if (($first_cell !== '総数') && ! str_contains($first_cell, '報告数')) {
572: foreach ($disease_map as $col_index => $disease_name) {
573: $val = isset($data[$col_index]) ? (int)str_replace(',', '', $data[$col_index]) : 0;
574: // $items[年][週][病名][都道府県コード] = 報告数
575: $prefCode = (string)array_search($first_cell, $PrefList);
576: $items[$year][$week][$disease_name][$prefCode] = $val;
577: }
578: }
579: }
580:
581: return TRUE;
582: }
解説:報告データをXMLファイルに保存
influenza.php
724: /**
725: * 報告データをXMLファイルに保存する.
726: * @param string $fname XMLファイル名
727: * @param array $items 報告データ
728: * @return bool TRUE:保存成功/FALSE:失敗
729: */
730: function saveXML($fname, $items) {
731: // $items を年の古い順、週の古い順にソートする
732: ksort($items);
733: foreach ($items as $year => &$weeks) {
734: ksort($weeks);
735: }
736: unset($weeks);
737:
738: // 保存データを作成する.
739: $fp = fopen($fname, "w");
740: if (! $fp) return FALSE;
741:
742: $now = date('Y-m-d H:m:s');
743:
744: fwrite($fp,"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
745: fwrite($fp,"<!-- IDWR速報データ -->\n");
746: fwrite($fp,"<atit:database xmlns:atit=\"urn:IDWRdata\">\n");
747: fwrite($fp,"<update>{$now}</update>\n");
748:
749: foreach ($items as $year=>$item) {
750: fwrite($fp,"<item year=\"{$year}\">\n");
751: foreach ($item as $week=>$dataset) {
752: fwrite($fp,"\t<dataset week=\"{$week}\">\n");
753: foreach ($dataset as $disease=>$prefs) {
754: fwrite($fp,"\t\t<dataset disease=\"{$disease}\">\n");
755: foreach ($prefs as $prefCode=>$data) {
756: fwrite($fp,"\t\t\t<data prefCode=\"{$prefCode}\">{$data}</data>\n");
757: }
758: fwrite($fp,"\t\t</dataset>\n");
759: }
760: fwrite($fp,"\t</dataset>\n");
761: }
762: fwrite($fp,"</item>\n");
763: }
764: fwrite($fp,"</atit:database>\n");
765:
766: fclose($fp);
767: return TRUE;
768: }
解説:報告データをJSONファイルに保存
influenza.php
649: /**
650: * 報告データを JSONファイルに保存する.
651: * @param string $fname JSONファイル名
652: * @param array $items 報告データ
653: * @return bool TRUE:保存成功/FALSE:失敗
654: */
655: function saveJSON($fname, $items) {
656: global $ListDiseases;
657:
658: $dataSet = [];
659: foreach ($items as $year=>$datasetWeek) {
660: foreach ($datasetWeek as $week=>$datasetDisease) {
661: foreach ($datasetDisease as $disease=>$datasetPref) {
662: $diseaseCode = (string)array_search($disease, $ListDiseases);
663: if ($diseaseCode === FALSE) continue;
664: $x = 0;
665: foreach ($datasetPref as $pref=>$value) {
666: $x += $value;
667: }
668: $dataSet[$year][$week][$diseaseCode] = $x;
669: }
670: }
671: }
672: $json = json_encode($dataSet);
673:
674: // ファイルに保存する.
675: return @file_put_contents($fname, $json) === FALSE ? FALSE : TRUE;
676: }
解説:報告データの読み込み
influenza.php
794: /**
795: * 報告データを配列に読み込む.
796: * 必要に応じて,最新のデータを公式から取得し,XMLとJSONの2つのファイルに保存
797: * @param array $items 報告データを格納する配列
798: * @param string $errmsg エラー・メッセージを格納する変数
799: * @return string 読み込んだファイルのタイムスタンプ/FALSE:読み込み失敗
800: */
801: function readData(&$items, &$errmsg) {
802: // 報告データ(JSON形式)を読み込む
803: $timestamp = loadJSON(FILENAME_JSON, $items);
804: if ($timestamp == FALSE) {
805: $errmsg = FILENAME_JSON . ' を読むことができません';
806: return FALSE;
807: }
808:
809: // 配列にある最新の年,週
810: list($lastYear, $lastWeek) = latestYearWeek($items);
811:
812: // 次の週を求める
813: $lastWeek++;
814: $date = new DateTime(sprintf('%04d-12-31', $lastYear));
815: // 次の年
816: if ($lastWeek > (int)$date->format('W')) {
817: $lastYear++;
818: $lastWeek = 1;
819: }
820:
821: // 今日の年,週を求める
822: $today = new DateTime('today');
823: $year = (int)$today->format('Y');
824: $week = (int)$today->format('W');
825:
826: // 今日より2週間前の週を求める(最新の公開週)
827: $week -= 2;
828: if ($week <= 0) {
829: $year--;
830: $date = new DateTime(sprintf('%04d-12-31', $year));
831: $week = (int)$date->format('W') - $week;
832: }
833:
834: if (($lastYear < $year) || (($lastYear == $year) && $lastWeek <= $week)) {
835: // XMLデータを読み込む
836: $items = [];
837: $res = loadXML(FILENAME_XML, $items);
838: if ($res === FALSE) {
839: $errmsg = FILENAME_XML . ' を読み込めない';
840: return FALSE;
841: }
842: // 最大10回分の未登録データを読み込み
843: for ($i = 0; $i < 10; $i++) {
844: $res = readNewData($lastYear, $lastWeek, $items);
845: if ($res === FALSE) break;
846: // 次の週を求める
847: $lastWeek++;
848: $date = new DateTime(sprintf('%04d-12-31', $lastYear));
849: // 次の年
850: if ($lastWeek > (int)$date->format('W')) {
851: $lastYear++;
852: $lastWeek = 1;
853: }
854: }
855: // XMLファイルに保存する
856: $res = saveXML(FILENAME_XML, $items);
857: if ($res === FALSE) {
858: $errmsg = FILENAME_XML . ' に書き込めない';
859: return FALSE;
860: }
861: // JSONファイルに保存する
862: $res = saveJSON(FILENAME_JSON, $items);
863: if ($res === FALSE) {
864: $errmsg = FILENAME_JSON . ' に書き込めない';
865: return FALSE;
866: }
867: // ファイルのタイムスタンプ
868: $timestamp = date('c', filemtime(FILENAME_JSON));
869: }
870: return $timestamp;
871: }
influenza.php
770: /**
771: * 公式サイトから指定年・週の報告データを読み込んで,配列に追加する.
772: * @param int $year 報告年
773: * @param int $week 報告週
774: * @param array $items 報告データを格納する配列
775: * @return bool TRUE:読込成功/FALSE:失敗
776: */
777: function readNewData($year, $week, &$items) {
778: $urlListYear = getURLlistYear();
779: if ($urlListYear === FALSE) return FALSE;
780: if (! isset($urlListYear[$year])) return FALSE;
781: $urlListWeek = getURLlistWeek($urlListYear[$year]);
782: if ($urlListWeek === FALSE) return FALSE;
783: if (! isset($urlListWeek[$week])) return FALSE;
784: $urlList = getURLreports($urlListWeek[$week]);
785: if ($urlList === FALSE) return FALSE;
786: $res = getData($urlList[0], $items, $year, $week);
787: if ($res === FALSE) return FALSE;
788: $res = getData($urlList[1], $items, $year, $week);
789: if ($res === FALSE) return FALSE;
790:
791: return TRUE;
792: }
解説:グラフを描く
influenza.php
880: /**
881: * 感染症グラフ描画
882: * @param array $item 報告データ
883: * @param int $startYear 描画開始年
884: * @param int $goalYear 描画終了年
885: * @param int $width グラフの高さ(ピクセル)
886: * @param int $height グラフの幅(ピクセル)
887: * @param string $bgcolor グラフの背景色
888: * @param string $diseaseCode 感染症コード
889: * @return object graphオブジェクト
890: */
891: function drawGraph($items, $startYear, $goalYear, $width, $height, $bgcolor, $diseaseCode) {
892: global $PlotColor, $PlotWeight, $ListDiseases;
893:
894: $disease = $ListDiseases[$diseaseCode];
895: $plotdata = array();
896:
897: // 月(横軸)を作成
898: for ($i = 1; $i <= MAX_WEEK; $i++) {
899: $data_x[$i] = (int)($i / 4.5 + 1);
900: }
901:
902: // グラフ描画処理開始
903: $graph = new Graph($width, $height, 'auto');
904: $graph->SetScale('textlin');
905: $graph->SetMargin(80, 100, 20, 40);
906:
907: // 背景色の指定
908: $graph->SetBackgroundGradient($bgcolor, $bgcolor, GRAD_HOR, BGRAD_MARGIN);
909:
910: // タイトル
911: $graph->img->SetAntiAliasing(FALSE); // アンチエイリアスOFF(太い折れ線を引くため)
912: $graph->title->SetColor('black');
913: $graph->title->SetFont(FF_PGOTHIC, FS_NORMAL, 14);
914: $graph->title->Set($disease . ':報告数(速報値)');
915: // フッタ
916: $graph->footer->right->SetFont(FF_PGOTHIC, FS_NORMAL, 10);
917: $graph->footer->right->Set('produced by NIID and JpGraph');
918: // X軸
919: $graph->xaxis->SetColor('black');
920: $graph->xaxis->title->SetFont(FF_PGOTHIC);
921: $graph->xaxis->SetTitle('月','center');
922: $graph->xaxis->SetTickLabels($data_x);
923: $graph->xaxis->SetTextTickInterval(5);
924: $graph->xaxis->SetTextLabelInterval(1);
925: // Y軸
926: $graph->yaxis->SetColor('black');
927: $graph->yaxis->title->SetFont(FF_PGOTHIC);
928: $graph->yaxis->title->Set('報告数');
929: $graph->yaxis->SetTitleMargin(60);
930: $graph->yaxis->SetLabelFormatCallback('numberFormatCallback');
931:
932: // 折れ線グラフを描く
933: foreach ($items as $year=>$data) {
934: // 描画版以外ならcontinue
935: if (($year < $startYear) || ($year > $goalYear)) continue;
936: // データがない年はスキップする.
937: if (! isset($items[$year][1][$diseaseCode]) &&
938: ! isset($items[$year][MAX_WEEK - 1][$diseaseCode])) continue;
939:
940: // プロットデータを初期化する.
941: for ($i = 0; $i < MAX_WEEK; $i++) $data_y[$i] = NULL;
942: // プロットデータを代入する.
943: foreach ($data as $week=>$val) {
944: if (isset($items[$year][$week][$diseaseCode])) {
945: $data_y[$week - 1] = $items[$year][$week][$diseaseCode];
946: } else {
947: $data_y[$week - 1] = NULL;
948: }
949: }
950:
951: // 折れ線グラフ作成
952: $colorIndex = ($goalYear - $startYear) - ($year - $startYear) % count($PlotColor);
953: $plotdata[$year] = new LinePlot($data_y);
954: $plotdata[$year]->SetLegend($year . '年'); // 凡例
955: // グラフを結合する
956: $graph->Add($plotdata[$year]);
957: $plotdata[$year]->SetColor($PlotColor[$colorIndex]); // 色
958: $plotdata[$year]->SetWeight($PlotWeight[$colorIndex]); // 太さ
959: }
960:
961: // 凡例
962: $graph->legend->SetColor('black');
963: $graph->legend->SetFont(FF_PGOTHIC); // フォント
964: $graph->legend->SetColumns(1); // 表示列数
965: $graph->legend->SetPos(0.01, 0.1, 'right', 'top'); // 位置
966:
967: return $graph;
968: }
まず、1年分のデータを配列 $data_y に代入し、折れ線グラフを1本描く。
この作業を年数分繰り返してゆく。
解説:画面出力
influenza.php
1240: // グラフを表示する.
1241: $ImageBase64 = '';
1242: if ($error == '') {
1243: // グラフを生成する.
1244: $graph = drawGraph($items, $startYear, $goalYear, $width, $height, $bgcolor, $diseaseCode);
1245: $im = $graph->Stroke(_IMG_HANDLER);
1246:
1247: // 画像データのみを出力する.
1248: if ($mode == 1) {
1249: header('Content-type: image/webp');
1250: imagewebp($im, NULL, 75); // WebP出力
1251: // PHP8.5:非推奨関数
1252: if (PHP_VERSION_ID < 80500) {
1253: imagedestroy($im); // 後処理
1254: }
1255: exit(1);
1256: // 画像データをBASE64エンコーディングする.
1257: } else {
1258: ob_start();
1259: imagewebp($im, NULL, 75); // WebP出力
1260: // PHP8.5:非推奨関数
1261: if (PHP_VERSION_ID < 80500) {
1262: imagedestroy($im); // 後処理
1263: }
1264: $ImageBase64 = base64_encode(ob_get_clean());
1265: }
1266: }
1267:
1268: // 表示HTMLを作成する.
1269: $HtmlBody = makeCommonBody($mode, $startYear, $goalYear, $height, $width, $diseaseCode, $ImageBase64, $items, $error, $timestamp);
1270:
1271: // 画面に表示する.
1272: echo $HtmlHeader;
1273: echo $HtmlBody;
1274: echo $HtmlFooter;
1275:
1276: /*
参考サイト
- JpGraph公式サイト
- アシアル株式会社
- PHPとJpGraphで人口ピラミッドを表示する:ぱふぅ家のホームページ
- IDWR速報:国立感染症研究所

感染症報告数は日々変化しているので、IDWR速報(感染症発生動向調査週報)を取り込むようにした。
(2026年2月28日)IDWR速報のページ改編に応じて大幅改訂