Migra de Geocoding v3 a v4

Desarrolladores del Espacio Económico Europeo (EEE)

La versión 4 de la API de Geocoding presenta varios métodos nuevos que reemplazan la funcionalidad de la versión 3 de la API. En esta guía, se muestra cómo migrar tu app para usar los nuevos métodos de la versión 4.

Puedes usar tus claves de API existentes con los nuevos métodos de la versión 4. Sin embargo, si solicitaste un aumento de cuota en la versión 3 de la API, debes solicitar un aumento en las nuevas APIs de la versión 4.

Migra desde la geocodificación directa de la versión 3

Si usas Geocoding v3 para geocodificar direcciones, debes migrar al método v4 Geocode an address, que acepta una solicitud GET.

La API v4 cambia los nombres, la estructura y la compatibilidad de varios parámetros. Te recomendamos que uses una máscara de campo para especificar los campos que deseas que se muestren en la respuesta.

Cambios en los parámetros de solicitud

Parámetro de v3 Parámetro de v4 Notas
address, components address La dirección no estructurada (v3 address) ahora se pasa en la ruta de URL. Los componentes de dirección estructurados (components de la versión 3) se pueden pasar como parámetros de consulta address.*. Consulta Reproduce los filtros de componentes de la versión 3.
bounds locationBias.rectangle Se cambió el nombre y la estructura cambió a objeto.
language languageCode Se cambió el nombre. Cuando se omite, la versión 3 usa el encabezado Accept-Language o, de forma predeterminada, en, mientras que la versión 4 usa de forma predeterminada el idioma y la escritura locales de la ubicación.
region regionCode Se cambió el nombre. Cuando se omite, la versión 3 se establece de forma predeterminada en us, mientras que la versión 4 no tiene un valor predeterminado.
extra_computations Eliminado Se reemplazó por el método SearchDestinations.

Cambios en el campo de respuesta

Campo de v3 Campo v4 Notas
status, error_message Eliminado La versión 4 usa códigos de estado HTTP y cuerpos de error.
results.address_components.long_name/results.address_components.short_name results.addressComponents.longText/results.addressComponents.shortText Se cambió el nombre.
results.geometry.location_type results.granularity Se cambió el nombre.
results.geometry.location results.location Nombres de los campos: lat/lng -> latitude/longitude.
results.geometry.viewport results.viewport Nombres de los campos: northeast/southwest -> high/low.
results.postcode_localities results.postalCodeLocalities Se cambió el nombre. Ahora se devuelve para una o más localidades (la versión 3 requería más de 1).
results.partial_match Eliminado
Nuevo results.addressComponents.languageCode Es el idioma del componente de dirección específico.
Nuevo results.bounds Límites explícitos con high/low.
Nuevo results.place Es el nombre del recurso del lugar.
Nuevo results.postalAddress Objeto PostalAddress estructurado.

Reproduce los filtros de componentes de la versión 3

La geocodificación directa en Geocoding v3 incluía un parámetro components que habilitaba el filtrado estricto de resultados para componentes específicos (p.ej., components=country:US). La geocodificación directa en v4 no admite este tipo de filtrado estricto en los parámetros de la solicitud. En la versión 4, puedes proporcionar información de la dirección como una sola cadena no estructurada en la ruta de acceso o como parámetros de consulta estructurados, como address.addressLines, address.locality, address.administrativeArea, address.postalCode y address.regionCode. Los parámetros estructurados no actúan como filtros estrictos. En cambio, todos los componentes proporcionados se combinan para formar la dirección completa que la API intentará geocodificar. La API busca la mejor coincidencia para la dirección completa especificada en todos los componentes. Proporcionar componentes de forma estructurada puede ayudar a la API a comprender mejor la intención, en especial cuando la información de la dirección se recopila de campos de formulario separados. Esto puede ayudar a la API a controlar errores tipográficos o ambigüedades menores dentro de cada componente, ya que el tipo de información en cada campo es claro.

Para lograr un comportamiento similar al de los filtros de componentes estrictos de la versión 3, debes implementar el filtrado del cliente en el array results que muestra la API de la versión 4 según los objetos postalAddress y addressComponents de la respuesta.

En la siguiente tabla, se muestra la mejor coincidencia entre los filtros components de la versión 3 y los campos postalAddress de la versión 4:

