GDAL-RASTER-REPROJECT(1)

🌸 GDAL-RASTER-REPROJECT 🌸

okay bestie, welcome to gdal-raster-reproject(1) β€” section 1 of the GDAL manual, and it's giving main character energy for real. this is the one that takes your raster and yeets it into a whole new projection. let's get into it πŸ’…


✨ NAME ✨

gdal-raster-reproject β€” Reproject a raster dataset

Added in version 3.11. (yes she's new, yes she's iconic πŸŽ€)


🫢 SYNOPSIS 🫢

Usage: gdal raster reproject [OPTIONS] <INPUT> <OUTPUT>

Reproject a raster dataset.

πŸ’– Positional arguments

  • -i, --input <INPUT> β€” Input raster datasets [required] [not available in pipelines]
  • -o, --output <OUTPUT> β€” Output raster dataset [required] [not available in pipelines]

🌈 Common Options

  • -h, --help β€” Display help message and exit
  • --json-usage β€” Display usage as JSON document and exit
  • --config <KEY>=<VALUE> β€” Configuration option [may be repeated]
  • -q, --quiet β€” Quiet mode (no progress bar or warning message) [not available in pipelines]

⭐ Options

  • -f, --of, --format, --output-format <OUTPUT-FORMAT> β€” Output format ("GDALG" allowed) [not available in pipelines]
  • --co, --creation-option <KEY>=<VALUE> β€” Creation option [may be repeated] [not available in pipelines]
  • --overwrite β€” Whether overwriting existing output dataset is allowed [not available in pipelines]
    • Mutually exclusive with --append
  • --append β€” Append as a subdataset to existing output [not available in pipelines]
    • Mutually exclusive with --overwrite
  • -s, --input-crs <INPUT-CRS> β€” Input CRS
  • --like <DATASET> β€” Dataset to use as a template for target bounds, CRS, size and nodata
  • -d, --output-crs <OUTPUT-CRS> β€” Output CRS
  • -r, --resampling <RESAMPLING> β€” Resampling method. RESAMPLING=nearest|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum (default: nearest)
  • --resolution <xres>,<yres> β€” Target resolution (in destination CRS units)
    • Mutually exclusive with --size
  • --size <width>,<height> β€” Target size in pixels
    • Mutually exclusive with --resolution
  • --bbox <BBOX> β€” Target bounding box (in destination CRS units)
  • --bbox-crs <BBOX-CRS> β€” CRS of target bounding box
  • -j, --num-threads <NUM-THREADS> β€” Number of jobs (or ALL_CPUS) (default: ALL_CPUS)

πŸ¦‹ Advanced Options

  • --if, --input-format <INPUT-FORMAT> β€” Input formats [may be repeated] [not available in pipelines]
  • --oo, --open-option <KEY>=<VALUE> β€” Open options [may be repeated] [not available in pipelines]
  • --target-aligned-pixels β€” Round target extent to target resolution
  • --input-nodata <INPUT-NODATA> β€” Set nodata values for input bands ('None' to unset). [1.. values]
  • --output-nodata <OUTPUT-NODATA> β€” Set nodata values for output bands ('None' to unset). [1.. values]
  • --add-alpha β€” Adds an alpha mask band to the destination when the source raster have none.
  • --wo, --warp-option <NAME>=<VALUE> β€” Warping option(s) [may be repeated]
  • --to, --transform-option <NAME>=<VALUE> β€” Transform option(s) [may be repeated]
  • --et, --error-threshold <ERROR-THRESHOLD> β€” Error threshold

πŸ’œ DESCRIPTION πŸ’œ

so, gdal raster reproject is literally here to reproject a raster dataset. and the way it can reproject to any supported projection? obsessed πŸ”₯

fr fr, first thing she's gotta do is figure out the extent and resolution of the output β€” unless you already told her with --bbox and --resolution. if you didn't, she figures it out by transforming a sample of points from the source CRS to the destination CRS. the whole procedure is documented over in GDALSuggestedWarpOutput(), iykyk. and β€” plot twist β€” if you hand her multiple inputs, the output extent gets calculated to cover all of them, at a resolution consistent with the highest-resolution input. queen behavior, honestly πŸ‘‘

once the dimensions of the output image are locked in, she divides the output image into chunks that can be processed independently. then she iterates over scanlines in those chunks, and for every single output pixel she determines a rectangular region of source pixels that contribute to the value of that output pixel. the dimensions of this rectangular region are typically determined by estimating the relative scales of the source and destination raster β€” but you can manually specify them too (see the docs for the XSCALE parameter in GDALWarpOptions::papszWarpOptions).

