We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cryptic error from MapMaker / make_counts_image
I accidentally typed this:
```python
import astropy.units as u
from gammapy.maps import WcsGeom
from gammapy.cube import MapMaker
from gammapy.data import DataStore
data_store = DataStore.from_dir('$GAMMAPY_EXTRA/datasets/cta-1dc/index/gps/')
obs_id = [110380, 111140, 111159]
obs_list = data_store.obs_list(obs_id)
geom = WcsGeom.create(
    skydir=(0, 0),
    npix=(800, 600),
    binsz=0.02,
    coordsys='GAL',
)
maker = MapMaker(geom, offset_max=u.Quantity('2 deg'))
images = maker.run(obs_list)
```
and it blows up with a cryptic error message:
```
$ python temp.py 
|===========================================>--------------------------------------------------------------------------------------|   1 /  3  (33.33%) ETA     0sTraceback (most recent call last):
  File "temp.py", line 15, in <module>
    images = maker.run(obs_list)
  File "/Users/deil/work/code/gammapy/gammapy/cube/new.py", line 324, in run
    self.process_obs(obs)
  File "/Users/deil/work/code/gammapy/gammapy/cube/new.py", line 280, in process_obs
    obs.events, cutout_geom, obs.pointing_radec, self.offset_max,
  File "/Users/deil/work/code/gammapy/gammapy/cube/new.py", line 79, in make_map_counts
    counts_map.data[:, offset_mask] = 0
IndexError: too many indices for array
```

The problem is in `make_map_counts` here:
https://github.com/gammapy/gammapy/blob/a013ff8ac532ab8b15cee95c5da2abb8937bde9c/gammapy/cube/new.py#L79

It doesn't work for 2D images.

There's other obvious issues one encounters when making maps, e.g. replacing `offset_max=u.Quantity('2 deg')` with `offset_max='2 deg'` above gives another cryptic error, because the mapmaker just does `self.offset_max = offset_max` but should do `self.offset_max = Angle(offset_max)` to be kind to users.

The solution is to rewrite the functions in `new.py` to take a mask instead of a max offset, and to improve their test coverage, e.g. also trying to run them on a 2D geom (and either succeed, or error out with a good error message).

I consider this high priority, we should do that tomorrow.

