PHPで潮位を計算する

(1/1)
釣り人でなくても、潮干狩り鳴門の渦潮観光など、潮見表を見ること少なくない。台風による浸水被害を予測するときにも必要だ。
さて、「PHPで日出没・月出没・月齢・潮を計算」では潮の計算を行うプログラムを紹介したが、潮位を計算するには、はるかに複雑な計算を行う必要がある。幸い、コンピュータの計算能力が飛躍的に向上したおかげで、潮位の計算もストレスなく行えるようになった。

そこで、これまで作ってきた "pahooCalendar.php" に、あらた潮位計算クラス "pahooTide" を追加し、Googleマップに表示されている地点の近くの海岸において、指定日から1週間の毎日の満潮・干潮の時刻と潮位を一覧およびグラフ表示するサンプル・プログラムを作ってみることにする。

(2025年8月24日).pahooEnv 導入
(2025年6月14日)GoogleMaps JavaScript APIの変更に対応した.

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

PHPで潮位を計算する
Googleマップ表示
PHPで潮位を計算する

目次

サンプル・プログラムのダウンロード

圧縮ファイルの内容
tide.phpサンプル・プログラム本体。
pahooCalendar.php暦・潮位計算クラス pahooCalendar。
暦・潮位計算クラスの使い方は「PHPで二十四節気・七十二候一覧を作成」「PHPで月齢を計算」「PHPで日出没・月出没・月齢・潮を計算」「PHPで潮位を計算する」などを参照。include_path が通ったディレクトリに配置すること。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
tideData.zip計算パラメータ。pahooTideが解凍せずに参照する圧縮ファイル。
pahooGeoCode.php住所・緯度・経度に関わるクラス pahooGeoCode。
使い方は「PHPで住所・ランドマークから最寄り駅を求める」「PHPで住所・ランドマークから緯度・経度を求める」などを参照。include_path が通ったディレクトリに配置すること。
moon/moon_00.png~moon_30.png月齢画像。
makeTideDataFiles.phptideData.zip を自動生成する。
サンプル・プログラム実行時には不要。
table_C1C2.xlsx表-C.1, C.2の元データ。
サンプル・プログラム実行時には不要。
dispC1C2table.php表-C.1, C.2をTABLE表示する。
サンプル・プログラム実行時には不要。
tide.php 更新履歴
バージョン 更新日 内容
3.4.0 2025/08/24 .pahooEnv導入
3.3.0 2022/07/09 数値入力にpahooInputData導入
3.21 2022/07/09 bug-fix
3.2 2022/06/04 PHP8対応,リファラ・チェック改良,計算パラメータ更新
3.11 2019/06/24 逆ジオコーディングサービスの選択肢を増やした
pahooCalendar.php 更新履歴
バージョン 更新日 内容
4.5.1 2025/05/31 deg2ddmm(), deg2hhmm() 不具合修正
4.5.0 2024/03/17 ヒジュラ暦メソッドを追加
4.4.1 2024/03/17 getCabinetOfficeHolidayTable() -- bug-fix
4.4.0 2024/02/25 内閣府の祝日表を参照できるようにした
4.3.2 2023/02/11 getSolarTerm72() 表記改訂:水澤腹堅→水沢腹堅
pahooInputData.php 更新履歴
バージョン 更新日 内容
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() 追加
pahooGeoCode.php 更新履歴
バージョン 更新日 内容
6.8.0 2025/08/10 アクセスキーなどを ".env" に分離
6.7.1 2025/07/26 jsLine_Gmap() - bug-fix
6.7.0 2025/07/20 drawJSmap,drawGMap -- 引数 $markerLevel 追加
6.6.0 2025/07/19 drawJSmap,drawGMap,drawLeaflet -- マップ中心マーカー表示引数を追加
6.5.0 2025/06/14 GoogleMaps JavaScript APIの変更に対応

準備:PHP の https対応

クラウド連携や相手先サイトのデータを読み込むのに https通信を使うため、PHPに OpenSSLモジュールが組み込まれている必要がある。関数  phpinfo  を使って、下図のように表示されればOKだ。
OpenSSL - PHP
そうでない場合は、次の手順に従ってOpenSSLを有効化し、PHPを再起動させる必要がある。

Windowsでは、"php.ini" の下記の行を有効化する。
extension=php_openssl.dll
Linuxでは --with-openssl=/usr オプションを付けて再ビルドする。→OpenSSLインストール手順

これで準備は完了だ。

準備:pahooInputData 関数群

PHPのバージョンや入力データのバリデーションなど、汎用的に使う関数群を収めたファイル "pahooInputData.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooInputData.php" を利用するが、常に最新のファイルを1つ配置すればよい。

また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。

準備:pahooCalendar クラス

