<?php
//検索場所
$address = $_GET["address"];
//検索キーワード
$keyword = $_GET["keyword"];
//検索範囲(メートル)
$radius = $_GET["radius"];
//お店情報取得
$resultHTML = getPlace($address, $keyword, $radius);
//HTMLを返却
echo $resultHTML;
/*
お店情報取得
$address:検索場所
$keyword:検索キーワード
$radius:検索範囲(メートル)
*/
function getPlace($address, $keyword, $radius) {
//Google Maps API の APIキー
$apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
//【Geocoding API】住所→緯度・経度
$geocodeApiUrl = "https://maps.googleapis.com/maps/api/geocode/json";
$geocodeApiUrl .= "?key=" . $apiKey;
$geocodeApiUrl .= "&address=" . urlencode($address);
//Geocoding APIにリクエスト
$context = stream_context_create(array(
'http' => array('ignore_errors' => true)
));
$geocodeJson = file_get_contents($geocodeApiUrl, false, $context);
//JSON文字列をデコードして連想配列にする
$geocodeData = json_decode($geocodeJson, true);
//緯度・経度の取得
if ($geocodeData["status"] == "OK"){
$lat = $geocodeData["results"][0]["geometry"]["location"]["lat"];
$lng = $geocodeData["results"][0]["geometry"]["location"]["lng"];
} else if($geocodeData["status"] == "ZERO_RESULTS") {
return "【Geocoding API】検索結果が0件です。";
} else if($geocodeData["status"] == "ERROR") {
return "【Geocoding API】サーバ接続に失敗しました。";
} else if($geocodeData["status"] == "INVALID_REQUEST") {
return "【Geocoding API】リクエストが無効でした。";
} else if($geocodeData["status"] == "OVER_QUERY_LIMIT") {
return "【Geocoding API】リクエストの利用制限回数を超えました。";
} else if($geocodeData["status"] == "REQUEST_DENIED") {
return "【Geocoding API】サービスが使えない状態でした。";
} else if($geocodeData["status"] == "UNKNOWN_ERROR") {
return "【Geocoding API】原因不明のエラーが発生しました。";
}
//【Places API】検索エリアのお店情報取得
$placeApiUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json";
$placeApiUrl .= "?key=" . $apiKey;
$placeApiUrl .= "&location=" . $lat . "," . $lng;
$placeApiUrl .= "&radius=" . $radius;
$placeApiUrl .= "&types=restaurant";
$placeApiUrl .= "&keyword=" . urlencode($keyword);
$placeApiUrl .= "&language=ja";
//Places APIにリクエスト
$context = stream_context_create(array(
'http' => array('ignore_errors' => true)
));
$placeJson = file_get_contents($placeApiUrl, false, $context);
//JSON文字列をデコードして連想配列にする
$placeData = json_decode($placeJson, true);
//お店情報取得
$placesList = array();
$nextPageToken = null;
if ($placeData["status"] == "OK"){
//resultsをplacesList配列にマージ
$placesList = array_merge($placesList, $placeData["results"]);
//next_page_tokenを取得
$nextPageToken = $placeData["next_page_token"];
} else if($placeData["status"] == "ZERO_RESULTS") {
return "【Places API】検索結果が0件です。";
} else if($placeData["status"] == "ERROR") {
return "【Places API】サーバ接続に失敗しました。";
} else if($placeData["status"] == "INVALID_REQUEST") {
return "【Places API】リクエストが無効でした。";
} else if($placeData["status"] == "OVER_QUERY_LIMIT") {
return "【Places API】リクエストの利用制限回数を超えました。";
} else if($placeData["status"] == "REQUEST_DENIED") {
return "【Places API】サービスが使えない状態でした。";
} else if($placeData["status"] == "UNKNOWN_ERROR") {
return "【Places API】原因不明のエラーが発生しました。";
}
//next_page_tokenが取得された場合は次ページあり。
//next_page_tokenが取得できなくなるまで、
//次ページ情報の取得を繰り返す。
while (empty($nextPageToken) == false){
//2秒程間隔をおく(連続リクエストすると取得に失敗する)
sleep(2);
//【Places API】次ページのお店情報取得
$placeApiUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json";
$placeApiUrl .= "?key=" . $apiKey;
$placeApiUrl .= "&pagetoken=" . $nextPageToken;
//Places APIにリクエスト
$context = stream_context_create(array(
'http' => array('ignore_errors' => true)
));
$placeJson = file_get_contents($placeApiUrl, false, $context);
//JSON文字列をデコードして連想配列にする
$placeData = json_decode($placeJson, true);
if ($placeData["status"] == "OK"){
//resultsをplacesList配列にマージ
$placesList = array_merge($placesList, $placeData["results"]);
//next_page_tokenを取得
$nextPageToken = $placeData["next_page_token"];
} else {
$nextPageToken = null;
}
}
//ソートを正しく行うため、
//ratingが設定されていないものを
//一旦「-1」に変更する。
for ($i = 0; $i < count($placesList); $i++) {
if (isset($placesList[$i]["rating"]) == false){
$placesList[$i]["rating"] = -1;
}
}
//ratingの降順でソート(多次元連想配列のソート)
foreach ((array) $placesList as $key => $value) {
$sort[$key] = $value['rating'];
}
array_multisort($sort, SORT_DESC, $placesList);
//placesList配列をループして
//結果表示のHTMLタグを組み立てる
$resultHTML = "<ol>\n";
for ($i = 0; $i < count($placesList); $i++) {
$name = $placesList[$i]["name"];
$vicinity = $placesList[$i]["vicinity"];
$rating = $placesList[$i]["rating"];
//ratingが-1のものは「---」に表示変更
if ($rating == -1) $rating = "---";
//表示内容(評価+名称)
$content = "【" . $rating . "】 " . $name;
//詳細表示のリンク作成
$resultHTML .= "<li>\n";
$resultHTML .= "<a href=\"https://maps.google.co.jp/maps?q=" . urlencode($name . " " . $vicinity) . "&z=15&iwloc=A\"";
$resultHTML .= " target=\"_blank\">" . $content . "</a>\n";
$resultHTML .= "</li>\n";
}
$resultHTML .= "</ol>";
return $resultHTML;
}
?>
[2〜7行目]
//検索場所
$address = $_GET["address"];
//検索キーワード
$keyword = $_GET["keyword"];
//検索範囲(メートル)
$radius = $_GET["radius"];
「$_GET」でGETパラメータを取得します。
[24〜33行目]
//【Geocoding API】住所→緯度・経度
$geocodeApiUrl = "https://maps.googleapis.com/maps/api/geocode/json";
$geocodeApiUrl .= "?key=" . $apiKey;
$geocodeApiUrl .= "&address=" . urlencode($address);
//Geocoding APIにリクエスト
$context = stream_context_create(array(
"http" => array("ignore_errors" => true)
));
$geocodeJson = file_get_contents($geocodeApiUrl, false, $context);
「Geocoding API」を呼び出して、住所(ランドマーク名)から緯度・経度を取得します。
file_get_contentsでURLを呼び出すときは、
「"ignore_errors" = true」を付けて、エラー発生時にもレスポンスが取得できるようにしてください。
[35〜36行目]
//JSON文字列をデコードして連想配列にする
$geocodeData = json_decode($geocodeJson, true);
APIの返却結果のJSON文字列を、PHPで扱いやすように連想配列にデコードします。
json_decodeの第2引数を「false」にすると、オブジェクト型にデコードされますが、PHPでは連想配列のほうが扱いやすいと思います。
[38〜56行目]
//緯度・経度の取得
if ($geocodeData["status"] == "OK"){
$lat = $geocodeData["results"][0]["geometry"]["location"]["lat"];
$lng = $geocodeData["results"][0]["geometry"]["location"]["lng"];
} else if($geocodeData["status"] == "ZERO_RESULTS") {
return "【Geocoding API】検索結果が0件です。";
} else if($geocodeData["status"] == "ERROR") {
return "【Geocoding API】サーバ接続に失敗しました。";
} else if($geocodeData["status"] == "INVALID_REQUEST") {
return "【Geocoding API】リクエストが無効でした。";
} else if($geocodeData["status"] == "OVER_QUERY_LIMIT") {
return "【Geocoding API】リクエストの利用制限回数を超えました。";
} else if($geocodeData["status"] == "REQUEST_DENIED") {
return "【Geocoding API】サービスが使えない状態でした。";
} else if($geocodeData["status"] == "UNKNOWN_ERROR") {
return "【Geocoding API】原因不明のエラーが発生しました。";
}
JSONデータのstatusが「OK」の場合は、結果が正常に受け取れています。
連想配列を辿って、緯度・経度を取得しましょう。
statusが「OK」以外の時はエラーなので、エラーメッセージを返します。
file_get_contentsで「"ignore_errors" = true」を付けていない場合は、Warning エラーが発生してエラーのレスポンスは取得できません。
[58〜71行目]
//【Places API】検索エリアのお店情報取得
$placeApiUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json";
$placeApiUrl .= "?key=" . $apiKey;
$placeApiUrl .= "&location=" . $lat . "," . $lng;
$placeApiUrl .= "&radius=" . $radius;
$placeApiUrl .= "&types=restaurant";
$placeApiUrl .= "&keyword=" . urlencode($keyword);
$placeApiUrl .= "&language=ja";
//Places APIにリクエスト
$context = stream_context_create(array(
'http' => array('ignore_errors' => true)
));
$placeJson = file_get_contents($placeApiUrl, false, $context);
「Places API」のnearbysearchを呼び出して、検索条件のお店情報を取得します。
keywordには全角文字が入る可能性があるので、
urlencodeでURLエンコードをしておきましょう。
[100〜116行目]
//next_page_tokenが取得された場合は次ページあり。
//next_page_tokenが取得できなくなるまで、
//次ページ情報の取得を繰り返す。
while (empty($nextPageToken) == false){
//2秒程間隔をおく(連続リクエストすると取得に失敗する)
sleep(2);
//【Places API】次ページのお店情報取得
$placeApiUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json";
$placeApiUrl .= "?key=" . $apiKey;
$placeApiUrl .= "&pagetoken=" . $nextPageToken;
//Places APIにリクエスト
$context = stream_context_create(array(
'http' => array('ignore_errors' => true)
));
$placeJson = file_get_contents($placeApiUrl, false, $context);
JSONデータの
next_page_tokenが取得できた場合は、次ページ情報があると判断できます。
nearbysearchの
「pagetoken」パラメータにnext_page_tokenを渡して、次ページ情報を取得しましょう。
next_page_tokenの値が取得できなくなるまで、次ページ情報の取得を繰り返します。
次ページ情報の取得は、連続リクエストすると上手く結果が得られませんので、
sleepで2秒程待機してから行います。
[133〜146行目]
//ソートを正しく行うため、
//ratingが設定されていないものを
//一旦「-1」に変更する。
for ($i = 0; $i < count($placesList); $i++) {
if (isset($placesList[$i]["rating"]) == false){
$placesList[$i]["rating"] = -1;
}
}
//ratingの降順でソート(多次元連想配列のソート)
foreach ((array) $placesList as $key => $value) {
$sort[$key] = $value['rating'];
}
array_multisort($sort, SORT_DESC, $placesList);
ratingの降順でソートし、ランキング表示を実現します。
ソートの前に、ratingが設定されていないものを
issetで判定して、「-1」としておきましょう。
ソートでは、
array_multisortで「多次元連想配列のソート」を行います。
参考までに、iframeでPHPを呼び出すソースも載せておきます。(本題ではありませんが)
<table>
<tr>
<td>検索場所:</td><td><input type="text" id="addressInput" value="横浜中華街" style="width: 200px"></td>
</tr>
<tr>
<td>KeyWord:</td><td><input type="text" id="keywordInput" value="中華料理" style="width: 200px"></td>
</tr>
<tr>
<td>検索範囲:</td>
<td>
<select id="radiusInput">
<option value="200" selected>200 m</option>
<option value="500">500 m</option>
<option value="800">800 m</option>
<option value="1000">1 km</option>
<option value="1500">1.5 km</option>
<option value="2000">2 km</option>
<select>
</td>
</tr>
<tr>
<td colspan="2" style="padding: 10px">
<input type="button" value="PHPでお店情報取得" onclick="getPlaces();">
</td>
</tr>
</table>
★結果★<br />
<iframe id="results" style="width: 100%; height: 200px; border: 1px dotted; overflow-y: scroll; background: white;"></iframe>
<script>
function getPlaces(){
var addressInput = document.getElementById("addressInput").value;
var keywordInput = document.getElementById("keywordInput").value;
var obj = document.getElementById("radiusInput");
var radiusInput = Number(obj.options[obj.selectedIndex].value);
var url = "https://www.delta-ss.com/labo/php/a016-sample.php";
url += "?address=" + encodeURIComponent(addressInput);
url += "&keyword=" + encodeURIComponent(keywordInput);
url += "&radius=" + radiusInput;
document.getElementById("results").src = url;
}
</script>