Skill Tracer
Skills › Scientific Agent Skills › geopandas › data-io.md

data-io.md

351 lines · skills/geopandas/references/data-io.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Vector I/O, GeoParquet, Arrow, and PostGIS
2
3GeoPandas 1.x defaults to pyogrio for vector-file I/O. pyogrio and Fiona are
4bindings to GDAL/OGR; actual formats, options, and behavior depend on the
5installed native GDAL and drivers.
6

Vector I/O, GeoParquet, Arrow, and PostGIS

GeoPandas 1.x defaults to pyogrio for vector-file I/O. pyogrio and Fiona are bindings to GDAL/OGR; actual formats, options, and behavior depend on the installed native GDAL and drivers.

Raw lines 1 to 6
7## Local-only intake policy
8
9Do not automatically pass any of these to GeoPandas/GDAL:
11- `http://`, `https://`, `s3://`, `gs://`, Azure, or another remote URI;
12- GDAL `/vsicurl/`, `/vsis3/`, `/vsizip/`, chained virtual filesystems, or
13 pyogrio `zip+...` paths;
14- ZIP/KMZ/TAR/GZ/7z/RAR or nested archives;
15- macro-enabled office files or an untrusted permissive driver;
16- a user-supplied PostGIS connection string.
18GeoPandas officially supports URLs and GDAL supports network/archive virtual
19filesystems, but that capability crosses network, decompression, parser, and
20credential trust boundaries. Obtain explicit approval, verify source/hash and
21size out of band, unpack in a sandbox with resource limits, then process an
22allowlisted local regular file.
24The bundled CLIs reject URL/VSI/archive syntax, symlinks, path traversal, and
25non-allowlisted suffixes.

Local-only intake policy

Do not automatically pass any of these to GeoPandas/GDAL:

  • http://, https://, s3://, gs://, Azure, or another remote URI;
  • GDAL /vsicurl/, /vsis3/, /vsizip/, chained virtual filesystems, or pyogrio zip+... paths;
  • ZIP/KMZ/TAR/GZ/7z/RAR or nested archives;
  • macro-enabled office files or an untrusted permissive driver;
  • a user-supplied PostGIS connection string.

GeoPandas officially supports URLs and GDAL supports network/archive virtual filesystems, but that capability crosses network, decompression, parser, and credential trust boundaries. Obtain explicit approval, verify source/hash and size out of band, unpack in a sandbox with resource limits, then process an allowlisted local regular file.

The bundled CLIs reject URL/VSI/archive syntax, symlinks, path traversal, and non-allowlisted suffixes.

Raw lines 7 to 26
27## Inspect before reading features
29For GDAL-backed formats:
31```python
32from pathlib import Path
33import pyogrio
34import geopandas
36path = Path("approved/input.gpkg")
37if not path.is_file() or path.is_symlink():
38 raise ValueError("Expected a vetted local regular file")
40layers = pyogrio.list_layers(path)
41info = pyogrio.read_info(
42 path, layer="approved_layer", force_feature_count=False, force_total_bounds=False
44drivers = pyogrio.list_drivers()
45```
47GeoPandas 1.2 adds `geopandas.read_file_info(path, layer=...)`, a small wrapper
48with only those two parameters. Use `pyogrio.read_info` directly for options
49such as `force_feature_count` and `force_total_bounds`; the wrapper rejects them.
51Record:
53- primary-file hash and byte size (a Shapefile hash does not cover sidecars);
54- driver, layer, declared feature count, fields/dtypes, encoding, geometry type;
55- CRS and whether bounds were present, but redact precise bounds by default;
56- `pyogrio.__gdal_version__`, `pyogrio.__gdal_geos_version__`, Shapely GEOS,
57 pyproj PROJ, package versions, and enabled driver capabilities.
59In pyogrio 0.13, `list_drivers()` reports capabilities containing `r`, `w`,
60and/or `a` (append, GDAL >=3.11); `list_drivers(append=True)` filters for known
61append support. A listed driver is not proof every field/geometry is supported. Treat drivers as
62an allowlist, not merely a discovered list.

Inspect before reading features

For GDAL-backed formats:

from pathlib import Path
import pyogrio
import geopandas

path = Path("approved/input.gpkg")
if not path.is_file() or path.is_symlink():
    raise ValueError("Expected a vetted local regular file")

layers = pyogrio.list_layers(path)
info = pyogrio.read_info(
    path, layer="approved_layer", force_feature_count=False, force_total_bounds=False
)
drivers = pyogrio.list_drivers()

