Text Search(新版)はテキストのクエリを取得して、一致する場所を返します。
テキスト検索(新版)は、「渋谷 ピザショップ」「表参道 靴店」「123 番地」といった文字列に対して、場所のセットについての情報を返します。テキスト文字列と、その時点で設定済みの地域バイアスをもとに、場所のリストを返すサービスです。テキスト検索(新版)では、場所のタイプ別の検索、営業時間や評価といった条件を使った絞り込み、特定の地域に限定または優先(バイアス)した検索が可能です。
Text Search(新版)を利用するには、ご使用の Google Cloud プロジェクトで「Places API(新版)」を有効化する必要があります。詳しくは利用開始をご覧ください。
テキストのクエリで場所を見つける
searchByText を呼び出すと、テキストによるクエリまたは電話番号に対して、場所のリストが返されます。リクエストを使用して検索パラメータを指定して、searchByText() を呼び出します。結果は Place オブジェクトのリストとして返されます。そのリストからPlace Detailsを取得できます。次のスニペットは、リクエストと searchByText 呼び出しの例を示しています。
TypeScript
const request = { textQuery: query, fields: ['displayName', 'location', 'businessStatus'], includedType: '', // Restrict query to a specific type (leave blank for any). useStrictTypeFiltering: true, locationRestriction: map.getBounds(), isOpenNow: true, language: 'en-US', maxResultCount: 8, minRating: 1, // Specify a minimum rating. region: 'us', }; const { places } = await Place.searchByText(request);
JavaScript
const request = { textQuery: query, fields: ['displayName', 'location', 'businessStatus'], includedType: '', // Restrict query to a specific type (leave blank for any). useStrictTypeFiltering: true, locationRestriction: map.getBounds(), isOpenNow: true, language: 'en-US', maxResultCount: 8, minRating: 1, // Specify a minimum rating. region: 'us', }; const { places } = await Place.searchByText(request);
textQueryパラメータを使用して、テキストのクエリや電話番号を指定して検索できます。- データ フィールドの指定(複数可)には
fieldsパラメータを使用します。キャメルケース表記のカンマ区切りのリストとして指定しましょう。リクエストするフィールドによって、リクエストの料金が決まります。データ フィールドを選択するをご覧ください。 includedTypeパラメータを使用して、指定された型の結果のみを返します。locationBiasまたはlocationRestrictionを使用して、テキスト検索の結果を特定の地域優先にしたり、制限したりできます。
クエリに電話番号が含まれる場合は、地域のパラメータを設定します。たとえば日本にある場所を電話番号で検索する場合、リクエストを行うドメインが jp であれば、region パラメータも「jp」に設定する必要があります。リクエストの region が省略されていると、API はデフォルトで地域を米国(「us」)に設定します。
Place オブジェクトのリストとして返されます。そのリストから場所の詳細を取得できます。例
次の例では、指定されたクエリテキストを使用して searchByText を呼び出し、結果を示すクリック可能なマーカーを地図に表示します。
TypeScript
let map: google.maps.Map; let markers: Record<string, google.maps.marker.AdvancedMarkerElement> = {}; let infoWindow: google.maps.InfoWindow; async function init() { const [{ Map, InfoWindow }, { ControlPosition }] = await Promise.all([ google.maps.importLibrary('maps'), google.maps.importLibrary('core'), ]); const center = { lat: 37.4161493, lng: -122.0812166 }; map = new Map(document.getElementById('map')!, { center, zoom: 11, mapTypeControl: false, mapId: 'DEMO_MAP_ID', }); const textInput = document.getElementById('text-input') as HTMLInputElement; const textInputButton = document.getElementById( 'text-input-button' ) as HTMLButtonElement; const card = document.getElementById('text-input-card')!; map.controls[ControlPosition.TOP_LEFT].push(card); textInputButton.addEventListener('click', () => { void findPlaces(textInput.value); }); textInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { void findPlaces(textInput.value); } }); infoWindow = new InfoWindow(); } async function findPlaces(query: string) { const [{ Place }, { AdvancedMarkerElement }] = await Promise.all([ google.maps.importLibrary('places'), google.maps.importLibrary('marker'), ]); const request = { textQuery: query, fields: ['displayName', 'location', 'businessStatus'], includedType: '', // Restrict query to a specific type (leave blank for any). useStrictTypeFiltering: true, locationRestriction: map.getBounds(), isOpenNow: true, language: 'en-US', maxResultCount: 8, minRating: 1, // Specify a minimum rating. region: 'us', }; const { places } = await Place.searchByText(request); if (places.length) { const { LatLngBounds } = await google.maps.importLibrary('core'); const bounds = new LatLngBounds(); // First remove all existing markers. for (const id in markers) { markers[id].map = null; } markers = {}; // Loop through and get all the results. places.forEach((place: google.maps.places.Place) => { const marker = new AdvancedMarkerElement({ map, position: place.location, title: place.displayName, gmpClickable: true, }); markers[place.id] = marker; marker.addEventListener('gmp-click', () => { map.panTo(place.location!); updateInfoWindow(place.displayName!, place.id, marker); }); if (place.location != null) { bounds.extend(place.location); } }); map.fitBounds(bounds); } else { console.log('No results'); } } // Helper function to create an info window. function updateInfoWindow( title: string | Element | null, content: string | Element | null, anchor: google.maps.marker.AdvancedMarkerElement ) { infoWindow.setContent(content); infoWindow.setHeaderContent(title); infoWindow.open({ map, anchor, shouldFocus: false, }); } void init();
JavaScript
let map; let markers = {}; let infoWindow; async function init() { const [{ Map, InfoWindow }, { ControlPosition }] = await Promise.all([ google.maps.importLibrary('maps'), google.maps.importLibrary('core'), ]); const center = { lat: 37.4161493, lng: -122.0812166 }; map = new Map(document.getElementById('map'), { center, zoom: 11, mapTypeControl: false, mapId: 'DEMO_MAP_ID', }); const textInput = document.getElementById('text-input'); const textInputButton = document.getElementById('text-input-button'); const card = document.getElementById('text-input-card'); map.controls[ControlPosition.TOP_LEFT].push(card); textInputButton.addEventListener('click', () => { void findPlaces(textInput.value); }); textInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { void findPlaces(textInput.value); } }); infoWindow = new InfoWindow(); } async function findPlaces(query) { const [{ Place }, { AdvancedMarkerElement }] = await Promise.all([ google.maps.importLibrary('places'), google.maps.importLibrary('marker'), ]); const request = { textQuery: query, fields: ['displayName', 'location', 'businessStatus'], includedType: '', // Restrict query to a specific type (leave blank for any). useStrictTypeFiltering: true, locationRestriction: map.getBounds(), isOpenNow: true, language: 'en-US', maxResultCount: 8, minRating: 1, // Specify a minimum rating. region: 'us', }; const { places } = await Place.searchByText(request); if (places.length) { const { LatLngBounds } = await google.maps.importLibrary('core'); const bounds = new LatLngBounds(); // First remove all existing markers. for (const id in markers) { markers[id].map = null; } markers = {}; // Loop through and get all the results. places.forEach((place) => { const marker = new AdvancedMarkerElement({ map, position: place.location, title: place.displayName, gmpClickable: true, }); markers[place.id] = marker; marker.addEventListener('gmp-click', () => { map.panTo(place.location); updateInfoWindow(place.displayName, place.id, marker); }); if (place.location != null) { bounds.extend(place.location); } }); map.fitBounds(bounds); } else { console.log('No results'); } } // Helper function to create an info window. function updateInfoWindow(title, content, anchor) { infoWindow.setContent(content); infoWindow.setHeaderContent(title); infoWindow.open({ map, anchor, shouldFocus: false, }); } void 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; } #text-input-card { width: 25%; background-color: #fff; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; margin: 10px; padding: 5px; font-family: Roboto, sans-serif; font-size: large; font-weight: bold; } #text-input { width: 100%; padding: 10px; margin: 0; box-sizing: border-box; } #text-input-button { display: inline-block; margin-top: 0.5rem; width: auto; padding: 0.6rem 0.75rem; font-size: 0.875rem; font-weight: 500; color: #fff; background-color: #2563eb; border: none; border-radius: 0.375rem; cursor: pointer; transition: background-color 0.15s ease-in-out; text-align: center; }
HTML
<html>
<head>
<title>Text 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>
<div id="text-input-card">
<input
type="text"
id="text-input"
placeholder="Search for a place" />
<input type="button" id="text-input-button" value="Search" />
</div>
<div id="map"></div>
</body>
</html>サンプルを試す
データ フィールドを選択する
リクエストするフィールドによって、各検索リクエストの料金が決まります。リクエストは、リクエストされたフィールドの中で最も高い SKU で 1 回請求されます。たとえば、reviews を fields に追加すると、リクエスト全体がテキスト検索 Enterprise + Atmosphere として課金されます。
検索リクエストを Text Search Pro に維持するには、検索結果に表示されるフィールド(id、displayName、formattedAddress、location など)のみをリクエストします。
ユーザーが場所を選択したら、その場所で fetchFields() を呼び出して、reviews、nationalPhoneNumber、websiteURI などのフィールドを取得します。各 fetchFields() 呼び出しは個別の Place Details リクエストであり、リクエストするフィールドの中で最も高い SKU で課金されます。この方法で費用を節約できるかどうかは、ユーザーが開く場所の数によって異なります。
各 SKU をトリガーするフィールド
次のフィールドは、テキスト検索 Essentials(ID のみ)SKU をトリガーします。
id次のフィールドは、テキスト検索 Pro SKU をトリガーします。
accessibilityOptions
addressComponents
adrFormatAddress
businessStatus
displayName
formattedAddress
googleMapsUri
iconBackgroundColor
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 については、Place Class のデータ フィールドをご覧ください。