Переход с Geocoding API версии 3 на версию 4

Разработчики из Европейской экономической зоны (ЕЭЗ)

В Geocoding API версии 4 появилось несколько новых методов, которые заменяют функции версии 3. В этом руководстве рассказывается, как перенести приложение на новые методы версии 4.

Вы можете использовать существующие ключи API с новыми методами v4. Однако если вы запрашивали увеличение квоты для версии 3 API, вам нужно будет запросить его и для версии 4.

Переход с прямого геокодирования версии 3

Если вы используете v3 Geocoding для геокодирования адресов, перейдите на метод Geocode an address v4, который принимает GET-запрос.

В API v4 изменены названия, структура и поддержка нескольких параметров. Мы настоятельно рекомендуем использовать маску полей, чтобы указать, какие поля должны возвращаться в ответе.

Изменения параметров запроса

Параметр v3 Параметр v4 Примечания
address, components address Неструктурированный адрес (v3 address) теперь передается в пути URL. Структурированные компоненты адреса (v3 components) можно передавать как параметры запроса address.*. Подробнее о том, как воспроизвести фильтры компонентов версии 3…
bounds locationBias.rectangle Переименовано; структура изменена на объект.
language languageCode Объект переименован. Если этот параметр не указан, в версии 3 используется заголовок Accept-Language или значение по умолчанию en, а в версии 4 – местный язык и письменность.
region regionCode Объект переименован. Если он не указан, в v3 по умолчанию используется us, а в v4 значения по умолчанию нет.
extra_computations Удалено Заменен методом SearchDestinations.

Изменения в поле ответа

Поле v3 Поле v4 Примечания
status, error_message Удалено В версии 4 используются коды статуса 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 требовалось больше одного).
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, необходимо реализовать фильтрацию на стороне клиента для массива results, возвращаемого API версии 4, на основе объектов postalAddress и addressComponents в ответе.

В таблице ниже показано, как фильтры components версии 3 соотносятся с полями postalAddress версии 4.

