Migrar da API Geocoding v3 para a v4

Desenvolvedores do Espaço Econômico Europeu (EEE)

A API Geocoding v4 apresenta vários métodos novos que substituem a funcionalidade da v3. Este guia mostra como migrar seu app para usar os novos métodos da v4.

É possível usar as chaves de API atuais com os novos métodos da v4. No entanto, se você solicitou um aumento de cota na v3 da API, será necessário solicitar um aumento nas novas APIs v4.

Migrar da geocodificação direta da v3

Se você usa a Geocoding v3 para geocodificar endereços, migre para o método Geocode an address da v4, que aceita uma solicitação GET.

A API v4 muda nomes, estrutura e suporte para vários parâmetros. Recomendamos que você use uma máscara de campo para especificar os campos que quer que sejam retornados na resposta.

Mudanças nos parâmetros de solicitação

Parâmetro v3 Parâmetro v4 Observações
address, components address O endereço não estruturado (v3 address) agora é transmitido no caminho do URL. Os componentes de endereço estruturados (v3 components) podem ser transmitidos como parâmetros de consulta address.*. Consulte Reproduzir filtros de componentes da v3.
bounds locationBias.rectangle Renomeado. A estrutura mudou para objeto.
language languageCode Renomeado. Quando omitido, a v3 usa o cabeçalho Accept-Language ou o padrão en, enquanto a v4 usa o idioma e o script locais do local.
region regionCode Renomeado. Quando omitido, a v3 usa us como padrão, enquanto a v4 não tem um valor padrão.
extra_computations Removido Substituído pelo método SearchDestinations.

Mudanças no campo de resposta

Campo v3 Campo da v4 Observações
status, error_message Removido A v4 usa códigos de status HTTP e corpos de erro.
results.address_components.long_name / results.address_components.short_name results.addressComponents.longText / results.addressComponents.shortText Renomeado.
results.geometry.location_type results.granularity Renomeado.
results.geometry.location results.location Nomes de campo: lat/lng -> latitude/longitude.
results.geometry.viewport results.viewport Nomes de campo: northeast/southwest -> high/low.
results.postcode_localities results.postalCodeLocalities Renomeado. Agora é retornado para uma ou mais localidades (a v3 requer >1).
results.partial_match Removido
Nova results.addressComponents.languageCode Idioma do componente de endereço específico.
Nova results.bounds Limites explícitos usando high/low.
Nova results.place Nome do recurso do lugar.
Nova results.postalAddress Objeto PostalAddress estruturado.

Reproduzir filtros de componentes da v3

A geocodificação direta na versão 3 incluía um parâmetro components que permitia a filtragem rígida de resultados para componentes específicos (por exemplo, components=country:US). A geocodificação direta na versão 4 não oferece suporte a esse tipo de filtragem rígida nos parâmetros de solicitação. Na v4, é possível fornecer informações de endereço como uma única string não estruturada no caminho ou como parâmetros de consulta estruturados, como address.addressLines, address.locality, address.administrativeArea, address.postalCode e address.regionCode. Os parâmetros estruturados não funcionam como filtros rígidos. Em vez disso, todos os componentes fornecidos são combinados para formar o endereço completo que a API vai tentar geocodificar. A API procura a melhor correspondência para o endereço completo especificado em todos os componentes. Fornecer componentes de maneira estruturada pode ajudar a API a entender melhor a intent, especialmente quando as informações de endereço são coletadas de campos de formulário separados. Isso pode ajudar a API a lidar com pequenos erros de digitação ou ambiguidades em cada componente, já que o tipo de informação em cada campo é claro.

Para conseguir um comportamento semelhante aos filtros de componentes rígidos da v3, é necessário implementar a filtragem do lado do cliente na matriz results retornada pela API v4 com base nos objetos postalAddress e addressComponents na resposta.

A tabela a seguir mostra a melhor correspondência entre os filtros components da v3 e os campos postalAddress da v4:

filtro de componente v3 Campo de resposta da v4
country postalAddress.regionCode
postal_code postalAddress.postalCode
administrative_area postalAddress.administrativeArea ou addressComponents

