AngularJSを使ってGoogleマップ(25)プレイスライブラリで店や施設を表示(4)オートコンプリート

プレイスライブラリの検索結果に対し、より詳細な情報を取得しマーカーウィンドウに表示するサンプルコードを作成しました。

(1)サンプルコード
 
デモ表示へ
 
①HTML本文

<div ng-controller="MyCtrl">
<div class="row">
  <div class="col-sm-12">
    <google-map 
      id="map-canvas"
      center="map.center"
      zoom="map.zoom"
      draggable="true"
      options="map.options"
      control="map.control"
      events="map.events"
    >
      <marker coords="map.marker" icon="map.marker.icon"
              options="map.marker.options">
        <window show="map.window.showWindow"
                options="map.window.options"
                templateUrl="map.window.templateUrl"
                templateParameter="map.window.templateParameter"
          >
        </window>
      </marker>
    </google-map>
    
    <div id="type-selector" class="well well-sm">
      <form class="form-inline">
        <div class="radio">
          <label>
            <input type="radio" name="type" id="changetype-all" checked>
            All
          </label>
        </div>
        <div class="radio">
          <label>
            <input type="radio" name="type" id="changetype-establishment">
            Establishments
          </label>
        </div>
        <div class="radio">
          <label>
            <input type="radio" name="type" id="changetype-geocode">
            Geocodes
          </label>
        </div>
      </form>
    </div>
    <input id="pac-input" class="form-control" type="text" placeholder="Enter a location">
  </div>
</div>
</div>

②スクリプト(js/map25.js)

var mapApp = angular.module('googleMapApp', ['google-maps','ui.bootstrap']);

mapApp.controller("MyCtrl", function ($scope) {
  angular.extend($scope, {
    map: {
      center: {
        latitude: 35.681382,
        longitude: 139.766084
      },
      options: {
        maxZoom: 20,
        minZoom: 3
      },
      zoom: 15,
      control: {},
      marker: {},
      window: {
        showWindow: '',
        templateUrl: 'http://www.example.com/wp/wp-content/themes/ang/assets/templates/place4.html',
        templateParameter: {
          name: '',
          address: ''
        },
        options: {
          pixelOffset: {
            height: -50,
            width: 20
          }
        }
      },
      events: { 
        projection_changed: function (map, eventName, originalEventArgs) {
          var types = document.getElementById('type-selector');
          var input = document.getElementById('pac-input');
          map.controls[google.maps.ControlPosition.TOP_LEFT].push(types);
          map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
          var defaultBounds = new google.maps.LatLngBounds(
            new google.maps.LatLng(35.690903370337175, 139.74145889282227),
            new google.maps.LatLng(35.67166118960477, 139.780855178833)
          );
          var options = {
            bounds: defaultBounds,
            types: ['establishment']
          };
          var autocomplete = new google.maps.places.Autocomplete(input,options);

          autocomplete.bindTo('bounds', map);
          google.maps.event.addListener(autocomplete, 'place_changed', function() {
            var place = autocomplete.getPlace();
            if (!place.geometry) {
              return;
            } 
            if (place.geometry.viewport) {
              map.fitBounds(place.geometry.viewport);
            } else {
              map.setCenter(place.geometry.location);
              map.setZoom(17);
            }
            $scope.map.marker.latitude = place.geometry.location.lat();
            $scope.map.marker.longitude = place.geometry.location.lng();
            $scope.map.marker.icon = place.icon;
            
            $scope.map.window.showWindow = true;
            var address = '';
            if (place.address_components) {
              address = [
               (place.address_components[0] && place.address_components[0].short_name || ''),
               (place.address_components[1] && place.address_components[1].short_name || ''),
               (place.address_components[2] && place.address_components[2].short_name || '')
              ].join(' ');
            }
            $scope.map.window.templateParameter.name = place.name;
            $scope.map.window.templateParameter.address = address;
          });
          var setupClickListener = function (id, types) {
            var radioButton = document.getElementById(id);
            google.maps.event.addDomListener(radioButton, 'click', function() {
              autocomplete.setTypes(types);
            });
          }
          setupClickListener('changetype-all', []);
          setupClickListener('changetype-establishment', ['establishment']);
          setupClickListener('changetype-geocode', ['geocode']);
        }
      }
    }
  });
});

③CSS

@import url('bootstrap/css/bootstrap.css');
.angular-google-map-container {
  height: 400px;
}

④テンプレート(assets/templates/place4.html)

<div>
  <strong>{{ parameter.name }}</strong><br />
  { { parameter.address }}
</div>

 
(2)サンプルコードの内容
 
1)全体
 
①placesライブラリを使用する
 
<script type=”text/javascript” src=”http://maps.googleapis.com/maps/api/js?key=自分のGoogleマップAPIキー&sensor=false&libraries=places”></script>
 
②テキスト入力ボックスを作成
 
(HTML)
<input id=”pac-input” type=”text” class=”form-control” placeholder=”Enter a location”>
 
(スクリプト)
図の左上部に表示します。
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
 
③Autocompleteインスタンス生成
 
var input = document.getElementById(‘pac-input’);
var autocomplete = new google.maps.places.Autocomplete(input);
 
④オートコンプリートのユーザ入力変化をリッスンするイベントリスナー定義

