目次
サンプル・プログラムの実行例
(例1)キーワードから場所を検索し,地図とグラフを表示する.
pollenGraph.php?query=%E5%90%8D%E5%8F%A4%E5%B1%8B%E9%A7%85
query = 住所またはランドマーク
(例2)緯度・経度から場所を検索し,グラフのみを表示する.
pollenGraph.php?latitude=35.7&longitude=139.7&mode=1
latitude = 緯度(世界測地系;日本国内のみ有効)
longitude = 経度(世界測地系;日本国内のみ有効)
mode = 1:グラフのみを表示
(例3)市区町村コードから場所を検索し,グラフのみを表示する.
pollenGraph.php?cityCode=13104&mode=1
cityCode = 市区町村コード
mode = 1:グラフのみを表示
(例4)市区町村コードから場所を検索し,花粉飛散数データの数を表示する.
pollenGraph.php?cityCode=13104&count
cityCode = 市区町村コード
count:花粉飛散数データの数を表示
サンプル・プログラム
| pollenGraph.php | サンプル・プログラム本体。 |
| .pahooEnv | クラウドサービスを利用するためのアカウント情報などを記入する .env ファイル。 使い方は「各種クラウド連携サービス(WebAPI)の登録方法」を参照。include_path が通ったディレクトリに配置すること。 |
| pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
| pahooGeoCode.php | 住所・緯度・経度に関わるクラス pahooGeoCode。 使い方は「PHPで住所・ランドマークから最寄り駅を求める」「PHPで住所・ランドマークから緯度・経度を求める」などを参照。include_path が通ったディレクトリに配置すること。 |
| pahooWeather.php | 気象情報に関わるクラス pahooWeather。 気象情報に関わるクラスの使い方は「PHPで天気予報を求める(その3)」「PHPで暑さ指数予測を地図上に表示する」などを参照。include_path が通ったディレクトリに配置すること。 |
| pahooCache.php | キャッシュ処理に関わるクラス pahooCache。 キャッシュ処理に関わるクラスの使い方は「PHPで天気予報を求める」を参照。include_path が通ったディレクトリに配置すること。 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.0.1 | 2026/03/10 | 「地図検索」が動作しない不具合を修正 |
| 1.0.0 | 2026/02/19 | 初版 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 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() 追加 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 6.10.0 | 2026/02/19 | getLocalGovernmentCode, address2LocalGovernmentCode, localGovernmentCode2addresss メソッド追加 |
| 6.9.1 | 2025/11/25 | PHP8.5対応:double→float |
| 6.9.0 | 2025/09/21 | jsPolygon, jsPolygon_Gmap, jsPolygon_Leaflet, loadGeoJSON, getPrefBorderList を追加 |
| 6.8.0 | 2025/08/10 | アクセスキーなどを ".pahooEnd" に分離 |
| 6.7.1 | 2025/07/26 | jsLine_Gmap() - bug-fix |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 5.9.0 | 2026/03/07 | currentWeatherOpenWeather, utc2jst, kelvin2celsius, addOpenWeatherCounterメソッド追加 |
| 5.8.0 | 2026/02/15 | getPollenメソッド追加 |
| 5.7.0 | 2025/08/09 | readEnvWBGTinfoSpots(), readEnvWBGTforecast(), getWBGTcolor() 追加 |
| 5.6.2 | 2025/04/10 | readJmaSpots() -- bug-fix |
| 5.6.1 | 2025/04/08 | getMyscriptPathURL() -- bug-fix |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.3.0 | 2025/12/06 | PHP8.5対応:curl_closeを使わない |
| 1.2.0 | 2025/09/06 | cLoad() HTTPヘッダを送信できるようにした |
| 1.1.3 | 2025/08/10 | var→public |
| 1.1.2 | 2023/07/22 | bug-fix |
| 1.1.1 | 2023/02/11 | コメント追記 |
サンプル・プログラムの流れ(メイン)
準備:PHP の https対応
Windowsでは、"php.ini" の下記の行を有効化する。
extension=php_openssl.dllLinuxでは --with-openssl=/usr オプションを付けて再ビルドする。→OpenSSLインストール手順
これで準備は完了だ。
準備:pahooInputData 関数群
また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。
準備:pahooGeoCode クラス
pahooGeoCode.php
41: class pahooGeoCode {
42: public $items; // 検索結果格納用
43: public $error; // エラー・フラグ
44: public $errmsg; // エラー・メッセージ
45: public $hits; // 検索ヒット件数
46: public $webapi; // 直前に呼び出したWebAPI URL
47:
48: // 都道府県境界線データ
49: // SimpleMaps.com is a product of Pareto Software, LLC. © 2010-2025
50: // https://simplemaps.com/gis/country/jp
51: // ※各自の環境に合わせて設定すること
52: public $GeoJsonJP = __DIR__ . '/jp.json';
53:
54: // -- 以下のデータは .env ファイルに記述可能
55: // Google Cloud Platform APIキー
56: // https://cloud.google.com/maps-platform/
57: // ※Google Maps APIを利用しないのなら登録不要
58: public $GOOGLE_API_KEY_1 = ''; // HTTPリファラ用
59: public $GOOGLE_API_KEY_2 = ''; // IP制限用
60: public $GOOGLE_MAP_ID = ''; // GoogleMaps ID
61:
62: // Yahoo! JAPAN Webサービス アプリケーションID
63: // https://e.developer.yahoo.co.jp/register
64: // ※Yahoo! JAPAN Webサービスを利用しないのなら登録不要
65: public $YAHOO_APPLICATION_ID = '';
66:
67: // OSM Nominatim Search API利用時に知らせるメールアドレス
68: // https://wiki.openstreetmap.org/wiki/JA:Nominatim#.E6.A4.9C.E7.B4.A2
69: // ※OSM Nominatim Search APIを利用しないのなら登録不要
70: public $NOMINATIM_EMAIL = '';
71:
72: // IP2Location.io APIキー
73: // https://www.ip2location.io/
74: // ※IP2Location.ioを利用しないのなら登録不要
75: public $IP2LOCATION_API_KEY = '';
地図や住所検索として Google を利用するのであれば Google Cloud Platform APIキー とマップID が必要で、その入手方法は「Google Cloud Platform - WebAPIの登録方法」を、Yahoo!JAPAN を利用するのであれば Yahoo! JAPAN Webサービス アプリケーションIDが必要で、その入手方法は「Yahoo!JAPAN デベロッパーネットワーク - WebAPIの登録方法」を、IP2Location.ioを利用するのであれば「PHPでIPアドレスやホスト名から住所を求める」を、それぞれ参照されたい。
PHPのクラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。
準備:pahooCache クラス
pahooCache.php
13: class pahooCache {
14: const LIFE_CACHE = (2 * 60); // キャッシュ保持時間(デフォルト;分)
15: const DEF_DIRCACHE = './pcache/'; // キャッシュ・ディレクトリ(デフォルト)
16:
17: public $lifeCache; // キャッシュ保持時間(分)(0:キャッシュしない)
18: public $dirCache; // キャッシュ用ディレクトリ
19: public $httpHeader; // HTTPヘッダ(空文字の時は何も送らない)
20: public $error; // エラーフラグ
21: public $errmsg; // エラーメッセージ
22: public $debug; // デバッグ用ファイル名
23:
24: /**
25: * コンストラクタ
26: * 参考サイト https://www.pahoo.org/e-soul/webtech/php06/php06-72-01.shtm
27: * @param int $life キャッシュ保持時間(分)(省略可能)
28: * @param string $dir キャッシュ・ディレクトリ(省略可能)
29: * @param array $httpHeader httpヘッダに渡す配列(省略可能)
30: * USER AGENT偽装に用いることを想定
31: * (例)
32: * array(
33: * 'User-Agent: Mozilla/5.0(Windows NT 10.0; Win64; x64) pahooAppy/pahoo.org AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
34: * 'Accept-Language: ja-JP'
35: * );
36: * @return なし
37: */
38: function __construct($life=self::LIFE_CACHE, $dir=self::DEF_DIRCACHE, $httpHeader=NULL) {
39: if ($life < 0) {
40: $life = 0;
41: }
42: if (preg_match('/\/$/ui', $dir) == 0) {
43: $dir = $dir . '/';
44: }
45: $this->error = FALSE;
46: $this->errmsg = '';
47: $this->debug = '';
48: $this->lifeCache = $life;
49: $this->dirCache = $dir;
50: $this->httpHeader = $httpHeader;
51:
52: // PHP5以上であることを調べる.
53: if (! $this->isphp5over()) {
54: $this->error = TRUE;
55: $this->errmsg = '動作にはPHP5以上が必要です';
56: return;
57: }
58:
59: // キャッシュ・ディレクトリが無ければ作成する.
60: if (! is_dir($this->dirCache)) {
61: $res = mkdir($this->dirCache, 0744);
62: if ($res == FALSE) {
63: $this->error = TRUE;
64: $this->errmsg = 'キャッシュ・ディレクトリ "' . $this->$dirCache . '" の作成に失敗しました';
65: return;
66: }
67: }
68: }
そこで、頻繁に変更がないデータについては、一度取り込んだら、こちら側のサーバのローカルストレージにキャッシュしておく仕組みを用意した。それが pahooCacheクラス である。同梱のクラス・ファイル "pahooCache.php" は include_path が通ったディレクトリに配置してほしい。他のプログラムでも pahooCacheクラス を利用するが、常に最新のクラス・ファイルを1つ配置すればよい。
pahooCacheクラス の注意ポイントは、キャッシュ時間(単位:分)とキャッシュを保存するディレクトリをコンストラクタで指定している点だ。これらはプログラムによって変わるものである。インスタンス化するときの値は、pahooCacheクラス を利用するメイン・プログラムの方で解説する。
サイトによっては、User-Agent などを必要とすることがあるだろう。そこで、第3引数に HTTPヘッダ として送信するデータを配列で渡すことができるようにした。配列の構造はコメントを参照していただきたい。
PHPのクラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。
準備:jqPlotプラグイン
pollenGraph.php
167: <!-- jqPlot 本体 -->
168: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/jquery.jqplot.min.css">
169: <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
170: <script src="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/jquery.jqplot.min.js"></script>
171:
172: <!-- プラグイン -->
173: <script src="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.barRenderer.min.js"></script>
174: <script src="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.canvasTextRenderer.min.js"></script>
175: <script src="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script>
176: <script src="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.highlighter.min.js"></script>
177: <style type="text/css">
HTMLの <head> タグに、ライブラリ「jQuery」と、必要な jPlotプラグインを記述する。
各種定数
pollenGraph.php
79: // 各種定数(START) ===========================================================
80:
81: // 地図描画サービスの選択
82: // 0:Google
83: // 2:地理院地図・OSM
84: define('MAPSERVICE', 2);
85:
86: // 住所検索サービスの選択
87: // 0:Google
88: // 1:Yahoo!JAPAN
89: // 11:HeartRails Geo API
90: // 12:OSM Nominatim Search API
91: // 13:国土地理院ジオコーディングAPI
92: define('GEOSERVICE', 1);
93:
94: // 逆ジオコーディングサービスの選択
95: // 0:Google
96: // 1:Yahoo!JAPAN
97: // 11:HeartRails Geo API
98: // 21:簡易ジオコーディングサービス
99: define('REVGEOSERVICE', 1);
100:
101: // マップID
102: define('MAP_ID', 'map_id');
103: // マップの表示サイズ(単位:ピクセル)
104: define('MAP_WIDTH', 600);
105: define('MAP_HEIGHT', 400);
106: // 初期値
107: define('DEF_LATITUDE', 35.7); // 緯度
108: define('DEF_LONGITUDE', 139.7); // 経度
109: define('DEF_TYPE', 'roadmap'); // マップタイプ
110: define('DEF_ZOOM', 13); // ズーム
111: define('DEF_CATEGORY', 'address'); // カテゴリ
112:
113: // マップ中心マーカーのURL;表示しなくなければNULLにする
114: define('CENTER_MARKER', 'https://www.google.com/mapfiles/arrow.png');
115:
116: // 検索キーの最小文字長
117: define('QUERY_MIN_LEN', 3);
118: // 検索キーの最大文字長
119: define('QUERY_MAX_LEN', 99);
120:
121: // グラフのHTMLオブジェクトID
122: define('GRAPH_ID', 'jqGraph');
123: // グラフの表示幅・高さ(単位:ピクセル)
124: define('GRAPH_WIDTH', 600);
125: define('GRAPH_HEIGHT', 400);
126: // 棒グラフの色
127: define('GRAPH_COLOR', '#0000FF');
128: // グラフの背景色
129: define('GRAPH_BGCOLOR', '#FFFFFF');
130:
131: // キャッシュ保持時間(分) 0:キャッシュしない
132: // weathernews API へのアクセス負荷軽減
133: define('LIFE_CACHE_DATA', (60 * 8));
134:
135: // キャッシュ・ディレクトリ
136: // 書き込み可能で,外部からアクセスされないディレクトリを指定してください.
137: define('DIR_CACHE_DATA', './pcache_pollen/');
138:
139: // キャッシュ保持時間(分) 0:キャッシュしない
140: // 総務省(標準地域コード取得)へのアクセス不可軽減
141: define('LIFE_CACHE_DATA_CITYCODE', (60 * 24 * 180));
142:
143: // 各種定数(END) =============================================================
住所検索サービスは、Google、Yahoo!JAPAN、HeartRails Geo APIから選べる。あらかじめ、定数 GEOSERVICE に値を設定すること。
逆ジオコーディングサービスは、Google、Yahoo!JAPAN、HeartRails Geo API、簡易ジオコーディングサービスから選べる。あらかじめ、定数 REVGEOSERVICE に値を設定すること。
その他、とくに【変更不可】の指定のない定数については、自由に変更できる。
WxTech Data API:花粉飛散数データ
| URL |
|---|
| https://wxtech.weathernews.com/opendata/v1/pollen |
| フィールド名 | 要否 | 内 容 |
|---|---|---|
| citycode | 必須 | 市区町村コード(総務省:標準地域コード) |
| start | 必須 | 取得開始年月日(例:20260208) |
| end | 必須 | 取得終了年月日(例:20260214;取得期間は31日以内) |
カンマ区切りのCSV形式。
date は1時間ごとで、ISO 8601 形式。
pollen は花粉飛散量で、単位は「個/cm2」。
citycode,date,pollen
13104,2026-02-11T00:00:00+09:00,0
13104,2026-02-11T01:00:00+09:00,0
13104,2026-02-11T02:00:00+09:00,0
13104,2026-02-11T03:00:00+09:00,0
13104,2026-02-11T04:00:00+09:00,0
13104,2026-02-11T05:00:00+09:00,0
13104,2026-02-11T06:00:00+09:00,0
13104,2026-02-11T07:00:00+09:00,0
13104,2026-02-11T08:00:00+09:00,0
13104,2026-02-11T09:00:00+09:00,0
13104,2026-02-11T10:00:00+09:00,0
13104,2026-02-11T11:00:00+09:00,0
13104,2026-02-11T12:00:00+09:00,0
13104,2026-02-11T13:00:00+09:00,0
13104,2026-02-11T14:00:00+09:00,0
13104,2026-02-11T15:00:00+09:00,0
13104,2026-02-11T16:00:00+09:00,0
13104,2026-02-11T17:00:00+09:00,0
13104,2026-02-11T18:00:00+09:00,0
13104,2026-02-11T19:00:00+09:00,0
13104,2026-02-11T20:00:00+09:00,1
13104,2026-02-11T21:00:00+09:00,0
13104,2026-02-11T22:00:00+09:00,0
13104,2026-02-11T23:00:00+09:00,0
13104,2026-02-12T00:00:00+09:00,1
13104,2026-02-12T01:00:00+09:00,0
13104,2026-02-12T02:00:00+09:00,0
13104,2026-02-12T03:00:00+09:00,0
――以下略――
花粉飛散数データ取得
pahooWeather.php
969: /**
970: * weathernewsの「WxTech Data API」を使って花粉飛散数データを取得する
971: * 参考サイト https://www.pahoo.org/e-soul/webtech/php06/php06-56-01.shtm
972: * @param string $cityCode 市区町村コード(総務省:標準地域コード)
973: * string $start 取得開始年月日(例:20260208)
974: * string $end 取得終了年月日(例:20260214;取得期間は31日以内)
975: * array $items 花粉飛散数データを格納する配列
976: * データ構造 $items[cityCode][日時] = 花粉飛散数の単位:個数/cm^2 (欠測値-9999)
977: * @return int 取得データ件数/FALSE:取得失敗
978: */
979: function getPollen($cityCode, $start, $end, &$items) {
980: // 入力値のバリデーション
981: if (preg_match('/^\d{5}$/iu', $cityCode) === 0) {
982: $this->seterror('花粉飛散数データAPI:市区町村コードが間違っている');
983: return FALSE;
984: }
985: if (preg_match('/^^\d{8}$/iu', $start) === 0) {
986: $this->seterror('花粉飛散数データAPI:取得開始年月日が間違っている');
987: return FALSE;
988: }
989: if (preg_match('/^^\d{8}$/iu', $end) === 0) {
990: $this->seterror('花粉飛散数データAPI:取得終了年月日が間違っている');
991: return FALSE;
992: }
993:
994: // リクエストURL生成
995: $url = 'https://wxtech.weathernews.com/opendata/v1/pollen?citycode=' . $cityCode . '&start=' . $start . '&end=' . $end;
996: $this->webapi = $url;
997:
998: // 花粉飛散数データを取得する;pahooCacheクラスを利用
999: $csv = $this->pcc->load($url);
1000:
1001: // 行分割
1002: $lines = explode("\n", trim($csv));
1003:
1004: // 1行目をヘッダとして取得
1005: $headers = str_getcsv(array_shift($lines), ',', '"', '\\');
1006:
1007: // 花粉飛散数データを変数 $items へ格納する
1008: $cnt = 0;
1009: foreach ($lines as $line) {
1010: if ($line === '') continue;
1011:
1012: // CSVとして安全に分解
1013: $values = str_getcsv($line, ',', '"', '\\');
1014:
1015: // 列数チェック
1016: if (count($values) !== count($headers)) {
1017: continue; // 壊れた行はスキップ
1018: }
1019:
1020: // 列に分解
1021: $row = array_combine($headers, $values);
1022:
1023: // データを格納
1024: $citycode = $row['citycode'];
1025: $date = $row['date'];
1026: $pollen = (int)$row['pollen'];
1027: $items[$citycode][$date] = $pollen;
1028: $cnt++;
1029: }
1030: return $cnt;
1031: }
応答は CSV形式データで1行目はラベル名になっている。そこで、まず組み込み関数 explode を使って、1行ずつ配列 $lines に格納する。
次に、組み込み関数 str_getcsv を使って、1行目をヘッダとして取得・分解する。
2行目以降も、組み込み関数 str_getcsv を使って分解して、配列 [$items] へ代入していく。データ構造は
$items[cityCode][日時] = 花粉飛散数の単位:個数/cm2 (欠測値-9999)ここで、日時は ISO 8601 形式である。
総務省:標準地域コード
総務省のページ「統計に用いる標準地域コード」に、全国の標準地域コード(CSVファイル)のリンク先が掲げられている。そこで、このページをスクレイピングして、目的のCSVファイルをダウンどーする。
カンマ区切りのCSV形式。文字エンコードはシフトJIS。
tiiki-code が市区町村コードに該当する。
ken-code,sityouson-code,tiiki-code,ken-name,sityouson-name1,sityouson-name2,sityouson-name3,yomigana
01,000,01000,北海道,,,,ほっかいどう
01,100,01100,北海道,札幌市,,,さっぽろし
01,101,01101,北海道,札幌市,,中央区,ちゅうおうく
01,102,01102,北海道,札幌市,,北区,きたく
01,103,01103,北海道,札幌市,,東区,ひがしく
01,104,01104,北海道,札幌市,,白石区,しろいしく
01,105,01105,北海道,札幌市,,豊平区,とよひらく
01,106,01106,北海道,札幌市,,南区,みなみく
01,107,01107,北海道,札幌市,,西区,にしく
01,108,01108,北海道,札幌市,,厚別区,あつべつく
01,109,01109,北海道,札幌市,,手稲区,ていねく
01,110,01110,北海道,札幌市,,清田区,きよたく
01,202,01202,北海道,,,函館市,はこだてし
01,203,01203,北海道,,,小樽市,おたるし
01,204,01204,北海道,,,旭川市,あさひかわし
01,205,01205,北海道,,,室蘭市,むろらんし
01,206,01206,北海道,,,釧路市,くしろし
――以下略――
総務省:標準地域コード一覧を取得
pahooGeoCode.php
2914: /**
2915: * 総務省の標準地域コード一覧を取得する
2916: * 参考サイト https://www.pahoo.org/e-soul/webtech/php06/php06-56-01.shtm#localGovernmentCodeList
2917: * @param object $pcc pahooCacheオブジェクト
2918: * @param array $cityCodeList 標準地域コードを格納する配列
2919: * データ構造 $items[cityCode] = 市区町村名(UTF-8)
2920: * @param string $encode 「統計に用いる標準地域コード」サイトのエンコード
2921: * 省略時:SJIS
2922: * @return int 格納したデータ数 / FALSE:読み込み失敗
2923: */
2924: function getLocalGovernmentCodeList($pcc, &$cityCodeList, $encode='SJIS') {
2925: static $errmsg = '標準地域コードを取得できない';
2926: $domain = 'https://www.soumu.go.jp';
2927: $contentURL = 'https://www.soumu.go.jp/toukei_toukatsu/index/seido/9-5.htm';
2928:
2929: // 「統計に用いる標準地域コード」サイトにアクセスする.
2930: $contents = $pcc->load($contentURL);
2931: if ($contents === FALSE) {
2932: $this->seterror("{$errmsg}({$contentURL} にアクセスできない)");
2933: return FALSE;
2934: }
2935: $contents = mb_convert_encoding($contents, INTERNAL_ENCODING, $encode);
2936:
2937: // 「統計に用いる標準地域コード」のURLを取得する.
2938: $rep = '/<a\s+href\=\"(\/main_content\/[0-9]+\.csv)\">/i';
2939: if (preg_match($rep, $contents, $arr) === FALSE) {
2940: $this->seterror("{$errmsg}(CSVファイルが見つからない)");
2941: return FALSE;
2942: }
2943:
2944: // 「統計に用いる標準地域コード」のCSVファイルを取得する.
2945: $url = $domain . $arr[1];
2946: $csv = $pcc->load($url);
2947: if ($csv === FALSE) {
2948: $this->seterror("{$errmsg}({$url} にアクセスできない)");
2949: return FALSE;
2950: }
2951: $csv = mb_convert_encoding($csv, INTERNAL_ENCODING, $encode);
2952:
2953: // 行分割
2954: $lines = explode("\n", trim($csv));
2955:
2956: // ヘッダ取得
2957: $headers = str_getcsv(array_shift($lines), ',', '"', '\\');
2958:
2959: // 標準地域コードを配列 $cityCodeList に格納する
2960: $cnt = 0;
2961: foreach ($lines as $line) {
2962: if (trim($line) === '') continue;
2963:
2964: // CSVとして分解
2965: $cols = str_getcsv($line, ',', '"', '\\');
2966:
2967: // 列数不一致ガード
2968: if (count($cols) !== count($headers)) continue;
2969:
2970: // ヘッダ+値 → 連想配列
2971: $row = array_combine($headers, $cols);
2972:
2973: // 標準地域コード
2974: $cityCode = $row['tiiki-code'];
2975:
2976: // 市区町村名を連結(空は除外)
2977: $nameParts = array_filter([
2978: $row['ken-name'],
2979: $row['sityouson-name1'],
2980: $row['sityouson-name2'],
2981: $row['sityouson-name3'],
2982: ]);
2983: $cityName = implode('', $nameParts);
2984:
2985: // 配列に格納する.
2986: if ($cityCode !== '') {
2987: $cityCodeList[$cityCode] = $cityName;
2988: $cnt++;
2989: }
2990: }
2991:
2992: return $cnt;
2993: }
まず、総務省のページ「統計に用いる標準地域コード」から、目的の CSV ファイルへのハイパーリンクをスクレイピングで捜し出し、ダウンロードする。
このCSVファイルは、市区町村の合併・廃止がないかぎり変更はないはずだが、念のため、キャッシュシステムを介して一定期間ごと(定数 LIFE_CACHE_DATA_CITYCODE で指定)に再ダウンロードするようにした。
CSVファイルの分解と配列への格納については、上述の getPollen メソッドとほぼ同じである。
データ構造は、
$items[cityCode]= 市区町村名(UTF-8)ここで、市区町村名は、CSVファイルのラベル ken-name, sityouson-name1, sityouson-name2, sityouson-name3 を結合した文字列である。
住所から標準地域コードを取得する
pahooGeoCode.php
2995: /**
2996: * 住所から標準地域コードを取得する.
2997: * 参考サイト https://www.pahoo.org/e-soul/webtech/php06/php06-56-01.shtm#php_address2LocalGovernmentCode
2998: * @param object $pcc pahooCacheオブジェクト
2999: * @param string 住所
3000: * @return array(標準地域コード, マッチした住所) / FALSE:変換失敗失敗
3001: */
3002: function address2LocalGovernmentCode($pcc, $address) {
3003: // 総務省:標準地域コードを取得する.
3004: $cityCodeList = array();
3005: $cnt = $this->getLocalGovernmentCodeList($pcc, $cityCodeList, 'SJIS');
3006: if ($cnt === FALSE) {
3007: $errmsg = $this->geterror();
3008: return FALSE;
3009: }
3010:
3011: // 住所から標準地域コードを取得する.
3012: $cityCode = '00000';
3013: $shortAddress = '';
3014: foreach ($cityCodeList as $code=>$str) {
3015: // 前方一致検索
3016: if (mb_strpos($address, $str) === 0) {
3017: $cityCode = $code;
3018: $shortAddress = $str;
3019: }
3020: }
3021:
3022: return array($cityCode, $shortAddress);
3023: }
前述のメソッド getLocalGovernmentCodeList を使って得られた一覧配列から、1つ1つ住所が一致するものを捜していく。
ここで、引数で渡した住所 $address の方が丁目、番地まで含む長い文字列なので、一覧配列から取り出した住所と先頭一致するかどうかで判定する。先頭一致には、組み込み関数 mb_strpos を使っている。
標準地域コードから住所を取得する
pahooGeoCode.php
3025: /**
3026: * 標準地域コードから住所を取得する.
3027: * 参考サイト https://www.pahoo.org/e-soul/webtech/php06/php06-56-01.shtm#php_localGovernmentCode2address
3028: * @param object $pcc pahooCacheオブジェクト
3029: * @param string $cityCode 標準地域コード
3030: * @return string 住所 / FALSE:該当する住所がない
3031: */
3032: function localGovernmentCode2address($pcc, $cityCode) {
3033: // 総務省:標準地域コードを取得する.
3034: $cityCodeList = array();
3035: $cnt = $this->getLocalGovernmentCodeList($pcc, $cityCodeList, 'SJIS');
3036: if ($cnt === FALSE) {
3037: $errmsg = $this->geterror();
3038: return FALSE;
3039: }
3040:
3041: // 住所を標準地域コードに変換する.
3042: $address = FALSE;
3043: if (array_key_exists($cityCode, $cityCodeList)) {
3044: $address = $cityCodeList[$cityCode];
3045: } else {
3046: $this->seterror('該当する住所がありません');
3047: }
3048:
3049: return $address;
3050: }
前述のメソッド getLocalGovernmentCodeList を使って得られた一覧配列から、標準地域コードに合致する添字があれば、その値(つまり住所)を返す。
花粉飛散量の棒グラフを描画
pollenGraph.php
242: /**
243: * jqPlot用のスクリプト
244: * 参考サイト https://www.pahoo.org/e-soul/webtech/php06/php06-56-01.shtm#jqPlot
245: * @param string $graphId グラフのHTMLオブジェクト名
246: * @param array $items データ配列
247: * @param string $title グラフのタイトル
248: * @param string $address 住所
249: * @return string スクリプト
250: */
251: function plot($graphId, $items, $title='', $address='') {
252: // 棒グラフの色
253: $color = GRAPH_COLOR;
254: // 棒グラフの幅
255: $barwidth = (int)(GRAPH_WIDTH / count($items) * 0.8);
256: // グラフの背景色
257: $bgcolor = GRAPH_BGCOLOR;
258:
259: $ticks = array();
260: $seen = array();
261: $values = array();
262:
263: $cnt = 0;
264: $prev = '';
265: foreach ($items as $datetime=>$value) {
266: // 花粉飛散量
267: $values[] = (int)$value;
268:
269: // 表示用ラベル(例: 02/10)
270: $date = substr($datetime, 0, 10);
271: $label = date('m/d', strtotime($date));
272: // 同じ日付は1回だけ表示
273: if (! isset($seen[$label])) {
274: $ticks[] = [$cnt, $label];
275: $seen[$label] = TRUE;
276: }
277: $cnt++;
278: }
279:
280: // プロットデータをJSON化
281: $jsValues = json_encode($values);
282: $jsTicks = json_encode($ticks);
283:
284: $js =<<< EOT
285: jQuery(function() {
286: const values = {$jsValues};
287: const ticks = {$jsTicks};
288: jQuery.jqplot('{$graphId}', [values], {
289: // タイトル
290: title: {
291: text: '{$title} <span style="font-size:14px;">({$address})</span>',
292: show: true,
293: fontFamily: 'sans-serif',
294: fontSize: '18px',
295: textAlign: 'center',
296: textColor: 'black',
297: },
298: // データ(系列)
299: seriesDefaults: {
300: renderer: jQuery.jqplot.BarRenderer,
301: rendererOptions: {
302: barWidth: {$barwidth},
303: shadowOffset: 0
304: },
305: color: '{$color}',
306: },
307: // 軸
308: axes: {
309: xaxis: {
310: label: '月 日',
311: ticks: ticks,
312: tickRenderer: jQuery.jqplot.CanvasAxisTickRenderer,
313: labelOptions:{
314: fontFamily: 'sans-serif',
315: fontSize: '16px',
316: textColor: 'black',
317: },
318: },
319: yaxis: {
320: labelRenderer: jQuery.jqplot.CanvasAxisLabelRenderer,
321: label: '花粉飛散量(個/㎠)',
322: min: 0,
323: tickOptions: {
324: formatString: '%d',
325: },
326: labelOptions:{
327: fontFamily: 'sans-serif',
328: fontSize: '16px',
329: textColor: 'black',
330: },
331: }
332: },
333: // ハイライター
334: highlighter: {
335: show: true,
336: showMarker: false,
337: tooltipLocation: 'n',
338: fadeTooltip: false,
339: bringSeriesToFront: true,
340: tooltipAxes: 'y',
341: formatString: '%d'
342: },
343: // グラフの背景
344: grid: {
345: drawBorder: true,
346: shadow: false,
347: background: '{$bgcolor}',
348: },
349: })
350: });
351:
352: EOT;
353:
354: return $js;
355: }
PHP側では、前述の getPollen メソッドを使って取得した花粉飛散量データ配列 $items を jqPlot で扱いやすいように、JSON形式に変換する。
まず、プロットするデータは配列 $value に格納する。
X軸のラベルだが、1時間おきにデータがあるので、1日につき24個のデータがプロットされる。この1つ1つに日時ラベルを表示するとX軸がまっ黒になってしまうので、月日までを表示する工夫を施した。すなわち、配列 $ticks に "[$cnt, $label]" というデータセットを代入していく。ここで、変数 $cnt は先頭データを 0 としたときの順番、$label は表示したい月日である。もし直前の $label と違う月日であれば配列 $ticks に格納するが、違えば格納しない。こうするこことで、配列 $ticks は $cnt が24ずつ離れた形の配列となる。
これを JSON 化して、jqPlot の axes オブジェクトに渡すことで、月日が切り替わるタイミング(午前0時)でX軸にラベルを表示するようになる。
メイン・プログラム
pollenGraph.php
487: // メイン・プログラム =======================================================
488:
489: // インスタンスを生成する.
490: $pcc = new pahooCache(LIFE_CACHE_DATA, DIR_CACHE_DATA);
491: $pccCityCode = new pahooCache(LIFE_CACHE_DATA_CITYCODE, DIR_CACHE_DATA);
492: $pwt = new pahooWeather($pcc);
493: $pgc = new pahooGeoCode();
494:
495: // 各種パラメータ取得と初期値
496: $errmsg = $address = $jsGraph = '';
497: $count = 0;
498: $items = array();
499: // 動作モード
500: $mode = (int)getValidNumber('mode', $errmsg, 0, TRUE, 0, 1);
501: if ($errmsg !== '') {
502: $errmsg = '表示モードの値が ' . $errmsg;
503: }
504: // 市区町村コード
505: $cityCode = (string)getValidString('cityCode', $errmsg, '', 5, 5, TRUE, ['/^\d{5}$/ui']);
506: if ($errmsg !== '') {
507: $errmsg = '市区町村コードが ' . $errmsg;
508: }
509: // 各種パラメータ
510: $query = (string)getValidString('query', $errmsg, '', QUERY_MIN_LEN, QUERY_MAX_LEN);
511: $countOnly = isButton('count');
512: $latitude = getParam('latitude', FALSE, DEF_LATITUDE); // 緯度
513: $longitude = getParam('longitude', FALSE, DEF_LONGITUDE); // 経度
514: $zoom = getParam('zoom', FALSE, DEF_ZOOM); // ズーム
515: $type = getParam('type', FALSE, DEF_TYPE); // マップタイプ
516: $category = getParam('category', FALSE, DEF_CATEGORY); // カテゴリ
517: $pgc->setYOLP_GeoSelectCategory($category);
518: $start = date('Ymd', strtotime('-7 days'));
519: $end = date('Ymd');
520:
521: // 検索キーをクリアする.
522: if (isButton('clear')) {
523: $query = '';
524: $latitude = DEF_LATITUDE;
525: $longitude = DEF_LONGITUDE;
526: $zoom = DEF_ZOOM;
527: $type = DEF_TYPE;
528: $category = DEF_CATEGORY;
529: $errmsg = $address = $jsGraph = '';
530:
531: // 緯度・経度を取得する.
532: } else if (($errmsg === '') && ! isButton('map') && ($query !== '')) {
533: list($n, $url) = $pgc->searchPoint3($query, GEOSERVICE, $category);
534: // エラーがなければ最初の住所を対象にする
535: if (! $pgc->iserror()) {
536: list($latitude, $longitude, $address) = $pgc->getPoint(1);
537: if ($address === '') {
538: $address = (($arr = $pgc->getAddress3($latitude, $longitude, REVGEOSERVICE)) == FALSE) ? '' : $arr['address'];
539: }
540: } else {
541: $errmsg = $pgc->geterror();
542: }
543:
544: // 住所を取得する.
545: } else if (($errmsg === '') && isButton('map')) {
546: $query = $address = (($arr = $pgc->getAddress3($latitude, $longitude, REVGEOSERVICE)) == FALSE) ? '' : $arr['address'];
547:
548:
549: // 市区町村コードから住所を取得する.
550: } else if ($cityCode !== '') {
551: $address = $pgc->localGovernmentCode2address($pccCityCode, $cityCode);
552: $errmsg = $pgc->geterror();
553:
554: // 緯度・経度から住所を取得
555: } else {
556: $address = (($arr = $pgc->getAddress3($latitude, $longitude, REVGEOSERVICE)) == FALSE) ? '' : $arr['address'];
557: }
558:
559: $arr = $pgc->address2LocalGovernmentCode($pccCityCode, $address);
560: if ($arr === FALSE) {
561: $errmsg = '該当市区町村が見当たりません';
562: $cityCode = '00000';
563: $shortAddress = '';
564: } else {
565: $cityCode = (string)$arr[0];
566: $shortAddress = $arr[1];
567: }
568:
569: if ($errmsg === '') {
570: $count = $pwt->getPollen($cityCode, $start, $end, $items);
571: $title= '花粉飛散量';
572: $jsGraph = plot(GRAPH_ID, $items[$cityCode], $title, $shortAddress);
573: }
574:
575: // マップ描画スクリプト
576: $jsmap = $pgc->drawJSMap(MAP_ID, $latitude, $longitude, $type, $zoom, NULL, NULL, MAPSERVICE, NULL, NULL, NULL, NULL, CENTER_MARKER);
577:
578: // 場所情報を配列へ
579: $spot['query'] = $query;
580: $spot['address'] = $address;
581: $spot['cityCode'] = $cityCode;
582: $spot['longitude'] = $longitude;
583: $spot['latitude'] = $latitude;
584: $spot['address'] = $address;
585: $spot['zoom'] = $zoom;
586: $spot['type'] = $type;
587:
588: // 表示用HTMLを作成する.
589: $HtmlBody = makeCommonBody($mode, $errmsg, $count, $jsmap, $jsGraph, $spot, $pwt->webapi, $pgc);
590:
591: // 花粉飛散数データの件数だけ表示
592: if ($countOnly) {
593: echo $count;
594:
595: // 画面に表示する.
596: } else {
597: echo $HtmlHeader;
598: echo $HtmlBody;
599: echo $HtmlFooter;
600: }
601:
602: // インスタンスを解放する.
603: $pgc = $pwt = $pcc = $pccCityCode = NULL;
604:
605: /*
参考サイト
- 花粉飛散数データ:ウェザーニューズ
- 統計に用いる標準地域コード:総務省
- PHPで住所・ランドマークから緯度・経度を求める:ぱふぅ家のホームページ
- PHPでNHK政治意識月例調査をグラフ表示:ぱふぅ家のホームページ