Exemplos de filtragem do lado do cliente

Os exemplos a seguir mostram como filtrar resultados para alcançar um comportamento semelhante à filtragem de componentes da v3 para campos compatíveis: país, área político-administrativa e código postal. Não é recomendável filtrar outros campos, porque não há critérios de filtragem confiáveis.

Filtrar por país

Combine o address.regionCode da sua solicitação com o postalAddress.regionCode em cada resultado. Embora a API aceite códigos de região em letras minúsculas na solicitação, ela sempre os retorna em letras maiúsculas na resposta. Portanto, é necessário normalizar a entrada para letras maiúsculas antes da comparação.

Exemplo de código 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')
Exemplo de código 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');
Filtrar por área político-administrativa

Combine o address.administrativeArea da sua solicitação com o postalAddress.administrativeArea em cada resultado. Assim como os códigos de país, é preciso normalizar a entrada para letras maiúsculas antes da comparação. Isso só é confiável quando o administrativeArea na sua solicitação é fornecido com uma abreviação padrão de duas letras. O postalAddress.administrativeArea na resposta pode não corresponder diretamente aos sufixos de código ISO 3166-2 por vários motivos. Primeiro, em algumas áreas, a subdivisão correspondente ao código ISO 3166-2 não faz parte da representação padrão de endereços postais. Por exemplo, na Espanha, a área político-administrativa de nível 1 (por exemplo, "CN" para Canárias) pode estar presente em addressComponents, mas postalAddress.administrativeArea pode conter a área de nível 2 (por exemplo, "Santa Cruz de Tenerife"). Em segundo lugar, em algumas áreas, a abreviação da subdivisão não é usada com frequência e é representada em postalAddress.administrativeArea com um nome mais longo. Por motivos semelhantes, o addressComponents.shortText do componente de endereço correspondente à subdivisão pode não corresponder ao sufixo do código ISO 3166-2 da subdivisão. Além disso, em alguns casos, a subdivisão pode ser representada no componente de endereço para administrative_area_level_n com n maior que 1. Para resultados mais abrangentes, verifique se há uma correspondência em postalAddress.administrativeArea e em qualquer addressComponents com um tipo administrative_area_level_n (por exemplo, administrative_area_level_1).

Exemplo de código 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')
Exemplo de código 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');
Filtrar por código postal

Combine o address.postalCode da sua solicitação com o postalAddress.postalCode em cada resultado. É necessário normalizar os códigos postais de entrada e de resultado antes da comparação. A lógica de normalização depende muito da região. Uma abordagem básica envolve remover espaços e hifens e converter para um padrão consistente.

Uma normalização mais avançada pode ser necessária para lidar com prefixos de código postal (por exemplo, "L2" no Reino Unido) ou sufixos (por exemplo, "+4" nos CEPs dos EUA). A lógica de normalização específica necessária varia de acordo com o país e os formatos dos códigos postais envolvidos. Talvez seja necessário adaptar as funções de normalização fornecidas ou implementar uma lógica mais sofisticada para as regiões que você está segmentando.

O exemplo fornecido lida com CEPs dos EUA e permite a correspondência na base de cinco dígitos, mesmo que o ZIP+4 seja fornecido ou retornado.

Exemplo de código Python (focado em CEPs dos EUA)
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')
Exemplo de código Node.js (focado em CEPs dos EUA)
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');

Migrar da geocodificação reversa da v3

Se você usa a geocodificação inversa da v3 para transformar coordenadas em endereços, migre para o método Geocodificação inversa de um local da v4, que aceita uma solicitação GET.

A API v4 muda nomes, estrutura e suporte para vários parâmetros. Recomendamos que você use uma máscara de campo para especificar os campos que quer que sejam retornados na resposta.

Mudanças nos parâmetros de solicitação

