diff --git a/apps/gdalalg_raster_reproject.cpp b/apps/gdalalg_raster_reproject.cpp index 5e09c07bd204..141add06f535 100644 --- a/apps/gdalalg_raster_reproject.cpp +++ b/apps/gdalalg_raster_reproject.cpp @@ -48,13 +48,14 @@ GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm(bool standaloneStep) .SetMaxCount(2) .SetRepeatedArgAllowed(false) .SetDisplayHintAboutRepetition(false) - .SetMetaVar(","); + .SetMetaVar(",") + .SetMutualExclusionGroup("resolution-size"); resArg.AddValidationAction( [&resArg]() { const auto &val = resArg.Get>(); CPLAssert(val.size() == 2); - if (!(val[0] >= 0 && val[1] >= 0)) + if (!(val[0] > 0 && val[1] > 0)) { CPLError(CE_Failure, CPLE_AppDefined, "Target resolution should be strictly positive."); @@ -63,7 +64,31 @@ GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm(bool standaloneStep) return true; }); + auto &sizeArg = AddArg("size", 0, _("Target size in pixels"), &m_size) + .SetMinCount(2) + .SetMaxCount(2) + .SetRepeatedArgAllowed(false) + .SetDisplayHintAboutRepetition(false) + .SetMetaVar(",") + .SetMutualExclusionGroup("resolution-size"); + sizeArg.AddValidationAction( + [&sizeArg]() + { + const auto &val = sizeArg.Get>(); + CPLAssert(val.size() == 2); + if (!(val[0] >= 0 && val[1] >= 0)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Target size should be positive or 0."); + return false; + } + return true; + }); + AddBBOXArg(&m_bbox, _("Target bounding box (in destination CRS units)")); + AddArg("bbox-crs", 0, _("CRS of target bounding box"), &m_bboxCrs) + .SetIsCRSArg() + .AddHiddenAlias("bbox_srs"); AddArg("target-aligned-pixels", 0, _("Round target extent to target resolution"), @@ -105,6 +130,12 @@ bool GDALRasterReprojectAlgorithm::RunStep(GDALProgressFunc, void *) aosOptions.AddString(CPLSPrintf("%.17g", m_resolution[0])); aosOptions.AddString(CPLSPrintf("%.17g", m_resolution[1])); } + if (!m_size.empty()) + { + aosOptions.AddString("-ts"); + aosOptions.AddString(CPLSPrintf("%d", m_size[0])); + aosOptions.AddString(CPLSPrintf("%d", m_size[1])); + } if (!m_bbox.empty()) { aosOptions.AddString("-te"); @@ -113,6 +144,11 @@ bool GDALRasterReprojectAlgorithm::RunStep(GDALProgressFunc, void *) aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[2])); aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[3])); } + if (!m_bboxCrs.empty()) + { + aosOptions.AddString("-te_srs"); + aosOptions.AddString(m_bboxCrs.c_str()); + } if (m_targetAlignedPixels) { aosOptions.AddString("-tap"); diff --git a/apps/gdalalg_raster_reproject.h b/apps/gdalalg_raster_reproject.h index 91086af83f78..3b6f63806241 100644 --- a/apps/gdalalg_raster_reproject.h +++ b/apps/gdalalg_raster_reproject.h @@ -45,6 +45,8 @@ class GDALRasterReprojectAlgorithm /* non final */ std::string m_resampling{}; std::vector m_resolution{}; std::vector m_bbox{}; + std::string m_bboxCrs{}; + std::vector m_size{}; bool m_targetAlignedPixels = false; }; diff --git a/autotest/utilities/test_gdalalg_raster_reproject.py b/autotest/utilities/test_gdalalg_raster_reproject.py index ddcbbff5121b..ce0a6aff4a72 100755 --- a/autotest/utilities/test_gdalalg_raster_reproject.py +++ b/autotest/utilities/test_gdalalg_raster_reproject.py @@ -62,3 +62,40 @@ def test_gdalalg_raster_reproject_failure(tmp_vsimem): out_filename, ], ) + + +def test_gdalalg_raster_reproject_size(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.tif") + + pipeline = get_reproject_alg() + pipeline.ParseRunAndFinalize( + [ + "--size=10,0", + "../gcore/data/byte.tif", + out_filename, + ], + ) + + with gdal.OpenEx(out_filename) as ds: + assert ds.RasterXSize == 10 + assert ds.RasterYSize == 10 + + +def test_gdalalg_raster_reproject_bbox_crs(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.tif") + + pipeline = get_reproject_alg() + pipeline.ParseRunAndFinalize( + [ + "--bbox=-117.638051657173,33.8904636339659,-117.627303823822,33.8995379597727", + "--bbox-crs=EPSG:4267", + "../gcore/data/byte.tif", + out_filename, + ], + ) + + with gdal.OpenEx(out_filename) as ds: + assert ds.RasterXSize == 17 + assert ds.RasterYSize == 17 diff --git a/doc/source/programs/gdal_raster_reproject.rst b/doc/source/programs/gdal_raster_reproject.rst index 6a27f2dde6de..2bcbf281265d 100644 --- a/doc/source/programs/gdal_raster_reproject.rst +++ b/doc/source/programs/gdal_raster_reproject.rst @@ -40,7 +40,11 @@ Synopsis -d, --dst-crs Destination CRS -r, --resampling Resampling method. RESAMPLING=nearest|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum --resolution , Target resolution (in destination CRS units) + Mutually exclusive with --size + --size , Target size in pixels + Mutually exclusive with --resolution --bbox ,,, Target bounding box (in destination CRS units) + --bbox-crs CRS of target bounding box --target-aligned-pixels Round target extent to target resolution Advanced Options: @@ -52,6 +56,113 @@ Description ----------- :program:`gdal raster reproject` can be used to reproject a raster dataset. +The program can reproject to any supported projection. + +Standard options +++++++++++++++++ + +.. include:: gdal_options/of_raster_create_copy.rst + +.. include:: gdal_options/co.rst + +.. include:: gdal_options/overwrite.rst + +.. option:: -s, --src-crs + + Set source spatial reference. If not specified the SRS found in the input + dataset will be used. + + .. include:: options/srs_def_gdalwarp.rst + +.. option:: -d, --dst-crs + + Set source spatial reference. If not specified the SRS found in the input + dataset will be used. + + .. include:: options/srs_def_gdalwarp.rst + +.. option:: -r, --resampling + + Resampling method to use. Available methods are: + + ``near``: nearest neighbour resampling (default, fastest algorithm, worst interpolation quality). + + ``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 (GDAL >= 3.3) + + ``mode``: mode resampling, selects the value which appears most often of all the sampled points. In the case of ties, the first value identified as the mode will be selected. + + ``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 (since GDAL 3.1) + + .. note:: + + When downsampling is performed (use of :option:`--resolution` or :option:`--size`), existing + overviews (either internal/implicit or external ones) on the source image + will be used by default by selecting the closest overview to the desired output + resolution. + The resampling method used to create those overviews is generally not the one you + specify through the :option:`-r` option. + +.. option:: --resolution , + + Set output file resolution (in target georeferenced units). + + If not specified (or not deduced from -te and -ts), gdalwarp will, in the + general case, generate an output raster with xres=yres. + + If neither :option:`--resolution` nor :option:`--size` are specified, + that no reprojection is involved (including taking into account geolocation arrays + or RPC), the resolution of the source file(s) will be preserved (in previous + version, an output raster with xres=yres was always generated). + +.. option:: --size , + + Set output file size in pixels and lines. If width or height is set to 0, + the other dimension will be guessed from the computed resolution. Note that + :option:`--size` cannot be used with :option:`--resolution` + +.. option:: --bbox ,,, + + Set georeferenced extents of output file to be created (in target SRS by + default, or in the SRS specified with :option:`--bbox-crs`) + +.. option:: --bbox-crs + + Specifies the SRS in which to interpret the coordinates given with :option:`--bbox`. + + .. include:: options/srs_def_gdalwarp.rst + + This must not be confused with :option:`--dst-crs` which is the target SRS of the output + dataset. :option:`--bbox-crs` is a convenience e.g. when knowing the output coordinates in a + geodetic long/lat SRS, but still wanting a result in a projected coordinate system. + +.. option:: --target-aligned-pixels + + Aign the coordinates of the extent of the output + file to the values of the :option:`--resolution`, such that the aligned extent + includes the minimum extent (edges lines/columns that are detected as + blank, before actual warping, will be removed). + Alignment means that xmin / resx, ymin / resy, + xmax / resx and ymax / resy are integer values. Examples --------