Geocoding API v4 引入了多个新方法,用于取代该 API v3 中的功能。本指南介绍了如何迁移应用以使用新的 v4 方法。
您可以将现有 API 密钥与新的 v4 方法搭配使用。不过,如果您已申请提高 v3 版 API 的配额,则必须申请提高新版 v4 API 的配额。
从 v3 正向地理编码迁移
如果您使用 v3 地理编码对地址进行地理编码,则应迁移到 v4 对地址进行地理编码方法,该方法接受 GET 请求。
v4 API 更改了多个参数的名称、结构和支持。我们强烈建议您使用字段掩码来指定要在响应中返回的字段。
请求参数更改
| v3 参数 | v4 参数 | 备注 |
|---|---|---|
address、components |
address |
非结构化地址 (v3 address) 现在通过网址路径传递。结构化地址组成部分(v3 components)可以作为 address.* 查询参数传递。请参阅重现 v3 组件过滤条件。 |
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 |
已重命名。现在会针对一个或多个地区返回(v3 要求 >1)。 |
results.partial_match |
已移除 | |
| 全新设计 | results.addressComponents.languageCode |
特定地址组成部分的语言。 |
| 全新设计 | results.bounds |
使用 high/low 的显式边界。 |
| 全新设计 | results.place |
地点的资源名称。 |
| 全新设计 | results.postalAddress |
结构化 PostalAddress 对象。 |
重现 v3 组件过滤条件
Geocoding v3 中的正向地理编码包含一个 components 参数,该参数可对特定组件(例如 components=country:US)的结果进行硬过滤。v4 中的正向地理编码不支持在请求参数中进行此类硬过滤。在 v4 中,您可以将地址信息作为路径中的单个非结构化字符串提供,也可以作为结构化查询参数(例如 address.addressLines、address.locality、address.administrativeArea、address.postalCode 和 address.regionCode)提供。结构化参数不会用作硬性过滤条件。而是将所有提供的组成部分组合在一起,形成 API 将尝试进行地理编码的完整地址。该 API 会在所有组成部分中搜索与指定的完整地址最匹配的结果。以结构化方式提供组成部分有助于 API 更好地理解意图,尤其是在从单独的表单字段收集地址信息时。这有助于 API 处理每个组成部分中的细微拼写错误或歧义,因为每个字段中的信息类型都很明确。
若要实现与 v3 的硬性组件过滤条件类似的行为,您必须根据响应中的 postalAddress 和 addressComponents 对象,对 v4 API 返回的 results 数组实现客户端过滤。
下表显示了 v3 components 过滤条件与 v4 postalAddress 字段之间的最佳匹配:
| v3 组件过滤条件 | v4 响应字段 |
|---|---|
country |
postalAddress.regionCode |
postal_code |
postalAddress.postalCode |
administrative_area |
postalAddress.administrativeArea 或 addressComponents |
客户端过滤示例
以下示例展示了如何过滤结果,以实现与 v3 组件过滤类似的行为,适用于以下受支持的字段:国家/地区、行政区和邮政编码。不建议对其他字段进行过滤,因为不存在可靠的过滤条件。
按国家/地区过滤
将请求中的 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 中可能包含一级行政区(例如“CN”代表加那利群岛),而 postalAddress.administrativeArea 可能包含二级行政区(例如“Santa Cruz de Tenerife”)。其次,在某些地区,行政区划的缩写并不常用,在 postalAddress.administrativeArea 中会以较长的名称表示(出于类似原因,与行政区划对应的地址组成部分的 addressComponents.shortText 可能与行政区划的 ISO 3166-2 代码后缀不匹配)。此外,在某些情况下,细分区域可能会在 administrative_area_level_n 的地址组成部分中表示,其中 n 大于 1。
为获得最全面的结果,您应在 postalAddress.administrativeArea 和任何具有 administrative_area_level_n 类型(例如 administrative_area_level_1)的 addressComponents 中检查是否存在匹配项。
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');
从 v3 反向地理编码迁移
如果您使用 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 对象。 |
从 v3 地址描述符迁移
在 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 轮廓)。请注意,对于邻近区域/地区或 v3 containment 枚举(NEAR、WITHIN、OUTSKIRTS),没有直接的 1:1 替代项。 |
从 v3 地点地理编码迁移
如果您使用 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 |
已重命名。现在会针对一个或多个地区返回(v3 要求 >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 v3 和 Geocoding API v4 的 SearchDestinations 方法的 API 响应中的表示方式有何不同。
进入次数
如需获取与 destination 关联的入口,请使用 destination.entrances 字段。
destination.entrances 中的每个 entrance 均包含以下字段(取代了旧版 v3 buildings.entrances 架构):
displayName- 一个新的可选字段,其中包含直观易懂的入口名称,例如"Gate B"。location- 使用latitude和longitude(替换 v3lat/lng)的LatLng类型的位置。tags- 与 Geocoding API v3 入口(例如PREFERRED)中的tags字段相同。place- 地点的资源名称(例如places/ChIJ...),用于替换 Geocoding API v3 中的building_place_id字段。在 v4 中,此地点可以代表任何目的地结构(建筑物、园区或公园),而不仅仅是建筑物。
导航点
如需获取与 destination 关联的导航点,请使用 destination.navigationPoints 字段。
destination.navigationPoints 中的每个 navigationPoint 均包含以下字段(取代了旧版 v3 navigation_points 架构):
navigationPointToken- 一个加密令牌字符串,用于在 Routes APIComputeRoutes方法 (Waypoint.navigationPointToken) 中路由到此特定导航点。请参阅使用导航点令牌进行路线规划。displayName- 一个新的可选字段,其中包含导航点的人类可读名称,例如"5th Ave"。location- 使用latitude和longitude的LatLng类型的位置。travelModes- 与 Geocoding API v3 中的restricted_travel_modes字段类似,但 v4 指定的是可接受的出行方式(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.primarydestination.containingPlacesdestination.subDestinations
请注意,placeView.displayPolygon 的格式与 Geocoding API v3 中的场地轮廓格式相匹配,即 GeoJSON 格式,采用 RFC 7946 格式。
结果的精确度
在 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 时,请使用以下代码配方来提取常见的空间和地址结构。
提取最佳建筑物多边形
如果您想渲染某个地点或目的地的建筑物轮廓,请执行以下操作:
- 检查
primary地点是否具有structureType === 'BUILDING'并包含displayPolygon。 - 如果不是,请检查
containingPlaces以找到封闭建筑(当主要目的地是较大建筑的子目的地时,此方法很有用)。 - 如果未找到封闭建筑,请检查
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; }
提取场地轮廓
如果您需要公寓大楼、企业园区、购物中心或公园的边界,请执行以下操作:
- 如果
primary地点代表整个建筑群 (structureType === 'GROUNDS'),请使用primary.displayPolygon。 - 如果
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 APIComputeRoutes方法 (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 用户无法直接从 v3 迁移到 v4。使用 API 密钥直接从客户端 JavaScript(例如在浏览器中)调用 v4 方法会使您的 API 密钥面临被盗和滥用的高风险。
HTTP 引荐来源网址限制虽然有用,但不足以保护 Web 服务端点,因为攻击者可以通过在请求中伪造 Referer 标头来轻松绕过这些限制。
建议的方法
建议通过您自己的后端服务器使用 Geocoding API v4。 您的客户端应用应向此中间服务器发出请求,然后该服务器使用受保护的 API 密钥(例如,存储在环境变量或 Secret Manager 中的密钥)安全地调用 Google API。这样可以确保您的 API 密钥永远不会在前端代码中公开。
客户端需求的替代方案
如果您有需要进行地理编码的客户端需求,请考虑使用以下现有客户端解决方案之一:
- Maps JavaScript API 中的地理编码服务:地理编码服务继续使用 v3 后端,专为在浏览器环境中使用而设计。
- Places UI Kit:使用 Places UI Kit(包括地点自动补全元素)来构建与地址相关的界面元素。