Фильтр компонентов v3 Поле ответа в версии 4
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. Значение postalAddress.administrativeArea в ответе может не совпадать с суффиксами кодов ISO 3166-2 по нескольким причинам. Во-первых, в некоторых регионах административная единица, соответствующая коду ISO 3166-2, не является частью стандартного представления почтовых адресов. Например, в Испании административный район первого уровня (например, "CN" для Канарских островов) может быть указан в поле addressComponents, а в поле postalAddress.administrativeArea может содержаться район второго уровня (например, "Санта-Крус-де-Тенерифе"). Во-вторых, в некоторых регионах сокращение административной единицы не используется, и в postalAddress.administrativeArea она представлена более длинным названием. По аналогичным причинам addressComponents.shortText для компонента адреса, соответствующего административной единице, может не совпадать с суффиксом кода ISO 3166-2 административной единицы. Кроме того, в некоторых случаях административная единица может быть представлена в компоненте адреса для administrative_area_level_n, где n больше 1. Чтобы получить наиболее полные результаты, проверьте совпадения как в postalAddress.administrativeArea, так и в любом addressComponents с типом administrative_area_level_n (например, 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.

Пример кода на 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-запрос.

В API v4 изменены названия, структура и поддержка нескольких параметров. Мы настоятельно рекомендуем использовать маску полей, чтобы указать, какие поля должны возвращаться в ответе.

Изменения параметров запроса

Параметр v3 Параметр v4 Примечания
language languageCode Объект переименован. Если этот параметр не указан, в версии 3 используется заголовок Accept-Language или значение по умолчанию en, а в версии 4 – местный язык и письменность.
region regionCode Объект переименован. Если он не указан, в v3 по умолчанию используется us, а в v4 значения по умолчанию нет.
result_type types Переименован; использует повторяющиеся параметры запроса.
location_type granularity Переименован; использует повторяющиеся параметры запроса.
extra_computations Удалено Заменен методом SearchDestinations.

Изменения в поле ответа

Поле v3 Поле v4 Примечания
status, error_message Удалено В версии 4 используются коды статуса 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 версии 3 передача extra_computations=ADDRESS_DESCRIPTORS возвращала объект address_descriptor, содержащий landmarks[] и areas[]. В Geocoding API v4 эта возможность заменена методом SearchDestinations (POST /v4/geocode/destinations), который возвращает подробный пространственный контекст непосредственно в каждом объекте Destination.

В таблице ниже показано, как поля address_descriptor устаревшей версии 3 сопоставляются с эквивалентными полями SearchDestinations версии 4.

Поле address_descriptor (версия 3) Поле SearchDestinations (версия 4) Примечания к переносу
landmarks[] destinations[].landmarks[] Запросите destinations.landmarks в X-Goog-FieldMask.
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 Вместо необработанного перечисления (ACROSS_THE_ROAD, AROUND_THE_CORNER) версия 4 возвращает локализованную, понятную человеку фразу (например, "Across from Central Park").
landmarks[].straight_line_distance_meters landmarks[].straightLineDistanceMeters Переименовано в lower camelCase.
landmarks[].travel_distance_meters landmarks[].travelDistanceMeters Переименовано в lower camelCase.
areas[] (перечисление containment) destinations[].containingPlaces[] и subDestinations[] Ближайший доступный эквивалент для структурной иерархии: окружающие комплексы, кампусы, торговые центры и здания возвращаются как структурированные объекты PlaceView в containingPlaces[] и subDestinations[] (с structureType, установленным на GROUNDS или BUILDING, и необязательными контурами displayPolygon). Обратите внимание, что для районов и округов, а также для перечисления containment в версии 3 (NEAR, WITHIN, OUTSKIRTS) нет прямой замены.

Переход с геокодирования мест в версии 3

Если вы используете place_id, чтобы получить адрес для определенного идентификатора места с помощью Geocoding v3, вам нужно перейти на метод геокодирования мест v4, который принимает GET-запрос.

В API v4 изменены названия, структура и поддержка нескольких параметров. Мы настоятельно рекомендуем использовать маску полей, чтобы указать, какие поля должны возвращаться в ответе.

Изменения параметров запроса

Параметр v3 Параметр v4 Примечания
place_id Поле place в прототипе запроса Идентификатор места теперь указывается как параметр пути places/{place}, например https://geocode.googleapis.com/v4/geocode/places/ChIJj61dQgK6j4AR4GeTYWZsKWw. Это сопоставляется с полем места в базовом запросе.
language languageCode Объект переименован. Если этот параметр не указан, в версии 3 используется заголовок Accept-Language или значение по умолчанию en, а в версии 4 – местный язык и письменность.
region regionCode Объект переименован. Если он не указан, в v3 по умолчанию используется us, а в v4 значения по умолчанию нет.

Изменения в поле ответа

Поле v3 Поле v4 Примечания
status, error_message Удалено В версии 4 используются коды статуса 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 требовалось больше одного).
Новое addressComponents.languageCode Язык определенного компонента адреса.
Новое bounds Явные границы с использованием high/low.
Новое place Название ресурса для места.
Новое postalAddress Структурированный объект PostalAddress.

Переход с геокодирования гиперлокальных данных на Destinations

Следующие функции Geocoding API версии 3 заменяются методом SearchDestinations Geocoding API версии 4:

  • Входы
  • Точки навигации
  • Контуры зданий
  • Территория

Если вы использовали Geocoding API версии 3 для указанных выше функций, используйте этот документ, чтобы перейти на метод SearchDestinations. В этом документе объясняется, где в ответе SearchDestinations найти эти функции и чем они отличаются в ответах Geocoding API версии 3 и метода SearchDestinations Geocoding API версии 4.

Входы

Чтобы получить входы, связанные с destination, используйте поле destination.entrances.

Каждый объект entrance в destination.entrances содержит следующие поля (заменяющие устаревшую схему buildings.entrances версии 3):

  • displayName – новое необязательное поле, которое содержит понятное название входа, например "Gate B".
  • location – местоположение типа LatLng, использующее latitude и longitude (заменяет lat/lng в версии 3).
  • tags – то же, что и поле tags из входов Geocoding API v3 (например, PREFERRED).
  • place – название ресурса места (например, places/ChIJ...), заменяющее поле building_place_id из Geocoding API версии 3. В версии 4 это место может представлять собой любую структуру назначения (здание, кампус или парк), а не только здание.

