Blog de Amazon Web Services (AWS)

Accede a Amazon Location Service desde Amazon Aurora

Las organizaciones suelen almacenar datos de negocio y de clientes en bases de datos como Amazon Relational Database Service (Amazon RDS) y Amazon Redshift, y a menudo quieren enriquecer estos datos integrándose con servicios externos. Uno de estos enriquecimientos consiste en añadir atributos espaciales, como las coordenadas de una dirección. Con el lanzamiento de Amazon Location Service, ahora tienes acceso a funcionalidades geoespaciales como visualización de mapas, geocodificación (geocoding) y geocodificación inversa (reverse geocoding), utilizando proveedores de datos como Esri y HERE.

Además, con la habilidad de invocar funciones de AWS Lambda desde bases de datos en Amazon Redshift y Amazon RDS a través de funciones definidas por el usuario (user-defined functions, UDFs) ahora puedes integrar estas bases de datos con funcionalidad geoespacial proporcionada por Amazon Location Service. Para más información acerca de las funciones definidas por el usuario en Amazon Aurora, echa un vistazo a Invoking an AWS Lambda function from an Aurora PostgreSQL DB cluster.

En este artículo tomaremos direcciones de cliente almacenadas en una base de datos de la versión compatible con PostgreSQL de Amazon Aurora y llamaremos a las APIs de geocodificación de Amazon Location service para encontrar las coordenadas de dichas direcciones y persistirlas en la base de datos. Si utilizas Amazon Redshift, echa un vistazo a Access Amazon Location Service from Amazon Redshift.

Requisitos previos

Antes de empezar, asegúrate de que cumples con los siguientes requisitos:

  • Conoces conceptos básicos como roles y políticas de AWS Identity and Access Management (IAM), funciones Lambda y Aurora PostgreSQL.
  • Tienes un cluster de Aurora PostgreSQL con una tabla que contiene direcciones de cliente y los siguientes campos: nombre de la calle, número de la calle, tipo de calle, y código de país. También necesitas una columna para almacenar las coordenadas de la dirección.
  • Una herramienta de desarrollo SQL de tu elección para conectarte al cluster de Aurora PostgreSQL.
  • Una cuenta de Amazon QuickSight con acceso a Aurora.

Resumen de la solución

Nuestra solución está formada por los siguientes componentes:

  • Una función Lambda escrita en Python que invoca al método search_place_index_for_text. Este método toma un input de texto y devuelve las coordenadas en latitud y longitud para cada resultado de búsqueda.
  • Un rol de IAM para permitir a Lambda llamar a la operación SearchPlaceIndexForText de Amazon Location Service.
  • Una UDF en la base de datos Aurora PostgreSQL para invocar a la función Lambda.
  • Un rol de IAM que permite a Aurora PostgreSQL invocar a la función Lambda.
  • Sentencias SQL para actualizar y obtener las coordenadas de los registros dentro de la base de datos Aurora PostgreSQL mediante la UDF.
  • Un dataset de QuickSight que utiliza las sentencias SQL para acceder a las coordenadas.
  • Un análisis de QuickSight que muestra la ubicación de las direcciones en un gráfico geoespacial.

El siguiente diagrama muestra la arquitectura de la solución:

Arquitectura de la solución

Para implementar la solución, debemos completar los siguientes pasos:

  1. Configurar un índice de ubicaciones (place index) en Amazon Location Service.
  2. Crear una función común de geocodificación de direcciones.
  3. Llamar a la API de Amazon Location Service desde Aurora PostgreSQL.
  4. Crear una función Lambda.
  5. Configurar Aurora PostgreSQL.
  6. Ejecutar sentencias SQL para llamar a la función Lambda.
  7. Visualizar la ubicación de las direcciones en QuickSight.

Presta atención a los nombres y parámetros usados en este artículo, ya que deben coincidir y ser consistentes a lo largo de todos los componentes de la solución.