Parâmetro v3 Parâmetro v4 Observações
language languageCode Renomeado. Quando omitido, a v3 usa o cabeçalho Accept-Language ou o padrão en, enquanto a v4 usa o idioma e o script locais do local.
region regionCode Renomeado. Quando omitido, a v3 usa us como padrão, enquanto a v4 não tem um valor padrão.
result_type types Renomeado. Usa parâmetros de consulta repetidos.
location_type granularity Renomeado. Usa parâmetros de consulta repetidos.
extra_computations Removido Substituído pelo método SearchDestinations.

Mudanças no campo de resposta

Campo v3 Campo da v4 Observações
status, error_message Removido A v4 usa códigos de status HTTP e corpos de erro.
results.address_components.long_name / results.address_components.short_name results.addressComponents.longText / results.addressComponents.shortText Renomeado.
results.geometry.location_type results.granularity Renomeado.
results.geometry.location results.location Nomes de campo: lat/lng -> latitude/longitude.
results.geometry.viewport results.viewport Nomes de campo: northeast/southwest -> high/low.
Nova results.addressComponents.languageCode Idioma do componente de endereço específico.
Nova results.bounds Limites explícitos usando high/low.
Nova results.place Nome do recurso do lugar.
Nova results.postalAddress Objeto PostalAddress estruturado.

Migrar dos descritores de endereço da v3

Na API Geocoding v3, transmitir extra_computations=ADDRESS_DESCRIPTORS retornava um objeto address_descriptor contendo landmarks[] e areas[]. Na API Geocoding v4, esse recurso é substituído pelo método SearchDestinations (POST /v4/geocode/destinations), que retorna um contexto espacial detalhado diretamente em cada objeto Destination.

A tabela a seguir mapeia os campos legados address_descriptor da v3 para os equivalentes SearchDestinations da v4:

Campo address_descriptor da v3 Campo SearchDestinations da v4 Notas de migração
landmarks[] destinations[].landmarks[] Solicite destinations.landmarks no seu X-Goog-FieldMask.
landmarks[].place_id landmarks[].place.placeId Encapsulado no objeto place (PlaceView).
landmarks[].display_name landmarks[].place.displayName Texto localizado (text e languageCode).
landmarks[].types landmarks[].place.types Matriz de strings de tipo de lugar.
landmarks[].spatial_relationship landmarks[].relationalDescription Em vez de um tipo enumerado bruto (ACROSS_THE_ROAD, AROUND_THE_CORNER), a v4 retorna uma frase localizada e legível por humanos (por exemplo, "Across from Central Park").
landmarks[].straight_line_distance_meters landmarks[].straightLineDistanceMeters Renomeado para lower camelCase.
landmarks[].travel_distance_meters landmarks[].travelDistanceMeters Renomeado para lower camelCase.
areas[] (tipo enumerado containment) destinations[].containingPlaces[] e subDestinations[] Equivalente mais próximo disponível para hierarquia estrutural: complexos, campi, shoppings e edifícios são retornados como objetos PlaceView estruturados em containingPlaces[] e subDestinations[] (com structureType definido como GROUNDS ou BUILDING e contornos displayPolygon opcionais). Não há uma substituição direta de 1:1 para áreas de proximidade de bairro/distrito ou o tipo enumerado containment da v3 (NEAR, WITHIN, OUTSKIRTS).

Migrar da geocodificação de lugares da v3

Se você usa place_id para receber o endereço de um ID de lugar específico com a Geocoding v3, migre para o método Place Geocoding da v4, que aceita uma solicitação GET.

A API v4 muda nomes, estrutura e suporte para vários parâmetros. Recomendamos que você use uma máscara de campo para especificar os campos que quer que sejam retornados na resposta.

Mudanças nos parâmetros de solicitação

Parâmetro v3 Parâmetro v4 Observações
place_id Campo place no proto de solicitação O ID de lugar agora é fornecido como um parâmetro de caminho places/{place}, por exemplo: https://geocode.googleapis.com/v4/geocode/places/ChIJj61dQgK6j4AR4GeTYWZsKWw. Isso é mapeado para o campo de lugar na solicitação.
language languageCode Renomeado. Quando omitido, a v3 usa o cabeçalho Accept-Language ou o padrão en, enquanto a v4 usa o idioma e o script locais do local.
region regionCode Renomeado. Quando omitido, a v3 usa us como padrão, enquanto a v4 não tem um valor padrão.

