サンプル・プログラムの実行例

グラフ描画する感染症は、インフルエンザを含めて19種類設定できる。コマンドライン・オプション d に0~18の番号を指定する。番号と感染症名の対応は、配列変数 $ListDiseases に定義している。
たとえば "influenza.php?d=8" と指定すると、百日咳のグラフを表示する。

コマンドライン・オプション img を指定すると、imgタグの中に画像データを埋め込むための文字列を生成する。詳しくは、「PHPでTrueTypeフォントを利用する(その2)」-「解説:画像をHTMLコンテンツに埋め込む」を参照のこと。

コマンドライン・オプション file に続いてファイル名を指定すると、国立感染症研究所が発表している患者数データではなく、指定したデータファイルからデータを読み込んで表示する。データファイルの書式は後述する。
サンプル・プログラム
influenza.php | サンプル・プログラム本体 |
influenza.txt | 読み込み用サンプル・データ |
バージョン | 更新日 | 内容 |
---|---|---|
2.4.1 | 2025/01/26 | PHP8.4対応 |
2.4.0 | 2023/05/05 | グラフにフッタを追加 |
2.3 | 2022/04/03 | PHP8対応,リファラ・チェック改良 |
2.21 | 2019/10/06 | http→https変更 |
2.2 | 2018/06/05 | JpGraph 4.2.1対応 |
解説:準備
influenza.php
39: // JpGraph(各自の環境に合わせて)
40: require_once('jpgraph/jpgraph.php');
41: require_once('jpgraph/jpgraph_line.php');
42:
解説:各種パラメータ
influenza.php
43: // グラフの大きさ
44: define('WIDTH', 600); // 幅(ピクセル)
45: define('HEIGHT', 400); // 高さ(ピクセル)
46:
47: // エラー表示用フォント・ファイル名;各自の環境に合わせて
48: define('FONT', '../../../../common/font/ipagp.ttf');
49:
50: // データファイル名
51: define('FNAME_DATA', 'influenza.txt');
52:
53: // IDWR速報データ
54: define('URI_IDWR', 'https://www.niid.go.jp/niid/ja/data.html');
55:
56: // IDWR速報データのエンコード
57: define('IDWR_ENCODING', 'SJIS');
58:
59: // 感染症リスト
60: $ListDiseases = array(
61: 0 => 'インフルエンザ',
62: 1 => '咽頭結膜熱',
63: 2 => 'A群溶血性レンサ球菌咽頭炎',
64: 3 => '感染性胃腸炎',
65: 4 => '水痘',
66: 5 => '手足口病',
67: 6 => '伝染性紅斑',
68: 7 => '突発性発しん',
69: 8 => '百日咳',
70: 9 => 'ヘルパンギーナ',
71: 10 => '流行性耳下腺炎',
72: 11 => '急性出血性結膜炎',
73: 12 => '流行性角結膜炎',
74: 13 => '細菌性髄膜炎',
75: 14 => '無菌性髄膜炎',
76: 15 => 'マイコプラズマ肺炎',
77: 16 => 'クラミジア肺炎',
78: 17 => '感染性胃腸炎(ロタウイルス)',
79: 18 => 'RSウイルス(報告数)'
80: );
81:
82: // データファイル名
83: $FnameData = 'influenza.txt';
84:
85: // 折れ線グラフの色(年ごとに少しずつ色を変える)
86: $PlotColor = array(
87: 0 => '#E5004F',
88: 1 => '#FFA100',
89: 2 => '#009944',
90: 3 => '#00A0E9',
91: 4 => '#920783',
92: 5 => '#F39800',
93: 6 => '#8FC31F',
94: 7 => '#0068B7',
95: 8 => '#E4007F',
96: 9 => '#009E96',
97: 10 => '#E60012',
98: 11 => '#FFA100',
99: 12 => '#8FC31F'
100: );
101:
102: // 折れ線グラフの太さ(直近のグラフだけ太線にする)
103: $PlotWeight = array(
104: 0 => 1,
105: 1 => 1,
106: 2 => 1,
107: 3 => 1,
108: 4 => 1,
109: 5 => 1,
110: 6 => 1,
111: 7 => 1,
112: 8 => 1,
113: 9 => 1,
114: 10 => 3,
115: 11 => 3,
116: 12 => 3
117: );
118:
119: // サブルーチン ==============================================================
色はRGB各8bitで配列変数 $PlotColor に、太さは配列変数 $PlotWeight にあらかじめ代入しておく。
解説:患者データの解析
influenza.php
256: /**
257: * IDWR速報データのページから目的のCSVファイル名を取り出す
258: * @return string CSVファイル名
259: */
260: function getIDWR_trend() {
261: static $pat = '/href\=\"(.+\-trend\.csv)/ui';
262:
263: $context = array(
264: 'ssl' => array(
265: 'verify_peer' => FALSE,
266: 'verify_peer_name' => FALSE,
267: )
268: );
269:
270: $infp = fopen(URI_IDWR, 'r', FALSE, stream_context_create($context));
271: if ($infp == FALSE) return FALSE;
272:
273: $fname = '';
274: while (! feof($infp)) {
275: $str = fgets($infp);
276: if (preg_match($pat, $str, $arr) > 0) {
277: $fname = $arr[1];
278: break;
279: }
280: }
281: fclose($infp);
282:
283: $arr = parse_url(URI_IDWR);
284: $fname = 'https://' . $arr['host'] . $fname;
285:
286: return $fname;
287: }
influenza.php
289: /**
290: * IDWR速報データを解析
291: * @param string $fname 解析ファイル名
292: * @param string $disease 感染症名
293: * @param array $item データを格納する配列
294: * @return int データの開始年/FALSE
295: */
296: function parseIDWR($fname, $disease, &$items) {
297: $pat = array(
298: '/' . $disease . ',/ui',
299: '/,([0-9]+)週,/ui',
300: '/([0-9]+)年,/ui',
301: '/^,$/ui'
302: );
303:
304: // ini_set('auto_detect_line_endings', TRUE);
305: $infp = fopen($fname, 'r');
306: if ($infp == FALSE) return FALSE;
307:
308: $start = 0;
309: $mode = 0;
310: while (! feof($infp)) {
311: $str = mb_convert_encoding(trim(fgets($infp)), INTERNAL_ENCODING, IDWR_ENCODING);
312: // データ領域の始まり
313: if (($mode == 0) && (preg_match($pat[0], $str) > 0)) {
314: $mode = 1;
315: // データ本体
316: } else if ($mode == 1) {
317: if (preg_match($pat[1], $str) > 0) {
318: } else if (preg_match($pat[2], $str, $arr) > 0) {
319: // 西暦年の行
320: $year = $arr[1];
321: if ($year < 2000) $year += 2000;
322: if ($start == 0) $start = $year;
323: // データの行
324: $arr = preg_split('/,/ui', $str);
325: foreach ($arr as $week=>$data) {
326: if (($week > 0) && ($data != '')) {
327: $items[$year][$week] = $data;
328: }
329: }
330: } else {
331: break;
332: }
333: }
334: }
335: fclose($infp);
336:
337: return $start;
338: }