⚠️ and because that source region is a simple rectangle, it is not possible for an output pixel to be associated with source pixels from both sides of the antimeridian or pole (when transforming from geographic coordinates). that's just the vibe, no cap.

that rectangular region of source pixels then gets handed to a function that performs the resampling algorithm you picked with --resampling. depending on the resampling algorithm and the relative scales of the source and destination rasters, source pixels may be weighted either:

  • by the approximate fraction of the source pixel that is covered by the destination pixel (e.g., "mean" and "sum" resampling), or
  • by horizontal and vertical Cartesian distances between the center of the source pixel and the center of the target pixel (e.g., bilinear or cubic spline resampling).

in that second case, the relative weight of an individual source pixel is determined by the product of the weights determined for its row and column β€” the diagonal Cartesian distance is not calculated. periodt. πŸ’…

also!! this subcommand is available as a potential step of gdal raster pipeline ✨ so you can slot her right into your little pipeline moment 🧚


more coming, bestie β€” this page keeps going 🫢

✨ PROGRAM-SPECIFIC OPTIONS ✨

okay so this is the part where it gets specific to this program, and honestly? iconic behavior. buckle up πŸŽ€

  • --add-alpha β€” makes an output alpha band so you can tell which pixels are nodata (aka unset / transparent). The value 0 means fully transparent, periodt. For the fully opaque pixels, the max value of that alpha band depends on the data type and on whether the NBITS band metadata item exists. If NBITS is there β†’ max is 2^NBITS-1. If it's not set and your alpha band is UInt16 (or Int16), you get 65535 (or 32767). Otherwise? 255. And you can always override the whole thing with --wo DST_ALPHA_MAX=<value>. Obsessed with the flexibility πŸ’…
  • --bbox <xmin>,<ymin>,<xmax>,<ymax> β€” sets the georeferenced extents of the output file you're about to create (in the target SRS by default, or in whatever SRS you gave to --bbox-crs).
  • --bbox-crs <BBOX-CRS> β€” tells the program which SRS to use when reading the coords you passed to --bbox. no cap, this one confuses people, so listen up 🫢

The CRSs you can pass are literally anything supported by the OGRSpatialReference.SetFromUserInput() call, which includes EPSG Projected, Geographic or Compound CRS (like EPSG:4296), a well known text (WKT) CRS definition, PROJ.4 declarations, or the name of a .prj file containing a WKT CRS definition.

And here's the twist: if the SRS has an explicit vertical datum that points to a geoid grid, and the input dataset is a single band dataset, a vertical correction gets applied to the values of the dataset. 🌊

Do not mix this up with --output-crs β€” that one is the target SRS of the output dataset. --bbox-crs is just a lil convenience, like when you know your output coordinates in a geodetic long/lat SRS but you still want your result in a projected coordinate system. it's giving practical queen πŸ‘‘

  • --like <DATASET> β€” names a GDAL input dataset that acts as a template for the default values of these options: --size, --resolution, --output-crs and --bbox. But heads up: the pixel values are not copied, and the spatial registration of that template dataset (through stuff like GCP, RPC or geolocation array) is ignored. so it's a vibe reference, not a data reference πŸ’€
  • --et, --error-threshold <ERROR-THRESHOLD> β€” the error threshold for the transformation approximation, measured as a number of source pixels. Defaults to 0.125 pixels β€” unless the RPC_DEM transformer option is specified, in which case an exact transformer is used, i.e. --error-threshold=0. precision era πŸ”₯
  • -j, --num-threads <value> β€” added in version 3.12. It's the number of jobs to run at once. Default: the number of CPUs detected. let her cook in parallel 🌈
  • --input-crs, -s <INPUT-CRS> β€” sets the input spatial reference. If you don't specify it, the SRS found in the input dataset gets used instead. slay, the program figured it out for you ✨

Same as before: the CRSs you can pass are anything supported by the OGRSpatialReference.SetFromUserInput() call, which includes EPSG Projected, Geographic or Compound CRS (like EPSG:4296), a well known text (WKT) CRS definition, PROJ.4 declarations, or the name of a .prj file containing a WKT CRS definition.

And again: if the SRS has an explicit vertical datum pointing to a geoid grid, and the input dataset is a single band dataset, a vertical correction will be applied to the values of the dataset. consistency is the whole aesthetic πŸ’œ

  • --input-nodata <NODATA> β€” sets nodata masking values for input bands (yes, you can give different values for each band, main character energy). If you supply more than one value, quote all of them so they stay together as a single operating system argument β€” otherwise the shell will snatch them apart 😭 Masked values will not be used in interpolation (the deets are in Nodata / source validity mask handling).

Use a value of None to ignore the intrinsic nodata settings on the source dataset.

When this option is set to a non-None value, it causes the UNIFIED_SRC_NODATA warping option (peep GDALWarpOptions::papszWarpOptions) to be set to YES, if it wasn't explicitly set.

If --input-nodata is not explicitly set, but the source dataset does have nodata values, they'll be taken into account, with UNIFIED_SRC_NODATA at PARTIAL by default. so it's never just ignoring them, ok? πŸ¦‹

  • --output-crs, -d, <OUTPUT-CRS> β€” sets the output spatial reference. If not specified, the SRS found in the input dataset will be used.

You already know the drill: the CRSs you can pass are anything supported by the OGRSpatialReference.SetFromUserInput() call, which includes EPSG Projected, Geographic or Compound CRS (like EPSG:4296), a well known text (WKT) CRS definition, PROJ.4 declarations, or the name of a .prj file containing a WKT CRS definition.

And one more time for the back row: if the SRS has an explicit vertical datum that points to a geoid grid, and the input dataset is a single band dataset, a vertical correction will be applied to the values of the dataset. 🌸

  • --output-nodata <NODATA> β€” sets nodata values for the output bands (again, different values per band is allowed πŸ’…). If you give more than one value, quote them all so they stay one single operating system argument. New files get initialized to this value, and if possible, the nodata value will be recorded in the output file. Use None to make absolutely sure nodata is not defined. If you don't use this argument at all, nodata values get copied from the source dataset.
⚠️ real talk: a bunch of output formats β€” GeoTIFF included β€” do not support different per-band nodata values. They only take a single one for all the bands. so don't be shocked πŸ™ˆ
  • -r, --resampling <RESAMPLING> β€” the resampling method to use, bestie. The available methods are:
    • nearest β€” nearest neighbour resampling. This is the default: fastest algorithm, worst interpolation quality. speed over beauty, honestly relatable 😭
    • bilinear β€” bilinear resampling.
    • cubic β€” cubic resampling.
    • cubicspline β€” cubic spline resampling.
    • lanczos β€” Lanczos windowed sinc resampling.
    • average β€” average resampling, computes the weighted average of all non-NODATA contributing pixels.
    • rms β€” root mean square / quadratic mean of all non-NODATA contributing pixels.
    • mode β€” mode resampling, selects the value which appears most often of all the sampled points. If there's a tie, the first value identified as the mode will be selected. no drama, first one wins πŸ‘‘
    • max β€” maximum resampling, selects the maximum value from all non-NODATA contributing pixels.
    • min β€” minimum resampling, selects the minimum value from all non-NODATA contributing pixels.
    • med β€” median resampling, selects the median value of all non-NODATA contributing pixels.
    • q1 β€” first quartile resampling, selects the first quartile value of all non-NODATA contributing pixels.
    • q3 β€” third quartile resampling, selects the third quartile value of all non-NODATA contributing pixels.
    • sum β€” compute the weighted sum of all non-NODATA contributing pixels.
NOTE: when downsampling happens (that's when you use --resolution or --size), any existing overviews on the source image β€” internal/implicit or external β€” will be used by default, by picking the closest overview to the desired output resolution. And lowkey important: the resampling method used to create those overviews is generally not the one you specify through the -r option. so don't come for me when it looks different 😀
  • --resolution <xres>,<yres> β€” sets the output file resolution (in target georeferenced units).

If you don't specify it (and it's not deduced from --size), then in the general case the program will generate an output raster with xres=yres. symmetric queen ✨

But if neither --resolution nor --size is specified, and no reprojection is involved (and yes that includes taking geolocation arrays or RPC into account), then the resolution of the source file(s) will be preserved. (in a previous version, an output raster with xres=yres was always generated β€” those were the dark ages πŸ’€)

Mutually exclusive with --size. pick a lane, babe πŸ’…

  • --size <width>,<height> β€” sets the output file size in pixels and lines. If you set width or height to 0, the other dimension will be guessed from the computed resolution. the program's got you 🫢

