從 Geocoding v3 遷移至 v4

歐洲經濟區 (EEA) 開發人員

Geocoding API 第 4 版推出多項新方法,取代第 3 版的功能。本指南說明如何遷移應用程式,改用新的第 4 版方法。

您可以使用現有的 API 金鑰搭配新的 v4 方法。不過,如果您已要求提高 API 第 3 版的配額,則必須要求提高新版第 4 版 API 的配額。

從第 3 版正向地理編碼遷移

如果您使用 v3 地理編碼對地址進行地理編碼,請改用 v4 Geocode an address 方法,該方法接受 GET 要求。

v4 API 變更了名稱、結構和對多個參數的支援。強烈建議您使用欄位遮罩,指定要在回應中傳回的欄位。

要求變更參數

v3 參數 v4 參數 附註
address、components address 非結構化地址 (v3 address) 現在會透過網址路徑傳遞。結構化地址元件 (第 3 版 components) 可做為 address.* 查詢參數傳遞。請參閱「重現第 3 版元件篩選器」。
bounds locationBias.rectangle 已重新命名;結構變更為物件。
language languageCode 已重新命名。如果省略,v3 會使用 Accept-Language 標頭或預設為 en,而 v4 則預設為地點的當地語言和文字。
region regionCode 已重新命名。如果省略,v3 預設為 us,v4 則沒有預設值。
extra_computations 已移除 由 SearchDestinations 方法取代。

回應欄位變更

v3 欄位 v4 欄位 附註
status、error_message 已移除 v4 使用 HTTP 狀態碼和錯誤主體。
results.address_components.long_name/results.address_components.short_name results.addressComponents.longText/results.addressComponents.shortText 已重新命名。
results.geometry.location_type results.granularity 已重新命名。
results.geometry.location results.location 欄位名稱:lat/lng -> latitude/longitude。
results.geometry.viewport results.viewport 欄位名稱:northeast/southwest -> high/low。
results.postcode_localities results.postalCodeLocalities 已重新命名。現在會傳回一或多個地區 (第 3 版必須大於 1)。
results.partial_match 已移除
新功能 results.addressComponents.languageCode 特定地址元件的語言。
新功能 results.bounds 使用 high/low 的明確邊界。
新功能 results.place 地點的資源名稱。
新功能 results.postalAddress 結構化 PostalAddress 物件。

重現第 3 版元件篩選器

Geocoding v3 的正向地理編碼包含 components 參數,可對特定元件 (例如 components=country:US) 的結果進行硬篩選。v4 的正向地理編碼不支援在要求參數中進行這類硬篩選。在第 4 版中,您可以將地址資訊提供為路徑中的單一非結構化字串,或提供為結構化查詢參數,例如 address.addressLines、address.locality、address.administrativeArea、address.postalCode 和 address.regionCode。結構化參數不會做為硬性篩選條件。而是會將所有提供的元件合併,形成 API 嘗試進行地理編碼的完整地址。API 會在所有元件中,搜尋與指定完整地址最相符的結果。以結構化方式提供元件,有助於 API 更好地理解意圖,尤其是從不同表單欄位收集地址資訊時。由於每個欄位的資訊類型都很明確,因此 API 可以處理各個元件中的輕微錯字或模糊不清之處。

如要達到類似第 3 版硬性元件篩選器的行為,您必須根據回應中的 postalAddress 和 addressComponents 物件,在第 4 版 API 傳回的 results 陣列上實作用戶端篩選。

下表列出 v3 components 篩選器和 v4 postalAddress 欄位之間最相符的對應關係:

v3 元件篩選器 v4 回應欄位
country postalAddress.regionCode
postal_code postalAddress.postalCode
administrative_area postalAddress.administrativeArea 或 addressComponents

用戶端篩選範例

以下範例說明如何篩選結果,針對支援的欄位 (國家/地區、行政區和郵遞區號) 達到類似第 3 版元件篩選的行為。不建議篩選其他欄位,因為沒有可靠的篩選條件。

依國家/地區篩選

將要求中的 address.regionCode 與各項結果中的 postalAddress.regionCode 進行比對。請注意,雖然 API 接受要求中的小寫區域代碼,但一律會在回應中傳回大寫代碼,因此您必須先將輸入內容標準化為大寫,再進行比較。

Python 程式碼範例
def filter_by_country(results, region_code):
    return [
        res for res in results
        if res.get('postalAddress', {}).get('regionCode') == region_code.upper()
    ]