Filtro de componentes de la versión 3 Campo de respuesta de v4
country postalAddress.regionCode
postal_code postalAddress.postalCode
administrative_area postalAddress.administrativeArea o addressComponents

Ejemplos de filtrado del cliente

En los siguientes ejemplos, se muestra cómo filtrar los resultados para lograr un comportamiento similar al filtrado de componentes de la versión 3 para los campos admitidos: país, área administrativa y código postal. No se recomienda filtrar por otros campos, ya que no existen criterios de filtrado confiables.

Filtrar por país

Haz coincidir el address.regionCode de tu solicitud con el postalAddress.regionCode de cada resultado. Ten en cuenta que, si bien la API acepta códigos de región en minúsculas en la solicitud, siempre los devuelve en mayúsculas en la respuesta, por lo que debes normalizar la entrada a mayúsculas antes de la comparación.

Ejemplo de código de 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')
Ejemplo de código de 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 administrativa

Haz coincidir el address.administrativeArea de tu solicitud con el postalAddress.administrativeArea de cada resultado. Al igual que con los códigos de país, debes normalizar la entrada a mayúsculas antes de la comparación. Esto solo es confiable cuando el administrativeArea de tu solicitud se proporciona con una abreviatura estándar de 2 letras. Es posible que el postalAddress.administrativeArea de la respuesta no coincida directamente con los sufijos de código ISO 3166-2 por varios motivos. En primer lugar, en algunas áreas, la subdivisión correspondiente al código ISO 3166-2 no forma parte de la representación estándar de las direcciones postales. Por ejemplo, en España, el nivel 1 del área administrativa (p.ej., "CN" para Canarias) podría estar presente en addressComponents, pero postalAddress.administrativeArea podría contener el área de nivel 2 (p.ej., "Santa Cruz de Tenerife"). En segundo lugar, en algunas áreas, la abreviatura de la subdivisión no se usa con frecuencia y se representa en postalAddress.administrativeArea con un nombre más largo (por motivos similares, es posible que addressComponents.shortText para el componente de dirección correspondiente a la subdivisión no coincida con el sufijo del código ISO 3166-2 de la subdivisión). Además, en algunos casos, la subdivisión se puede representar en el componente de dirección para administrative_area_level_n con n mayor que 1. Para obtener los resultados más completos, debes verificar si hay coincidencias tanto en postalAddress.administrativeArea como en cualquier addressComponents con un tipo administrative_area_level_n (p.ej., administrative_area_level_1).

Ejemplo de código de 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')
Ejemplo de código de 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

Haz coincidir el address.postalCode de tu solicitud con el postalAddress.postalCode de cada resultado. Debes normalizar los códigos postales de entrada y de resultado antes de la comparación. La lógica de normalización depende en gran medida de la región. Un enfoque básico implica quitar los espacios y los guiones, y convertir el texto a un uso de mayúsculas y minúsculas coherente.

Es posible que se necesite una normalización más avanzada para controlar los prefijos de los códigos postales (p. ej., "L2" en el Reino Unido) o los sufijos (p. ej., "+4" en los códigos postales de EE.UU.). La lógica de normalización específica requerida variará según el país y los formatos de los códigos postales involucrados. Es posible que debas adaptar las funciones de normalización proporcionadas o implementar una lógica más sofisticada para las regiones a las que te orientas.

El ejemplo proporcionado controla los códigos postales de EE.UU. y permite la coincidencia en la base de 5 dígitos, incluso si se proporciona o se devuelve el código ZIP+4.

Ejemplo de código de Python (enfocado en códigos postales de EE.UU.)
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')
Ejemplo de código de Node.js (enfocado en el código postal de EE.UU.)
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');

Migra desde la geocodificación inversa de la versión 3

Si usas la geocodificación inversa de la versión 3 para convertir coordenadas en direcciones, debes migrar al método Geocodificación inversa de una ubicación de la versión 4, que acepta una solicitud GET.

La API v4 cambia los nombres, la estructura y la compatibilidad de varios parámetros. Te recomendamos que uses una máscara de campo para especificar los campos que deseas que se muestren en la respuesta.

Cambios en los parámetros de solicitud