Mutually exclusive with --resolution.

  • --target-aligned-pixels β€” aligns the coordinates of the extent of the output file to the values of the --resolution, so that the aligned extent includes the minimum extent. (edge lines/columns that get detected as blank, before the actual warping, will be removed.) "Alignment" here means that xmin / resx, ymin / resy, xmax / resx and ymax / resy are all integer values. so satisfying, honestly πŸŽ€
  • --to, --transform-option <NAME>=<VALUE> β€” set a transformer option suitable to pass to GDALCreateGenImgProjTransformer2(). For RPC-specific options, go look at GDALCreateRPCTransformerV2().

And if you wanna match the gdalwarp -rpc option, you use --to METHOD=RPC. iykyk 🌟

  • --wo, --warp-option <NAME>=<VALUE> β€” set a warp option. The GDALWarpOptions::papszWarpOptions docs show you all the options. You can list multiple options too. stack them like accessories πŸ’œ

πŸ’– STANDARD OPTIONS πŸ’–

  • --append β€” glues your input raster onto an existing output file as a brand-new subdataset. Heads up bestie: this only works with drivers that actually support adding subdatasets, like GTiff -- the GeoTIFF File Format -- and GPKG -- the GeoPackage raster. Also, if the output dataset doesn't exist yet, this will just create it for you. Iconic behaviour honestly.
  • --co, --creation-option <NAME>=<VALUE> β€” soooo many formats have optional creation options that let you control the little details of the file being made. Like, the GeoTIFF driver has creation options for compression, and for whether the file should be tiled. ✨

Can be given more than once (may be repeated).

Which creation options exist depends entirely on the format driver, and some simple formats have literally zero creation options. You can list what a format supports with the --formats command line option, but fr fr the documentation for the format itself is the definitive source of truth on driver creation options. Peep the Raster drivers format specific documentation for the legal creation options of each format. πŸ‘‘

  • --if, --input-format <format> β€” the format/driver name you want it to try opening your input file(s) with. Usually you don't need this at all, but it's a lifesaver for skipping automatic driver detection when that detection fails to pick the right driver. You can repeat this option several times to list several candidate drivers. Careful though: it does not force those drivers to open the dataset. Some drivers have requirements around file extensions, and they will simply not cooperate. πŸ™ˆ

May be repeated.

  • --oo, --open-option <NAME>=<VALUE> β€” dataset open option, and yes it is format specific.

May be repeated.

  • -f, --of, --format, --output-format <OUTPUT-FORMAT> β€” which output raster format to use. You can get the allowed values with:
gdal --formats | grep raster | grep rw | sort
  • --overwrite β€” lets the program overwrite an existing target file or dataset. Without it, the default is that gdal just errors out the moment the target file or dataset already exists. No sneaky overwriting around here. πŸ’…
  • -q, --quiet β€” suppresses the progress bar and some warning messages. For when you want the calm, silent, main-character processing vibe. 🀫

🫢 NODATA / SOURCE VALIDITY MASK HANDLING 🫢

Invalid values in the source pixels β€” whether that's from a nodata value metadata set on the source band, a mask band, an alpha band (and for an alpha band, a value of 0 means invalid; other values get used for blending values), or from using --input-nodata β€” will not be used in interpolation. The exact way that gets taken into account depends on the resampling kernel, so let's break it all the way down:

  • for nearest resampling: for each target pixel, the coordinate of its center gets projected back to source coordinates, and the source pixel containing that coordinate is identified. If that source pixel is invalid, the target pixel is considered as nodata. Simple as. πŸ’€
  • for bilinear, cubic, cubicspline and lanczos: for each target pixel, the coordinate of its center is projected back to source coordinates and a corresponding source pixel is identified. If that source pixel is invalid, the target pixel is considered as nodata (and in this case, valid pixels within the kernel radius would not be considered). Now β€” given that these resampling kernels have a non-null kernel radius, that source pixel is just one among several other source pixels, and it might be possible that there are invalid values in those other contributing source pixels too. The weights used to take those invalid values into account get set to zero so they're just ignored. Bye. ✌️
  • for the other resampling methods: source pixels contributing to the target pixel are ignored if they're invalid. Only the valid ones get taken into account. And if there are none at all, the target pixel is considered as nodata.

🌈 APPROXIMATE TRANSFORMATION 🌈

By default gdal raster reproject uses a linear approximator for the transformations, with a permitted error of 0.125 pixels in the source dataset. For each processing chunk, the approximator precisely transforms three points per output scanline (the start, the middle, and the end) from a row and column in the output dataset to a row and column in the source dataset. Then it compares a linear approximation of the center point coordinates to the precisely transformed value. If the sum of the horizontal and vertical errors is less than the error threshold, the remaining source points get approximated using linear interpolation between the start and middle point, and between the middle and end point. If the error exceeds the threshold, the scanline is split into two sections, and the approximator is recursively applied to each section until the error is less than the threshold or all points have been exactly computed. It's giving "divide and conquer but make it math". 🧚

