Repository: gammapy/gammapy (python)

Issue:
Angle wrapping fails for `PiecewiseNormSpatialModel`
I came across this problem in #5082 

# Example
Create `PiecewiseNormSpatialModel`
```python
from gammapy.maps import WcsGeom, MapCoord
import astropy.units as u

geom = WcsGeom.create(skydir=(0, 0), npix=(2, 2), binsz=0.3, frame="galactic")
coords = MapCoord.create(geom.footprint)
coords["lon"] *= u.deg
coords["lat"] *= u.deg

model = PiecewiseNormSpatialModel(coords, frame="galactic")
model.to_dict()
```
```python
Out[104]: 
{'spatial': {'type': 'PiecewiseNormSpatialModel',
  'frame': 'galactic',
  'parameters': [{'name': 'norm_0', 'value': 1.0},
   {'name': 'norm_1', 'value': 1.0},
   {'name': 'norm_2', 'value': 1.0},
   {'name': 'norm_3', 'value': 1.0}],
  'lon': {'data': [0.15, 0.15, -0.14999999999997726, -0.14999999999997726],
   'unit': 'deg'},
  'lat': {'data': [-0.15, 0.15, 0.15, -0.15], 'unit': 'deg'}}}
```
Creating a new instance from the dict changes the lon values:
```python
PiecewiseNormSpatialModel.from_dict(model.to_dict()).to_dict()
```
```python
{'spatial': {'type': 'PiecewiseNormSpatialModel',
  'frame': 'galactic',
  'parameters': [{'name': 'norm_0', 'value': 1.0},
   {'name': 'norm_1', 'value': 1.0},
   {'name': 'norm_2', 'value': 1.0},
   {'name': 'norm_3', 'value': 1.0}],
  'lon': {'data': [-359.85,
    -359.85,
    -0.14999999999997726,
    -0.14999999999997726],
   'unit': 'deg'},
  'lat': {'data': [-0.15, 0.15, 0.15, -0.15], 'unit': 'deg'}}}
```

This is because of the wrapping of the Angle in the init of the class
https://github.com/gammapy/gammapy/blob/90459a5bb99d7b22f0230627c05268d94212ffbc/gammapy/modeling/models/spatial.py#L1668-L1673

**But:** Why are the lon values not getting wrapped in the creation of the first model above?

Pull request:
Fix wrap in PiecewiseNormSpatialModel
Fix wrap in PiecewiseNormSpatialModel
Resolves #5084

Patch:
diff --git a/gammapy/modeling/models/spatial.py b/gammapy/modeling/models/spatial.py
index fe0f4dd6d..66c4c7724 100644
--- a/gammapy/modeling/models/spatial.py
+++ b/gammapy/modeling/models/spatial.py
@@ -1678,9 +1678,9 @@ class PiecewiseNormSpatialModel(SpatialModel):
 
     def __init__(self, coords, norms=None, interp="lin", **kwargs):
         self._coords = coords.copy()
-        self._coords["lon"] = Angle(coords.lon).wrap_at(0 * u.deg)
-        self._wrap_angle = (coords.lon.max() - coords.lon.min()) / 2
-        self._coords["lon"] = Angle(coords.lon).wrap_at(self._wrap_angle)
+        lon = Angle(coords.lon).wrap_at(0 * u.deg)
+        self._wrap_angle = (lon.max() - lon.min()) / 2
+        self._coords["lon"] = Angle(lon).wrap_at(self._wrap_angle)
         self._interp = interp
 
         if norms is None:
@@ -1726,6 +1726,7 @@ class PiecewiseNormSpatialModel(SpatialModel):
         coords = [value.value for value in self.coords._data.values()]
         # TODO: apply axes scaling in this loop
         coords = list(zip(*coords))
+        lon = Angle(lon).wrap_at(0 * u.deg)
         lon = Angle(lon).wrap_at(self._wrap_angle)
         # by default rely on CloughTocher2DInterpolator
         # (Piecewise cubic, C1 smooth, curvature-minimizing interpolant)


Test patch:
diff --git a/gammapy/modeling/models/tests/test_spatial.py b/gammapy/modeling/models/tests/test_spatial.py
index 4797e39ea..0fe757c78 100644
--- a/gammapy/modeling/models/tests/test_spatial.py
+++ b/gammapy/modeling/models/tests/test_spatial.py
@@ -706,6 +706,7 @@ def test_piecewise_spatial_model_gc():
 
     model_dict = model.to_dict()
     new_model = PiecewiseNormSpatialModel.from_dict(model_dict)
+    assert model_dict == new_model.to_dict()
 
     assert_allclose(new_model.evaluate_geom(geom.to_image()), expected, atol=1e-5)
 
@@ -720,11 +721,14 @@ def test_piecewise_spatial_model():
         geom = WcsGeom.create(
             skydir=(lon, 2.3), npix=(2, 2), binsz=0.3, frame="galactic"
         )
+
         coords = MapCoord.create(geom.footprint)
+        print(coords["lon"])
         coords["lon"] *= u.deg
         coords["lat"] *= u.deg
 
         model = PiecewiseNormSpatialModel(coords, frame="galactic")
+        print(model.coords["lon"].deg)
 
         assert_allclose(model(*geom.to_image().center_coord), 1.0)
 
@@ -741,6 +745,7 @@ def test_piecewise_spatial_model():
 
         model_dict = model.to_dict()
         new_model = PiecewiseNormSpatialModel.from_dict(model_dict)
+        assert model_dict == new_model.to_dict()
 
         assert_allclose(new_model.evaluate_geom(geom.to_image()), expected, atol=1e-5)