GeoPandas 1.2 adds geopandas.read_file_info(path, layer=...), a small wrapper with only those two parameters. Use pyogrio.read_info directly for options such as force_feature_count and force_total_bounds; the wrapper rejects them.

Record:

  • primary-file hash and byte size (a Shapefile hash does not cover sidecars);
  • driver, layer, declared feature count, fields/dtypes, encoding, geometry type;
  • CRS and whether bounds were present, but redact precise bounds by default;
  • pyogrio.__gdal_version__, pyogrio.__gdal_geos_version__, Shapely GEOS, pyproj PROJ, package versions, and enabled driver capabilities.

In pyogrio 0.13, list_drivers() reports capabilities containing r, w, and/or a (append, GDAL >=3.11); list_drivers(append=True) filters for known append support. A listed driver is not proof every field/geometry is supported. Treat drivers as an allowlist, not merely a discovered list.

Raw lines 27 to 63
64## `read_file`
66Stable signature:
68```python
69gdf = geopandas.read_file(
70 filename,
71 bbox=None,
72 mask=None,
73 columns=None,
74 rows=None,
75 engine="pyogrio",
76 use_arrow=True,
78```
80Rules:
82- `bbox` and `mask` are mutually exclusive.
83- With pyogrio, a bbox tuple must already be in the dataset CRS. Fiona can
84 reproject a GeoSeries/GeoDataFrame bbox; do not depend on engine-specific
85 implicit behavior.
86- A mask must have an explicit CRS compatible with the source.
87- `columns=[]` reads geometry without attributes; `ignore_geometry=True`
88 returns a pandas DataFrame.
89- `rows=n` reads the first n rows; `rows=slice(a, b)` reads a slice.
90- `where=` is evaluated by a driver SQL dialect. Do not concatenate untrusted
91 expressions.
92- Encoding auto-detection can fail; set a verified encoding explicitly.
93- `use_arrow=True` requires PyArrow and speeds pyogrio bulk transfer, but does
94 not change parser trust or correctness requirements.
95- Bound both file bytes and features. Some drivers cannot cheaply report a
96 count; read at most `limit + 1` and fail closed when the limit is exceeded.
98GeoPandas may use HTTP range requests or download an entire URL in memory.
99This skill therefore does not include remote-read examples.

read_file

Stable signature:

gdf = geopandas.read_file(
    filename,
    bbox=None,
    mask=None,
    columns=None,
    rows=None,
    engine="pyogrio",
    use_arrow=True,
)

Rules:

  • bbox and mask are mutually exclusive.
  • With pyogrio, a bbox tuple must already be in the dataset CRS. Fiona can reproject a GeoSeries/GeoDataFrame bbox; do not depend on engine-specific implicit behavior.
  • A mask must have an explicit CRS compatible with the source.
  • columns=[] reads geometry without attributes; ignore_geometry=True returns a pandas DataFrame.
  • rows=n reads the first n rows; rows=slice(a, b) reads a slice.
  • where= is evaluated by a driver SQL dialect. Do not concatenate untrusted expressions.
  • Encoding auto-detection can fail; set a verified encoding explicitly.
  • use_arrow=True requires PyArrow and speeds pyogrio bulk transfer, but does not change parser trust or correctness requirements.
  • Bound both file bytes and features. Some drivers cannot cheaply report a count; read at most limit + 1 and fail closed when the limit is exceeded.

GeoPandas may use HTTP range requests or download an entire URL in memory. This skill therefore does not include remote-read examples.

Raw lines 64 to 100
101## Filters are not always exact
103pyogrio documents:
105- `bbox`/`mask` coordinates must use the dataset CRS;
106- when GDAL is built with GEOS, geometry intersection filtering is exact;
107- without GEOS, filters can return features whose **bounding boxes** intersect,
108 requiring a second exact predicate check;
109- Arrow reads involving skip/max may read batches beyond the requested slice
110 before slicing;
111- feature IDs are driver-specific and may start at 0, 1, or another value.
113Do not treat driver FID as a portable stable feature ID.

Filters are not always exact

pyogrio documents:

  • bbox/mask coordinates must use the dataset CRS;
  • when GDAL is built with GEOS, geometry intersection filtering is exact;
  • without GEOS, filters can return features whose bounding boxes intersect, requiring a second exact predicate check;
  • Arrow reads involving skip/max may read batches beyond the requested slice before slicing;
  • feature IDs are driver-specific and may start at 0, 1, or another value.