@registerrier - you or me?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gammapy/cube/make.py`
Content:
```
1 # Licensed under a 3-clause BSD style license - see LICENSE.rst
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 import logging
4 from astropy.utils.console import ProgressBar
5 from astropy.nddata.utils import PartialOverlapError
6 from astropy.coordinates import Angle
7 from ..maps import WcsNDMap
8 from .counts import make_map_counts
9 from .exposure import make_map_exposure_true_energy
10 from .background import make_map_background_irf, make_map_background_fov
11 
12 __all__ = [
13     'MapMaker',
14 ]
15 
16 log = logging.getLogger(__name__)
17 
18 
19 class MapMaker(object):
20     """Make all basic maps from observations.
21 
22     Parameters
23     ----------
24     geom : `~gammapy.maps.WcsGeom`
25         Reference image geometry
26     offset_max : `~astropy.coordinates.Angle`
27         Maximum offset angle
28     cutout_mode : {'trim', 'strict'}, optional
29         Options for making cutouts, see :func: `~gammapy.maps.WcsNDMap.make_cutout`
30         Should be left to the default value 'trim'
31         unless you want only fully contained observations to be added to the map
32     """
33 
34     def __init__(self, geom, offset_max, cutout_mode="trim"):
35         self.geom = geom
36         self.offset_max = Angle(offset_max)
37 
38         # We instantiate the end products of the MakeMaps class
39         self.counts_map = WcsNDMap(self.geom)
40 
41         self.exposure_map = WcsNDMap(self.geom, unit="m2 s")
42 
43         self.background_map = WcsNDMap(self.geom)
44 
45         # We will need this general exclusion mask for the analysis
46         self.exclusion_map = WcsNDMap(self.geom)
47         self.exclusion_map.data += 1
48 
49         self.cutout_mode = cutout_mode
50         self.maps = {}
51 
52     def process_obs(self, obs):
53         """Process one observation and add it to the cutout image
54 
55         Parameters
56         ----------
57         obs : `~gammapy.data.DataStoreObservation`
58             Observation
59         """
60         # First make cutout of the global image
61         try:
62             exclusion_mask_cutout, cutout_slices = self.exclusion_map.make_cutout(
63                 obs.pointing_radec, 2 * self.offset_max, mode=self.cutout_mode
64             )
65         except PartialOverlapError:
66             # TODO: can we silently do the right thing here? Discuss
67             log.info("Observation {} not fully contained in target image. Skipping it.".format(obs.obs_id))
68             return
69 
70         cutout_geom = exclusion_mask_cutout.geom
71 
72         offset = exclusion_mask_cutout.geom.separation(obs.pointing_radec)
73         offset_mask = offset >= self.offset_max
74 
75         counts_obs_map = make_map_counts(obs.events, cutout_geom)
76         counts_obs_map.data[:, offset_mask] = 0
77 
78         expo_obs_map = make_map_exposure_true_energy(
79             obs.pointing_radec, obs.observation_live_time_duration,
80             obs.aeff, cutout_geom
81         )
82         expo_obs_map.data[:, offset_mask] = 0
83 
84         acceptance_obs_map = make_map_background_irf(
85             obs.pointing_radec, obs.observation_live_time_duration,
86             obs.bkg, cutout_geom
87         )
88         acceptance_obs_map.data[:, offset_mask] = 0
89 
90         background_obs_map = make_map_background_fov(
91             acceptance_obs_map, counts_obs_map, exclusion_mask_cutout,
92         )
93         background_obs_map.data[:, offset_mask] = 0
94 
95         self._add_cutouts(cutout_slices, counts_obs_map, expo_obs_map, background_obs_map)
96 
97     def _add_cutouts(self, cutout_slices, counts_obs_map, expo_obs_map, acceptance_obs_map):
98         """Add current cutout to global maps."""
99         self.counts_map.data[cutout_slices] += counts_obs_map.data
100         self.exposure_map.data[cutout_slices] += expo_obs_map.quantity.to(self.exposure_map.unit).value
101         self.background_map.data[cutout_slices] += acceptance_obs_map.data
102 
103     def run(self, obs_list):
104         """
105         Run MapMaker for a list of observations to create
106         stacked counts, exposure and background maps
107 
108         Parameters
109         --------------
110         obs_list: `~gammapy.data.ObservationList`
111             List of observations
112 
113         Returns
114         -----------
115         maps: dict of stacked counts, background and exposure maps.
116         """
117         for obs in ProgressBar(obs_list):
118             self.process_obs(obs)
119 
120         self.maps = {
121             'counts_map': self.counts_map,
122             'background_map': self.background_map,
123             'exposure_map': self.exposure_map
124         }
125         return self.maps
126
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gammapy/cube/make.py b/gammapy/cube/make.py
--- a/gammapy/cube/make.py
+++ b/gammapy/cube/make.py
@@ -32,6 +32,9 @@
     """
 
     def __init__(self, geom, offset_max, cutout_mode="trim"):
+        if geom.is_image:
+            raise ValueError('MapMaker only works with geom with an energy axis')
+
         self.geom = geom
         self.offset_max = Angle(offset_max)

{"golden_diff": "diff --git a/gammapy/cube/make.py b/gammapy/cube/make.py\n--- a/gammapy/cube/make.py\n+++ b/gammapy/cube/make.py\n@@ -32,6 +32,9 @@\n     \"\"\"\n \n     def __init__(self, geom, offset_max, cutout_mode=\"trim\"):\n+        if geom.is_image:\n+            raise ValueError('MapMaker only works with geom with an energy axis')\n+\n         self.geom = geom\n         self.offset_max = Angle(offset_max)\n", "issue": "Cryptic error from MapMaker / make_counts_image\nI accidentally typed this:\r\n```python\r\nimport astropy.units as u\r\nfrom gammapy.maps import WcsGeom\r\nfrom gammapy.cube import MapMaker\r\nfrom gammapy.data import DataStore\r\ndata_store = DataStore.from_dir('$GAMMAPY_EXTRA/datasets/cta-1dc/index/gps/')\r\nobs_id = [110380, 111140, 111159]\r\nobs_list = data_store.obs_list(obs_id)\r\ngeom = WcsGeom.create(\r\n    skydir=(0, 0),\r\n    npix=(800, 600),\r\n    binsz=0.02,\r\n    coordsys='GAL',\r\n)\r\nmaker = MapMaker(geom, offset_max=u.Quantity('2 deg'))\r\nimages = maker.run(obs_list)\r\n```\r\nand it blows up with a cryptic error message:\r\n```\r\n$ python temp.py \r\n|===========================================>--------------------------------------------------------------------------------------|   1 /  3  (33.33%) ETA     0sTraceback (most recent call last):\r\n  File \"temp.py\", line 15, in <module>\r\n    images = maker.run(obs_list)\r\n  File \"/Users/deil/work/code/gammapy/gammapy/cube/new.py\", line 324, in run\r\n    self.process_obs(obs)\r\n  File \"/Users/deil/work/code/gammapy/gammapy/cube/new.py\", line 280, in process_obs\r\n    obs.events, cutout_geom, obs.pointing_radec, self.offset_max,\r\n  File \"/Users/deil/work/code/gammapy/gammapy/cube/new.py\", line 79, in make_map_counts\r\n    counts_map.data[:, offset_mask] = 0\r\nIndexError: too many indices for array\r\n```\r\n\r\nThe problem is in `make_map_counts` here:\r\nhttps://github.com/gammapy/gammapy/blob/a013ff8ac532ab8b15cee95c5da2abb8937bde9c/gammapy/cube/new.py#L79\r\n\r\nIt doesn't work for 2D images.\r\n\r\nThere's other obvious issues one encounters when making maps, e.g. replacing `offset_max=u.Quantity('2 deg')` with `offset_max='2 deg'` above gives another cryptic error, because the mapmaker just does `self.offset_max = offset_max` but should do `self.offset_max = Angle(offset_max)` to be kind to users.\r\n\r\nThe solution is to rewrite the functions in `new.py` to take a mask instead of a max offset, and to improve their test coverage, e.g. also trying to run them on a 2D geom (and either succeed, or error out with a good error message).\r\n\r\nI consider this high priority, we should do that tomorrow.\r\n\r\n@registerrier - you or me?\n", "before_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\nfrom __future__ import absolute_import, division, print_function, unicode_literals\nimport logging\nfrom astropy.utils.console import ProgressBar\nfrom astropy.nddata.utils import PartialOverlapError\nfrom astropy.coordinates import Angle\nfrom ..maps import WcsNDMap\nfrom .counts import make_map_counts\nfrom .exposure import make_map_exposure_true_energy\nfrom .background import make_map_background_irf, make_map_background_fov\n\n__all__ = [\n    'MapMaker',\n]\n\nlog = logging.getLogger(__name__)\n\n\nclass MapMaker(object):\n    \"\"\"Make all basic maps from observations.\n\n    Parameters\n    ----------\n    geom : `~gammapy.maps.WcsGeom`\n        Reference image geometry\n    offset_max : `~astropy.coordinates.Angle`\n        Maximum offset angle\n    cutout_mode : {'trim', 'strict'}, optional\n        Options for making cutouts, see :func: `~gammapy.maps.WcsNDMap.make_cutout`\n        Should be left to the default value 'trim'\n        unless you want only fully contained observations to be added to the map\n    \"\"\"\n\n    def __init__(self, geom, offset_max, cutout_mode=\"trim\"):\n        self.geom = geom\n        self.offset_max = Angle(offset_max)\n\n        # We instantiate the end products of the MakeMaps class\n        self.counts_map = WcsNDMap(self.geom)\n\n        self.exposure_map = WcsNDMap(self.geom, unit=\"m2 s\")\n\n        self.background_map = WcsNDMap(self.geom)\n\n        # We will need this general exclusion mask for the analysis\n        self.exclusion_map = WcsNDMap(self.geom)\n        self.exclusion_map.data += 1\n\n        self.cutout_mode = cutout_mode\n        self.maps = {}\n\n    def process_obs(self, obs):\n        \"\"\"Process one observation and add it to the cutout image\n\n        Parameters\n        ----------\n        obs : `~gammapy.data.DataStoreObservation`\n            Observation\n        \"\"\"\n        # First make cutout of the global image\n        try:\n            exclusion_mask_cutout, cutout_slices = self.exclusion_map.make_cutout(\n                obs.pointing_radec, 2 * self.offset_max, mode=self.cutout_mode\n            )\n        except PartialOverlapError:\n            # TODO: can we silently do the right thing here? Discuss\n            log.info(\"Observation {} not fully contained in target image. Skipping it.\".format(obs.obs_id))\n            return\n\n        cutout_geom = exclusion_mask_cutout.geom\n\n        offset = exclusion_mask_cutout.geom.separation(obs.pointing_radec)\n        offset_mask = offset >= self.offset_max\n\n        counts_obs_map = make_map_counts(obs.events, cutout_geom)\n        counts_obs_map.data[:, offset_mask] = 0\n\n        expo_obs_map = make_map_exposure_true_energy(\n            obs.pointing_radec, obs.observation_live_time_duration,\n            obs.aeff, cutout_geom\n        )\n        expo_obs_map.data[:, offset_mask] = 0\n\n        acceptance_obs_map = make_map_background_irf(\n            obs.pointing_radec, obs.observation_live_time_duration,\n            obs.bkg, cutout_geom\n        )\n        acceptance_obs_map.data[:, offset_mask] = 0\n\n        background_obs_map = make_map_background_fov(\n            acceptance_obs_map, counts_obs_map, exclusion_mask_cutout,\n        )\n        background_obs_map.data[:, offset_mask] = 0\n\n        self._add_cutouts(cutout_slices, counts_obs_map, expo_obs_map, background_obs_map)\n\n    def _add_cutouts(self, cutout_slices, counts_obs_map, expo_obs_map, acceptance_obs_map):\n        \"\"\"Add current cutout to global maps.\"\"\"\n        self.counts_map.data[cutout_slices] += counts_obs_map.data\n        self.exposure_map.data[cutout_slices] += expo_obs_map.quantity.to(self.exposure_map.unit).value\n        self.background_map.data[cutout_slices] += acceptance_obs_map.data\n\n    def run(self, obs_list):\n        \"\"\"\n        Run MapMaker for a list of observations to create\n        stacked counts, exposure and background maps\n\n        Parameters\n        --------------\n        obs_list: `~gammapy.data.ObservationList`\n            List of observations\n\n        Returns\n        -----------\n        maps: dict of stacked counts, background and exposure maps.\n        \"\"\"\n        for obs in ProgressBar(obs_list):\n            self.process_obs(obs)\n\n        self.maps = {\n            'counts_map': self.counts_map,\n            'background_map': self.background_map,\n            'exposure_map': self.exposure_map\n        }\n        return self.maps\n", "path": "gammapy/cube/make.py"}], "after_files": [{"content": "# Licensed under a 3-clause BSD style license - see LICENSE.rst\nfrom __future__ import absolute_import, division, print_function, unicode_literals\nimport logging\nfrom astropy.utils.console import ProgressBar\nfrom astropy.nddata.utils import PartialOverlapError\nfrom astropy.coordinates import Angle\nfrom ..maps import WcsNDMap\nfrom .counts import make_map_counts\nfrom .exposure import make_map_exposure_true_energy\nfrom .background import make_map_background_irf, make_map_background_fov\n\n__all__ = [\n    'MapMaker',\n]\n\nlog = logging.getLogger(__name__)\n\n\nclass MapMaker(object):\n    \"\"\"Make all basic maps from observations.\n\n    Parameters\n    ----------\n    geom : `~gammapy.maps.WcsGeom`\n        Reference image geometry\n    offset_max : `~astropy.coordinates.Angle`\n        Maximum offset angle\n    cutout_mode : {'trim', 'strict'}, optional\n        Options for making cutouts, see :func: `~gammapy.maps.WcsNDMap.make_cutout`\n        Should be left to the default value 'trim'\n        unless you want only fully contained observations to be added to the map\n    \"\"\"\n\n    def __init__(self, geom, offset_max, cutout_mode=\"trim\"):\n        if geom.is_image:\n            raise ValueError('MapMaker only works with geom with an energy axis')\n\n        self.geom = geom\n        self.offset_max = Angle(offset_max)\n\n        # We instantiate the end products of the MakeMaps class\n        self.counts_map = WcsNDMap(self.geom)\n\n        self.exposure_map = WcsNDMap(self.geom, unit=\"m2 s\")\n\n        self.background_map = WcsNDMap(self.geom)\n\n        # We will need this general exclusion mask for the analysis\n        self.exclusion_map = WcsNDMap(self.geom)\n        self.exclusion_map.data += 1\n\n        self.cutout_mode = cutout_mode\n        self.maps = {}\n\n    def process_obs(self, obs):\n        \"\"\"Process one observation and add it to the cutout image\n\n        Parameters\n        ----------\n        obs : `~gammapy.data.DataStoreObservation`\n            Observation\n        \"\"\"\n        # First make cutout of the global image\n        try:\n            exclusion_mask_cutout, cutout_slices = self.exclusion_map.make_cutout(\n                obs.pointing_radec, 2 * self.offset_max, mode=self.cutout_mode\n            )\n        except PartialOverlapError:\n            # TODO: can we silently do the right thing here? Discuss\n            log.info(\"Observation {} not fully contained in target image. Skipping it.\".format(obs.obs_id))\n            return\n\n        cutout_geom = exclusion_mask_cutout.geom\n\n        offset = exclusion_mask_cutout.geom.separation(obs.pointing_radec)\n        offset_mask = offset >= self.offset_max\n\n        counts_obs_map = make_map_counts(obs.events, cutout_geom)\n        counts_obs_map.data[:, offset_mask] = 0\n\n        expo_obs_map = make_map_exposure_true_energy(\n            obs.pointing_radec, obs.observation_live_time_duration,\n            obs.aeff, cutout_geom\n        )\n        expo_obs_map.data[:, offset_mask] = 0\n\n        acceptance_obs_map = make_map_background_irf(\n            obs.pointing_radec, obs.observation_live_time_duration,\n            obs.bkg, cutout_geom\n        )\n        acceptance_obs_map.data[:, offset_mask] = 0\n\n        background_obs_map = make_map_background_fov(\n            acceptance_obs_map, counts_obs_map, exclusion_mask_cutout,\n        )\n        background_obs_map.data[:, offset_mask] = 0\n\n        self._add_cutouts(cutout_slices, counts_obs_map, expo_obs_map, background_obs_map)\n\n    def _add_cutouts(self, cutout_slices, counts_obs_map, expo_obs_map, acceptance_obs_map):\n        \"\"\"Add current cutout to global maps.\"\"\"\n        self.counts_map.data[cutout_slices] += counts_obs_map.data\n        self.exposure_map.data[cutout_slices] += expo_obs_map.quantity.to(self.exposure_map.unit).value\n        self.background_map.data[cutout_slices] += acceptance_obs_map.data\n\n    def run(self, obs_list):\n        \"\"\"\n        Run MapMaker for a list of observations to create\n        stacked counts, exposure and background maps\n\n        Parameters\n        --------------\n        obs_list: `~gammapy.data.ObservationList`\n            List of observations\n\n        Returns\n        -----------\n        maps: dict of stacked counts, background and exposure maps.\n        \"\"\"\n        for obs in ProgressBar(obs_list):\n            self.process_obs(obs)\n\n        self.maps = {\n            'counts_map': self.counts_map,\n            'background_map': self.background_map,\n            'exposure_map': self.exposure_map\n        }\n        return self.maps\n", "path": "gammapy/cube/make.py"}]}