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:xxxx— transforms 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:xxxx— assigns (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.boreholeswrites into thegeologyschema with table nameboreholes.-lco GEOMETRY_NAME=geomnames the geometry column predictably.-lco SPATIAL_INDEX=GISTbuilds the GiST index on load, so bbox queries are fast immediately.-nlt PROMOTE_TO_MULTIpromotes mixed Polygon/MultiPolygon (very common in real data) to a single MultiPolygon type, avoiding "geometry type mismatch" failures mid-load.-overwritedrops and recreates the table; use-appendinstead 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 -alon input and output, orSELECT count(*)in PostGIS. A mismatch means a filter, encoding, or geometry-type rejection dropped rows. - CRS is what you intended.
ogrinfo -soshows the output SRS; confirm it is the-t_srstarget, not the source. - Geometry validity. Add
-makevalidif the output will go into PostGIS or be used for overlay analysis, and verify withST_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-1if 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_srswhen 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_srstransform has no starting point and either fails or assumes WGS84 incorrectly. - Shapefile field truncation. Output to
.shpsilently 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_MULTIfails 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
- gdalwarp for Raster Reprojection
- gdalinfo for Raster Quality Checks
- Shapefile vs GeoPackage vs GeoJSON
- Spatial data products