{"action":"created","comment":{"url":"https://api.github.com/repos/astropy/astropy/pulls/comments/34364782","id":34364782,"diff_hunk":"@@ -456,3 +469,267 @@ def block_replicate(data, block_size, conserve_sum=True):\n         data = data / float(np.prod(block_size))\n \n     return data\n+\n+\n+class Cutout(object):\n+    def __init__(self, data, position, shape, wcs=None, mode='trim',\n+                 fill_value=np.nan):\n+        \"\"\"\n+        Create a cutout object from a 2D array.\n+\n+        The returned object will contain the 2D cutout array and the\n+        updated WCS, if a `~astropy.wcs.WCS` object is input.\n+\n+        Parameters\n+        ----------\n+        data : `~numpy.ndarray`\n+            The 2D data array from which to extract the cutout array.\n+\n+        position : tuple or `~astropy.coordinates.SkyCoord`\n+            The position of the cutout array's center with respect to\n+            the ``data`` array.  The position can be specified either as\n+            a ``(y, x)`` (``(row, col)``) tuple of pixel coordinates or\n+            a `~astropy.coordinates.SkyCoord`, in which case ``wcs`` is\n+            a required input.  For pixel coordinates, integer positions\n+            are at the pixel centers.\n+\n+        shape : tuple\n+            The shape of the cutout array in pixel coordinates (but see\n+            the ``mode`` keyword for additional details).\n+\n+        wcs : `~astropy.wcs.WCS`, optional\n+            A WCS object associated with the input ``data`` array.  If\n+            ``wcs`` is not `None`, then the returned cutout object will\n+            contain the updated WCS for the cutout data array.\n+\n+        mode : {'trim', 'partial', 'strict'}, optional\n+            The mode used for creating the cutout data array.  For the\n+            ``'partial'`` and ``'trim'`` modes, a partial overlap of the\n+            cutout array and the input ``data`` array is sufficient.\n+            For the ``'strict'`` mode, the cutout array has to be fully\n+            contained within the ``data`` array, otherwise an\n+            `~astropy.nddata.utils.PartialOverlapError` is raised.   In\n+            all modes, non-overlapping arrays will raise a\n+            `~astropy.nddata.utils.NoOverlapError`.  In ``'partial'``\n+            mode, positions in the cutout array that do not overlap with\n+            the ``data`` array will be filled with ``fill_value``.  In\n+            ``'trim'`` mode only the overlapping elements are returned,\n+            thus the resulting cutout array may be smaller than the\n+            requested ``shape``.\n+\n+        fill_value : number, optional\n+            If ``mode='partial'``, the value to fill pixels in the\n+            cutout array that do not overlap with the input ``data``.\n+            ``fill_value`` must have the same ``dtype`` as the input\n+            ``data`` array.\n+\n+        Returns\n+        -------\n+        result : `~astropy.nddata.utils.Cutout`\n+            A cutout object containing the 2D cutout data array and the\n+            updated WCS, if ``wcs`` is input.\n+\n+        Examples\n+        --------\n+        >>> import numpy as np\n+        >>> from astropy.nddata.utils import Cutout\n+        >>> data = np.arange(20.).reshape(5, 4)\n+        >>> c1 = Cutout(data, (2, 2), (3, 3))\n+        >>> print(c1.data)\n+        [[  5.   6.   7.]\n+         [  9.  10.  11.]\n+         [ 13.  14.  15.]]\n+\n+        >>> print(c1.center_large)\n+        (2.0, 2.0)\n+        >>> print(c1.center_small)\n+        (1.0, 1.0)\n+        >>> print(c1.origin_large)\n+        (1, 1)\n+\n+        >>> c2 = Cutout(data, (0, 0), (3, 3))\n+        >>> print(c2.data)\n+        [[ 0.  1.]\n+         [ 4.  5.]]\n+\n+        >>> c3 = Cutout(data, (0, 0), (3, 3), mode='partial')\n+        >>> print(c3.data)\n+        [[ nan  nan  nan]\n+         [ nan   0.   1.]\n+         [ nan   4.   5.]]\n+        \"\"\"\n+\n+        if isinstance(position, SkyCoord):\n+            if wcs is None:\n+                raise ValueError('wcs must be input if position is a '\n+                                 'SkyCoord')\n+            position = skycoord_to_pixel(position, wcs, mode='all')\n+\n+        if mode not in ['partial', 'trim', 'strict']:\n+            raise ValueError(\"Valid modes are 'partial', 'trim', and \"\n+                             \"'strict'.\")\n+\n+        data = np.asanyarray(data)\n+        self.data, self.input_position_small  = extract_array(data, shape,\n+            position, mode=mode, fill_value=fill_value, return_position=True)\n+\n+        slices_large, slices_small = overlap_slices(data.shape, shape,\n+                                                    position, mode=mode)\n+        self.slices_large = slices_large\n+        self.slices_small = slices_small\n+\n+        self.shape = self.data.shape\n+        self.input_position_large = position\n+        self.shape_input = shape\n+\n+        (self.ymin_large, self.xmin_large,\n+         self.ymax_large, self.xmax_large) = self.bbox_large\n+\n+        (self.ymin_small, self.xmin_small,\n+         self.ymax_small, self.xmax_small) = self.bbox_small\n+\n+        # the true origin pixel of the cutout array, including any\n+        # filled cutout values\n+        self._origin_large_true = (self.origin_large[1] -\n+                                   self.slices_small[1].start,\n+                                   self.origin_large[0] -\n+                                   self.slices_small[0].start)\n+\n+        if wcs is not None:\n+            self.wcs = copy.deepcopy(wcs)\n+            self.wcs.wcs.crpix -= self._origin_large_true\n+        else:\n+            self.wcs = None\n+\n+    def to_large(self, position):\n+        \"\"\"\n+        Calculate the pixel position in the large array corresponding to\n+        the input pixel position in the cutout array.\n+\n+        Parameters\n+        ----------\n+        position : tuple\n+            The ``(y, x)`` pixel position in the cutout array.\n+\n+        Returns\n+        -------\n+        position : tuple\n+            The corresponding ``(y, x)`` pixel position in the large\n+            array.\n+        \"\"\"\n+        return tuple(position[i] + self.origin_large[i] for i in [0, 1])","path":"astropy/nddata/utils.py","position":439,"original_position":439,"commit_id":"8e58bbcc06c7617f73f611743b2e47e160d6ecd3","original_commit_id":"8e58bbcc06c7617f73f611743b2e47e160d6ecd3","user":{"login":"cdeil","id":852409,"avatar_url":"https://avatars.githubusercontent.com/u/852409?v=3","gravatar_id":"","url":"https://api.github.com/users/cdeil","html_url":"https://github.com/cdeil","followers_url":"https://api.github.com/users/cdeil/followers","following_url":"https://api.github.com/users/cdeil/following{/other_user}","gists_url":"https://api.github.com/users/cdeil/gists{/gist_id}","starred_url":"https://api.github.com/users/cdeil/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdeil/subscriptions","organizations_url":"https://api.github.com/users/cdeil/orgs","repos_url":"https://api.github.com/users/cdeil/repos","events_url":"https://api.github.com/users/cdeil/events{/privacy}","received_events_url":"https://api.github.com/users/cdeil/received_events","type":"User","site_admin":false},"body":"The `to_large` and `from_large` one-line methods are the only ones not covered by unit tests.\r\nI think they are covered by docs tests, but those don't run for Python 3, so maybe useful to add a unit test for those?","created_at":"2015-07-10T15:21:17Z","updated_at":"2015-07-10T15:21:17Z","html_url":"https://github.com/astropy/astropy/pull/3823#discussion_r34364782","pull_request_url":"https://api.github.com/repos/astropy/astropy/pulls/3823","_links":{"self":{"href":"https://api.github.com/repos/astropy/astropy/pulls/comments/34364782"},"html":{"href":"https://github.com/astropy/astropy/pull/3823#discussion_r34364782"},"pull_request":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3823"}}},"pull_request":{"url":"https://api.github.com/repos/astropy/astropy/pulls/3823","id":37065586,"html_url":"https://github.com/astropy/astropy/pull/3823","diff_url":"https://github.com/astropy/astropy/pull/3823.diff","patch_url":"https://github.com/astropy/astropy/pull/3823.patch","issue_url":"https://api.github.com/repos/astropy/astropy/issues/3823","number":3823,"state":"open","locked":false,"title":"Postage stamp cutouts with WCS","user":{"login":"larrybradley","id":4992897,"avatar_url":"https://avatars.githubusercontent.com/u/4992897?v=3","gravatar_id":"","url":"https://api.github.com/users/larrybradley","html_url":"https://github.com/larrybradley","followers_url":"https://api.github.com/users/larrybradley/followers","following_url":"https://api.github.com/users/larrybradley/following{/other_user}","gists_url":"https://api.github.com/users/larrybradley/gists{/gist_id}","starred_url":"https://api.github.com/users/larrybradley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larrybradley/subscriptions","organizations_url":"https://api.github.com/users/larrybradley/orgs","repos_url":"https://api.github.com/users/larrybradley/repos","events_url":"https://api.github.com/users/larrybradley/events{/privacy}","received_events_url":"https://api.github.com/users/larrybradley/received_events","type":"User","site_admin":false},"body":"This PR is an implementation of https://github.com/astropy/astropy/issues/3594.  I still need to add tests.  ","created_at":"2015-06-05T20:09:58Z","updated_at":"2015-07-10T15:21:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"76a5d1af3085ea4c0eaf0f292e383f1dc9fa84a2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/astropy/astropy/pulls/3823/commits","review_comments_url":"https://api.github.com/repos/astropy/astropy/pulls/3823/comments","review_comment_url":"https://api.github.com/repos/astropy/astropy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/astropy/astropy/issues/3823/comments","statuses_url":"https://api.github.com/repos/astropy/astropy/statuses/8e58bbcc06c7617f73f611743b2e47e160d6ecd3","head":{"label":"larrybradley:cutout","ref":"cutout","sha":"8e58bbcc06c7617f73f611743b2e47e160d6ecd3","user":{"login":"larrybradley","id":4992897,"avatar_url":"https://avatars.githubusercontent.com/u/4992897?v=3","gravatar_id":"","url":"https://api.github.com/users/larrybradley","html_url":"https://github.com/larrybradley","followers_url":"https://api.github.com/users/larrybradley/followers","following_url":"https://api.github.com/users/larrybradley/following{/other_user}","gists_url":"https://api.github.com/users/larrybradley/gists{/gist_id}","starred_url":"https://api.github.com/users/larrybradley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larrybradley/subscriptions","organizations_url":"https://api.github.com/users/larrybradley/orgs","repos_url":"https://api.github.com/users/larrybradley/repos","events_url":"https://api.github.com/users/larrybradley/events{/privacy}","received_events_url":"https://api.github.com/users/larrybradley/received_events","type":"User","site_admin":false},"repo":{"id":13567623,"name":"astropy","full_name":"larrybradley/astropy","owner":{"login":"larrybradley","id":4992897,"avatar_url":"https://avatars.githubusercontent.com/u/4992897?v=3","gravatar_id":"","url":"https://api.github.com/users/larrybradley","html_url":"https://github.com/larrybradley","followers_url":"https://api.github.com/users/larrybradley/followers","following_url":"https://api.github.com/users/larrybradley/following{/other_user}","gists_url":"https://api.github.com/users/larrybradley/gists{/gist_id}","starred_url":"https://api.github.com/users/larrybradley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larrybradley/subscriptions","organizations_url":"https://api.github.com/users/larrybradley/orgs","repos_url":"https://api.github.com/users/larrybradley/repos","events_url":"https://api.github.com/users/larrybradley/events{/privacy}","received_events_url":"https://api.github.com/users/larrybradley/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/larrybradley/astropy","description":"My fork of the Repository for the Astropy core package","fork":true,"url":"https://api.github.com/repos/larrybradley/astropy","forks_url":"https://api.github.com/repos/larrybradley/astropy/forks","keys_url":"https://api.github.com/repos/larrybradley/astropy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/larrybradley/astropy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/larrybradley/astropy/teams","hooks_url":"https://api.github.com/repos/larrybradley/astropy/hooks","issue_events_url":"https://api.github.com/repos/larrybradley/astropy/issues/events{/number}","events_url":"https://api.github.com/repos/larrybradley/astropy/events","assignees_url":"https://api.github.com/repos/larrybradley/astropy/assignees{/user}","branches_url":"https://api.github.com/repos/larrybradley/astropy/branches{/branch}","tags_url":"https://api.github.com/repos/larrybradley/astropy/tags","blobs_url":"https://api.github.com/repos/larrybradley/astropy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/larrybradley/astropy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/larrybradley/astropy/git/refs{/sha}","trees_url":"https://api.github.com/repos/larrybradley/astropy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/larrybradley/astropy/statuses/{sha}","languages_url":"https://api.github.com/repos/larrybradley/astropy/languages","stargazers_url":"https://api.github.com/repos/larrybradley/astropy/stargazers","contributors_url":"https://api.github.com/repos/larrybradley/astropy/contributors","subscribers_url":"https://api.github.com/repos/larrybradley/astropy/subscribers","subscription_url":"https://api.github.com/repos/larrybradley/astropy/subscription","commits_url":"https://api.github.com/repos/larrybradley/astropy/commits{/sha}","git_commits_url":"https://api.github.com/repos/larrybradley/astropy/git/commits{/sha}","comments_url":"https://api.github.com/repos/larrybradley/astropy/comments{/number}","issue_comment_url":"https://api.github.com/repos/larrybradley/astropy/issues/comments{/number}","contents_url":"https://api.github.com/repos/larrybradley/astropy/contents/{+path}","compare_url":"https://api.github.com/repos/larrybradley/astropy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/larrybradley/astropy/merges","archive_url":"https://api.github.com/repos/larrybradley/astropy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/larrybradley/astropy/downloads","issues_url":"https://api.github.com/repos/larrybradley/astropy/issues{/number}","pulls_url":"https://api.github.com/repos/larrybradley/astropy/pulls{/number}","milestones_url":"https://api.github.com/repos/larrybradley/astropy/milestones{/number}","notifications_url":"https://api.github.com/repos/larrybradley/astropy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/larrybradley/astropy/labels{/name}","releases_url":"https://api.github.com/repos/larrybradley/astropy/releases{/id}","created_at":"2013-10-14T17:09:24Z","updated_at":"2015-06-24T17:39:31Z","pushed_at":"2015-07-08T03:50:14Z","git_url":"git://github.com/larrybradley/astropy.git","ssh_url":"git@github.com:larrybradley/astropy.git","clone_url":"https://github.com/larrybradley/astropy.git","svn_url":"https://github.com/larrybradley/astropy","homepage":"http://astropy.org","size":36463,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"astropy:master","ref":"master","sha":"6d0e5b050c5b71be9049ec662143f5b55b2a9a41","user":{"login":"astropy","id":847984,"avatar_url":"https://avatars.githubusercontent.com/u/847984?v=3","gravatar_id":"","url":"https://api.github.com/users/astropy","html_url":"https://github.com/astropy","followers_url":"https://api.github.com/users/astropy/followers","following_url":"https://api.github.com/users/astropy/following{/other_user}","gists_url":"https://api.github.com/users/astropy/gists{/gist_id}","starred_url":"https://api.github.com/users/astropy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astropy/subscriptions","organizations_url":"https://api.github.com/users/astropy/orgs","repos_url":"https://api.github.com/users/astropy/repos","events_url":"https://api.github.com/users/astropy/events{/privacy}","received_events_url":"https://api.github.com/users/astropy/received_events","type":"Organization","site_admin":false},"repo":{"id":2081289,"name":"astropy","full_name":"astropy/astropy","owner":{"login":"astropy","id":847984,"avatar_url":"https://avatars.githubusercontent.com/u/847984?v=3","gravatar_id":"","url":"https://api.github.com/users/astropy","html_url":"https://github.com/astropy","followers_url":"https://api.github.com/users/astropy/followers","following_url":"https://api.github.com/users/astropy/following{/other_user}","gists_url":"https://api.github.com/users/astropy/gists{/gist_id}","starred_url":"https://api.github.com/users/astropy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astropy/subscriptions","organizations_url":"https://api.github.com/users/astropy/orgs","repos_url":"https://api.github.com/users/astropy/repos","events_url":"https://api.github.com/users/astropy/events{/privacy}","received_events_url":"https://api.github.com/users/astropy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/astropy/astropy","description":"Repository for the Astropy core package","fork":false,"url":"https://api.github.com/repos/astropy/astropy","forks_url":"https://api.github.com/repos/astropy/astropy/forks","keys_url":"https://api.github.com/repos/astropy/astropy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/astropy/astropy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/astropy/astropy/teams","hooks_url":"https://api.github.com/repos/astropy/astropy/hooks","issue_events_url":"https://api.github.com/repos/astropy/astropy/issues/events{/number}","events_url":"https://api.github.com/repos/astropy/astropy/events","assignees_url":"https://api.github.com/repos/astropy/astropy/assignees{/user}","branches_url":"https://api.github.com/repos/astropy/astropy/branches{/branch}","tags_url":"https://api.github.com/repos/astropy/astropy/tags","blobs_url":"https://api.github.com/repos/astropy/astropy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/astropy/astropy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/astropy/astropy/git/refs{/sha}","trees_url":"https://api.github.com/repos/astropy/astropy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/astropy/astropy/statuses/{sha}","languages_url":"https://api.github.com/repos/astropy/astropy/languages","stargazers_url":"https://api.github.com/repos/astropy/astropy/stargazers","contributors_url":"https://api.github.com/repos/astropy/astropy/contributors","subscribers_url":"https://api.github.com/repos/astropy/astropy/subscribers","subscription_url":"https://api.github.com/repos/astropy/astropy/subscription","commits_url":"https://api.github.com/repos/astropy/astropy/commits{/sha}","git_commits_url":"https://api.github.com/repos/astropy/astropy/git/commits{/sha}","comments_url":"https://api.github.com/repos/astropy/astropy/comments{/number}","issue_comment_url":"https://api.github.com/repos/astropy/astropy/issues/comments{/number}","contents_url":"https://api.github.com/repos/astropy/astropy/contents/{+path}","compare_url":"https://api.github.com/repos/astropy/astropy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/astropy/astropy/merges","archive_url":"https://api.github.com/repos/astropy/astropy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/astropy/astropy/downloads","issues_url":"https://api.github.com/repos/astropy/astropy/issues{/number}","pulls_url":"https://api.github.com/repos/astropy/astropy/pulls{/number}","milestones_url":"https://api.github.com/repos/astropy/astropy/milestones{/number}","notifications_url":"https://api.github.com/repos/astropy/astropy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/astropy/astropy/labels{/name}","releases_url":"https://api.github.com/repos/astropy/astropy/releases{/id}","created_at":"2011-07-21T01:33:49Z","updated_at":"2015-07-09T12:43:45Z","pushed_at":"2015-07-10T04:28:23Z","git_url":"git://github.com/astropy/astropy.git","ssh_url":"git@github.com:astropy/astropy.git","clone_url":"https://github.com/astropy/astropy.git","svn_url":"https://github.com/astropy/astropy","homepage":"www.astropy.org","size":65283,"stargazers_count":602,"watchers_count":602,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":406,"mirror_url":null,"open_issues_count":565,"forks":406,"open_issues":565,"watchers":602,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3823"},"html":{"href":"https://github.com/astropy/astropy/pull/3823"},"issue":{"href":"https://api.github.com/repos/astropy/astropy/issues/3823"},"comments":{"href":"https://api.github.com/repos/astropy/astropy/issues/3823/comments"},"review_comments":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3823/comments"},"review_comment":{"href":"https://api.github.com/repos/astropy/astropy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/astropy/astropy/pulls/3823/commits"},"statuses":{"href":"https://api.github.com/repos/astropy/astropy/statuses/8e58bbcc06c7617f73f611743b2e47e160d6ecd3"}}}}