Do not treat driver FID as a portable stable feature ID.

Raw lines 101 to 114
115## Writing traditional vector formats
117Write a new path and reopen it:
119```python
120output = Path("derived/result.gpkg")
121if output.exists() or output.is_symlink():
122 raise FileExistsError("Choose a new output path")
124gdf.to_file(
125 output,
126 layer="result",
127 driver="GPKG",
128 engine="pyogrio",
129 index=False,
130 use_arrow=True,
133roundtrip = geopandas.read_file(output, layer="result", engine="pyogrio")
134```
136Never use implicit overwrite/append. Driver behavior varies and multi-file
137formats complicate atomic writes.

Writing traditional vector formats

Write a new path and reopen it:

output = Path("derived/result.gpkg")
if output.exists() or output.is_symlink():
    raise FileExistsError("Choose a new output path")

gdf.to_file(
    output,
    layer="result",
    driver="GPKG",
    engine="pyogrio",
    index=False,
    use_arrow=True,
)

roundtrip = geopandas.read_file(output, layer="result", engine="pyogrio")

Never use implicit overwrite/append. Driver behavior varies and multi-file formats complicate atomic writes.

Raw lines 115 to 138
139### Format tradeoffs
141- **GeoPackage**: good general local interchange; multiple layers, one geometry
142 column per layer, SQL-backed metadata.
143- **GeoJSON**: broadly interoperable but normally WGS84 longitude/latitude,
144 limited type fidelity, text-heavy, and easy to leak precise coordinates.
145- **Shapefile**: legacy multi-file format with field-name, type, null, encoding,
146 geometry, and size constraints. Avoid for new work.
147- **FlatGeobuf**: efficient stream/spatial-index format, but interoperability
148 still depends on driver versions.
149- **GeoParquet**: efficient columnar storage with multiple geometry columns and
150 explicit geospatial metadata.
152Traditional formats often cannot store lists, structs, arbitrary objects, or
153multiple geometry columns. Plan conversions and reject silent loss.

Format tradeoffs

  • GeoPackage: good general local interchange; multiple layers, one geometry column per layer, SQL-backed metadata.
  • GeoJSON: broadly interoperable but normally WGS84 longitude/latitude, limited type fidelity, text-heavy, and easy to leak precise coordinates.
  • Shapefile: legacy multi-file format with field-name, type, null, encoding, geometry, and size constraints. Avoid for new work.
  • FlatGeobuf: efficient stream/spatial-index format, but interoperability still depends on driver versions.
  • GeoParquet: efficient columnar storage with multiple geometry columns and explicit geospatial metadata.

Traditional formats often cannot store lists, structs, arbitrary objects, or multiple geometry columns. Plan conversions and reject silent loss.

Raw lines 139 to 154
155## GeoParquet and Feather
157```python
158gdf.to_parquet(
159 "derived/result.parquet",
160 index=False,
161 compression="snappy",
162 geometry_encoding="WKB",
163 write_covering_bbox=False,
164 schema_version="1.1.0",
167roundtrip = geopandas.read_parquet(
168 "derived/result.parquet",
169 columns=["feature_id", "geometry"],
171```
173GeoPandas 1.2.0 write semantics:
175- all geometry columns are preserved;
176- default `geometry_encoding="WKB"` maximizes interoperability;
177- default supported stable schema is **1.1.0**; request 1.0.0 explicitly for
178 consumers that require the older metadata contract;
179- `geometry_encoding="geoarrow"` requires GeoParquet 1.1.0, supports
180 single-geometry native encodings, and is still described as experimental;
181- `write_covering_bbox=True` adds a per-row `bbox` column and 1.1 covering
182 metadata. It costs compute and may reveal precise extents;
183- `schema_version` replaces the removed/deprecated old `version=` usage;
184- `index=None` writes non-RangeIndex values as columns and stores RangeIndex as
185 metadata. Use an explicit stable feature-ID column instead.
187Read semantics:
189- selecting no geometry columns raises; use `pandas.read_parquet` for a
190 non-spatial result;
191- if the stored primary geometry is omitted, the first selected geometry
192 becomes active;
193- `bbox=` filtering works with covering metadata, native `point` encoding,
194 or GeoParquet 2.0 logical geometry types. Bounding-box intersection can admit
195 false positives for non-point geometries; apply an exact predicate afterward;
196- if GeoParquet `crs` metadata is **missing**, the specification default is
197 `OGC:CRS84`;
198- an explicit `crs: null` means unknown/undefined, which is different;
199- WKB/native coordinates are x/y regardless of authority axis order.
200- 1.2 writes and restores JSON-serializable `gdf.attrs`. Review or clear these
201 attributes before publication; paths, source IDs, and study notes can leak
202 through file metadata even when columns are clean.
204GeoParquet 1.1 metadata requires a `geo` JSON value, `primary_column`, and
205metadata for every geometry column. Geometry columns must be root-level and may
206be optional; native child coordinates cannot contain nulls. `edges` defaults to
207`planar`. Feature identifiers are outside the core specification, so define and
208document your own stable-ID metadata/column.
210Do not use bbox covering for public sensitive-location data without
211generalization and approval.

