Google Maps Platform(JavaScript)で付近のビジネス検索サービスを構築

1. 始める前に

Google Maps Platform の Maps API と Places API を使用してローカル ビジネス検索を作成し、ユーザーの現在地や付近の注目スポットを表示する方法を学習します。このアプリには、位置情報、Place Details、プレイスフォトなどが統合されています。

前提条件

  • HTML、CSS、JavaScript の基本的な知識
  • 請求先アカウントを含むプロジェクト(まだお持ちでない場合は、次のステップに従ってください)。
  • 以下の有効化の手順では、Maps JavaScript APIPlaces API を有効にする必要があります。
  • 上記のプロジェクトの API キー。

Google Maps Platform を使ってみる

Google Maps Platform を初めて使用する場合は、Google Maps Platform スタートガイドを参照するか、再生リスト「Getting Started with Google Maps Platform」を視聴して、以下の手順を行ってください。

  1. 請求先アカウントを作成します。
  2. プロジェクトを作成します。
  3. Google Maps Platform の API と SDK(前セクションに記載のもの)を有効化します。
  4. API キーを生成します。

演習内容

  • Google マップを表示するウェブページを作成する
  • ユーザーの現在地が地図の中心に表示されるようになりました
  • 付近の場所を見つけて、結果をクリック可能なマーカーとして表示する
  • 各場所の詳細を取得して表示します

ae1caf211daa484d.png

必要なもの

  • Google Chrome(推奨)、Firefox、Safari、Internet Explorer などのウェブブラウザ
  • お気に入りのテキスト エディタやコードエディタ

サンプルコードを取得する

  1. コマンドライン インターフェース(MacOS の場合は Terminal、Windows の場合はコマンド プロンプト)を開き、次のコマンドを使用してサンプルコードをダウンロードします。
git clone https://github.com/googlecodelabs/google-maps-nearby-search-js/

それでも問題が解決しない場合は、次のボタンをクリックしてこの Codelab のすべてのコードをダウンロードし、ファイルを解凍します。

コードをダウンロードする

  1. クローンを作成またはダウンロードしたディレクトリに移動します。
cd google-maps-nearby-search-js

stepN フォルダには、この Codelab の各ステップの望ましい最終状態が含まれています。参考用に用意されています。コーディング作業はすべて、work というディレクトリで行います。

2. デフォルトの中心点を含む地図を作成する

ウェブページに Google マップを作成するには、次の 3 つの手順を行います。

  1. HTML ページを作成する
  2. 地図を追加する
  3. API キーを貼り付ける

1. HTML ページを作成する

以下は、このステップで作成したマップです。マップの中心はシドニー(オーストラリア)のシドニー オペラハウスです。ユーザーが位置情報の取得を許可しなかった場合、地図はデフォルトではこの位置情報を使用しますが、興味深い検索結果を表示します。

569b9781658fec74.png

  1. work/ フォルダに移動します。Codelab の残りの部分を通して、work/ フォルダのバージョンを編集します。
cd work
  1. work/ ディレクトリで、テキスト エディタを使用して index.html という名前の空のファイルを作成します。
  2. 次のコードを index.html にコピーします。

index.html

<!DOCTYPE html>
<html>

<head>
  <title>Sushi Finder</title>
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
  <meta charset="utf-8">
  <style>
    /* Always set the map height explicitly to define the size of the div
     * element that contains the map. */
    #map {
      height: 100%;
      background-color: grey;
    }

    /* Optional: Makes the sample page fill the window. */
    html,
    body {
      height: 100%;
      margin: 0;
      padding: 0;
    }

    /* TODO: Step 4A1: Make a generic sidebar. */
  </style>
</head>

<body>
  <!-- TODO: Step 4A2: Add a generic sidebar -->

  <!-- Map appears here -->
  <div id="map"></div>

  <!-- TODO: Step 1B, Add a map -->
</body>

</html>
  1. ウェブブラウザで index.html ファイルを開きます。
open index.html