pahooCalendar.php

  11: class pahooCalendar {
  12:     var $CONVERGE = 0.00005;            //逐次近似計算収束判定値
  13:     var $ASTRO_REFRACT = 0.585556;      //大気差
  14:     var $TDIFF = +9.0;          //世界時との時差
  15:     var $error, $errmsg;        //エラーフラグ,エラーメッセージ
  16:     var $year, $month, $day;    //西暦年月日
  17:     var $tblmoon;               //グレゴリオ暦=旧暦テーブル
  18:     var $language;              //表示言語(jp:日本語, en:英語, en3:英語略記)
  19:     var $resolve2033;           //旧暦2033年問題解決案 0:解決しない,1:案1,2:案2,3:対応案3 → https://www.pahoo.org/e-soul/webtech/php02/php02-45-01.shtm#php_resolveLunarCalendar2033
  20:     var $pcc;                   //pahooCacheインスタンス
  21:     var $CabinetOfficeHolidayTables;    //内閣府が公開している祝日表
  22:     const CABINETOFFICE_HOLIDAY_FILE = 'https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv';   //内閣府が公開している祝日表URL
  23: 
  24: /**
  25:  * コンストラクタ
  26:  * @param   string $language 表示言語;省略時 jp
  27:  * @param   float  $tdiff    世界時との時差(省略時 +9.0;日本標準時)
  28:  * @param   object $pcc      pahooCacheインスタンス;省略時 NULL
  29:  *              インターネット経由で内閣府の国民の祝日を参照するときに指定
  30:  * @return  bool オブジェクト/FALSE:$tdiffが不正
  31: */
  32: function __construct($language='jp', $tdiff=+9.0, $pcc=NULL) {
  33:     $this->error  = FALSE;
  34:     $this->errmsg = '';
  35:     $this->year  = date('Y');
  36:     $this->month = date('n');
  37:     $this->day   = date('j');
  38:     $this->resolve2033 = 0;
  39:     $this->pcc = $pcc;
  40:     $this->CabinetOfficeHolidayTables = array();
  41: 
  42:     $this->setLanguage($language);
  43:     if ($this->setTimeDifference($tdiff) == FALSE) {
  44:         $this->error = TRUE;
  45:         $this->errmsg = 'illegal tdiff';
  46:     }
  47: }

西暦(グレゴリオ暦)計算やヒジュラ暦(イスラム暦)への変換、祝祭日、六曜、二十四節気の算出などの暦関係の計算、太陽や月の位置計算、潮位などを求めるクラス・ファイル "pahooCalendar.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooCalendar.php" を利用するが、常に最新のクラス・ファイルを1つ配置すればよい。

準備:pahooGeoCode クラス

pahooGeoCode.php

  40: class pahooGeoCode {
  41:     public $items;      // 検索結果格納用
  42:     public $error;      // エラー・フラグ
  43:     public $errmsg;     // エラー・メッセージ
  44:     public $hits;       // 検索ヒット件数
  45:     public $webapi// 直前に呼び出したWebAPI URL
  46: 
  47:     // -- 以下のデータは .env ファイルに記述可能
  48:     // Google Cloud Platform APIキー
  49:     // https://cloud.google.com/maps-platform/
  50:     // ※Google Maps APIを利用しないのなら登録不要
  51:     public $GOOGLE_API_KEY_1 = '';      // HTTPリファラ用
  52:     public $GOOGLE_API_KEY_2 = '';      // IP制限用
  53:     public $GOOGLE_MAP_ID    = '';      // GoogleMaps ID
  54: 
  55:     // Yahoo! JAPAN Webサービス アプリケーションID
  56:     // https://e.developer.yahoo.co.jp/register
  57:     // ※Yahoo! JAPAN Webサービスを利用しないのなら登録不要
  58:     public $YAHOO_APPLICATION_ID = '';
  59: 
  60:     // OSM Nominatim Search API利用時に知らせるメールアドレス
  61:     // https://wiki.openstreetmap.org/wiki/JA:Nominatim#.E6.A4.9C.E7.B4.A2
  62:     // ※OSM Nominatim Search APIを利用しないのなら登録不要
  63:     public $NOMINATIM_EMAIL = '';
  64: 
  65:     // IP2Location.io APIキー
  66:     // https://www.ip2location.io/
  67:     // ※IP2Location.ioを利用しないのなら登録不要
  68:     public $IP2LOCATION_API_KEY = '';

GoogleマップやLeafletなどによる地図描画や住所検索を行うためのクラスが pahooGeoCode である。同梱のクラス・ファイル "pahooGeoCode.php" は include_path が通ったディレクトリに配置してほしい。他のプログラムでも pahooGeoCodeクラス を利用するが、常に最新のクラス・ファイルを1つ配置すればよい。

地図や住所検索として 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でクラスを使ってテキストの読みやすさを調べる」を参照されたい。

準備:各種定数など

tide.php

  56: // 各種定数(START) ===========================================================
  57: 
  58: // jqPlotのあるフォルダ
  59: define('JQPLOT', '../../../../common/jqplot/');
  60: 
  61: // 月の満ち欠け画像ファイルの場所
  62: // 画像作成方法→https://www.pahoo.org/e-soul/webtech/phpgd/phpgd-17-01.shtm
  63: define('MOONAGE', './moon/');
  64: 
  65: // 潮位表:計算期間(日)の初期値
  66: define('INTERVAL_DEF', 7);
  67: 
  68: // 潮位グラフ:計算期間(日)の初期値
  69: define('INTERVAL_GRAPH_DEF', 5);
  70: 
  71: // 潮位グラフのプロット間隔(日)
  72: define('INTERVAL_GRAPH', 0.02);
  73: 
  74: // 潮位グラフの名前
  75: define('GRAPH_TIDE', 'jqPlot_tide');
  76: 
  77: // 地図描画サービスの選択
  78: //    0:Google
  79: //    2:地理院地図・OSM
  80: define('MAPSERVICE', 2);
  81: 
  82: // 住所検索サービスの選択
  83: //    0:Google
  84: //    1:Yahoo!JAPAN
  85: //   11:HeartRails Geo API
  86: define('GEOSERVICE', 1);
  87: 
  88: // 逆ジオコーディングサービスの選択
  89: //    0:Google
  90: //    1:Yahoo!JAPAN
  91: //   11:HeartRails Geo API
  92: //   21:簡易ジオコーディングサービス
  93: define('REVGEOSERVICE', 1);
  94: 
  95: // 指定できる西暦年の範囲
  96: define('MIN_YEAR', 1901);
  97: define('MAX_YEAR', 2099);
  98: 
  99: // 表示言語(jp:日本語, en:英語, en3:英語略記)
 100: define('LANGUAGE', 'jp');
 101: 
 102: // 世界時からの時差(日本標準時)
 103: define('UTCDIFF', +9.0);
 104: 
 105: // 観測点のデフォルト値(東京駅)
 106: define('DEF_LOCATION', 'TK');   // 東京
 107: define('DEF_LATITUDE',  35.681111);
 108: define('DEF_LONGITUDE', 139.766667);
 109: define('DEF_HEIGHT',    4.0);
 110: 
 111: // 月齢計算時刻
 112: define('MOONAGE_HOUR', 21);
 113: 
 114: // マップID
 115: define('MAPID', 'map_id');
 116: // 初期値
 117: define('MAP_WIDTH',     800);           // 表示幅
 118: define('MAP_HEIGHT',    300);           // 表示高
 119: define('DEF_TYPE',      'roadmap');     // デフォルトのマップタイプ
 120: define('DEF_ZOOM',      8);             // デフォルトの拡大率
 121: define('DEF_CAT',       'address');     // カテゴリ
 122: 
 123: // 各種定数(END) ===============================================================

定数は、とくに断り書きがないかぎり、自由に変更できる。
ユーザークラス "pahooTide" が参照するデータファイル "tideData.zip" はクラスファイルと同じディレクトリに配置すること。

準備:地図サービス(WebAPI)の選択

表示する地図は、Googleマップ地理院地図・オープンストリートマップ(OSM)から選べる。あらかじめ、定数 MAPSERVIC に値を設定すること。
住所検索サービスは、GoogleYahoo!JAPANHeartRails Geo APIOSM Nominatim Search API から選べる。あらかじめ、定数 GEOSERVICE に値を設定すること。
逆ジオコーディングサービスは、GoogleYahoo!JAPANHeartRails Geo API簡易ジオコーディングサービスから選べる。あらかじめ、定数 REVGEOSERVICE に値を設定すること。

全体の流れ

ここでは、『港湾空港技術研究所資料 No.1168』(2007年12月,独立行政法人 港湾空港技術研究所)の「付録C 潮汐の推算」を参考に、プログラムを作成していく。プログラムの動作確認にあたっては、気象庁の潮位表、および TIDE for WINを利用した。後者はVisual Basicのソースコードが公開されており、開発の参考になるだろう。

潮汐は複数の分潮の合成であり、下記の式で求めることができる。
$$ \displaystyle \eta\ =\ Z_0\ +\ \sum_{i=1}^{k}\ f_i\ H_i\ cos(V_i\ +\ u_i\ -\ k_i) \tag{C.1} $$
η潮位
Z0観測値の平均水面
fi分潮iの振幅に対する補正係数
Hi観測地において計算された振幅
Vi時間とともに変化する分潮引数
ui時間とともに変化する位相の補正係数
ti観測地において計算された位相遅れ
$ Z_0 $、 $ H_i $、 $ u_i $は観測地に固有の値であり、後述するが、気象庁が公開している各観測地点における分潮一覧表から入手することができる。
ここからしばらくは、クラスファイル "pahooCalendar.php" にある潮位計算クラス "pahooTide" のコードと見比べながら計算原理を説明してゆく。

pahooCalendar.php

2803: // 潮位計算 ===============================================================
2804: // @参考URL https://www.pahoo.org/e-soul/webtech/php02/php02-51-01.shtm
2805: class pahooTide {
2806:     const FILE_ZIPNAME = 'tideData.zip';    //計算用パラメータ格納ZIPファイル
2807:     const FILE_C1      = '_C1.txt';         //表-C.1
2808:     const FILE_C2      = '_C2.txt';         //表-C.2
2809:     const FILE_INDEX   = '_index.txt';      //地点一覧
2810:     const FILE_EXT     = '.txt';            //拡張子
2811:     const JST          = -9;                //日本時の時差
2812:     const COMMENT = '#';                    //コメント文字
2813:     var $error, $errmsg;                    //エラーフラグ,エラーメッセージ
2814:     var $_s, $_h, $_p, $_N;                 //天文引数
2815: 
2816:     var $c1, $c2;                           //表-C.1, C.2
2817:     var $index;                             //地点一覧
2818:     var $port;                              //ある地点の分潮一覧表
2819: 
2820: //分潮記号
2821: var $keys = array('Sa', 'Ssa', 'Mm', 'MSf', 'Mf', '2Q1', 'σ1', 'Q1', 'ρ1', 'O1', 'MP1', 'M1', 'χ1', 'π1', 'P1', 'S1', 'K1', 'ψ1', 'φ1', 'θ1', 'J1', 'SO1', 'OO1', 'OQ2', 'MNS2', '2N2', 'μ2', 'N2', 'ν2', 'OP2', 'M2', 'MKS2', 'λ2', 'L2', 'T2', 'S2', 'R2', 'K2', 'MSN2', 'KJ2', '2SM2', 'MO3', 'M3', 'SO3', 'MK3', 'SK3', 'MN4', 'M4', 'SN4', 'MS4', 'MK4', 'S4', 'SK4', '2MN6', 'M6', 'MSN6', '2MS6', '2MK6', '2SM6', 'MSK6');

潮汐と分潮

潮汐は、月と太陽の引力によって起きるわけだが、地球や月の公転軌道が真円ではないため、潮汐力は単純な振動方程式にならない。そこで、複数の正弦周期(サインカーブ)の和によって潮汐力の周期変動を表すことが行われている。
1つ1つの周期を分潮 (ぶんちょう) と呼び、60種類が定義されている。このうち影響の大きい下記の4つを、主要4分潮を呼ぶ。
M2月の引力によるもので、周期は約12時間25分。
S2太陽の引力によるもので、周期は約12時間。
O1月の引力によるもので、周期は約25時間49分。
K1観月と太陽の合成引力によるもので、周期は約23時間56分。

天文引数の計算

まず、周期計算のベースになる天文引数を求める。 $$ \begin{align} s &= 211.728\ +\ 129.38471\ y\ +\ 13.176396\ d \tag{C.2} \\ h &= 279.974\ -\ 0.23871\ y\ +\ 0.985647\ d \tag{C.3} \\ p &= 83.298\ +\ 40.66229\ y\ +\ 0.111404\ d \tag{C.4} \\ N &= 125.071\ -\ 19.32812\ y\ -\ 0.052954\ d \tag{C.5} \\ y &= Y\ -\ 2000 \tag{C.6} \\ d &= D\ +\ L \tag{C.7}\end{align} $$
Y西暦
Dその年の1月1日からの経過日数
L西暦2000年からその年の年初までの閏日の数
$$ \displaystyle L\ =\ \bigg\{ \frac{(Y\ +\ 3)}{4}\ \bigg\} -\ 500 $$
{ }は中の数値を整数に丸めることを意味する。Lは2000年以前では負数とする。

pahooCalendar.php

2906: /**
2907:  * 指定した年月日から天文引数を求める.
2908:  * @param   int $year, $month, $day  グレゴリオ暦による年月日
2909:  * @return  なし
2910: */
2911: function sun_moon($year, $month, $day) {
2912:     $y = (float)$year - 2000.0;
2913:     $L = (float)floor(($year + 3.0) / 4.0- 500.0;
2914:     $d = (float)date('z', mktime(0, 0, 0, $month, $day, $year)) + $L;
2915: 
2916:     $this->_s = (float)211.728 + 129.38471 * $y + 13.176396 * $d;
2917:     $this->_h = (float)279.974 -   0.23871 * $y +  0.985647 * $d;
2918:     $this->_p = (float83.298 +  40.66229 * $y +  0.111404 * $d;
2919:     $this->_N = (float)125.071 -  19.32812 * $y -  0.052954 * $d;
2920: 
2921:     $this->_s = $this->_stdegree($this->_s);
2922:     $this->_h = $this->_stdegree($this->_h);
2923:     $this->_p = $this->_stdegree($this->_p);
2924:     $this->_N = $this->_stdegree($this->_N);
2925: }

分潮引数 Vi の計算

(C.1)式の $ V_i\ +\ u_i $ は次のように表せる。
$$ \displaystyle V_i\ +\ u_i\ =\ V_{0i}\ +\ u_i\ +\ nLat\ +\ \sigma _i(t_l\ +\ t_s) \tag{C.9} $$
V0i世界時0時における分潮引数 $ V_i $
σi分潮iの角速度で、後述する表-C.2から取得する。
n1日周期は1、半日周期は2、1/4周期は4‥‥となる定数で、後述する表-C.2から取得する。
tlローカル時間(時)。
ts世界時との時差(時)。日本標準時の場合は -9。
Lat観測地点の経度。東経はプラス、西経はマイナス。

基準となる $ V_{0i} $ は次のようにして計算できる。
$$ V_{0i}\ =\ a_ss\ +\ a_hh\ +\ a_pp\ +\ c \tag{C.10} $$
$ a_s $、 $ a_h $、 $ a_p $、 $ c $ は、後述する表-C.2から取得できる。

補正係数 fi、ui の計算

分潮によって $ f_i $、 $ u_i $ の計算方法は3種類に分かれる。

1)L2、M1分潮 $$ \displaystyle \begin{align} (f\ cos\ u)(L_2) &= 1 - 0.2505\ cos\ 2p - 0.1102\ cos(2p-N) - 0.0156\ cos(2p-2N)-0.0370\ cosN \tag{C.17} \\ (f sin\ u)(L_2) &= - 0.2505\ sin\ 2p - 0.1102\ sin(2p-N) - 0.0156\ sin(2p-2N)-0.0370\ sinN \tag{C.18} \\ (f\ cos\ u)(M_1) &= 2\ cos\ p + 0.4\ cos(p-N) \tag{C.19} \\ (f\ sin\ u)(M_1) &= sin\ p + 0.2\ cos(p-N) \tag{C.20} \\ f_i &= \sqrt{(f cos\ u)_{2}^{i} + (f\ sin\ u)_{2}^{i}} \tag{C.21} \\ u_i &= tan^{-1}\frac{(f\ sin\ u)_i}{(f\ cos\ u)_i} \tag{C.22} \end{align} $$

pahooCalendar.php

2927: /**
2928:  * L2, M1分潮のfi, uiを求める.
2929:  * @param   string $i  分潮記号 'L2' または 'M1'
2930:  * @param   string $fn 'fi' または 'ui'
2931:  * @return  float 計算結果
2932: */
2933: function fiui_L2M1($i, $fn) {
2934:     //L2
2935:     if ($i == 'L2') {
2936:         $fcosu = (float)1 - 0.2505 * cos(deg2rad(2 * $this->_p))
2937:                 - 0.1102 * cos(deg2rad(2 * $this->_p - $this->_N))
2938:                 - 0.0156 * cos(deg2rad(2 * $this->_p - 2 * $this->_N))
2939:                 - 0.0370 * cos(deg2rad($this->_N));
2940:         $fsinu = (float)-0.2505 * sin(deg2rad(2 * $this->_p))
2941:                 - 0.1102 * sin(deg2rad(2 * $this->_p - $this->_N))
2942:                 - 0.0156 * sin(deg2rad(2 * $this->_p - 2 * $this->_N))
2943:                 - 0.0370 * sin(deg2rad($this->_N));
2944:     //M1
2945:     } else {
2946:         $fcosu = (float)2 * cos(deg2rad($this->_p))
2947:                 + 0.4 * cos(deg2rad($this->_p - $this->_N));
2948:         $fsinu = (float)sin(deg2rad($this->_p))
2949:                  + 0.2 * sin(deg2rad($this->_p - $this->_N));
2950:     }
2951: 
2952:     //fi
2953:     if ($fn == 'fi') {
2954:         $res = (float)sqrt($fcosu * $fcosu + $fsinu * $fsinu);
2955:     //ui
2956:     } else {
2957:         $res = (float)atan($fcosu / $fsinu);
2958:     }
2959: 
2960:     return $res;
2961: }

2)表-C.1の8分潮 $$ \displaystyle \begin{align} f_i &= b_{i0} + b_{i1}cosN + b_{i2}\ cos2N\ + b_{i3}cos\ 3N \tag{C.11} \\ u_i &= c_{i1}\ sinN\ +\ c_{i2}\ sin2N\ +\ c_{i3}\ sin3N \tag{C.12} \end{align} $$
3)1,2以外の分潮
表-C.2の列に記載した計算式によって求める。

pahooCalendar.php

2963: /**
2964:  * fi, uiを求める.
2965:  * @param   string $i  分潮記号
2966:  * @param   string $fn 'fi' または 'ui'
2967:  * @return  float 計算結果
2968: */
2969: function fiui($i, $fn) {
2970:     //L2 or M1
2971:     if (($i == 'L2'|| ($i == 'M1')) {
2972:         $res = (float)$this->fiui_L2M1($i, $fn);
2973: 
2974:     //fi
2975:     } else if ($fn == 'fi') {
2976:         //表-C.1による計算
2977:         if (isset($this->c1[$i])) {
2978:             $res = (float)$this->c1[$i][0]
2979:             + (float)$this->c1[$i][1* cos(deg2rad($this->_N))
2980:             + (float)$this->c1[$i][2* cos(2 * deg2rad($this->_N))
2981:             + (float)$this->c1[$i][3* cos(3 * deg2rad($this->_N));
2982:         //表-C.2の参照
2983:         } else {
2984:             $res = $this->c2[$i][5];
2985:             if (! is_numeric($res)) $res = (float)$this->fiui($res, $fn);
2986:         }
2987:     //ui
2988:     } else {
2989:         //表-C.1による計算
2990:         if (isset($this->c1[$i])) {
2991:             $res = (float)$this->c1[$i][4* sin(deg2rad($this->_N))
2992:             + (float)$this->c1[$i][5* sin(2 * deg2rad($this->_N))
2993:             + (float)$this->c1[$i][6* sin(3 * deg2rad($this->_N));
2994:         //表-C.2の参照
2995:         } else {
2996:             $res = (float)$this->c2[$i][6];
2997:             if (! is_numeric($res)) $res = (float)$this->fiui($res, $fn);
2998:         }
2999:     }
3000:     return $res;
3001: }

文字列として取得した計算式を評価し、結果の数値を出力するメソッド evalFiUi を用意した。

表-C.1、表-C.2

ダウンロードしたファイルを解凍して得られる "table_C1C2.xlsx" を元データとして、列区切りをタブ、行区切りを改行としたテキストファイル "_C1.txt" および "_C2.txt" をZIP圧縮ファイルに格納している。

pahooCalendar.php

2872: /**
2873:  * 表から配列へパラメータを格納する.
2874:  * @param   string $str パラメータ表
2875:  * @param   array  $arr パラメータを格納する配列
2876:  * @return  なし
2877: */
2878: function str2array($str, &$arr) {
2879:     $tok = strtok($str, "\n");
2880:     while ($tok !FALSE) {
2881:         $ss = trim($tok);
2882:         if ($ss == "")      continue;
2883:         $cols = preg_split("/\t/iu", $ss);
2884:         $key = trim($cols[0]);
2885:         if (mb_substr($key, 0, 1!self::COMMENT) {
2886:             for ($i = 1$i < count($cols); $i++) {
2887:                 if (mb_substr($cols[$i], 0, 1) == self::COMMENT)    break;      //コメントから行末まで無視
2888:                 $arr[$key][$i - 1] = trim($cols[$i]);
2889:             }
2890:         }
2891:         $tok = strtok("\n");
2892:     }
2893: }

PHPプログラムでは、ZIP圧縮ファイルからテキストを読み込み、計算しやすいように配列に格納する。
表-C.1 fi, Uiの係数
[海上保安庁(1992)]
分潮記号fiui
1cos Ncos 2Ncos 3Nsin Nsin 2Nsin 3N
Mm1.0000-0.13000.00130.00000.000.000.00
Mf1.04290.4135-0.00400.0000-23.742.68-0.38
O11.00890.1871-0.01470.001410.80-1.340.19
K11.00600.1150-0.00880.0006-8.860.68-0.07
J11.01290.1676-0.01700.0016-12.941.34-0.19
OO11.10270.65040.0317-0.0014-36.684.02-0.57
M21.0004-0.03730.00020.0000-2.140.000.00
K21.02410.28630.0083-0.0015-17.740.68-0.04
表-C.2 各分潮の補正係数、角速度など
[海上保安庁(1992),気象庁(1999)]
分潮記号shpcnfiuiσ
(deg/hour)
period
(hour)
Sa01000100.04106868765.8211
Ssa02000100.08213734382.9052
Mm10-100MmMm0.5443747661.3092
MSf2-2000M2-M21.0158958354.3671
Mf20000MfMf1.0980331327.8590
2Q1-4122701O1O112.854286228.0062
σ1-4302701O1O112.927139827.8484
Q1-3112701O1O112.398660926.8684
ρ1-33-12701O1O113.471514526.7231
O1-2102701O1O113.943035625.8193
MP1-230901M2M214.025172925.6681
M1-110901M1M114.492052124.8412
χ1-13-1901J1J114.569547624.7091
π10-2019311014.917864724.1321
P10-1027011014.958931424.0659
S100018011015.000000024.0000
K1010901K1K115.041068623.9345
ψ102016711015.082135323.8693
φ10309011015.123205923.8045
θ11-11901J1J115.512589723.2070
J111-1901J1J115.585443323.0985
SO12-10901J1J116.056964422.4202
OO1210901OO1OO116.139101722.3061
OQ2-5211802O1*Q1O1+Q127.341696413.1667
MNS2-54102M2^22*M227.423833713.1273
2N2-42202M2M227.895354812.9054
μ2-44002M2M227.968208412.8718
N2-32102M2M228.439729512.6583
ν2-34-102M2M228.512583112.6260
OP2-2001802O1*P1O1+P128.901966912.4559
M2-22002M2M228.984104212.4206
MKS2-24002M2*K2M2+K229.066241512.3855
λ2-1011802M2M229.455625312.2218
L2-12-11802L2L229.528478912.1916
T20-1028321029.958933312.0164
S2000021030.000000012.0000
R201025721030.041066711.9836
K202002K2K230.082137311.9672
MSN210-102M2^22*M230.544374711.7861
KJ212-11802K1*J1K1+J130.626512011.7545
2SM22-2002M2-M231.015895811.6070
MO3-4302703M2*O1M2+O142.92713988.3863
M3-3301803M2^1.51.5*M243.47615638.2804
SO3-2102703O1O143.94303568.1924
MK3-230903M2*K1M2+K144.02517298.1771
SK3010903K1K145.04106867.9927
MN4-54104M2^22*M257.42383376.2692
M4-44004M2^22*M257.96820846.2103
SN4-32104M2M258.43972956.1602
MS4-22004M2M258.98410426.1033
MK4-24004M2*K2M2+K259.06624156.0949
S4000041060.00000006.0000
SK402004K2K260.08213735.9918
2MN6-76106M2^33*M286.40793804.1663
M6-66006M2^33*M286.95231274.1402
MSN6-54106M2^22*M287.42383374.1179
2MS6-44006M2^22*M287.96820844.0924
2MK6-46006M2^2*K22*M2+K288.05034574.0886
2SM6-22006M2M288.98410424.0457
MSK6-24006M2*K2M2+K289.06624154.0419

Zi、Hi、ui の入手

前述の通り、 $ Z_o $、 $ H_i $、 $ u_i $ は観測地点の固有の値である。
これらの値は、気象庁の潮位表掲載地点一覧表から取得することができる。対象は、一覧表のうち、分潮一覧表の列のリンク先にコンテンツがあるもののみである。

これらのコンテンツを自動的に読み込んでゆき、各地点の分潮一覧表をZIP圧縮ファイルに格納していくプログラムが "makeTideDataFiles.php" である。

ある日の干潮、満潮時刻と潮位を求める

pahooTide::tide_level を繰り返すことで、指定地点のある日の干潮、満潮時刻と潮位を求めることができる。
干潮、満潮は、1日に各々最大4回起きる。1分ごとに潮位を計算し、上昇から下降へ転じる点が満潮時刻、その逆が干潮時刻である。だが、1分ごとに計算すると、1日で 60×24=1440回も計算しなければならない。
計算回数を減らすために、まず15分ごとに計算することで変化点を検出し、そこから±15分間を1分ごとに計算することで正確な時刻を求める。計算回数は、1日で4×24+15×8=216回に減らすことができる。

pahooCalendar.php

3120: /**
3121:  * 指定地点のある日の干潮,満潮時刻と潮位を求める.
3122:  * @param   string $code 地点記号
3123:  * @param   int $year, $month, $day グレゴリオ暦による年月日
3124:  * @param   array $items 結果を格納する配列
3125:  *              array('high'=>array('hh:mm',潮位), 'low'=>array('hh:mm',潮位))
3126:  * @return  TRUE/FALSE
3127: */
3128: function tide_day($code, $year, $month, $day, &$items) {
3129:     $this->setLocation($code);
3130:     if ($this->error)   return FALSE;
3131: 
3132:     $this->sun_moon($year, $month, $day);
3133: 
3134:     $interval = 15.0;           //第1段階刻み(分)
3135:     $td0 = $this->tide_level($year, $month, $day, self::JST, -$interval);
3136:     $flag = 0;
3137:     $cnt_high = 0;
3138:     $cnt_low = 0;
3139:     for ($i = 0$i <24 * 60$i +$interval) {
3140:         //第1段階刻み
3141:         $hh = floor($i / 60.0);
3142:         $mm = $i - $hh * 60.0;
3143:         $td1 = $this->tide_level($year, $month, $day, $hh + self::JST, $mm);
3144:         if ($flag == 0) {
3145:             if ($td1 > $td0)    $flag = +1;     //上昇
3146:             else                $flag = -1;     //下降
3147:         } else if ($flag < 0) {
3148:             if ($td1 > $td0) {                      //上昇へ転じた
3149:                 //第2段階刻み
3150:                 $td0 = $this->tide_level($year, $month, $day, $hh + self::JST, $mm - $interval - 1);
3151:                 for ($j = -$interval$j < $interval$j++) {
3152:                     $hh = floor(($i + $j) / 60.0);
3153:                     $mm = ($i + $j- $hh * 60.0;
3154:                     $td1 = $this->tide_level($year, $month, $day, $hh + self::JST, $mm);
3155:                     if ($td1 > $td0) {              //上昇へ転じた
3156:                         $items['low'][$cnt_low]['hhmm'] = sprintf("%02d:%02d", $hh, $mm);
3157:                         $items['low'][$cnt_low]['lev'] = (int)$td0;
3158:                         $cnt_low++;
3159:                         $flag = +1;
3160:                         break;
3161:                     }
3162:                     $td0 = $td1;
3163:                 }
3164:             }
3165:         } else {
3166:             if ($td1 < $td0) {                       //下降へ転じた
3167:                 //第2段階刻み
3168:                 $td0 = $this->tide_level($year, $month, $day, self::JST, $mm - $interval - 1);
3169:                 for ($j = -$interval$j < $interval$j++) {
3170:                     $hh = floor(($i + $j) / 60.0);
3171:                     $mm = ($i + $j- $hh * 60.0;
3172:                     $td1 = $this->tide_level($year, $month, $day, $hh + self::JST, $mm);
3173:                     if ($td1 < $td0) {               //下降へ転じた
3174:                         $items['high'][$cnt_high]['hhmm'] = sprintf("%02d:%02d", $hh, $mm);
3175:                         $items['high'][$cnt_high]['lev'] = (int)$td0;
3176:                         $cnt_high++;
3177:                         $flag = -1;
3178:                         break;
3179:                     }
3180:                     $td0 = $td1;
3181:                 }
3182:             }
3183:         }
3184:         $td0 = $td1;
3185:     }
3186:     return TRUE;
3187: }

解説:各種定数

ここからは、サンプル・プログラム本体 "tide.php" の解説をしてゆく。

tide.php

  58: // jqPlotのあるフォルダ
  59: define('JQPLOT', '../../../../common/jqplot/');
  60: 
  61: // 月の満ち欠け画像ファイルの場所
  62: // 画像作成方法→https://www.pahoo.org/e-soul/webtech/phpgd/phpgd-17-01.shtm
  63: define('MOONAGE', './moon/');
  64: 
  65: // 潮位表:計算期間(日)の初期値
  66: define('INTERVAL_DEF', 7);
  67: 
  68: // 潮位グラフ:計算期間(日)の初期値
  69: define('INTERVAL_GRAPH_DEF', 5);
  70: 
  71: // 潮位グラフのプロット間隔(日)
  72: define('INTERVAL_GRAPH', 0.02);
  73: 
  74: // 潮位グラフの名前
  75: define('GRAPH_TIDE', 'jqPlot_tide');

これらの定数は自由に変更できる。
ただし、冒頭に述べたとおり計算量が大きいので、INTERVAL_DEFINTERVAL_GRAPH_DEF は、あまり大きな値にしない方がいいだろう。

潮位グラフを描くために、jQueryプラグイン「jqPlot」を利用する。使い方については、「PHPでNHK政治意識月例調査をグラフ表示」を参照のこと。定数 JQPLOT の示すディレクトリに配置する。
また、月齢を示す月の画像は、定数 MOONAGE の示すディレクトリに配置する。圧縮ファイルに含まれている画像は、「PHPで月の満ち欠けを描画」で作成したモノだ。

解説:地点セレクタ

tide.php

 360: /**
 361:  * 地点セレクタを作成する.
 362:  * @param   pahooTide $pt  潮位計算クラス
 363:  * @param   string $code 地点記号
 364:  * @param   string $errmsg エラーメッセージ格納用;エラーなければ空文字
 365:  * @return  string HTML/FALSE:失敗
 366: */
 367: function makeJSlocation($pt, $code) {
 368:     static $prefs = array(
 369: '--選択--','北海道', '青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県', 
 370: '茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県', 
 371: '新潟県', '富山県', '石川県', '福井県', '山梨県', '長野県', '岐阜県', '静岡県', 
 372: '愛知県', '三重県', '滋賀県', '京都府', '大阪府', '兵庫県', '奈良県',
 373: '和歌山県', '鳥取県', '島根県', '岡山県', '広島県', '山口県', '徳島県',
 374: '香川県', '愛媛県', '高知県', '福岡県', '佐賀県', '長崎県', '熊本県', '大分県', 
 375: '宮崎県', '鹿児島県', '沖縄県');
 376: 
 377:     if ($code == '') {
 378:         $pp = '';
 379:     } else if (! isset($pt->index[$code])) {
 380:         $errmsg = "not exist location '{$code}'";
 381:         return FALSE;
 382:     } else {
 383:         $pp = $pt->index[$code][4];
 384:     }
 385: 
 386:     // 都道府県
 387:     $js = "var pref = [\n";
 388:     foreach ($prefs as $key=>$val) {
 389:         $selected = ($val == $pp? 'selected' : '';
 390:         $js ."{'type':{$key}, 'text':'{$val}', 'value':'{$val}', 'selected':'{$selected}'},\n";
 391:     }
 392:     // 地点
 393:     $js ."];\nvar loc = [\n";
 394:     foreach ($prefs as $key=>$val) {
 395:         $js ."{'type':{$key}, 'text':'--選択--', 'value':''},\n";
 396:         foreach ($pt->index as $cd=>$arr) {
 397:             if ($arr[4] == $val) {
 398:                 $selected = ($cd == $code? 'selected' : '';
 399:                 $js ."{'type':{$key}, 'text':'{$arr[0]}', 'value':'{$cd}', 'selected':'{$selected}'},\n";
 400:             }
 401:         }
 402:     }
 403: 
 404:     // スクリプト
 405:     $js .=<<< EOT
 406: ];
 407: 
 408: $(function() {
 409:     var html = '';
 410:     for (var i in pref) {
 411:         html += '<option value="'+ pref[i].value + '" ' + pref[i].selected + '>'+ pref[i].text +'</option>';
 412:     }
 413:     $('#pref').append(html);
 414: 
 415:     var html = '';
 416:     var index = $('#pref option:selected').index();
 417:     for (var i in loc) {
 418:         if (loc[i].type == index) {
 419:             html += '<option value="'+ loc[i].value + '" ' + loc[i].selected + '>' + loc[i].text +'</option>';
 420:         }
 421:     }
 422:     $('#code').append(html);
 423: 
 424:     // 都道府県を選択した時の挙動
 425:     $('#pref').change(function() {
 426:         var html = '';
 427:         var index = $('#pref option:selected').index();
 428:         // 地点の内容を削除
 429:         $('#code').empty();
 430:         // 対応する地点の内容を表示
 431:         for (var i in loc) {
 432:             if (loc[i].type == index) {
 433:                 html += '<option value="'+ loc[i].value +'">'+ loc[i].text +'</option>';
 434:             }
 435:         }
 436:         $('#code').append(html);
 437:     });
 438: });
 439: 
 440: EOT;
 441:     return $js;
 442: }

観測地点は、都道府県のセレクタと、観測地点のセレクタによって選択する。
観測地点セレクタを都道府県セレクタに連動させるために、jQuery を利用した。

解説:潮位表の計算と作成

tide.php

 445: /**
 446:  * 潮位表:潮位・月齢を計算し,配列へ格納する.
 447:  * @param   pahooTide $pt  潮位計算クラス
 448:  * @param   pahooCalendar $pc  暦計算クラス
 449:  * @param   array  $inputs 計算用パラメータ
 450:  * @param   array  $items  計算結果格納用配列
 451:  * @param   array  $locs   地点情報格納用配列
 452:  * @param   string $errmsg エラーメッセージ格納用;エラーなければ空文字
 453:  * @return  bool TRUE:計算成功/FALSE:失敗
 454: */
 455: function calcTideTable($pt, $pc, $params, &$items, &$locs, &$errmsg) {
 456:     // 地点を設定
 457:     $pt->setLocation($params['code']);
 458:     if ($pt->iserror()) return FALSE;
 459: 
 460:     // 地点を取得
 461:     $pt->getLocation($params['code'], $locs);
 462:     if ($pt->iserror()) return FALSE;
 463: 
 464:     // 月齢・潮位を計算
 465:     $res = TRUE;
 466:     $jd = $pc->Gregorian2JD($params['year'], $params['month'], $params['day'], 0, 0, 0);
 467:     for ($i = 0$i < $params['interval']; $i++) {
 468:         $arr = array();
 469:         list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($jd);
 470:         $yb = $pc->getWeekString($year, $month, $day);
 471:         $items[$i]['dt'] = sprintf("%04d-%02d-%02d(%s)", $year, $month, $day, $yb);
 472:         $items[$i]['moonage'] = $pc->moon_age($year, $month, $day, MOONAGE_HOUR, 0, 0);
 473:         $moonmeridian = $pc->moon_time(2, $params['longitude'], $params['latitude'], $params['height'], $year, $month, $day);
 474:         $moonmeridian = ($moonmeridian == FALSE? '---' : $pc->day2hhmm($moonmeridian);
 475:         $items[$i]['tide'] = (preg_match('/([0-9]+)\:([0-9]+)/', $moonmeridian, $arr> 0? $pc->tide($year, $month, $day, $arr[1], $arr[2], 0: '';
 476:         unset($arr);
 477: 
 478:         $arr = array();
 479:         $pt->tide_day($params['code'], $year, $month, $day, $arr);
 480:         // 満潮
 481:         for ($j = 0$j < 4$j++) {
 482:             if (isset($arr['high'][$j])) {
 483:                 $items[$i]['high'][$j]['hhmm']  = $arr['high'][$j]['hhmm'];
 484:                 $items[$i]['high'][$j]['lev']   = sprintf("%d", $arr['high'][$j]['lev']);
 485:                 $items[$i]['high'][$j]['align'] = 'text-align:right;';
 486:             } else {
 487:                 $items[$i]['high'][$j]['hhmm']  = '*';
 488:                 $items[$i]['high'][$j]['lev']   = '*';
 489:                 $items[$i]['high'][$j]['align'] = 'text-align:center;';
 490:             }
 491:         }
 492:         // 干潮
 493:         for ($j = 0$j < 4$j++) {
 494:             if (isset($arr['low'][$j])) {
 495:                 $items[$i]['low'][$j]['hhmm']  = $arr['low'][$j]['hhmm'];
 496:                 $items[$i]['low'][$j]['lev']   = sprintf("%d", $arr['low'][$j]['lev']);
 497:                 $items[$i]['low'][$j]['align'] = 'text-align:right;';
 498:             } else {
 499:                 $items[$i]['low'][$j]['hhmm']  = '*';
 500:                 $items[$i]['low'][$j]['lev']   = '*';
 501:                 $items[$i]['low'][$j]['align'] = 'text-align:center;';
 502:             }
 503:         }
 504:         $jd++;
 505:     }
 506: 
 507:     return $res;
 508: }

計算開始年月日と期間を指定して、tide::tide_day を繰り返し実行して満潮・干潮を計算してゆくのがユーザー関数 calcTideTable である。入力パラメータが多いので、配列で渡す。
月齢・潮の計算は、「PHPで日出没・月出没・月齢・潮を計算」で紹介したとおりである。

tide.php

 510: /**
 511:  * 潮位表:表示用HTMLを作成する.
 512:  * @param   pahooTide     $pt      潮位計算クラス
 513:  * @param   pahooCalendar $pc      暦計算クラス
 514:  * @param   array         $params  計算用パラメータ
 515:  * @param   string        $js      スクリプト格納用
 516:  * @param   string        $html    HTML格納用
 517:  * @param   string        $errmsg  エラーメッセージ格納用
 518:  * @return  bool TRUE:成功/FALSE:失敗
 519:  * @return  表示用HTML
 520: */
 521: function makeTable($pt, $pc, $params, &$js, &$html, &$errmsg) {
 522:     $js = $html = '';
 523:     $items = array();
 524:     $locs  = array();
 525:     if (calcTideTable($pt, $pc, $params, $items, $locs, $errmsg) == FALSE)  return FALSE;
 526: 
 527:     $hour = MOONAGE_HOUR;
 528:     $html =<<< EOT
 529: <table class="tide">
 530: <caption>{$locs['title']}({$locs['prefecture']}{$locs['address']})</caption>
 531: <tr><th rowspan="2">年月日</th><th rowspan="2">月齢<br /><span style="font-size:small; font-weight:normal;">{$hour}時</span></th><th rowspan="2">潮</th><th colspan="8">満潮</th><th colspan="8">干潮</th></tr>
 532: 
 533: EOT;
 534:     $html ."<tr>";
 535:     for ($j = 0$j < 8$j++) {
 536:         $html ."<th class=\"index\">時刻</th><th class=\"index\">潮位<br />(cm)</th>";
 537:     }
 538:     $html ."</tr>\n";
 539:     foreach ($items as $item) {
 540:         $html .sprintf("<tr><td>%s</td><td>%.1f</td><td>%s</td>", $item['dt'], $item['moonage'], $item['tide']);
 541:         for ($j = 0$j < 4$j++) {
 542:             $html .sprintf("<td>%s</td><td style=\"%s\">%s</td>", $item['high'][$j]['hhmm'], $item['high'][$j]['align'], $item['high'][$j]['lev']);
 543:         }
 544:         for ($j = 0$j < 4$j++) {
 545:             $html .sprintf("<td>%s</td><td style=\"%s\">%s</td>", $item['low'][$j]['hhmm'], $item['low'][$j]['align'], $item['low'][$j]['lev']);
 546:         }
 547:         $html ."</tr>\n";
 548:     }
 549:     $html ."</table>\n";
 550: 
 551:     return TRUE;
 552: }

calcTideTable を呼び出し、ブラウザ上に潮位表を表示するためのユーザー関数が makeTable である。

解説:潮位グラフの計算と作成

tide.php

 554: /**
 555:  * 潮位グラフ:潮位・月齢を計算し,配列に格納する.
 556:  * @param   pahooTide $pt  潮位計算クラス
 557:  * @param   pahooCalendar $pc  暦計算クラス
 558:  * @param   array  $params 計算用パラメータ
 559:  * @param   array  $items  潮位計算結果格納用配列
 560:  * @param   array  $moons  月齢計算結果格納用配列
 561:  * @param   array  $locs   地点情報格納用配列
 562:  * @param   string $errmsg エラーメッセージ格納用;エラーなければ空文字
 563:  * @return  bool TRUE:計算成功/FALSE:失敗
 564: */
 565: function calcTideGraph($pt, $pc, $params, &$items, &$moons, &$locs, &$errmsg) {
 566:     // 地点を設定
 567:     $pt->setLocation($params['code']);
 568:     if ($pt->iserror()) return FALSE;
 569: 
 570:     // 地点を取得
 571:     $pt->getLocation($params['code'], $locs);
 572:     if ($pt->iserror()) return FALSE;
 573: 
 574:     $res = TRUE;
 575: 
 576:     // 潮位を計算
 577:     $jd = $pc->Gregorian2JD($params['year'], $params['month'], $params['day'], 0, 0);
 578:     $n = $params['interval'] / INTERVAL_GRAPH;
 579:     for ($i = 0$i < $n$i++) {
 580:         list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($jd);
 581:         $items[$i]['dt'] = sprintf("%04d/%02d/%02d %02d:%02d", $year, $month, $day, $hour, $min);
 582:         list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($jd);
 583:         $pt->sun_moon($year, $month, $day);
 584:         $items[$i]['tlevel'] = $pt->tide_level($year, $month, $day, $hour, $min);
 585:         $jd +INTERVAL_GRAPH;
 586:     }
 587: 
 588:     // 月齢を計算
 589:     $height = 0;        // 標高0メートルを仮定
 590:     $n = $params['interval'];
 591:     $jd = $pc->Gregorian2JD($params['year'], $params['month'], $params['day'], 0, 0, 0);
 592:     $i = $d = 0;
 593:     while ($i < $n) {
 594:         // 月の南中時刻
 595:         list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($jd);
 596:         $moonmeridian = $pc->moon_time(2, $params['longitude'], $params['latitude'], $height, $year, $month, $day);
 597:         if ($moonmeridian !FALSE) {
 598:             $x = (double)$i + $moonmeridian;
 599:             $moons[$i]['t']   = $d + $moonmeridian;
 600:             $moons[$i]['age'] = $pc->moon_age($year, $month, $day, $moonmeridian * 24, 0, 0);
 601:             $i++;
 602:         }
 603:         $jd++;
 604:         $d++;
 605:     }
 606: 
 607:     return $res;
 608: }

ユーザー関数 calcTideGraph はメソッド pahooTide::tide_level を呼び出し、指定日時における潮位を配列 $items に格納してゆく。この配列を、「PHPでNHK政治意識月例調査をグラフ表示」で紹介したjQueryプラグイン「jqPlot」を用いてグラフ表示する。

月齢は、月の画像をグラフ上にオーバーライトすることを目指す。プロットする場所は、月の南中時刻とすべく、メソッド pahooCalendar::moon_time によって南中時刻を求める。

tide.php

 610: /**
 611:  * 潮位グラフ:表示用スクリプトおよびHTMLを求める.
 612:  * @param   pahooTide     $pt      潮位計算クラス
 613:  * @param   pahooCalendar $pc      暦計算クラス
 614:  * @param   array         $params  計算用パラメータ
 615:  * @param   string        $js      スクリプト格納用
 616:  * @param   string        $html    HTML格納用
 617:  * @param   string        $errmsg  エラーメッセージ格納用
 618:  * @return  bool TRUE:成功/FALSE:失敗
 619: */
 620: function makeGraph($pt, $pc, $params, &$js, &$html, &$errmsg) {
 621:     $js = $html = '';
 622:     $items = array();
 623:     $moons = array();
 624:     $locs  = array();
 625:     if (calcTideGraph($pt, $pc, $params, $items, $moons, $locs, $errmsg) == FALSE)  return FALSE;
 626: 
 627:     $title = "{$locs['title']}({$locs['prefecture']}{$locs['address']})";
 628:     $name   = GRAPH_TIDE;
 629:     $width  = MAP_WIDTH;
 630:     $height = MAP_HEIGHT;
 631: 
 632:     // 潮位プロット・データ作成
 633:     $data = '';
 634:     foreach ($items as $item) {
 635:         $data .sprintf("['%s', %f], ", $item['dt'], $item['tlevel']);
 636:     }
 637: 
 638:     // 月齢データ作成
 639:     $moon = '';
 640:     $path = MOONAGE;
 641:     foreach ($moons as $val) {
 642:         $x = round(30 + (MAP_WIDTH - 70) / $params['interval'* $val['t']);
 643:         $fname = sprintf("{$path}moon_%02d.png", round($val['age']));
 644:         $moon .=<<< EOT
 645: <img style="position:absolute; top:35px; left:{$x}px; width:40px; height:40px; z-index:999;" src="{$fname}" />
 646: 
 647: EOT;
 648:     }
 649: 
 650:     $js =<<< EOT
 651: $(function() {
 652:     jQuery.jqplot('{$name}',
 653:     [
 654:         [ {$data} ]
 655:     ],
 656:     {
 657:         // タイトル
 658:         title: {
 659:             text: '{$title}',
 660:             show: true,
 661:             fontSize: '16px',
 662:             textAlign: 'center',
 663:             textColor: 'black'
 664:         },
 665:         // 背景
 666:         grid: {
 667:             background: '#EEFFFF'
 668:         },
 669:         // グラフ
 670:         seriesDefaults: {
 671:             showLine: true,
 672:             rendererOptions: { smooth: false },
 673:             markerOptions: { size: 0 },
 674:             color: 'blue',
 675:         },
 676:         // 軸ラベル
 677:         axes: {
 678:             xaxis: {
 679:                 renderer: $.jqplot.DateAxisRenderer,
 680:                 tickOptions: { formatString: '%m/%d' },
 681:                 tickInterval: '1 days'
 682:             },
 683:             yaxis: {
 684:                 label: '潮位(cm)'
 685:             }
 686:         }
 687:     }
 688:     );
 689: });
 690: 
 691: EOT;
 692:     $html =<<< EOT
 693: <div id="{$name}" style="width:{$width}px; height:{$height}px;">
 694: {$moon}
 695: </div>
 696: </body>
 697: 
 698: EOT;
 699: 
 700:     return TRUE;
 701: }

calcTideGraph を呼び出し、ブラウザ上に潮位グラフを表示するためのユーザー関数が makeGraph である。
jqPlot に渡すスクリプトと、表示するHTMLは別々に生成する。jqPlot で画像を扱えないため、無理をしてHTML側でグラフ上にオーバーライトしている。

解説:メインプログラム

メインプログラムは、これまで紹介してきたものを流用している。
Googleマップの表示や住所検索については「PHPでGoogle等を利用して住所から緯度・経度を求める」を、UI:Datepicker を利用した日付入力については「PHPで日付入力:カレンダーから選択」を参照してほしい。

参考サイト

(この項おわり)
header