1. 事前準備
瞭解如何使用 Google 地圖平台地圖和地點 API 建構在地商家搜尋功能,為使用者進行地理位置定位,並顯示附近的有趣地點。這個應用程式整合了地理位置、Place Details、Place Photos 等功能。
必要條件
- 具備 HTML、CSS 和 JavaScript 基礎知識
- 已連結帳單帳戶的專案 (如果沒有,請按照下一個步驟的指示操作)。
- 在下方的啟用步驟中,您需要啟用 Maps JavaScript API 和 Places API。
- 上述專案的 API 金鑰。
開始使用 Google 地圖平台
如果您從未使用過 Google 地圖平台,請按照「開始使用 Google 地圖平台」指南或「開始使用 Google 地圖平台」播放清單中的操作說明,完成下列步驟:
- 建立帳單帳戶。
- 建立專案。
- 啟用 Google 地圖平台 API 和 SDK (如上一節所列)。
- 產生 API 金鑰。
執行步驟
- 建構顯示 Google 地圖的網頁
- 將地圖中心設為使用者所在位置
- 尋找附近地點,並以可點選的標記顯示結果
- 擷取並顯示每個地點的詳細資訊
軟硬體需求
- 網路瀏覽器,例如 Google Chrome (建議)、Firefox、Safari 或 Internet Explorer
- 慣用的文字或程式碼編輯器
取得範例程式碼
- 開啟指令列介面 (MacOS 上的「終端機」或 Windows 上的「命令提示字元」),然後使用下列指令下載範例程式碼:
git clone https://github.com/googlecodelabs/google-maps-nearby-search-js/
如果無法解決問題,請點選下方按鈕下載這個程式碼研究室的所有程式碼,然後解壓縮檔案:
- 變更為您剛複製或下載的目錄。
cd google-maps-nearby-search-js
stepN
資料夾包含本程式碼研究室每個步驟的最終狀態。僅供參考。在名為 work
的目錄中完成所有編碼工作。
2. 建立以預設中心為中心的地圖
在網頁上建立 Google 地圖時,步驟共有三個:
- 建立 HTML 網頁
- 新增地圖
- 貼上 API 金鑰
1. 建立 HTML 網頁
以下是這個步驟建立的地圖。地圖中心位於澳洲雪梨的雪梨歌劇院。如果使用者拒絕授予位置存取權,地圖會預設顯示這個位置,但仍會提供有趣的搜尋結果。
- 將目錄變更為
work/
資料夾。在本程式碼研究室的其餘部分,請在work/
資料夾中的版本進行編輯。
cd work
- 在
work/
目錄中,使用文字編輯器建立名為index.html
的空白檔案。 - 將下列程式碼複製到
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>
- 在網路瀏覽器中開啟
index.html
檔案。
open index.html
2. 新增地圖
本節說明如何在網頁中載入 Maps JavaScript API,以及如何自行編寫 JavaScript,以使用 API 在網頁中加入地圖。
- 在
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 金鑰
- 在
<!-- TODO: Step 1C, Get an API 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>
- 儲存您正在處理的 HTML 檔案。
測試
重新載入瀏覽器中您正在編輯的檔案。現在應該會看到地圖,取代先前的灰色矩形。如果看到錯誤訊息,請確認您已將最終 <script>
標記中的「YOUR_API_KEY
」換成自己的 API 金鑰。如尚未取得 API 金鑰,請參閱上文瞭解如何取得。
完整程式碼範例
您可以在 Github 取得這個專案目前為止的完整程式碼。
3. 找出使用者的地理位置
接下來,您要使用瀏覽器的 HTML5 地理位置功能以及 Maps JavaScript API,在 Google 地圖上顯示使用者或裝置的地理位置。
以下是地圖範例,顯示您在加州山景城瀏覽時的地理位置:
什麼是地理位置?
地理位置是指透過各種資料收集機制,針對使用者或運算裝置識別出的地理位置。一般而言,大部分的地理定位服務利用網路路線規劃位址或利用內部 GPS 裝置,來判斷位置。這個應用程式會使用網路瀏覽器的 W3C 地理位置標準 navigator.geolocation
屬性,判斷使用者的位置。
親自試試
將註解 TODO: Step 2, Geolocate your user
和 END 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 */
測試
- 儲存檔案。
- 重新載入頁面。
瀏覽器現在應該會要求您授權應用程式分享位置資訊。
- 按一下「Block」一次,看看是否能順利處理錯誤,並維持以雪梨為中心。
- 再次重新載入,然後按一下「允許」,查看地理位置資訊是否正常運作,並將地圖移至目前所在位置。
完整程式碼範例
您可以在 Github 取得這個專案目前為止的完整程式碼。
4. 搜尋附近地點
「搜尋附近」可讓您依照關鍵字或類型,搜尋指定區域內的地點。「搜尋附近」必須一律包含位置,可透過以下任一方法指定:
- 定義矩形搜尋區域的
LatLngBounds
物件 - 使用
location
屬性 (將圓心指定為LatLng
物件) 與半徑 (以公尺為單位) 的組合定義圓形區域。
呼叫 PlacesService
nearbySearch()
方法,啟動「搜尋附近」要求,傳回 PlaceResult
物件的陣列。
A. 載入 Places Library
首先,如要存取 Places Library 服務,請更新指令碼來源網址,加入 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
物件,或location
和radius
;前者需要採用google.maps.LatLng
物件,後者需要採用簡單整數,代表圓形的半徑 (以公尺為單位)。允許的最大半徑為 50,000 公尺。請注意,當rankBy
設為DISTANCE
時,您必須指定位置,但無法指定半徑或範圍。- 要與所有可用欄位比對的
keyword
,包括但不限於名稱、類型、地址、顧客評論和其他第三方內容,或type
,可限制結果只顯示符合指定類型的地點。您只能指定一種類型 (如果提供多種類型,系統會忽略第一種類型之後輸入的所有類型)。請參閱支援類型清單。
在本程式碼研究室中,您會使用使用者的目前位置做為搜尋位置,並依距離排序結果。
- 在註解
TODO: Step 3B1
中新增下列內容,編寫兩個函式來呼叫搜尋並處理回應。
系統會使用 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 */
- 在註解
TODO: Step 3B2
處,將這行程式碼新增至initMap
函式的結尾。
/* TODO: Step 3B2, Call the Places Nearby Search */
// Call Places Nearby Search on user's location
getNearbyPlaces(pos);
- 在註解
TODO: Step 3B3
處,將這行程式碼新增至handleLocationError
函式的結尾。
/* 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
後方新增下列程式碼,為回應中傳回的每個地點設定位置、地圖和標題。您也可以使用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 */
測試
- 儲存並重新載入頁面,然後按一下「允許」,授予地理位置資訊權限。
地圖中心位置周圍最多會顯示 20 個紅色標記。
- 再次重新載入網頁,這次請封鎖地理位置資訊存取權。
您是否仍會在預設地圖中心 (在範例中,預設中心位於澳洲雪梨) 取得結果?
完整程式碼範例
您可以在 Github 取得這個專案目前為止的完整程式碼。
5. 視需要顯示地點詳細資料
取得地點的 Place ID (以 Nearby Search 結果中的其中一個欄位形式提供) 後,您就可以要求該地點的其他詳細資料,例如完整地址、電話號碼、使用者評分和評論。在本程式碼研究室中,您將製作側邊欄來顯示豐富的 Place Details,並讓標記成為互動式元素,方便使用者選取地點來查看詳細資料。
A. 製作一般側欄
您需要顯示地點詳細資料的位置,因此以下提供側邊欄的簡單程式碼,使用者點選標記時,即可滑出並顯示地點詳細資料。
- 在註解
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;
}
- 在
map
div 前方的body
區段中,新增詳細資料面板的 div。
<!-- TODO: Step 4A2: Add a generic sidebar -->
<!-- The slide-out panel for showing place details -->
<div id="panel"></div>
- 在
TODO: Step 4A3
註解後方的initMap()
函式中,初始化infoPane
變數,如下所示:
/* TODO: Step 4A3: Add a generic sidebar */
infoPane = document.getElementById('panel');
B. 將點按事件監聽器新增至標記
- 在
createMarkers
函式中,建立每個標記時,請為標記新增點按事件監聽器。
點擊事件監聽器會擷取與該標記相關聯的地點詳細資料,並呼叫函式來顯示詳細資料。
- 將下列程式碼貼到程式碼註解
TODO: Step 4B
的createMarkers
函式中。
下一節會實作 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. 在資訊視窗中顯示地點詳細資料
資訊視窗會在對話方塊中顯示內容 (通常為文字或圖片),並顯示在地圖上特定位置的上方。資訊視窗是以一個內容區域以及一個錐形柄所組成。錐形柄的尖端會連接地圖上的指定位置。一般來說,資訊視窗會附加至標記,但您也可以附加至特定經緯度。
- 在註解
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 的任意變數名稱。每當使用者點選新標記時,這段程式碼會關閉側邊欄 (如果已開啟)、清除舊的詳細資料、新增詳細資料,然後開啟側邊欄。
- 在註解
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. 顯示地點相片和地點詳細資料
getDetails
結果會傳回與 placeId
相關聯的陣列,最多可包含 10 張相片。在這裡,您會在側欄中的地點名稱上方顯示第一張相片。
- 如要讓相片顯示在側欄頂端,請在建立
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);
}
測試
- 儲存並重新載入瀏覽器中的頁面,然後允許地理位置資訊權限。
- 按一下標記,即可查看標記彈出的資訊視窗,當中會顯示一些詳細資料,而側欄則會從左側滑出,顯示更多詳細資料。
- 重新載入並拒絕地理位置權限後,測試搜尋功能是否仍可運作。編輯搜尋關鍵字,查詢不同內容,並探索該搜尋傳回的結果。
完整程式碼範例
您可以在 Github 取得這個專案目前為止的完整程式碼。
6. 恭喜
恭喜!您使用了 Maps JavaScript API 的許多功能,包括 Places
程式庫。
涵蓋內容
- 使用 google.maps.Map 類別建立地圖
- 使用使用者的瀏覽器進行地理位置查詢,並在地圖上顯示結果
- 在地圖中加入標記,並回應使用者點擊標記的事件
- 新增資訊視窗,在使用者點選標記時顯示更多資訊
- 載入 Places Library 並執行附近搜尋
- 擷取及顯示地點詳細資料和地點相片
瞭解詳情
如要進一步運用地圖,請參閱 Maps JavaScript API 說明文件和 Places Library 說明文件,兩者都包含指南、教學課程、API 參考資料、更多程式碼範例和支援管道。熱門功能包括將資料匯入地圖、開始設定地圖樣式,以及新增 Street View 服務。
您最希望我們接下來建構哪種類型的程式碼研究室?
如果想找的程式碼研究室未列於上方,請在這裡提出新的問題。