Mudanças no campo de resposta

Campo v3 Campo da v4 Observações
status, error_message Removido A v4 usa códigos de status HTTP e corpos de erro.
results (raiz) A v4 retorna um único objeto de resultado, não uma matriz results.
results.address_components.long_name / results.address_components.short_name addressComponents.longText / addressComponents.shortText Renomeado.
results.geometry.location_type granularity Renomeado.
results.geometry.location location Nomes de campo: lat/lng -> latitude/longitude.
results.geometry.viewport viewport Nomes de campo: northeast/southwest -> high/low.
results.postcode_localities postalCodeLocalities Renomeado. Agora é retornado para uma ou mais localidades (v3 requer >1).
Nova addressComponents.languageCode Idioma do componente de endereço específico.
Nova bounds Limites explícitos usando high/low.
Nova place Nome do recurso do lugar.
Nova postalAddress Objeto PostalAddress estruturado.

Migrar de dados hiperlocais de geocodificação para destinos

Os seguintes recursos da API Geocoding v3 estão sendo substituídos pelo método SearchDestinations da API Geocoding v4:

  • Entradas
  • Pontos de navegação
  • Contornos de edifícios
  • Acesso geral

Se você estava usando a API Geocoding v3 para os recursos acima, use este documento para saber como usar o método SearchDestinations para ter esses recursos. Neste documento, explicamos onde encontrar esses recursos na resposta de SearchDestinations e as diferenças na representação deles nas respostas da API Geocoding v3 e do método SearchDestinations da API Geocoding v4.

Entradas

Para ver as entradas associadas a um destination, use o campo destination.entrances.

Cada entrance em destination.entrances tem os seguintes campos (substituindo o esquema legado v3 buildings.entrances):

  • displayName: um novo campo opcional que contém um nome legível por humanos para a entrada, por exemplo, "Gate B".
  • location: um local do tipo LatLng usando latitude e longitude (substituindo lat/lng da v3).
  • tags: igual ao campo tags das entradas da API Geocoding v3 (como PREFERRED).
  • place: nome do recurso do lugar (por exemplo, places/ChIJ...), substituindo o campo building_place_id da API Geocoding v3. Na v4, esse lugar pode representar qualquer estrutura de destino (prédio, campus ou parque), não apenas um prédio.

Para ver os pontos de navegação associados a um destination, use o campo destination.navigationPoints.

Cada navigationPoint em destination.navigationPoints tem os seguintes campos (substituindo o esquema legado v3 navigation_points):

  • navigationPointToken: uma string de token criptografada usada para rotear até esse ponto de navegação específico no método ComputeRoutes da API Routes (Waypoint.navigationPointToken). Consulte Rotear usando tokens de ponto de navegação.
  • displayName: um novo campo opcional que contém um nome legível por humanos para o ponto de navegação, por exemplo, "5th Ave".
  • location: um local do tipo LatLng usando latitude e longitude.
  • travelModes: semelhante ao campo restricted_travel_modes da API Geocoding v3, mas a v4 especifica os meios de transporte aceitáveis (DRIVE, WALK) em vez dos restritos.
  • usage: um novo campo que especifica os casos de uso de chegada compatíveis com o ponto de navegação (como PICKUP, DROPOFF ou PARKING).

Contornos de edifícios

Para acessar os contornos de edifícios associados a um destination, use o campo displayPolygon dos objetos placeView no destination que representam edifícios (substituindo a matriz buildings.building_outlines legada da v3). Para cada placeView, verifique se é um edifício com o campo placeView.structureType. Se o tipo de estrutura for BUILDING, você poderá acessar o contorno no campo placeView.displayPolygon. O placeView também terá campos adicionais para o edifício que não estavam na API Geocoding v3.

