Skip to content

Commit 93f77d7

Browse files
committed
gdalwarp: fix error when using -ct and -cutline (master only), and actually use the -ct when possible if sourceCRS != targetCRS and targetCRS == cutlineCRS
1 parent bba72b1 commit 93f77d7

File tree

2 files changed

+107
-18
lines changed

2 files changed

+107
-18
lines changed

apps/gdalwarp_lib.cpp

+47-18
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,40 @@ static CPLString GetSrcDSProjection(GDALDatasetH hDS, CSLConstList papszTO)
400400
return pszProjection ? pszProjection : "";
401401
}
402402

403+
/************************************************************************/
404+
/* CreateCTCutlineToSrc() */
405+
/************************************************************************/
406+
407+
static std::unique_ptr<OGRCoordinateTransformation> CreateCTCutlineToSrc(
408+
const OGRSpatialReference *poRasterSRS, const OGRSpatialReference *poDstSRS,
409+
const OGRSpatialReference *poCutlineSRS, CSLConstList papszTO)
410+
{
411+
const OGRSpatialReference *poCutlineOrTargetSRS =
412+
poCutlineSRS ? poCutlineSRS : poDstSRS;
413+
std::unique_ptr<OGRCoordinateTransformation> poCTCutlineToSrc;
414+
if (poCutlineOrTargetSRS && poRasterSRS &&
415+
!poCutlineOrTargetSRS->IsSame(poRasterSRS))
416+
{
417+
OGRCoordinateTransformationOptions oOptions;
418+
// If the cutline SRS is the same as the target SRS and there is
419+
// an explicit -ct between the source SRS and the target SRS, then
420+
// use it in the reverse way to transform from the cutline SRS to
421+
// the source SRS.
422+
if (poDstSRS && poCutlineOrTargetSRS->IsSame(poDstSRS))
423+
{
424+
const char *pszCT =
425+
CSLFetchNameValue(papszTO, "COORDINATE_OPERATION");
426+
if (pszCT)
427+
{
428+
oOptions.SetCoordinateOperation(pszCT, /* bInverse = */ true);
429+
}
430+
}
431+
poCTCutlineToSrc.reset(OGRCreateCoordinateTransformation(
432+
poCutlineOrTargetSRS, poRasterSRS, oOptions));
433+
}
434+
return poCTCutlineToSrc;
435+
}
436+
403437
/************************************************************************/
404438
/* CropToCutline() */
405439
/************************************************************************/
@@ -470,16 +504,10 @@ static CPLErr CropToCutline(const OGRGeometry *poCutline, CSLConstList papszTO,
470504
}
471505

472506
auto poCutlineGeom = std::unique_ptr<OGRGeometry>(poCutline->clone());
473-
const OGRSpatialReference *poCutlineOrTargetSRS =
474-
poCutlineSRS ? poCutlineSRS : poDstSRS.get();
475-
std::unique_ptr<OGRCoordinateTransformation> poCTCutlineToSrc;
476-
std::unique_ptr<OGRCoordinateTransformation> poCTSrcToDst;
507+
auto poCTCutlineToSrc = CreateCTCutlineToSrc(poSrcSRS.get(), poDstSRS.get(),
508+
poCutlineSRS, papszTO);
477509

