1.はじめに
投稿された情報(緯度経度)からGooleMapsAPIを使用して地図を表示、地点間のルート検索機能、周辺情報の取得を実装したので知識の定着、復習のために記事を書きます。
今回はその3回目で周辺情報取得機能を実装していきます。
2.環境
Ruby (2.6.4)
Rails (6.1.7)
Maps JavaScript API
3.前提条件
APIキー取得済み
投稿機能を実装している
下記を実装している
GoogleMapsAPIを使用した開発1(地図の表示)
GoogleMapsAPIを使用した開発2(現在地取得)
4.実装
1.PlacesAPIを有効にする
- 下記公式サイトを参考に導入してください。
Places API公式
2.initMap関数とgetCurrentLocationAndAddMarker関数にsearchPlaces関数を呼び出す記述を追加する
// 周辺のカフェを検索
searchPlaces({
lat: spot.latitude,
lng: spot.longitude
});
-
searchPlaces({ lat: spot.latitude, lng: spot.longitude });
で、searchPlaces 関数を呼び出しています。
引数として、表示されている投稿の緯度、経度を渡しています。
// 周辺のカフェを検索
searchPlaces(currentLatLng);
-
searchPlaces(currentLatLng);
でsearchPlaces関数を呼び出しています。 - 引数として、現在地の緯度、経度を渡しています。
3.searchPlaces関数を使用して周辺のカフェを検索
変数宣言
let service;
function searchPlaces(location) {
service = new google.maps.places.PlacesService(map); // ①
if (service) { // ②
const request = {
location: location,
radius: 300,
types: ['cafe']
};
service.nearbySearch(request, function (results, status) { // ④
if (status === google.maps.places.PlacesServiceStatus.OK) { // ⑤
for (let i = 0; i < results.length; i++) { // ⑥
createMarker(results[i]);
}
}
});
} else { // ⑦
console.error('PlacesService is not available.');
}
}
①service = new google.maps.places.PlacesService(map);
でPlacesAPIのPlacesServiceインスタンスを新しく作成します。
- このサービスを使用して、場所の検索を行います。
- 引数は、
new google.maps.Map
で取得したMapインスタンスです。
②if (service) { ... }
でPlacesService が正常に作成されたかどうかを確認します。
③const request = { ... };
で検索のためのリクエストオブジェクトを作成します。
- 指定された位置周辺の半径300メートル以内のカフェを検索するように設定しています。
④service.nearbySearch(request, function (results, status) { ... });
ではnearbySearch メソッドを使用して、検索リクエストをPlaces APIに送信します。
- 結果が戻ると、指定されたコールバック関数が実行されます。
-
request
は検索のためのパラメータが設定されたオブジェクトです。location(検索の基準となる位置情報)、radius(検索半径)、types(検索する施設の種類)などが含まれています。 -
function (results, status) { ... }:
は、検索が完了した後に呼び出されるコールバック関数です。この関数には、検索結果と検索のステータスが渡されます。resultsには検索結果が含まれ、statusには検索のステータスが含まれます。 - 具体的な検索結果やステータスはこのコールバック関数内で処理されます。statusが
google.maps.places.PlacesServiceStatus.OK
の場合には検索が成功し、resultsに検索結果が含まれることになります。その後、resultsを元に処理を行います。
⑤if (status === google.maps.places.PlacesServiceStatus.OK) { ... }
は検索が成功した場合の条件分岐です。
⑥for (let i = 0; i < results.length; i++) { ... }
では検索結果の数だけ繰り返し処理を行い、各検索結果に対して createMarker 関数を呼び出してマーカーを作成します。
- resultsは検索結果の配列で、各要素は周辺の施設を表しています。
- forループはresults内の各施設に対して繰り返し処理を行います。
- createMarker(results[i])は、createMarker関数を呼び出し、引数として現在の施設を渡します。
- createMarker関数内で、引数として渡された施設情報を元に、新しいGoogle マップのマーカーを作成し、そのマーカーを地図上に表示します。
⑦console.error('PlacesService is not available.');
で、PlacesServiceが利用できない場合にコンソールにエラーメッセージを表示させています。
4.PlacesAPIの検索結果に基づいてマーカーを作成
function createMarker(place) {
const placeLoc = place.geometry.location; // ①
const customIconUrl = '<%= asset_path("cafe32.png") %>'; // ②
const marker = new google.maps.Marker({ // ③
map: map,
position: placeLoc,
icon: customIconUrl
});
①const placeLoc = place.geometry.location;
で引数として渡されたplaceオブジェクトから、場所のジオメトリ情報を取得します。
-
geometry.location
には場所の緯度と経度が含まれています。
②const customIconUrl = '<%= asset_path("cafe32.png") %>';
でカスタムアイコンの URL を指定します。
- Railsのasset_pathメソッドを使用して、アセットのパスを取得しています。
③const marker = new google.maps.Marker({ ... });
で新しいマーカーオブジェクトを作成します。
以下のプロパティを設定します。
- map: map: マーカーを表示する地図オブジェクト。
- position: placeLoc: マーカーの位置を指定するための緯度と経度のオブジェクト。
- icon: customIconUrl: マーカーに使用するカスタムアイコンのURL。
5.PlacesAPIの詳細情報を取得
google.maps.event.addListener(marker, 'click', function () { // ①
const request = { // ②
placeId: place.place_id,
fields: ['name', 'formatted_address', 'formatted_phone_number', 'website', 'rating', 'opening_hours',
'photos'
]
};
①google.maps.event.addListener(marker, 'click', function () { ... });
でマーカーがクリックされたときのイベントリスナーを設定します。
- マーカーがクリックされると、指定された関数が実行されます。
②const request = { ... };
でPlacesAPIの詳細情報を取得するためのリクエストオブジェクトを定義します。
- placeId プロパティには、取得したい場所の一意の識別子である place_id が指定されます。
- fields プロパティには、取得したい情報のフィールドが指定されます。
- 今回は、name(名前)、formatted_address(住所)、formatted_phone_number(電話番号)、website(ウェブサイト)、rating、opening_hours(営業時間)、photos(写真) の情報を取得するように指定されています。
6.詳細情報を含む情報ウィンドウを作成
service.getDetails(request, function (placeDetails, status) { // ①
if (status === google.maps.places.PlacesServiceStatus.OK) { // ②
infoWindow.setContent(formatPlaceDetails(placeDetails, placeLoc)); // ③
infoWindow.open(map, marker); // ④
}
});
});
}
①service.getDetails(request, function (placeDetails, status) { ... });
でservice オブジェクトの getDetails メソッドを呼び出します。
- このメソッドは、詳細情報を取得するためのリクエストを行います。request パラメータには、どの場所の詳細情報を取得するかと取得する情報の種類が含まれています。
-
request
施設の詳細情報を取得するためのリクエストパラメータが含まれたオブジェクトです。このオブジェクトには、placeId(施設の一意の識別子)や取得したい情報のfields(取得するフィールド)などが含まれます。 -
function (placeDetails, status) { ... }
は、リクエストの結果を処理するためのコールバック関数です。 -
placeDetails
には詳細情報が含まれ、statusにはリクエストのステータスが含まれます。
②if (status === google.maps.places.PlacesServiceStatus.OK) { ... }
でリクエストのステータスが成功した場合に処理を実行します。
- google.maps.places.PlacesServiceStatus.OK は、リクエストが成功したことを示す定数です。
③infoWindow.setContent(formatPlaceDetails(placeDetails, placeLoc));
で取得した詳細情報を含む情報ウィンドウのコンテンツを設定します。
- formatPlaceDetails関数には、詳細情報と場所のジオメトリ情報が渡され、それを適切な形式で整形する処理が含まれています。
④infoWindow.open(map, marker);
で地図上に情報ウィンドウを表示します。
- map パラメータには、情報ウィンドウを表示する地図オブジェクトが指定されており、markerパラメータには、情報ウィンドウを表示するマーカーオブジェクトが指定されています。
7.PlaceDetailsを整形する関数の設定
function formatPlaceDetails(placeDetails, placeLoc) {
let content = `<strong>${placeDetails.name}</strong><br>`; // ①
content += `住所: ${placeDetails.formatted_address}<br>`; // ②
content += `電話番号: ${placeDetails.formatted_phone_number}<br>`; // ③
if (placeDetails.website) { // ④
content += `ウェブサイト: <a href="${placeDetails.website}" target="_blank">${placeDetails.website}</a><br>`;
}
if (placeDetails.rating) { // ⑤
content += `評価: ${placeDetails.rating}/5<br>`;
}
if (placeDetails.opening_hours) { // ⑥
content += '営業時間:<br>';
content += '<ul>';
placeDetails.opening_hours.weekday_text.forEach((weekdayText) => {
content += `<li>${weekdayText}</li>`;
});
content += '</ul>';
}
// 写真が存在する場合のみ処理
if (placeDetails.photos && placeDetails.photos.length > 0) { // ⑦
content += '<br>写真:<br>';
content += '<div>';
// 1枚目の画像だけを取得
const firstPhoto = placeDetails.photos[0];
const imageUrl = firstPhoto.getUrl({ // ⑧
maxWidth: 200,
maxHeight: 200
});
content += `<img src="${imageUrl}" alt="Place Photo"><br>`; // ⑨
content += '</div>';
}
return content; // ⑩
}
①let content = <strong>${placeDetails.name}</strong><br>;
②content += 住所: ${placeDetails.formatted_address}<br>;
③content += 電話番号: ${placeDetails.formatted_phone_number}<br>;
- それぞれ場所、住所、電話番号を表示するためのHTML文字列を生成します。
④if (placeDetails.website) { ... }
- ウェブサイトが存在する場合、それを表示するための HTML 文字列を追加します。${placeDetails.website} は場所のウェブサイトを示しています。
⑤if (placeDetails.rating) { ... }
- 評価が存在する場合、それを表示するための HTML 文字列を追加します。${placeDetails.rating} は場所の評価を示しています。
⑥if (placeDetails.opening_hours) { ... }
- 営業時間が存在する場合、それを表示するための HTML 文字列を追加します。${placeDetails.opening_hours.weekday_text} は場所の営業時間を示しています。
⑦if (placeDetails.photos && placeDetails.photos.length > 0) { ... }
- 写真が存在する場合、それを表示するためのHTML文字列を追加します。
- ${placeDetails.photos} は場所の写真のリストを示しています。
⑧const imageUrl = firstPhoto.getUrl({ maxWidth: 200, maxHeight: 200 });
最初の写真のURLを取得します。maxWidth、maxHeightパラメータは、表示する画像の最大の幅と高さを指定しています。
⑨content += <img src="${imageUrl}" alt="Place Photo"><br>;
- 画像を表示するための HTML 文字列を追加します。
- ${imageUrl} は画像のURLを示しています。
⑩return content;
- 最終的な HTML 文字列を返します。
5.おわりに
以上で周辺情報取得機能まで実装できました。
ご指摘があればご連絡いただけると幸いです。