Short answer

ogr2ogr is GDAL's command-line tool for converting, reprojecting, filtering, and reshaping vector data. The minimal form is ogr2ogr -f <output_driver> output input, where -f names the format driver and the output comes before the input — the single most common ordering mistake. Because the whole operation is one line of text, it documents exactly how a dataset was produced and can be rerun against fresh source data without clicking through menus.

ogr2ogr -f GPKG wells.gpkg wells.shp

How ogr2ogr thinks about data

Internally ogr2ogr reads from a source driver, applies an optional set of transformations (reprojection, filtering, SQL, geometry edits), and writes through a target driver. List the drivers your build supports with ogrinfo --formats. Inspect any source before converting — schema, geometry type, feature count, and CRS — with:

ogrinfo -so -al wells.shp

-so means "summary only" (no per-feature dump) and -al means "all layers." This shows the layer name, geometry type, feature count, extent, declared SRS, and the field list with types. Run it first every time; half of all conversion bugs are visible here before you convert anything.

Format conversion: the everyday cases

Shapefile to GeoPackage. GeoPackage is usually the better delivery container — one file, no 10-character field-name limit, no 2 GB ceiling, UTF-8 by default:

ogr2ogr -f GPKG output.gpkg input.shp -nln boreholes

-nln ("new layer name") sets the layer name inside the container. Without it the layer inherits the input filename.

Multiple layers into one GeoPackage. Append rather than overwrite with -update -append, or just run repeated commands against the same .gpkg; each adds a layer.

GeoJSON output. Per RFC 7946, GeoJSON should be WGS84 lon/lat, so combine the conversion with a reprojection:

ogr2ogr -f GeoJSON out.geojson in.gpkg -t_srs EPSG:4326 -lco RFC7946=YES

-lco RFC7946=YES makes the driver emit strictly RFC 7946-compliant output (right-hand-rule winding, WGS84).

CSV with geometry to a spatial layer. Point the driver at the X/Y columns:

ogr2ogr -f GPKG points.gpkg points.csv -oo X_POSSIBLE_NAMES=lon -oo Y_POSSIBLE_NAMES=lat -a_srs EPSG:4326

-oo are open options for the input driver; -a_srs assigns a CRS to data that has none (it does not transform).

Reprojection: -t_srs, -s_srs, and -a_srs

These three flags are constantly confused, and the difference matters:

  • -t_srs EPSG:xxxxtransforms coordinates into a target CRS. Use this to actually move data between coordinate systems.
  • -s_srs EPSG:xxxx — declares the source CRS. Only needed when the source has no embedded CRS, or its embedded CRS is wrong and you want to override the assumption for a transform.
  • -a_srs EPSG:xxxxassigns (relabels) a CRS without moving any coordinate. This is the equivalent of "define projection," used when metadata is missing but the numbers are already correct.

A correct transform from British National Grid to Web Mercator:

ogr2ogr -t_srs EPSG:3857 out.gpkg in.gpkg

If the source had no CRS but you know it is EPSG:27700:

ogr2ogr -s_srs EPSG:27700 -t_srs EPSG:3857 out.gpkg in.gpkg

For datum shifts where accuracy matters (e.g. NAD27 to NAD83, or any transform crossing datums), GDAL uses PROJ's pipeline. You can pin a specific transformation with -ct or allow grid-based shifts by ensuring the PROJ data grids are installed; otherwise PROJ falls back to a lower-accuracy ballpark transform, which can introduce metres of error silently.

Filtering and reshaping

Attribute filter:

ogr2ogr -where "depth_m > 100 AND status = 'open'" out.gpkg in.gpkg

Spatial filter (a bounding box in the source CRS):

ogr2ogr -spat -6 47 -2 50 out.gpkg in.gpkg

Column selection:

ogr2ogr -select "well_id,depth_m,lithology" out.gpkg in.gpkg

Full SQL with the OGR SQL or SQLite dialect, for renaming, computing fields, and casting:

ogr2ogr -f GPKG out.gpkg in.gpkg \
  -dialect SQLite \
  -sql "SELECT well_id, depth_m * 3.28084 AS depth_ft, geom FROM wells WHERE depth_m > 100"

The SQLite dialect (vs the default OGR SQL) unlocks spatial functions like ST_Centroid, ST_Buffer, and joins, which is often reason enough to use it.

Worked example: load to PostGIS

A clean load that survives reuse:

ogr2ogr -f PostgreSQL \
  PG:"host=localhost dbname=geo user=loader password=$PGPASS" \
  boreholes.gpkg \
  -nln geology.boreholes \
  -t_srs EPSG:3857 \
  -lco GEOMETRY_NAME=geom \
  -lco FID=id \
  -lco SPATIAL_INDEX=GIST \
  -nlt PROMOTE_TO_MULTI \
  -overwrite

What each piece does:

  • -nln geology.boreholes writes into the geology schema with table name boreholes.
  • -lco GEOMETRY_NAME=geom names the geometry column predictably.
  • -lco SPATIAL_INDEX=GIST builds the GiST index on load, so bbox queries are fast immediately.
  • -nlt PROMOTE_TO_MULTI promotes mixed Polygon/MultiPolygon (very common in real data) to a single MultiPolygon type, avoiding "geometry type mismatch" failures mid-load.
  • -overwrite drops and recreates the table; use -append instead to add rows to an existing one.

Validation before you trust the output

A command exiting 0 is not proof of success. Check:

  • Feature counts match. ogrinfo -so -al on input and output, or SELECT count(*) in PostGIS. A mismatch means a filter, encoding, or geometry-type rejection dropped rows.
  • CRS is what you intended. ogrinfo -so shows the output SRS; confirm it is the -t_srs target, not the source.
  • Geometry validity. Add -makevalid if the output will go into PostGIS or be used for overlay analysis, and verify with ST_IsValid.
  • Attribute integrity. Field names get truncated to 10 chars on shapefile output, and types can coerce. Diff the schema with ogrinfo.
  • Encoding. Non-ASCII attributes from a Latin-1 shapefile may garble. Set source encoding with -oo ENCODING=ISO-8859-1 if accents look wrong.

Common pitfalls and why they happen

  • Arguments in the wrong order. ogr2ogr expects output input. Reversing them either errors or, worse, treats your data as the destination. The convention is unusual, which is why it bites everyone once.
  • Using -a_srs when you meant -t_srs. This relabels coordinates without moving them, so a layer lands in the wrong place while reporting the "right" CRS. It is the command-line version of the "assign vs reproject" trap.
  • No CRS on the source. With nothing declared and no -s_srs, a -t_srs transform has no starting point and either fails or assumes WGS84 incorrectly.
  • Shapefile field truncation. Output to .shp silently shortens field names to 10 characters and can collide them. Use GeoPackage to avoid it.
  • Single vs multi geometry. Loading a layer with both Polygon and MultiPolygon features without -nlt PROMOTE_TO_MULTI fails partway. The fix is to promote up front.

Bathyl perspective

When a conversion will be repeated against future data deliveries, we keep the exact ogr2ogr invocation in the project's pipeline scripts, with the source version and CRS recorded alongside it. The command is the documentation: anyone can read it and know precisely how the published layer was produced, and rerun it byte-for-byte when the source updates.

Related reading

Sources