Bekerja dengan data geospasial di Bigtable
Halaman ini menjelaskan cara menyimpan dan membuat kueri data geospasial di Bigtable menggunakan fungsi geografi GoogleSQL. Data geospasial mencakup representasi geometris permukaan Bumi dalam bentuk titik, garis, dan poligon. Anda dapat mengelompokkan entitas ini ke dalam koleksi untuk melacak lokasi, rute, atau area menarik lainnya.
Operasi dasar
Fungsi geografi GoogleSQL khusus memungkinkan Bigtable mengoptimalkan performa database Anda untuk perhitungan geospasial dan waktu kueri. Dalam contoh penggunaan dasar, kita menggunakan tabel
tourist_points_of_interest dengan satu family kolom bernama poi_data.
| poi_data | |||
|---|---|---|---|
| kunci baris | nama | lokasi | |
| p1 | Menara Eiffel | POINT(2.2945 48.8584) |
Menulis data geografi
Untuk menulis data geospasial ke kolom Bigtable, Anda harus memberikan representasi string entitas geografi dalam format GeoJSON atau Well-Known Text (WKT). Bigtable menyimpan data pokok sebagai byte mentah, sehingga Anda dapat memberikan kombinasi entity apa pun dalam satu kolom (seperti Points, LineStrings, Polygons, MultiPoints, MultiLineStrings, atau MultiPolygons). Jenis entity geospasial yang Anda berikan penting pada waktu kueri karena Anda harus menggunakan fungsi geografi yang sesuai untuk menafsirkan data.
Misalnya, untuk menambahkan beberapa tempat wisata ke tabel tourist_points_of_interest, Anda dapat menggunakan alat CLI cbt seperti berikut:
- Dengan 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)'
- Dengan 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] ]] }'
Mengkueri data geografi
Anda dapat membuat kueri tabel tourist_points_of_interest dengan GoogleSQL di
Bigtable Studio dan
memfilter hasilnya dengan
fungsi geografi.
Misalnya, untuk menemukan semua objek wisata dalam jarak 1.500 meter dari Trocadéro, Anda dapat menggunakan fungsi
ST_DWITHIN:
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);
Hasilnya akan mirip dengan berikut ini:

Kolom location menampilkan representasi tekstual asli dari
data yang diberikan dalam operasi penulisan, tetapi Bigtable menyimpannya sebagai byte.
Dalam contoh ini, meskipun Place Charles de Gaulle ditentukan sebagai poligon yang membentang lebih jauh dari batas 1.500 meter, poligon tersebut disertakan dalam hasil kueri.
Hal itu karena fungsi ST_DWITHIN menampilkan nilai benar jika titik mana pun yang ada dalam entitas geospasial berada dalam jarak yang diharapkan.
Penggunaan lanjutan
Bagian berikut menjelaskan cara menggunakan fungsi geografi untuk skenario kompleks seperti analisis geofencing. Bagian Pengoptimalan kueri menjelaskan cara menggunakan tampilan terwujud berkelanjutan untuk meningkatkan performa.
Untuk menggambarkan penggunaan lanjutan fungsi geografi di Bigtable, bayangkan skenario saat Anda adalah peneliti yang memantau perilaku penguin kaisar yang tinggal di sisi selatan Pulau Snow Hill.
Peralatan Anda memberikan ratusan ping lokasi untuk setiap penguin,
jadi Anda membuat tabel penguin_movements untuk menyimpannya:
| penguin_details | |||
|---|---|---|---|
| kunci baris | penguin_id | timestamp | lokasi |
| 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) |
Contoh: Menemukan siapa yang melintasi batas wilayah
Selama penelitian, Anda mengamati perilaku penguin di sekitar tempat makan tertentu. Area yang ditentukan dengan batas virtual biasanya disebut sebagai
geofence. Area pemberian makan dapat dianggap sebagai area yang dibatasi secara geografis.
Anda ingin mengetahui apakah ada penguin yang mengunjungi tempat pemberian makan pada 3 Desember 2025. Untuk menjawab pertanyaan, Anda menggunakan fungsi
ST_CONTAINS, seperti berikut:
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))
)
Anda akan mendapatkan hasil yang mirip dengan berikut ini:
+------------+----------------------+------------------------+
| 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 |
+------------+----------------------+------------------------+
Contoh: Mengubah titik menjadi rute
Untuk memvisualisasikan perjalanan penguin dengan lebih baik, Anda memutuskan untuk mengatur setiap ping lokasi ke dalam LineStrings menggunakan fungsi
ST_MAKELINE
seperti berikut:
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
Dengan kueri ini, Anda bisa mendapatkan insight yang lebih canggih untuk riset Anda. Luaskan bagian berikut untuk melihat contoh kueri dan hasil.
Berapa jarak tempuh harian untuk setiap penguin?
Untuk menghitung jarak yang ditempuh setiap penguin setiap hari, Anda dapat menggunakan
fungsi ST_LENGTH.
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;
Kueri ini menampilkan hasil yang mirip dengan berikut ini:
+------------+------------+-----------------------+ | 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 | +------------+------------+-----------------------+
Berapa banyak rute harian yang berada dalam area ber-geofence?
Untuk menjawab pertanyaan ini, Anda memfilter hasil dengan fungsi ST_INTERSECTION
untuk memeriksa bagian rute penguin yang berpotongan dengan area ber-geofence.
Kemudian, fungsi ST_LENGTH dapat membantu menghitung
rasio pergerakan di dalam area mencari makan dengan jarak penuh
yang ditempuh pada hari itu.
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))'))
Kueri ini menampilkan hasil yang mirip dengan berikut ini:
+------------+------------+----------------------+ | 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 | +------------+------------+----------------------+
Mengoptimalkan kueri dengan tampilan terwujud berkelanjutan
Kueri geospasial pada set data besar dapat berjalan lambat karena mungkin memerlukan pemindaian banyak baris. Bigtable tidak mendukung indeks geospasial khusus, tetapi Anda dapat meningkatkan performa kueri dengan tampilan terwujud berkelanjutan.
Untuk membuat kunci baris unik untuk tampilan tersebut, Anda dapat mengubah jenis GEOGRAPHY
menjadi sel S2.
S2 adalah library yang memungkinkan Anda melakukan trigonometri kompleks untuk geometri bola.
Menghitung jarak berdasarkan koordinat geografis dapat memerlukan banyak komputasi. Sel S2 dapat merepresentasikan area tertentu di permukaan Bumi sebagai bilangan bulat 64-bit, sehingga ideal untuk pengindeksan geospasial dengan tampilan terwujud berkelanjutan.
Untuk menentukan sel S2, Anda memanggil fungsi
S2_CELLIDFROMPOINT(location, level)
dan memberikan argumen perincian level. Argumen ini adalah
angka antara 0 dan 30 dan menentukan ukuran setiap sel: makin tinggi
angkanya, makin kecil selnya.
Dalam contoh riset penguin, Anda dapat membuat tampilan terwujud berkelanjutan yang diindeks berdasarkan lokasi dan stempel waktu setiap penguin:
-- 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
Mengirim kueri tampilan ini untuk menjawab pertanyaan
Siapa yang melintasi geofence?
menjadi jauh lebih efisien dengan pendekatan dua fase.
Anda terlebih dahulu menggunakan indeks cell_id dalam tampilan materialis berkelanjutan untuk menemukan baris kandidat dengan cepat di sekitar lokasi umum, lalu menerapkan fungsi ST_CONTAINS yang akurat pada set data yang diperkecil:
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);
Batasan
Bigtable tidak mendukung fitur berikut saat bekerja dengan data geografi:
- Geometri tiga dimensi. Geometri ini mencakup akhiran "Z" dalam format WKT, dan koordinat ketinggian dalam format GeoJSON.
- Sistem referensi linear. Opsi ini mencakup akhiran "M" dalam format WKT.
- Objek geometri WKT selain primitif geometri atau geometri multibagian. Secara khusus, Bigtable hanya mendukung Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, dan GeometryCollection.
- Fungsi
ST_CLUSTERDBSCANtidak didukung.
Untuk batasan khusus format input GeoJson dan WKT, lihat
ST_GEOGFROMGEOJSON
dan
ST_GEOGFROMTEXT.
Apa langkah selanjutnya?
- Pelajari lebih lanjut fungsi geografi yang tersedia. Lihat Ringkasan fungsi geografi GoogleSQL.