Nearby Search

Sélectionnez une plate-forme : Android iOS JavaScript Services Web
Développeurs de l'Espace économique européen (EEE)

Avec Nearby Search (nouveau), vous spécifiez un ou plusieurs types de lieux, et le service renvoie une liste de lieux correspondants. Dans l'exemple suivant, vous pouvez choisir un type de lieu et voir les résultats sur une carte. Cliquez sur les repères pour afficher plus de détails sur les lieux.

Nearby Search (nouveau) renvoie des informations concernant un ensemble de lieux correspondant aux types de lieux que vous spécifiez (par exemple, restaurant, book_store ou bowling_alley). Vous obtenez ainsi une liste de tels lieux dans le rayon indiqué par locationRestriction.

Pour utiliser Nearby Search (nouveau), vous devez activer l'API Places (nouveau) dans votre projet Google Cloud. Pour en savoir plus, consultez Commencer.

Trouver des lieux à proximité

Appelez searchNearby() pour renvoyer une liste de lieux qui correspondent aux types de lieux, à l'emplacement et au rayon spécifiés. Indiquez des paramètres de recherche dans une requête, puis appelez searchNearby(). Les résultats sont renvoyés sous la forme d'une liste d'objets Place, à partir desquels vous pouvez obtenir des détails sur les lieux. L'extrait suivant montre un exemple de requête et un appel à 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);
  • Utilisez le paramètre fields (obligatoire) pour spécifier une liste d'un ou plusieurs champs de données séparés par une virgule. Les champs que vous demandez définissent le prix de la requête. Consultez Choisir des champs de données.
  • Utilisez le paramètre locationRestriction (obligatoire) pour spécifier un rayon (jusqu'à 50 000 mètres).
  • Utilisez le paramètre includedPrimaryTypes pour spécifier un ou plusieurs types de lieux à rechercher.
  • Utilisez le paramètre rankPreference pour spécifier la valeur de SearchNearbyRankPreference (soit POPULARITY, soit DISTANCE).
  • Consultez la liste complète des paramètres.

Exemple

L'exemple suivant utilise searchNearby() pour rechercher des lieux du type sélectionné dans les limites de la carte, et insère des repères sur une carte pour montrer les résultats.

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>

Choisir des champs de données

Les champs que vous demandez définissent le prix de chaque requête de recherche. La requête est facturée une seule fois, au SKU le plus élevé parmi les champs demandés. Par exemple, l'ajout de reviews à fields entraîne la facturation de l'intégralité de la requête en tant que Nearby Search Enterprise + Atmosphere.

Pour que les requêtes de recherche restent au niveau Nearby Search Pro, ne demandez que les champs affichés dans vos résultats de recherche, tels que id, displayName, formattedAddress et location.

Lorsqu'un utilisateur sélectionne un lieu, appelez fetchFields() sur ce lieu pour obtenir des champs tels que reviews, nationalPhoneNumber ou websiteURI. Chaque appel fetchFields() est une requête Place Details distincte, facturée au SKU le plus élevé parmi les champs qu'il demande. Cette approche permet d'économiser de l'argent en fonction du nombre de lieux ouverts par vos utilisateurs.

Champs qui déclenchent chaque SKU

  • Les champs suivants déclenchent le SKU Nearby Search Pro :

    accessibilityOptions
    addressComponents
    adrFormatAddress
    businessStatus
    displayName
    formattedAddress
    googleMapsUri
    iconBackgroundColor
    id
    location
    photos
    plusCode
    svgIconMaskURI
    types
    utcOffsetMinutes
    viewport

  • Les champs suivants déclenchent le SKU Nearby Search Enterprise :

    internationalPhoneNumber
    nationalPhoneNumber
    priceLevel
    rating
    regularOpeningHours
    userRatingCount
    websiteURI

  • Les champs suivants déclenchent le SKU Nearby Search 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

Pour connaître le SKU de chaque champ, consultez Champs de données de la classe Place.