Parámetro de v3 Parámetro de v4 Notas
language languageCode Se cambió el nombre. Cuando se omite, la versión 3 usa el encabezado Accept-Language o, de forma predeterminada, en, mientras que la versión 4 usa de forma predeterminada el idioma y la escritura locales de la ubicación.
region regionCode Se cambió el nombre. Cuando se omite, la versión 3 se establece de forma predeterminada en us, mientras que la versión 4 no tiene un valor predeterminado.
result_type types Se cambió el nombre y se usan parámetros de consulta repetidos.
location_type granularity Se cambió el nombre y se usan parámetros de consulta repetidos.
extra_computations Eliminado Se reemplazó por el método SearchDestinations.

Cambios en el campo de respuesta

Campo de v3 Campo v4 Notas
status, error_message Eliminado La versión 4 usa códigos de estado HTTP y cuerpos de error.
results.address_components.long_name/results.address_components.short_name results.addressComponents.longText/results.addressComponents.shortText Se cambió el nombre.
results.geometry.location_type results.granularity Se cambió el nombre.
results.geometry.location results.location Nombres de los campos: lat/lng -> latitude/longitude.
results.geometry.viewport results.viewport Nombres de los campos: northeast/southwest -> high/low.
Nuevo results.addressComponents.languageCode Es el idioma del componente de dirección específico.
Nuevo results.bounds Límites explícitos con high/low.
Nuevo results.place Es el nombre del recurso del lugar.
Nuevo results.postalAddress Objeto PostalAddress estructurado.

Migra desde los descriptores de direcciones de la versión 3

En la versión 3 de la API de Geocoding, pasar extra_computations=ADDRESS_DESCRIPTORS devolvía un objeto address_descriptor que contenía landmarks[] y areas[]. En la versión 4 de la API de Geocoding, esta función se reemplaza por el método SearchDestinations (POST /v4/geocode/destinations), que devuelve un contexto espacial enriquecido directamente dentro de cada objeto Destination.

En la siguiente tabla, se asignan los campos heredados de la versión 3 de address_descriptor a sus equivalentes de la versión 4 de SearchDestinations:

Campo address_descriptor de v3 Campo SearchDestinations de la v4 Notas de migración
landmarks[] destinations[].landmarks[] Solicita destinations.landmarks en tu X-Goog-FieldMask.
landmarks[].place_id landmarks[].place.placeId Se une dentro del objeto place (PlaceView).
landmarks[].display_name landmarks[].place.displayName Texto localizado (text y languageCode)
landmarks[].types landmarks[].place.types Es un array de cadenas de tipos de lugares.
landmarks[].spatial_relationship landmarks[].relationalDescription En lugar de un enum sin procesar (ACROSS_THE_ROAD, AROUND_THE_CORNER), la versión 4 devuelve una frase localizada y legible por humanos (p.ej., "Across from Central Park").
landmarks[].straight_line_distance_meters landmarks[].straightLineDistanceMeters Se cambió el nombre a lowerCamelCase.
landmarks[].travel_distance_meters landmarks[].travelDistanceMeters Se cambió el nombre a lowerCamelCase.
areas[] (enum containment) destinations[].containingPlaces[] y subDestinations[] Equivalente disponible más cercano para la jerarquía estructural: los complejos, campus, centros comerciales y edificios circundantes se devuelven como objetos PlaceView estructurados en containingPlaces[] y subDestinations[] (con structureType establecido en GROUNDS o BUILDING y contornos displayPolygon opcionales). Ten en cuenta que no hay un reemplazo directo 1:1 para las áreas de proximidad de vecindarios o distritos, ni para la enumeración containment de la versión 3 (NEAR, WITHIN, OUTSKIRTS).

Migra desde la geocodificación de Place de la versión 3

Si usas place_id para obtener la dirección de un ID de lugar específico con Geocoding v3, debes migrar al método Place Geocoding de la versión v4, que acepta una solicitud GET.

La API v4 cambia los nombres, la estructura y la compatibilidad de varios parámetros. Te recomendamos que uses una máscara de campo para especificar los campos que deseas que se muestren en la respuesta.

Cambios en los parámetros de solicitud

Parámetro de v3 Parámetro de v4 Notas
place_id Campo place en el proto de solicitud Ahora, el ID de lugar se proporciona como un parámetro de ruta places/{place}, por ejemplo: https://geocode.googleapis.com/v4/geocode/places/ChIJj61dQgK6j4AR4GeTYWZsKWw. Esto se asigna al campo de lugar en la solicitud subyacente.
language languageCode Se cambió el nombre. Cuando se omite, la versión 3 usa el encabezado Accept-Language o, de forma predeterminada, en, mientras que la versión 4 usa de forma predeterminada el idioma y la escritura locales de la ubicación.
region regionCode Se cambió el nombre. Cuando se omite, la versión 3 se establece de forma predeterminada en us, mientras que la versión 4 no tiene un valor predeterminado.

