{"action":"created","issue":{"url":"https://api.github.com/repos/mapbox/rasterio/issues/430","repository_url":"https://api.github.com/repos/mapbox/rasterio","labels_url":"https://api.github.com/repos/mapbox/rasterio/issues/430/labels{/name}","comments_url":"https://api.github.com/repos/mapbox/rasterio/issues/430/comments","events_url":"https://api.github.com/repos/mapbox/rasterio/issues/430/events","html_url":"https://github.com/mapbox/rasterio/issues/430","id":97072647,"number":430,"title":"Adjust affine.EPSILON to allow matrix inversion when working with a numerically small cell size","user":{"login":"geowurster","id":2676083,"avatar_url":"https://avatars.githubusercontent.com/u/2676083?v=3","gravatar_id":"","url":"https://api.github.com/users/geowurster","html_url":"https://github.com/geowurster","followers_url":"https://api.github.com/users/geowurster/followers","following_url":"https://api.github.com/users/geowurster/following{/other_user}","gists_url":"https://api.github.com/users/geowurster/gists{/gist_id}","starred_url":"https://api.github.com/users/geowurster/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/geowurster/subscriptions","organizations_url":"https://api.github.com/users/geowurster/orgs","repos_url":"https://api.github.com/users/geowurster/repos","events_url":"https://api.github.com/users/geowurster/events{/privacy}","received_events_url":"https://api.github.com/users/geowurster/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/mapbox/rasterio/labels/bug","name":"bug","color":"fc2929"},{"url":"https://api.github.com/repos/mapbox/rasterio/labels/intermediate","name":"intermediate","color":"1d76db"},{"url":"https://api.github.com/repos/mapbox/rasterio/labels/proj","name":"proj","color":"fef2c0"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/mapbox/rasterio/milestones/8","html_url":"https://github.com/mapbox/rasterio/milestones/1.0","labels_url":"https://api.github.com/repos/mapbox/rasterio/milestones/8/labels","id":686337,"number":8,"title":"1.0","description":null,"creator":{"login":"sgillies","id":33697,"avatar_url":"https://avatars.githubusercontent.com/u/33697?v=3","gravatar_id":"","url":"https://api.github.com/users/sgillies","html_url":"https://github.com/sgillies","followers_url":"https://api.github.com/users/sgillies/followers","following_url":"https://api.github.com/users/sgillies/following{/other_user}","gists_url":"https://api.github.com/users/sgillies/gists{/gist_id}","starred_url":"https://api.github.com/users/sgillies/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgillies/subscriptions","organizations_url":"https://api.github.com/users/sgillies/orgs","repos_url":"https://api.github.com/users/sgillies/repos","events_url":"https://api.github.com/users/sgillies/events{/privacy}","received_events_url":"https://api.github.com/users/sgillies/received_events","type":"User","site_admin":false},"open_issues":36,"closed_issues":23,"state":"open","created_at":"2014-06-10T00:10:13Z","updated_at":"2016-04-04T16:05:32Z","due_on":null,"closed_at":null},"comments":1,"created_at":"2015-07-24T15:04:42Z","updated_at":"2016-04-05T15:25:46Z","closed_at":null,"body":"The affine transformation for a mid to high resolution image in a geographic CRS creates an affine transformation that cannot be inverted.  The default affine settings think the matrix will collapse to an effective area of zero, even though in reality it represents a large area.\r\n\r\nTwo proposed solutions so far by @sgillies: incorporate an `affine.EPSILON` value into the CRS, or adjust the value when affine is import in `rasterio.transform`.\r\n\r\nFor reference:\r\n\r\n- Default `affine.EPSILON` is `1e-5`.\r\n- Landsat 8 scene with center point at `(-78.44728303391639, 38.892281158941344)` is `-9.990276844954696e-08`.\r\n- NAIP dataset with a center point at `(-78.25038895262065, 39.56306925990321)` is `-1.0719267342552452e-10`.\r\n\r\nSee https://github.com/sgillies/affine/issues/16 for some more background."},"comment":{"url":"https://api.github.com/repos/mapbox/rasterio/issues/comments/205857304","html_url":"https://github.com/mapbox/rasterio/issues/430#issuecomment-205857304","issue_url":"https://api.github.com/repos/mapbox/rasterio/issues/430","id":205857304,"user":{"login":"perrygeo","id":1151287,"avatar_url":"https://avatars.githubusercontent.com/u/1151287?v=3","gravatar_id":"","url":"https://api.github.com/users/perrygeo","html_url":"https://github.com/perrygeo","followers_url":"https://api.github.com/users/perrygeo/followers","following_url":"https://api.github.com/users/perrygeo/following{/other_user}","gists_url":"https://api.github.com/users/perrygeo/gists{/gist_id}","starred_url":"https://api.github.com/users/perrygeo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/perrygeo/subscriptions","organizations_url":"https://api.github.com/users/perrygeo/orgs","repos_url":"https://api.github.com/users/perrygeo/repos","events_url":"https://api.github.com/users/perrygeo/events{/privacy}","received_events_url":"https://api.github.com/users/perrygeo/received_events","type":"User","site_admin":false},"created_at":"2016-04-05T15:25:46Z","updated_at":"2016-04-05T15:25:46Z","body":"Currently, anywhere a user grabs `src.affine`, depending on the `affine.EPSILON` global we could see different behavior.  \r\n\r\n```\r\n>>> src.affine\r\nAffine(0.00121485571987208, 0.0, -120.93749999989646,\r\n       0.0, -0.00121485571987208, 38.82259097616849)\r\n>>> ~src.affine\r\n...\r\nTransformNotInvertibleError: Cannot invert degenerate transform\r\n>>> aff = src.affine\r\n>>> import affine\r\n>>> affine.set_epsilon(src.affine.determinant * 0.1)\r\n>>> ~src.affine\r\nAffine(823.1430149625476, 0.0, 99548.85837194786,\r\n       0.0, -823.1430149625476, 31956.544584781124)\r\n>>> ~aff\r\n...\r\nTransformNotInvertibleError: Cannot invert degenerate transform\r\n```\r\n\r\nThat's sort of surprising behavior. The invertability of an affine instance is determined by the global EPSILON. And because closes over the EPSILON value whenever we the object is constructed, we have no way of changing it (do we even have a way to query it?). But more to the point, there's nothing we can do to truly prevent problems since the behavior is determined by a global variable. Python modules are effectively singletons so having a mutable global variable leading to behavior like this:\r\n\r\n```\r\n>>> import affine as affineA\r\n>>> import affine as affineB\r\n>>> affineA.EPSILON\r\n1e-05\r\n>>> affineB.EPSILON\r\n1e-05\r\n>>> instA = affineA.Affine(0.001215, 0.0, -120.9375, 0.0, -0.001215, 38.823)\r\n>>> instA.is_degenerate\r\nTrue\r\n>>> affineA.set_epsilon(1e-20)\r\n>>> instA.is_degenerate  # doesn't change existing instances\r\nTrue\r\n>>> instB = affineB.Affine(0.001215, 0.0, -120.9375, 0.0, -0.001215, 38.823)\r\n>>> instB.is_degenerate  # but it does change new instances, even from different module names\r\nFalse\r\n>>> affineB.EPSILON\r\n1e-20\r\n>>> affineA.EPSILON\r\n1e-20\r\n```\r\n\r\nTwo thoughts\r\n* We should address this in the affine module by making epsilon a property of the Affine class, not a global. Maybe add a `Affine.adjust_epsilon_assume_invertable()` method. Then in rasterio, everywhere we construct an affine transform, we we could check for invertability and bump up the epsilon for that instance.\r\n* Would there be any harm in defaulting the EPSILON to a very low value like `1e-40`?\r\n"}}