路線・駅検索をPHPで実装する方法解説。GoogleMapsの緯度経度から計算し検索
2018/09/04
路線・駅検索をPHPで実装する方法を解説
 
路線・駅検索を利用するシステムはいっぱいあります。
 
例えば Webサイトでは、求人検索サイトや、不動産の物件検索サイト、飲食店検索サイトなどです。
路線、駅を選択し、駅から 5分、10分の距離にある企業や物件、店舗などを検索するシステムです。
 
その路線・駅検索のシステムをより簡単に実装できないか、ということを考えました。
 
 
一般的な路線・駅検索の仕組み
 
一般的な路線・駅検索の仕組みは、検索対象となるスポット(求人企業の事務所の位置、不動産物件の位置など)の「最寄りとなる駅」と「駅からの距離(もしくは、徒歩でかかる時間)」を登録しておきます。
最寄りの駅が複数ある場合は、複数の駅を登録しておきます。
 
そして、検索をする際には、その登録している駅と駅からの距離の情報を対象に検索を実行し、結果を処理します。
 
 
一般的な路線・駅検索の問題点
 
ですが、路線・駅検索は結構ハードルが高いのです。
かかるコストも高いのです。
 
なぜなら、そもそも、路線、駅のデータが公的な情報としてまとまって提供されているわけではないためです。
また、路線、駅のデータは相応に膨大な量になり、かつ、駅や路線は増えたり減ったりすることがあるからです。
 
そして、もう一つの根本的な問題点として、鉄道には駅で電車を乗り換える、ということがある点です。
 
 
例えば、日本一の乗降客がある新宿駅。
新宿駅には多くの路線が乗り入れています。
JR、京王線、小田急線、東京メトロ丸の内線、都営地下鉄大江戸線などです。
また、最初に JRと書きましたが、JRの中にも山手線、中央線、総武線各駅などなど複数路線があります。
 
また、「新宿駅」という名前ではないものの、実際には乗換駅である場合もあります。
東京メトロ丸の内線、副都心線の新宿三丁目駅、都営大江戸線の新宿西口駅などです。
 
さらに、西武新宿線の「新宿駅」は、「新宿駅」という名前が付いていますが、都営大江戸線の新宿西口駅よりも遠くにあります。
乗り換えには、屋根がない地上を数百メートルほど歩く必要があり(横断歩道などで大通りを通る必要などもあります)、同じ新宿駅としていいのか、という疑問がわきます。
 
同じように、同じ駅名なのに離れた場所にある駅や、違う駅名なのにすぐ近くの駅であったり、ということが全国にあるのです。
 
どの駅を乗換駅とするか、しないか、それをどう判断するのかは非常に煩雑なのです。
 
 
駅・路線検索の問題点の一つの解決策
 
そのため、一般的な最寄りの駅を登録する方法を止め、指定された駅と、検索対象となるスポットの緯度経度を取得し、そこから 2点間の距離を計算し、それに基づいて条件抽出したらいいんじゃないか、という発想をしたわけです。
(まぁ、この発想に基づいて処理をする APIがすでにあるようなので、特段新しい発想ではありませんが...)
 
 
ただし、この方法の問題点は、駅からの距離は分かるものの、駅から徒歩で何分かかるのかが分からない、ということです。
駅から 500mであったとしても、直線で行ける場合と、遠回りしないとたどり着けない場合とあり、どれだけかかるかが分からないという点です。
 
また、駅の場所として処理される緯度経度は、駅の中の特定の場所で、駅の出口からの距離ではないところも問題点となりえます。
 
 
もっとも、一般的な表示である「駅から徒歩 5分」と書かれていても、坂道や信号待ちの時間などは考慮されていませんし、そもそも地下鉄や大きな駅など、ホームから駅の出口までに 10分ほどかかるような駅もありますので、「駅から徒歩 5分」と書かれていても、電車を降りてからどれくらい時間がかかるのかを正確に示したものではない、ということになります。
 
