הצגת מקומות באמצעות רכיב חיפוש מקומות ו-Maps JavaScript API

מטרה

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

מהו רכיב חיפוש מקומות?

האלמנט Place Search הוא חלק מ-Places UI Kit ב-Maps JavaScript API. זהו רכיב HTML שמציג את תוצאות החיפוש של מקום ישירות בפורמט של רשימה בתוך האפליקציה. האלמנט הזה מפשט את תהליך הצגת המקומות שנמצאו באמצעות חיפוש בקרבת מקום או חיפוש טקסטואלי, ומספק חוויית משתמש חלקה לגילוי מקומות. כשמשתמש בוחר מקום מהרשימה, אפשר להציג את הפרטים שלו במפה, לרוב באמצעות חלון מידע ורכיב פרטי המקום.

הדמיה של גילוי מקומות

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

תמונה

תרחישים לדוגמה לחיפוש מקומות

שילוב של רכיב חיפוש המקומות יכול לשפר מגוון אפליקציות בתחומים שונים:

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

תהליך העבודה של הפתרון: הטמעה של תכונת איתור מקומות

בקטע הזה מוסבר איך לשלב את רכיב חיפוש המקומות כדי לגלות מקומות במפה, כולל קטעי קוד לאינטראקציה עם ערכת כלי ה-UI של Places. במאמר הזה נסביר איך להפעיל את המפה ואיך להטמיע את הפונקציות של חיפוש בקרבת מקום וחיפוש טקסטואלי. לבסוף, נשתמש ברכיב Place Details כדי להציג פרטים נוספים על מקום מסוים, בלחיצה על הסיכה שלו במפה.

דרישות מוקדמות

מומלץ לעיין במסמכים הבאים:

מפעילים את Maps JavaScript API ואת Places UI Kit בפרויקט.

לפני שמתחילים, צריך לוודא שטענתם את Maps JavaScript API וייבאתם את הספריות הנדרשות. בנוסף, אנחנו מניחים שיש לכם ידע מעשי בפיתוח אתרים, כולל HTML, ‏ CSS ו-JavaScript.

הוספת מפה לדף

השלב הראשון הוא להוסיף מפה לדף. המפה הזו תשמש להצגת תוצאות של רכיב חיפוש מקומות כסיכות שאפשר לבחור.

יש שתי דרכים להוסיף מפה לדף:

  1. שימוש בgmp-map רכיב אינטרנט HTML.
  2. שימוש ב-JavaScript.

קטעי הקוד בדף הזה נוצרו באמצעות מפת JavaScript.

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

אם אתם רוצים להציג מקומות בקרבת מיקום קבוע, כמו מלון, הוסיפו סמן למפה כדי לייצג את המקום הזה. לדוגמה:

תמונה

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

הגדרת אלמנט חיפוש מקומות

עכשיו אפשר להגדיר את ה-HTML ואת ה-CSS כדי להציג את רכיב חיפוש המקומות. בדוגמה הזו, נציף את הרכיב מעל הצד השמאלי של המפה, אבל מומלץ לנסות פריסות שונות כדי להתאים לאפליקציה שלכם.

האלמנט Place Search משתמש בגישה הצהרתית. במקום להגדיר אותו כולו ב-JavaScript, מגדירים את סוג החיפוש ישירות ב-HTML על ידי הטמעה של רכיב בקשה, כמו <gmp-place-nearby-search-request>, בתוך הרכיב הראשי <gmp-place-search>.

בקוד ה-HTML, מאתחלים רכיב <gmp-place-search>. כדי להפעיל אירועי קליקים בתוצאות, צריך להשתמש במאפיין selectable. בתוך התג, מוסיפים את התג <gmp-place-nearby-search-request> כדי לציין שהרכיב הזה ישמש לחיפוש בסביבה.

<gmp-place-search selectable>
  <gmp-place-nearby-search-request>
  </gmp-place-nearby-search-request>
</gmp-place-search>

כדי לבצע חיפוש ראשוני ולהציג תוצאות, נשתמש ב-JavaScript כדי לקבל הפניה לרכיב הבקשה המקונן ולהגדיר את המאפיינים שלו. מאתחלים Circle לשימוש כ-locationRestriction, באמצעות מיקום הסמן מהשלב הקודם כנקודת המרכז. לאחר מכן, מגדירים את המאפיינים locationRestriction ו-includedPrimaryTypes ברכיב הבקשה כדי להפעיל את החיפוש.

קטע הקוד שמשמש לכך הוא:

// Get references to the Place Search and its nested request element
const placeSearchElement = document.querySelector("gmp-place-search");
const placeSearchRequestElement = document.querySelector("gmp-place-nearby-search-request");

// Define the location restriction for the search
const circleRestriction = new Circle({
    center: marker.position,
    radius: 500
});