GeoParquet and Feather

gdf.to_parquet(
    "derived/result.parquet",
    index=False,
    compression="snappy",
    geometry_encoding="WKB",
    write_covering_bbox=False,
    schema_version="1.1.0",
)

roundtrip = geopandas.read_parquet(
    "derived/result.parquet",
    columns=["feature_id", "geometry"],
)

GeoPandas 1.2.0 write semantics:

  • all geometry columns are preserved;
  • default geometry_encoding="WKB" maximizes interoperability;
  • default supported stable schema is 1.1.0; request 1.0.0 explicitly for consumers that require the older metadata contract;
  • geometry_encoding="geoarrow" requires GeoParquet 1.1.0, supports single-geometry native encodings, and is still described as experimental;
  • write_covering_bbox=True adds a per-row bbox column and 1.1 covering metadata. It costs compute and may reveal precise extents;
  • schema_version replaces the removed/deprecated old version= usage;
  • index=None writes non-RangeIndex values as columns and stores RangeIndex as metadata. Use an explicit stable feature-ID column instead.

Read semantics:

  • selecting no geometry columns raises; use pandas.read_parquet for a non-spatial result;
  • if the stored primary geometry is omitted, the first selected geometry becomes active;
  • bbox= filtering works with covering metadata, native point encoding, or GeoParquet 2.0 logical geometry types. Bounding-box intersection can admit false positives for non-point geometries; apply an exact predicate afterward;
  • if GeoParquet crs metadata is missing, the specification default is OGC:CRS84;
  • an explicit crs: null means unknown/undefined, which is different;
  • WKB/native coordinates are x/y regardless of authority axis order.
  • 1.2 writes and restores JSON-serializable gdf.attrs. Review or clear these attributes before publication; paths, source IDs, and study notes can leak through file metadata even when columns are clean.

GeoParquet 1.1 metadata requires a geo JSON value, primary_column, and metadata for every geometry column. Geometry columns must be root-level and may be optional; native child coordinates cannot contain nulls. edges defaults to planar. Feature identifiers are outside the core specification, so define and document your own stable-ID metadata/column.

Do not use bbox covering for public sensitive-location data without generalization and approval.

Raw lines 155 to 212
213### GeoParquet 2.0 opt-in
215GeoPandas 1.2 supports the **upcoming** 2.0.0 specification through
216`schema_version="2.0.0"`. Writing needs PyArrow >=21 and uses WKB with the
217Parquet Geometry logical type; `geometry_encoding="geoarrow"` is rejected.
218The release supports bbox filtering without a separate covering column for
219these files. Treat this as an explicit interoperability choice and test the
220actual downstream reader. The bundled export planner deliberately supports
221only stable 1.0/1.1 contracts; do not infer 2.0 support from planner success.

GeoParquet 2.0 opt-in

GeoPandas 1.2 supports the upcoming 2.0.0 specification through schema_version="2.0.0". Writing needs PyArrow >=21 and uses WKB with the Parquet Geometry logical type; geometry_encoding="geoarrow" is rejected. The release supports bbox filtering without a separate covering column for these files. Treat this as an explicit interoperability choice and test the actual downstream reader. The bundled export planner deliberately supports only stable 1.0/1.1 contracts; do not infer 2.0 support from planner success.

Raw lines 213 to 222
223### Arrow in memory
225GeoPandas 1.0 added `to_arrow()` and `from_arrow()` using GeoArrow extension
226types. These improve interchange but do not make an array self-validating:
227verify extension metadata, CRS, geometry encoding/type, nulls, and active
228geometry after round-trip. GeoPandas 1.1 adds `to_pandas_kwargs` controls for
229non-geometry Arrow conversion.

Arrow in memory