それを考えると、「どういう計算で算出している情報なのか」さえ明記しておけばいいんじゃないか、と考えました。
(一般的には道のりで時間を出していますので、誤解は発生しやすいかとは思いますが。)
 
 
路線・駅の情報は「駅データ.jp」を利用
今回紹介している路線・駅のデータは、下記の「駅データ.jp」さんのデータを利用しています。
「駅データ.jp」さんが路線、駅のデータ、および、検索の APIを提供してくださっていますので、このサービスが提供されている間は駅、路線のデータは容易に取得することができるでしょう。
システムをより簡易に作成したい場合は、APIを利用すると便利でしょう。
 https://www.ekidata.jp/
 
 
路線・駅検索のサンプルプログラム
路線・駅検索のサンプルプログラムの使い方
 
路線・駅検索のサンプルプログラムを下記にアップしました。
 https://s-giken.info/station_search/station_search.php
 
 
上記のような入力画面があり、都道府県、路線、駅を選択し、そこからの距離を数値で入力すると、指定の距離以内にある駅の一覧が表示されます。
 
普通は、不動産の物件や、求人企業の事務所、飲食店などの場所を検索するものだと思いますが、サンプル用にそれらの物件の位置情報のデータベースを構築することが現実的ではなかったため、物件情報の代わりに「駅データ.jp」から取得した駅の情報を検索対象の物件としても利用しています。
 
そのため、駅から駅を検索するという一般的には見ない路線・駅検索システムが出来上がっています(笑)。
 
 
路線・駅検索のサンプルプログラムのテーブルの構成
 
テーブルは、Company(事業者データ)、Line(路線データ)、Station(駅データ)の 3つで構成されていまして、それぞれ以下の構成をしています。
 
「駅データ.jp」の csvファイルをそのまま使うことを目的としましたので、「駅データ.jp」の構成と同じです。
 
