Repository: sunpy/sunpy

Issue:
top_right_coord may be off by one
The `top_right_coord` attribute on `sunpy.map.GenericMap` is calculated as,

https://github.com/sunpy/sunpy/blob/2a3d9a5cb34ba496fcf55d861e9307e3cec783db/sunpy/map/mapbase.py#L521

While WCS indexing does begin at 1, in astropy.wcs.WCS, our origin is defined such that `(0,0)*u.pixel` corresponds to the center of the bottom left corner. This would mean that, for an `N`-by-`N` image, the top right corner should correspond to `(N-1,N-1)*u.pixel`.

Further testing is needed to determine whether this is actually the case.


Discussion:
It does indeed seem to be off by one. The following code prints the coord as (4, 4), despite the top right of the map being at (3, 3).

```python
import numpy as np
import sunpy.map
import sunpy.coordinates
from astropy.coordinates import SkyCoord
import astropy.units as u

data = np.arange(4).reshape((2, 2))
ref_coord = SkyCoord(0, 0, frame='helioprojective', obstime='now', unit='deg')
ref_pix = [1, 1] * u.pix
scale = [2, 2] * u.arcsec / u.pix
header = sunpy.map.make_fitswcs_header(data, ref_coord, reference_pixel=ref_pix, scale=scale)

m = sunpy.map.Map(data, header)
print(m.top_right_coord)
m.peek()
```

![Figure_1](https://user-images.githubusercontent.com/6197628/81411481-50537980-913a-11ea-8ed9-59ee13ff5f73.png)


Resolving patch:
diff --git a/sunpy/map/mapbase.py b/sunpy/map/mapbase.py
--- a/sunpy/map/mapbase.py
+++ b/sunpy/map/mapbase.py
@@ -671,16 +671,20 @@ def bottom_left_coord(self):
     @property
     def top_right_coord(self):
         """
-        The physical coordinate at the center of the the top left ([-1, -1]) pixel.
+        The physical coordinate at the center of the the top right ([-1, -1]) pixel.
         """
-        return self.pixel_to_world(*self.dimensions)
+        top_right = u.Quantity(self.dimensions) - 1 * u.pix
+        return self.pixel_to_world(*top_right)
 
     @property
     def center(self):
         """
         Return a coordinate object for the center pixel of the array.
+
+        If the array has an even number of pixels in a given dimension,
+        the coordinate returned lies on the edge between the two central pixels.
         """
-        center = u.Quantity(self.dimensions) / 2.
+        center = (u.Quantity(self.dimensions) - 1 * u.pix) / 2.
         return self.pixel_to_world(*center)
 
     @property


Test patch:
diff --git a/sunpy/map/tests/test_mapbase.py b/sunpy/map/tests/test_mapbase.py
--- a/sunpy/map/tests/test_mapbase.py
+++ b/sunpy/map/tests/test_mapbase.py
@@ -429,6 +429,23 @@ def test_shift_history(generic_map):
     assert resultant_shift[1] == y_shift1 + y_shift2
 
 
+def test_corners(simple_map):
+    # These are the centers of the corner pixels
+    assert u.allclose(simple_map.top_right_coord.Tx, 1 * u.arcsec)
+    assert u.allclose(simple_map.top_right_coord.Ty, 0.5 * u.arcsec)
+    assert u.allclose(simple_map.bottom_left_coord.Tx, -1 * u.arcsec)
+    assert u.allclose(simple_map.bottom_left_coord.Ty, -0.5 * u.arcsec)
+
+
+def test_center(simple_map):
+    assert u.allclose(simple_map.center.Tx, 0 * u.arcsec, atol=1e-26 * u.arcsec)
+    assert u.allclose(simple_map.center.Ty, 0 * u.arcsec)
+
+
+def test_dimensions(simple_map):
+    assert simple_map.dimensions[0] == 2 * u.pix
+    assert simple_map.dimensions[1] == 2 * u.pix
+
 # Check that submap works with units convertable to pix but that aren't pix
 @pytest.mark.parametrize('unit', [u.pix, u.mpix * 1e3])
 def test_submap(generic_map, unit):
@@ -463,6 +480,26 @@ def test_reference_coordinate(simple_map):
     assert simple_map.reference_pixel.y == 0.5 * u.pix
 
 
+def test_resample(simple_map):
+    # Test resampling a 2x2 map
+    resampled = simple_map.resample([1, 1] * u.pix)
+    # Should be the mean of [0, 1, 2, 3]
+    assert resampled.data == np.array([[1.5]])
+    assert resampled.scale.axis1 == 2 * simple_map.scale.axis1
+    assert resampled.scale.axis2 == 2 * simple_map.scale.axis2
+
+    # Check that the corner coordinates of the input and output are the same
+    resampled_lower_left = resampled.pixel_to_world(-0.5 * u.pix, -0.5 * u.pix)
+    original_lower_left = simple_map.pixel_to_world(-0.5 * u.pix, -0.5 * u.pix)
+    assert resampled_lower_left.Tx == original_lower_left.Tx
+    assert resampled_lower_left.Ty == original_lower_left.Ty
+
+    resampled_upper_left = resampled.pixel_to_world(0.5 * u.pix, 0.5 * u.pix)
+    original_upper_left = simple_map.pixel_to_world(1.5 * u.pix, 1.5 * u.pix)
+    assert resampled_upper_left.Tx == original_upper_left.Tx
+    assert resampled_upper_left.Ty == original_upper_left.Ty
+
+
 resample_test_data = [('linear', (100, 200) * u.pixel), ('neighbor', (128, 256) * u.pixel),
                       ('nearest', (512, 128) * u.pixel), ('spline', (200, 200) * u.pixel)]
 
diff --git a/sunpy/tests/figure_hashes_py36.json b/sunpy/tests/figure_hashes_py36.json
--- a/sunpy/tests/figure_hashes_py36.json
+++ b/sunpy/tests/figure_hashes_py36.json
@@ -1,6 +1,6 @@
 {
-    "sunpy.map.tests.test_compositemap.test_plot_composite_map": "be9eabc2f0bfa85eeb8dd9ddb2aab141f1937a3f206b234f47b33934e7d7c7fc",
-    "sunpy.map.tests.test_compositemap.test_plot_composite_map_linewidths": "be9eabc2f0bfa85eeb8dd9ddb2aab141f1937a3f206b234f47b33934e7d7c7fc",
+    "sunpy.map.tests.test_compositemap.test_plot_composite_map": "d6dacc4450e31f095b30b37b3dc3aac919c14a3bac5b299b4d82399f401f87b0",
+    "sunpy.map.tests.test_compositemap.test_plot_composite_map_linewidths": "d6dacc4450e31f095b30b37b3dc3aac919c14a3bac5b299b4d82399f401f87b0",
     "sunpy.map.tests.test_plotting.test_draw_contours_aia": "4256007c254efdb03dd6b6bd9a53ad89e695a8f4c22a016ba3bfcefe109f61fd",
     "sunpy.map.tests.test_plotting.test_draw_grid_aia171": "79fc8e423bb83d9b0cb164fe91e635f3c1b3e6fd98ca320752888f563f3184ac",
     "sunpy.map.tests.test_plotting.test_heliographic_grid_annotations": "83b3e21c8b421789ed69c7b17175394f740621cf097b028d9e9122db057c3f58",
@@ -13,13 +13,13 @@
     "sunpy.map.tests.test_plotting.test_peek_limb_aia171": "f6f6af1d8ab2365349598c46870d756d0de3b1c9b9f05986162d5926f5eb0ef9",
     "sunpy.map.tests.test_plotting.test_plot_aia171": "75774c889cdbc2ce692c0358bcae878bda5b25171d1cd43b955396e77dd24fd0",
     "sunpy.map.tests.test_plotting.test_plot_aia171_clip": "6201f0d15d0efe111380370c3bb0177c4fbae29ab41c7a75e4736c3d225dc0eb",
-    "sunpy.map.tests.test_plotting.test_plot_aia171_nowcsaxes": "17c8647224acfdcf123a924113aab520c44d286f83c5188598d040f6d257320e",
-    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel": "be27bf051c13fec7ef781d8977d29a2097b42378ce689cfe12327097c0126c8d",
-    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel_nowcsaxes": "d8b186711d81706df5442c205f4bf696734e9231ecc58d0e874f18e46f5077ea",
+    "sunpy.map.tests.test_plotting.test_plot_aia171_nowcsaxes": "d19b9e9ff52442c666d8579e35f54a019c7e6b46876765470dc2de5db04758ca",
+    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel": "7a15bea94ea3398e651066ac41023bd42508a1c54b5383a32e1d6bb46c26d4c3",
+    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel_nowcsaxes": "fbe8dc4e2ca3c1557e30af52b1bf9f3afda7c635432fbb807094b49b7ef91ca0",
     "sunpy.map.tests.test_plotting.test_plot_masked_aia171": "adddbb854800af4a662265d6b8d0969befd7da27fd6393a41e60037b74f383b7",
-    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_nowcsaxes": "317d560e1cc35e41662f71968d2b9847f8f24e99fe4db3069dd54772e407954e",
-    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel": "40d053eca1d23a24bec6b282978a30e0410b8a4e82286ace848b3c382c731c69",
-    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel_nowcsaxes": "0f0225378d2bd9820af2d2d526a804296496dcffa4e9a7659fc2bb91223a138b",
+    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_nowcsaxes": "c9964dedd3e0c7c49b73bc741154f6419ac775b11bf255506fafd8b488be3bf9",
+    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel": "c3c64de66ecf10fd3a648a818527ca03add507f3c9787b7d18126322b8bfe0b4",
+    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel_nowcsaxes": "30205651b4369d27e812881a0819b96899d6d26f347c4b316c7edc329f5530da",
     "sunpy.map.tests.test_plotting.test_rectangle_aia171": "b59915b551c809d66149c7ea3687c01b186138f0c6893d9c308bf06850b089cc",
     "sunpy.timeseries.tests.test_timeseriesbase.test_esp_peek": "c25e927aaa39d576482a35438acee56fab915aede44e62744eb0bf2cefc889b4",
     "sunpy.timeseries.tests.test_timeseriesbase.test_eve_peek": "d2e7093fa95dc1a4bbde958275bba31d9061967ab5ef003d1af4f9519658d14b",
diff --git a/sunpy/tests/figure_hashes_py37.json b/sunpy/tests/figure_hashes_py37.json
--- a/sunpy/tests/figure_hashes_py37.json
+++ b/sunpy/tests/figure_hashes_py37.json
@@ -1,6 +1,6 @@
 {
-    "sunpy.map.tests.test_compositemap.test_plot_composite_map": "be9eabc2f0bfa85eeb8dd9ddb2aab141f1937a3f206b234f47b33934e7d7c7fc",
-    "sunpy.map.tests.test_compositemap.test_plot_composite_map_linewidths": "be9eabc2f0bfa85eeb8dd9ddb2aab141f1937a3f206b234f47b33934e7d7c7fc",
+    "sunpy.map.tests.test_compositemap.test_plot_composite_map": "d6dacc4450e31f095b30b37b3dc3aac919c14a3bac5b299b4d82399f401f87b0",
+    "sunpy.map.tests.test_compositemap.test_plot_composite_map_linewidths": "d6dacc4450e31f095b30b37b3dc3aac919c14a3bac5b299b4d82399f401f87b0",
     "sunpy.map.tests.test_plotting.test_draw_contours_aia": "4256007c254efdb03dd6b6bd9a53ad89e695a8f4c22a016ba3bfcefe109f61fd",
     "sunpy.map.tests.test_plotting.test_draw_grid_aia171": "79fc8e423bb83d9b0cb164fe91e635f3c1b3e6fd98ca320752888f563f3184ac",
     "sunpy.map.tests.test_plotting.test_heliographic_grid_annotations": "83b3e21c8b421789ed69c7b17175394f740621cf097b028d9e9122db057c3f58",
@@ -13,13 +13,13 @@
     "sunpy.map.tests.test_plotting.test_peek_limb_aia171": "f6f6af1d8ab2365349598c46870d756d0de3b1c9b9f05986162d5926f5eb0ef9",
     "sunpy.map.tests.test_plotting.test_plot_aia171": "75774c889cdbc2ce692c0358bcae878bda5b25171d1cd43b955396e77dd24fd0",
     "sunpy.map.tests.test_plotting.test_plot_aia171_clip": "6201f0d15d0efe111380370c3bb0177c4fbae29ab41c7a75e4736c3d225dc0eb",
-    "sunpy.map.tests.test_plotting.test_plot_aia171_nowcsaxes": "17c8647224acfdcf123a924113aab520c44d286f83c5188598d040f6d257320e",
-    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel": "be27bf051c13fec7ef781d8977d29a2097b42378ce689cfe12327097c0126c8d",
-    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel_nowcsaxes": "d8b186711d81706df5442c205f4bf696734e9231ecc58d0e874f18e46f5077ea",
+    "sunpy.map.tests.test_plotting.test_plot_aia171_nowcsaxes": "d19b9e9ff52442c666d8579e35f54a019c7e6b46876765470dc2de5db04758ca",
+    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel": "7a15bea94ea3398e651066ac41023bd42508a1c54b5383a32e1d6bb46c26d4c3",
+    "sunpy.map.tests.test_plotting.test_plot_aia171_superpixel_nowcsaxes": "fbe8dc4e2ca3c1557e30af52b1bf9f3afda7c635432fbb807094b49b7ef91ca0",
     "sunpy.map.tests.test_plotting.test_plot_masked_aia171": "adddbb854800af4a662265d6b8d0969befd7da27fd6393a41e60037b74f383b7",
-    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_nowcsaxes": "317d560e1cc35e41662f71968d2b9847f8f24e99fe4db3069dd54772e407954e",
-    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel": "40d053eca1d23a24bec6b282978a30e0410b8a4e82286ace848b3c382c731c69",
-    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel_nowcsaxes": "0f0225378d2bd9820af2d2d526a804296496dcffa4e9a7659fc2bb91223a138b",
+    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_nowcsaxes": "c9964dedd3e0c7c49b73bc741154f6419ac775b11bf255506fafd8b488be3bf9",
+    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel": "c3c64de66ecf10fd3a648a818527ca03add507f3c9787b7d18126322b8bfe0b4",
+    "sunpy.map.tests.test_plotting.test_plot_masked_aia171_superpixel_nowcsaxes": "30205651b4369d27e812881a0819b96899d6d26f347c4b316c7edc329f5530da",
     "sunpy.map.tests.test_plotting.test_rectangle_aia171": "b59915b551c809d66149c7ea3687c01b186138f0c6893d9c308bf06850b089cc",
     "sunpy.timeseries.tests.test_timeseriesbase.test_esp_peek": "c25e927aaa39d576482a35438acee56fab915aede44e62744eb0bf2cefc889b4",
     "sunpy.timeseries.tests.test_timeseriesbase.test_eve_peek": "d2e7093fa95dc1a4bbde958275bba31d9061967ab5ef003d1af4f9519658d14b",