PHPでNHK政治意識月例調査をグラフ表示

(1/1)
NHK選挙WEB(2020年3月までは「NHK政治意識月例調査」)に、内閣支持率や政党支持率が公開されている。PHPを用いて、これらの数値データを取り出し、グラフに表示するプログラムを作る。応答速度を向上するため、一度読み込んだデータはXMLファイルとしてローカルに保存しておく。

(2024年5月12日)HTMLヘッダ見直し
(2024年3月24日)2023年(令和5年)8月以降のデータに対応
(2023年5月17日)政党支持率に「日本維新の会」「国民民主党」「れいわ新選組」を追加

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

NHK政治意識月例調査をグラフ表示

目次

サンプル・プログラム

圧縮ファイルの内容
viewPolls.phpサンプル・プログラム本体。
polls.xml内閣支持率・政党支持率のデータ・ファイル。
圧縮ファイルの内容
viewPolls.phpサンプル・プログラム本体。
viewPolls.php 更新履歴
バージョン 更新日 内容
3.4.0 2024/05/12 HTMLヘッダ見直し
3.3.0 2024/03/24 2023年8月以降のデータに対応
3.2.0 2023/05/17 plotParty()に3政党を追加
3.1 2021/05/08 POLLS_FILE へのセーブ/ロード
3.0 2021/05/08 2020年4月以降に対応,PHP8対応

解説:準備

  21: //jqPlotのあるフォルダ
  22: define('JQPLOT', '../../../../common/jqplot/');
  23: 
  24: //描画期間(年)
  25: define('PERIOD', 7);
  26: 
  27: //最古データ(年)
  28: define('OLDYEAR', 1998);
  29: 
  30: //グラフを描くDOMオブジェジェクト名
  31: define('GRAPH_ID', 'jqPlot_polls');
  32: 
  33: //表示幅(ピクセル)
  34: define('WIDTH', 600);
  35: 
  36: //表示高さ(ピクセル)
  37: define('HEIGHT', 500);
  38: 
  39: //保存用データファイル名
  40: define('POLLS_FILE', './polls.xml');

今回は折れ線グラフを描くのに、jQueryプラグイン「jqPlot」を利用する。
公式サイト「jqPlot Charts and Graphs for jQuery」から圧縮ファイルをダウンロードし、適当な場所に解凍しておく。その場所を定数 JQPLOT に定義する。

また、描画期間(当年から過去何年分遡るか)を定数 PERIOD に定義しておく。
なお、スクレイピングしたデータはXML形式ファイルとして定数 POLLS_FILE で示すファイルにセーブし、次に描画するときは、このファイルからロードできるようにした。