Cambios en el campo de respuesta

Campo de v3 Campo v4 Notas
status, error_message Eliminado La versión 4 usa códigos de estado HTTP y cuerpos de error.
results (raíz) La versión 4 devuelve un solo objeto de resultado, no un array results.
results.address_components.long_name/results.address_components.short_name addressComponents.longText/addressComponents.shortText Se cambió el nombre.
results.geometry.location_type granularity Se cambió el nombre.
results.geometry.location location Nombres de los campos: lat/lng -> latitude/longitude.
results.geometry.viewport viewport Nombres de los campos: northeast/southwest -> high/low.
results.postcode_localities postalCodeLocalities Se cambió el nombre. Ahora se devuelve para una o más localidades (la versión 3 requería más de 1).
Nuevo addressComponents.languageCode Es el idioma del componente de dirección específico.
Nuevo bounds Límites explícitos con high/low.
Nuevo place Es el nombre del recurso del lugar.
Nuevo postalAddress Objeto PostalAddress estructurado.

Migra de Geocoding Hyperlocal Data a Destinations

Las siguientes funciones de la API de Geocoding v3 se reemplazarán por el método SearchDestinations de la API de Geocoding v4:

  • Entradas
  • Puntos de navegación
  • Contornos de edificios
  • Terrenos

Si usabas la API de Geocoding v3 para las funciones anteriores, consulta este documento para usar el método SearchDestinations en su lugar y obtener estas funciones. En este documento, se explica dónde encontrar estas funciones en la respuesta de SearchDestinations y las diferencias en cómo se representan estas funciones en las respuestas de la API entre la versión 3 de la API de Geocoding y el método SearchDestinations de la versión 4 de la API de Geocoding.

Entradas

Para obtener las entradas asociadas con un destination, usa el campo destination.entrances.

Cada entrance en destination.entrances tiene los siguientes campos (que reemplazan el esquema buildings.entrances heredado de la versión 3):

  • displayName: Es un nuevo campo opcional que contiene un nombre legible por humanos para la entrada, por ejemplo, "Gate B".
  • location: Es una ubicación de tipo LatLng que usa latitude y longitude (reemplaza a lat/lng de la versión 3).
  • tags: Es igual al campo tags de las entradas de la API de Geocoding v3 (como PREFERRED).
  • place: Es el nombre del recurso del lugar (por ejemplo, places/ChIJ...), que reemplaza el campo building_place_id de la API de Geocoding v3. En la versión 4, este lugar puede representar cualquier estructura de destino (edificio, campus o parque), no solo un edificio.

Para obtener los puntos de navegación asociados con un destination, usa el campo destination.navigationPoints.

Cada navigationPoint en destination.navigationPoints tiene los siguientes campos (que reemplazan el esquema navigation_points heredado de la versión 3):

  • navigationPointToken: Es una cadena de token encriptada que se usa para enrutar a este punto de navegación específico en el método ComputeRoutes de la API de Routes (Waypoint.navigationPointToken). Consulta Cómo trazar una ruta con tokens de puntos de navegación.
  • displayName: Es un campo opcional nuevo que contiene un nombre legible por humanos para el punto de navegación, por ejemplo, "5th Ave".
  • location: Es una ubicación de tipo LatLng que usa latitude y longitude.
  • travelModes: Es similar al campo restricted_travel_modes de la API de Geocoding v3, excepto que la v4 especifica los modos de viaje aceptables (DRIVE, WALK) en lugar de los modos de viaje restringidos.
  • usage: Es un campo nuevo que especifica los casos de uso de llegada admitidos para el punto de navegación (como PICKUP, DROPOFF o PARKING).

Contornos de edificios

Para obtener los contornos de los edificios asociados con un destination, usa el campo displayPolygon de los objetos placeView en el destination que representan edificios (reemplaza el array buildings.building_outlines heredado de la versión 3). Para cada placeView, verifica si es un edificio con el campo placeView.structureType. Si el tipo de estructura es BUILDING, puedes obtener el contorno del campo placeView.displayPolygon. La placeView también tendrá campos adicionales para el edificio que no estaban en la API de Geocoding v3.