Um destination pode ter um objeto placeView que representa um edifício nos seguintes campos:

  • destination.primary: o lugar principal do destino.
  • destination.containingPlaces: um campo repetido que contém lugares maiores que "contêm" o lugar principal. Por exemplo, se o lugar principal for um subpremise, containingPlaces geralmente vai conter o placeView que representa o edifício.
  • destination.subDestinations: um campo repetido que contém subdestinos do lugar principal (por exemplo, apartamentos ou suítes individuais em um prédio).

O formato de placeView.displayPolygon corresponde ao formato de contorno de edifício na API Geocoding v3, que é o formato GeoJSON, usando o formato RFC 7946.

Acesso geral

Assim como nos contornos de edifícios, para acessar os terrenos associados a um destination, use o campo displayPolygon dos objetos placeView no destination que representam terrenos (substituindo a matriz legada grounds da v3). Para cada placeView, verifique se é um polígono de terreno com o campo placeView.structureType. Se o tipo de estrutura for GROUNDS, você poderá acessar o contorno no campo placeView.displayPolygon. A placeView também terá campos adicionais para os motivos que não estavam na API Geocoding v3.

Um destination pode ter um objeto placeView que representa um terreno nos seguintes campos:

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

O formato de placeView.displayPolygon corresponde ao formato de contorno de terrenos na API Geocoding v3, que é o formato GeoJSON, usando o formato RFC 7946.

Precisão dos resultados

Na API Geocoding v3, o campo location_type na geometria de resposta indicava a precisão dos resultados, e alguns clientes classificavam ou filtravam os resultados com base nesses valores (ROOFTOP, RANGE_INTERPOLATED, GEOMETRIC_CENTER e APPROXIMATE). Nas migrações padrão da API Geocoding v4, esse campo é renomeado como granularity.

Na API Destinations (v4 SearchDestinations), não há o campo location_type. Em vez disso, as informações espaciais são processadas de maneira diferente:

  • Não é necessário filtrar manualmente no lado do cliente: enquanto a geocodificação padrão fornece vários resultados em diferentes granularidades, o método SearchDestinations minimiza a ambiguidade ao resolver um único destino otimizado sempre que possível. Isso elimina a necessidade de os clientes filtrarem por tipo de local para determinar qual resultado é o melhor.
  • Informações espaciais representadas por tipo de estrutura e polígonos de exibição: a geometria e a estrutura espaciais são indicadas por:
    • displayPolygon (para geometria exata).
    • O campo structureType no objeto placeView.
  • Mapeamento de tipo de estrutura:
    • Um structureType de POINT, BUILDING ou SECTION geralmente corresponde ao que era chamado de ROOFTOP anteriormente.
    • Um structureType de GROUNDS geralmente corresponde a GEOMETRIC_CENTER.

Usar uma máscara de campo para solicitar esses recursos

O método SearchDestinations requer uma máscara de campo, conforme explicado em Escolher campos para retornar. A máscara de campo pode ser definida como * para retornar todos os campos ou como os campos específicos que você quer receber. Por exemplo, a solicitação de API a seguir define a máscara de campo para receber todos os campos necessários para acessar as entradas, os pontos de navegação, os contornos de edifícios e os terrenos de um destino:

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

Receitas de migração e padrões de código comuns

Ao migrar fluxos de trabalho complexos da Geocoding v3 ou Geocoding++ para SearchDestinations, use as seguintes receitas de código para extrair estruturas comuns de espaço e endereço.

Extrair o melhor polígono de construção

Se você quiser renderizar a planta de um local ou destino:

  1. Verifique se o lugar primary tem structureType === 'BUILDING' e contém um displayPolygon.
  2. Caso contrário, inspecione containingPlaces para encontrar o edifício que o contém (útil quando o destino principal é um subdestino de um edifício maior).
  3. Se nenhum prédio for encontrado, verifique subDestinations para ver se os prédios individuais estão listados.
Exemplo de código 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
Exemplo de código 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;
}

Extrair contorno do solo