Note that a processing chunk does not necessarily correspond to a whole destination scanline. If the output dataset is tiled, its shape will typically be one or several tiles. The OPTIMIZE_SIZE warping option can also influence the shape of the processing chunk. Consequently, for a single (non-zero) value of the error threshold, the linear approximation might result in slightly different values in coordinate transformation β€” but all of them stay within the permitted error. 🌸

You can control the error threshold (in source dataset pixels) with the --error-threshold switch. And if you want to compare a true pixel-by-pixel reprojection, use --error-threshold=0, which disables this approximator entirely. No approximation, only truth. πŸ‘‘


πŸ’… FREQUENTLY ASKED QUESTIONS πŸ’…

Q1. Why does the quality of the output looks so bad (no anti-aliasing)?

Did you specify a resampling method with --resampling other than the default nearest neighbour? Like, asking for a reason bestie. 😭

Q2. Why do I get slightly different results whether the output dataset is tiled or not?

This is related to the fact that an approximate coordinate transformation is used by default to speed-up computation. If you want to get the same results whether the output is tiled or not, set --error-threshold to zero. Note, however, that this will only work for relatively small images; other factors can still result in different results. See the following question (Q3). πŸ™ƒ

Q3. Why do I observe artifacts, that look like resolution changes and are aligned with rectangular areas of the output raster, when warping sufficiently large rasters, particularly in areas where the reprojection involves significant deformation and only with non-nearest resampling?

The warping engine operates on rectangular areas of the output dataset (generally aligned with tile boundaries for a compressed tile dataset).

During reprojection, a single source pixel does not generally correspond to a single output pixel. The resampling method must therefore properly account for this and compute a ratio between the number of source and target pixels in the horizontal (X) and vertical (Y) directions. These ratios are computed per warping chunk. This maximizes the local quality of the warping but has the downside of creating visual discontinuities between warping chunks. Classic tradeoff, no cap. πŸ’€

If you favor a seamless result, you may manually specify the XSCALE and YSCALE warping options with --wo. The XSCALE (resp. YSCALE) value is the ratio expressing the resampling factor, i.e. the number of destination pixels per source pixel, along the horizontal (resp. vertical) axis. It equals to one for no resampling, is below one for downsampling, and above one for upsampling. So basically: 1 = same size, <1 = shrinking, >1 = growing. Slay. ✨


πŸ¦‹ GDALG OUTPUT (ON-THE-FLY / STREAMED DATASET) πŸ¦‹

This program supports serializing the command line as a JSON file using the GDALG output format. The resulting file can then be opened as a raster dataset using the GDALG: GDAL Streamed Algorithm driver, and apply the specified pipeline in a on-the-fly / streamed way. Streamed pipeline energy, we love to see it. 🌈


⭐ RETURN STATUS CODE ⭐

The program returns status code 0 in case of success, and non-zero in case of error (and jsyk, non-blocking errors emitted as warnings are considered as a successful execution). So 0 = we ate, anything else = we did not. πŸ”₯


πŸŽ€ EXAMPLES πŸŽ€

Example 1: Reproject a GeoTIFF file to CRS EPSG:32632 ("WGS 84 / UTM zone 32N")

$ gdal raster reproject --output-crs=EPSG:32632 in.tif out.tif --overwrite

Example 2: Converting a raster that uses an embedded CRS without a known identifier

In this example, the input file has an embedded CRS defined in WKT2 but without an associated identifier, and it will be reprojected to the known ESRI:54052 CRS identifier. πŸ—ΊοΈ

Using "PROFILE=BASELINE" creates a PAM file that stores the CRS using the ESRI:54052 identifier, instead of embedding CRS information in GeoTIFF metadata tags. See GTiff PROFILE.

$ gdal raster reproject --creation-option "PROFILE=BASELINE" --output-crs=ESRI:54052 input.tif output.tif --overwrite

πŸ‘‘ AUTHOR πŸ‘‘

Even Rouault <even.rouault@spatialys.com>

1998-2026


and that's the whole page, bestie!! this lil masterpiece is from the GDAL-RASTER-REPROJECT(1) manual page, dated Aug 18, 2026 β€” go forth and reproject responsibly, the vibes are immaculate, periodt πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from