まず、ユーザー関数 getIDWR_trend により、読み込むべきCSVファイル名を取得する。CSVファイル名は毎週変化するためである。

次に、ユーザー関数 parseIDWR により、CSVファイルを解析し、データの開始年とデータ本体を取得する。
ターゲットのCSVファイルはシフトJISでエンコードされているが、改行コードが CR であるため、 fgets が正常に機能しない。そこで、事前に ini_set に 'auto_detect_line_endings' を指定することで、CR を改行として認識できるようにしておく。
解説:グラフを描く
influenza.php
340: /**
341: * 感染症グラフ描画
342: * @param array $item データ
343: * @param int $start 描画開始年
344: * @param string $disease 感染症名
345: * @return object graphオブジェクト
346: */
347: function drawGraph($items, $start, $disease) {
348: global $PlotColor, $PlotWeight;
349:
350: $plotdata = array();
351:
352: // 月(横軸)を作成
353: for ($i = 1; $i <= 53; $i++) {
354: $data_x[$i] = (int)($i / 4.5 + 1);
355: }
356:
357: // グラフ描画処理開始
358: $graph = new Graph(WIDTH, HEIGHT, 'auto');
359: $graph->SetScale('textlin');
360: $graph->SetBackgroundGradient('#FFFFCC','#FFFFCC', GRAD_HOR, BGRAD_MARGIN);
361:
362: // タイトル、凡例などの設定
363: $graph->img->SetImgFormat('png');
364: $graph->img->SetMargin(60, 100, 20, 40);
365: $graph->title->SetFont(FF_PGOTHIC, FS_NORMAL, 14);
366: $graph->title->Set($disease . ':定点あたり報告数(速報値)');
367: $graph->xaxis->title->SetFont(FF_PGOTHIC);
368: $graph->xaxis->SetTitle('月','center');
369: $graph->xaxis->SetTickLabels($data_x);
370: $graph->xaxis->SetTextTickInterval(5);
371: $graph->xaxis->SetTextLabelInterval(1);
372: // フッタを設定する.
373: $graph->footer->right->SetFont(FF_PGOTHIC, FS_NORMAL, 10);
374: $graph->footer->right->Set('produced by JpGraph');
375:
376: $graph->yaxis->title->SetFont(FF_PGOTHIC);
377: $graph->yaxis->title->Set('報告数');
378: $graph->yaxis->SetTitleMargin(40);
379:
380: // 折れ線グラフ
381: foreach ($items as $year=>$data) {
382: unset($data_y);
383: foreach ($data as $week=>$val) {
384: $data_y[$week - 1] = $items[$year][$week];
385: }
386:
387: // 折れ線グラフ作成
388: $plotdata[$year] = new LinePlot($data_y);
389: $plotdata[$year]->SetLegend($year . '年'); // 凡例
390: // グラフを結合する
391: $graph->Add($plotdata[$year]);
392: $plotdata[$year]->SetColor($PlotColor[$year - $start]); // 色
393: $plotdata[$year]->SetWeight($PlotWeight[$year - $start]); // 太さ
394: }
395:
396: // 凡例
397: $graph->legend->SetFont(FF_PGOTHIC); // フォント
398: $graph->legend->SetColumns(1); // 表示列数
399: $graph->legend->SetPos(0.01, 0.1, 'right', 'top'); // 位置
400:
401: return $graph;
402: }