Se você precisar do limite de um condomínio, campus corporativo, shopping center ou parque:

  1. Se o lugar primary representa todo o complexo (structureType === 'GROUNDS'), use primary.displayPolygon.
  2. Se o lugar primary for um edifício ou uma entrada localizada dentro de um complexo maior, encontre os terrenos ao redor em containingPlaces.
Exemplo de código 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
Exemplo de código 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;
}

Selecionar pontos de referência para exibição de endereço em comparação com a chegada na última etapa

A Geocoding v3 address_descriptor retornou um tipo enumerado spatial_relationship (WITHIN, BESIDE, BEHIND, NEAR, ACROSS_THE_ROAD, AROUND_THE_CORNER). Em SearchDestinations:

  • Não há um tipo enumerado de relacionamento bruto. Em vez disso, use relationalDescription.text diretamente. Ele contém frases localizadas e prontas para exibição, como "Across from Central Park" ou "Behind the convention center". Não inclua "Near " manualmente.
  • Use tags para selecionar o ponto de referência certo para seu caso de uso:
    • ARRIVAL: pontos de referência hiperlocais situados imediatamente ao lado, em frente ou do outro lado da rua do destino (normalmente entre 50 e 100 metros). Ideal para chegada de motoristas em tempo real, confirmação de entrega por transportadora ou cartões de chegada no app.
    • ADDRESS: pontos de referência locais no bairro ao redor (normalmente entre 100 e 500 metros). Ideal para finalização de compra, revisão de pedidos ou desambiguação de destino antes da viagem.
    • Se um ponto de referência tiver as tags ADDRESS e ARRIVAL, ele será conhecido no bairro e estará imediatamente ao lado do destino.
Exemplo de código 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
Exemplo de código 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;
}

Traçar rotas para pontos de navegação de veículos e entradas preferenciais

Os fluxos de trabalho de envio e chegada de última etapa combinam dois tipos complementares de pontos de acesso espacial:

  • Navegação guiada: direcione os veículos para pontos de desembarque designados usando o navigationPoints. Os pontos de navegação são pré-classificados por proximidade e relevância. Assim, o primeiro candidato com o modo de viagem desejado (como 'DRIVE') e a tag de uso (como 'DROPOFF' ou 'PICKUP') fornece o waypoint ideal do veículo. Envie o navigationPointToken para o método ComputeRoutes (Waypoint.navigationPointToken) da API Routes ou para o SDK Navigation. Se não houver um ponto de trajeto de carro ajustado à via, use a entrada do edifício ou o centroide principal.
  • Acesso físico à porta: use destinations.entrances para orientar o entregador a pé ou o passageiro até a entrada do prédio. Verifique tags para 'PREFERRED' para encontrar entradas que provavelmente oferecem acesso físico direto. Se não houver dados de entrada disponíveis, use um ponto de navegação para pedestres (travelModes, incluindo 'WALK') ou o centroide principal.
Exemplo de código 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,
    }
Exemplo de código 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,
    };
}

Considerações sobre segurança

A API Geocoding v4 foi projetada como uma API de servidor para servidor. Não há um caminho de migração direto para usuários de JavaScript da v3 para a v4. Chamar os métodos da v4 diretamente do JavaScript do lado do cliente (por exemplo, em um navegador) usando uma chave de API expõe essa chave a um alto risco de roubo e uso indevido.

As restrições de referenciador HTTP, embora úteis, não são proteção suficiente para endpoints de serviço da Web, já que podem ser facilmente contornadas por invasores que falsificam o cabeçalho Referer nas solicitações.

A maneira recomendada de usar a API Geocoding v4 é pelo seu próprio servidor de back-end. Seu aplicativo cliente deve fazer solicitações para esse servidor intermediário, que chama a API do Google de forma segura usando uma chave de API protegida (por exemplo, uma chave armazenada em uma variável de ambiente ou em um gerenciador de secrets). Isso garante que sua chave de API nunca seja exposta no código de front-end.

Alternativas para necessidades do lado do cliente

Se você tiver necessidades do lado do cliente que exijam geocodificação, use uma das soluções disponíveis: