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

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

(2025年5月24日)2024~2025年(令和7年)のHTML表記ゆれに対応

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

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

目次

サンプル・プログラム

圧縮ファイルの内容
viewPolls.phpサンプル・プログラム本体。
polls.xml内閣支持率・政党支持率のデータ・ファイル。
圧縮ファイルの内容
viewPolls.phpサンプル・プログラム本体。
viewPolls.php 更新履歴
バージョン 更新日 内容
3.5.0 2025/05/24 2024~2025年のHTML表記ゆれに対応
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 へのセーブ/ロード

解説:準備

viewPolls.php

  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 で示すファイルにセーブし、次に描画するときは、このファイルからロードできるようにした。

解説:スクレイピング

viewPolls.php

 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: }

viewPolls.php

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

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

viewPolls.php

 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>|\s*)/ui";
 449:     $pat11 = "/<h3>内閣支持([0-9]+)[%\%]、不支持([0-9]+)[%\%]/ui";
 450: //  $pat12 = "/(\p{Han}+内閣)を「支持する/ui";
 451:     $pat12 = "/([一-龠]+内閣)を「支持する/ui";
 452:     $pat21 = "/<h2><span>政党支持率<\/span>/ui";
 453:     $pat31 = "/<td>([^<]+)<\/td>/ui";
 454:     $pat32 = "/<td\s+class\=\"right\">([0-9\.]+)<\/td>/ui";
 455:     // 2023年8月以降
 456:     $pat22 = "/各党の支持率は/ui";
 457:     $pat33 = '/「([^」]+)」が([0-9\.]+)[%\%]/ui';
 458: 
 459:     $flag = FALSE;
 460:     while (! feof($infp)) {
 461:         $instr1 = trim(fgets($infp));
 462:         $instr2 = strip_tags($instr1);
 463:         // 月
 464:         if (preg_match($pat01, $instr1, $arr> 0) {
 465:             $month = (int)$arr[1];
 466:         // 内閣支持率・不支持率
 467:         } else if (preg_match($pat11, $instr1, $arr> 0) {
 468:             $items1[$month - 1]['month'] = (int)$month;
 469:             $items1[$month - 1]['yes']   = (int)$arr[1];
 470:             $items1[$month - 1]['no']    = (int)$arr[2];
 471:         // 内閣名
 472:         } else if (preg_match($pat12, $instr2, $arr> 0) {
 473:             $items1[$month - 1]['cabinet'] = (string)$arr[1];
 474:         // 政党名と支持率 2023年8月以降
 475:         } else if (preg_match($pat22, $instr1> 0) {
 476:             $items2[$month - 1]['month'] = (int)$month;
 477:             if (preg_match_all($pat33, $instr1, $arr, PREG_PATTERN_ORDER> 0) {
 478:                 foreach ($arr[1as $key=>$party) {
 479:                     if ($party == '特に支持している政党はない') {
 480:                         $party = '支持政党なし';
 481:                     }
 482:                     if (array_search($party, $table!FALSE) {
 483:                         $items2[$month - 1][$party] = (string)$arr[2][$key];
 484:                     }
 485:                 }
 486:             }
 487:             break;
 488:         // 政党支持率の解析開始
 489:         } else if (preg_match($pat21, $instr1, $arr> 0) {
 490:             $flag = TRUE;
 491:             $items2[$month - 1]['month'] = (int)$month;
 492:         // 政党名
 493:         } else if ($flag && (preg_match($pat31, $instr1, $arr> 0)) {
 494:             $party = (string)$arr[1];
 495:             if ($party == '特に支持している政党はない') {
 496:                 $party = '支持政党なし';
 497:             }
 498:         // 支持率
 499:         } else if ($flag && (preg_match($pat32, $instr1, $arr> 0)) {
 500:             $items2[$month - 1][$party] = (string)$arr[1];
 501:         }
 502:     }
 503:     return TRUE;
 504: }

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

解説:XMLへの格納

viewPolls.php

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

viewPolls.php

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

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

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

viewPolls.php

 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: }

viewPolls.php

 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以前にも対応するよう代替スクリプトを用意している。

viewPolls.php

  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スクリプト

viewPolls.php

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

内閣支持率は、ユーザー関数 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%' に指定する。

viewPolls.php

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

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

参考サイト

(この項おわり)
header