From 1395b79749e805b33be12d7fab836899bbda596b Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Tue, 10 Feb 2026 16:40:14 +0000 Subject: [PATCH 1/6] WCS stuff implemented --- autoarray/config/visualize/plots.yaml | 2 +- autoarray/geometry/geometry_2d.py | 34 ++- autoarray/geometry/geometry_util.py | 35 +++ test_autoarray/geometry/test_geometry_util.py | 275 ++++++++++++++++++ 4 files changed, 343 insertions(+), 3 deletions(-) diff --git a/autoarray/config/visualize/plots.yaml b/autoarray/config/visualize/plots.yaml index 7d10414f6..ed4da3b0c 100644 --- a/autoarray/config/visualize/plots.yaml +++ b/autoarray/config/visualize/plots.yaml @@ -21,7 +21,7 @@ fit: # Settings for plots of all fits (e.g fit_imaging: {} # Settings for plots of fits to imaging datasets (e.g. FitImagingPlotter). inversion: # Settings for plots of inversions (e.g. InversionPlotter). subplot_inversion: true # Plot subplot of all quantities in each inversion (e.g. reconstrucuted image, reconstruction)? - subplot_mappings: true # Plot subplot of the image-to-source pixels mappings of each pixelization? + subplot_mappings: false # Plot subplot of the image-to-source pixels mappings of each pixelization? data_subtracted: false # Plot individual plots of the data with the other inversion linear objects subtracted? reconstruction_noise_map: false # Plot image of the noise of every mesh-pixel reconstructed value? sub_pixels_per_image_pixels: false # Plot the number of sub pixels per masked data pixels? diff --git a/autoarray/geometry/geometry_2d.py b/autoarray/geometry/geometry_2d.py index fa5d4b24a..1cba44efb 100644 --- a/autoarray/geometry/geometry_2d.py +++ b/autoarray/geometry/geometry_2d.py @@ -134,8 +134,8 @@ def pixel_coordinates_2d_from( self, scaled_coordinates_2d: Tuple[float, float] ) -> Tuple[float, float]: """ - Convert a 2D (y,x) scaled coordinate to a 2D (y,x) pixel coordinate, which are returned as floats such that they - include the decimal offset from each pixel's top-left corner relative to the input scaled coordinate. + Convert a 2D (y,x) scaled coordinate to a 2D (y,x) pixel coordinate, which are returned as integers such that + they do not include the decimal offset from each pixel's top-left corner relative to the input scaled coordinate. The conversion is performed according to the 2D geometry on a uniform grid, where the pixel coordinate origin is at the top left corner, such that the pixel [0,0] corresponds to the highest (most positive) y scaled @@ -190,6 +190,36 @@ def scaled_coordinates_2d_from( origins=self.origin, ) + def pixel_coordinates_wcs_2d_from( + self, scaled_coordinates_2d: Tuple[float, float] + ) -> Tuple[float, float]: + """ + Convert a 2D (y,x) scaled coordinate to a 2D (y,x) pixel coordinate, which are returned as floats such that they + include the decimal offset from each pixel's top-left corner relative to the input scaled coordinate. + + The conversion is performed according to the 2D geometry on a uniform grid, where the pixel coordinate origin + is at the top left corner, such that the pixel [0,0] corresponds to the highest (most positive) y scaled + coordinate and lowest (most negative) x scaled coordinate on the gird. + + The scaled coordinate is defined by an origin and coordinates are shifted to this origin before computing their + 1D grid pixel coordinate values. + + Parameters + ---------- + scaled_coordinates_2d + The 2D (y,x) coordinates in scaled units which are converted to pixel coordinates. + + Returns + ------- + A 2D (y,x) pixel-value coordinate. + """ + return geometry_util.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=scaled_coordinates_2d, + shape_native=self.shape_native, + pixel_scales=self.pixel_scales, + origins=self.origin, + ) + def scaled_coordinate_2d_to_scaled_at_pixel_centre_from( self, scaled_coordinate_2d: Tuple[float, float] ) -> Tuple[float, float]: diff --git a/autoarray/geometry/geometry_util.py b/autoarray/geometry/geometry_util.py index 54af5ca8b..649744b06 100644 --- a/autoarray/geometry/geometry_util.py +++ b/autoarray/geometry/geometry_util.py @@ -357,6 +357,41 @@ def scaled_coordinates_2d_from( return (y_pixel, x_pixel) +def pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d, + shape_native, + pixel_scales, + origins=(0.0, 0.0), +): + """ + Return FITS / WCS pixel coordinates (1-based, pixel-centre convention) as floats. + + This function returns continuous pixel coordinates suitable for Astropy WCS + transforms (e.g. wcs_pix2world with origin=1). Pixel centres lie at integer + values; for an image of shape (ny, nx) the geometric centre is: + ((ny + 1)/2, (nx + 1)/2) + e.g. (100, 100) -> (50.5, 50.5). + """ + ny, nx = shape_native + + # Geometric centre in WCS pixel coordinates (1-based, pixel centres at integers) + ycen_wcs = (ny + 1) / 2.0 + xcen_wcs = (nx + 1) / 2.0 + + # Continuous WCS pixel coordinates (NO int-cast, NO +0.5 binning) + y_wcs = ( + (-scaled_coordinates_2d[0] + origins[0]) / pixel_scales[0] + + ycen_wcs + ) + x_wcs = ( + (scaled_coordinates_2d[1] - origins[1]) / pixel_scales[1] + + xcen_wcs + ) + + return (y_wcs, x_wcs) + + + def transform_grid_2d_to_reference_frame( grid_2d: np.ndarray, centre: Tuple[float, float], angle: float, xp=np ) -> np.ndarray: diff --git a/test_autoarray/geometry/test_geometry_util.py b/test_autoarray/geometry/test_geometry_util.py index e65608bf1..3792a2b19 100644 --- a/test_autoarray/geometry/test_geometry_util.py +++ b/test_autoarray/geometry/test_geometry_util.py @@ -972,6 +972,281 @@ def test__pixel_coordinates_2d_from(): assert scaled_coordinates == (0.0, 6.0) +import pytest + +def test__pixel_coordinates_wcs_2d_from(): + # ----------------------------- + # (2,2) grid: centre is (1.5, 1.5) in WCS pixels + # pixel_scales = (2,2) + # ----------------------------- + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(1.0, -1.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(1.0, 1.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 2.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-1.0, -1.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((2.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-1.0, 1.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((2.0, 2.0)) + + # ----------------------------- + # (3,3) grid: centre is (2.0, 2.0) in WCS pixels + # pixel_scales = (3,3) + # ----------------------------- + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(3.0, -3.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(3.0, 0.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 2.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(3.0, 3.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 3.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.0, -3.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((2.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.0, 0.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((2.0, 2.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.0, 3.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((2.0, 3.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-3.0, -3.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((3.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-3.0, 0.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((3.0, 2.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-3.0, 3.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((3.0, 3.0)) + + # ----------------------------------------- + # Inputs near corners (continuous coordinates) + # ----------------------------------------- + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(1.99, -1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((0.505, 0.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(1.99, -0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((0.505, 1.495)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.01, -1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.495, 0.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.01, -0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.495, 1.495)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(2.01, 0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((0.495, 1.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(2.01, 1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((0.495, 2.495)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.01, 0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.495, 1.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.01, 1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.495, 2.495)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-0.01, -1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.505, 0.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-0.01, -0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.505, 1.495)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-1.99, -1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((2.495, 0.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-1.99, -0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((2.495, 1.495)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-0.01, 0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.505, 1.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-0.01, 1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((1.505, 2.495)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-1.99, 0.01), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((2.495, 1.505)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(-1.99, 1.99), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + ) + assert pixel_coordinates == pytest.approx((2.495, 2.495)) + + # ----------------------------------------- + # Inputs are centres (origins shift), still continuous outputs + # ----------------------------------------- + + # Inputs are centres (origins shift), continuous outputs + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(2.0, 0.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + origins=(1.0, 1.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(2.0, 2.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + origins=(1.0, 1.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 2.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.0, 0.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + origins=(1.0, 1.0), + ) + assert pixel_coordinates == pytest.approx((2.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(0.0, 2.0), + shape_native=(2, 2), + pixel_scales=(2.0, 2.0), + origins=(1.0, 1.0), + ) + assert pixel_coordinates == pytest.approx((2.0, 2.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(6.0, 0.0), + shape_native=(3, 3), + pixel_scales=(3.0, 3.0), + origins=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((1.0, 1.0)) + + pixel_coordinates = aa.util.geometry.pixel_coordinates_wcs_2d_from( + scaled_coordinates_2d=(6.0, 3.0), + shape_native=(4, 4), + pixel_scales=(3.0, 3.0), + origins=(3.0, 3.0), + ) + assert pixel_coordinates == pytest.approx((1.5, 2.5)) + + def test__transform_2d_grid_to_reference_frame(): grid_2d = np.array([[0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]) From 0cf7ee286ae322653be527d58fa8f258f91528ca Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Tue, 10 Feb 2026 17:18:40 +0000 Subject: [PATCH 2/6] all tests pass and working in Euclid --- autoarray/geometry/geometry_util.py | 11 ++--------- test_autoarray/geometry/test_geometry_util.py | 1 + 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/autoarray/geometry/geometry_util.py b/autoarray/geometry/geometry_util.py index 649744b06..b3db6cd0a 100644 --- a/autoarray/geometry/geometry_util.py +++ b/autoarray/geometry/geometry_util.py @@ -379,19 +379,12 @@ def pixel_coordinates_wcs_2d_from( xcen_wcs = (nx + 1) / 2.0 # Continuous WCS pixel coordinates (NO int-cast, NO +0.5 binning) - y_wcs = ( - (-scaled_coordinates_2d[0] + origins[0]) / pixel_scales[0] - + ycen_wcs - ) - x_wcs = ( - (scaled_coordinates_2d[1] - origins[1]) / pixel_scales[1] - + xcen_wcs - ) + y_wcs = (-scaled_coordinates_2d[0] + origins[0]) / pixel_scales[0] + ycen_wcs + x_wcs = (scaled_coordinates_2d[1] - origins[1]) / pixel_scales[1] + xcen_wcs return (y_wcs, x_wcs) - def transform_grid_2d_to_reference_frame( grid_2d: np.ndarray, centre: Tuple[float, float], angle: float, xp=np ) -> np.ndarray: diff --git a/test_autoarray/geometry/test_geometry_util.py b/test_autoarray/geometry/test_geometry_util.py index 3792a2b19..ad93fd321 100644 --- a/test_autoarray/geometry/test_geometry_util.py +++ b/test_autoarray/geometry/test_geometry_util.py @@ -974,6 +974,7 @@ def test__pixel_coordinates_2d_from(): import pytest + def test__pixel_coordinates_wcs_2d_from(): # ----------------------------- # (2,2) grid: centre is (1.5, 1.5) in WCS pixels From 706e7a80251676b108e0a2d05775c54fb7580990 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Tue, 10 Feb 2026 17:28:54 +0000 Subject: [PATCH 3/6] Update autoarray/geometry/geometry_util.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- autoarray/geometry/geometry_util.py | 41 ++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/autoarray/geometry/geometry_util.py b/autoarray/geometry/geometry_util.py index b3db6cd0a..cd2fd6533 100644 --- a/autoarray/geometry/geometry_util.py +++ b/autoarray/geometry/geometry_util.py @@ -358,19 +358,42 @@ def scaled_coordinates_2d_from( def pixel_coordinates_wcs_2d_from( - scaled_coordinates_2d, - shape_native, - pixel_scales, - origins=(0.0, 0.0), -): + scaled_coordinates_2d: Tuple[float, float], + shape_native: Tuple[int, int], + pixel_scales: ty.PixelScales, + origins: Tuple[float, float] = (0.0, 0.0), +) -> Tuple[float, float]: """ Return FITS / WCS pixel coordinates (1-based, pixel-centre convention) as floats. This function returns continuous pixel coordinates suitable for Astropy WCS - transforms (e.g. wcs_pix2world with origin=1). Pixel centres lie at integer - values; for an image of shape (ny, nx) the geometric centre is: - ((ny + 1)/2, (nx + 1)/2) - e.g. (100, 100) -> (50.5, 50.5). + transforms (e.g. ``wcs_pix2world`` with ``origin=1``). Pixel centres lie at + integer values; for an image of shape ``(ny, nx)`` the geometric centre is:: + + ((ny + 1) / 2, (nx + 1) / 2) + + e.g. ``(100, 100) -> (50.5, 50.5)``. + + Parameters + ---------- + scaled_coordinates_2d + The 2D (y, x) coordinates in scaled units which are converted to WCS + pixel coordinates. + shape_native + The (y, x) shape of the 2D array on which the scaled coordinates are + defined, used to determine the geometric centre in WCS pixel units. + pixel_scales + The (y, x) conversion factors from scaled units to pixel units. + origins + The (y, x) origin in scaled units about which the coordinates are + defined. The scaled coordinates are shifted by this origin before being + converted to WCS pixel coordinates. + + Returns + ------- + pixel_coordinates_wcs_2d + A 2D (y, x) WCS pixel coordinate in the 1-based, pixel-centre + convention, returned as floats. """ ny, nx = shape_native From df52fa4858b819351dff2955429cea8d0f76c27c Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Tue, 10 Feb 2026 17:29:13 +0000 Subject: [PATCH 4/6] Update autoarray/geometry/geometry_2d.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- autoarray/geometry/geometry_2d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoarray/geometry/geometry_2d.py b/autoarray/geometry/geometry_2d.py index 1cba44efb..7d703c773 100644 --- a/autoarray/geometry/geometry_2d.py +++ b/autoarray/geometry/geometry_2d.py @@ -132,7 +132,7 @@ def central_scaled_coordinates(self) -> Tuple[float, float]: def pixel_coordinates_2d_from( self, scaled_coordinates_2d: Tuple[float, float] - ) -> Tuple[float, float]: + ) -> Tuple[int, int]: """ Convert a 2D (y,x) scaled coordinate to a 2D (y,x) pixel coordinate, which are returned as integers such that they do not include the decimal offset from each pixel's top-left corner relative to the input scaled coordinate. From 0a081b87f3a2aa59d5b9e7d08bf29764061bc85f Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Tue, 10 Feb 2026 17:29:31 +0000 Subject: [PATCH 5/6] Update autoarray/geometry/geometry_2d.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- autoarray/geometry/geometry_2d.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/autoarray/geometry/geometry_2d.py b/autoarray/geometry/geometry_2d.py index 7d703c773..89c9fe293 100644 --- a/autoarray/geometry/geometry_2d.py +++ b/autoarray/geometry/geometry_2d.py @@ -194,24 +194,29 @@ def pixel_coordinates_wcs_2d_from( self, scaled_coordinates_2d: Tuple[float, float] ) -> Tuple[float, float]: """ - Convert a 2D (y,x) scaled coordinate to a 2D (y,x) pixel coordinate, which are returned as floats such that they - include the decimal offset from each pixel's top-left corner relative to the input scaled coordinate. + Convert a 2D (y,x) scaled coordinate to a 2D (y,x) WCS/FITS-style pixel coordinate. - The conversion is performed according to the 2D geometry on a uniform grid, where the pixel coordinate origin - is at the top left corner, such that the pixel [0,0] corresponds to the highest (most positive) y scaled - coordinate and lowest (most negative) x scaled coordinate on the gird. + The returned pixel coordinates follow the standard WCS convention: - The scaled coordinate is defined by an origin and coordinates are shifted to this origin before computing their - 1D grid pixel coordinate values. + - Coordinates are 1-based rather than 0-based, so that the centre of the top-left pixel is at (y, x) = (1.0, 1.0). + - Coordinates refer to pixel centres, not pixel corners. + - Values are continuous floats, so the fractional part encodes the sub-pixel offset from the pixel centre. + + This differs from integer pixel-index conversions (e.g. ``pixel_coordinates_2d_from``), which return 0-based + indices associated with pixel corners/top-left positions. + + The mapping from scaled coordinates to WCS pixel coordinates is defined by this geometry's ``origin``: scaled + coordinates are first shifted by the specified origin(s) before being converted using the pixel scale and + array shape. Changing ``origin`` therefore translates the returned WCS pixel coordinates by a constant offset. Parameters ---------- scaled_coordinates_2d - The 2D (y,x) coordinates in scaled units which are converted to pixel coordinates. + The 2D (y,x) coordinates in scaled units to be converted to WCS-style pixel coordinates. Returns ------- - A 2D (y,x) pixel-value coordinate. + A 2D (y,x) WCS pixel coordinate, expressed as 1-based, pixel-centre, floating-point values. """ return geometry_util.pixel_coordinates_wcs_2d_from( scaled_coordinates_2d=scaled_coordinates_2d, From 822ebec5b36bce627f762ae3e6faa2ef73c5b04f Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Tue, 10 Feb 2026 17:29:38 +0000 Subject: [PATCH 6/6] Update test_autoarray/geometry/test_geometry_util.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test_autoarray/geometry/test_geometry_util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test_autoarray/geometry/test_geometry_util.py b/test_autoarray/geometry/test_geometry_util.py index ad93fd321..0f5781702 100644 --- a/test_autoarray/geometry/test_geometry_util.py +++ b/test_autoarray/geometry/test_geometry_util.py @@ -972,7 +972,6 @@ def test__pixel_coordinates_2d_from(): assert scaled_coordinates == (0.0, 6.0) -import pytest def test__pixel_coordinates_wcs_2d_from():