# Example usage:
# v4_response = ... # API call response
# filtered = filter_by_country(v4_response.get('results', []), 'US')
Node.js 程式碼範例
function filterByCountry(results, regionCode) {
    return results.filter(res => res.postalAddress?.regionCode === regionCode.toUpperCase());
}

// Example usage:
// const v4Response = ... # API call response
// const filtered = filterByCountry(v4Response.results, 'US');
依行政區篩選

將要求中的 address.administrativeArea 與各項結果中的 postalAddress.administrativeArea 進行比對。與國家/地區代碼類似,您應先將輸入內容標準化為大寫,再進行比較。只有在要求中的 administrativeArea 採用標準 2 字母縮寫時,這項功能才可靠。回應中的 postalAddress.administrativeArea 可能不會直接與 ISO 3166-2 代碼後置字串相符,原因有幾個。首先,在某些地區,ISO 3166-2 代碼對應的子行政區並非郵政地址標準表示法的一部分。舉例來說,在西班牙,addressComponents 中可能會有第 1 級行政區 (例如「CN」代表加那利群島),但 postalAddress.administrativeArea 可能包含第 2 級區域 (例如「聖克魯斯德特內里費」)。其次,在某些地區,行政區的縮寫並不常用,因此在 postalAddress.administrativeArea 中會以較長的名稱表示 (基於類似原因,對應行政區的地址元件 addressComponents.shortText 可能與行政區的 ISO 3166-2 代碼後置字串不符)。此外,在某些情況下,子行政區可能會以 administrative_area_level_n 的地址元件表示,其中 n 大於 1。 為取得最全面的結果,請檢查 postalAddress.administrativeArea 和任何具有 administrative_area_level_n 類型的 addressComponents (例如 administrative_area_level_1) 是否相符。

Python 程式碼範例
def filter_by_admin_area(results, admin_area):
    target = admin_area.upper()
    filtered = []
    for res in results:
        # Check postalAddress
        pa_aa = res.get('postalAddress', {}).get('administrativeArea', '')
        if pa_aa == target:
            filtered.append(res)
            continue
        # Check all administrative area levels in addressComponents
        for comp in res.get('addressComponents', []):
            is_admin_level = any(t.startswith('administrative_area_level_')
                               for t in comp.get('types', []))
            if is_admin_level:
                short = comp.get('shortText', '')
                if short == target:
                    filtered.append(res)
                    break
    return filtered

# Example usage:
# filtered = filter_by_admin_area(v4_response.get('results', []), 'CA')
Node.js 程式碼範例
function filterByAdminArea(results, adminArea) {
    const target = adminArea.toUpperCase();
    return results.filter(res => {
        // Check postalAddress.administrativeArea
        if (res.postalAddress?.administrativeArea === target) {
            return true;
        }
        // Check addressComponents for any administrative area level
        return res.addressComponents?.some(c => {
            const isAdmin = c.types?.some(t => t.startsWith('administrative_area_level_'));
            return isAdmin && c.shortText === target;
        });
    });
}

// Example usage:
// const filtered = filterByAdminArea(v4Response.results, 'CA');
依郵遞區號篩選

將要求中的 address.postalCode 與各項結果中的 postalAddress.postalCode 進行比對。比較前,請務必將輸入和結果郵遞區號正規化。正規化邏輯會因區域而異。基本做法包括移除空格和連字號,並轉換為一致的大小寫。

如要處理郵遞區號前置字元 (例如英國的「L2」) 或後置字元 (例如美國郵遞區號的「+4」),可能需要更進階的正規化。具體所需的正規化邏輯,會因國家/地區和郵遞區號格式而異。您可能需要調整提供的正規化函式,或針對指定區域實作更複雜的邏輯。

提供的範例會處理美國郵遞區號,即使提供或傳回 ZIP+4,也能比對 5 位數的基本郵遞區號。

Python 程式碼範例 (以美國郵遞區號為主)
import re

def normalize_postal_code_us(pc):
    # Basic normalization for US: uppercase, alphanumeric only
    if not pc:
        return ""
    normalized = re.sub(r'[^A-Z0-9]', '', pc.upper())
    # Return only the first 5 digits for US ZIP codes
    return normalized[:5]

def filter_by_postal_code_us(results, postal_code):
    normalized_input = normalize_postal_code_us(postal_code)
    if not normalized_input:
        return []
    filtered_results = []
    for res in results:
        pa_pc = res.get('postalAddress', {}).get('postalCode', '')
        if normalize_postal_code_us(pa_pc) == normalized_input:
            filtered_results.append(res)
    return filtered_results

# Example usage:
# Matches '94043', '94043-1351', '940431351'
# filtered = filter_by_postal_code_us(v4_response.get('results', []), '94043')
# filtered = filter_by_postal_code_us(v4_response.get('results', []), '94043-1234')
Node.js 程式碼範例 (以美國郵遞區號為主)
function normalizePostalCodeUs(pc) {
    // Basic normalization for US: uppercase, alphanumeric only
    const normalized = (pc || '').toUpperCase().replace(/[^A-Z0-9]/g, '');
    // Return only the first 5 digits for US ZIP codes
    return normalized.substring(0, 5);
}

function filterByPostalCodeUs(results, postalCode) {
    const normalizedInput = normalizePostalCodeUs(postalCode);
    if (!normalizedInput) {
        return [];
    }
    return results.filter(res => {
        const paPc = res.postalAddress?.postalCode || '';
        return normalizePostalCodeUs(paPc) === normalizedInput;
    });
}

// Example usage:
// Matches '94043', '94043-1351', '940431351'
// const filtered = filterByPostalCodeUs(v4Response.results, '94043');
// const filtered = filterByPostalCodeUs(v4Response.results, '94043-1234');

從第 3 版反向地理編碼遷移

如果您使用 v3 反向地理編碼將座標轉換為地址,請改用 v4 反向地理編碼位置方法,該方法接受 GET 要求。

v4 API 變更了名稱、結構和對多個參數的支援。強烈建議您使用欄位遮罩,指定要在回應中傳回的欄位。

要求變更參數

v3 參數 v4 參數 附註
language languageCode 已重新命名。如果省略,v3 會使用 Accept-Language 標頭或預設為 en,而 v4 則預設為地點的當地語言和文字。
region regionCode 已重新命名。如果省略,v3 預設為 us,v4 則沒有預設值。
result_type types 已重新命名,使用重複的查詢參數。
location_type granularity 已重新命名,使用重複的查詢參數。
extra_computations 已移除 由 SearchDestinations 方法取代。

回應欄位變更

v3 欄位 v4 欄位 附註
status、error_message 已移除 v4 使用 HTTP 狀態碼和錯誤主體。
results.address_components.long_name/results.address_components.short_name results.addressComponents.longText/results.addressComponents.shortText 已重新命名。
results.geometry.location_type results.granularity 已重新命名。
results.geometry.location results.location 欄位名稱:lat/lng -> latitude/longitude。
results.geometry.viewport results.viewport 欄位名稱:northeast/southwest -> high/low。
新功能 results.addressComponents.languageCode 特定地址元件的語言。
新功能 results.bounds 使用 high/low 的明確邊界。
新功能 results.place 地點的資源名稱。
新功能 results.postalAddress 結構化 PostalAddress 物件。

從第 3 版地址描述元遷移

在 Geocoding API v3 中,傳遞 extra_computations=ADDRESS_DESCRIPTORS 會傳回包含 landmarks[] 和 areas[] 的 address_descriptor 物件。在 Geocoding API v4 中,這項功能已由 SearchDestinations 方法 (POST /v4/geocode/destinations) 取代,可直接在每個 Destination 物件中傳回豐富的空間背景資訊。

下表列出舊版 v3 address_descriptor 欄位與 v4 SearchDestinations 對應欄位的對應關係:

v3 address_descriptor 欄位 v4 SearchDestinations 欄位 遷移注意事項
landmarks[] destinations[].landmarks[] 在X-Goog-FieldMask中要求 destinations.landmarks。
landmarks[].place_id landmarks[].place.placeId 包裝在 place (PlaceView) 物件中。
landmarks[].display_name landmarks[].place.displayName 本地化文字 (text 和 languageCode)。
landmarks[].types landmarks[].place.types 地點類型字串陣列。
landmarks[].spatial_relationship landmarks[].relationalDescription v4 不會傳回原始列舉 (ACROSS_THE_ROAD、AROUND_THE_CORNER),而是傳回本地化且人類可讀的詞組 (例如 "Across from Central Park")。
landmarks[].straight_line_distance_meters landmarks[].straightLineDistanceMeters 重新命名為小駝峰式命名法。
landmarks[].travel_distance_meters landmarks[].travelDistanceMeters 重新命名為小駝峰式命名法。
areas[] (containment 列舉) destinations[].containingPlaces[]和subDestinations[] 最接近的結構階層等效項目:封閉式建築群、校園、購物中心和建築物,在 containingPlaces[] 和 subDestinations[] 中會以結構化 PlaceView 物件傳回 (structureType 設為 GROUNDS 或 BUILDING,以及選用的 displayPolygon 輪廓)。請注意,鄰近區域/行政區或第 3 版 containment 列舉 (NEAR、WITHIN、OUTSKIRTS) 沒有直接的 1:1 替代項目。