478-
if (!poCutlineOrTargetSRS->IsSame(poSrcSRS.get()))
479-
{
480-
poCTCutlineToSrc.reset(OGRCreateCoordinateTransformation(
481-
poCutlineOrTargetSRS, poSrcSRS.get()));
482-
}
510+
std::unique_ptr<OGRCoordinateTransformation> poCTSrcToDst;
483511
if (!poSrcSRS->IsSame(poDstSRS.get()))
484512
{
485513
poCTSrcToDst.reset(
@@ -4775,15 +4803,8 @@ static CPLErr TransformCutlineToSource(GDALDataset *poSrcDS,
47754803
"Cutline results may be incorrect.");
47764804
}
47774805

4778-
const OGRSpatialReference *poCutlineOrTargetSRS =
4779-
poCutlineSRS ? poCutlineSRS : poDstSRS.get();
4780-
std::unique_ptr<OGRCoordinateTransformation> poCTCutlineToSrc;
4781-
if (poCutlineOrTargetSRS && poRasterSRS &&
4782-
!poCutlineOrTargetSRS->IsSame(poRasterSRS.get()))
4783-
{
4784-
poCTCutlineToSrc.reset(OGRCreateCoordinateTransformation(
4785-
poCutlineOrTargetSRS, poRasterSRS.get()));
4786-
}
4806+
auto poCTCutlineToSrc = CreateCTCutlineToSrc(
4807+
poRasterSRS.get(), poDstSRS.get(), poCutlineSRS, papszTO_In);
47874808

47884809
CPLStringList aosTO(papszTO_In);
47894810

@@ -4792,6 +4813,9 @@ static CPLErr TransformCutlineToSource(GDALDataset *poSrcDS,
47924813
// Avoid any reprojection when using the GenImgProjTransformer
47934814
aosTO.SetNameValue("DST_SRS", osProjection.c_str());
47944815
}
4816+
aosTO.SetNameValue("SRC_COORDINATE_EPOCH", nullptr);
4817+
aosTO.SetNameValue("DST_COORDINATE_EPOCH", nullptr);
4818+
aosTO.SetNameValue("COORDINATE_OPERATION", nullptr);
47954819

47964820
/* -------------------------------------------------------------------- */
47974821
/* It may be unwise to let the mask geometry be re-wrapped by */
@@ -4834,7 +4858,12 @@ static CPLErr TransformCutlineToSource(GDALDataset *poSrcDS,
48344858
}
48354859
}
48364860
if (poMultiPolygon->transform(&oTransformer) != OGRERR_NONE)
4861+
{
4862+
CPLError(CE_Failure, CPLE_AppDefined,
4863+
"poMultiPolygon->transform(&oTransformer) failed at line %d",
4864+
__LINE__);
48374865
eErr = OGRERR_FAILURE;
4866+
}
48384867
const double dfInitialMaxLengthInPixels =
48394868
GetMaximumSegmentLength(poMultiPolygon.get());
48404869

autotest/utilities/test_gdalwarp_lib.py

+60
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,66 @@ def test_gdalwarp_lib_21():
472472
ds = None
473473

474474

475+
###############################################################################
476+
# Test cutline with sourceCRS != targetCRS and targetCRS == cutlineCRS
477+
478+
479+
@pytest.mark.require_driver("CSV")
480+
@pytest.mark.require_driver("GPKG")
481+
def test_gdalwarp_lib_cutline_reprojection(tmp_vsimem):
482+
483+
cutline_filename = str(tmp_vsimem / "cutline.gpkg")
484+
gdal.VectorTranslate(
485+
cutline_filename, "data/cutline.vrt", dstSRS="EPSG:4267", reproject=True
486+
)
487+
488+
ds = gdal.Warp(
489+
"",
490+
"../gcore/data/utmsmall.tif",
491+
format="MEM",
492+
dstSRS="EPSG:4267",
493+
cutlineDSName=cutline_filename,
494+
)
495+
assert ds is not None
496+
497+
assert ds.GetRasterBand(1).Checksum() == 18883, "Bad checksum"
498+
499+
ds = None
500+
501+
502+
###############################################################################
503+
# Test cutline with sourceCRS != targetCRS and targetCRS == cutlineCRS and coordinateOperation
504+
505+
506+
@pytest.mark.require_driver("CSV")
507+
@pytest.mark.require_driver("GPKG")
508+
def test_gdalwarp_lib_cutline_reprojection_and_coordinate_operation(tmp_vsimem):
509+
510+
cutline_filename = str(tmp_vsimem / "cutline.gpkg")
511+
ct = "+proj=pipeline +step +proj=affine +xoff=10000 +step +inv +proj=utm +zone=11 +ellps=clrk66 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"
512+
gdal.VectorTranslate(
513+
cutline_filename,
514+
"data/cutline.vrt",
515+
dstSRS="EPSG:4267",
516+
coordinateOperation=ct,
517+
reproject=True,
518+
)
519+
520+
ds = gdal.Warp(
521+
"",
522+
"../gcore/data/utmsmall.tif",
523+
format="MEM",
524+
dstSRS="EPSG:4267",
525+
cutlineDSName=cutline_filename,
526+
coordinateOperation=ct,
527+
)
528+
assert ds is not None
529+
530+
assert ds.GetRasterBand(1).Checksum() == 18914, "Bad checksum"
531+
532+
ds = None
533+
534+
475535
###############################################################################
476536
# Test cutline from PostGIS (mostly to check that we open the dataset in
477537
# vector mode, and not in raster mode, which would cause the PostGISRaster

0 commit comments

Comments
 (0)