借助地点自动补全数据 API,您可以以编程方式获取地点预测,从而打造自定义的自动补全体验,与自动补全 widget 相比,这种体验具有更精细的控制程度。在本指南中,您将学习如何使用地点自动填充数据 API 根据用户查询发出自动填充请求。
以下示例展示了一个简化的自动补全集成。输入搜索查询内容,例如“披萨”或“poke”,然后点击以选择所需的结果。
“自动填充”请求
自动补全请求会获取查询输入字符串,并返回地点预测结果列表。如需发出自动补全请求,请调用 fetchAutocompleteSuggestions()
并传递包含所需属性的请求。input
属性包含要搜索的字符串;在典型应用中,此值会在用户输入查询时更新。请求应包含 sessionToken
,用于结算。
以下代码段展示了如何创建请求正文并添加会话令牌,然后调用 fetchAutocompleteSuggestions()
以获取 PlacePrediction
的列表。
// Add an initial request body. let request = { input: "Tadi", locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78, }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ["restaurant"], language: "en-US", region: "us", }; // Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token;
限制自动补全预测结果
默认情况下,地点自动补全服务会显示所有地点类型(偏向于用户所在位置附近的预测结果),并提取用户所选地点的所有可用数据字段。通过限制或自定义调整结果来设置地点自动补全选项,可以显示更相关的预测结果。
限制预测结果会导致 Autocomplete widget 忽略限制区域以外的任何结果。常见做法是将结果范围限定在地图边界内。自定义调整结果会使 Autocomplete widget 显示指定区域内的结果,但某些匹配项可能不在这个指定区域内。
使用 origin
属性指定起始点,以便计算到目的地的测地线距离。如果省略此值,则不会返回距离。
使用 includedPrimaryTypes
属性可指定最多 5 个地点类型。
如果未指定任何类型,系统将返回所有类型的地点。
获取地点详情
如需从地点预测结果中返回 Place
对象,请先调用 toPlace()
,然后对生成的 Place
对象调用 fetchFields()
(地点预测中的会话 ID 会自动包含在内)。调用 fetchFields()
会结束自动补全会话。
let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place. await place.fetchFields({ fields: ["displayName", "formattedAddress"], }); const placeInfo = document.getElementById("prediction"); placeInfo.textContent = "First predicted place: " + place.displayName + ": " + place.formattedAddress;
会话令牌
会话令牌将用户自动补全搜索的查询和选择阶段归入不同的会话,以便进行结算。会话在用户开始输入内容时开始。 当用户选择地点并调用“地点详情”时,会话结束。
如需创建新的会话令牌并将其添加到请求中,请创建 AutocompleteSessionToken
的实例,然后设置请求的 sessionToken
属性以使用令牌,如以下代码段所示:
// Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token;
当调用 fetchFields()
时,会话结束。创建 Place
实例后,您无需将会话令牌传递给 fetchFields()
,因为系统会自动处理此问题。
await place.fetchFields({ fields: ["displayName", "formattedAddress"], });
通过创建 AutocompleteSessionToken
的新实例,为下一个会话生成会话令牌。
会话令牌建议:
- 针对所有“地点自动补全”调用使用会话令牌。
- 为每个会话生成一个新的令牌。
- 为每个新会话传递唯一的会话令牌。针对多个会话使用同一令牌会导致每个请求被单独计费。
您可以选择从请求中省略自动补全会话令牌。如果省略会话令牌,系统会单独结算每个请求,从而触发自动补全 - 按请求结算 SKU。如果您重复使用某个会话令牌,相应会话会被视为无效,并且系统会按未提供会话令牌的情况为请求计费。
示例
当用户输入查询内容时,系统会每隔几次击键(而不是每次击键)调用一次自动补全请求,并返回一个可能的结果列表。当用户从结果列表中进行选择时,该选择会被视为一个请求,并且搜索期间的所有请求都会捆绑在一起,并计为一个请求。如果用户选择某个地点,则搜索查询是免费的,只有地点数据请求需要付费。如果用户在会话开始后的几分钟内未做出选择,则仅对搜索查询收费。
从应用的角度来看,事件流如下所示:
- 用户开始输入查询内容,以搜索“法国巴黎”。
- 检测到用户输入后,应用会创建一个新的会话令牌“令牌 A”。
- 随着用户输入,API 每隔几个字符就会发出一次自动补全请求,并针对每个字符显示新的潜在结果列表:
“P”
“Par”
“Paris”
“Paris, Fr”
- 当用户做出选择时:
- 由查询产生的所有请求都会被分组并作为单个请求添加到由“令牌 A”表示的会话中。
- 用户的选择会被计为“地点详情”请求,并添加到由“令牌 A”表示的会话中。
- 会话结束,应用舍弃“令牌 A”。
完整示例代码
本部分包含完整的示例,展示了如何使用地点自动补全数据 API。地点自动补全预测结果
以下示例演示了如何针对输入“Tadi”调用 fetchAutocompleteSuggestions()
,然后对第一个预测结果调用 toPlace()
,最后调用 fetchFields()
以获取地点详情。
TypeScript
/** * Demonstrates making a single request for Place predictions, then requests Place Details for the first result. */ async function init() { // @ts-ignore const { Place, AutocompleteSessionToken, AutocompleteSuggestion } = await google.maps.importLibrary("places") as google.maps.PlacesLibrary; // Add an initial request body. let request = { input: "Tadi", locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78 }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ["restaurant"], language: "en-US", region: "us", }; // Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token; // Fetch autocomplete suggestions. const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request); const title = document.getElementById('title') as HTMLElement; title.appendChild(document.createTextNode('Query predictions for "' + request.input + '":')); for (let suggestion of suggestions) { const placePrediction = suggestion.placePrediction; // Create a new list element. const listItem = document.createElement('li'); const resultsElement = document.getElementById("results") as HTMLElement; listItem.appendChild(document.createTextNode(placePrediction.text.toString())); resultsElement.appendChild(listItem); } let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place. await place.fetchFields({ fields: ['displayName', 'formattedAddress'], }); const placeInfo = document.getElementById("prediction") as HTMLElement; placeInfo.textContent = 'First predicted place: ' + place.displayName + ': ' + place.formattedAddress; } init();
JavaScript
/** * Demonstrates making a single request for Place predictions, then requests Place Details for the first result. */ async function init() { // @ts-ignore const { Place, AutocompleteSessionToken, AutocompleteSuggestion } = await google.maps.importLibrary("places"); // Add an initial request body. let request = { input: "Tadi", locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78, }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ["restaurant"], language: "en-US", region: "us", }; // Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token; // Fetch autocomplete suggestions. const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request); const title = document.getElementById("title"); title.appendChild( document.createTextNode('Query predictions for "' + request.input + '":'), ); for (let suggestion of suggestions) { const placePrediction = suggestion.placePrediction; // Create a new list element. const listItem = document.createElement("li"); const resultsElement = document.getElementById("results"); listItem.appendChild( document.createTextNode(placePrediction.text.toString()), ); resultsElement.appendChild(listItem); } let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place. await place.fetchFields({ fields: ["displayName", "formattedAddress"], }); const placeInfo = document.getElementById("prediction"); placeInfo.textContent = "First predicted place: " + place.displayName + ": " + place.formattedAddress; } init();
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; }
HTML
<html> <head> <title>Place Autocomplete Data API Predictions</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="title"></div> <ul id="results"></ul> <p><span id="prediction"></span></p> <img class="powered-by-google" src="https://storage.googleapis.com/geo-devrel-public-buckets/powered_by_google_on_white.png" alt="Powered by Google" /> <!-- prettier-ignore --> <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))}) ({key: "AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg", v: "weekly"});</script> </body> </html>
试用示例
地点自动补全(含会话)
此示例演示了如何根据用户查询调用 fetchAutocompleteSuggestions()
、显示预测地点列表作为响应,以及最终检索所选地点的地点详情。该示例还演示了如何使用会话令牌将用户查询与最终的“地点详情”请求分组。
TypeScript
let titleElement; let resultsContainerElement; let inputElement; let newestRequestId = 0; // Add an initial request body. const request = { input: '', locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78 }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ['restaurant'], language: 'en-US', region: 'us', }; function init() { titleElement = document.getElementById('title'); resultsContainerElement = document.getElementById('results'); inputElement = document.querySelector('input'); inputElement.addEventListener('input', makeAutocompleteRequest); refreshToken(request); } async function makeAutocompleteRequest(inputEvent) { // Reset elements and exit if an empty string is received. if (inputEvent.target.value == '') { titleElement.innerText = ''; resultsContainerElement.replaceChildren(); return; } // Add the latest char sequence to the request. request.input = inputEvent.target.value; // To avoid race conditions, store the request ID and compare after the request. const requestId = ++newestRequestId; // Fetch autocomplete suggestions and show them in a list. // @ts-ignore const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request); // If the request has been superseded by a newer request, do not render the output. if (requestId !== newestRequestId) return; titleElement.innerText = `Query predictions for "${request.input}"`; // Clear the list first. resultsContainerElement.replaceChildren(); for (const suggestion of suggestions) { const placePrediction = suggestion.placePrediction; // Create a link for the place, add an event handler to fetch the place. const a = document.createElement('a'); a.addEventListener('click', () => { onPlaceSelected(placePrediction!.toPlace()); }); a.innerText = placePrediction!.text.toString(); // Create a new list item element. const li = document.createElement('li'); li.appendChild(a); resultsContainerElement.appendChild(li); } } // Event handler for clicking on a suggested place. async function onPlaceSelected(place) { await place.fetchFields({ fields: ['displayName', 'formattedAddress'], }); const placeText = document.createTextNode(`${place.displayName}: ${place.formattedAddress}`); resultsContainerElement.replaceChildren(placeText); titleElement.innerText = 'Selected Place:'; inputElement.value = ''; refreshToken(request); } // Helper function to refresh the session token. function refreshToken(request) { // Create a new session token and add it to the request. request.sessionToken = new google.maps.places.AutocompleteSessionToken(); } declare global { interface Window { init: () => void; } } window.init = init;
JavaScript
let titleElement; let resultsContainerElement; let inputElement; let newestRequestId = 0; // Add an initial request body. const request = { input: '', locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78 }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ['restaurant'], language: 'en-US', region: 'us', }; function init() { titleElement = document.getElementById('title'); resultsContainerElement = document.getElementById('results'); inputElement = document.querySelector('input'); inputElement.addEventListener('input', makeAutocompleteRequest); refreshToken(request); } async function makeAutocompleteRequest(inputEvent) { // Reset elements and exit if an empty string is received. if (inputEvent.target.value == '') { titleElement.innerText = ''; resultsContainerElement.replaceChildren(); return; } // Add the latest char sequence to the request. request.input = inputEvent.target.value; // To avoid race conditions, store the request ID and compare after the request. const requestId = ++newestRequestId; // Fetch autocomplete suggestions and show them in a list. // @ts-ignore const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request); // If the request has been superseded by a newer request, do not render the output. if (requestId !== newestRequestId) return; titleElement.innerText = `Query predictions for "${request.input}"`; // Clear the list first. resultsContainerElement.replaceChildren(); for (const suggestion of suggestions) { const placePrediction = suggestion.placePrediction; // Create a link for the place, add an event handler to fetch the place. const a = document.createElement('a'); a.addEventListener('click', () => { onPlaceSelected(placePrediction.toPlace()); }); a.innerText = placePrediction.text.toString(); // Create a new list item element. const li = document.createElement('li'); li.appendChild(a); resultsContainerElement.appendChild(li); } } // Event handler for clicking on a suggested place. async function onPlaceSelected(place) { await place.fetchFields({ fields: ['displayName', 'formattedAddress'], }); const placeText = document.createTextNode(`${place.displayName}: ${place.formattedAddress}`); resultsContainerElement.replaceChildren(placeText); titleElement.innerText = 'Selected Place:'; inputElement.value = ''; refreshToken(request); } // Helper function to refresh the session token. function refreshToken(request) { // Create a new session token and add it to the request. request.sessionToken = new google.maps.places.AutocompleteSessionToken(); } window.init = init;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } a { cursor: pointer; text-decoration: underline; color: blue; } input { width: 300px; }
HTML
<html> <head> <title>Place Autocomplete Data API Session</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <input id="input" type="text" placeholder="Search for a place..." /> <div id="title"></div> <ul id="results"></ul> <img class="powered-by-google" src="https://storage.googleapis.com/geo-devrel-public-buckets/powered_by_google_on_white.png" alt="Powered by Google" /> <!-- The `defer` attribute causes the script to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises. See https://developers.google.com/maps/documentation/javascript/load-maps-js-api for more information. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8&callback=init&libraries=places&v=weekly" defer ></script> </body> </html>