從第 3 版 Place 地理編碼遷移

如果您使用 place_id 搭配 Geocoding v3 取得特定地點 ID 的地址,則必須遷移至 v4 Place Geocoding 方法,該方法接受 GET 要求。

v4 API 變更了名稱、結構和對多個參數的支援。強烈建議您使用欄位遮罩,指定要在回應中傳回的欄位。

要求變更參數

v3 參數 v4 參數 附註
place_id 要求 proto 中的 place 欄位 地點 ID 現在會以路徑參數 places/{place} 的形式提供,例如:https://geocode.googleapis.com/v4/geocode/places/ChIJj61dQgK6j4AR4GeTYWZsKWw。這會對應至基礎要求中的地點欄位。
language languageCode 已重新命名。如果省略,v3 會使用 Accept-Language 標頭或預設為 en,而 v4 則預設為地點的當地語言和文字。
region regionCode 已重新命名。如果省略,v3 預設為 us,v4 則沒有預設值。

回應欄位變更

v3 欄位 v4 欄位 附註
status、error_message 已移除 v4 使用 HTTP 狀態碼和錯誤主體。
results (root) v4 傳回單一結果物件,而非 results 陣列。
results.address_components.long_name/results.address_components.short_name addressComponents.longText/addressComponents.shortText 已重新命名。
results.geometry.location_type granularity 已重新命名。
results.geometry.location location 欄位名稱:lat/lng -> latitude/longitude。
results.geometry.viewport viewport 欄位名稱:northeast/southwest -> high/low。
results.postcode_localities postalCodeLocalities 已重新命名。現在會傳回一或多個地區 (第 3 版必須大於 1)。
新功能 addressComponents.languageCode 特定地址元件的語言。
新功能 bounds 使用 high/low 的明確邊界。
新功能 place 地點的資源名稱。
新功能 postalAddress 結構化 PostalAddress 物件。

從 Geocoding Hyperlocal Data 遷移至 Destinations

Geocoding API v3 的下列功能,將由 Geocoding API v4 的 SearchDestinations 方法取代:

  • 進入
  • 導航點
  • 建築物輪廓
  • 園區

如果您先前使用 Geocoding API v3 執行上述功能,請參閱本文件,瞭解如何改用 SearchDestinations 方法。本文說明在 SearchDestinations 回應中,何處可找到這些功能,以及在 Geocoding API 第 3 版和 Geocoding API 第 4 版的 SearchDestinations 方法之間,這些功能在 API 回應中的表示方式有何差異。

進入

如要取得與 destination 相關聯的入口,請使用 destination.entrances 欄位。

destination.entrances 中的每個 entrance 包含下列欄位 (取代舊版 v3 buildings.entrances 結構定義):

  • displayName:新的選填欄位,包含入口的人類可讀名稱,例如 "Gate B"。
  • location:使用 latitude 和 longitude 的 LatLng 類型位置 (取代 v3 lat/lng)。
  • tags:與 Geocoding API v3 入口 (例如 PREFERRED) 的 tags 欄位相同。
  • place:地點的資源名稱 (例如 places/ChIJ...),取代 Geocoding API v3 的 building_place_id 欄位。在第 4 版中,這個地點可以代表任何目的地結構 (建築物、校園或公園),不限於建築物。

如要取得與 destination 相關聯的導航點,請使用 destination.navigationPoints 欄位。