Чтобы получить точки навигации, связанные с destination, используйте поле destination.navigationPoints.

Каждый объект navigationPoint в destination.navigationPoints содержит следующие поля (заменяющие устаревшую схему navigation_points версии 3):

  • navigationPointToken – зашифрованная строка токена, используемая для маршрутизации к этой конкретной точке навигации в методе ComputeRoutes Routes API (Waypoint.navigationPointToken). Подробнее о маршрутизации с использованием токенов точек навигации…
  • displayName – новое необязательное поле, содержащее понятное название точки навигации, например "5th Ave".
  • location – местоположение типа LatLng, использующее latitude и longitude.
  • travelModes – аналогично полю restricted_travel_modes из Geocoding API версии 3, за исключением того, что в версии 4 указываются допустимые способы передвижения (DRIVE, WALK), а не запрещенные.
  • usage – новое поле, в котором указываются поддерживаемые варианты прибытия для точки навигации (например, PICKUP, DROPOFF или PARKING).

Контуры зданий

Чтобы получить контуры зданий, связанные с destination, используйте поле displayPolygon объектов placeView в destination, которые представляют здания (заменяет устаревший массив buildings.building_outlines из версии 3). Для каждого объекта 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 версии 3, который является форматом GeoJSON, использующим формат RFC 7946.

Территория

Чтобы получить территорию, связанную с объектом destination, используйте поле displayPolygon объектов placeView в destination, которые представляют территорию (заменяет устаревший массив grounds в v3). Для каждого объекта placeView проверьте, является ли он многоугольником территории, с помощью поля placeView.structureType. Если тип структуры – GROUNDS, то контур можно получить из поля placeView.displayPolygon. В placeView также будут дополнительные поля для территорий, которых не было в Geocoding API v3.

Объект destination может содержать объект placeView, который представляет территорию в следующих полях:

  • destination.primary
  • destination.containingPlaces
  • destination.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 (для точной геометрии).
    • Поле structureType в объекте placeView.
  • Сопоставление типов структур:
    • Значение structureType, равное POINT, BUILDING или SECTION, обычно соответствует тому, что ранее называлось ROOFTOP.
    • structureType GROUNDS обычно соответствует 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 в метод ComputeRoutes Routes API (Waypoint.navigationPointToken) или Navigation SDK. Если точки привязки к дороге нет, используйте вход в здание или основной центроид.
  • Доступ к дверям. Направьте курьера или пассажира к входу в здание, используя destinations.entrances. Проверьте tags для 'PREFERRED', чтобы найти входы, которые, вероятно, обеспечивают прямой физический доступ. Если данных о входе нет, используйте точку навигации для пешеходов (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 версии 4 предназначен для межсерверного взаимодействия. Прямого пути перехода с версии 3 на версию 4 для пользователей JavaScript нет. Вызов методов v4 непосредственно из клиентского кода JavaScript (например, в браузере) с использованием ключа API подвергает ваш ключ API высокому риску кражи и неправомерного использования.

Ограничения по заголовку HTTP referer полезны, но недостаточны для защиты конечных точек веб-сервисов, поскольку злоумышленники могут легко обойти их, подделав заголовок Referer в своих запросах.

Рекомендуемый способ использования Geocoding API версии 4 – с вашего собственного бэкенд-сервера. Ваше клиентское приложение должно отправлять запросы на этот промежуточный сервер, который затем безопасно вызывает Google API, используя защищенный ключ API (например, ключ, хранящийся в переменной среды или диспетчере секретов). Это гарантирует, что ключ API не будет виден в коде интерфейса.

Альтернативные варианты для клиентской стороны

Если вам нужно геокодирование на стороне клиента, используйте одно из существующих решений:

  • Сервис Geocoding в Maps JavaScript API. Сервис Geocoding по-прежнему использует серверную часть версии 3 и предназначен для использования в браузере.
  • Places UI Kit. Используйте Places UI Kit, включая элементы автозаполнения мест, для элементов пользовательского интерфейса, связанных с адресами.