Short answer
gdal_translate is GDAL's raster format-conversion and subsetting utility. It changes the container format (GeoTIFF, COG, PNG, VRT, NetCDF), the pixel data type, the band selection, the value range, and the compression of a raster — but it deliberately does not reproject. The moment you need a different coordinate reference system you switch to gdalwarp. Think of gdal_translate as a faithful copy with controlled edits, and gdalwarp as a resampling engine.
A useful mental model: gdal_translate works in the source pixel grid. Every output pixel maps to source pixels by simple selection or rescaling, never by a coordinate transform. That is why it is the right tool for "give me a DEFLATE-compressed COG of this DEM" or "extract bands 4-3-2 as an 8-bit JPEG-compressed image", and the wrong tool for "put this UTM raster into Web Mercator".
The command anatomy
The basic form is:
gdal_translate [options] source.tif destination.tif
The options that actually change pipeline behaviour, roughly in order of how often they matter:
-of <FORMAT>— output driver.GTiff,COG,VRT,PNG,JPEG,NetCDF,EHdr. The driver decides which-cooptions are legal.-ot <TYPE>— output data type:Byte,Int16,UInt16,Float32,Float64. Changing this without-scalewill truncate or overflow values.-b <n>— band selection, repeatable.-b 1 -b 2 -b 3reorders or subsets bands.-scale [src_min src_max [dst_min dst_max]]— linear rescale of values, almost always paired with-ot Byte.-srcwin/-projwin— crop by pixel window or georeferenced window.-a_nodata <value>/-a_srs <crs>— assign (not change) NoData or CRS metadata.-co KEY=VALUE— creation options passed to the driver (compression, tiling, predictor).-r <method>— resampling, but only used together with-outsize.
Worked example 1: a Cloud Optimized GeoTIFF from a survey DEM
You have a 1 m LiDAR DEM, dem_raw.tif, as an untiled Float32 GeoTIFF, and you want a COG that streams efficiently from object storage.
gdal_translate dem_raw.tif dem_cog.tif \
-of COG \
-co COMPRESS=DEFLATE \
-co PREDICTOR=3 \
-co BLOCKSIZE=512 \
-co RESAMPLING=BILINEAR \
-co OVERVIEW_RESAMPLING=BILINEAR
Why each flag matters:
PREDICTOR=3is the floating-point predictor. For continuous elevation it typically improves DEFLATE ratios by 20-40% versus no predictor. For categorical Byte data you would usePREDICTOR=2(horizontal differencing) or none.BLOCKSIZE=512sets the internal tile size. 256 or 512 are the common choices; larger tiles mean fewer HTTP range requests when a viewer pans, at the cost of reading more data per request.BILINEARresampling is correct for continuous surfaces. Never use it for thematic/classified rasters — useNEARESTthere to avoid inventing class codes that do not exist.
The COG driver writes the overviews and arranges the IFD layout so that an HTTP client can read the header, then fetch only the tiles it needs. You can confirm validity with the validate_cloud_optimized_geotiff.py script shipped with GDAL.
Worked example 2: scaling Float32 to Byte for a web preview
A continuous DEM cannot be drawn as 8-bit imagery without a value mapping. First read the real range:
gdalinfo -stats dem_cog.tif
Suppose it reports Minimum=412.0, Maximum=1987.0. To produce an 8-bit grayscale preview:
gdal_translate dem_cog.tif dem_8bit.tif \
-ot Byte \
-scale 412 1987 0 255 \
-co COMPRESS=DEFLATE
The critical detail is that -scale does not clamp by default: a pixel below 412 maps to a value below 0 and one above 1987 to a value above 255, then the -ot Byte cast truncates it — add -exponent 1 if you want the input genuinely clipped to 412-1987 first. And if you let GDAL pick the range with a bare -scale, it uses the band min/max, which means a single spurious -9999 NoData value still stored as data will blow out the entire stretch. Set NoData first, or pass explicit limits. For perceptually even relief, consider -exponent 0.5 for a gamma-style nonlinear stretch instead of a flat linear one.
Worked example 3: band subsetting and cropping
To pull a true-colour subset (bands 4,3,2 in a Landsat-style stack) over a study window expressed in map coordinates:
gdal_translate scene.tif rgb_subset.tif \
-b 4 -b 3 -b 2 \
-projwin 500000 4650000 520000 4630000 \
-co COMPRESS=DEFLATE -co TILED=YES
-projwin takes upper-left X/Y then lower-right X/Y in the raster's own CRS, so the Y values descend. Use -srcwin xoff yoff xsize ysize when you think in pixels instead. Both cut on existing pixel boundaries — there is no resampling and therefore no positional smearing.
translate vs warp vs the SQL path
| Need | Tool |
|---|---|
| Format change, compression, COG | gdal_translate |
| Data type / value rescale | gdal_translate -ot -scale |
| Band selection, simple crop | gdal_translate -b -srcwin |
| Reproject / resample to new CRS | gdalwarp |
| Mosaic many tiles | gdalwarp or gdalbuildvrt |
| Store raster in a database | raster2pgsql into PostGIS |
A common pipeline is gdalbuildvrt to stitch a virtual mosaic, then a single gdal_translate -of COG to materialise it — the VRT step costs almost no disk, and the translate step does the one expensive read/write.
Common pitfalls and why they happen
- Changing
-otwithout-scale. Casting Float32 reflectance (0.0-1.0) to Byte gives an image of all zeros, because the cast truncates toward zero. The fix is-scale 0 1 0 255. - Assuming
-a_srsreprojects. The-a_prefix means "assign/override metadata". It relabels the CRS tag without touching pixels. If the pixels are genuinely in another CRS, this corrupts georeferencing silently. Usegdalwarp -t_srsinstead. - Forgetting NoData on the way out. If the source NoData is not declared,
-scaleincludes it in the stretch and overviews average across it, producing dark halos at edges. Add-a_nodataor carry it through. - JPEG compression on data you will analyse.
-co COMPRESS=JPEGis lossy. Fine for an RGB visual product, never for a DEM or any band you will run analysis on. UseDEFLATE,LZW, orZSTDfor analytical rasters. - Untiled output feeding a tile server. Without
-co TILED=YES(implicit for COG) the file is stored in strips, so every tile request reads whole scanlines. Always tile rasters destined for streaming.
Validation
After conversion, do not trust file existence as success:
gdalinfo -stats -checksum dem_cog.tif
Confirm the band count, the data type, the CRS string, the NoData value, the min/max, and that overviews are present. Compare the checksum or statistics against the source where the operation should be value-preserving (a format-only change should leave statistics unchanged). For COGs, run the validator. When the output feeds an automated pipeline, capture the exact command in version control so the artefact is reproducible from the source.
Bathyl perspective
We treat gdal_translate as the boundary control on a raster pipeline: it is where data type, compression, and NoData get pinned down before anything downstream consumes the file. Getting those three right at conversion time prevents the slow, hard-to-trace failures — washed-out stretches, lossy analysis inputs, broken streaming — that surface much later in a client deliverable.
Related reading
- gdalinfo for Raster Quality Checks
- gdalwarp for Raster Reprojection
- GDAL Build VRT Explained
- Shapefile vs GeoPackage vs GeoJSON
- Spatial data products