El código completo está disponible en GitHub. El mismo también incluye una plantilla de AWS CloudFormation. Por favor, dirígete a la sección Apéndice de este artículo para desplegar la plantilla de CloudFormation.

Configurar un índice de ubicaciones en Amazon Location Service

Amazon Location Service utiliza un recurso llamado “índice de ubicaciones” (place index) para ofrecer la funcionalidad de geocodificación y geocodificación inversa. Comencemos creando un nuevo índice de ubicaciones. Puede que tu cuenta ya tenga un índice de ubicaciones por defecto, pero no lo usaremos en este artículo ya que no está configurado para almacenar los resultados.

  1. En la consola de Amazon Location Service, utiliza el panel de navegación para lanzar el asistente para crear un nuevo índice de ubicaciones.
  2. En Name, introduce placeindex.aurora.
  3. En Data provider, escoge cualquiera de los proveedores de datos.
  4. En Data storage options, selecciona Yes, results will be stored ya que vamos a almacenar los resultados de la geocodificación en un campo de la base de datos.
  5. Deja el resto de valores como están, y haz click en Create place index.

Crear place index

Usaremos el nombre del índice en el código de la función Lambda como un parámetro de la llamada a search_place_index_for_text.

Crear una función común de geocodificación de direcciones

A continuación, vamos a crear una función para llamar a la API de Amazon Location Service. Reutilizaremos el código de esta función tanto para Aurora como para Amazon Redshift, ya que la llamada subyacente al servicio es la misma.

Por cuestiones de brevedad, en el siguiente fragmento de código hemos omitido la gestión de errores. El código completo se encuentra disponible en GitHub.

Hacemos uso de country_code para limitar la operación search_place_index_for_text a un país en concreto haciendo uso de los códigos de país ISO 3166-1 alpha 3. En caso de recibir un input con un valor no válido, Amazon Location Service devolverá una excepción.

Crea un fichero nuevo llamado geocode.py con el siguiente fragmento de código:

import boto3
import os

location = boto3.client("location")

def geocode_address(address_line, municipality_name, state_code, post_code, country_code):

    text = ("%s, %s %s %s" % (address_line, municipality_name, state_code, post_code))
    response = location.search_place_index_for_text(IndexName="placeindex.aurora", FilterCountries=[country_code], Text=text)

    data = response["Results"]
    if len(data) >= 1:
        response = {
            "Longitude": data[0]["Place"]["Geometry"]["Point"][0],
            "Latitude": data[0]["Place"]["Geometry"]["Point"][1],
            "Label": data[0]["Place"]["Label"],
            "MultipleMatch": False
        }
        
        if len(data) > 1:
            response["MultipleMatch"] = True
    else:
        response = {
            "Error": "No geocoding results found"
        }
        
    return response

Llamar a la API de Amazon Location Service desde Amazon Aurora PostgreSQL

La base de datos Aurora PostgreSQL utiliza las extensiones de PostgreSQL aws_lambda y aws_common. Para obtener instrucciones sobre cómo instalarlas, echa un vistazo a Invoking an AWS Lambda function from an Aurora PostgreSQL DB cluster.

Cada llamada que Aurora PostgreSQL realiza a una Lambda lleva un documento JSON que representa los parámetros de la UDF. Aurora PostgreSQL envía un registro de la base de datos por cada llamada a la función.

{
"address_line": "410 TERRY AVE N",
"municipality_name": "SEATTLE",
"state_code": "WA",
"post_code": "98109",
"country_code": "USA"
}

La interfaz espera la respuesta como un documento JSON.

{
"Longitude":-122.33664798659117,
"Latitude":47.62231899780329,
"Label":"410 Terry Ave N, Seattle, WA, 98109, USA",
"MultipleMatch":false
}

En caso de excepción, la función puede devolver un mensaje de error.

{
"Exception": "Error in processing request"
}

Crear una función Lambda

Ahora crearemos una función Lambda llamada GeocodeAddress-Aurora utilizando el

runtime de Python.

Por cuestiones de brevedad, en el siguiente fragmento de código hemos omitido la gestión de errores. El código completo se encuentra disponible en GitHub.

  1. Crea el fichero geocode.py como se describe en la sección anterior. Es importante establecer la concurrencia de la función Lambda para permitir que múltiples peticiones desde Aurora PostgreSQL se ejecuten de manera concurrente.
  2. Sustituye el código generado por defecto en lambda_function.py con el siguiente:
    import logging
    from geocode import *
    
    def lambda_handler(event, context):
        try: 
            response = geocode_address(event["address_line"],
                                       event["municipality_name"],
                                       event["state_code"],
                                       event["post_code"],
                                       event["country_code"])
        except Exception as e:
            response = {
                "Exception": str(e)
            }
        
        return response
  3. Esta función Lambda necesita permiso para llamar a la API search_place_index_for_text y así poder geocodificar direcciones usando el índice de ubicaciones placeindex.aurora que creamos anteriormente.
  4. Actualiza el rol IAM de la función Lambda añadiendo la siguiente política en línea llamada GeocodeAddress-Aurora-policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "geo:SearchPlaceIndexForText"
            ],
            "Resource": "arn:aws:geo:::place-index/placeindex.aurora",
            "Effect": "Allow"
        }
    ]
}

Configurar Aurora PostgreSQL

Ahora crearemos una nueva UDF en Aurora PostgreSQL y la configuraremos para usar un rol de IAM que le dará permiso para invocar nuestra función Lambda. Para más información, echa un vistazo a Invoking an AWS Lambda function from an Aurora PostgreSQL DB cluster.

    1. Crear un rol IAM llamado Aurora-Lambda-role y añádele la siguiente política en línea Aurora-Lambda-policy para permitir a Aurora PostgreSQL que invoque la función GeocodeAddress-Aurora que hemos creado previamente:
      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": "lambda:InvokeFunction",
                  "Resource": [
                      "arn:aws:lambda:::function:GeocodeAddress-Aurora"
                  ]
              }
          ]
      }

      Necesitamos añadir este rol al cluster de Aurora PostgreSQL.

    2. En la consola de Aurora PostgreSQL, selecciona el cluster a utilizar.
    3. Bajo Manage IAM Roles, añade el rol Aurora-Lambda-role y escoge Lambda en Feature.
      Agregar el rol de IAM
    4. Espera a que el cluster aplique la modificación y vuelva al estado Available.

Ahora crearemos una UDF de Aurora PostgreSQL para invocar a la función Lambda.

    1. Utiliza tu editor SQL favorito para conectarte al cluster de Aurora PostgreSQL y crea una nueva UDF llamada f_geocode_address en Aurora PostgreSQL usando el siguiente código:
CREATE OR REPLACE FUNCTION f_geocode_address(p_address_line varchar(85),
                                             p_municipality_name varchar(60),
                                             p_state_code varchar(2),
                                             p_post_code varchar(10),
                                             p_country_code varchar(3))
  RETURNS character varying
  LANGUAGE plpgsql
AS $function$
    declare 
        result varchar(200);
    begin
        SELECT payload into result
        FROM aws_lambda.invoke(aws_commons.create_lambda_function_arn(
                                                          'GeocodeAddress-Aurora'),
         concat('{"address_line":"',p_address_line,'",
                  "municipality_name":"',p_municipality_name,'",
                  "state_code":"',p_state_code,'",
                  "post_code":"',p_post_code,'",
                  "country_code":"',p_country_code,'"}')::json);
                
        return result;
    end;
$function$

Esta UDF llama a la función usando los permisos proporcionados por el rol Aurora-Lambda-role que creamos anteriormente.

Ejecutar sentencias SQL para llamar a la función Lambda

Ya estamos listos para ejecutar sentencias SQL que tomarán direcciones de la tabla customer_address en la base de datos Aurora PostgreSQL y las geocodificarán usando Amazon Location Service.

Si no tienes una tabla customer_address, puedes crearla utilizando el script incluido en el código completo en GitHub.

Como parte de la sentencia SQL, puedes optar por simplemente obtener los resultados de la geocodificación, como se muestra en el siguiente código:

SELECT customer_id,
       f_geocode_address(address_line,
                         municipality_name,
                         state_code,
                         post_code,
                         country_code)
FROM customer_address
WHERE address_status = 'NEW'
  and country_code IS NOT NULL;

Los resultados de la geocodificación pueden almacenarse en un campo de la base de datos en Aurora PostgreSQL. Entonces podemos parsear el JSON almacenado para extraer las coordenadas geográficas.

UPDATE customer_address
SET geocode_result = f_geocode_address(address_line,
                                       municipality_name,
                                       state_code,
                                       post_code,
                                       country_code)
WHERE address_status = 'NEW'
  AND country_code IS NOT NULL;

Ahora extraigamos las coordenadas geográficas del campo con los resultados.

SELECT customer_id,
       cast(geocode_result->'Longitude' as float) as Longitude,
       cast(geocode_result->'Latitude' as float) as Latitude
FROM customer_address
WHERE address_status = 'NEW'
  and country_code IS NOT NULL;

Visualizar la ubicación de las direcciones usando QuickSight

Vamos a configurar un dataset en QuickSight y a crear un análisis para el mismo.

    1. Crear un nuevo origen de datos de Aurora llamado Aurora-Geocode y configúralo con los detalles del endpoint.

Crear un data source

    1. Crea un nuevo dataset usando el origen de datos anterior y la siguiente sentencia SELECT como SQL personalizado.

Crear la consulta SQL personalizada

Ahora estamos listos para configurar nuestro análisis.

    1. En la consola de QuickSight, crea un nuevo análisis de QuickSight usando el dataset de ubicaciones de direcciones.
    2. Escoge el tipo de visualización Point on map.

Tipos de visuales

    1. Escoge los campos longitude y latitude de la lista de campos y arrástralos al cuadro de campos Geospatial, bajo Field wells.

Field wells

Ahora deberías ver puntos en el mapa representando las direcciones de los clientes.

Conclusión

¡Enhorabuena! Hemos integrado con éxito Amazon Location Service y Amazon Aurora, y hemos geocodificado direcciones en una tabla de Aurora PostgreSQL sin salir de nuestro entorno SQL. Hemos enriquecido las direcciones añadiendo coordinadas de ubicación. También hemos visualizado con éxito las direcciones en un mapa en QuickSight. Ahora puedes probar a extender esta solución a otras funcionalidades de Amazon Location Service, como el geocoding inverso, o incluso integrarla con otras funciones Lambda para cualquier otra funcionalidad personalizada.

Apéndice

Para empezar, despliega el stack de CloudFormation en tu cuenta de AWS.

  1. En Stack name introduce un nombre para tu stack.
  2. En DB cluster introduce el identificador de tu cluster de Aurora PostgreSQL que contiene la tabla con las direcciones de cliente.
  3. Marca la casilla al lado de I acknowledge that AWS CloudFormation might create IAM resources with custom names, y haz click en Create stack.

Stack de CloudFormation

Este blogpost es una traducción por Carlos Afonso (AWS Startup Solutions Architect) del original en Inglés.

Parag Srivastava

Parag Srivastava

Parag Srivastava es Arquitecto de Soluciones en Amazon Web Services (AWS), que ayuda a los clientes enterprise a adoptar y migrar con éxito a la nube. Durante su carrera profesional, se ha involucrado ampliamente en proyectos complejos de transformación digital. También le apasiona la creación de soluciones innovadoras en torno a los aspectos geoespaciales de las direcciones.