Skip to content

Commit 2f3e348

Browse files
authored
Fix one-pixel shift with xy_coords="center" (#94)
Closes #68, #93.
1 parent 3a58906 commit 2f3e348

File tree

3 files changed

+30
-25
lines changed

3 files changed

+30
-25
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 0.2.2 (...)
44
- Support [pystac](https://github.com/stac-utils/pystac) ItemCollections
55
- Fix bug where repeated metadata values would be None
6+
- Fix one-pixel shift when using `xy_coords="center"` [@gjoseph92](https://github.com/gjoseph92) [@Kirill888](https://github.com/Kirill888) [@maawoo](https://github.com/maawoo)
67

78
## 0.2.1 (2021-05-07)
89
Support [xarray 0.18](http://xarray.pydata.org/en/stable/whats-new.html#v0-18-0-6-may-2021) and beyond, as well as looser version requirements for some other dependencies.

stackstac/prepare.py

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -391,32 +391,27 @@ def to_coords(
391391
)
392392

393393
transform = spec.transform
394-
if transform.is_rectilinear:
395-
# faster-path for rectilinear transforms: just `arange` it instead of doing the affine math
396-
minx, miny, maxx, maxy = spec.bounds
397-
xres, yres = spec.resolutions_xy
398-
399-
if pixel_center:
400-
half_xpixel, half_ypixel = xres / 2, yres / 2
401-
minx, miny, maxx, maxy = (
402-
minx + half_xpixel,
403-
miny + half_ypixel,
404-
maxx + half_xpixel,
405-
maxy + half_ypixel,
406-
)
394+
# We generate the transform ourselves in `RasterSpec`, and it's always constructed to be rectilinear.
395+
# Someday, this should not always be the case, in order to support non-rectilinear data without warping.
396+
assert (
397+
transform.is_rectilinear
398+
), f"Non-rectilinear transform generated: {transform}"
399+
minx, miny, maxx, maxy = spec.bounds
400+
xres, yres = spec.resolutions_xy
401+
402+
if pixel_center:
403+
half_xpixel, half_ypixel = xres / 2, yres / 2
404+
minx, miny, maxx, maxy = (
405+
minx + half_xpixel,
406+
miny - half_ypixel,
407+
maxx + half_xpixel,
408+
maxy - half_ypixel,
409+
)
407410

408-
height, width = spec.shape
409-
# Wish pandas had an RangeIndex that supported floats...
410-
xs = pd.Float64Index(np.linspace(minx, maxx, width, endpoint=False))
411-
ys = pd.Float64Index(np.linspace(maxy, miny, height, endpoint=False))
412-
else:
413-
height, width = spec.shape
414-
if pixel_center:
415-
xs, _ = transform * (np.arange(width) + 0.5, np.zeros(width) + 0.5)
416-
_, ys = transform * (np.zeros(height) + 0.5, np.arange(height) + 0.5)
417-
else:
418-
xs, _ = transform * (np.arange(width), np.zeros(width))
419-
_, ys = transform * (np.zeros(height), np.arange(height))
411+
height, width = spec.shape
412+
# Wish pandas had an RangeIndex that supported floats...
413+
xs = pd.Float64Index(np.linspace(minx, maxx, width, endpoint=False))
414+
ys = pd.Float64Index(np.linspace(maxy, miny, height, endpoint=False))
420415

421416
coords["x"] = xs
422417
coords["y"] = ys

stackstac/raster_spec.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ class RasterSpec:
1919
bounds: Bbox
2020
resolutions_xy: Resolutions
2121

22+
def __post_init__(self):
23+
xres, yres = self.resolutions_xy
24+
assert xres > 0, f"X resolution {xres} must be > 0"
25+
assert yres > 0, f"Y resolution {yres} must be > 0"
26+
27+
minx, miny, maxx, maxy = self.bounds
28+
assert minx < maxx, f"Invalid bounds: {minx=} >= {maxx=}"
29+
assert miny < maxy, f"Invalid bounds: {miny=} >= {maxy=}"
30+
2231
@cached_property
2332
def transform(self) -> affine.Affine:
2433
return affine.Affine(

0 commit comments

Comments
 (0)