// Set the properties on the request element to perform an initial search for restaurants.
placeSearchRequestElement.locationRestriction = circleRestriction;
placeSearchRequestElement.includedPrimaryTypes = ['restaurant'];

דוגמה לאופן שבו האפליקציה יכולה להיראות בשלב הזה:

תמונה

רכיב חיפוש המקומות מאפשר שתי אפשרויות חיפוש:

אלה אלמנטים מקוננים בתוך <gmp-place-search>. לאחר מכן מפעילים חיפושים על ידי הגדרת מאפיינים ברכיב הבקשה המקונן הזה באמצעות JavaScript.

בקטע הזה מוסבר איך מטמיעים את שתי השיטות.

תמונה

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

מומלץ לבחור קבוצת משנה של סוגים שרלוונטיים לתרחיש השימוש שלכם. לדוגמה, אם אתם מפתחים אפליקציה שמציגה לתיירים מה יש בסביבת מלון, יכול להיות שתבחרו באפשרויות הבאות: bakery,‏ coffee_shop,‏ museum,‏ restaurant ו-tourist_attraction.

קוד ה-HTML צריך להכיל את האלמנט <gmp-place-search> עם האלמנט <gmp-place-nearby-search-request> שמוטמע בתוכו.

<gmp-place-search selectable>
  <gmp-place-nearby-search-request>
  </gmp-place-nearby-search-request>
</gmp-place-search>

לאחר מכן, יוצרים מאזין JavaScript לאירוע change בבורר סוג המקום. המאזין הזה יקרא לפונקציה שמעדכנת את המאפיינים של רכיב <gmp-place-nearby-search-request>, מה שגורם אוטומטית להפעלת חיפוש חדש ולעדכון הרשימה.

// Get a reference to the nested request element
const placeSearchRequestElement = document.querySelector('gmp-place-nearby-search-request');

// Function to update the place search based on the selected type
function updatePlaceList() {
    const selectedType = placeTypeSelect.value;
    if (!selectedType) {
        // If no type is selected, don't perform a search.
        // You could optionally hide the list or clear previous results here.
        placeSearchElement.style.display = 'none';
        return;
    }
    placeSearchElement.style.display = 'block';

    // Set properties on the request element to trigger a new search
    placeSearchRequestElement.locationRestriction = searchCircle;
    placeSearchRequestElement.maxResultCount = 8;
    placeSearchRequestElement.includedPrimaryTypes = [selectedType];
}

אותו searchCircle משלב ההגדרה משמש עבור locationRestriction. הנכס includedPrimaryTypes מוגדר לערך שנבחר על ידי המשתמש. אפשר גם להגדיר maxResultCount כדי להגביל את מספר התוצאות.

תמונה

כדי להפעיל חיפוש טקסט, צריך לשנות את הגדרת ה-HTML. במקום בקשת חיפוש בסביבה, צריך להוסיף רכיב <gmp-place-text-search-request>.

<gmp-place-search selectable>
  <gmp-place-text-search-request>
  </gmp-place-text-search-request>
</gmp-place-search>

מוסיפים ל-UI תיבת קלט טקסט ולחצן חיפוש. יוצרים מאזין JavaScript לאירוע click של הלחצן. ה-event handler יקבל את הקלט של המשתמש ויעדכן את המאפיינים של רכיב <gmp-place-text-search-request> כדי לבצע את החיפוש.

// Get a reference to the text search request element
const placeTextSearchRequestElement = document.querySelector('gmp-place-text-search-request');
const textSearchInput = document.getElementById('textSearchInput');
const textSearchButton = document.getElementById('textSearchButton');

textSearchButton.addEventListener('click', performTextSearch);

function performTextSearch() {
    const query = textSearchInput.value.trim();
    if (!query) {
        console.log("Search query is empty.");
        return;
    }
    // Set properties on the request element to trigger a new search
    placeTextSearchRequestElement.textQuery = query;
    placeTextSearchRequestElement.locationBias = map.getBounds();
    placeTextSearchRequestElement.maxResultCount = 8;
}

כאן אנחנו מגדירים את המאפיין textQuery עם הקלט של המשתמש. אנחנו גם מספקים locationBias באמצעות גבולות המפה הנוכחיים, שמורה ל-API להעדיף תוצאות באזור הזה בלי להגביל אותן באופן מוחלט. הפרמטר האופציונלי maxResultCount מגביל את מספר התוצאות שמוחזרות.

הצגת סיכות ופרטים של מקומות

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

  • הצגת סיכות במפה לכל מקום שאוכלס ברכיב Place Search.
  • לאפשר למשתמש ללחוץ על סיכה או על המקום בתוך רכיב חיפוש המקומות כדי להציג פרטים נוספים על המקום הספציפי הזה.

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

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

let markers = {};

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

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

כדי להאזין לזמינות של תוצאות החיפוש, מוסיפים gmp-load event listener לרכיב <gmp-place-search>. האירוע הזה מופעל אחרי שחיפוש מסתיים והתוצאות מוצגות.

נוסיף את ה-listener בתוך פונקציית החיפוש (למשל, updatePlaceList), ומשתמשים באפשרות { once: true } כדי לוודא שהיא מופעלת רק עבור התוצאות של החיפוש הנוכחי.

// In your search function, after setting the request properties:
placeSearchElement.addEventListener('gmp-load', addMarkers, { once: true });

הפונקציה addMarkers נראית כך:

async function addMarkers() {
    const { LatLngBounds } = await google.maps.importLibrary("core");
    const bounds = new LatLngBounds();

    if (placeSearchElement.places.length > 0) {
        // Remove existing markers
        for (const m in markers) {
            markers[m].map = null;
        }
        markers = {};

        // Loop through each place from the search results
        // and add a marker for each one.
        for (const place of placeSearchElement.places) {
            const marker = new google.maps.marker.AdvancedMarkerElement({
                map: map,
                position: place.location,
            });

            markers[place.id] = marker;
            bounds.extend(place.location);
            marker.collisionBehavior = google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL;

            // Add a click listener for each marker.
            marker.addListener('gmp-click', (event) => {
                // The main logic for showing details will go here.
            });
        }
        // Position the map to display all markers.
        map.setCenter(bounds.getCenter());
        map.fitBounds(bounds);
    }
}

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

תמונה

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

מוסיפים לקוד את ה-HTML של רכיב קומפקטי של פרטי מקום, לדוגמה:

<gmp-place-details-compact orientation="vertical" style="display: none;">
    <gmp-place-all-content></gmp-place-all-content>
    <gmp-place-attribution light-scheme-color="gray" dark-scheme-color="white"></gmp-place-attribution>
</gmp-place-details-compact>

ההגדרה של style היא display: none, והיא לא תהיה גלויה עד שיהיה בה צורך. ‫gmp-place-all-content מועבר כדי לעבד את כל תוכן הרכיב. כדי לבחור איזה תוכן להציג, אפשר לעיין במסמכים בנושא Place Details Compact Element.

יוצרים משתנה גלובלי ב-JavaScript כדי להחזיק הפניה לרכיב Place Details Compact, ומאכלסים אותו בקוד האתחול, לדוגמה:

let placeDetailsElement;
...
placeDetailsElement = document.querySelector('gmp-place-details-compact');

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

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

לבסוף, המפה ממוקמת באזור התצוגה של המקום שנבחר, כך שהיא גלויה.

async function addMarkers() {
          ...
            marker.addListener('gmp-click', (event) => {
                //Set up Place Details Compact Widget
                placeDetailsElement.style.display = "block";
                // Remove any existing place request element
                const existingPlaceRequest = placeDetailsElement.querySelector('gmp-place-details-place-request');
                if (existingPlaceRequest) {
                    existingPlaceRequest.remove();
                }
                // Create and configure the new place request element
                const placeRequestElement = new google.maps.places.PlaceDetailsPlaceRequestElement({ place: place.id });
                // Prepend the new place request element to the main widget
                placeDetailsElement.prepend(placeRequestElement);
                if (infoWindow.isOpen) {
                    infoWindow.close();
                }
                infoWindow.setOptions({
                    content: placeDetailsElement
                });
                infoWindow.open({
                    anchor: marker,
                    map: map
                });
                // Position the map to show the selected place
                placeDetailsElement.addEventListener('gmp-load', () => {
                    map.fitBounds(place.viewport, { top: 500, left: 400 });
                });
            });
          ...
        });
    }
}

כדי לאפשר למשתמש ללחוץ על מקום ברשימת המקומות כדי להציג את רכיב הפרטים של המקום, מוסיפים את הקוד הבא ל-JavaScript אחרי הקריאה ל-configureFromSearchNearbyRequest.

placeSearchElement.addEventListener("gmp-select", ({ place }) => {
    if (markers[place.id]) {
        markers[place.id].click();
    }
});

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

האפליקציה תיראה כך:

תמונה

סיכום

השילוב של Place Search Element (רכיב חיפוש מקומות) עם Place Details Compact Element (רכיב קומפקטי של פרטי מקום) מאפשר להוסיף בקלות תכונות עשירות של גילוי מקומות לאפליקציות של Google Maps Platform.

כדאי לנסות את Places UI Kit עוד היום כדי לאפשר למשתמשים שלכם למצוא מקומות ולחקור אותם באמצעות חיפושים בסביבה וחיפושים טקסטואליים, ולהציג פרטים עשירים על המקומות, וכך לשפר את האינטראקציה שלהם עם תרחישי השימוש שלכם לגילוי מקומות.

תורמים

Henrik Valve | DevX Engineer