【Company(事業者データ)】
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13  | 
						CREATE TABLE IF NOT EXISTS `company` (   `company_cd` int(11) NOT NULL,   `rr_cd` int(11) NOT NULL,   `company_name` text NOT NULL,   `company_name_k` text,   `company_name_h` text,   `company_name_r` text,   `company_url` text,   `company_type` char(1) DEFAULT NULL,   `e_status` char(1) DEFAULT NULL,   `e_sort` int(11) DEFAULT NULL,   UNIQUE KEY `company_cd` (`company_cd`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  | 
					
 
【Line(路線データ)】
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  | 
						CREATE TABLE IF NOT EXISTS `line` (   `line_cd` int(11) NOT NULL,   `company_cd` int(11) NOT NULL,   `line_name` text NOT NULL,   `line_name_k` text,   `line_name_h` text,   `line_color_c` text,   `line_color_t` text,   `line_type` char(1) DEFAULT NULL,   `lon` decimal(9,6) DEFAULT NULL,   `lat` decimal(9,6) DEFAULT NULL,   `zoom` char(2) DEFAULT NULL,   `e_status` char(1) DEFAULT NULL,   `e_sort` int(11) DEFAULT NULL,   UNIQUE KEY `line_cd` (`line_cd`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  | 
					
 
【Station(駅データ)】
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  | 
						CREATE TABLE IF NOT EXISTS `station` (   `station_cd` int(11) NOT NULL,   `station_g_cd` int(11) NOT NULL,   `station_name` text NOT NULL,   `station_name_k` text,   `station_name_r` text,   `line_cd` int(11) NOT NULL,   `pref_cd` int(11) DEFAULT NULL,   `post` varchar(10) DEFAULT NULL,   `address` text,   `lon` decimal(9,6) DEFAULT NULL,   `lat` decimal(9,6) DEFAULT NULL,   `open_ymd` char(10) DEFAULT NULL,   `close_ymd` char(10) DEFAULT NULL,   `e_status` char(1) DEFAULT NULL,   `e_sort` int(11) DEFAULT NULL,   UNIQUE KEY `station_cd` (`station_cd`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  | 
					
 
 
路線・駅検索のサンプルプログラムの駅を検索する SQL文
 
そして、一番重要な検索する際の SQLは以下のようになります。
 
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  | 
						select   a.company_name,    b.line_name,    c.station_name,    c.pref_cd,    c.address,    c.lon,    c.lat,    sqrt ( pow ( ( c.lat - 【駅の緯度】 ) * 【緯度1°の距離】, 2 ) + pow ( ( c.lon - 【駅の経度】 ) * 【経度1°の距離】, 2 ) ) as d from   company as a,    line as b,    station as c  where   c.line_cd = b.line_cd and    b.company_cd = a.company_cd and    ( 【入力の距離】 > sqrt ( pow ( ( c.lat - 【駅の緯度】 ) * 【緯度1°の距離】, 2 ) + pow ( ( c.lon - 【駅の経度】 ) * 【経度1°の距離】, 2 ) ) ) order by d  | 
					
 
「【入力の距離】」は、入力された値です。
「【駅の緯度】」「【駅の経度】」は、選択された「駅」の情報から「駅データ.jp」の APIを利用して、算出した値を編集します。
 
計算式を含む SQL文ですのでちょっと長いですが、SQL文としては非常にシンプルな形式です。
 
 
また、「【緯度1°の距離】」「【経度1°の距離】」は、あらかじめ計算をして算出をしておきます。
 
詳細な解説は、2点間の緯度経度から距離を計算する記事「路線・駅検索のために緯度経度からPHPで2点間の距離を計算する処理解説」を読んでいただければ、と思いますが、計算式は以下のようになります。
単位は「m」です。
 
| 
					 1 2 3  | 
						【緯度1°の距離】=【極半径】 × 2 × π ÷ 360 × 1000 【経度1°の距離】= cos ( 【駅の緯度】 ÷ 180 × π ) × 【赤道半径】 × 2 × π ÷ 360 × 1000  | 
					
 
 
路線・駅検索についてのまとめ
 
路線・駅検索は、作ってみると意外に簡単でしたね。
考え方もそれほど難しいものではないですので、路線・駅検索を必要としている方は、あなたのシステムにも組み込んでいけるんじゃないでしょうか。
 
ただ、緯度と経度がごっちゃになったり、すっかり忘れてしまっていた三角関数など、違うところで苦戦しました...
 
 
また、この記事は 3本で 1セットの内容です。
まず「2地点の緯度経度から距離を計算する方法を考察」し、2つ目のこの記事で、その 2点間の距離を計算する方法を基にして、「路線・駅検索の仕組みを考察」しました。
そして、3本目の記事で、その「路線・駅検索の仕組みを WordPressに実装する方法を考察」する、という流れです。
 
これだけのステップを踏まないとたどり着けない、ということではありますが、ブログの記事としては 3本の記事を作ることができました(笑)。
 路線・駅検索のために緯度経度からPHPで2点間の距離を計算する処理解説
 路線・駅検索をPHPで実装する方法解説。GoogleMapsの緯度経度から計算し検索
 「WordPressで駅検索を実装」(鋭意執筆中)
 
 
3本目の記事は、現在鋭意作成中です。
なぜ、WordPressに組み込むことを目指すのか、と言いますと、「月極駐車場検索エース」というサイトを作りましたが、ここには都道府県検索はありますが、路線・駅検索がありません。
そのため、「月極駐車場検索エース」に路線・駅検索を組み込むことを目指して思考を始めましたので、最終目標は WordPressへの組み込みなわけです。
 
ただ、WordPressに組み込むには、カスタムフィールドだけで処理するのは難しく、オリジナルのテーブルを構築する必要がありそうで、オリジナルテーブルにアクセスする方法と、オリジナルテーブルにデータを保存する方法、さらに、それらとカテゴリ検索や各フィールドの条件検索と組み合わせる方法などを理解する必要がありそうで、少し時間がかかりそうです。
GoogleAdwords
GoogleAdwords
この記事が参考になったと思いましたらソーシャルメディアで共有していただけると嬉しいです!
関連記事
-  
            
 - 
      
QRコード作成ライブラリ「cakePHP-QR-Code-Helper」をPHPで使うカスタマイズ
CakePHP2用のQRコード作成ライブラリ「cakePHP-QR-Code-Helper」をプレーンのPHPでも使うためのカスタマイズ方法を解説。1ファイルを設置するだけでQRコードが作れるため使い勝手がいい。
 
-  
            
 - 
      
数値文字参照コード変換ツール(HTML特殊文字コード変換ツール)
テキストを数値文字参照コード(特殊文字コード)に変換するツール。テキストを数値文字参照コードに簡単変換。数値文字参照、文字実体参照、特殊文字などの違いも解説。
 
-  
            
 - 
      
ECCUBEを開発環境から本番ドメインに変更でエラーが・パス変更について
レンタルサーバでサーバ会社から割り当てられたURLで開発し、本番公開時にドメインを当てたらエラーが!そんな場合の対処方法の解説。対処方法は簡単ですが管理画面からは対応不可。
 
-  
            
 - 
      
路線・駅検索のために緯度経度からPHPで2点間の距離を計算する処理解説
路線・駅検索の仕組みの構築は大変。それを簡易に実装するために緯度経度を元に距離計算をする仕組みを考案。まずは2点間の距離を計算する仕組みを解説し、距離計算にまつわる関連技術も紹介。
 
-  
            
 - 
      
ECCUBEでカード決済NGの受注情報をマイページ購入履歴に表示しない方法解説
ECCUBEでカード決済に失敗しても購入履歴一覧に注文情報(受注情報)が表示される問題への対処方法を解説。受注情報レコードの作成の流れとステイタスについても解説。
 
-  
            
 - 
      
PHPで特定の日間の日付を for、strtotimeで表示する
ある特定の間の日付の情報を for文、strtotimeを使って作成し、その解説をしています。
 
-  
            
 - 
      
ECCUBEの商品一覧ページのSEO対策!rel=”next” rel=”prev”を設定
Googleは関連あるページはその旨明示するよう求めています。ECCUBEの商品一覧ページでその求めに応じるための「rel=”next”」「rel=”prev”」について解説します。
 
-  
            
 - 
      
PHPのソースで見慣れない記号が出てきた・アロー演算子(->)、ダブルアロー演算子(=>)
PHPのプログラムソースには見慣れない記号が出てきます。その意味や調べ方です。
 
-  
            
 - 
      
PHPで正規表現の検証には preg_match_allが便利
PHPで正規表現の検証には preg_match_allが便利です。その便利さの使い方の解説です。
 
-  
            
 - 
      
PHPのcURLでAPIやWebサイトへのアクセス方法。file_get_contentsとの比較
PHPからWebサイトにアクセスしてHTMLを取得、APIにアクセスして情報を取得する場合は、cURLがオススメ。file_get_contentsでも可能だがエラー制御に難がありトラブルのもとになる。
 
 

            
            
            
            
            
            
            
            
            
            
Comment
掲載されてる以下のSQLに緯度1°の距離が一度も出てきていないので間違いだと思うのですが、どうでしょうか。
select
a.company_name,
b.line_name,
c.station_name,
c.pref_cd,
c.address,
c.lon,
c.lat,
sqrt ( pow ( ( c.lat – 【駅の緯度】 ) * 【経度1°の距離】, 2 ) + pow ( ( c.lon – 【駅の経度】 ) * 【経度1°の距離】, 2 ) ) as d
from
company as a,
line as b,
station as c
where
c.line_cd = b.line_cd and
b.company_cd = a.company_cd and
( 【入力の距離】 > sqrt ( pow ( ( c.lat – 【駅の緯度】 ) * 【経度1°の距離】, 2 ) + pow ( ( c.lon – 【駅の経度】 ) * 【経度1°の距離】, 2 ) ) )
order by d
やまちゃんさん、コメントありがとうございます。
ご指摘の通り、間違いがありましたので修正をさせていただきました。
記事を書くときに文字列をコピペして、必要な変更を仕切れていなかったんだろうな、と思います。
ご連絡ありがとうございました。