2. 地図を追加する

このセクションでは、Maps JavaScript API をウェブページに読み込み、API を使って地図をウェブページに追加する独自の JavaScript を作成する方法を説明します。

  1. このスクリプト スクリプトを、map div の後の <!-- TODO: Step 1B, Add a map --> 終了タグ </body> の前に追加します。

step1/index.html

<!-- TODO: Step 1B, Add a map -->
<script>
    /* Note: This example requires that you consent to location sharing when
     * prompted by your browser. If you see the error "Geolocation permission
     * denied.", it means you probably did not give permission for the browser * to locate you. */

    /* TODO: Step 2, Geolocate your user
     * Replace the code from here to the END TODO comment with new code from
     * codelab instructions. */
    let pos;
    let map;
    function initMap() {
        // Set the default location and initialize all variables
        pos = {lat: -33.857, lng: 151.213};
        map = new google.maps.Map(document.getElementById('map'), {
            center: pos,
            zoom: 15
        });
    }
    /* END TODO: Step 2, Geolocate your user */
</script>

<!-- TODO: Step 1C, Get an API key -->
<!-- TODO: Step 3A, Load the Places Library -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

3. API キーを貼り付ける

  1. <!-- TODO: Step 1C, Get an API key --> の後の行で、スクリプト ソース URL のキーパラメータの値をコピーして、前提条件で作成した API キーに置き換えます。

step1/index.html

<!-- TODO: Step 1C, Get an API key -->
<!-- TODO: Step 3A, Load the Places Library -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
  1. 作業中の HTML ファイルを保存します。

テスト

編集中のファイルのブラウザビューを再読み込みします。グレーの長方形が配置されていた地図が表示されます。エラー メッセージが表示される場合は、最後の <script> タグの「YOUR_API_KEY」をご自身の API キーに置き換えてください。API キーを取得する方法については、上記を参照してください。

完全なサンプルコード

ここまでのこのプロジェクトの完全なコードは、GitHub で入手できます

3. ユーザーの位置情報を取得する

次に、ブラウザ上の HTML5 Geolocation 機能と Maps JavaScript API を併用して、ユーザーやデバイスの地理的位置を Google マップに表示します。

次の画像は、カリフォルニア州マウンテンビューからブラウジングしているときに、地理的位置を示したものです。

1dbb3fec117cd895.png

位置情報とは

位置情報とは、さまざまなデータ収集メカニズムによって、ユーザーまたはコンピューティング デバイスの地理的位置を特定することです。通常、ほとんどの位置情報サービスでは、ネットワークのルーティング アドレスや内蔵 GPS デバイスを使用してこの位置を判断します。このアプリは、ウェブブラウザの W3C Geolocation 規格 navigator.geolocation プロパティを使用して、ユーザーの位置情報を判定します。

実際に試す

コメント TODO: Step 2, Geolocate your userEND TODO: Step 2, Geolocate your user の間にあるコードを次のコードで置き換えます。

step2/index.html

/* TODO: Step 2, Geolocate your user
    * Replace the code from here to the END TODO comment with this code
    * from codelab instructions. */
let pos;
let map;
let bounds;
let infoWindow;
let currentInfoWindow;
let service;
let infoPane;
function initMap() {
    // Initialize variables
    bounds = new google.maps.LatLngBounds();
    infoWindow = new google.maps.InfoWindow;
    currentInfoWindow = infoWindow;
    /* TODO: Step 4A3: Add a generic sidebar */

    // Try HTML5 geolocation
    if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(position => {
        pos = {
        lat: position.coords.latitude,
        lng: position.coords.longitude
        };
        map = new google.maps.Map(document.getElementById('map'), {
        center: pos,
        zoom: 15
        });
        bounds.extend(pos);

        infoWindow.setPosition(pos);
        infoWindow.setContent('Location found.');
        infoWindow.open(map);
        map.setCenter(pos);

        /* TODO: Step 3B2, Call the Places Nearby Search */
    }, () => {
        // Browser supports geolocation, but user has denied permission
        handleLocationError(true, infoWindow);
    });
    } else {
    // Browser doesn't support geolocation
    handleLocationError(false, infoWindow);
    }
}

