Repository: rasterio/rasterio

Issue:
Cannot use calculate_default_transform()
This may be user error, but...
Doing my best to call calculate_default_transform(), using release '1.0a3' on Mac OS 10.10.5.  My builds of rasterio/gdal came to me via conda-forge.  

gdalwarp --version => GDAL 2.1.2, released 2016/10/24

Here's a snippet of code that fails:
from rasterio.warp import calculate_default_transform
src = {'datum': 'WGS84', 'ellps': 'WGS84', 'proj': 'longlat', 'no_defs': True}
dst = {'datum': 'WGS84', 'ellps': 'WGS84', 'zone': 40, 'no_defs': True, 'proj': 'utm', 'units': 'm'}
srcwidth,srcheight = 100,100
dst_affine, dst_width, dst_height = _calculate_default_transform(src,dst,srcwidth,srcheight)

I get the following error, which I do not really understand:

CPLE_AppDefinedError: The transformation is already "north up" or a transformation between pixel/line and georeferenced coordinates cannot be computed for cb968650-5093-45bf-befe-9743b7fc8515. There is no affine transformation and no GCPs. Specify transformation option SRC_METHOD=NO_GEOTRANSFORM to bypass this check.

Also, the method definition indicates that input width,height are required but the documentation string says that they are optional.


Discussion:
@mhearne-usgs: `width, height, left, bottom, right, top` are only optional *if* you are using ground control points. Without GCPs, they are required. 

Can you try it with 
```
calculate_default_transform(src,dst, srcwidth, srcheight, left, bottom, right, top)
```
and let us know how that works. 

We should clarify the function signature and docstrings to make this more clear plus add some additional logic to check for this case. Thanks for pointing out the discrepancy.

Resolving patch:
diff --git a/rasterio/warp.py b/rasterio/warp.py
--- a/rasterio/warp.py
+++ b/rasterio/warp.py
@@ -295,11 +295,11 @@ def calculate_default_transform(src_crs, dst_crs, width, height,
         Example: CRS({'init': 'EPSG:4326'})
     dst_crs: CRS or dict
         Target coordinate reference system.
-    width, height: int, optional
-        Source raster width and height. Not needed if gcps are used.
+    width, height: int
+        Source raster width and height.
     left, bottom, right, top: float, optional
         Bounding coordinates in src_crs, from the bounds property of a
-        raster.
+        raster. Required unless using gcps.
     gcps: sequence of GroundControlPoint, optional
         Instead of a bounding box for the source, a sequence of ground
         control points may be provided.
@@ -327,6 +327,10 @@ def calculate_default_transform(src_crs, dst_crs, width, height,
         raise ValueError("Bounding values and ground control points may not"
                          "be used together.")
 
+    if any(x is None for x in (left, bottom, right, top)) and not gcps:
+        raise ValueError("Either four bounding values or ground control points"
+                         "must be specified")
+
     dst_affine, dst_width, dst_height = _calculate_default_transform(
         src_crs, dst_crs, width, height, left, bottom, right, top, gcps)
 


Test patch:
diff --git a/tests/test_warp_transform.py b/tests/test_warp_transform.py
--- a/tests/test_warp_transform.py
+++ b/tests/test_warp_transform.py
@@ -17,6 +17,13 @@ def test_gcps_bounds_exclusivity():
             'epsg:4326', 'epsg:3857', width=1, height=1, left=1.0, gcps=[1])
 
 
+def test_one_of_gcps_bounds():
+    """at least one of gcps or bounds parameters must be provided"""
+    with pytest.raises(ValueError):
+        calculate_default_transform(
+            'epsg:4326', 'epsg:3857', width=1, height=1)
+
+
 def test_identity():
     """Get the same transform and dimensions back for same crs."""
     # Tile: [53, 96, 8]
@@ -132,3 +139,15 @@ def test_gdal_transform_fail_dst_crs_xfail():
                 bottom=30,
                 right=-80,
                 top=70)
+
+
+def test_gcps_calculate_transform():
+    src_gcps = [
+        GroundControlPoint(row=0, col=0, x=156113, y=2818720, z=0),
+        GroundControlPoint(row=0, col=800, x=338353, y=2785790, z=0),
+        GroundControlPoint(row=800, col=800, x=297939, y=2618518, z=0),
+        GroundControlPoint(row=800, col=0, x=115698, y=2651448, z=0)]
+    _, width, height = calculate_default_transform(
+        'epsg:3857', 'epsg:4326', width=800, height=800, gcps=src_gcps)
+    assert width == 1087
+    assert height == 895