התכונה "חיפוש בסביבה (חדש)" מקבלת סוג אחד או יותר של מקומות ומחזירה רשימה של מקומות תואמים. בדוגמה הבאה, אתם יכולים לבחור סוג מקום ולראות את התוצאות במפה. כדי לראות פרטים נוספים על המקומות, לוחצים על הסמנים.
התכונה "חיפוש בסביבה (חדש)" מחזירה מידע על קבוצה של מקומות על סמך סוגי המקומות שאתם מציינים – לדוגמה, restaurant או book_store או bowling_alley. השירות מחזיר רשימה של מקומות שתואמים לסוגי המקומות שצוינו ברדיוס של ה-locationRestriction שצוין.
כדי להשתמש בגרסה החדשה של "חיפוש בסביבה", צריך להפעיל את "Places API (חדש)" בפרויקט בענן ב-Google Cloud. פרטים נוספים מופיעים במאמר על תחילת העבודה.
חיפוש מקומות בסביבה
מפעילים את searchNearby() כדי להחזיר רשימה של מקומות על סמך סוגי המקומות, המיקום והרדיוס שצוינו. מציינים את פרמטרי החיפוש באמצעות בקשה, ואז קוראים ל-searchNearby(). התוצאות מוחזרות כרשימה של אובייקטים מסוג Place, שמהם אפשר לקבל פרטי מקומות.
בקטע הקוד הבא מוצגת דוגמה לבקשה ולקריאה ל-searchNearby():
TypeScript
// Get bounds and radius to constrain search. center = mapElement.center!; const ne = innerMap.getBounds()!.getNorthEast(); const sw = innerMap.getBounds()!.getSouthWest(); const diameter = spherical.computeDistanceBetween(ne, sw); const radius = Math.min(diameter / 2, 50000); // Radius cannot be more than 50000. const request = { // required parameters fields: [ 'displayName', 'location', 'formattedAddress', 'googleMapsURI', ], locationRestriction: { center, radius, }, // optional parameters includedPrimaryTypes: [typeSelect.value], maxResultCount: 5, rankPreference: SearchNearbyRankPreference.POPULARITY, }; const { places } = await Place.searchNearby(request);
JavaScript
// Get bounds and radius to constrain search. center = mapElement.center; const ne = innerMap.getBounds().getNorthEast(); const sw = innerMap.getBounds().getSouthWest(); const diameter = spherical.computeDistanceBetween(ne, sw); const radius = Math.min(diameter / 2, 50000); // Radius cannot be more than 50000. const request = { // required parameters fields: [ 'displayName', 'location', 'formattedAddress', 'googleMapsURI', ], locationRestriction: { center, radius, }, // optional parameters includedPrimaryTypes: [typeSelect.value], maxResultCount: 5, rankPreference: SearchNearbyRankPreference.POPULARITY, }; const { places } = await Place.searchNearby(request);
- משתמשים בפרמטר
fields(חובה) כדי לציין רשימה מופרדת בפסיקים של שדה נתונים אחד או יותר. השדות שאתם מבקשים קובעים את מחיר הבקשה. איך בוחרים שדות נתונים - משתמשים בפרמטר
locationRestriction(חובה) כדי לציין רדיוס של עד 50,000 מטרים. - משתמשים בפרמטר
includedPrimaryTypesכדי לציין סוגי מקומות אחד או יותר לחיפוש. - משתמשים בפרמטר
rankPreferenceכדי לצייןSearchNearbyRankPreferenceשלPOPULARITYאוDISTANCE. - לרשימת הפרמטרים המלאה
דוגמה
בדוגמה הבאה נעשה שימוש ב-searchNearby() כדי לשלוח שאילתה לגבי מקומות מהסוג שנבחר בגבולות המפה, ולאכלס מפה בסמנים כדי להציג את התוצאות.
TypeScript
const mapElement = document.querySelector('gmp-map')!; let innerMap: google.maps.Map; let center: google.maps.LatLngLiteral | google.maps.LatLng; let typeSelect: HTMLSelectElement; let infoWindow: google.maps.InfoWindow; async function init() { const [{ InfoWindow }, { event }] = await Promise.all([ google.maps.importLibrary('maps'), google.maps.importLibrary('core'), ]); innerMap = mapElement.innerMap; innerMap.setOptions({ mapTypeControl: false, }); typeSelect = document.querySelector('.type-select')!; typeSelect.addEventListener('change', () => { void nearbySearch(); }); infoWindow = new InfoWindow(); // Kick off an initial search once map has loaded. event.addListenerOnce(innerMap, 'idle', () => { void nearbySearch(); }); } async function nearbySearch() { const [ { Place, SearchNearbyRankPreference }, { AdvancedMarkerElement }, { spherical }, ] = await Promise.all([ google.maps.importLibrary('places'), google.maps.importLibrary('marker'), google.maps.importLibrary('geometry'), ]); // Get bounds and radius to constrain search. center = mapElement.center!; const ne = innerMap.getBounds()!.getNorthEast(); const sw = innerMap.getBounds()!.getSouthWest(); const diameter = spherical.computeDistanceBetween(ne, sw); const radius = Math.min(diameter / 2, 50000); // Radius cannot be more than 50000. const request = { // required parameters fields: [ 'displayName', 'location', 'formattedAddress', 'googleMapsURI', ], locationRestriction: { center, radius, }, // optional parameters includedPrimaryTypes: [typeSelect.value], maxResultCount: 5, rankPreference: SearchNearbyRankPreference.POPULARITY, }; const { places } = await Place.searchNearby(request); if (places.length) { const { LatLngBounds } = await google.maps.importLibrary('core'); const bounds = new LatLngBounds(); // First remove all existing markers. for (const marker of mapElement.querySelectorAll('gmp-advanced-marker')) marker.remove(); // Loop through and get all the results. places.forEach((place) => { if (!place.location) return; bounds.extend(place.location); const marker = new AdvancedMarkerElement({ map: innerMap, position: place.location, title: place.displayName, gmpClickable: true, }); // Build the content of the InfoWindow safely using DOM elements. const content = document.createElement('div'); const address = document.createElement('div'); address.textContent = place.formattedAddress || ''; const placeId = document.createElement('div'); placeId.textContent = place.id; content.append(address, placeId); if (place.googleMapsURI) { const link = document.createElement('a'); link.href = place.googleMapsURI; link.target = '_blank'; link.textContent = 'View Details on Google Maps'; content.appendChild(link); } marker.addEventListener('gmp-click', () => { innerMap.panTo(place.location!); updateInfoWindow(place.displayName!, content, marker); }); }); innerMap.fitBounds(bounds, 100); } else { console.log('No results'); } } function updateInfoWindow( title: string | Element | null, content: string | Element | null, anchor: google.maps.marker.AdvancedMarkerElement ) { infoWindow.setContent(content); infoWindow.setHeaderContent(title); infoWindow.open({ anchor, }); } void init();
JavaScript
const mapElement = document.querySelector('gmp-map'); let innerMap; let center; let typeSelect; let infoWindow; async function init() { const [{ InfoWindow }, { event }] = await Promise.all([ google.maps.importLibrary('maps'), google.maps.importLibrary('core'), ]); innerMap = mapElement.innerMap; innerMap.setOptions({ mapTypeControl: false, }); typeSelect = document.querySelector('.type-select'); typeSelect.addEventListener('change', () => { void nearbySearch(); }); infoWindow = new InfoWindow(); // Kick off an initial search once map has loaded. event.addListenerOnce(innerMap, 'idle', () => { void nearbySearch(); }); } async function nearbySearch() { const [ { Place, SearchNearbyRankPreference }, { AdvancedMarkerElement }, { spherical }, ] = await Promise.all([ google.maps.importLibrary('places'), google.maps.importLibrary('marker'), google.maps.importLibrary('geometry'), ]); // Get bounds and radius to constrain search. center = mapElement.center; const ne = innerMap.getBounds().getNorthEast(); const sw = innerMap.getBounds().getSouthWest(); const diameter = spherical.computeDistanceBetween(ne, sw); const radius = Math.min(diameter / 2, 50000); // Radius cannot be more than 50000. const request = { // required parameters fields: [ 'displayName', 'location', 'formattedAddress', 'googleMapsURI', ], locationRestriction: { center, radius, }, // optional parameters includedPrimaryTypes: [typeSelect.value], maxResultCount: 5, rankPreference: SearchNearbyRankPreference.POPULARITY, }; const { places } = await Place.searchNearby(request); if (places.length) { const { LatLngBounds } = await google.maps.importLibrary('core'); const bounds = new LatLngBounds(); // First remove all existing markers. for (const marker of mapElement.querySelectorAll('gmp-advanced-marker')) marker.remove(); // Loop through and get all the results. places.forEach((place) => { if (!place.location) return; bounds.extend(place.location); const marker = new AdvancedMarkerElement({ map: innerMap, position: place.location, title: place.displayName, gmpClickable: true, }); // Build the content of the InfoWindow safely using DOM elements. const content = document.createElement('div'); const address = document.createElement('div'); address.textContent = place.formattedAddress || ''; const placeId = document.createElement('div'); placeId.textContent = place.id; content.append(address, placeId); if (place.googleMapsURI) { const link = document.createElement('a'); link.href = place.googleMapsURI; link.target = '_blank'; link.textContent = 'View Details on Google Maps'; content.appendChild(link); } marker.addEventListener('gmp-click', () => { innerMap.panTo(place.location); updateInfoWindow(place.displayName, content, marker); }); }); innerMap.fitBounds(bounds, 100); } else { console.log('No results'); } } function updateInfoWindow(title, content, anchor) { infoWindow.setContent(content); infoWindow.setHeaderContent(title); infoWindow.open({ anchor, }); } void init();
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ gmp-map { height: 100%; } #map-container { display: flex; flex-direction: row; height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } .type-select { width: 400px; height: 32px; border: 1px solid #000; border-radius: 10px; flex-grow: 1; padding: 0 10px; margin-left: 10px; margin-top: 10px; }
HTML
<html>
<head>
<title>Nearby Search</title>
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script>
<script>
// prettier-ignore
(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: "GOOGLE_MAPS_API_KEY"
});
</script>
</head>
<body>
<gmp-map center="45.438646,12.327573" zoom="14" map-id="DEMO_MAP_ID"
><!-- Map id is required for Advanced Markers. -->
<gmp-advanced-marker></gmp-advanced-marker>
<div id="controls" slot="control-inline-start-block-start">
<select name="types" class="type-select">
<option value="cafe" selected>Cafe</option>
<option value="restaurant">Restaurant</option>
<option value="museum">Museum</option>
<option value="monument">Monument</option>
<option value="park">Park</option>
</select>
</div>
</gmp-map>
</body>
</html>בחירת שדות נתונים
השדות שאתם מבקשים קובעים את המחיר של כל בקשת חיפוש. הבקשה מחויבת פעם אחת, לפי המק"ט הגבוה ביותר מבין השדות המבוקשים. לדוגמה, הוספה של
reviews
אל
fields
תחייב את כל הבקשה בתור חיפוש בסביבה Enterprise + Atmosphere.
כדי שהבקשות לחיפוש יישארו ב-Nearby Search Pro, צריך לבקש רק את השדות שמוצגים בתוצאות החיפוש, כמו id, displayName, formattedAddress ו-location.
כשמשתמש בוחר מקום, מפעילים את fetchFields() במקום הזה כדי לקבל שדות כמו reviews, nationalPhoneNumber או websiteURI.
כל קריאה ל-fetchFields() היא בקשה נפרדת ל-Place Details, והחיוב עליה מתבצע לפי המק"ט הגבוה ביותר מבין השדות שהיא מבקשת. הגישה הזו חוסכת כסף בהתאם למספר המקומות שהמשתמשים פותחים.
השדות שמפעילים כל מק"ט
השדות הבאים מפעילים את המק"ט של "חיפוש בסביבה (Pro)":
accessibilityOptions
addressComponents
adrFormatAddress
businessStatus
displayName
formattedAddress
googleMapsUri
iconBackgroundColor
id
location
photos
plusCode
svgIconMaskURI
types
utcOffsetMinutes
viewport
השדות הבאים מפעילים את המק"ט של "חיפוש בסביבה" ברמת Enterprise:
internationalPhoneNumber
nationalPhoneNumber
priceLevel
rating
regularOpeningHours
userRatingCount
websiteURIהשדות הבאים מפעילים את המק"ט "חיפוש בסביבה" (Enterprise + Atmosphere):
allowsDogs
editorialSummary
evChargeOptions
fuelOptions
hasCurbsidePickup
hasDelivery
hasDineIn
hasLiveMusic
hasMenuForChildren
hasOutdoorSeating
hasRestroom
hasTakeout
hasWifi
isGoodForChildren
isGoodForGroups
isGoodForWatchingSports
isReservable
parkingOptions
paymentOptions
reviews
servesBeer
servesBreakfast
servesBrunch
servesCocktails
servesCoffee
servesDessert
servesDinner
servesLunch
servesVegetarianFood
servesWine
לגבי המק"ט של כל שדה, ראו שדות נתונים של מקומות.