GanosBase GeomGrid is a spatio-temporal extension for PolarDB for PostgreSQL that lets you store, query, and analyze geographic grids directly in the database. It supports two industry-standard grid systems — GeoSOT and H3 — and provides spatial operations, relationship identification, and index types optimized for grid data.
Key concepts
Geographic grid model
A geographic grid model divides the Earth's surface into hierarchical, regularly shaped cells. Each cell has a unique code, turning continuous geographic space into discrete, addressable units. This makes it practical to associate spatial data with grid cells, run proximity queries, and aggregate results at any precision level.
GeoSOT
GeoSOT is a Chinese-developed Earth subdivision theory based on quad-tree projection. It expands the Earth's surface from 180°×360° to a 512°×512° plane and recursively subdivides that plane to level 32, producing grids that range from the full globe down to a few centimeters. Each cell is encoded as a quaternary number of up to 32 bits. The encoding interleaves latitude and longitude into a 62-bit Morton code, prefixed with a hemisphere indicator (G0 = northeast, G1 = northwest, G2 = southeast, G3 = southwest).
GeoSOT supports a 3D extension: the height dimension is subdivided consistently with longitude and latitude. Each height grid corresponds to a 1° ground surface grid, adding 256 levels above ground and 256 levels below.
H3
H3 is a two-dimensional geographic grid that projects the Earth onto a sphere-circumscribed icosahedron (20 triangular faces, 12 vertices), then covers each face with hexagonal cells. This yields 122 base cells, each recursively divided into 7 finer cells. H3 codes are up to 63 bits and are typically serialized as hexadecimal strings.
GeomGrid and H3Grid
GeomGrid is the GanosBase data type for GeoSOT grids. H3Grid is the data type for H3 grids. Both types support import from and export to other GanosBase types (Text, Bytea, Geometry, Meshgeom, Sfmesh, Vomesh).
Choose between GeoSOT and H3
| Dimension | GeoSOT | H3 |
|---|---|---|
| Cell shape | Polygon (quad-tree subdivision) | Hexagon |
| Max precision | Level 32 (~centimeter) | Higher resolutions support finer precision |
| 3D support | Yes (height dimension: 256 levels above and below ground) | No |
| Encoding | Quaternary Morton code (up to 32 bits) | 63-bit integer, hex string |
| Subdivision | Each cell divides into 4 child cells | Each cell divides into 7 child cells |
| Typical use | Path planning, 3D UAV (unmanned aerial vehicle) routing | Aggregation, heatmaps |
GeoSOT precision levels
The grid level determines cell size. Use this table to select the level that matches your precision requirement.
| Level | Grid dimension | Example use |
|---|---|---|
| 1–9 | Degree-level | Country or province-scale queries |
| 10–15 | Minute-level | City or district-scale queries |
| 16–21 | Second-level | Street or building-level queries |
| 22–32 | Sub-second (down to ~centimeter) | High-precision location |
Use cases
Grid query
Convert a point or area into a grid code and query which stored grid codes intersect or contain it. Supports degenerated grids and parent/child node traversal.
Example: When a shared bicycle is parked, convert the GPS position to a grid code, then query the database to find the nearest designated parking area.
Grid aggregation
Aggregate trajectory data — such as ship or vehicle paths — into grid cells to build heatmaps or population distribution maps. The trajectory points are converted to grid codes, and the count of points per cell becomes the heat value.

Grid pathfinding
Use 3D GeoSOT grids to plan routes in complex environments, such as UAV flight paths. Assign traversal costs to obstacle cells, then apply a pathfinding algorithm to find the optimal path.

How indexes work
Without an index, a spatial query scans every row in the table. For large datasets, this is prohibitively slow. GanosBase GeomGrid supports three index types:
| Index | How it works | When to use it |
|---|---|---|
| B-tree | Compares grid codes as ordered values | Equality and range queries on a single grid column |
| GiST | R-tree structure over bounding boxes; filters candidates with a fast box test, then verifies each candidate with an exact spatial check | Intersection and containment queries on GeomGrid |
| GridGin | Extends PostgreSQL GIN; indexes individual grid codes within array columns | Grid array queries, degenerated grid queries, grid aggregation |
GiST and GridGin use a two-phase approach: the index first identifies candidate rows using bounding boxes, then the database verifies each candidate with a precise spatial test. This avoids scanning the full table while keeping results exact.
Index selection guide
Use GridGin for grid array columns (
geomgrid[]) — it supports degenerated grid queries and accelerates aggregation.Use GiST for single GeomGrid columns where you need intersection or containment queries.
Use B-tree for equality lookups or sorting on grid code values.
Quick start
Prerequisites
Before you begin, make sure you have:
A PolarDB for PostgreSQL cluster
Database access with permission to create extensions
GeoSOT grid operations
Create the extension in the
publicschema to avoid permission issues.If the command fails with an error about a missing dependent extension such as
ganos_sfmesh, contact us.CREATE EXTENSION Ganos_GeomGrid WITH SCHEMA public CASCADE;Create a table with geometry and grid code columns.
-- Table with geometry source and multi-precision grid columns. CREATE TABLE t_grid ( id integer, geom geometry, -- Source geometry grid1 geomgrid[], -- Grid codes at precision level 10 grid2 geomgrid[], -- Grid codes at precision level 15 grid3 geomgrid[] -- Grid codes at precision level 26 );Insert geometry data.
-- Insert point data. INSERT INTO t_grid (id, geom) VALUES (1, ST_GeomFromText('POINT(116.31522216796875 39.910277777777778)', 4490)), (2, ST_GeomFromText('POINT(116.31522217796875 39.910277776777778)', 4490)), (3, ST_GeomFromText('POINT(116.31522217797875 39.910277776787778)', 4490)), (4, ST_GeomFromText('POINT(116.31522227796875 39.910277776775778)', 4490)); -- Insert polygon data. INSERT INTO t_grid (id, geom) VALUES (5, 'SRID=4490;POLYGON((-0.08077 -0.02814, 0.0482 -0.03, 0.07426 0.03724, -0.08077 -0.02814))'::geometry); -- Insert 3D arc data. INSERT INTO t_grid (id, geom) VALUES (6, 'SRID=4490;CIRCULARSTRING Z (-63.597471 44.8071 20,-63.597 44.807 0,-63.5974 44.807 40)'::geometry);Calculate grid codes at different precision levels.
-- Populate grid codes at three precision levels. UPDATE t_grid SET grid1 = ST_AsGrid(geom, 10), grid2 = ST_AsGrid(geom, 15), grid3 = ST_AsGrid(geom, 26); -- Calculate a degenerated grid code for a polygon (id = 5). UPDATE t_grid SET grid1 = ST_AsGrid(geom, 18, true) WHERE id = 5; -- Calculate a 3D grid code (id = 6). UPDATE t_grid SET grid1 = ST_As3DGrid(geom, 25) WHERE id = 6;Create GIN indexes on each grid column to accelerate queries.
CREATE INDEX idx_grid_gin1 ON t_grid USING GIN (grid1); CREATE INDEX idx_grid_gin2 ON t_grid USING GIN (grid2); CREATE INDEX idx_grid_gin3 ON t_grid USING GIN (grid3);Query the grid data. All examples use array operators with the indexed grid columns.
-- Find rows whose grid2 codes are contained in a specific grid. SELECT id FROM t_grid WHERE grid2 = ARRAY[ST_GridFromText('G001310322230230')]; -- Find rows whose grid3 codes intersect with a specific grid. SELECT id FROM t_grid WHERE grid3 @> ARRAY[ST_GridFromText('G00131032223023031031033223')]; -- Find rows whose grid3 codes intersect with any of multiple grids. SELECT id FROM t_grid WHERE grid3 && ARRAY[ ST_GridFromText('G00131032223023031031211001'), ST_GridFromText('G00131032223023031031211111') ]; -- Find rows whose grid3 codes intersect with the grid coverage of a line. SELECT id FROM t_grid WHERE grid3 && ST_AsGrid( ST_GeomFromText('LINESTRING(116.31522216796875 39.910277777777778, 116.31522217797875 39.910277776787778)', 4490), 26 );
H3 grid operations
Create a table with an H3 grid column.
CREATE TABLE h3_grid ( id integer, geom geometry, -- Source geometry h3 h3grid[] -- H3 grid codes );Insert geometry data.
INSERT INTO h3_grid VALUES (1, 'POINT(102.5 25.7)'::geometry); INSERT INTO h3_grid VALUES (2, 'POLYGON((-0.08077 -0.02814, 0.0482 -0.03, 0.07426 0.03724, -0.08077 -0.02814))'::geometry);Calculate H3 grid codes.
-- Regular H3 grid codes at resolution level 7. UPDATE h3_grid SET h3 = ST_AsH3Grid(geom, 7); -- Degenerated H3 grid codes at resolution level 7. UPDATE h3_grid SET h3 = ST_AsH3Grid(geom, 7, true);Create a B-tree index on the H3 column.
CREATE INDEX h3_grid_btree ON h3_grid (h3);Query the H3 data.
-- Display H3 codes as text. SELECT ST_AsText(h3[1]) FROM h3_grid; -- Query rows with H3 codes greater than a reference value. SELECT * FROM h3_grid WHERE h3 > ARRAY[ST_H3FromText('884a126689fffff')];
Remove the extension
Run the following command to remove GanosBase GeomGrid and its dependent objects.
DROP EXTENSION Ganos_GeomGrid CASCADE;What's next
GeomGrid SQL reference — Full function reference for GeomGrid spatial operations, type conversions, and relationship functions