google.maps.event.addListener(autocomplete, 'place_changed', function() {
 :
 :

※place_changedイベント
・ユーザーがプレイスを選択してPlaceResultが利用できる状態になると発行
 
・コントロールによって推測されていなかった場所を新たに入力した場合や詳細なリクエストが失敗した場合、name属性にユーザー入力値が含まれる形でイベントが発行される。
 
※上記イベント内の処理内容
 
1.getPlace()でユーザー入力に応じて取得された結果(PlaceResultオブジェクト)を取得
var place = autocomplete.getPlace();
 
2.取得したPlaceResultオブジェクトの属性に応じて処理実行
 
位置、ビューポート、名前、住所などの情報を取得し、地図の中心の設定、Boundsの設定、ズームアップを行う。

if (!place.geometry) {
  return;
}

if (place.geometry.viewport) {
  map.fitBounds(place.geometry.viewport);
} else {
  map.setCenter(place.geometry.location);
  map.setZoom(17);.
}

 
2)マーカー表示
 
①HTMLにmarkerディレクティブ追加
 
<marker coords=”map.marker” icon=”map.marker.icon”>
</marker>
 
②スクリプトにマーカーの定義追加

google.maps.event.addListener(autocomplete, 'place_changed', function() {
  var place = autocomplete.getPlace();
  :
  $scope.map.marker.latitude = place.geometry.location.lat();
  $scope.map.marker.longitude = place.geometry.location.lng();
  $scope.map.marker.icon = place.icon;
});

※マップマーカーアイコンの属性設定について
 
angular-google-mapsのmarkerディレクティブのicon属性にはURLしか設定できません。その他のアイコンの設定を行おうとマーカーオプションを使って下記のように設定してみましたがうまくいきませんでした。
 
(HTML)
<marker coords=”map.marker” options=”map.marker.options”>
</marker>
(スクリプト)
$scope.map.marker.options.iocn.url = place.icon;
$scope.map.marker.options.iocn.size = new google.maps.Size(71, 71);
$scope.map.marker.options.iocn.origin =new google.maps.Point(0, 0);
$scope.map.marker.options.iocn.anchor = new google.maps.Point(17, 34);
$scope.map.marker.options.iocn.scaledSize = new google.maps.Size(35, 35);
 
3)情報ウィンドウを表示
 
①HTMLのmarkerディレクティブ内にwindowディレクティブを追加

<marker coords="map.marker" icon="map.marker.icon"
        options="map.marker.options">
  <window show="map.window.showWindow"
          options="map.window.options"
          templateUrl="map.window.templateUrl"
          templateParameter="map.window.templateParameter"
    >
  </window>
</marker>

②情報ウィンドウのテンプレートファイルを作成
 
オートコンプリートをクリックして取得したplaceの名前と住所を情報ウィンドウに設定して表示します。
 
(assets/templates/place4.html)

<div>
  <strong>{{ parameter.name }}</strong><br />
  {{ parameter.address }}
</div>

③スクリプト内で情報ウィンドウの表示内容を定義
 
ウィンドウのオプションpixelOffsetで情報ウィンドウの表示する位置を設定します。マーカーと重ならないようにオフセットを設定しています。
 
オートコンプリートで取得したplace(PlaceResultオプジェクト)から名前と住所を取得して情報ウィンドウで表示するパラメータに設定します。

window: {
  showWindow: '',
  templateUrl: 'assets/templates/place4.html',
  templateParameter: {
    name: '',
    address: ''
  },
  options: {
    pixelOffset: {
      height: -50,
      width: 20
    }
  }
},

$scope.map.window.showWindow = true;
var address = '';
if (place.address_components) {
  address = [
   (place.address_components[0] && place.address_components[0].short_name || ''),
   (place.address_components[1] && place.address_components[1].short_name || ''),
   (place.address_components[2] && place.address_components[2].short_name || '')
  ].join(' ');
}
$scope.map.window.templateParameter.name = place.name;
$scope.map.window.templateParameter.address = address;

 
4)オートコンプリートのタイプを選択
 
①HTMLにラジオボタン追加
 
・マップ上に表示するように設定する際に識別するIDとして”type-selector”を指定
 
・3つのラジオボタンを横に並べるためにブートストラップ3の”form-inline”クラスを使用。

<div id="type-selector" class="well well-sm">
  <form class="form-inline">
    <div class="radio">
      <label>
        <input type="radio" name="type" id="changetype-all" checked>
        All
      </label>
    </div>
    <div class="radio">
      <label>
        <input type="radio" name="type" id="changetype-establishment">
        Establishments
      </label>
    </div>
    <div class="radio">
      <label>
        <input type="radio" name="type" id="changetype-geocode">
        Geocodes
      </label>
    </div>
  </form>
</div>

②スクリプトでラジオボタンをマップ上に表示するように設定
 
var types = document.getElementById(‘type-selector’);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(types);
 
③ユーザーがラジオボタンで選択したタイプをAutocompleteのsetTypesメソッドで設定
 
ここで”geocode”を選択すると住所の結果のみ返し、”establishment”を指定するとお店やサービスの結果のみ返します。

var setupClickListener = function (id, types) {
  var radioButton = document.getElementById(id);
  google.maps.event.addDomListener(radioButton, 'click', function() {
    autocomplete.setTypes(types);
  });
}
setupClickListener('changetype-all', []);
setupClickListener('changetype-establishment', ['establishment']);
setupClickListener('changetype-geocode', ['geocode']);

 
5)プレイス検索でオートコンプリートで検索範囲を指定
 
Autocompleteのコンストラクタにはオプションを引数として指定でき、”bounds”属性でプレイスを検索する範囲を指定できます。

検索結果はこの領域内のプレイスが優先されたものとなるようですが、この領域内に限定されるわけではないようです。
 

  var defaultBounds = new google.maps.LatLngBounds(
    new google.maps.LatLng(35.690903370337175, 139.74145889282227),
    new google.maps.LatLng(35.67166118960477, 139.780855178833));
  var options = {
    bounds: defaultBounds,
    types: ['establishment']
  };
  var autocomplete = new google.maps.places.Autocomplete(input,options);

 
AngularJS、Googleマップの他の記事の目次

関連記事の目次

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です