Разместить API данных автозаполнения

Разработчики Европейской экономической зоны (ЕЭЗ)

API данных автозаполнения мест позволяет программно получать подсказки мест для создания персонализированных вариантов автозаполнения с более точным контролем, чем с помощью виджета автозаполнения. В этом руководстве вы узнаете, как использовать API данных автозаполнения мест для создания запросов автозаполнения на основе пользовательских запросов.

В следующем примере показана упрощённая интеграция с опережающим вводом. Введите поисковый запрос, например «пицца» или «поке», затем нажмите, чтобы выбрать нужный результат.

Автозаполнение запросов

Запрос автодополнения принимает входную строку запроса и возвращает список подсказок мест. Чтобы создать запрос автодополнения, вызовите функцию 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;

Ограничить прогнозы автозаполнения

По умолчанию автозаполнение мест отображает все типы мест, ориентируясь на прогнозы, близкие к местоположению пользователя, и извлекает все доступные поля данных для выбранного пользователем места. Настройте параметры автозаполнения мест, чтобы отображать более релевантные прогнозы, ограничивая или изменяя результаты.

Ограничение результатов приводит к тому, что виджет автозаполнения игнорирует все результаты, находящиеся за пределами области ограничения. Распространенной практикой является ограничение результатов границами карты. Смещение результатов приводит к тому, что виджет автозаполнения отображает результаты в пределах указанной области, но некоторые совпадения могут находиться за её пределами.

Используйте свойство origin для указания начальной точки, от которой рассчитывается геодезическое расстояние до пункта назначения. Если это значение пропущено, расстояние не возвращается.

Используйте свойство includedPrimaryTypes , чтобы указать до пяти типов мест . Если типы не указаны, будут возвращены места всех типов.

См. справочник API

Получить информацию о месте

Чтобы вернуть объект Place из результата прогнозирования места, сначала вызовите toPlace() , а затем вызовите fetchFields() для полученного объекта Place (идентификатор сеанса из прогнозирования места добавляется автоматически). Вызов 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;

Токены сеанса

Токены сеанса объединяют этапы запроса и выбора в рамках поиска с автодополнением в отдельный сеанс для целей выставления счетов. Сеанс начинается с момента начала ввода текста пользователем. Сеанс завершается выбором места и вызовом Place Details.

Чтобы создать новый токен сеанса и добавить его в запрос, создайте экземпляр 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 .

Рекомендации по токенам сеанса:

  • Используйте токены сеанса для всех вызовов Place Autocomplete.
  • Генерируйте новый токен для каждого сеанса.
  • Передавайте уникальный токен сеанса для каждого нового сеанса. Использование одного и того же токена для нескольких сеансов приведёт к тому, что каждый запрос будет оплачиваться отдельно.

При желании вы можете исключить токен сеанса автозаполнения из запроса. Если токен сеанса отсутствует, каждый запрос оплачивается отдельно, активируя SKU « Автозаполнение — за запрос» . При повторном использовании токена сеанса он считается недействительным, и запросы оплачиваются так, как если бы токен сеанса не был предоставлен.

Пример

По мере ввода пользователем запроса каждые несколько нажатий клавиш (не посимвольно) вызывается запрос автодополнения, и возвращается список возможных результатов. Когда пользователь делает выбор из списка результатов, этот выбор считается запросом, и все запросы, сделанные в ходе поиска, объединяются и учитываются как один запрос. Если пользователь выбирает место, поисковый запрос предоставляется бесплатно, а плата взимается только за запрос данных о месте. Если пользователь не делает выбор в течение нескольких минут после начала сеанса, плата взимается только за поисковый запрос.

С точки зрения приложения поток событий выглядит следующим образом:

  1. Пользователь начинает вводить поисковый запрос «Париж, Франция».
  2. При обнаружении пользовательского ввода приложение создает новый токен сеанса — «Токен А».
  3. По мере того, как пользователь печатает, API каждые несколько символов отправляет запрос на автодополнение, отображая для каждого из них новый список потенциальных результатов:
    "П"
    "Пар"
    "Париж,"
    "Париж, Фр."
  4. Когда пользователь делает выбор:
    • Все запросы, полученные в результате запроса, группируются и добавляются в сеанс, представленный «Токеном А», как один запрос.
    • Выбор пользователя учитывается как запрос сведений о месте и добавляется к сеансу, представленному «Токеном A».
  5. Сеанс завершается, и приложение сбрасывает «Токен А».
Узнайте, как оплачиваются сеансы

Полный пример кода

В этом разделе содержатся полные примеры, демонстрирующие, как использовать API Place Autocomplete Data.

Место автозаполнения прогнозов

В следующем примере демонстрируется вызов fetchAutocompleteSuggestions() для входных данных «Tadi», затем вызов toPlace() для первого результата прогнозирования, а затем вызов fetchFields() для получения сведений о месте.

