Skip to main content
WherobotsDB stores tables in Apache Iceberg. New tables in Wherobots-managed catalogs, such as org_catalog, are format-version 3 (V3), which has geometry and geography column types, and Wherobots adds a raster column type. Tables created before September 15, 2026 were created at format-version 2 (V2), and Wherobots doesn’t raise them to V3. Which column types a table can have, and whether WherobotsDB skips data files for spatial filters, depend on its format version; see the Iceberg Tables Overview. This page shows how to create, write, and query tables with geometry and geography columns. The examples use the catalog name org_catalog; replace it with the name of the catalog you write to.

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.
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.

Creating a table with a geometry column

Apache Iceberg tables have a GEOMETRY data type to represent geospatial data. Declare the column with its CRS in parentheses, as an SRID:
This will create an empty table partitioned by the bucketed value of 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.
The table was created with TBLPROPERTIES ('format-version'='2'). Remove the property:
The column was declared as geometry with no CRS in parentheses. Add the SRID:

Inspecting the schema

Inspect the table schema with DESCRIBE TABLE:
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):
The statement SHOW CREATE TABLE returns recreates an equivalent table, so it is also the way to clone the schema of an existing table.
To see a table’s Apache Iceberg format version, run SHOW TBLPROPERTIES org_catalog.test_db.test_table ('format-version').

Columns with no declared CRS

A geometry column created without a CRS renders as geom GEOMETRY, with no parentheses, in SHOW CREATE TABLE; DESCRIBE TABLE shows geometry for both kinds of column. Tables created before geometry(<srid>) was available have such columns, and so does a table created from the result of an ST_ function, as described in Creating a table from a query or a DataFrame. These columns keep working: reads, writes, and every ST_ function behave the same. The SRID of written values isn’t checked, Iceberg engines outside Wherobots read them as binary, and WherobotsDB skips data files for spatial filters on them only in a format-version 1 or 2 table. In a format-version 3 table, it reads every data file for those filters; see Spatial file skipping. 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:
Don’t keep a column with no declared CRS in a format-version 3 table. That is what CREATE TABLE ... AS SELECT and writeTo(...).create() give you by default when the geometry comes from an ST_ function, and spatial filters on that column read every data file. Nothing reports it: queries return correct results, only more slowly.
To use geometry(0), create the table, then set each value’s SRID to 0 as you write it:
Writing a value that still carries its SRID fails with 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.
From a DataFrame, use 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

Insert rows with INSERT INTO ... VALUES. Every value has to carry the SRID of the column:
Or insert the result of a query with INSERT INTO ... SELECT:

Matching the column CRS

Geometry constructors such as ST_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.
A value disagrees with the column CRS. The message names the fix:

Writing a DataFrame to a table

Append a DataFrame to a table with writeTo(...).append(). The same SRID rule applies to the DataFrame’s values:
This is equivalent to INSERT INTO ... SELECT.

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

UPDATE changes the rows that match a filter, which can be a spatial filter:

Deleting data

DELETE FROM removes the rows that match a filter, which can be a spatial filter:

Merging data using MERGE INTO

MERGE INTO has the same syntax as in Apache Iceberg; see Apache Iceberg - MERGE INTO. 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

Load a table into a DataFrame with sedona.table(...):
You can set read options when you load the table. For example, with a smaller split size, the table is read into more partitions:
Run a spatial range query with Spatial SQL:
Or with the DataFrame API:
You can also load a table by naming the data source with .format("iceberg"). This loads an isolated table reference, which doesn’t refresh automatically when the table changes:
WherobotsDB skips the data files whose geometries can’t match a spatial filter, which makes selective spatial range queries fast. See Spatial Filter Push-down and Clustering for how it works and how to cluster a table so more files are skipped.

Working with geometry data

Every ST_ function in WherobotsDB works on a geometry column. For example, ST_Buffer creates a buffer around each geometry:
To write the result back to a table, create the table with its geometry column first, then insert the result of the query. ST_Buffer keeps the SRID of its input, so the buffered values match a geometry(4326) column:
Or call writeTo(...).append() on the DataFrame the query returns:

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.
A geography column rejects a value whose SRID is a different CRS, the same as a geometry column. Unlike a geometry column, it accepts a value with no SRID, such as the result of 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.

Iceberg Tables Overview

Format versions, spatial file skipping, and format-version 3 features.

Upgrade Existing Geometry Tables

Copy a table with no declared CRS, or geometry stored as WKB, WKT, or GeoJSON, into a table with a GEOMETRY(<srid>) column.

Spatial Filter Push-down and Clustering

Cluster or partition a table so WherobotsDB skips more data files for spatial filters.

Apache Iceberg documentation for Spark

Table maintenance, procedures, and the rest of the Iceberg Spark integration.