Skip to content

Commit 5556cbf

Browse files
dbastonjjimenezshawctoneyrouault
authored
gdallocationinfo: Allow querying raster corner points (OSGeo#12087)
* fix read out of bounds in GDALInterpExtractValuesWindow * InterpolateAtPoint: exclude points just out of range with NearestNeighbour * InterpolateAtPoint: allow points right at the bottom or right edge with NearestNeighbour * InterpolateAtPoint: add a small margin to avoid numeric precision issues * gdallocationinfo: Allow querying raster corner points * InterpolateAtGeoLocation: add test for corner points --------- Co-authored-by: Javier Jimenez Shaw <j1@jimenezshaw.com> Co-authored-by: Chris Toney <jctoney@gmail.com> Co-authored-by: Even Rouault <even.rouault@spatialys.com>
1 parent 46dcd39 commit 5556cbf

File tree

4 files changed

+82
-10
lines changed

4 files changed

+82
-10
lines changed

alg/gdal_interpolateatpoint.cpp

+26-8
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,20 @@ bool GDALInterpExtractValuesWindow(GDALRasterBand *pBand,
124124
// Compose the cached block to the final buffer
125125
for (int j = 0; j < nLinesToCopy; j++)
126126
{
127-
memcpy(padfAsDouble + ((nFirstLineInOutput + j) * nWidth +
128-
nFirstColInOutput) *
129-
nTypeFactor,
130-
poValue->data() +
131-
((nFirstLineInCachedBlock + j) * nReqXSize +
132-
nFirstColInCachedBlock) *
133-
nTypeFactor,
127+
const size_t dstOffset =
128+
(static_cast<size_t>(nFirstLineInOutput + j) * nWidth +
129+
nFirstColInOutput) *
130+
nTypeFactor;
131+
const size_t srcOffset =
132+
(static_cast<size_t>(nFirstLineInCachedBlock + j) *
133+
nReqXSize +
134+
nFirstColInCachedBlock) *
135+
nTypeFactor;
136+
if (srcOffset + nColsToCopy * nTypeFactor > poValue->size())
137+
{
138+
return false;
139+
}
140+
memcpy(padfAsDouble + dstOffset, poValue->data() + srcOffset,
134141
nColsToCopy * sizeof(T));
135142
}
136143
}
@@ -158,9 +165,20 @@ template <typename T>
158165
bool GDALInterpolateAtPointImpl(GDALRasterBand *pBand,
159166
GDALRIOResampleAlg eResampleAlg,
160167
std::unique_ptr<DoublePointsCache> &cache,
161-
const double dfXIn, const double dfYIn, T &out)
168+
double dfXIn, double dfYIn, T &out)
162169
{
163170
const gdal::Vector2i rasterSize{pBand->GetXSize(), pBand->GetYSize()};
171+
172+
if (eResampleAlg == GRIORA_NearestNeighbour)
173+
{
174+
// Allow input coordinates right at the bottom or right edge
175+
// with GRIORA_NearestNeighbour.
176+
// "introduce" them in the pixel of the image.
177+
if (dfXIn >= rasterSize.x() && dfXIn <= rasterSize.x() + 1e-5)
178+
dfXIn -= 0.25;
179+
if (dfYIn >= rasterSize.y() && dfYIn <= rasterSize.y() + 1e-5)
180+
dfYIn -= 0.25;
181+
}
164182
const gdal::Vector2d inLoc{dfXIn, dfYIn};
165183

166184
int bGotNoDataValue = FALSE;

apps/gdallocationinfo.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,9 @@ MAIN_START(argc, argv)
439439

440440
bool bPixelReport = true;
441441

442-
if (iPixel < 0 || iLine < 0 || iPixel >= GDALGetRasterXSize(hSrcDS) ||
443-
iLine >= GDALGetRasterYSize(hSrcDS))
442+
if (iPixel < 0 || iLine < 0 ||
443+
dfPixel > static_cast<double>(GDALGetRasterXSize(hSrcDS) + 1e-5) ||
444+
dfLine > static_cast<double>(GDALGetRasterYSize(hSrcDS) + 1e-5))
444445
{
445446
if (bAsXML)
446447
osXML += "<Alert>Location is off this file! No further details "

autotest/gcore/interpolateatpoint.py

+39
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,25 @@ def test_interpolateatpoint_outofrange():
4444
assert res is None
4545
res = ds.GetRasterBand(1).InterpolateAtPoint(10, 1200, gdal.GRIORA_Bilinear)
4646
assert res is None
47+
res = ds.GetRasterBand(1).InterpolateAtPoint(-1, 0, gdal.GRIORA_NearestNeighbour)
48+
assert res is None
49+
res = ds.GetRasterBand(1).InterpolateAtPoint(0, -0.5, gdal.GRIORA_NearestNeighbour)
50+
assert res is None
51+
52+
53+
def test_interpolateatpoint_right_at_border():
54+
55+
ds = gdal.Open("data/byte.tif")
56+
res = ds.GetRasterBand(1).InterpolateAtPoint(20, 20, gdal.GRIORA_NearestNeighbour)
57+
assert res == pytest.approx(107.0, 1e-5)
58+
res = ds.GetRasterBand(1).InterpolateAtPoint(18, 20, gdal.GRIORA_NearestNeighbour)
59+
assert res == pytest.approx(99.0, 1e-5)
60+
res = ds.GetRasterBand(1).InterpolateAtPoint(20, 18, gdal.GRIORA_NearestNeighbour)
61+
assert res == pytest.approx(123.0, 1e-5)
62+
res = ds.GetRasterBand(1).InterpolateAtPoint(20, 20, gdal.GRIORA_Bilinear)
63+
assert res == pytest.approx(107.0, 1e-5)
64+
res = ds.GetRasterBand(1).InterpolateAtPoint(20.1, 20.1, gdal.GRIORA_Bilinear)
65+
assert res is None
4766

4867

4968
@gdaltest.disable_exceptions()
@@ -444,3 +463,23 @@ def test_interpolateatgeolocation_1():
444463
assert ungeoreferenced_ds.GetRasterBand(1).InterpolateAtGeolocation(
445464
0, 0, None, gdal.GRIORA_NearestNeighbour
446465
)
466+
467+
468+
def test_interpolateatgeolocation_corners():
469+
470+
gdaltest.importorskip_gdal_array()
471+
472+
ds = gdal.Open("data/byte.tif")
473+
band = ds.GetRasterBand(1)
474+
data = band.ReadAsArray()
475+
476+
values = {}
477+
for loc, coord in gdal.Info(ds, format="json")["cornerCoordinates"].items():
478+
values[loc] = band.InterpolateAtGeolocation(
479+
coord[0], coord[1], None, gdal.GRIORA_NearestNeighbour
480+
)
481+
482+
assert values["upperLeft"] == data[0, 0]
483+
assert values["upperRight"] == data[0, -1]
484+
assert values["lowerLeft"] == data[-1, 0]
485+
assert values["lowerRight"] == data[-1, -1]

autotest/utilities/test_gdallocationinfo.py

+14
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@ def test_gdallocationinfo_4(gdallocationinfo_path):
101101
assert ret.startswith(expected_ret)
102102

103103

104+
# Test -geoloc at lower right corner
105+
def test_gdallocationinfo_lr(gdallocationinfo_path):
106+
107+
ret = gdaltest.runexternal(
108+
gdallocationinfo_path + " -geoloc ../gcore/data/byte.tif 441920.000 3750120.000"
109+
)
110+
ret = ret.replace("\r\n", "\n")
111+
expected_ret = """Report:
112+
Location: (20P,20L)
113+
Band 1:
114+
Value: 107"""
115+
assert ret.startswith(expected_ret)
116+
117+
104118
###############################################################################
105119
# Test -lifonly
106120

0 commit comments

Comments
 (0)