org_catalog, are V3 tables. Tables created before September 15, 2026 were created at format-version 2 (V2), and Wherobots doesn’t raise them to V3. To check a table, run SHOW TBLPROPERTIES <table> ('format-version'). Geometry and geography columns declared with a CRS, such as GEOMETRY(4326), use the V3 geometry and geography types. Wherobots adds a raster column type and the CREATE SPATIAL INDEX statement, which rewrites a table’s data files clustered by a spatial column.
On V3 tables, merge-on-read DELETE, UPDATE, and MERGE write deletion vectors, the row lineage columns _row_id and _last_updated_sequence_number can be queried, and variant columns can be written and read. Column default values can’t be set, and a table with a timestamp_ns column can’t be read.
This document describes how to create, write, and query tables with geometry and geography columns.
Tables created before September 15, 2026, and tables created from the result of an
ST_ function, have geometry columns with no declared CRS. To check a table and upgrade it, see Upgrade Existing Geometry Tables.Readable as geometry by other engines
The geometry type is recorded in the table schema and in the Parquet files, so an Apache Iceberg engine outside Wherobots that supports geospatial types sees a geometry column instead of a binary column it has to decode itself.
One CRS per column, enforced
A column carries exactly one Coordinate Reference System (CRS). A value whose Spatial Reference Identifier (SRID) disagrees with it is rejected instead of being stored under the wrong CRS.
org_catalog throughout this page is an example catalog name. Replace it with the name of the catalog you are writing to.Creating Table with Geometry Column
Apache Iceberg tables have aGEOMETRY data type to represent geospatial data. Declare the column with its CRS in parentheses, as an SRID:
- Python
- Scala
- Java
id, with a geometry column in SRID 4326, which is World Geodetic System 1984 (WGS84) longitude and latitude. A geometry column requires a declared CRS: write geometry(4326), not geometry.
Currently, a geometry(<srid>) column can declare SRID 0, 3857, or 4326, and a geography(<srid>) column can declare 4326. Declaring another SRID, such as 4269 or 32633, fails with ST_INVALID_SRID_VALUE. For data in another CRS, see Data in another CRS.
USING iceberg names the Apache Iceberg data source. havasu.iceberg is an accepted alias for it, in USING and in format(...).
A table with a geometry(<srid>) or geography(<srid>) column is Apache Iceberg format-version 3, which is the default for new tables, so leave 'format-version' out of TBLPROPERTIES.
Error: ... is not supported until v3
Error: ... is not supported until v3
The table was created with
TBLPROPERTIES ('format-version'='2'). Remove the property:Error: bare GEOMETRY in a column definition does not declare a CRS
Error: bare GEOMETRY in a column definition does not declare a CRS
The column was declared as
geometry with no CRS in parentheses. Add the SRID:DESCRIBE command:
- Python
- Scala
- Java
DESCRIBE TABLE shows the type as geometry without the CRS. To see the CRS, run SHOW CREATE TABLE, which renders the column as geom GEOMETRY(4326):
- Python
- Scala
- Java
A geometry column created without a CRS, which is how tables were created before
geometry(<srid>) was available, renders as geom GEOMETRY with no parentheses in SHOW CREATE TABLE. DESCRIBE TABLE shows geometry for both kinds of column. To see a table’s Apache Iceberg format version, run SHOW TBLPROPERTIES org_catalog.test_db.test_table ('format-version').Tables created without a CRS keep working: reads, writes, and every ST_ function behave the same. Spatial filters on such a column skip data files only in a format-version 1 or 2 table; in a format-version 3 table, they read every data file. These columns do not check the SRID of written values, and other Iceberg engines read them as binary. Wherobots will upgrade the read-only public tables in the wherobots_open_data and wherobots_pro_data catalogs. To upgrade your own tables, see Upgrade Existing Geometry Tables.Data in another CRS
Until more SRIDs can be declared, there are three ways to store data in another CRS, such as a Universal Transverse Mercator (UTM) zone. Spatial filters skip data files with all three:
To use
geometry(0), create the table, then set each value’s SRID to 0 as you write it:
Cannot write geometry with SRID 32633 into a column with SRID 0. Build query shapes with SRID 0 too, for example with ST_GeomFromText and no SRID argument. Distances and areas come out in the CRS’s units, meters for UTM. The table doesn’t record the CRS, so keep track of it yourself, and label the values before you reproject them: ST_Transform(ST_SetSRID(geom, 32633), 'EPSG:4326').
To use a format-version 2 table, create it from a query or a DataFrame with the format version set. CREATE TABLE with a geometry column and no SRID is rejected, even at format-version 2.
df.writeTo("org_catalog.test_db.parcels_utm").tableProperty("format-version", "2").create(). Later writes with INSERT INTO or writeTo(...).append() keep the table at format-version 2. Each value keeps its own SRID, which the column doesn’t check. A format-version 2 table can’t use format-version 3 features or take a geometry(<srid>) column. When your SRID can be declared, copy the table as described in Upgrade Existing Geometry Tables.
Writing Data
INSERT INTO
User can insert data into a table usingINSERT INTO table_name VALUES. Every value has to carry the SRID of the column:
- Python
- Scala
- Java
INSERT INTO table_name SELECT ... to insert result set of a query into the table:
- Python
- Scala
- Java
Matching the column CRS
Geometry constructors such asST_GeomFromText and ST_Point return SRID 0, which is a distinct CRS rather than “unset”, so a geometry(4326) column rejects it. Which function you need depends on the SRID your values already carry:
ST_SetSRID relabels without touching the coordinates. Applying it to metre coordinates declares them as longitude and latitude, which puts the data in the wrong place. Use ST_Transform when the coordinates are in another CRS.
Error: Cannot write geometry with SRID ...
Error: Cannot write geometry with SRID ...
A value disagrees with the column CRS. The message names the fix:
Writing a DataFrame to a table
User can write a DataFrame containing geometry data to a table. The same SRID rule applies to the DataFrame’s values:- Python
- Scala
- Java
INSERT INTO table_name SELECT ... statement.
Creating a table from a query or a DataFrame
CREATE TABLE ... AS SELECT, writeTo(...).create(), and writeTo(...).createOrReplace() create a table from the result of a query or a DataFrame. When the geometry comes from a geometry(<srid>) column read from a table, the new column has the same declared CRS. When the geometry is the result of an ST_ function, such as ST_Point, ST_SetSRID, or ST_Buffer, the new column has no declared CRS. In a Wherobots-managed catalog, such as org_catalog, the new table is format-version 3 unless you set 'format-version', and in a format-version 3 table, spatial filters on a column with no declared CRS read every data file. To declare the CRS of the new column, create the table with CREATE TABLE first and then write to it with INSERT INTO ... SELECT or writeTo(...).append(), as shown in Working with Geometry Data.
Updating data
Iceberg tables support UPDATE queries that update matching rows. Update queries accept a filter to match rows to update, including spatial filters.- Python
- Scala
- Java
Deleting data
Iceberg tables support DELETE FROM queries to remove rows. Delete queries accept a filter to match rows to delete, including spatial filters.- Python
- Scala
- Java
Merging data using MERGE INTO
Iceberg tables support MERGE INTO.
The syntax is identical to the open source Apache Iceberg, please refer to Apache Iceberg - MERGE INTO for more information.
UPDATE, DELETE, and MERGE INTO work in both copy-on-write and merge-on-read mode, which the write.update.mode, write.delete.mode, and write.merge.mode table properties select.
Querying Data
User can load data from a table usingsedona.table(...):
- Python
- Scala
- Java
- Python
- Scala
- Java
- Python
- Scala
- Java
- Python
- Scala
- Java
.format("iceberg"), this will load an isolated table reference that will not automatically refresh tables used by queries.
- Python
- Scala
- Java
Working with Geometry Data
User can use anyST_ functions provided by WherobotsDB to manipulate the data in a geometry column. For example, user can use ST_Buffer to create a buffer around the geometry column:
- Python
- Scala
- Java
ST_Buffer keeps the SRID of its input, so the buffered values match a geometry(4326) column:
- Python
- Scala
- Java
writeTo(...).append() on the resulting DataFrame of the query:
- Python
- Scala
- Java
Geography Columns
GEOGRAPHY(<srid>) interprets coordinates on a sphere rather than a plane, so ST_Distance, ST_Area, and ST_Length return spherical results in meters without projecting first.
ST_GeogFromWKT without the SRID argument, and stores it under the column CRS. There is no cast from GEOMETRY to GEOGRAPHY; use ST_GeomToGeography to convert, and ST_GeogToGeometry in the other direction. See the geography function reference for the full list.
Further Reading
WherobotsDB tables are Apache Iceberg tables. Please refer to Apache Iceberg documentation for Spark for more information. If you have spatial data stored in an Iceberg table or Parquet files as WKT or WKB, copy it into a table with aGEOMETRY(<srid>) column as described in Upgrade Existing Geometry Tables.
WherobotsDB pushes spatial filters down to skip data files and can optimize spatial
range queries.
Please refer to Cluster Tables with a Spatial Index for how to organize your spatial data for better performance.