// Handle a geolocation error
function handleLocationError(browserHasGeolocation, infoWindow) {
    // Set default location to Sydney, Australia
    pos = {lat: -33.856, lng: 151.215};
    map = new google.maps.Map(document.getElementById('map'), {
    center: pos,
    zoom: 15
    });

    // Display an InfoWindow at the map center
    infoWindow.setPosition(pos);
    infoWindow.setContent(browserHasGeolocation ?
    'Geolocation permissions denied. Using default location.' :
    'Error: Your browser doesn\'t support geolocation.');
    infoWindow.open(map);
    currentInfoWindow = infoWindow;

    /* TODO: Step 3B3, Call the Places Nearby Search */
}
/* END TODO: Step 2, Geolocate your user */
/* TODO: Step 3B1, Call the Places Nearby Search */

テスト

  1. ファイルを保存します。
  2. ページを再読み込みします。

ブラウザで現在地情報の共有を求めるメッセージが表示されます。

  1. [ブロック] を 1 回クリックして、エラーが適切に処理され、中心がシドニーにあるかどうかを確認します。
  2. もう一度再読み込みを行い、[許可] をクリックして、位置情報が機能するかどうかを確認し、地図を現在地に移動します。

完全なサンプルコード

ここまでのこのプロジェクトの完全なコードは、GitHub で入手できます

4. 付近の場所を検索する

周辺検索では、キーワードまたはタイプで、指定した地域内の場所を検索できます。周辺検索には、次のいずれかの方法で指定して、場所を含める必要があります。

  • 長方形の検索領域を定義する LatLngBounds オブジェクト
  • location プロパティ(円の中心を LatLng オブジェクトとして指定)と半径(メートル単位)の組み合わせとして定義された円形の領域

Nearby Search を開始するには、PlacesService nearbySearch() メソッドを呼び出します。このメソッドは PlaceResult オブジェクトの配列を返します。

A. プレイス ライブラリを読み込む

まず、プレイス ライブラリ サービスにアクセスするには、スクリプトのソース URL を更新して、libraries パラメータを追加し、値として places を追加します。

step3/index.html

<!-- TODO: Step 3A, Load the Places Library -->
<script async defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap">

B. Places Nearby Search リクエストを呼び出し、レスポンスを処理します

次に、PlaceSearch リクエストを作成します。最小必須項目は次のとおりです。

最小必須項目は次のとおりです。

  • bounds: 長方形の検索領域を定義する google.maps.LatLngBounds オブジェクト、または locationradius である必要があります。前者は google.maps.LatLng オブジェクト、後者は円の半径(メートル単位)を表す単純な整数を取ります。最大半径は 50,000 メートルです。rankByDISTANCE に設定されている場合、場所を指定する必要がありますが、半径または境界を指定することはできません。
  • keyword利用可能なすべてのフィールド(名前、種類、住所、およびユーザーのレビューと第三者のコンテンツまたはtypeを含むが、これらに限定されない)に一致させ、結果を指定したタイプに一致する場所に限定します。指定できるタイプは 1 つのみです(複数のタイプが指定されている場合、最初の入力に続くすべてのタイプは無視されます)。サポートされるタイプのリストをご覧ください。

この Codelab では、検索の位置としてユーザーの現在位置を使用し、距離に基づいて結果をランク付けします。

  1. コメント TODO: Step 3B1 に次のコードを追加して、検索を呼び出し、レスポンスを処理する 2 つの関数を記述します。

検索キーワードとして「sushi」を使用していますが、変更することもできます。createMarkers 関数を定義するコードは次のセクションで説明します。

step3/index.html