destination.navigationPoints 中的每個 navigationPoint 包含下列欄位 (取代舊版 v3 navigation_points 結構定義):

  • navigationPointToken:加密權杖字串,用於在 Routes API ComputeRoutes 方法 (Waypoint.navigationPointToken) 中轉送至這個特定導航點。請參閱「使用導航點權杖規劃路線」。
  • displayName:新的選用欄位,包含導航點的人類可讀名稱,例如 "5th Ave"。
  • location:使用 latitude 和 longitude 的 LatLng 類型位置。
  • travelModes:與 Geocoding API 第 3 版的 restricted_travel_modes 欄位類似,但第 4 版指定的是「可接受」的交通方式 (DRIVE、WALK),而非受限的交通方式。
  • usage:新欄位,用於指定導航點支援的抵達用途 (例如 PICKUP、DROPOFF 或 PARKING)。

建築物輪廓

如要取得與 destination 相關聯的建築物輪廓,請使用 destination 中代表建築物的 placeView 物件 displayPolygon 欄位 (取代舊版 v3 buildings.building_outlines 陣列)。針對每個 placeView,請檢查是否為具有 placeView.structureType 欄位的建築物。如果結構類型為 BUILDING,您可以從 placeView.displayPolygon 欄位取得大綱。placeView 也會提供 Geocoding API v3 沒有的建築物額外欄位。

destination 可包含 placeView 物件,代表下列欄位中的建築物:

  • destination.primary:目的地的主要地點。
  • destination.containingPlaces:重複欄位,用於存放「包含」主要地點的較大地點。舉例來說,如果主要地點是 subpremise,containingPlaces 通常會保留代表建築物的 placeView。
  • destination.subDestinations:重複欄位,用於存放主要地點的子目的地 (例如建築物內的個別公寓單位或套房)。

請注意,placeView.displayPolygon 的格式與 Geocoding API v3 中的建築物輪廓格式相符,也就是 GeoJSON 格式,使用 RFC 7946 格式。

園區

與建築物輪廓類似,如要取得與 destination 相關聯的地面,請使用 destination 中代表地面的 placeView 物件 displayPolygon 欄位 (取代舊版 v3 grounds 陣列)。針對每個 placeView,請使用 placeView.structureType 欄位檢查是否為地面多邊形。如果結構類型為 GROUNDS,您可以從 placeView.displayPolygon 欄位取得大綱。placeView 也會提供 Geocoding API v3 中沒有的理由欄位。

destination 可包含 placeView 物件,代表下列欄位中的理由:

  • destination.primary
  • destination.containingPlaces
  • destination.subDestinations

請注意,placeView.displayPolygon 的格式與 Geocoding API v3 中的場地輪廓格式相符,也就是採用 RFC 7946 格式的 GeoJSON 格式。

結果精確度

在 Geocoding API v3 中,回應幾何圖形中的 location_type 欄位會指出結果的精確度,部分用戶端會根據這些值 (ROOFTOP、RANGE_INTERPOLATED、GEOMETRIC_CENTER 和 APPROXIMATE) 排序或篩選結果。在標準 Geocoding API v4 遷移作業中,這個欄位會重新命名為 granularity。

Destinations API (v4 SearchDestinations) 沒有 location_type 欄位。空間資訊的處理方式則有所不同:

  • 不需手動篩選用戶端:標準地理編碼會提供多個不同精細程度的結果,而 SearchDestinations 方法會盡可能解析為單一最佳目的地,將模糊不清的情況降到最低。這樣一來,用戶端就不需要依地點類型篩選,判斷哪個結果最合適。
  • 以結構類型和顯示多邊形表示的空間資訊: 空間幾何圖形和結構會以以下方式表示:
    • displayPolygon (用於精確幾何圖形)。
    • placeView 物件中的 structureType 欄位。
  • 結構類型對應:
    • POINT、BUILDING 或 SECTION 的 structureType 通常對應到先前稱為 ROOFTOP 的項目。
    • GROUNDS 的 structureType 通常對應 GEOMETRIC_CENTER。

使用欄位遮罩要求這些功能

SearchDestinations 方法需要欄位遮罩,詳情請參閱「選擇要傳回的欄位」。欄位遮罩可設為 *,以便傳回所有欄位,也可以設為您要接收的特定欄位。舉例來說,下列 API 要求會設定欄位遮罩,接收取得目的地入口、導航點、建築物輪廓和地面所需的所有欄位:

curl -X POST -d '{"place": "places/ChIJG3kh4hq6j4AR_XuFQnV0_t8"}' \
  -H "X-Goog-Api-Key: API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Goog-FieldMask: destinations.entrances,destinations.navigationPoints,destinations.primary,destinations.containingPlaces,destinations.subDestinations" \
  https://geocode.googleapis.com/v4/geocode/destinations