まず、1年分のデータを配列 $data_y に代入し、折れ線グラフを1本描く。
この作業を年数分繰り返してゆく。
解説:画面出力
influenza.php
433: // グラフを表示
434: $graph = drawGraph($items, $start, $disease);
435: if ($mode == 1) {
436: header('Content-type: image/png');
437: $graph->Stroke();
438: } else {
439: $im = $graph->Stroke(_IMG_HANDLER);
440: ob_start();
441: ImagePNG($im);
442: imagedestroy($im);
443: $res = base64_encode(ob_get_clean());
444: echo $res;
445: }
446:

コマンドライン・オプション img が指定された場合は、出力データをimgタグの中に画像データを埋め込むための文字列を生成する。詳しくは、「PHPでTrueTypeフォントを利用する(その2)」-「解説:画像をHTMLコンテンツに埋め込む」を参照のこと。
解説:データを配列に読み込む
influenza.php
226: /**
227: * データを配列に読み込む
228: * @param string $fname 読み込むデータファイル名
229: * @param string $disease 感染症名を格納
230: * @param array $rec データを格納する配列
231: * @return int データの開始年/FALSE
232: */
233: function readData($fname, &$disease, &$rec) {
234: $infp = @fopen($fname, 'r');
235: if ($infp == FALSE) return FALSE;
236:
237: $start = 0;
238: $disease = trim(fgets($infp));
239: while (($arr = fgetcsv($infp, 1000, ',', '"', '\\')) != FALSE) {
240: foreach ($arr as $key=>$val) {
241: if ($key == 0) {
242: $year = $val;
243: if ($start == 0) $start = $year;
244: $week = 1;
245: } else {
246: $rec[$year][$week] = $val;
247: $week++;
248: }
249: }
250: }
251: fclose($infp);
252:
253: return $start;
254: }

あらかじめ、次のような書式のデータファイルを用意しておく。
1行目は感染症名。2行目以降が1年分のデータで、カンマ区切り。1カラム目は西暦年である。
インフルエンザファウンロード・ファイルには、サンプルとしてデータファイル "influeza.txt" を同梱してある。
1999,8.86,20.3,33.56,33.86,27.16,22.67,20.95,20.96,19.2,14.95,11.71,5.47,,1.5,
1.22,0.9,0.59,0.26,0.27,0.17,0.13,0.1,0.08,0.06,0.06,0.05,0.05,0.05,0.04,0.04,
0.03,0.02,0.01,0.02,0.03,0.03,0.02,0.02,0.03,0.04,0.05,0.07,0.08,0.09,0.1,0.
17,0.21,0.35,0.74,1.53,3.22,3.21,
2000,6.15,10.24,23.32,34.26,36.16,24.7,13.8,8.36,4.62,2.41,1.2,0.51,0.26,0.13,
0.1,0.07,0.05,0.03,0.04,0.03,0.03,0.03,0.03,0.02,0.02,0.02,0.02,0.01,0.01,
0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.02,0.02,0.02,0.03,0.03,0.04,
0.05,0.06,0.08,0.15,0.23,0.27,0.32,0.36,

データを読み込むユーザー関数 readData:blue の流れは、「PHPでCSVファイルを読み込む」で解説した通りである。
参考サイト
- JpGraph公式サイト
- アシアル株式会社
- PHPとJpGraphで人口ピラミッドを表示する:ぱふぅ家のホームページ
- IDWR速報データ:国立感染症研究所
インフルエンザ患者数は日々変化しているので、国立感染症研究所が発表している患者数データを取り込むようにした。
(2025年1月26日)PHP8.4対応