header

PHPで2地点間の直線距離を求める

(1/2)

今回は、日本語の住所から緯度経度を求めるプログラムの発展形として、2地点間の直線距離を求めるプログラムを作ってみることにする。

サンプル・プログラム

0001: <?php
0002: /** distance.php
0003:  * 2地点間の直線距離を求める(MapFanを利用)
0004:  *
0005:  * @copyright (c)studio pahoo
0006:  * @author     パパぱふぅ
0007:  * @version     1.0  2007/04/18
0008: */
0009: mb_internal_encoding("EUC-JP");
0010: 
0011: /**
0012:  * HTTP通信を行う
0013:  * @param string $url "http://" から始まるURL
0014:  * @param string $method GET,POST,HEAD (省略時はGET)
0015:  * @param string $headers その他の任意のヘッダ (省略時は"")
0016:  * @param array  $post POST変数を格納した連想配列("変数名"=>"値") (省略時はNULL)
0017:  * @param string $cookie Cookie(利用するときは常に$method="POST") (省略時は"")
0018:  * @return string 取得したコンテンツ/FALSE 取得エラー
0019: */
0020: function http($url$method="GET", $headers="", $post=NULL$cookie="") {
0021:     if ($cookie != "")    $method = "POST";
0022:     $URL = parse_url($url);
0023: 
0024:     $URL["query"] = isset($URL["query"]) ? $URL["query"] : "";   //クエリ
0025:     $URL["port"]  = isset($URL["port"])  ? $URL["port"]  : 80;    //ポート番号
0026: 
0027:     //リクエストライン
0028:     $request  = $method . " " . $URL["path"] . $URL["query"] . " HTTP/1.1\r\n";
0029: 
0030:     //リクエストヘッダ
0031:     $request .= "Host: " . $URL["host"] . "\r\n";
0032:     $request .= "User-Agent: PHP/" . phpversion() . "\r\n";
0033: 
0034:     //Basic認証用のヘッダ
0035:     if (isset($URL["user"]) && isset($URL["pass"])) {
0036:         $request .= "Authorization: Basic " . base64_encode($URL["user"] . ":" . $URL["pass"]) . "\r\n";
0037:     }
0038: 
0039:     //追加ヘッダ
0040:     $request .= $headers;
0041: 
0042:     //POSTの時
0043:     if (strtoupper($method) == "POST") {
0044:         while (list($name$value) = each($post)) {
0045:             $POST[] = $name . "=" . urlencode($value);
0046:         }
0047:         $postdata = implode("&", $POST);
0048:         $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
0049:         $request .= "Content-Length: " . strlen($postdata) . "\r\n";
0050:         if ($cookie != "")    $request .= "Cookie: $cookie\r\n";
0051:         $request .= "\r\n";
0052:         $request .= $postdata;
0053:     } else {
0054:         $request .= "\r\n";
0055:     }
0056: 
0057:     //接続
0058:     $fp = fsockopen($URL["host"]$URL["port"]);
0059:     //エラー処理
0060:     if (!$fp)    return FALSE;
0061: 
0062:     //リクエスト送信
0063:     fputs($fp$request);
0064: 
0065:     //応答データ受信
0066:     $response = "";
0067:     while (!feof($fp)) $response .= fgets($fp);
0068: 
0069:     fclose($fp);
0070: 
0071:     return $response;
0072: }
0073: 
0074: /**
0075:  * 住所から緯度経度を求める(MapFan利用)
0076:  * @param string $address 住所(EUC-JP)
0077:  * @return string 緯度経度/NULL エラー
0078: */
0079: function address2geo($address) {
0080:     $url = "http://www.mapfan.com/keywordsrch.cgi";        //mapfan
0081:     $method = "POST";
0082:     $post["SRCHKIND"] = "SRCH_ADR";
0083:     $post["PARAM"] = $address;
0084: 
0085:     $result = http($url$method, "", $post);
0086:     preg_match("/(Location: http)(.*)/", $result$arr);
0087:     if (isset($arr[2]) == FALSE)    return NULL;
0088: 
0089:     $url = "http" . $arr[2];
0090:     $result = file_get_contents($url);
0091: 
0092:     //緯度経度
0093:     preg_match("/var MAP = \"([A-Z|0-9|\.]*)\"/", $result$arr);
0094: 
0095:     return isset($arr[1]) ? $arr[1] : NULL;
0096: }
0097: 
0098: /**
0099:  * 2地点間の直線距離を求める(Hubenyの簡易式による;日本測地系!)
0100:  * @param string $a A地点の緯度経度(日本語またはExxx.xx.xxNxx.xx.xx表記)
0101:  * @param string $b B地点の緯度経度(日本語またはExxx.xx.xxNxx.xx.xx表記)
0102:  * @return double 直線距離(メートル)
0103: */
0104: function calc_distance($a$b) {
0105:     //A地点の経度・緯度を小数に
0106:     preg_match("/E(\d+)\.(\d+)\.(\d+)\.(\d+)N(\d+)\.(\d+)\.(\d+)\.(\d+)/", $a$regs);
0107:     $a_long = $regs[1] + $regs[2] / 60 + $regs[3] / 3600 + $regs[4] / 36000;
0108:     $a_lati = $regs[5] + $regs[6] / 60 + $regs[7] / 3600 + $regs[8] / 36000;
0109: 
0110:     //B地点の経度・緯度を小数に
0111:     preg_match("/E(\d+)\.(\d+)\.(\d+)\.(\d+)N(\d+)\.(\d+)\.(\d+)\.(\d+)/", $b$regs);
0112:     $b_long = $regs[1] + $regs[2] / 60 + $regs[3] / 3600 + $regs[4] / 36000;
0113:     $b_lati = $regs[5] + $regs[6] / 60 + $regs[7] / 3600 + $regs[8] / 36000;
0114: 
0115:     //ラジアンに変換
0116:     $a_long = deg2rad($a_long);
0117:     $a_lati = deg2rad($a_lati);
0118:     $b_long = deg2rad($b_long);
0119:     $b_lati = deg2rad($b_lati);
0120: 
0121:     $latave = ($a_lati + $b_lati) / 2;
0122:     $latidiff = $a_lati - $b_lati;
0123:     $longdiff = $a_long - $b_long;
0124: 
0125:     //子午線曲率半径
0126:     $meridian = 6334834 / sqrt(pow(1 - 0.006674 * sin($latave) * sin($latave), 3));
0127:     //卯酉線曲率半径
0128:     $primevertical = 6377397 / sqrt(1 - 0.006674 * sin($latave) * sin($latave));
0129: 
0130:     //Hubenyの簡易式
0131:     $x = $meridian * $latidiff;
0132:     $y = $primevertical * cos($latave) * $longdiff;
0133: 
0134:     return sqrt($x * $x + $y * $y);
0135: }
0136: 
0137: // メイン・プログラム =======================================================
0138: // パラメータのリセット
0139: if (isset($_POST['reset'])) {
0140:     $addressA = '';
0141:     $addressB = '';
0142:     $distance = '';
0143: 
0144: // 距離計算
0145: else if (isset($_POST['exec'])) {
0146:     $addressA = htmlspecialchars($_POST['addressA']);
0147:     $addressB = htmlspecialchars($_POST['addressB']);
0148: 
0149:     //入力は経度・緯度表記も可能に
0150:     $geoA = (preg_match("/E(\d+)\.(\d+)\.(\d+)\.(\d+)N(\d+)\.(\d+)\.(\d+)\.(\d+)/", $addressA) == 0) ? address2geo($addressA) : $addressA;
0151:     $geoB = (preg_match("/E(\d+)\.(\d+)\.(\d+)\.(\d+)N(\d+)\.(\d+)\.(\d+)\.(\d+)/", $addressB) == 0) ? address2geo($addressB) : $addressB;
0152: 
0153:     if ($geoA == NULL) {
0154:         $distance = "A地点の住所が正しくありません";
0155:     } else if ($geoB == NULL) {
0156:         $distance = "B地点の住所が正しくありません";
0157:     } else {
0158:         $dd = calc_distance($geoA$geoB) / 1000;
0159:         $distance = sprintf("%8.2f キロメートル", $dd);
0160:     }
0161: 
0162: // パラメータ入力
0163: else {
0164:     $addressA = isset($_POST['addressA']) ? $_POST['addressA'] : "";
0165:     $addressB = isset($_POST['addressB']) ? $_POST['addressB'] : "";
0166:     $distance = isset($_POST['distance']) ? $_POST['distance'] : "";
0167: }
0168: 
0169: 
0170: // 表示処理 ==================================================================
0171: echo <<< EOF
0172: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
0173:  "http://www.w3.org/TR/html4/loose.dtd">
0174: <html lang="ja">
0175: <head>
0176: <meta http-equiv="Content-Type" content="text/html; charset=EUC-JP" />
0177: <title>2地点間の直線距離を求める</title>
0178: <meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
0179: </head>
0180: <body>
0181: <h1>■2地点間の直線距離を求める</h1>
0182: <form name="myForm" method="post" enctype="multipart/form-data" action="$myself">
0183: <table border="0" cellspacing="10">
0184: <tr>
0185: <td>A地点</td>
0186: <td><input type="text" name="addressA" size="80" value="$addressA" /></td>
0187: </tr>
0188: <tr>
0189: <td>B地点</td>
0190: <td><input type="text" name="addressB" size="80" value="$addressB" /></td>
0191: </tr>
0192: <tr>
0193: <td>A〜B間の直線距離</td>
0194: <td>$distance</td>
0195: </tr>
0196: <tr>
0197: <td>&nbsp;</td>
0198: <td>
0199: <input type="submit" name="exec" value="計算" /> 
0200: <input type="submit" name="reset" value="リセット" />
0201: </td>
0202: </tr>
0203: <tr>
0204: <td colspan="2">
0205: <hr />
0206: <b>【使い方】</b>
0207: <ol>
0208: <li>[<b>A地点</b>],[<b>B地点</b>] に住所(都道府県名から番地・号まで)または緯度経度を入力してください。
0209: <br />ただし、住所に漢数字は使えません。
0210: <br />緯度経度は E139.45.56.9N35.41.0.7 (東経 139度45分56.9秒、北緯 35度41分0.7秒)のように入力してください。</li>
0211: <li>[<b>計算</b>] ボタンを押してください。</li>
0212: <li>2地点間の直線距離(キロメートル)が [<b>A〜B間の直線距離</b>] に表示されます。</li>
0213: <li>[<b>リセット</b>] ボタンを押すと、表示がクリアされます。</li>
0214: </ol>
0215: </td>
0216: </tr>
0217: </table>
0218: </form>
0219: </body>
0220: </html>
0221: EOF;
0222: ?>

プログラムを実行する

(この項つづく)