Repository: rasterio/rasterio

Issue:
Standardize use of GDAL geotransforms and Affine() in the internal API
_See https://github.com/mapbox/rasterio/pull/763#issuecomment-224481538 for background_

Part of prepping for 1.0 is to [remove deprecated parameters](https://github.com/mapbox/rasterio/issues/516), which involves switching from GDAL style geotransforms to `affine.Affine()`.  https://github.com/mapbox/rasterio/pull/763 switches the public API over to `transform=affine.Affine()`, but the internal API (i.e. `rasterize -> _rasterize`) still uses GDAL geotransforms, which is fine, but we still call it a `transform`.  For instance, `rasterio._io.InMemoryRaster()` has a property called `transform` that returns a GDAL geotransform, which presents a confusing naming conflict when compared to `src.transform` now returning an `Affine()`.



Discussion:
We may also want to modify the `_transform` function and property in `_base.pyx` 

It seems like the issue is largely naming, correct? IOW we could just use the naming convention of `gdal_transform` and `transform` to distinguish between the two. And if we've done it right, the `gdal_transform` will never be exposed in the public API.

@perrygeo partly naming, and partly where operations occur.  Your naming convention makes sense.  I think we should push nearly all operations that use GDAL transforms down into the Cython layer (since these can happen shortly prior to calling the GDAL C functions), rather than sometimes at the interface between Python and Cython layer as it is now with reproject.  Then the only GDAL transform operations that will likely remain publicly accessible will be in `Affine` for conversion.

As mentioned in https://github.com/mapbox/rasterio/pull/763#discussion_r67634948 we need to rename `src.get_transform()` as well.

A quick inspection shows that we are still converting the transforms in `reproject` to GDAL transforms before calling into Cython, so this issue still applies if we want to pursue it.

Resolving patch:
diff --git a/rasterio/features.py b/rasterio/features.py
--- a/rasterio/features.py
+++ b/rasterio/features.py
@@ -105,7 +105,7 @@ def shapes(source, mask=None, connectivity=4, transform=IDENTITY):
 
     """
     transform = guard_transform(transform)
-    for s, v in _shapes(source, mask, connectivity, transform.to_gdal()):
+    for s, v in _shapes(source, mask, connectivity, transform):
         yield s, v
 
 
@@ -308,7 +308,7 @@ def format_cast_error(param, dtype):
         raise ValueError("width and height must be > 0")
 
     transform = guard_transform(transform)
-    _rasterize(valid_shapes, out, transform.to_gdal(), all_touched, merge_alg)
+    _rasterize(valid_shapes, out, transform, all_touched, merge_alg)
     return out
 
 
@@ -476,21 +476,21 @@ def is_valid_geom(geom):
         if geom_type == 'MultiLineString':
             # Multi lines must have at least one LineString
             return (len(coords) > 0 and len(coords[0]) >= 2 and
-                    len(coords[0][0]) >=2)
+                    len(coords[0][0]) >= 2)
 
         if geom_type == 'Polygon':
             # Polygons must have at least 1 ring, with at least 4 coordinates,
             # with at least x, y for a coordinate
             return (len(coords) > 0 and len(coords[0]) >= 4 and
-                    len(coords[0][0]) >=2)
+                    len(coords[0][0]) >= 2)
 
         if geom_type == 'MultiPolygon':
             # Muti polygons must have at least one Polygon
             return (len(coords) > 0 and len(coords[0]) > 0 and
-                    len(coords[0][0]) >= 4 and len(coords[0][0][0]) >=2)
+                    len(coords[0][0]) >= 4 and len(coords[0][0][0]) >= 2)
 
     if geom_type == 'GeometryCollection':
-        if not 'geometries' in geom:
+        if 'geometries' not in geom:
             return False
 
         if not len(geom['geometries']) > 0:
diff --git a/rasterio/warp.py b/rasterio/warp.py
--- a/rasterio/warp.py
+++ b/rasterio/warp.py
@@ -7,7 +7,6 @@
 from math import ceil
 
 from affine import Affine
-from affine import identity
 import numpy as np
 
 import rasterio
@@ -17,7 +16,6 @@
 from rasterio.enums import Resampling
 from rasterio.env import ensure_env, GDALVersion, require_gdal_version
 from rasterio.errors import GDALBehaviorChangeException
-from rasterio.transform import guard_transform
 
 
 # Gauss (7) is not supported for warp
@@ -257,14 +255,16 @@ def reproject(source, destination, src_transform=None, gcps=None,
     out: None
         Output is written to destination.
     """
+
+    # Only one type of georeferencing is permitted.
     if src_transform and gcps:
         raise ValueError("src_transform and gcps parameters may not"
                          "be used together.")
 
-    # Resampling guard.
+    # Guard against invalid or unsupported resampling algorithms.
     try:
-        if resampling == 7:  # gauss resampling is not supported
-            raise ValueError
+        if resampling == 7:
+            raise ValueError("Gauss resampling is not supported")
 
         Resampling(resampling)
 
@@ -274,40 +274,16 @@ def reproject(source, destination, src_transform=None, gcps=None,
                 ['Resampling.{0}'.format(r.name) for r in
                  SUPPORTED_RESAMPLING])))
 
-    # If working with identity transform, assume it is crs-less data
-    # and that translating the matrix very slightly will avoid #674
-    eps = 1e-100
-    if src_transform:
-        src_transform = guard_transform(src_transform)
-        # if src_transform is like `identity` with positive or negative `e`,
-        # translate matrix very slightly to avoid #674 and #1272.
-        if src_transform.almost_equals(identity) or src_transform.almost_equals(Affine(1, 0, 0, 0, -1, 0)):
-            src_transform = src_transform.translation(eps, eps)
-    if dst_transform:
-        dst_transform = guard_transform(dst_transform)
-        if dst_transform.almost_equals(identity) or dst_transform.almost_equals(Affine(1, 0, 0, 0, -1, 0)):
-            dst_transform = dst_transform.translation(eps, eps)
-
-    if src_transform:
-        src_transform = guard_transform(src_transform).to_gdal()
-    if dst_transform:
-        dst_transform = guard_transform(dst_transform).to_gdal()
-
-    # Passing None can cause segfault, use empty dict
-    if src_crs is None:
-        src_crs = {}
-    if dst_crs is None:
-        dst_crs = {}
-
+    # Call the function in our extension module.
     _reproject(source, destination, src_transform, gcps, src_crs, src_nodata,
                dst_transform, dst_crs, dst_nodata, resampling,
                init_dest_nodata, **kwargs)
 
 
 @ensure_env
-def calculate_default_transform(src_crs, dst_crs, width, height,
-                                left=None, bottom=None, right=None, top=None,
-                                gcps=None, resolution=None):
+def calculate_default_transform(
+        src_crs, dst_crs, width, height, left=None, bottom=None, right=None,
+        top=None, gcps=None, resolution=None):
     """Output dimensions and transform for a reprojection.
 
     Source and destination coordinate reference systems and output


Test patch: