Trabaja con datos geoespaciales en Bigtable
En esta página, se describe cómo almacenar y consultar datos geoespaciales en Bigtable con las funciones geográficas de GoogleSQL. Los datos geoespaciales incluyen representaciones geométricas de la superficie de la Tierra en forma de puntos, líneas y polígonos. Puedes agrupar estas entidades en colecciones para hacer un seguimiento de ubicaciones, rutas o cualquier otra área de interés.
Operaciones básicas
Las funciones geográficas de GoogleSQL dedicadas
permiten que Bigtable optimice el rendimiento de tu base de datos para los cálculos geoespaciales
y los tiempos de consulta. En los ejemplos de uso básico, usamos una tabla tourist_points_of_interest con una familia de columnas llamada poi_data.
| poi_data | |||
|---|---|---|---|
| clave de fila | nombre | ubicación | |
| p1 | Torre Eiffel | POINT(2.2945 48.8584) |
Escribe datos geográficos
Para escribir datos geoespaciales en columnas de Bigtable, debes proporcionar representaciones de cadenas de entidades geográficas en
los formatos GeoJSON o
texto conocido (WKT). Bigtable almacena los datos subyacentes como bytes sin procesar, por lo que puedes proporcionar
cualquier combinación de entidades en una sola columna (como Points,
LineStrings, Polygons, MultiPoints, MultiLineStrings, o MultiPolygons).
El tipo de entidad geoespacial que proporciones es importante en el momento de la consulta, ya que debes usar
la función geográfica adecuada para interpretar los datos.
Por ejemplo, para agregar algunas atracciones a la tabla tourist_points_of_interest,
puedes usar la herramienta de CLI cbt de la siguiente manera:
- Con WKT:
cbt set tourist_points_of_interest p2 poi_data:name='Louvre Museum' cbt set tourist_points_of_interest p2 poi_data:location='POINT(2.3376 48.8611)' cbt set tourist_points_of_interest p3 poi_data:name='Place Charles de Gaulle' cbt set tourist_points_of_interest p3 poi_data:location='POLYGON(2.2941 48.8733, 2.2941 48.8742, 2.2957 48.8742, 2.2958 48.8732, 2.2941 48.8733)'
- Con GeoJSON:
cbt set tourist_points_of_interest p2 poi_data:name='Louvre Museum' cbt set tourist_points_of_interest p2 poi_data:location='{"type": "Point", "coordinates": [2.3376, 48.8611]}' cbt set tourist_points_of_interest p3 poi_data:name='Place Charles de Gaulle' cbt set tourist_points_of_interest p3 poi_data:location='{"type": "Polygon", "coordinates": [[ [2.2941, 48.8733], [2.2941, 48.8742], [2.2957, 48.8742], [2.2958, 48.8732], [2.2941, 48.8733] ]] }'
Consulta datos geográficos
Puedes consultar la tabla tourist_points_of_interest con GoogleSQL en
Bigtable Studio y
filtrar los resultados con
funciones geográficas.
Por ejemplo, para encontrar todas las atracciones turísticas en un radio de 1,500 metros de Trocadéro,
puedes usar la
ST_DWITHIN
función:
SELECT _key, poi_data['name'], poi_data['location']
FROM points_of_interest
WHERE ST_DWITHIN(CAST(poi_data['location'] AS STRING), ST_GEOGPOINT(2.2874, 48.86322), 1500);
El resultado sería similar al siguiente:

La columna location muestra la representación textual original de los datos proporcionados en la operación de escritura, pero Bigtable la almacena como bytes.
En este ejemplo, aunque Place Charles de Gaulle se define como un polígono que se extiende más allá del límite de 1,500 metros, se incluye en los resultados de la consulta.
Esto se debe a que la función ST_DWITHIN muestra el valor verdadero si cualquier punto contenido en una entidad geoespacial está dentro de la distancia esperada.
Uso avanzado
En las siguientes secciones, se describe cómo usar funciones geográficas para situaciones complejas, como el análisis de geocercas. En la sección Optimización de consultas , se explica cómo usar vistas materializadas continuas para mejorar el rendimiento.
Para ilustrar los usos avanzados de las funciones geográficas en Bigtable, imagina una situación en la que eres un investigador que supervisa el comportamiento de los pingüinos emperadores que viven en el lado sur de la isla Snow Hill.
Tu equipo proporciona cientos de pings de ubicación para cada pingüino, por lo que creas la tabla penguin_movements para almacenarlos:
| penguin_details | |||
|---|---|---|---|
| clave de fila | penguin_id | timestamp | ubicación |
| ping#123 | pen_01 | 2025-12-06 08:15:22+00 | POINT(-57.51 -64.42) |
| ping#124 | pen_01 | 2025-12-06 10:22:05+00 | POINT(-57.55 -64.43) |
| ping#125 | pen_01 | 2025-12-07 12:35:45+00 | POINT(-57.58 -64.41) |
| ping#126 | pen_02 | 2025-12-12 06:05:11+00 | POINT(-57.49 -64.39) |
Ejemplo: Encuentra quién cruzó la geocerca
Durante la investigación, observas el comportamiento de los pingüinos alrededor de un área de alimentación específica. Las áreas definidas con límites virtuales se conocen comúnmente como geocercas. El área de alimentación se puede considerar un área con geocerca.
Quieres saber si algún pingüino visitó el área de alimentación el 3 de diciembre de 2025. Para responder la pregunta, usa la
ST_CONTAINS
función de la siguiente manera:
SELECT
penguin_details['penguin_id'],
penguin_details['location'],
penguin_details['timestamp']
FROM
penguin_movements
WHERE
penguin_details['timestamp'] >= '2025-12-03 00:00:00 UTC'
AND penguin_details['timestamp'] < '2025-12-04 00:00:00 UTC'
-- The feeding ground boundary is defined with a WKT POLYGON entity
-- Polygon's last point must be equal to its first point to close the loop
AND ST_CONTAINS(
ST_GEOGFROMTEXT('POLYGON((-57.21 -64.51, -57.23 -64.55, -57.08 -64.56, -57.06 -64.51, -57.21 -64.51))'),
ST_GEOGFROMTEXT(CAST(penguin_details['location'] AS STRING))
)
Obtendrás resultados similares a los siguientes:
+------------+----------------------+------------------------+
| penguin_id | location | timestamp |
+------------+----------------------+------------------------+
| pen_01 | POINT(-57.15 -64.53) | 2025-12-03 08:15:22+00 |
| pen_02 | POINT(-57.18 -64.54) | 2025-12-03 14:30:05+00 |
| pen_10 | POINT(-57.10 -64.52) | 2025-12-03 11:45:10+00 |
+------------+----------------------+------------------------+
Ejemplo: Transforma puntos en rutas
Para visualizar mejor cómo viajan los pingüinos, decides organizar los
pings de ubicación individuales en LineStrings con la
ST_MAKELINE
función de la siguiente manera:
SELECT
penguin_id,
DATE(timestamp, 'UTC') AS route_date,
ST_MAKELINE(ARRAY_AGG(location_cast_to_geog)) AS route
FROM (
-- Sub-expression to sort all location pings by timestamp
-- This way you make sure individual points can form a realistic route
SELECT
penguin_details['penguin_id'] AS penguin_id,
-- Extract and cast timestamp once
CAST(CAST(penguin_details['timestamp'] AS STRING) AS TIMESTAMP) AS timestamp,
-- Extract and cast location once
ST_GEOGFROMTEXT(CAST(penguin_details['location'] AS STRING)) AS location_cast_to_geog
FROM
penguin_movements
ORDER BY
-- Pre-sorts location pings for the ARRAY_AGG function
penguin_id, timestamp ASC
)
GROUP BY
penguin_id,
route_date
Con esta consulta, puedes obtener estadísticas más avanzadas para tu investigación. Expande las siguientes secciones para ver ejemplos de consultas y resultados.
¿Cuál es la distancia de viaje diaria de cada pingüino?
Para calcular la distancia que recorre cada pingüino todos los días, puedes usar
la ST_LENGTH función.
SELECT penguin_id, route_date, ST_LENGTH(route) AS daily_distance_meters FROM ( -- Subquery to aggregate points into daily routes SELECT penguin_id, DATE(timestamp, 'UTC') AS route_date, ST_MAKELINE(ARRAY_AGG(location_cast_to_geog)) AS route FROM ( -- Sub-expression to sort all location pings by timestamp -- This way you make sure individual points can form a realistic route SELECT penguin_details['penguin_id'] AS penguin_id, -- Extract and cast timestamp once CAST(CAST(penguin_details['timestamp'] AS STRING) AS TIMESTAMP) AS timestamp, -- Extract and cast location once ST_GEOGFROMTEXT(CAST(penguin_details['location'] AS STRING)) AS location_cast_to_geog FROM penguin_movements ORDER BY -- Pre-sorts location pings for the ARRAY_AGG function penguin_id, timestamp ASC ) GROUP BY penguin_id, route_date ) AS DailyRoutes;
Esta consulta muestra un resultado similar al siguiente:
+------------+------------+-----------------------+ | penguin_id | route_date | daily_distance_meters | +------------+------------+-----------------------+ | pen_01 | 2025-12-02 | 15420.7 | | pen_03 | 2025-12-03 | 22105.1 | | pen_32 | 2025-12-03 | 9850.3 | +------------+------------+-----------------------+
¿Qué parte de la ruta diaria ocurre dentro del área con geocerca?
Para responder esta pregunta, filtra los resultados con la ST_INTERSECTION
función para verificar qué partes de la ruta del pingüino se cruzan con el área con geocerca.
Luego, la función ST_LENGTH puede ayudar a calcular
la proporción de movimiento dentro de las áreas de alimentación con respecto a la distancia total
recorrida ese día.
SELECT penguin_id, route_date, ST_LENGTH(ST_INTERSECTION(route, ST_GEOGFROMTEXT('POLYGON((-57.21 -64.51, -57.23 -64.55, -57.08 -64.56, -57.06 -64.51, -57.21 -64.51))'))) / ST_LENGTH(route) AS proportion_in_ground FROM ( -- Subquery to aggregate points into daily routes SELECT penguin_id, DATE(timestamp, 'UTC') AS route_date, ST_MAKELINE(ARRAY_AGG(location_cast_to_geog)) AS route FROM ( -- Sub-expression to sort all location pings by timestamp -- This way you make sure individual points can form a realistic route SELECT penguin_details['penguin_id'] AS penguin_id, -- Extract and cast timestamp once CAST(CAST(penguin_details['timestamp'] AS STRING) AS TIMESTAMP) AS timestamp, -- Extract and cast location once ST_GEOGFROMTEXT(CAST(penguin_details['location'] AS STRING)) AS location_cast_to_geog FROM penguin_movements ORDER BY -- Pre-sorts location pings for the ARRAY_AGG function penguin_id, timestamp ASC ) GROUP BY penguin_id, route_date ) AS DailyRoutes WHERE ST_INTERSECTS(route, ST_GEOGFROMTEXT('POLYGON((-57.21 -64.51, -57.23 -64.55, -57.08 -64.56, -57.06 -64.51, -57.21 -64.51))'))
Esta consulta muestra un resultado similar al siguiente:
+------------+------------+----------------------+ | penguin_id | route_date | proportion_in_ground | +------------+------------+----------------------+ | pen_01 | 2025-12-03 | 0.652 | | pen_03 | 2025-12-03 | 0.918 | | pen_32 | 2025-12-04 | 0.231 | +------------+------------+----------------------+
Optimiza las consultas con vistas materializadas continuas
Las consultas geoespaciales en conjuntos de datos grandes pueden ser lentas porque pueden requerir el análisis de muchas filas. Bigtable no admite índices geoespaciales dedicados, pero puedes mejorar el rendimiento de las consultas con vistas materializadas continuas.
Para crear una clave de fila única para esas vistas, puedes transformar el GEOGRAPHY
tipo en una celda S2.
S2 es una biblioteca que te permite realizar trigonometría compleja para la geometría de esferas.
Calcular distancias basadas en coordenadas geográficas puede ser costoso desde el punto de vista computacional. Las celdas S2 pueden representar un área específica en la superficie de la Tierra como un número entero de 64 bits, lo que las hace ideales para la indexación geoespacial con vistas materializadas continuas.
Para definir una celda S2, llama a la
función S2_CELLIDFROMPOINT(location, level)
y proporciona el argumento level de granularidad. Este argumento es un
número entre 0 y 30 y define el tamaño de cada celda: cuanto mayor sea el
número, más pequeña será la celda.
En el ejemplo de investigación de pingüinos, puedes crear una vista materializada continua indexada en la ubicación y la marca de tiempo de cada pingüino:
-- Query used to create the continuous materialized view
SELECT
-- Create S2 cells for each penguin's location.
-- Note that the `level` value is the same for each location so that
-- every cell is the same size. This ensures consistency for your data.
S2_CELLIDFROMPOINT(ST_GEOGFROMTEXT(CAST(penguin_details['location'] AS STRING)), level => 16) AS s2_cell_id,
penguin_details['timestamp'] AS observation_time,
penguin_details['penguin_id'] AS penguin_id,
penguin_details['location'] AS location
FROM
penguin_movements
Consultar esta vista para responder la
pregunta ¿Quién cruzó la geocerca?
se vuelve mucho más eficiente con un enfoque de dos fases.
Primero, usas el índice cell_id en la vista materializada continua para encontrar rápidamente las filas candidatas dentro de la vecindad general y, luego, aplicas la función ST_CONTAINS precisa en el conjunto de datos reducido:
SELECT
idx.penguin_id,
idx.location,
idx.observation_time
FROM
penguin_s2_time_index AS idx
WHERE
-- Part one: use an approximate spatial filter and timestamp filter
-- for fast scans on continuous materialized view keys.
-- Use the S2_COVERINGCELLIDS function to create an array of S2 cells
-- that cover the feeding ground polygon. The `level` argument must be the
-- same value as the one you used to create the continuous materialized view.
idx.s2_cell_id IN UNNEST(S2_COVERINGCELLIDS(
ST_GEOGFROMTEXT('POLYGON((-57.21 -64.51, -57.23 -64.55, -57.08 -64.56, -57.06 -64.51, -57.21 -64.51))'),
min_level => 16,
max_level => 16,
max_cells => 500
))
AND idx.observation_time >= '2025-12-03 00:00:00 UTC'
AND idx.observation_time < '2025-12-04 00:00:00 UTC'
-- Part two: use ST_CONTAINS() on the returned set to ensure precision.
-- S2 cells are squares, so they don't equal arbitrary geofence polygons.
-- You still need to check if a specific point is contained within the area,
-- but the filter applies to a smaller data set and is much faster.
AND ST_CONTAINS(ST_GEOGFROMTEXT('POLYGON((-57.21 -64.51, -57.23 -64.55, -57.08 -64.56, -57.06 -64.51, -57.21 -64.51))'), idx.location);
Limitaciones
Bigtable no admite las siguientes funciones cuando se trabaja con datos geográficos:
- Geometrías tridimensionales. Esto incluye el sufijo "Z" en el formato WKT y la coordenada de altitud en el formato GeoJSON.
- Sistemas de referencia lineal. Esto incluye el sufijo "M" en el formato WKT.
- Objetos de geometría WKT aparte de las primitivas de geometría y las geometrías multiparte. En particular, Bigtable solo admite Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon y GeometryCollection.
- No se admite la función
ST_CLUSTERDBSCAN.
Para conocer las restricciones específicas de los formatos de entrada GeoJson y WKT, consulta
ST_GEOGFROMGEOJSON
y
ST_GEOGFROMTEXT.
Próximos pasos
- Obtén más información sobre las funciones geográficas disponibles. Consulta Descripción general de las funciones geográficas de GoogleSQL.