/* TODO: Step 3B1, Call the Places Nearby Search */
// Perform a Places Nearby Search Request
function getNearbyPlaces(position) {
    let request = {
    location: position,
    rankBy: google.maps.places.RankBy.DISTANCE,
    keyword: 'sushi'
    };

    service = new google.maps.places.PlacesService(map);
    service.nearbySearch(request, nearbyCallback);
}

// Handle the results (up to 20) of the Nearby Search
function nearbyCallback(results, status) {
    if (status == google.maps.places.PlacesServiceStatus.OK) {
    createMarkers(results);
    }
}

/* TODO: Step 3C, Generate markers for search results */
  1. 次の行を initMap 関数の最後にあるコメント TODO: Step 3B2 に追加します。
/* TODO: Step 3B2, Call the Places Nearby Search */
// Call Places Nearby Search on user's location
getNearbyPlaces(pos);
  1. 次の行を handleLocationError 関数の最後にあるコメント TODO: Step 3B3 に追加します。
/* TODO: Step 3B3, Call the Places Nearby Search */
// Call Places Nearby Search on the default location
getNearbyPlaces(pos);

C. 検索結果のマーカーを生成する

マーカーは、地図上の場所を特定するものです。デフォルトでは、マーカーには標準画像が使用されます。マーカー画像のカスタマイズについて詳しくは、マーカーをご覧ください。

google.maps.Marker コンストラクタは、単一の Marker options オブジェクト リテラルを受け取ってマーカーの初期プロパティを指定します。

以下のフィールドは、特に重要であり、マーカーの構築時に一般的に設定されます。

  • position(必須)は、マーカーの初期位置を識別する LatLng を指定します。
  • map(省略可)は、マーカーを配置するマップを指定します。マーカーの作成時に地図を指定しなかった場合、マーカーは作成されても、地図には追加(または表示)されません。後でマーカーを追加するには、マーカーの setMap() メソッドを呼び出します。
  • コメント TODO: Step 3C の後に次のコードを追加して、レスポンスで返される場所ごとに 1 つのマーカーの位置、地図、タイトルを設定します。また、bounds 変数の extend メソッドを使用して、中央とすべてのマーカーが地図上に表示されるようにします。

step3/index.html

/* TODO: Step 3C, Generate markers for search results */
// Set markers at the location of each place result
function createMarkers(places) {
    places.forEach(place => {
    let marker = new google.maps.Marker({
        position: place.geometry.location,
        map: map,
        title: place.name
    });

    /* TODO: Step 4B: Add click listeners to the markers */

    // Adjust the map bounds to include the location of this marker
    bounds.extend(place.geometry.location);
    });
    /* Once all the markers have been placed, adjust the bounds of the map to
    * show all the markers within the visible area. */
    map.fitBounds(bounds);
}

/* TODO: Step 4C: Show place details in an info window */

テスト

  1. ページを保存して再読み込みし、[許可] をクリックして位置情報の利用を許可します。

地図の中心付近に、最大 20 個の赤いマーカーが表示されます。

  1. もう一度ページを再読み込みし、今回は位置情報の利用許可をブロックします。

地図のデフォルト中央に検索結果は引き続き表示されますか?(このサンプルでは、オーストラリアのシドニーがデフォルトで表示されます)。

完全なサンプルコード

ここまでのこのプロジェクトの完全なコードは、GitHub で入手できます

5. オンデマンドで Place Details を表示

場所 ID(Nearby Search の検索結果のフィールドの 1 つとして提供されます)を取得したら、その住所に関する追加情報(住所、電話番号、ユーザーの評価やクチコミなど)をリクエストできます。この Codelab では、詳細な Place を表示するためのサイドバーを作成し、マーカーがインタラクティブな場所になるようにして、ユーザーが詳細を表示する場所を選択できるようにします。

A. 一般的なサイドバーを作成する

ここでは、Place Details を表示する場所が必要です。ここでは、サイドバー上でシンプルなマーカーコードを紹介します。このコードは、ユーザーがマーカーをクリックしたときに、場所の詳細をスライドして表示するために使用できます。

  1. コメント TODO: Step 4A1 の後に style タグを追加します。

