2015年5月9日 星期六

Hybrid Apps開發系列之6.2:使用Google Maps API顯示地圖

地圖服務是行動apps最常提供的功能。本文介紹如何在Ionic Apps裡使用Google Maps API顯示地圖。
 圖1. 顯示Google Map

前置動作:產生Google API Key

Google API的使用方式如下:
http://maps.googleapis.com/maps/api/js?key=[你的API金鑰]&sensor=true
因此必須先取得API金鑰,步驟如下:
  1. 前往Google Developers Console (https://console.developers.google.com/)。
  2. 以gmail帳號登入後,點選「建立專案」,並輸入專案名稱。
  3. 進到專案頁面,選取左側選單「API和驗證」項目下的「API」,從右邊各種API列表中選取Google Maps JavaScript API,並在下一個畫面啟動之。
  4. 在「API和驗證」項目下的「憑證」頁面,點選「建立新的金鑰」,選擇「瀏覽器金鑰」,並輸入http:/localhost:8100/*。
  5. 將「API 金鑰」複製起來,放到index.html引用Google Maps API的地方,例如:
  6. <script src="http://maps.googleapis.com/maps/api/js?key=[你的API 金鑰]&sensor=true"></script>

此外,如果需要行動平台的目前位置資訊,則需要安裝Apache Cordova Geolocation外掛。
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-geolocation.git

建立Ionic 專案

ionic start GoogleMap blank
cd GoogleMap
ionic platform add android
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-geolocation.git

加入Google Maps API

Google Maps API無法下載放進專案內,必須直接加入index.html:
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="js/services.js"></script>
<script src="http://maps.googleapis.com/maps/api/js?key=[API金鑰]&sensor=true"></script>
將前置動作取得的API key置換上去,便完成設定。

程式碼說明

如圖1所示,範例程式有兩個頁面:清單頁面templates/list.html與細節瀏覽頁面templates/detail.html,Google地圖置於後頁。資料部份與地圖呼叫服務放在js/services.js(請在index.html內加入js/services.js的設定,並於js/app.js注入js/services.js內的自訂模組),方便後續擴充。

js/services.js

angular.module('starter.services', [])
        .factory('MAP', function () {
            var self = this;
            self.initialize = function (id) {
                var LatLng = navigator.geolocation.getCurrentPosition(function (pos) {
                    return new google.maps.LatLng(pos.coords.latitude,
                            pos.coords.longitude)
                }) // 目前位置
                var mapOptions = {
                    center: LatLng,
                    zoom: 15,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                };
                var map = new google.maps.Map(document.getElementById(id), mapOptions);
                return map;
            }

            self.setLocation = function (map, pos) {
                var marker = new google.maps.Marker({
                    position: new google.maps.LatLng(pos.lat, pos.lng),
                    map: map,
                    title: pos.title
                });
                map.setCenter(marker.getPosition());
                return map;
            }
            return self
        })
        .value('POIs', [{
                "entrance": "宜蘭縣南澳鄉",
                "level": "國家級步道",
                "manager": "羅東林區管理處",
                "name": "蘇花古道:大南澳越嶺段",
                "number": "1",
                "second_system": "無",
                "system": "蘇花-比亞毫國家步道系統",
                "latlng": [24.26467, 121.740875]
            }, {
                ...[略]...
            }]);
  • 第4-16行:定義initialize()。其中第5行使用Cordova Geolocation外掛,取得現在位置的經緯度,做為地圖的預設位置。其餘都是根據Google Maps API的文件說明,建立地圖。詳情可參考Google Maps API官網
  • 第18-26行:定義setLocation()。同樣根據Google Maps API的文件說明,建立marker,並以marker為中心顯示地圖。
  • 第29行以後:步道資料部份。

主程式js/app.js

var map=null;
angular.module('starter', ['ionic', 'starter.services'])
        .config(function ($stateProvider, $urlRouterProvider) {
            $stateProvider
                    .state('list', {
                        url: '/list',
                        templateUrl: 'templates/list.html',
                        controller: 'listCtrl'
                    })
                    .state('detail', {
                        url: '/list/:id',
                        templateUrl: 'templates/detail.html',
                        controller: 'detailCtrl'
                    })
            $urlRouterProvider.otherwise('/list')
        })
        .controller('listCtrl', function ($scope, POIs, $location) {
            $scope.pois = POIs;
            $scope.detail = function (id) {
                $location.path('/list/' + id);
            }
        })
        .controller('detailCtrl', function ($scope, MAP, $stateParams, POIs, $filter) {
            $scope.poi = $filter('filter')(POIs, {number: $stateParams.id})[0];
            map = MAP.initialize('map');  // 載入地圖 'map'是顯示地圖區塊的id
            var marker = {lat: ($scope.poi.latlng)[0], lng: ($scope.poi.latlng)[1], title: $scope.poi.name};
            MAP.setLocation(map, marker);

        })
  • 第2行:注入starter.services模組。定義於js/services.js內。注意index.html也必須加入:
    <script src="js/services.js"></script>
  • 第4-16行:定義兩個states。第11行url值設定為'/list/:id',id便成為之後頁面傳遞的參數名稱。
  • 第17-22行:清單頁面的controller。其中POIs是js/services.js以.value()定義的步道資料內容。第19行的id對應到步道資料內容的'number'欄位。
  • 第23-29行:細節瀏覽頁面的controller。MAP是js/services.js以.factory()定義的Google Maps相關服務。
  • 第24行:以$filter('filter')(array, 過濾條件)的方式,從POIs篩出要顯示的資料。此處用number欄位,篩出值為$stateParams.id的資料。
  • 第25行:建立Google地圖。Map.initialize()的參數是顯示地圖<div>的id設定值。
  • 第26行:建立marker。
  • 第27行:以marker為中心顯示地圖。

templates/list.html, templates/detail.html與css/style.css

list.html檔
<ion-view title="步道系統">
    <ion-content>
        <ion-list ng-repeat="item in pois">
            <ion-item ng-click="detail({{item.number}})">
                {{item.name}}
            </ion-item>
        </ion-list>       
    </ion-content>
</ion-view>
detail.html檔
<ion-view title="{{poi.name}}">    
    <ion-content>
        <div class="card list">
            <div class="item item-avatar-left">
                <img src="img/ionic.png">
                <h2>{{poi.name}}</h2>
                <p class="row">
                    <span class="col-20">地點</span><span class="col-80">{{poi.entrance}}</span>
                </p>
                <p class="row">
                    <span class="col-20">等級</span><span class="col-80">{{poi.level}}</span>
                </p>
                <p class="row">
                    <span class="col-20">系統</span><span class="col-80">{{poi.system}}</span>
                </p>
            </div>
            <div class="item item-body">
                <div class="row"> 
                    <div class="col col-center">
                        <div style="width:250px;height:250px;" id="map" data-tap-disabled="true"></div>
                    </div>
                </div>
            </div>
        </div>
    </ion-content>
</ion-view>
自訂CSS:css/style.css,由於ionic的加了許多css設定,此檔覆蓋一些css設定,讓google地圖能正常顯示。
.scroll {
    height: 100%;
} 
#map {
    width: 100%;
    height: 100%;
}
完整程式可參考GitHub Gist專案

沒有留言:

張貼留言