Un destination puede tener un objeto placeView que represente un edificio en los siguientes campos:

  • destination.primary: Es el lugar principal del destino.
  • destination.containingPlaces: Es un campo repetido que contiene lugares más grandes que "contienen" el lugar principal. Por ejemplo, si el lugar principal es un subpremise, containingPlaces suele contener el placeView que representa el edificio.
  • destination.subDestinations: Es un campo repetido que contiene subdestinos del lugar principal (por ejemplo, unidades de departamentos o suites individuales dentro de un edificio).

Ten en cuenta que el formato de placeView.displayPolygon coincide con el formato del contorno del edificio en la API de Geocoding v3, que es el formato GeoJSON, con el formato RFC 7946.

Terrenos

Al igual que con los contornos de edificios, para obtener los terrenos asociados con un destination, usa el campo displayPolygon de los objetos placeView en el destination que representan terrenos (reemplazando el array grounds heredado de la versión 3). Para cada placeView, verifica si es un polígono de terreno con el campo placeView.structureType. Si el tipo de estructura es GROUNDS, puedes obtener el contorno del campo placeView.displayPolygon. El placeView también tendrá campos adicionales para los terrenos que no estaban en la API de Geocoding v3.

Un destination puede tener un objeto placeView que represente un terreno en los siguientes campos:

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

Ten en cuenta que el formato de placeView.displayPolygon coincide con el formato de contorno de los terrenos en la API de Geocoding v3, que es el formato GeoJSON, con el formato RFC 7946.

Precisión de los resultados

En la API de Geocoding v3, el campo location_type de la geometría de respuesta indicaba la precisión de los resultados, y algunos clientes clasificaban o filtraban los resultados en función de estos valores (ROOFTOP, RANGE_INTERPOLATED, GEOMETRIC_CENTER y APPROXIMATE). En las migraciones estándar de la API de Geocoding v4, este campo se cambia a granularity.

En la API de Destinations (v4 SearchDestinations), no hay un campo location_type. En cambio, la información espacial se maneja de otra manera:

  • No se necesita un filtrado manual del cliente: Si bien la geocodificación estándar proporciona varios resultados con diferentes niveles de detalle, el método SearchDestinations minimiza la ambigüedad, ya que resuelve un solo destino optimizado siempre que sea posible. Esto elimina la necesidad de que los clientes filtren por tipo de ubicación para determinar qué resultado es el mejor.
  • Información espacial representada por el tipo de estructura y los polígonos de visualización: La geometría y la estructura espaciales se indican de la siguiente manera:
    • El displayPolygon (para la geometría exacta).
    • El campo structureType dentro del objeto placeView.
  • Asignación de tipos de estructura:
    • Un structureType de POINT, BUILDING o SECTION suele corresponder a lo que antes se llamaba ROOFTOP.
    • Un structureType de GROUNDS suele corresponder a GEOMETRIC_CENTER.

Usa una máscara de campo para solicitar estas funciones

El método SearchDestinations requiere una máscara de campo, como se explica en Elige los campos que se mostrarán. La máscara de campo se puede configurar en * para que se muestren todos los campos, o puedes configurarla para que se muestren los campos específicos que deseas recibir. Por ejemplo, la siguiente solicitud a la API establece la máscara de campo para recibir todos los campos necesarios para obtener las entradas, los puntos de navegación, los contornos de los edificios y los terrenos de un 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

Recetas de migración y patrones de código comunes

Cuando migres flujos de trabajo complejos de Geocoding v3 o Geocoding++ a SearchDestinations, usa las siguientes recetas de código para extraer estructuras comunes de direcciones y datos espaciales.

Extrae el mejor polígono de edificio

Si deseas renderizar el perímetro de un edificio para una ubicación o un destino, sigue estos pasos:

  1. Verifica si el lugar primary tiene structureType === 'BUILDING' y contiene un displayPolygon.
  2. De lo contrario, inspecciona containingPlaces para encontrar el edificio que lo contiene (útil cuando el destino principal es un subdestino de un edificio más grande).
  3. Si no se encuentra ningún edificio que lo contenga, consulta subDestinations para ver si se enumeran edificios individuales.
Ejemplo de código de 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
Ejemplo de código de 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;
}

Extrae el contorno de los terrenos