常見的遷移方法和程式碼模式

從 Geocoding v3 或 Geocoding++ 遷移複雜的工作流程至 SearchDestinations 時,請使用下列程式碼範例,擷取常見的空間和地址結構。

擷取最佳建築物多邊形

如要算繪某個位置或目的地的建築物足跡,請按照下列步驟操作:

  1. 檢查 primary 地點是否包含 structureType === 'BUILDING' 和 displayPolygon。
  2. 如果不是,請檢查 containingPlaces,找出包圍的建築物 (如果主要目的地是大型建築物的子目的地,這項功能就很有用)。
  3. 如果找不到涵蓋的建築物,請查看 subDestinations,確認是否列出個別建築物。
Python 程式碼範例
def get_best_building_polygon(destination):
    primary = destination.get('primary', {})
    if primary.get('structureType') == 'BUILDING' and 'displayPolygon' in primary:
        return primary['displayPolygon']

    # Check containing places for the parent building
    for place in destination.get('containingPlaces', []):
        if place.get('structureType') == 'BUILDING' and 'displayPolygon' in place:
            return place['displayPolygon']

    # Fallback to sub-destinations if available
    for place in destination.get('subDestinations', []):
        if place.get('structureType') == 'BUILDING' and 'displayPolygon' in place:
            return place['displayPolygon']

    return None
Node.js 程式碼範例
function getBestBuildingPolygon(destination) {
    const { primary = {}, containingPlaces = [], subDestinations = [] } = destination;

    if (primary.structureType === 'BUILDING' && primary.displayPolygon) {
        return primary.displayPolygon;
    }

    const parentBuilding = containingPlaces.find(
        p => p.structureType === 'BUILDING' && p.displayPolygon
    );
    if (parentBuilding) {
        return parentBuilding.displayPolygon;
    }

    const subBuilding = subDestinations.find(
        p => p.structureType === 'BUILDING' && p.displayPolygon
    );
    return subBuilding?.displayPolygon || null;
}

擷取地面輪廓

如需公寓大樓、企業園區、購物中心或公園的邊界:

  1. 如果 primary 地點代表整個建築群 (structureType === 'GROUNDS'),請使用 primary.displayPolygon。
  2. 如果 primary 地點是位於大型建築群內的建築物或入口,請在 containingPlaces 中找出周圍的土地。
Python 程式碼範例
def get_grounds_polygon(destination):
    primary = destination.get('primary', {})
    if primary.get('structureType') == 'GROUNDS' and 'displayPolygon' in primary:
        return primary['displayPolygon']

    for place in destination.get('containingPlaces', []):
        if place.get('structureType') == 'GROUNDS' and 'displayPolygon' in place:
            return place['displayPolygon']

    return None
Node.js 程式碼範例
function getGroundsPolygon(destination) {
    const { primary = {}, containingPlaces = [] } = destination;

    if (primary.structureType === 'GROUNDS' && primary.displayPolygon) {
        return primary.displayPolygon;
    }

    const groundsPlace = containingPlaces.find(
        p => p.structureType === 'GROUNDS' && p.displayPolygon
    );
    return groundsPlace?.displayPolygon || null;
}

選取地標,用於顯示地址和最後一哩路抵達

Geocoding v3 address_descriptor 傳回 spatial_relationship 列舉 (WITHIN、BESIDE、BEHIND、NEAR、ACROSS_THE_ROAD、AROUND_THE_CORNER)。在 SearchDestinations 中:

  • 沒有原始關係列舉。請改為直接使用 relationalDescription.text。其中包含本地化、可供顯示的詞組,例如 "Across from Central Park" 或 "Behind the convention center"。請勿手動在前方加上 "Near "。
  • 使用 tags 選取適合您用途的地標:
    • ARRIVAL:緊鄰目的地、面向目的地或與目的地隔街相望的超地方地標 (通常在 50 到 100 公尺內)。最適合用於即時駕駛抵達、快遞送達確認,或應用程式內抵達卡。
    • ADDRESS:周邊地區 (通常在 100 到 500 公尺內) 的當地知名地標。最適合用於客戶結帳、訂單審查或行程前目的地確認。
    • 如果地標同時具有 ADDRESS 和 ARRIVAL 標記,表示該地標在附近知名,且緊鄰目的地。
Python 程式碼範例
def get_best_relational_landmark(destination, is_last_mile=True):
    """Select the best landmark based on user context.

    Args:
        destination: Destination object from SearchDestinations.
        is_last_mile: True for arrival and delivery guidance (prefers ARRIVAL tags).
                      False for address display and disambiguation (prefers ADDRESS tags).

    Returns:
        Dict with landmark description, distances, tags, and placeId, or None.
    """
    landmarks = destination.get('landmarks', [])
    if not landmarks:
        return None

    target_tag = 'ARRIVAL' if is_last_mile else 'ADDRESS'

    # Landmarks are ranked by relevance and proximity. Select the first candidate matching the target tag.
    for lm in landmarks:
        tags = lm.get('tags', [])
        if target_tag in tags or ('ARRIVAL' in tags and 'ADDRESS' in tags):
            description = lm.get('relationalDescription', {}).get('text')
            if description:
                return {
                    'description': description,
                    'travelDistanceMeters': lm.get('travelDistanceMeters'),
                    'straightLineDistanceMeters': lm.get('straightLineDistanceMeters'),
                    'tags': tags,
                    'placeId': lm.get('place', {}).get('placeId')
                }

    # Fallback to the first available landmark with a relational description if no tagged match is found
    for lm in landmarks:
        description = lm.get('relationalDescription', {}).get('text')
        if description:
            return {
                'description': description,
                'travelDistanceMeters': lm.get('travelDistanceMeters'),
                'straightLineDistanceMeters': lm.get('straightLineDistanceMeters'),
                'tags': lm.get('tags', []),
                'placeId': lm.get('place', {}).get('placeId')
            }

    return None
Node.js 程式碼範例
/**
 * Select the best landmark based on user context.
 *
 * @param {Object} destination Destination object from SearchDestinations.
 * @param {boolean} [isLastMile=true] True for arrival and delivery guidance
 *        (prefers ARRIVAL tags). False for address display and disambiguation
 *        (prefers ADDRESS tags).
 * @return {Object|null}
 */
function getBestRelationalLandmark(destination, isLastMile = true) {
    const landmarks = destination.landmarks || [];
    if (!landmarks.length) return null;

    const targetTag = isLastMile ? 'ARRIVAL' : 'ADDRESS';

    // Landmarks are ranked by relevance and proximity. Select the first candidate matching the target tag.
    const match = landmarks.find((lm) => {
        const tags = lm.tags || [];
        return (tags.includes(targetTag) || (tags.includes('ARRIVAL') && tags.includes('ADDRESS'))) &&
               lm.relationalDescription?.text;
    }) || landmarks.find((lm) => lm.relationalDescription?.text);

    if (match) {
        return {
            description: match.relationalDescription.text,
            travelDistanceMeters: match.travelDistanceMeters,
            straightLineDistanceMeters: match.straightLineDistanceMeters,
            tags: match.tags || [],
            placeId: match.place?.placeId || null,
        };
    }

    return null;
}

規劃前往車輛導航點和偏好入口的路線

最後一哩路派遣和抵達工作流程結合了兩種互補的空間存取點:

  • 即時路線:使用 navigationPoints 將車輛導航至指定的道路下車點。導航點會預先依鄰近程度和關聯性排序,因此第一個符合所需交通方式 (例如 'DRIVE') 和用途標記 (例如 'DROPOFF' 或 'PICKUP') 的候選點,就是最佳車輛路線控點。將 navigationPointToken 傳送至 Routes API ComputeRoutes 方法 (Waypoint.navigationPointToken) 或 Navigation SDK。如果沒有道路貼齊行車點,則會改用建築物入口或主要群集中心。
  • 實體門口通道:使用 destinations.entrances 引導步行快遞員或乘客前往建築物入口。查看 'PREFERRED' 的 tags,找出可能提供直接實體存取權的入口。如果沒有入口資料,則改用行人導航點 (travelModes,包括 'WALK') 或主要群集中心。