GeoPandas 1.0 added to_arrow() and from_arrow() using GeoArrow extension types. These improve interchange but do not make an array self-validating: verify extension metadata, CRS, geometry encoding/type, nulls, and active geometry after round-trip. GeoPandas 1.1 adds to_pandas_kwargs controls for non-geometry Arrow conversion.

Raw lines 223 to 230
231## PostGIS without credential leakage
233The following PostGIS examples are illustrative; no live database was tested.
234Required writing dependencies are SQLAlchemy, GeoAlchemy2, and psycopg/psycopg2.
235Create connections from named secrets without embedding or logging a connection
236URL:
238```python
239import os
240from sqlalchemy import URL, create_engine
242db_url = URL.create(
243 "postgresql+psycopg",
244 username=os.environ["GEOPANDAS_POSTGIS_USER"],
245 password=os.environ["GEOPANDAS_POSTGIS_PASSWORD"],
246 host=os.environ["GEOPANDAS_POSTGIS_HOST"],
247 port=int(os.environ["GEOPANDAS_POSTGIS_PORT"]),
248 database=os.environ["GEOPANDAS_POSTGIS_DATABASE"],
250engine = create_engine(db_url)
251```
253Read only those named variables (or secret-manager equivalents). Never print
254`db_url`, `engine.url`, exception payloads containing it, or the broader
255environment.
257Use parameterized values and trusted SQL identifiers:
259```python
260from sqlalchemy import text
262query = text(
263 "SELECT feature_id, geom FROM approved_schema.features "
264 "WHERE category = :category"
266chunks = geopandas.read_postgis(
267 query,
268 con=engine,
269 geom_col="geom",
270 params={"category": approved_category},
271 chunksize=10_000,
273```
275`read_postgis` infers one CRS from the SRID of the first geometry and assigns it
276to all rows unless `crs=` is supplied. Verify all geometries share the expected
277SRID. With `chunksize`, it returns an iterator; validate every chunk and enforce
278a total-row limit.
280For writes:
282```python
283with engine.begin() as connection:
284 gdf.to_postgis(
285 "derived_features",
286 con=connection,
287 schema="approved_schema",
288 if_exists="fail",
289 index=False,
290 chunksize=10_000,
291 )
292```
294- Default to `if_exists="fail"`.
295- `replace` drops an existing table and is destructive.
296- Validate schema/table/geometry column names against an allowlist; do not
297 interpolate user input.
298- GeoPandas 1.1.2 fixed SQL injection through a geometry-column name; remain on
299 a patched version and still validate identifiers. Version 1.1.4 added
300 further hardening; 1.2.0 includes both fixes.
301- Use least-privilege database roles and a transaction.

PostGIS without credential leakage

The following PostGIS examples are illustrative; no live database was tested. Required writing dependencies are SQLAlchemy, GeoAlchemy2, and psycopg/psycopg2. Create connections from named secrets without embedding or logging a connection URL:

import os
from sqlalchemy import URL, create_engine

db_url = URL.create(
    "postgresql+psycopg",
    username=os.environ["GEOPANDAS_POSTGIS_USER"],
    password=os.environ["GEOPANDAS_POSTGIS_PASSWORD"],
    host=os.environ["GEOPANDAS_POSTGIS_HOST"],
    port=int(os.environ["GEOPANDAS_POSTGIS_PORT"]),
    database=os.environ["GEOPANDAS_POSTGIS_DATABASE"],
)
engine = create_engine(db_url)

Read only those named variables (or secret-manager equivalents). Never print db_url, engine.url, exception payloads containing it, or the broader environment.

Use parameterized values and trusted SQL identifiers:

from sqlalchemy import text

query = text(
    "SELECT feature_id, geom FROM approved_schema.features "
    "WHERE category = :category"
)
chunks = geopandas.read_postgis(
    query,
    con=engine,
    geom_col="geom",
    params={"category": approved_category},
    chunksize=10_000,
)

read_postgis infers one CRS from the SRID of the first geometry and assigns it to all rows unless crs= is supplied. Verify all geometries share the expected SRID. With chunksize, it returns an iterator; validate every chunk and enforce a total-row limit.

For writes:

with engine.begin() as connection:
    gdf.to_postgis(
        "derived_features",
        con=connection,
        schema="approved_schema",
        if_exists="fail",
        index=False,
        chunksize=10_000,
    )
  • Default to if_exists="fail".
  • replace drops an existing table and is destructive.
  • Validate schema/table/geometry column names against an allowlist; do not interpolate user input.
  • GeoPandas 1.1.2 fixed SQL injection through a geometry-column name; remain on a patched version and still validate identifiers. Version 1.1.4 added further hardening; 1.2.0 includes both fixes.
  • Use least-privilege database roles and a transaction.