step4/index.html

/* TODO: Step 4A1: Make a generic sidebar */
/* Styling for an info pane that slides out from the left. 
    * Hidden by default. */
#panel {
    height: 100%;
    width: null;
    background-color: white;
    position: fixed;
    z-index: 1;
    overflow-x: hidden;
    transition: all .2s ease-out;
}

.open {
    width: 250px;
}

/* Styling for place details */
.hero {
    width: 100%;
    height: auto;
    max-height: 166px;
    display: block;
}

.place,
p {
    font-family: 'open sans', arial, sans-serif;
    padding-left: 18px;
    padding-right: 18px;
}

.details {
    color: darkslategrey;
}

a {
    text-decoration: none;
    color: cadetblue;
}
  1. body セクションの map div の直前に、詳細パネル用の div を追加します。
<!-- TODO: Step 4A2: Add a generic sidebar -->
<!-- The slide-out panel for showing place details -->
<div id="panel"></div>
  1. initMap() 関数で、TODO: Step 4A3 コメントの後で、次のように infoPane 変数を初期化します。
/* TODO: Step 4A3: Add a generic sidebar */
infoPane = document.getElementById('panel');

B. マーカーにクリック リスナーを追加する

  1. createMarkers 関数で、マーカーを作成する際にクリック リスナーを追加します。

クリック リスナーは、そのマーカーに関連付けられている場所の詳細を取得し、関数を呼び出して詳細を表示します。

  1. コードコメント TODO: Step 4BcreateMarkers 関数内に次のコードを貼り付けます。

showDetails メソッドは次のセクションで実装します。

step4/index.html

/* TODO: Step 4B: Add click listeners to the markers */
// Add click listener to each marker
google.maps.event.addListener(marker, 'click', () => {
    let request = {
    placeId: place.place_id,
    fields: ['name', 'formatted_address', 'geometry', 'rating',
        'website', 'photos']
    };

    /* Only fetch the details of a place when the user clicks on a marker.
    * If we fetch the details for all place results as soon as we get
    * the search response, we will hit API rate limits. */
    service.getDetails(request, (placeResult, status) => {
    showDetails(placeResult, marker, status)
    });
});

addListener リクエストでは、placeId プロパティに詳細リクエストの場所を指定します。fields プロパティにはその場所の詳細情報のフィールド名の配列を指定します。リクエストできるフィールドの完全なリストについては、PlaceResult インターフェースをご覧ください。

C. 情報ウィンドウに Place Details を表示する

情報ウィンドウは、地図上の特定の場所でダイアログの上にコンテンツ(通常はテキストや画像)を表示します。情報ウィンドウには、コンテンツ領域と先が細くなった部分があります。茎の先端が地図上の指定された場所に付けられます。通常、情報ウィンドウはマーカーに関連付けられますが、特定の緯度/経度に関連付けることもできます。

  1. コメント TODO: Step 4C に次のコードを追加して、ビジネスの名前と評価を表示し、そのウィンドウをマーカーに追加する InfoWindow を作成します。

次のセクションでサイドバーに詳細を表示するための showPanel を定義します。

step4/index.html

/* TODO: Step 4C: Show place details in an info window */
// Builds an InfoWindow to display details above the marker
function showDetails(placeResult, marker, status) {
    if (status == google.maps.places.PlacesServiceStatus.OK) {
    let placeInfowindow = new google.maps.InfoWindow();
    placeInfowindow.setContent('<div><strong>' + placeResult.name +
        '</strong><br>' + 'Rating: ' + placeResult.rating + '</div>');
    placeInfowindow.open(marker.map, marker);
    currentInfoWindow.close();
    currentInfoWindow = placeInfowindow;
    showPanel(placeResult);
    } else {
    console.log('showDetails failed: ' + status);
    }
}

/* TODO: Step 4D: Load place details in a sidebar */

D. サイドバーに場所の詳細を読み込む