Python 程式碼範例
def get_routing_and_arrival_points(destination, usage_tag=None):
    navigation_points = destination.get('navigationPoints', [])
    entrances = destination.get('entrances', [])
    primary_location = destination.get('primary', {}).get('location')

    # 1. Doorstep / Physical Entrance
    # Prefer PREFERRED entrance -> any entrance -> WALK nav point -> primary location
    preferred_entrance = None
    for ent in entrances:
        if 'PREFERRED' in ent.get('tags', []):
            preferred_entrance = ent
            break
    if not preferred_entrance and entrances:
        preferred_entrance = entrances[0]

    walk_nav_point = None
    for np in navigation_points:
        if 'WALK' in np.get('travelModes', []):
            walk_nav_point = np
            break

    if preferred_entrance:
        doorstep_location = preferred_entrance.get('location')
    elif walk_nav_point:
        doorstep_location = walk_nav_point.get('location')
    else:
        doorstep_location = primary_location

    # 2. Vehicle Routing Waypoint
    # Find a driving navigation point. If usage_tag (such as 'DROPOFF' or 'PICKUP')
    # is specified, prioritize a candidate with that usage tag; otherwise fall back
    # to the first available driving point.
    drive_nav_point = None
    fallback_drive_point = None
    for np in navigation_points:
        if 'DRIVE' in np.get('travelModes', []):
            if usage_tag and usage_tag in np.get('usages', []):
                drive_nav_point = np
                break  # Exact match for desired usage tag
            elif fallback_drive_point is None:
                fallback_drive_point = np  # First driving point as fallback

    if not drive_nav_point:
        drive_nav_point = fallback_drive_point

    if drive_nav_point:
        vehicle_location = drive_nav_point.get('location')
        nav_token = drive_nav_point.get('navigationPointToken')
    elif preferred_entrance:
        vehicle_location = preferred_entrance.get('location')
        nav_token = None
    else:
        vehicle_location = primary_location
        nav_token = None

    return {
        'vehicleRoutingWaypoint': {
            'navigationPointToken': nav_token,
            'location': vehicle_location,
        },
        'doorstepLocation': doorstep_location,
    }
Node.js 程式碼範例
function getRoutingAndArrivalPoints(destination, usageTag = null) {
    const navigationPoints = destination.navigationPoints || [];
    const entrances = destination.entrances || [];
    const primaryLocation = destination.primary?.location || null;

    // 1. Doorstep / Physical Entrance
    const preferredEntrance = entrances.find(ent => ent.tags?.includes('PREFERRED'))
        || entrances[0]
        || null;

    const walkNavPoint = navigationPoints.find(np => np.travelModes?.includes('WALK'))
        || null;

    const doorstepLocation = preferredEntrance?.location
        || walkNavPoint?.location
        || primaryLocation;

    // 2. Vehicle Routing Waypoint
    // Find a driving navigation point. If usageTag (such as 'DROPOFF' or 'PICKUP')
    // is specified, prioritize a candidate with that usage tag; otherwise fall back
    // to the first available driving point.
    const driveNavPoint = (usageTag && navigationPoints.find(np => np.travelModes?.includes('DRIVE') && np.usages?.includes(usageTag)))
        || navigationPoints.find(np => np.travelModes?.includes('DRIVE'))
        || null;

    let vehicleLocation = primaryLocation;
    let navToken = null;
    if (driveNavPoint) {
        vehicleLocation = driveNavPoint.location;
        navToken = driveNavPoint.navigationPointToken;
    } else if (preferredEntrance) {
        vehicleLocation = preferredEntrance.location;
    }

    return {
        vehicleRoutingWaypoint: {
            navigationPointToken: navToken,
            location: vehicleLocation,
        },
        doorstepLocation: doorstepLocation,
    };
}

安全性考量

Geocoding API v4 是伺服器對伺服器 API。JavaScript 使用者無法直接從第 3 版遷移至第 4 版。如果使用 API 金鑰,直接從用戶端 JavaScript (例如在瀏覽器中) 呼叫 v4 方法,API 金鑰很容易遭竊和濫用。

HTTP 參照網址限制雖然實用,但不足以保護 Web 服務端點,因為攻擊者只要在要求中偽造 Referer 標頭,就能輕易規避這類限制。

建議您從自己的後端伺服器使用 Geocoding API v4。用戶端應用程式應向這個中介伺服器提出要求,中介伺服器再使用受保護的 API 金鑰 (例如儲存在環境變數或 Secret Manager 中的金鑰),安全地呼叫 Google API。這樣一來,API 金鑰就不會出現在前端程式碼中。

用戶端需求的替代方案

如果您有需要進行地理編碼的用戶端需求,請考慮使用下列其中一種現有的用戶端解決方案: