Repository: sunpy/sunpy

Issue:
Functionality to convert hpc to hcc with different observer and vice versa issue: #2712
Previously an user cannot convert hpc to hcc for different observer (#2712). This changes will allow user to convert between hpc to hcc. This is done by converting hcc ->hgs->hcc 

Closes #2754


Discussion:

Resolving patch:
diff --git a/sunpy/coordinates/transformations.py b/sunpy/coordinates/transformations.py
--- a/sunpy/coordinates/transformations.py
+++ b/sunpy/coordinates/transformations.py
@@ -41,7 +41,7 @@
 __all__ = ['hgs_to_hgc', 'hgc_to_hgs', 'hcc_to_hpc',
            'hpc_to_hcc', 'hcc_to_hgs', 'hgs_to_hcc',
            'hpc_to_hpc',
-           'hcrs_to_hgs', 'hgs_to_hcrs']
+           'hcrs_to_hgs', 'hgs_to_hcrs', 'hcc_to_hcc']
 
 
 def _carrington_offset(obstime):
@@ -117,12 +117,9 @@ def hcc_to_hpc(helioccoord, heliopframe):
     Convert from Heliocentic Cartesian to Helioprojective Cartesian.
     """
     if not _observers_are_equal(helioccoord.observer, heliopframe.observer):
-        raise ConvertError("Cannot directly transform heliocentric coordinates to "
-                           "helioprojective coordinates for different "
-                           "observers {} and {}. See discussion in this GH issue: "
-                           "https://github.com/sunpy/sunpy/issues/2712. Try converting to "
-                           "an intermediate heliographic Stonyhurst frame.".format(
-                               helioccoord.observer, heliopframe.observer))
+        heliocframe = Heliocentric(observer=heliopframe.observer)
+        new_helioccoord = helioccoord.transform_to(heliocframe)
+        helioccoord = new_helioccoord
 
     x = helioccoord.x.to(u.m)
     y = helioccoord.y.to(u.m)
@@ -148,12 +145,10 @@ def hpc_to_hcc(heliopcoord, heliocframe):
     Convert from Helioprojective Cartesian to Heliocentric Cartesian.
     """
     if not _observers_are_equal(heliopcoord.observer, heliocframe.observer):
-        raise ConvertError("Cannot directly transform helioprojective coordinates to "
-                           "heliocentric coordinates for different "
-                           "observers {} and {}. See discussion in this GH issue: "
-                           "https://github.com/sunpy/sunpy/issues/2712. Try converting to "
-                           "an intermediate heliographic Stonyhurst frame.".format(
-                               heliopcoord.observer, heliocframe.observer))
+        heliocframe_heliopobs = Heliocentric(observer=heliopcoord.observer)
+        helioccoord_heliopobs = heliopcoord.transform_to(heliocframe_heliopobs)
+        helioccoord = helioccoord_heliopobs.transform_to(heliocframe)
+        return helioccoord
 
     if not isinstance(heliopcoord.observer, BaseCoordinateFrame):
         raise ConvertError("Cannot transform helioprojective coordinates to "
@@ -354,11 +349,25 @@ def hgs_to_hcrs(hgscoord, hcrsframe):
 
 
 @frame_transform_graph.transform(FunctionTransform, HeliographicStonyhurst, HeliographicStonyhurst)
-def hgs_to_hgs(from_coo, to_frame):
-    if np.all(from_coo.obstime == to_frame.obstime):
-        return to_frame.realize_frame(from_coo.data)
+def hgs_to_hgs(hgscoord, hgsframe):
+    if np.all(hgscoord.obstime == hgsframe.obstime):
+        return hgsframe.realize_frame(hgscoord.data)
     else:
-        return from_coo.transform_to(HCRS).transform_to(to_frame)
+        return hgscoord.transform_to(HCRS).transform_to(hgsframe)
 
 
+@frame_transform_graph.transform(FunctionTransform, Heliocentric, Heliocentric)
+def hcc_to_hcc(hcccoord, hccframe):
+    """
+    Convert from  Heliocentric to Heliocentric
+    """
+    if _observers_are_equal(hcccoord.observer, hccframe.observer, string_ok=True):
+        return hccframe.realize_frame(hcccoord._data)
+
+    hgscoord = hcccoord.transform_to(HeliographicStonyhurst)
+    hgscoord.observer = hccframe.observer
+    hcccoord = hgscoord.transform_to(hccframe)
+
+    return hcccoord
+
 __doc__ += make_transform_graph_docs()


Test patch:
diff --git a/sunpy/coordinates/tests/test_transformations.py b/sunpy/coordinates/tests/test_transformations.py
--- a/sunpy/coordinates/tests/test_transformations.py
+++ b/sunpy/coordinates/tests/test_transformations.py
@@ -215,8 +215,7 @@ def test_hgs_cartesian_rep_to_hgc():
 def test_hcc_to_hpc_different_observer():
     # This test checks transformation HCC->HPC in the case where the HCC and HPC frames are
     # defined by different observers.
-    # NOTE: This test is currently expected to fail because the HCC<->HPC transformation does
-    # not account for observer location. It will be updated once this is fixed.
+
     rsun = 1*u.m
     D0 = 1*u.km
     L0 = 1*u.deg
@@ -225,15 +224,16 @@ def test_hcc_to_hpc_different_observer():
     hcc_frame = Heliocentric(observer=observer_1)
     hpc_frame = Helioprojective(observer=observer_2)
     hcccoord = SkyCoord(x=rsun, y=rsun, z=rsun, frame=hcc_frame)
-    with pytest.raises(ConvertError):
-        hcccoord.transform_to(hpc_frame)
-
+    hpccoord_out = hcccoord.transform_to(hpc_frame)
+    hpccoord_expected = hcccoord.transform_to(HeliographicStonyhurst).transform_to(hpc_frame)
+    assert_quantity_allclose(hpccoord_out.Tx, hpccoord_expected.Tx)
+    assert_quantity_allclose(hpccoord_out.Ty, hpccoord_expected.Ty)
+    assert_quantity_allclose(hpccoord_out.distance, hpccoord_expected.distance)
 
 def test_hpc_to_hcc_different_observer():
     # This test checks transformation HPC->HCC in the case where the HCC and HPC frames are
     # defined by different observers.
-    # NOTE: This test is currently expected to fail because the HCC<->HPC transformation does
-    # not account for observer location. It will be updated once this is fixed.
+
     D0 = 1*u.km
     L0 = 1*u.deg
     observer_1 = HeliographicStonyhurst(lat=0*u.deg, lon=0*u.deg, radius=D0)
@@ -241,5 +241,37 @@ def test_hpc_to_hcc_different_observer():
     hcc_frame = Heliocentric(observer=observer_1)
     hpc_frame = Helioprojective(observer=observer_2)
     hpccoord = SkyCoord(Tx=0*u.arcsec, Ty=0*u.arcsec, frame=hpc_frame)
-    with pytest.raises(ConvertError):
-        hpccoord.transform_to(hcc_frame)
+    hcccoord_out = hpccoord.transform_to(hcc_frame)
+    hcccoord_expected = hpccoord.transform_to(HeliographicStonyhurst).transform_to(hcc_frame)
+    assert_quantity_allclose(hcccoord_out.x, hcccoord_expected.x)
+    assert_quantity_allclose(hcccoord_out.y, hcccoord_expected.y)
+    assert_quantity_allclose(hcccoord_out.z, hcccoord_expected.z)
+
+def test_hcc_to_hpc_same_observer():
+    # This test checks transformation HCC->HPC in the case of same observer
+
+    rsun = 1*u.m
+    D0 = 1*u.km
+    observer = HeliographicStonyhurst(lat=0*u.deg, lon=0*u.deg, radius=D0)
+    hcc_frame = Heliocentric(observer=observer)
+    hpc_frame = Helioprojective(observer=observer)
+    hcccoord = SkyCoord(x=rsun, y=rsun, z=rsun, frame=hcc_frame)
+    hpccoord_out = hcccoord.transform_to(hpc_frame)
+    hpccoord_expected = hcccoord.transform_to(HeliographicStonyhurst).transform_to(hpc_frame)
+    assert_quantity_allclose(hpccoord_out.Tx, hpccoord_expected.Tx)
+    assert_quantity_allclose(hpccoord_out.Ty, hpccoord_expected.Ty)
+    assert_quantity_allclose(hpccoord_out.distance, hpccoord_expected.distance)
+
+def test_hpc_to_hcc_same_observer():
+    # This test checks transformation HPC->HCC in the case of same observer
+
+    D0 = 1 * u.km
+    observer = HeliographicStonyhurst(lat=0 * u.deg, lon=0 * u.deg, radius=D0)
+    hcc_frame = Heliocentric(observer=observer)
+    hpc_frame = Helioprojective(observer=observer)
+    hpccoord = SkyCoord(Tx=0 * u.arcsec, Ty=0 * u.arcsec, frame=hpc_frame)
+    hcccoord_out = hpccoord.transform_to(hcc_frame)
+    hcccoord_expected = hpccoord.transform_to(HeliographicStonyhurst).transform_to(hcc_frame)
+    assert_quantity_allclose(hcccoord_out.x, hcccoord_expected.x)
+    assert_quantity_allclose(hcccoord_out.y, hcccoord_expected.y)
+    assert_quantity_allclose(hcccoord_out.z, hcccoord_expected.z)