Si necesitas el límite de un complejo de departamentos, un campus corporativo, un centro comercial o un parque, sigue estos pasos:

  1. Si el lugar primary representa todo el complejo (structureType === 'GROUNDS'), usa primary.displayPolygon.
  2. Si el lugar primary es un edificio o una entrada que se encuentra dentro de un complejo más grande, busca los terrenos circundantes en containingPlaces.
Ejemplo de código de 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
Ejemplo de código de 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;
}

Selecciona puntos de referencia para mostrar la dirección en comparación con la llegada de última milla

La versión 3 de Geocoding address_descriptor devolvió una enumeración spatial_relationship (WITHIN, BESIDE, BEHIND, NEAR, ACROSS_THE_ROAD, AROUND_THE_CORNER). En SearchDestinations:

  • No hay un enum de relación sin procesar. En su lugar, usa relationalDescription.text directamente. Contiene frases localizadas y listas para mostrar, como "Across from Central Park" o "Behind the convention center". No antepongas "Near " de forma manual.
  • Usa tags para seleccionar el punto de referencia adecuado para tu caso de uso:
    • ARRIVAL: Son puntos de referencia hiperlocales situados inmediatamente junto al destino, frente a él o al otro lado de la calle (por lo general, a una distancia de entre 50 y 100 metros). Es ideal para la llegada de conductores en tiempo real, la confirmación de entrega de mensajería o las tarjetas de llegada en la app.
    • ADDRESS: Son puntos de referencia destacados a nivel local dentro del vecindario circundante (por lo general, entre 100 y 500 metros). Es ideal para la confirmación de la compra de cara al cliente, la revisión de pedidos o la desambiguación del destino antes del viaje.
    • Si un punto de referencia tiene las etiquetas ADDRESS y ARRIVAL, significa que es conocido en el vecindario y que está inmediatamente adyacente al destino.
Ejemplo de código de 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
Ejemplo de código de 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;
}

Cómo trazar rutas hacia puntos de navegación de vehículos y entradas preferidas

Los flujos de trabajo de despacho y llegada de última milla combinan dos tipos complementarios de puntos de acceso espacial:

  • Navegación paso a paso en auto: Dirige los vehículos a los puntos de llegada designados en la ruta con navigationPoints. Los puntos de navegación se clasifican previamente por proximidad y relevancia, por lo que el primer candidato con el modo de viaje deseado (como 'DRIVE') y la etiqueta de uso (como 'DROPOFF' o 'PICKUP') proporciona el punto de referencia óptimo para el vehículo. Envía su navigationPointToken al método ComputeRoutes (Waypoint.navigationPointToken) de la API de Routes o al SDK de Navigation. Si no existe un punto de conducción ajustado a la ruta, recurre a la entrada del edificio o al centroide principal.
  • Acceso físico a la puerta: Guía al mensajero a pie o al pasajero hasta la entrada del edificio con destinations.entrances. Consulta tags para 'PREFERRED' y encuentra entradas que probablemente proporcionen acceso físico directo. Si no hay datos de entrada disponibles, recurre a un punto de navegación peatonal (travelModes, incluido 'WALK') o al centroide principal.
Ejemplo de código de 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,
    }
Ejemplo de código de 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,
    };
}

Consideraciones de seguridad

La API de Geocoding v4 está diseñada como una API de servidor a servidor. No hay una ruta de migración directa para los usuarios de JavaScript de la versión 3 a la 4. Llamar a los métodos de la versión 4 directamente desde JavaScript del cliente (por ejemplo, en un navegador) con una clave de API expone tu clave de API a un alto riesgo de robo y uso inadecuado.

Las restricciones de URLs de referencia HTTP, si bien son útiles, no son una protección suficiente para los extremos de servicios web, ya que los atacantes pueden omitirlas fácilmente falsificando el encabezado Referer en sus solicitudes.

La forma recomendada de usar la API de Geocoding v4 es desde tu propio servidor de backend. Tu aplicación cliente debe realizar solicitudes a este servidor intermediario, que, luego, llama de forma segura a la API de Google con una clave de API protegida (por ejemplo, una clave almacenada en una variable de entorno o un Secret Manager). Esto garantiza que tu clave de API nunca se exponga en el código de frontend.

Alternativas para las necesidades del cliente

Si tienes necesidades del cliente que requieren geocodificación, considera usar una de las soluciones existentes del cliente: