NetCDF Writer
Write CF-convention NetCDF (.nc) — the inverse of the
netcdf_gbx reader. df.write.format("netcdf_gbx") writes
raster tile DataFrames as CF grids (the default raster mode) or point DataFrames
as CF Discrete Sampling Geometry (DSG) files (vector mode).
netcdf_gbx is light-only: there is no heavyweight NetCDF writer. Unlike
GeoTIFF or the catch-all raster writer (which ship in both tiers), NetCDF writing
is the pure-Python (netcdf4-backed, JAR-free) writer only — it runs on
Serverless, standard (shared), ARM, and classic compute. This mirrors the
read side, where the swath-flattening vector leg is a lightweight-only capability.
The lightweight Python DataSources are not auto-registered — call
register(spark) once per session before using the netcdf_gbx writer.
# Register the lightweight DataSources (once per session)
from databricks.labs.gbx.ds.register import register
register(spark)
Schema
The writer consumes whichever schema its mode expects:
- Raster mode — the shared
(source, tile)raster schema (exactly what the raster readers emit). Each row is one CF grid. - Vector mode — the lightweight vector schema: attribute columns plus a
geom_0WKB point column andgeom_0_srid(+ geom_0_srid_proj) CRS-companion columns.geom_0*are geometry/CRS metadata and are not written as data variables; every other column becomes a per-observation data variable. A frame that does not use thegeom_0convention can point the writer at its own columns withgeomCol/sridCol/projCol(see Options). Vector output is point-only (CF-DSGfeatureType="point").
Output: the writer shards by default — one .nc per row (raster) or per Spark
partition (vector) — which is safe at any scale. Opt in to a single consolidated
.nc with singleFile, or fold already-written parts with merge.
.mode("overwrite") clears the target directory's .nc files before writing;
.mode("append") writes new shards alongside existing ones (parts mode). With
singleFile, both modes write the resolved single file fresh from the current
DataFrame — append does not fold rows into a pre-existing single file. To
combine already-written .nc files without recomputing, use merge (which ignores
the DataFrame rows and requires .mode("append") since it never deletes its inputs).
Raster mode (default)
Each (source, tile) row is written as one CF grid .nc with lat and lon
coordinate variables, a _FillValue for NoData pixels, and — when the tile CRS
resolves to an EPSG code other than 4326 — a crs grid-mapping variable carrying
that code in its spatial_epsg attribute (which the reader recovers on re-read).
# Raster mode (default): each (source, tile) grid row -> one CF grid .nc.
# lat/lon coordinate variables, a _FillValue for NoData, and — when the tile CRS
# resolves to an EPSG code other than 4326 — a `crs` grid-mapping variable.
df = (spark.read.format("netcdf_gbx")
.option("variable", "t2m")
.load("/Volumes/main/geobrix_samples/netcdf/era5_sample.nc"))
(df.write
.format("netcdf_gbx")
.save("/Volumes/main/geobrix_samples/netcdf/output"))
# nameCol supplies the output filename stem; varNameCol overrides the variable name.
(df.write
.format("netcdf_gbx")
.option("nameCol", "file_stem")
.option("varNameCol", "var_name")
.save("/Volumes/main/geobrix_samples/netcdf/output"))
The variable name is resolved as varNameCol (explicit override) → the source
subdataset selector (NETCDF:"{path}":{var} → the segment after the last :) →
"data". The filename stem is nameCol when set, otherwise the resolved variable
name, falling back to <partPrefix>-<uuid>.
Vector mode
Vector mode writes the DataFrame's attribute columns plus lon/lat as a CF Discrete
Sampling Geometry file (featureType="point") — one .nc per Spark partition. Null
attribute cells are written as CF fill: an integer _FillValue sentinel for integer
columns, NaN for float columns.
# Vector mode: point rows -> one CF DSG .nc per Spark partition
# (featureType="point"). Attribute columns plus lon/lat are written; null cells
# become CF fill (integer _FillValue for integer columns, NaN for float columns).
df = (spark.read.format("netcdf_gbx")
.option("mode", "vector")
.option("variables", "methane_mixing_ratio_bias_corrected,qa_value")
.load("/Volumes/main/geobrix_samples/netcdf/s5p_ch4_sample.nc"))
(df.write
.format("netcdf_gbx")
.option("mode", "vector")
.save("/Volumes/main/geobrix_samples/netcdf/output-points"))
Options
| Option | Default | Description |
|---|---|---|
mode | "raster" | raster (tile → CF grid) or vector (points → CF DSG). |
nameCol | — | Column whose value supplies the output filename stem per row/part. Absent (raster) → the stem is derived from the source value; absent (vector) → the part uses <partPrefix>-<uuid>. |
varNameCol | — | Raster mode only. Column whose value supplies the NetCDF variable name per row. Absent → parsed from the source subdataset selector (segment after the last :), or "data" if unparseable. |
singleFile | "false" | Write one .nc instead of sharded parts. Vector → all points concatenated into one CF-DSG file; raster → distinct variables sharing one grid merged into one CF grid file. See Single-file & directory merge. |
merge | "false" | Post-hoc: merge the .nc files already present in the output directory into one file, without re-running the source DataFrame. Implies single output and wins over singleFile if both are set. |
keepParts | "false" | Only meaningful with singleFile / merge. When false (default), the source part files are deleted after the merged output is validated and durably written; when true, the parts are kept alongside the merged file. A failed or partial merge always leaves every part intact. |
fileName | — (derived) | Output filename stem for singleFile / merge output. Absent → derived. |
partPrefix | "part" | Filename stem for sharded parts-mode files (<partPrefix>-<uuid>.nc) — label your shards. |
geomCol | auto-detected from the *_srid column | Vector mode only. Override the geometry (WKB point) column name. Locates the geometry and its SRID companion (<geomCol>_srid). |
sridCol | <geomCol>_srid | Vector mode only. Override the SRID column name. Required — supplies the CRS authority code (e.g. "4326", or "0" if unknown), recorded as spatial_epsg on the crs grid-mapping variable for non-4326 points. |
projCol | <geomCol>_srid_proj | Vector mode only. Override the PROJ4 column name (optional fallback CRS when sridCol is "0"). |
Single-file & directory merge
By default the writer shards output into one .nc per row (raster) or per Spark
partition (vector) — safe at any scale. To consolidate into one .nc, opt in
with singleFile, or merge already-written parts after the fact with merge.
# Consolidate into ONE .nc with singleFile="true".
# vector -> all points concatenated into one CF-DSG file.
# raster -> distinct variables sharing one grid merged into one CF grid file.
# The merge is funneled through the driver, so reach for it when the combined
# output fits in driver memory; otherwise keep the default sharded parts.
(df.write
.format("netcdf_gbx")
.option("mode", "vector")
.option("singleFile", "true")
.option("fileName", "combined") # optional output stem
.save("/Volumes/main/geobrix_samples/netcdf/output-single"))
# Post-hoc: merge the .nc files ALREADY in a directory into one, without
# re-running the source DataFrame (the DataFrame rows are ignored). Use keepParts
# to retain the source parts; partPrefix/fileName control shard/merged names.
(spark.range(1).write # any DataFrame; the source rows are ignored
.format("netcdf_gbx")
.mode("append")
.option("mode", "vector")
.option("merge", "true")
.option("keepParts", "true") # keep the source parts alongside the merged file
.save("/Volumes/main/geobrix_samples/netcdf/output-points"))
- Vector
singleFileconcatenates all points across partitions into one CF Discrete Sampling Geometry file (featureType="point"), streaming fragment by fragment through an UNLIMITEDobsdimension to bound driver memory. - Raster
singleFilemerges tiles carrying distinct variables that share one grid into a single CF grid file: multiple data variables over sharedlat/loncoordinate variables, each with its own_FillValue, plus a sharedcrsgrid-mapping variable for non-4326 grids. It deliberately does not mosaic many spatial-window tiles of one variable — if two tiles have incompatible grids or the same variable name, the writer raises a clear error pointing atgbx_rst_merge_agg. mergeruns the same merge core on the.ncfiles already in the output directory, so the "I already wrote sharded parts, now just combine them" case needs no recompute. Merging an empty directory raises.mergeimplies single output and wins oversingleFilewhen both are set.keepParts(defaultfalse) controls the source parts. After a successful, validated, durably-written merge the parts are deleted (consolidate-in-place); setkeepParts="true"to keep them. Parts are never deleted until the merged output is validated and verified on disk — a failed or partial merge always leaves every part intact.
The raster singleFile / merge path merges distinct variables on a shared
grid, not many spatial-window tiles of a single variable. To combine window-tiles
of one variable into a single grid, mosaic them with gbx_rst_merge_agg (or
gbx_rst_merge) before the writer:
-- Mosaic window-tiles of one variable upstream, THEN write one file.
SELECT gbx_rst_merge_agg(tile) AS tile
FROM tiles
GROUP BY source
then write the mosaicked DataFrame with singleFile="true".
The single-file merge is funneled through the driver, so sharded parts (the default)
are safer at very large scale. Reach for singleFile / merge when the combined
output comfortably fits in driver memory; otherwise keep the default sharded parts.
Round-trip
Reading a .nc file with netcdf_gbx raster mode and writing it back with
netcdf_gbx raster mode reproduces the physical values, CRS, and NoData mask.
Reading with netcdf_gbx vector mode and writing back preserves lon/lat coordinates
and attribute columns.
Decoded physical values only. The writer outputs the values it receives — after
any CF scale_factor / add_offset decoding the reader applied — as floating-point
data. It does not re-pack values to compressed integers. To reproduce a source file's
packed storage format, apply the encoding transform manually before writing.
Geographic (lon/lat) grids on the raster path. The raster writer targets
EPSG:4326 geographic grids. Tiles in a projected CRS (for example EPSG:3857 or
EPSG:27700) should be reprojected to EPSG:4326 with gbx_rst_transform before
writing: the lat/lon coordinate variables are always written in degrees, and a
non-4326 EPSG is recorded only as a spatial_epsg code on the crs grid-mapping
variable — the coordinate values themselves are not reprojected, so a projected tile
would be mislabeled as degrees.
Next Steps
- NetCDF Reader — the corresponding read paths (raster, vector, heavyweight).
- GeoTIFF Writer — for COG/GeoTIFF raster output.
- Writers Overview — all writer formats and the tier split.
- Raster Functions — tessellation, band math, tiling,
gbx_rst_merge_agg.