PlaceResult オブジェクトで返された詳細情報を使用して、別の div を入力します。このサンプルでは、infoPane を使用します。これは、ID が「panel」の div の任意の変数名です。ユーザーが新しいマーカーをクリックするたびに、このコードはサイドバーを閉じ、すでにある詳細情報を消去し、新しい詳細を追加して、サイドバーを開きます。

  1. コメント TODO: Step 4D の後に次のコードを追加します。

step4/index.html

/* TODO: Step 4D: Load place details in a sidebar */
// Displays place details in a sidebar
function showPanel(placeResult) {
    // If infoPane is already open, close it
    if (infoPane.classList.contains("open")) {
    infoPane.classList.remove("open");
    }

    // Clear the previous details
    while (infoPane.lastChild) {
    infoPane.removeChild(infoPane.lastChild);
    }

    /* TODO: Step 4E: Display a Place Photo with the Place Details */

    // Add place details with text formatting
    let name = document.createElement('h1');
    name.classList.add('place');
    name.textContent = placeResult.name;
    infoPane.appendChild(name);
    if (placeResult.rating != null) {
    let rating = document.createElement('p');
    rating.classList.add('details');
    rating.textContent = `Rating: ${placeResult.rating} \u272e`;
    infoPane.appendChild(rating);
    }
    let address = document.createElement('p');
    address.classList.add('details');
    address.textContent = placeResult.formatted_address;
    infoPane.appendChild(address);
    if (placeResult.website) {
    let websitePara = document.createElement('p');
    let websiteLink = document.createElement('a');
    let websiteUrl = document.createTextNode(placeResult.website);
    websiteLink.appendChild(websiteUrl);
    websiteLink.title = placeResult.website;
    websiteLink.href = placeResult.website;
    websitePara.appendChild(websiteLink);
    infoPane.appendChild(websitePara);
    }

    // Open the infoPane
    infoPane.classList.add("open");
}

E. Place Details で場所の写真を表示する

getDetails の結果は、placeId に関連付けられている最大 10 枚の写真の配列を返します。サイドバーにある場所名の上に最初の写真が表示されます。

  1. サイドバーの上部に写真を表示する場合は、name 要素を作成する前にこのコードを配置します。

step4/index.html

/* TODO: Step 4E: Display a Place Photo with the Place Details */
// Add the primary photo, if there is one
if (placeResult.photos != null) {
    let firstPhoto = placeResult.photos[0];
    let photo = document.createElement('img');
    photo.classList.add('hero');
    photo.src = firstPhoto.getUrl();
    infoPane.appendChild(photo);
}

テスト

  1. ブラウザでページを保存して再読み込みし、位置情報の利用許可を許可します。
  2. 任意のマーカーをクリックすると、情報ウィンドウがポップアップ表示され、詳細情報が表示され、サイドバーが左側からスライドして詳細が表示されます。
  3. 位置情報の利用許可を再読み込みして拒否した場合にも、検索が機能するかどうかをテストします。別のクエリの検索キーワードを編集し、その検索結果を返します。

ae1caf211daa484d.png

完全なサンプルコード

ここまでのこのプロジェクトの完全なコードは、GitHub で入手できます

6. 完了

これで、Places ライブラリなど、Maps JavaScript API のさまざまな機能を使用しました。

学習した内容

詳細

地図をさらに活用するには、Maps JavaScript API のドキュメントPlaces Library のドキュメントをご覧ください。両方のガイドには、ガイド、チュートリアル、API リファレンス、その他のコードサンプル、サポート チャネルが記載されています。主な機能としては、マップへのデータのインポート地図のスタイル設定ストリートビュー サービスなどがあります。

次に最も構築したい Codelab のタイプはどれですか。

その他の Place Details 情報の活用例 Maps Platform JavaScript API を使用したその他の Codelab Android 向けのその他の Codelab iOS でのその他の Codelab 位置情報に基づくデータの地図上のデータの可視化 ストリートビューのカスタマイズ

ご希望の Codelab が上記にない場合、こちらからリクエストしてください