Машинопись

/**
 * 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() на основе запросов пользователя и отображение списка предсказанных мест в ответе.
  • Использование токенов сеанса для группировки запроса пользователя с конечным запросом сведений о месте.
  • Получение информации о выбранном месте и отображение маркера.
  • Использование слотирования управления для вложения элементов пользовательского интерфейса в элемент gmp-map .

Машинопись

const mapElement = document.querySelector('gmp-map') as google.maps.MapElement;
let innerMap: google.maps.Map;
let marker: google.maps.marker.AdvancedMarkerElement;
let titleElement = document.querySelector('.title') as HTMLElement;
let resultsContainerElement = document.querySelector('.results') as HTMLElement;
let inputElement = document.querySelector('input') as HTMLInputElement;
let tokenStatusElement = document.querySelector('.token-status') as HTMLElement;
let newestRequestId = 0;
let tokenCount = 0;

// Create an initial request body.
const request: google.maps.places.AutocompleteRequest = {
    input: '',
    includedPrimaryTypes: [
        'restaurant',
        'cafe',
        'museum',
        'park',
        'botanical_garden',
    ],
}

async function init() {
    await google.maps.importLibrary('maps');
    innerMap = mapElement.innerMap;
    innerMap.setOptions({
        mapTypeControl: false,
    });

    // Update request center and bounds when the map bounds change.
    google.maps.event.addListener(innerMap, 'bounds_changed', async () => {
        request.locationRestriction = innerMap.getBounds();
        request.origin = innerMap.getCenter();
    });

    inputElement.addEventListener('input', makeAutocompleteRequest);
}

async function makeAutocompleteRequest(inputEvent) {
    // To avoid race conditions, store the request ID and compare after the request.
    const requestId = ++newestRequestId;

    const { AutocompleteSuggestion } = (await google.maps.importLibrary(
        'places'
    )) as google.maps.PlacesLibrary;

    if (!inputEvent.target?.value) {
        titleElement.textContent = '';
        resultsContainerElement.replaceChildren();
        return;
    }

    // Add the latest char sequence to the request.
    request.input = (inputEvent.target as HTMLInputElement).value;

    // Fetch autocomplete suggestions and show them in a list.
    const { suggestions } =
        await AutocompleteSuggestion.fetchAutocompleteSuggestions(request);

    // If the request has been superseded by a newer request, do not render the output.
    if (requestId !== newestRequestId) return;

    titleElement.innerText = `Place predictions for "${request.input}"`;

    // Clear the list first.
    resultsContainerElement.replaceChildren();

    for (const suggestion of suggestions) {
        const placePrediction = suggestion.placePrediction;

        if (!placePrediction) {
            continue;
        }

        // Create a link for the place, add an event handler to fetch the place.
        // We are using a button element to take advantage of its a11y capabilities.
        const placeButton = document.createElement('button');
        placeButton.addEventListener('click', () => {
            onPlaceSelected(placePrediction.toPlace());
        });
        placeButton.textContent = placePrediction.text.toString();
        placeButton.classList.add('place-button');

        // Create a new list item element.
        const li = document.createElement('li');
        li.appendChild(placeButton);
        resultsContainerElement.appendChild(li);
    }
}

// Event handler for clicking on a suggested place.
async function onPlaceSelected(place: google.maps.places.Place) {
    const { AdvancedMarkerElement } = (await google.maps.importLibrary(
        'marker'
    )) as google.maps.MarkerLibrary;

    await place.fetchFields({
        fields: ['displayName', 'formattedAddress', 'location'],
    });

    resultsContainerElement.textContent = `${place.displayName}: ${place.formattedAddress}`;
    titleElement.textContent = 'Selected Place:';
    inputElement.value = '';

    await refreshToken();

    // Remove the previous marker, if it exists.
    if (marker) {
        marker.remove();
    }

    // Create a new marker.
    marker = new AdvancedMarkerElement({
        map: innerMap,
        position: place.location,
        title: place.displayName,
    })

    // Center the map on the selected place.
    if (place.location) {
        innerMap.setCenter(place.location);
        innerMap.setZoom(15);
    }
}

// Helper function to refresh the session token.
async function refreshToken() {
    const { AutocompleteSessionToken } = (await google.maps.importLibrary(
        'places'
    )) as google.maps.PlacesLibrary;

    // Increment the token counter.
    tokenCount++;

    // Create a new session token and add it to the request.
    request.sessionToken = new AutocompleteSessionToken();
    tokenStatusElement.textContent = `Session token count: ${tokenCount}`;
}

init();

JavaScript

const mapElement = document.querySelector('gmp-map');
let innerMap;
let marker;
let titleElement = document.querySelector('.title');
let resultsContainerElement = document.querySelector('.results');
let inputElement = document.querySelector('input');
let tokenStatusElement = document.querySelector('.token-status');
let newestRequestId = 0;
let tokenCount = 0;
// Create an initial request body.
const request = {
    input: '',
    includedPrimaryTypes: [
        'restaurant',
        'cafe',
        'museum',
        'park',
        'botanical_garden',
    ],
};
async function init() {
    await google.maps.importLibrary('maps');
    innerMap = mapElement.innerMap;
    innerMap.setOptions({
        mapTypeControl: false,
    });
    // Update request center and bounds when the map bounds change.
    google.maps.event.addListener(innerMap, 'bounds_changed', async () => {
        request.locationRestriction = innerMap.getBounds();
        request.origin = innerMap.getCenter();
    });
    inputElement.addEventListener('input', makeAutocompleteRequest);
}
async function makeAutocompleteRequest(inputEvent) {
    // To avoid race conditions, store the request ID and compare after the request.
    const requestId = ++newestRequestId;
    const { AutocompleteSuggestion } = (await google.maps.importLibrary('places'));
    if (!inputEvent.target?.value) {
        titleElement.textContent = '';
        resultsContainerElement.replaceChildren();
        return;
    }
    // Add the latest char sequence to the request.
    request.input = inputEvent.target.value;
    // Fetch autocomplete suggestions and show them in a list.
    const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request);
    // If the request has been superseded by a newer request, do not render the output.
    if (requestId !== newestRequestId)
        return;
    titleElement.innerText = `Place predictions for "${request.input}"`;
    // Clear the list first.
    resultsContainerElement.replaceChildren();
    for (const suggestion of suggestions) {
        const placePrediction = suggestion.placePrediction;
        if (!placePrediction) {
            continue;
        }
        // Create a link for the place, add an event handler to fetch the place.
        // We are using a button element to take advantage of its a11y capabilities.
        const placeButton = document.createElement('button');
        placeButton.addEventListener('click', () => {
            onPlaceSelected(placePrediction.toPlace());
        });
        placeButton.textContent = placePrediction.text.toString();
        placeButton.classList.add('place-button');
        // Create a new list item element.
        const li = document.createElement('li');
        li.appendChild(placeButton);
        resultsContainerElement.appendChild(li);
    }
}
// Event handler for clicking on a suggested place.
async function onPlaceSelected(place) {
    const { AdvancedMarkerElement } = (await google.maps.importLibrary('marker'));
    await place.fetchFields({
        fields: ['displayName', 'formattedAddress', 'location'],
    });
    resultsContainerElement.textContent = `${place.displayName}: ${place.formattedAddress}`;
    titleElement.textContent = 'Selected Place:';
    inputElement.value = '';
    await refreshToken();
    // Remove the previous marker, if it exists.
    if (marker) {
        marker.remove();
    }
    // Create a new marker.
    marker = new AdvancedMarkerElement({
        map: innerMap,
        position: place.location,
        title: place.displayName,
    });
    // Center the map on the selected place.
    if (place.location) {
        innerMap.setCenter(place.location);
        innerMap.setZoom(15);
    }
}
// Helper function to refresh the session token.
async function refreshToken() {
    const { AutocompleteSessionToken } = (await google.maps.importLibrary('places'));
    // Increment the token counter.
    tokenCount++;
    // Create a new session token and add it to the request.
    request.sessionToken = new AutocompleteSessionToken();
    tokenStatusElement.textContent = `Session token count: ${tokenCount}`;
}
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;
}

.place-button {
  height: 3rem;
  width: 100%;
  background-color: transparent;
  text-align: left;
  border: none;
  cursor: pointer;
}

.place-button:focus-visible {
  outline: 2px solid #0056b3;
  border-radius: 2px;
}

.input {
  width: 300px;
  font-size: small;
  margin-bottom: 1rem;
}

/* Styles for the floating panel */
.controls {
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  font-family: sans-serif;
  font-size: small;
  margin: 12px;
  padding: 1rem;
}

.title {
  font-weight: bold;
  margin-top: 1rem;
  margin-bottom: 0.5rem;
}

.results {
  list-style-type: none;
  margin: 0;
  padding: 0;
}

.results li:not(:last-child) {
  border-bottom: 1px solid #ddd;
}

.results li:hover {
  background-color: #eee;
}

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>
        <!-- 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: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8", v: "weekly"});</script>
    </head>
    <body>
        <gmp-map center="37.7893, -122.4039" zoom="12" map-id="DEMO_MAP_ID">
            <div
                class="controls"
                slot="control-inline-start-block-start"
            >
                <input
                    type="text"
                    class="input"
                    placeholder="Search for a place..."
                    autocomplete="off"
                /><!-- Turn off the input's own autocomplete (not supported by all browsers).-->
                <div class="token-status"></div>
                <div class="title"></div>
                <ol class="results"></ol>
            </div>
        </gmp-map>
    </body>
</html>

Попробуйте образец