解説:スクレイピング

 355: /**
 356:  * 内閣支持率を解析する
 357:  * @param   int $infp 解析中のURL
 358:  * @param   array $items 解析結果を格納する配列
 359:  * @return  bool TRUE/FALSE
 360: */
 361: function analyseCabinet($infp, &$items) {
 362:     $pat1 = "/<h[0-9]+[^>]*>内閣支持率<\/h[0-9]+>/u";
 363:     $pat2 = "/<tr[^>]*>/u";
 364:     $pat3 = "/<t[hd][^>]*>(<[^>]+>)?([0-9\-\.]+)/u";
 365:     $pat31 = "/<td[^>]*>(.+)/u";
 366:     $pat4 = "/<t[hd][^>]*>支持(<[^>]+>)?する/u";
 367:     $pat5 = "/<t[hd][^>]*>(<[^>]+>)?支持(<[^>]+>)?しない/u";
 368:     $pat6 = "/<t[hd][^>]*>内閣/u";
 369:     $pat61 = "/<\/table>/u";
 370:     $pat7 = "/<td colspan\=\"([0-9]+)\"[^>]*>([^<]+)/u";
 371:     $pat8 = "/<td[^>]*>([^<]+)/u";
 372:     $pat9 = "/<h[0-9]+[^>]*>.*政党支持率<\/h[0-9]+>/u";
 373:     $flag = 0;
 374:     while (! feof($infp)) {
 375:         $instr = trim(fgets($infp));
 376:         if (($flag == 0&& (preg_match($pat1, $instr> 0)) {
 377:             $flag = 1;
 378:         } else if (($flag == 1&& (preg_match($pat2, $instr> 0)) {
 379:             $flag = 2;
 380:             $i = 0;
 381:         //月
 382:         } else if ($flag == 2) {
 383:             if (preg_match($pat4, $instr> 0) {
 384:                 $flag = 3;
 385:                 $i = 0;
 386:             } else if (preg_match($pat3, $instr, $arr> 0) {
 387:                 $items[$i]['month']   = $arr[2];
 388:                 $items[$i]['yes']     = '-';
 389:                 $items[$i]['no']      = '-';
 390:                 $items[$i]['cabinet'] = '-';
 391:                 $i++;
 392:             }
 393:         //支持する
 394:         } else if ($flag == 3) {
 395:             if (preg_match($pat5, $instr> 0) {
 396:                 $flag = 4;
 397:                 $i = 0;
 398:             } else if (preg_match($pat31, $instr, $arr> 0) {
 399:                 $items[$i]['yes'] = strip_tags($arr[1]);
 400:                 $i++;
 401:             }
 402:         //支持しない
 403:         } else if ($flag == 4) {
 404:             if (preg_match($pat31, $instr, $arr> 0) {
 405:                 $items[$i]['no'] = strip_tags($arr[1]);
 406:                 $i++;
 407:             } else if (preg_match($pat6, $instr> 0) {
 408:                 $flag = 5;
 409:                 $i = 0;
 410:             } else if (preg_match($pat61, $instr> 0) {
 411:                 $flag = 5;
 412:                 $i = 0;
 413:             }
 414:         //内閣
 415:         } else if ($flag == 5) {
 416:             if (preg_match($pat7, $instr, $arr> 0) {
 417:                 $n = $i + $arr[1];
 418:                 while ($i < $n) {
 419:                     $items[$i]['cabinet'] = $arr[2];
 420:                     $i++;
 421:                 }
 422:             } else if (preg_match($pat8, $instr, $arr> 0) {
 423:                 $items[$i]['cabinet'] = $arr[1];
 424:                 $i++;
 425:             } else if (preg_match($pat9, $instr> 0) {
 426:                 $flag = 6;
 427:             }
 428:         } else if ($flag == 6) {
 429:             break;
 430:         }
 431:     }
 432: 
 433:     return TRUE;
 434: }

 539: /**
 540:  * 政党支持率を解析する
 541:  * @param   int   $infp  解析中のURL
 542:  * @param   array $items 解析結果を格納する配列
 543:  * @return  bool TRUE/FALSE
 544: */
 545: function analyseParty($infp, &$items) {
 546:     $pat1 = "/<tr[^>]*>/u";
 547:     $pat2 = "/<t[hd][^>]*>(<[^>]+>)?([0-9\-\.]+)/u";
 548:     $pat3 = "/<\/tr>/u";
 549:     $pat4 = "/<t[hd][^>]*>([^<]+)/u";
 550:     $pat5 = "/<\/table>/u";
 551:     $flag = 0;
 552:     while (! feof($infp)) {
 553:         $instr = trim(fgets($infp));
 554:         if (($flag == 0&& (preg_match($pat1, $instr> 0)) {
 555:             $flag = 1;
 556:             $i = 0;
 557:         //月
 558:         } else if ($flag == 1) {
 559:             if (preg_match($pat2, $instr, $arr> 0) {
 560:                 $items[$i]['month'] = $arr[2];
 561:                 $i++;
 562:             } else if (preg_match($pat3, $instr> 0) {
 563:                 $flag = 2;
 564:                 $i = 0;
 565:             }
 566:         //政党支持率
 567:         } else if ($flag == 2) {
 568:             if (preg_match($pat2, $instr, $arr> 0) {
 569:                 $items[$i][$party] = $arr[2];
 570:                 $i++;
 571:             } else if (preg_match($pat4, $instr, $arr> 0) {
 572:                 $party = $arr[1];
 573:                 $i = 0;
 574:             } else if (preg_match($pat5, $instr> 0) {
 575:                 $flag = 3;
 576:             }
 577:         } else if ($flag == 3) {
 578:             break;
 579:         }
 580:     }
 581:     return TRUE;
 582: }

NHK政治意識月例調査をスクレイピングする処理は、ユーザー関数 analyseCabinet および analyseParty で行う。1行ずつ読み込み、正規表現を使ってデータを分離していく。
スクレイピングしたデータは、配列に格納する。

 436: /**
 437:  * 内閣支持率・政党支持率を解析する【2020年1月以降】
 438:  * @param   int   $infp  解析中のURL
 439:  * @param   array $items 内閣支持率の解析結果を格納する配列
 440:  * @param   array $items 政党支持率の解析結果を格納する配列
 441:  * @return  bool TRUE/FALSE
 442: */
 443: function analyse2020($infp, &$items1, &$items2) {
 444:     //プロットする政党名
 445:     static $table = array(
 446:     '', '自民党', '立憲民主党', '日本維新の会', '公明党', '共産党', '国民民主党', 'れいわ新選組', '社民党', 'みんなでつくる党', '参政党', '特に支持している政党はない', '支持政党なし');
 447: 
 448:     $pat01 = "/<i\s+class\=\"far\s+fa\-calendar\-alt\"><\/i>[0-9]+年([0-9]+)月<span>/ui";
 449:     $pat11 = "/<h3>内閣支持([0-9]+)[%\%]、不支持([0-9]+)[%\%]/ui";
 450:     $pat12 = "/(\p{Han}+内閣)を「支持する/ui";
 451:     $pat21 = "/<h2><span>政党支持率<\/span>/ui";
 452:     $pat31 = "/<td>([^<]+)<\/td>/ui";
 453:     $pat32 = "/<td\s+class\=\"right\">([0-9\.]+)<\/td>/ui";
 454:     //2023年8月以降
 455:     $pat22 = "/各党の支持率は/ui";
 456:     $pat33 = '/「([^」]+)」が([0-9\.]+)\%/ui';
 457: 
 458:     $flag = FALSE;
 459:     while (! feof($infp)) {
 460:         $instr1 = trim(fgets($infp));
 461:         $instr2 = strip_tags($instr1);
 462:         //月
 463:         if (preg_match($pat01, $instr1, $arr> 0) {
 464:             $month = (int)$arr[1];
 465:         //内閣支持率・不支持率
 466:         } else if (preg_match($pat11, $instr1, $arr> 0) {
 467:             $items1[$month - 1]['month'] = (int)$month;
 468:             $items1[$month - 1]['yes']   = (int)$arr[1];
 469:             $items1[$month - 1]['no']    = (int)$arr[2];
 470:         //内閣名
 471:         } else if (preg_match($pat12, $instr2, $arr> 0) {
 472:             $items1[$month - 1]['cabinet'] = (string)$arr[1];
 473:         //政党支持率の解析開始
 474:         } else if (preg_match($pat21, $instr1, $arr> 0) {
 475:             $flag = TRUE;
 476:             $items2[$month - 1]['month'] = (int)$month;
 477:         //政党名
 478:         } else if ($flag && (preg_match($pat31, $instr1, $arr> 0)) {
 479:             $party = (string)$arr[1];
 480:             if ($party == '特に支持している政党はない') {
 481:                 $party = '支持政党なし';
 482:             }
 483:         //支持率
 484:         } else if ($flag && (preg_match($pat32, $instr1, $arr> 0)) {
 485:             $items2[$month - 1][$party] = (string)$arr[1];
 486:         //政党名と支持率 2023年8月以降
 487:         } else if (preg_match($pat22, $instr1, $arr> 0) {
 488:             if (preg_match_all($pat33, $instr1, $arr, PREG_PATTERN_ORDER> 0) {
 489:                 foreach ($arr[1as $key=>$party) {
 490:                     if ($party == '特に支持している政党はない') {
 491:                         $party = '支持政党なし';
 492:                     }
 493:                     if (array_search($party, $table!FALSE) {
 494:                         $items2[$month - 1][$party] = (string)$arr[2][$key];
 495:                     }
 496:                 }
 497:             }
 498:         }
 499:     }
 500:     return TRUE;
 501: }

2020年(令和2年)4月以降は、NHK選挙WEBに移動し、ページのレイアウトが大きく変わったので、別のユーザー関数 analyse2020 を用意した。スクレイピングしたデータは、同様に配列に格納する。

解説:XMLへの格納

 503: /**
 504:  * 内閣支持率をXMLオブジェクトに追加/更新する
 505:  * @param   array  $items 解析結果を格納した配列
 506:  * @param   int    $year  西暦年
 507:  * @param   object $xml   XMLオブジェクト
 508:  * @return  bool TRUE/FALSE
 509: */
 510: function addCabinet($items, $year, &$xml) {
 511:     //西暦年の頭出し
 512:     $flag = FALSE;
 513:     foreach ($xml->research as $elem1) {
 514:         if ($elem1->year == $year) {
 515:             break;
 516:         }
 517:     }
 518:     //月次処理
 519:     foreach ($items as $item) {
 520:         foreach ($elem1->article as $elem2) {
 521:             if ($elem2->month == $item['month']) {
 522:                 $flag = TRUE;
 523:                 break;
 524:             }
 525:         }
 526:         if (! $flag) {
 527:             $elem2 = $elem1->addChild('article');
 528:             $elem2->addChild('month', $item['month']);
 529:         }
 530:         if (! isset($elem2->cabinet)) {
 531:             $elem2->addChild('cabinet');
 532:             $elem2->cabinet->addChild('name', $item['cabinet']);
 533:             $elem2->cabinet->addChild('yes', $item['yes']);
 534:             $elem2->cabinet->addChild('no', $item['no']);
 535:         }
 536:     }
 537: }

 584: /**
 585:  * 政党支持率をXMLオブジェクトに追加/更新する
 586:  * @param   array  $items 解析結果を格納した配列
 587:  * @param   int    $year  西暦年
 588:  * @param   object $xml   XMLオブジェクト
 589:  * @return  bool TRUE/FALSE
 590: */
 591: function addParty($items, $year, &$xml) {
 592:     //西暦年の頭出し
 593:     $flag = FALSE;
 594:     foreach ($xml->research as $elem1) {
 595:         if ($elem1->year == $year) {
 596:             break;
 597:         }
 598:     }
 599: 
 600:     //月次処理
 601:     foreach ($items as $item) {
 602:         foreach ($elem1->article as $elem2) {
 603:             if (isset($item['month']) && ($elem2->month == $item['month'])) {
 604:                 $flag = TRUE;
 605:                 break;
 606:             }
 607:         }
 608:         if (! $flag && isset($item['month'])) {
 609:             $elem2 = $elem1->addChild('article');
 610:             $elem2->addChild('month', $item['month']);
 611:         }
 612: 
 613:         foreach ($item as $key=>$val) {
 614:             if ($key == 'month')    continue;
 615:             $flag = FALSE;
 616:             foreach ($elem2->party as $elem3) {
 617:                 if (isset($elem3->name&& ($elem3->name == $key)) {
 618:                     $flag = TRUE;
 619:                     break;
 620:                 }
 621:             }
 622:             if (! $flag) {
 623:                 $elem4 = $elem2->addChild('party');
 624:                 $elem4->addChild('name', $key);
 625:                 $elem4->addChild('yes', $val);
 626:             }
 627:         }
 628:     }
 629: }

配列に格納したデータを、XMLオブジェクトへ展開していく処理は、ユーザー関数 addCabinet および addParty で行う。

解説:XMLファイルへのセーブとロード

 318: /**
 319:  * データ・ファイルを書き込む
 320:  * @param   string $fname 出力ファイル名
 321:  * @param   object XMLオブジェクト
 322:  * @return  bool TRUE/FALSE
 323: */
 324: function writeDataFile($fname, $xml) {
 325:     $str = $xml->asXML();
 326:     $str = cleanUpXML($str);
 327:     $outfp = fopen($fname, 'w');
 328:     fwrite($outfp, $str);
 329: 
 330:     return fclose($outfp);
 331: }

 291: /**
 292:  * データ・ファイルを読み込む
 293:  * @param   string $fname 入力ファイル名
 294:  * @return  object XMLオブジェクト/FALSE:読み込み失敗
 295: */
 296: function readDataFile($fname) {
 297:     $xmlstr =<<< EOT
 298: <?xml version="1.0" encoding="utf-8" ?>
 299: <polls>
 300: </polls>
 301: 
 302: EOT;
 303:     if (! isphp5over()) return FALSE;       //PHP5以上でないと動作しない
 304: 
 305:     //ファイル無し
 306:     if (! file_exists($fname)) {
 307:         $xml = new SimpleXMLElement($xmlstr);
 308: 
 309:     //ファイル有り
 310:     } else {
 311:         $xml = simplexml_load_file($fname);
 312:         if ($xml == FALSE)      return FALSE;
 313:     }
 314: 
 315:     return $xml;
 316: }

XMLオブジェクトをXMLファイルへセーブするユーザー関数は writeDataFile、ロードするユーザー関数は readDataFile である。

解説:jqPlotの導入

グラフを描くために、jqPlot というJavaScriptのオープンソース・ライブラリを使用する。PHP側でデータを描画スクリプトを生成し、クライアント側(ブラウザ)でグラフを描かせる。

公式サイト http://www.jqplot.com/ から最新のライブラリをダウンロードしたら、本プログラム冒頭の定数 JQPLOT で示すディレクトリへ解凍する。jqPlotjQUery UI を使うので、jQueryも定数 JQPLOT で示すディレクトリへ格納してほしい。jQuery 1.7.1 以降のmin版があればよい。また、jqPlot は canvas文を使うのでHTML5対応ブラウザがあった方がいいが、IE8以前にも対応するよう代替スクリプトを用意している。

  92: <script type="text/javascript" src="{$jqplot}jquery.min.js"></script>
  93: <!--[if lt IE 9]>
  94:     <script language="javascript" type="text/javascript" src="{$jqplot}excanvas.min.js"></script>
  95: <![endif]-->
  96: <script type="text/javascript" src="{$jqplot}jquery.jqplot.min.js"></script>
  97: <script type="text/javascript" src="{$jqplot}plugins/jqplot.dateAxisRenderer.min.js"></script>
  98: <script type="text/javascript" src="{$jqplot}plugins/jqplot.pointLabels.min.js"></script>
  99: <script type="text/javascript" src="{$jqplot}plugins/jqplot.highlighter.min.js"></script>
 100: <link rel="stylesheet" type="text/css" href="{$jqplot}jquery.jqplot.min.css">

jqPlot 関連のライブラリとスタイルシートを読み込むのが、PHPプログラムの上述の部分になる。
ここでは、jqPlotプラグインの「jqplot.dateAxisRenderer.js」(時間軸を扱う)、「jqplot.highlighter.js」(ツールチップなどハイライト表示を行う)、「jqplot.pointLabels.js」(データポイントラベルを表示する)の3つを使っている。
このように、jqPlotは使いたい機能に応じてJavaScriptのプラグインを読み込む形をとる。
jqPlotの使い方は、「jqPlot - jQuery プラグイン」(アルファシス)が詳しい。

解説:jqPlotスクリプト

 747: /**
 748:  * jqPlot用のスクリプト:内閣支持率
 749:  * @param   object $xml XMLオブジェクト
 750:  * @param   int    $start  開始年
 751:  * @param   int    $finish 終了年
 752:  * @return  string スクリプト
 753: */
 754: function plotCabinet($xml, $start, $finish) {
 755:     $graphId = GRAPH_ID;
 756: 
 757:     //系列の生成
 758:     $series1 = '';      //内閣支持率
 759:     $series2 = '';      //内閣不支持率
 760:     $old = '';
 761:     foreach ($xml as $research) {
 762:         //描画範囲内かどうか
 763:         $year = $research->year;
 764:         if (($year < $start|| ($year > $finish))   continue;
 765:         //描画データ作成
 766:         foreach ($research as $article) {
 767:             $month = $article->month;
 768:             if ($month >1 && $month <12) {
 769:                 $name = '';
 770:                 if ($old != (string)$article->cabinet->name) {
 771:                     $name = (string)$article->cabinet->name;
 772:                     $old  = (string)$article->cabinet->name;
 773:                 }
 774:                 $yes = $article->cabinet->yes;
 775:                 $no  = $article->cabinet->no;
 776:                 if ($month > 0 && $yes > 0) {
 777:                     $series1 .sprintf("['%04d-%02d-01',%d,'<span style=\"background-color:#FFFFFF;\">%s</span>'],", $year, $month, $yes, $name);
 778:                     $series2 .sprintf("['%04d-%02d-01',%d,''],", $year, $month, $no);
 779:                 }
 780:             }
 781:         }
 782:     }
 783: 
 784:     //X軸の最小値
 785:     $xmin = sprintf('%04d-01-01', $start);
 786:     //X軸の最大値
 787:     if (date('Y') == $finish) {
 788:         $xmax = date('Y-m-01', strtotime('next month'));
 789:      } else {
 790:         $xmax = sprintf('%04d-12-31', $finish);
 791:     }
 792: 
 793:     //グラフ描画
 794:     $js =<<< EOT
 795: jQuery(function() {
 796:     jQuery.jqplot('{$graphId}',
 797:     [
 798:         [ {$series1} ],
 799:         [ {$series2} ]
 800:     ],
 801:     {
 802:         //系列
 803:         series: [
 804:             { label: '支持率',   color: '#88CC44' },
 805:             { label: '不支持率', color: '#CC4488'  }
 806:         ],
 807:         legend: {
 808:             show: true,
 809:             placement: 'inside',
 810:             location: 'sw',
 811:             renderer: $.jqplot.EnhancedLegendRenderer,
 812:             rendererOptions: { numberRows: 1 }
 813:         },
 814:         seriesDefaults: {
 815:             showLine: true,
 816:             rendererOptions: { smooth: false },
 817:             markerOptions: { size: 0 },
 818:             pointLabels: {
 819:                 show :true,
 820:                 escapeHTML: false,
 821:                 location: 'ne'
 822:             }
 823:         },
 824:         //軸
 825:         axes: {
 826:             xaxis: {
 827:                 renderer:$.jqplot.DateAxisRenderer,
 828:                 min: '{$xmin}',
 829:                 max: '{$xmax}',
 830:                 tickOptions: { formatString: '%Y/%#m' },
 831:                 label: '年月',
 832:             },
 833:             yaxis: {
 834:                 label: '支持率(%)'
 835:             }
 836:         },
 837:         //ハイライター
 838:         highlighter: {
 839:             show: true,
 840:             showMarker: true,
 841:             tooltipLocation: 'sw',
 842:             fadeTooltip: false,
 843:             bringSeriesToFront: true,
 844:             tooltipAxes: 'xy',
 845:             formatString: '%s<br>(不)支持率%s%'
 846:         }
 847:     }
 848:     );
 849: });
 850: 
 851: EOT;
 852: 
 853:     return $js;
 854: }

内閣支持率は、ユーザー関数 plotCabinet により、XMLオブジェクトに格納したデータから jqPlot を使って折れ線グラフに描く。

内閣支持率と不支持率の2本の折れ線グラフを描くのだが、各々、プロットする1つの点を "[x, y]" で表し、これをカンマで区切った文字列として生成する。ここでは "[日付, パーセント]" の形式で、支持率を変数 $seriese1 へ、不支持率を 変数 $seriese2 へ格納していく。

jqPlotスクリプトは jQuery(function() 以降の部分になる。
まず、グラフを描画する領域のDOMオブジェクト名を設定する。本プログラムでは、HTML側のdivタグで表示をしており、グラフの縦横サイズはdivタグのスタイルとして指定する。
上述のプロットデータ(文字列)はオブジェクト series に格納する。
オブジェクト legend凡例の表示方法を指定する。表示場所(placement)はグラフの内側(inside)で、表示位置を南西(sw;下右)に、表示は1列(numberRows: 1)にする。表示を1列にするのに rendererOptionsを使うので、あわせて拡張機能 renderer: $.jqplot.EnhancedLegendRenderer を指定する。
オブジェクト seriesDefaultsグラフの表示方法を指定する。折れ線(showLine)を表示し(true)、折れ線のスムーズ処理(smooth)は行わず(false)、点(markerOptions)をプロットしない(size: 0)。
オブジェクト axesX軸のラベルや目盛りの表示方法を指定する。X軸の最小値(min)は変数 $xmin を指定し、最大値(max)は $xmax を指定する。目盛りの書式(formatString)は年/月(%Y/%#m)にして、ラベル(label)は年月にする。ここで、目盛りの書式を使うために、拡張機能 renderer: $.jqplot.DateAxisRenderer を指定する。Y軸については、ラベルのみを表示する。
オブジェクト highlighter は、折れ線の上にマウスを乗せるデータを表示するハイライト表示する方法を指定する。ハイライターを表示し(show: true)、マーカーも表示し(showMarker: true)、表示位置(tooltipLocation)は南西(sw;下右)に、フェード効果(fadeTooltip)は無効に(false)、ハイライト表示した系列を他の系列よりも前面に移動し(bringSeriesToFront: true)、ツールチップにXとYの値を表示し(tooltipAxes: 'xy')、表示書式(formatString)は '%s
支持率%s%' に指定する。

 856: /**
 857:  * jqPlot用のスクリプト:政党支持率
 858:  * @param   object $xml XMLオブジェクト
 859:  * @param   int $start  開始年
 860:  * @param   int $finish 終了年
 861:  * @return  string スクリプト
 862: */
 863: function plotParty($xml, $start, $finish) {
 864:     $graphId = GRAPH_ID;
 865: 
 866:     //プロットする政党名
 867:     static $table = array(
 868: '自民党', '公明党', '共産党', '民進党', '立憲民主党', '民主党', '日本維新の会', '国民民主党', 'れいわ新選組', '支持政党なし');
 869: 
 870:     //系列の生成
 871:     $str2 = '';
 872:     foreach ($table as $key=>$val) {
 873:         $str2 ."\t\t\t{ label: '{$val}' },\n";
 874:         $series[] = '';
 875:     }
 876: 
 877:     foreach ($xml as $research) {
 878:         $year = (int)$research->year;
 879:         //描画範囲内かどうか
 880:         if (($year < $start|| ($year > $finish))   continue;
 881:         //描画データ作成
 882:         foreach ($research as $article) {
 883:             $month = (int)$article->month;
 884:             foreach ($article as $party) {
 885:                 foreach ($table as $key=>$val) {
 886:                     if ($val == (string)$party->name) {
 887:                         if (preg_match('/[0-9\.]+/', $party->yes> 0 && $year > 0 && $month >1 && $month <12) {
 888:                             $series[$key.sprintf("['%04d-%02d-01',%4.1f],", $year, $month, (double)$party->yes);
 889:                         }
 890:                     }
 891:                 }
 892:             }
 893:         }
 894:     }
 895:     $str1 = '';
 896:     foreach ($series as $val) {
 897:         $str1 ."\t\t[ " . $val . "],\n";
 898:     }
 899: 
 900:     //X軸の最小値
 901:     $xmin = sprintf('%04d-01-01', $start);
 902:     if (date('Y') == $finish) {
 903:         $xmax = date('Y-m-01', strtotime('next month'));
 904:      } else {
 905:         $xmax = sprintf('%04d-12-31', $finish);
 906:     }
 907: 
 908:     //グラフ描画
 909:     $js =<<< EOT
 910: jQuery(function() {
 911:     jQuery.jqplot('{$graphId}',
 912:     [
 913: {$str1}
 914:     ],
 915:     {
 916:         //系列
 917:         series: [
 918: {$str2}
 919:         ],
 920:         legend: {
 921:             show: true,
 922:             placement: 'inside',
 923:             location: 'sw',
 924:             renderer: $.jqplot.EnhancedLegendRenderer,
 925:             rendererOptions: { numberRows: 1 }
 926:         },
 927:         seriesDefaults: {
 928:             showLine: true,
 929:             rendererOptions: { smooth: false },
 930:             markerOptions: { size: 0 },
 931:         },
 932:         //軸
 933:         axes: {
 934:             xaxis: {
 935:                 renderer: $.jqplot.DateAxisRenderer,
 936:                 min: '{$xmin}',
 937:                 max: '{$xmax}',
 938:                 tickOptions: { formatString: '%Y/%#m' },
 939:                 label: '年月',
 940:             },
 941:             yaxis: {
 942:                 label: '支持率(%)'
 943:             }
 944:         },
 945:         //ハイライター
 946:         highlighter: {
 947:             show: true,
 948:             showMarker: true,
 949:             tooltipLocation: 'sw',
 950:             fadeTooltip: false,
 951:             bringSeriesToFront: true,
 952:             tooltipAxes: 'xy',
 953:             formatString: '%s<br>支持率%s%'
 954:         }
 955:     }
 956:     );
 957: });
 958: 
 959: EOT;
 960: 
 961:     return $js;
 962: }

政党支持率は、ユーザー関数 plotParty により、XMLオブジェクトに格納したデータから jqPlot を使って折れ線グラフに描く。生成する jqPlotスクリプトは、上述のユーザー関数 plotCabinet とほぼ同じであるが、折れ線の数が増える。

参考サイト

(この項おわり)
header