Raw lines 231 to 302
303## Fiona-to-pyogrio migration
305GeoPandas 1.0 changed the default engine from Fiona to pyogrio. Differences
306include:
308- schema/metadata keywords and driver options;
309- writing attribute-only tables;
310- handling empty geometries and unsupported field types;
311- datetime resolution/timezone behavior;
312- append and encoding behavior;
313- error/warning text and filter behavior.
315Set `engine=` explicitly for reproducibility and test round-trips before
316migration. Do not assume identical outputs merely because both engines use GDAL.

Fiona-to-pyogrio migration

GeoPandas 1.0 changed the default engine from Fiona to pyogrio. Differences include:

  • schema/metadata keywords and driver options;
  • writing attribute-only tables;
  • handling empty geometries and unsupported field types;
  • datetime resolution/timezone behavior;
  • append and encoding behavior;
  • error/warning text and filter behavior.

Set engine= explicitly for reproducibility and test round-trips before migration. Do not assume identical outputs merely because both engines use GDAL.

Raw lines 303 to 317
318## Export verification and provenance
320For every output:
3221. choose a new local path and explicit format/driver/layer;
3232. record source hashes, source layer, stack/native versions, CRS, precision,
324 repair, and transformation choices;
3253. record field names/types/nullability, geometry columns/types, stable ID,
326 index policy, encoding, dimensions, and expected losses;
3274. write, then reopen with an independent code path when feasible;
3285. compare row count, stable-ID set, null/empty/invalid/type counts, CRS, bounds
329 at protected precision, and representative attribute values;
3306. hash the completed artifact and store the audit separately.
332Use `scripts/vector_inventory.py` for redacted intake and
333`scripts/export_plan.py` for a non-executing output contract.

Export verification and provenance

For every output:

  1. choose a new local path and explicit format/driver/layer;
  2. record source hashes, source layer, stack/native versions, CRS, precision, repair, and transformation choices;
  3. record field names/types/nullability, geometry columns/types, stable ID, index policy, encoding, dimensions, and expected losses;
  4. write, then reopen with an independent code path when feasible;
  5. compare row count, stable-ID set, null/empty/invalid/type counts, CRS, bounds at protected precision, and representative attribute values;
  6. hash the completed artifact and store the audit separately.

Use scripts/vector_inventory.py for redacted intake and scripts/export_plan.py for a non-executing output contract.

Raw lines 318 to 334
Runs code scripts/vector_inventory.py :332
Runs code scripts/export_plan.py :333
335## Sources (verified 2026-10-01)
337- [GeoPandas reading and writing files](https://geopandas.org/en/stable/docs/user_guide/io.html).
338- [geopandas.read_file](https://geopandas.org/en/stable/docs/reference/api/geopandas.read_file.html).
339- [GeoDataFrame.to_file](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.to_file.html).
340- [GeoDataFrame.to_parquet](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.to_parquet.html).
341- [geopandas.read_parquet](https://geopandas.org/en/stable/docs/reference/api/geopandas.read_parquet.html).
342- [geopandas.read_postgis](https://geopandas.org/en/stable/docs/reference/api/geopandas.read_postgis.html).
343- [GeoDataFrame.to_postgis](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.to_postgis.html).
344- [Fiona-to-pyogrio migration](https://geopandas.org/en/stable/docs/user_guide/fiona_to_pyogrio.html).
345- [pyogrio introduction](https://pyogrio.readthedocs.io/en/stable/introduction.html).
346- [pyogrio API](https://pyogrio.readthedocs.io/en/stable/api.html).
347- [GeoParquet 1.1.0 specification](https://geoparquet.org/releases/v1.1.0/).
348- [GeoArrow specification](https://github.com/geoarrow/geoarrow).
349- [GeoPandas 1.1.2 security/bug-fix release](https://github.com/geopandas/geopandas/releases/tag/v1.1.2) — released 2025-12-22.
350- [GeoPandas 1.2.0 Arrow implementation](https://github.com/geopandas/geopandas/blob/v1.2.0/geopandas/io/arrow.py).
351- [pyogrio 0.13.0 metadata API source](https://github.com/geopandas/pyogrio/blob/v0.13.0/pyogrio/core.py).