주변 지역 검색

플랫폼 선택: Android iOS JavaScript 웹 서비스
유럽 경제 지역 (EEA) 개발자

주변 지역 검색 (신규)은 하나 이상의 장소 유형을 취하여 일치하는 장소의 목록을 반환합니다. 다음 예에서는 장소 유형을 선택하고 지도에서 결과를 확인할 수 있습니다. 마커를 클릭하여 장소에 관한 세부정보를 확인합니다.

주변 지역 검색(신규)은 지정한 장소 유형(예: restaurant, book_store, bowling_alley)을 기반으로 일련의 장소에 대한 정보를 반환합니다. 이 서비스는 지정된 locationRestriction의 반경 내에서 지정된 장소 유형과 일치하는 장소의 목록을 반환합니다.

주변 지역 검색(신규)을 사용하려면 Google Cloud 프로젝트에서 'Places API(신규)'를 사용 설정해야 합니다. 자세한 내용은 시작하기를 참고하세요.

주변 장소 찾기

지정된 장소 유형, 위치, 반경을 기반으로 장소 목록을 반환하려면 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 매개변수를 사용하여 POPULARITY 또는 DISTANCE의 SearchNearbyRankPreference를 지정합니다.
  • 매개변수의 전체 목록을 확인합니다.

예

다음 예에서는 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>

데이터 필드 선택

요청하는 필드에 따라 각 검색 요청의 가격이 설정됩니다. 요청은 요청된 필드 중 가장 높은 SKU로 한 번 청구됩니다. 예를 들어 reviews를 fields에 추가하면 전체 요청에 대해 주변 지역 검색 Enterprise + Atmosphere로 요금이 청구됩니다.

검색 요청을 Nearby Search Pro로 유지하려면 검색 결과에 표시되는 필드(예: id, displayName, formattedAddress, location)만 요청하세요.

사용자가 장소를 선택하면 해당 장소에서 fetchFields()를 호출하여 reviews, nationalPhoneNumber 또는 websiteURI와 같은 필드를 가져옵니다. 각 fetchFields() 호출은 별도의 장소 세부정보 요청이며, 요청하는 필드 중 가장 높은 SKU로 청구됩니다. 이 접근 방식으로 비용을 절감할 수 있는지 여부는 사용자가 여는 장소의 수에 따라 달라집니다.

각 SKU를 트리거하는 필드

  • 다음 필드는 주변 지역 검색 Pro SKU를 트리거합니다.

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

  • 다음 필드는 주변 지역 검색 Enterprise SKU를 트리거합니다.

    internationalPhoneNumber
    nationalPhoneNumber
    priceLevel
    rating
    regularOpeningHours
    userRatingCount
    websiteURI

  • 다음 필드는 주변 지역 검색 Enterprise + Atmosphere SKU를 트리거합니다.

    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

모든 필드의 SKU는 장소 클래스 데이터 필드를 참고하세요.