> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wherobots.com/llms.txt
> Use this file to discover all available pages before exploring further.

# File Geodatabase Reader

> Read feature classes from Esri File Geodatabases (.gdb) into a WherobotsDB DataFrame from local or cloud storage, with no native libraries and with filter, column, and limit pushdown.

The `filegdb` data source reads a feature class (layer) from an Esri [File Geodatabase](https://pro.arcgis.com/en/pro-app/latest/help/data/geodatabases/manage-file-gdb/file-geodatabases.htm), a `.gdb` directory, into a WherobotsDB DataFrame.

The reader parses the `.gdb` binary format directly on the JVM through the Hadoop FileSystem API. It needs no GDAL or Esri native libraries, and it reads local paths and object storage such as S3 the same way. Large layers are split into partitions that are read in parallel, and filters, column selections, and limits are applied while scanning so that attributes and geometries your query does not use are never decoded.

## Usage

A File Geodatabase is a directory. Pass the `.gdb` directory as the load path and name the feature class to read with the required `layer` option:

<Tabs>
  <Tab title="Python">
    ```python theme={"system"}
    df = sedona.read.format("filegdb").option("layer", "Roads").load("s3://<bucket>/cities.gdb")
    df.printSchema()
    df.show()
    ```
  </Tab>

  <Tab title="Scala">
    ```scala theme={"system"}
    val df = sedona.read.format("filegdb").option("layer", "Roads").load("s3://<bucket>/cities.gdb")
    df.printSchema()
    df.show()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={"system"}
    Dataset<Row> df = sedona.read().format("filegdb").option("layer", "Roads").load("s3://<bucket>/cities.gdb");
    df.printSchema();
    df.show();
    ```
  </Tab>
</Tabs>

The schema contains one column per attribute field in the feature class, plus the layer's shape field (commonly named `SHAPE`) as a geometry column that works with every `ST_*` function. The geometry column carries the layer's spatial reference as an SRID, taken from the geodatabase's well-known ID or identified from its coordinate system WKT. See [Data types](#data-types) for the type mapping and [Spatial reference](#spatial-reference) for how the SRID is resolved.

Each `load()` reads one feature class. To read several feature classes from the same geodatabase, call the reader once per layer.

### Example: Census TIGER/Line geodatabase

The U.S. Census Bureau publishes [TIGER/Line geodatabases](https://www.census.gov/geographies/mapping-files/time-series/geo/tiger-geodatabase-file.html) as zipped `.gdb` directories. Each state has an *edges* geodatabase, such as [`tlgdb_2023_a_44_ri_edges.gdb.zip`](https://www2.census.gov/geo/tiger/TGRGDB23/tlgdb_2023_a_44_ri_edges.gdb.zip) for Rhode Island, whose single feature class `All_Lines` holds every linear feature in the state: roads, rail, hydrography, and boundary lines. Unzip it to your storage, read the feature class, and run spatial SQL on it:

```python theme={"system"}
df = (
    sedona.read.format("filegdb")
    .option("layer", "All_Lines")
    .load("s3://<bucket>/tiger/tlgdb_2023_a_44_ri_edges.gdb")
)
df.createOrReplaceTempView("ri_lines")

sedona.sql("""
    SELECT MTFCC, COUNT(*) AS edges, ROUND(SUM(ST_Length(ST_Transform(SHAPE, 'EPSG:5070'))) / 1000, 1) AS km
    FROM ri_lines
    WHERE MTFCC LIKE 'S1%'
    GROUP BY MTFCC
    ORDER BY km DESC
""").show()
```

```
+-----+-----+------+
|MTFCC|edges|    km|
+-----+-----+------+
|S1400|72335|9675.2|
|S1200|10806|1333.1|
|S1100| 1750| 368.6|
|S1630| 2239| 275.0|
|S1740|  476| 105.7|
|S1500|  174|  66.3|
|S1750|  370|  51.8|
|S1710|  205|  45.4|
|S1820|  248|  42.3|
|S1780|   40|   5.3|
|S1640|   24|   2.7|
|S1730|   27|   1.4|
+-----+-----+------+
```

The `MTFCC LIKE 'S1%'` predicate selects the road classes and is pushed into the scan, so the geometries of the other features are never decoded. The Census geodatabases record their coordinate system (NAD83) as WKT without a well-known ID; the reader identifies it as EPSG:4269, so `ST_Transform` needs only the target CRS. See [Spatial reference](#spatial-reference).

### Finding feature class names

If the `layer` option names a feature class that does not exist, the error lists the feature classes in the geodatabase:

```
Layer 'roads' not found in File Geodatabase 's3://<bucket>/cities.gdb'. Available layers: Roads, Parcels, Buildings
```

Layer names are case-sensitive. System tables (`GDB_*`) and attachment tables are not listed.

## Options

Option names are case-insensitive.

| Option             | Description                                                                                                                                                                                                                                                                                                                                                                         | Default                         |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| `layer`            | Name of the feature class to read. Required.                                                                                                                                                                                                                                                                                                                                        | —                               |
| `numPartitions`    | Number of Spark partitions to split the layer into. When set, overrides `maxPartitionRows`. Values below 1 are rejected with an error. Values larger than the number of row-index entries are clamped to that number, which is the only ceiling: a value of ten million on a layer with 138,240 row-index entries yields 138,240 partitions. Ignored when a `LIMIT` is pushed down. | Derived from `maxPartitionRows` |
| `maxPartitionRows` | Target upper bound on row-index entries per partition when `numPartitions` is not set. Large layers fan out across cores, small layers stay in a single partition. Values below 1 are rejected with an error.                                                                                                                                                                       | `500000`                        |

## Data types

| Esri field type            | Spark type  |
| -------------------------- | ----------- |
| Object ID                  | `integer`   |
| Short integer              | `short`     |
| Long integer               | `integer`   |
| Float                      | `float`     |
| Double                     | `double`    |
| Text                       | `string`    |
| Date                       | `timestamp` |
| GUID, Global ID            | `string`    |
| XML                        | `string`    |
| Blob                       | `binary`    |
| Geometry (the shape field) | `geometry`  |

Raster fields are not supported: reading a feature class that contains one fails at schema inference with a clear message. Field types introduced in ArcGIS Pro 3.2 (64-bit integer, date only, time only, timestamp offset) are not supported either.

### Spatial reference

The SRID of the geometry column is resolved in this order:

1. The `LatestWKID`, then the `WKID`, that the geodatabase's `GDB_Items` catalog records for the layer.
2. An `AUTHORITY["EPSG", ...]` entry in the layer's coordinate system WKT, in the `GDB_Items` catalog or in the shape field's definition.
3. Identification of the coordinate system WKT itself against the EPSG registry. This covers geodatabases that record only Esri-style WKT with no well-known ID, such as the Census TIGER/Line files (`GCS_North_American_1983` resolves to 4269), and Esri-named projected systems such as `NAD_1983_UTM_Zone_19N` or State Plane zones.

The WKT is kept in the column metadata in every case. A layer with no coordinate system, or whose WKT describes a custom system that matches no EPSG definition, yields SRID 0, and the reader logs a warning with the WKT when identification fails. For such a layer, set the SRID with `ST_SetSRID` or pass the source CRS to `ST_Transform` explicitly:

```sql theme={"system"}
SELECT ST_Transform(SHAPE, 'EPSG:4269', 'EPSG:5070') AS shape_albers FROM my_layer
```

Shapes become WherobotsDB geometries as follows:

| Shape type | Geometry                                                                   |
| ---------- | -------------------------------------------------------------------------- |
| Point      | `POINT`                                                                    |
| Multipoint | `MULTIPOINT`                                                               |
| Polyline   | `LINESTRING` for a single part, `MULTILINESTRING` for multiple parts       |
| Polygon    | `POLYGON` for a single outer ring, `MULTIPOLYGON` for multiple outer rings |

Exterior rings and holes are distinguished by their orientation, so polygons decode with the correct outer boundaries and holes. Only X and Y coordinates are decoded: Z and M values are dropped. Features with a null shape produce a null geometry, and rows deleted in the geodatabase are skipped.

## Pushdown and parallelism

* **Filter pushdown.** Predicates on numeric, text, GUID, and Object ID fields are evaluated inside the scan, before the geometry is decoded: `=`, `<`, `<=`, `>`, `>=`, `IN`, `IS NULL`, `IS NOT NULL`, `AND`, `OR`, `NOT`, and the `LIKE`-style `startsWith`, `endsWith`, and `contains` on text fields. Predicates on date, blob, or geometry columns, and anything else the scan cannot evaluate, are still applied by Spark, so results are always correct.
* **Column pruning.** Only the selected fields are decoded. Leaving the geometry column out of a query skips geometry decoding entirely, and selecting only the geometry skips every attribute.
* **Limit pushdown.** `LIMIT n` stops the scan after `n` rows.

The reader splits the layer's row index into contiguous ranges that are read in parallel. The number of partitions is `numPartitions` when set, otherwise the row-index size divided by `maxPartitionRows`, rounded up. With the default `maxPartitionRows` of 500,000, a layer splits only once it has more than 500,000 row-index entries; a statewide TIGER/Line layer of 138,191 features reads in a single partition unless `numPartitions` is set. Row-index entries include deleted rows, so a partition can hold fewer live features than the bound. Asking for more partitions than there are row-index entries yields one partition per entry, never an empty partition, so the row-index size is the only ceiling on `numPartitions`. A query with a pushed `LIMIT` reads in a single partition regardless of these options.

<Note>
  Spatial predicates such as `ST_Intersects` are WherobotsDB SQL functions rather than Spark data source filters, so Spark evaluates them after the scan. The geodatabase's spatial index is not used.
</Note>

## Limitations

* Read-only. The reader does not write File Geodatabases.
* One feature class per `load()`.
* The `.gdb` directory must be unzipped. Zipped geodatabases are not read directly; pointing the reader at a `.gdb.zip` fails with a missing-layer error that lists no feature classes.
* True curve segments (circular arcs, ellipses, Bézier curves) in polyline and polygon shapes are not supported. Reading a shape that contains them raises an error instead of silently approximating the curve. Linearize such layers first, for example with `ogr2ogr -nlt CONVERT_TO_LINEAR`. Layers written by ArcGIS that merely flag curve support without containing curves, which is common, read normally.
* Z and M values are dropped.
* Raster fields and the ArcGIS Pro 3.2 field types listed above are not supported.
* A coordinate system that matches no EPSG definition, such as a custom projection, produces geometries with SRID 0. See [Spatial reference](#spatial-reference).

## Related

* [Load from File Geodatabase](/tutorials/wherobotsdb/vector-data/vector-load#load-from-file-geodatabase) in the vector data loading tutorial
* [STAC Reader](/reference/wherobots-db/vector-data/stac)
