Google Maps Platform(JavaScript)で周辺検索サービスを作成する

1. 始める前に

Google Maps Platform の Maps API と Places API を使用して、ユーザーの位置情報を取得し、近くの興味深い場所を表示するローカル ビジネス検索を作成する方法を学びます。このアプリは、ジオロケーション、Place Details、Place Photos などを統合しています。

前提条件

  • 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 の場合はターミナル、Windows の場合はコマンド プロンプト)を開き、次のコマンドでサンプルコードをダウンロードします。
git clone https://github.com/googlecodelabs/google-maps-nearby-search-js/

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

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

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

この Codelab の各ステップにおける望ましい最終状態は、stepN フォルダに格納されています。これらは参照用に用意されています。コーディング作業はすべて、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 の後、閉じ </body> タグの前の <!-- TODO: Step 1B, Add a map --> の場所に挿入します。

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 の key パラメータの値をコピーして、前提条件で作成した 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 位置情報機能と 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. 付近の場所を検索する

Nearby Search では、指定された範囲内で、キーワードやタイプによる場所の検索が可能です。周辺検索には、次のいずれかの方法で指定して、場所を含める必要があります。

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

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

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

まず、Places ライブラリ サービスにアクセスするには、スクリプトのソース 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 Details を表示するサイドバーを作成し、マーカーをインタラクティブにして、ユーザーが場所を選択して詳細を表示できるようにします。

A. 汎用サイドバーを作成する

Place Details を表示する場所が必要なので、ユーザーがマーカーをクリックしたときにスライドして 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. map div の直前の body セクションに、詳細パネルの 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. createMarkers 関数のコードコメント TODO: Step 4B に次のコードを貼り付けます。

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 を入力します。このサンプルでは、ID が「panel」の div の任意の変数名である infoPane を使用します。ユーザーが新しいマーカーをクリックするたびに、このコードは、サイドバーがすでに開いている場合は閉じて、古い詳細情報を消去し、新しい詳細情報を追加して、サイドバーを開きます。

  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 とともに Place Photo を表示する

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 ライブラリのドキュメントをご覧ください。どちらにも、ガイド、チュートリアル、API リファレンス、その他のコードサンプル、サポート チャネルが記載されています。人気の機能には、地図へのデータのインポート地図のスタイル設定を開始するストリートビュー サービスの追加などがあります。

次に作成してほしい Codelab の種類を教えてください。

豊富なプレイス情報を活用するその他の例 Maps Platform JavaScript API を使用するその他の Codelab Android 向けのその他の Codelab iOS 向けのその他の Codelab 地図上に位置情報データを可視化する 地図のカスタム スタイル設定 ストリート ビューを使用する

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