Place Autocomplete Data API

באמצעות Place Autocomplete Data API אפשר לאחזר תחזיות של מקומות באופן פרוגרמטי, כדי ליצור חוויות מותאמות אישית של השלמה אוטומטית עם רמת שליטה גבוהה יותר מזו שאפשר לקבל באמצעות הווידג'ט של ההשלמה האוטומטית. במדריך הזה תלמדו איך להשתמש ב-Place Autocomplete Data 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"],
});
await place.fetchFields({
    fields: ['displayName'],
  });

יוצרים מופע חדש של AutocompleteSessionToken כדי ליצור אסימון סשן לסשן הבא.

המלצות בנוגע לטוקן לסשן:

  • שימוש באסימוני סשן בכל הקריאות להשלמה האוטומטית של מקומות.
  • יוצרים טוקן חדש לכל סשן.
  • מעבירים אסימון סשן ייחודי לכל סשן חדש. שימוש באותו אסימון ביותר מסשן אחד יביא לחיוב בנפרד על כל בקשה.

אפשר להשמיט את אסימון הסשן של ההשלמה האוטומטית מהבקשה. אם לא תכללו את אסימון הסשן, כל בקשה תחויב בנפרד, ותגרום לחיוב לפי המק"ט Autocomplete – Per Request. אם משתמשים שוב באסימון סשן, הסשן נחשב לא תקף והבקשות יחויבו כאילו לא סופק אסימון סשן.

דוגמה

כשהמשתמש מקלידים שאילתות, המערכת מפעילה בקשה להשלמה אוטומטית אחרי כל כמה הקשות (לא לכל תו) ומציגה רשימה של תוצאות אפשריות. כשהמשתמש בוחר מתוך רשימת התוצאות, הבחירה נספרת כבקשה, וכל הבקשות שנשלחות במהלך החיפוש מקובצות ונספרות כבקשה אחת. אם המשתמש בוחר מקום, שאילתת החיפוש זמינה ללא תשלום, ורק הבקשה לנתוני המקום מחויבת. אם המשתמש לא מבצע בחירה תוך כמה דקות מתחילת הסשן, תחויבו רק על שאילתה החיפוש.

מנקודת המבט של אפליקציה, רצף האירועים הוא:

  1. משתמש מתחיל להקליד שאילתת חיפוש עבור 'פריז, צרפת'.
  2. כשהאפליקציה מזהה קלט של משתמש, היא יוצרת טוקן סשן חדש, 'אסימון א'.
  3. כשהמשתמש מקלידים, ה-API שולח בקשה להשלמה אוטומטית אחרי כל כמה תווים, ומציג רשימה חדשה של תוצאות אפשריות לכל אחד מהם:
    "P"
    "Par"
    "Paris,"
    "Paris, Fr"
  4. כשהמשתמש מבצע בחירה:
    • כל הבקשות שנובעות מהשאילתה מקובצות ומתווספות לסשן שמיוצג על ידי אסימון א', כבקשה אחת.
    • הבחירה של המשתמש נספרת כבקשה לפרטים של מקום, ומתווספת לסשן שמיוצג על ידי אסימון א'.
  5. הסשן מסתיים והאפליקציה משליכה את אסימון א'.
איך מתבצע החיוב על סשנים

קוד לדוגמה מלא

בקטע הזה מפורטות דוגמאות מלאות לשימוש ב-Place Autocomplete Data API .

הצעות להשלמה אוטומטית של מקומות

בדוגמה הבאה מוצגת קריאה ל-fetchAutocompleteSuggestions() עם הקלט 'Tadi', ואז קריאה ל-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 title;
let results;
let input;
let token;

// Add an initial request body.
let 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",
};

async function init() {
    token = new google.maps.places.AutocompleteSessionToken();

    title = document.getElementById('title');
    results = document.getElementById('results');
    input = document.querySelector("input");
    input.addEventListener("input", makeAcRequest);
    request = refreshToken(request) as any;
}

async function makeAcRequest(input) {
    // Reset elements and exit if an empty string is received.
    if (input.target.value == '') {
        title.innerText = '';
        results.replaceChildren();
        return;
    }

    // Add the latest char sequence to the request.
    request.input = input.target.value;

    // Fetch autocomplete suggestions and show them in a list.
    // @ts-ignore
    const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request);

    title.innerText = 'Query predictions for "' + request.input + '"';

    // Clear the list first.
    results.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 element.
        const li = document.createElement('li');
        li.appendChild(a);
        results.appendChild(li);
    }
}

// Event handler for clicking on a suggested place.
async function onPlaceSelected(place) {
    await place.fetchFields({
        fields: ['displayName', 'formattedAddress'],
    });
    let placeText = document.createTextNode(place.displayName + ': ' + place.formattedAddress);
    results.replaceChildren(placeText);
    title.innerText = 'Selected Place:';
    input.value = '';
    refreshToken(request);
}

// Helper function to refresh the session token.
async function refreshToken(request) {
    // Create a new session token and add it to the request.
    token = new google.maps.places.AutocompleteSessionToken();
    request.sessionToken = token;
    return request;
}

declare global {
    interface Window {
      init: () => void;
    }
  }
  window.init = init;

JavaScript

let title;
let results;
let input;
let token;
// Add an initial request body.
let 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",
};

async function init() {
  token = new google.maps.places.AutocompleteSessionToken();
  title = document.getElementById("title");
  results = document.getElementById("results");
  input = document.querySelector("input");
  input.addEventListener("input", makeAcRequest);
  request = refreshToken(request);
}

async function makeAcRequest(input) {
  // Reset elements and exit if an empty string is received.
  if (input.target.value == "") {
    title.innerText = "";
    results.replaceChildren();
    return;
  }

  // Add the latest char sequence to the request.
  request.input = input.target.value;

  // Fetch autocomplete suggestions and show them in a list.
  // @ts-ignore
  const { suggestions } =
    await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(
      request,
    );

  title.innerText = 'Query predictions for "' + request.input + '"';
  // Clear the list first.
  results.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 element.
    const li = document.createElement("li");

    li.appendChild(a);
    results.appendChild(li);
  }
}

// Event handler for clicking on a suggested place.
async function onPlaceSelected(place) {
  await place.fetchFields({
    fields: ["displayName", "formattedAddress"],
  });

  let placeText = document.createTextNode(
    place.displayName + ": " + place.formattedAddress,
  );

  results.replaceChildren(placeText);
  title.innerText = "Selected Place:";
  input.value = "";
  refreshToken(request);
}

// Helper function to refresh the session token.
async function refreshToken(request) {
  // Create a new session token and add it to the request.
  token = new google.maps.places.AutocompleteSessionToken();
  request.sessionToken = token;
  return request;
}

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=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=init&libraries=places&v=weekly"
      defer
    ></script>
  </body>
</html>

ניסיון של דוגמה