Repository: rasterio/rasterio

Issue:
Throw a warning for RPC-based reprojection and supplied source CRS other than EPSG:4326
From [here](https://rasterio.readthedocs.io/en/latest/topics/reproject.html):
> When reprojecting a dataset with gcps or rpcs, the src_crs parameter should be supplied with the coordinate reference system that the gcps or rpcs are referenced against. By definition rpcs are always referenced against WGS84 ellipsoid with geographic coordinates (EPSG:4326).

I think it worth to throw a warning in case of reprojecting a dataset with rpcs and provided `src_crs` other than EPSG:4326.

cc @underchemist 


Discussion:
that makes sense to me, this also is a good opportunity for me to find a reference for the last statement in the quote there :)

Resolving patch:
diff --git a/rasterio/errors.py b/rasterio/errors.py
--- a/rasterio/errors.py
+++ b/rasterio/errors.py
@@ -60,7 +60,10 @@ class NotGeoreferencedWarning(UserWarning):
 
 
 class TransformWarning(UserWarning):
-    """Error raised when a GDALRPCTransform or GDALGCPTransform call fails."""
+    """Warn that coordinate transformations may behave unexpectedly"""
+
+class RPCError(ValueError):
+    """Raised when RPC transformation is invalid"""
 
 
 class ShapeSkipWarning(UserWarning):
diff --git a/rasterio/warp.py b/rasterio/warp.py
--- a/rasterio/warp.py
+++ b/rasterio/warp.py
@@ -1,6 +1,7 @@
 """Raster warping and reprojection."""
 
 from math import ceil, floor
+import warnings
 
 from affine import Affine
 import numpy as np
@@ -12,7 +13,7 @@
     from rasterio._base import _transform
     from rasterio.enums import Resampling
     from rasterio.env import GDALVersion, ensure_env, require_gdal_version
-    from rasterio.errors import GDALBehaviorChangeException, TransformError
+    from rasterio.errors import GDALBehaviorChangeException, TransformError, RPCError
     from rasterio.transform import array_bounds
     from rasterio._warp import (
         _calculate_default_transform,
@@ -312,12 +313,21 @@ def reproject(source, destination=None, src_transform=None, gcps=None, rpcs=None
 
             src_crs = src_crs or src_rdr.crs
 
+            # raise exception when reprojecting with rpcs using a CRS that is not EPSG:4326
+            if rpcs:
+                if isinstance(src_crs, str):
+                    src_crs_obj = rasterio.crs.CRS.from_string(src_crs)
+                else:
+                    src_crs_obj = src_crs
+                if src_crs is not None and src_crs_obj.to_epsg() != 4326:
+                    raise RPCError("Reprojecting with rational polynomial coefficients using source CRS other than EPSG:4326")
+
             if isinstance(src_bidx, int):
                 src_bidx = [src_bidx]
 
             src_count = len(src_bidx)
             src_height, src_width = src_shape
-            gcps = src_rdr.gcps[0] if src_rdr.gcps[0] else None
+            gcps = src_rdr.gcps[0] if src_rdr.gcps[0] and not rpcs else None
 
         dst_height = None
         dst_width = None


Test patch:
diff --git a/tests/test_warp.py b/tests/test_warp.py
--- a/tests/test_warp.py
+++ b/tests/test_warp.py
@@ -19,6 +19,7 @@
     CRSError,
     GDALVersionError,
     TransformError,
+    RPCError,
     WarpOperationError,
 )
 from rasterio.warp import (
@@ -2023,3 +2024,16 @@ def test_reproject_error_propagation(http_error_server, caplog):
             )
 
     assert len([rec for rec in caplog.records if "Retrying again" in rec.message]) == 2
+
+
+def test_rpcs_non_epsg4326():
+    with pytest.raises(RPCError):
+        with rasterio.open('tests/data/RGB.byte.rpc.vrt') as src:
+            src_rpcs = src.rpcs
+            reproject(
+                rasterio.band(src, src.indexes),
+                src_crs="EPSG:3857",
+                rpcs=src_rpcs,
+                dst_crs="EPSG:4326",
+                resampling=Resampling.nearest,
+            )
\ No newline at end of file