Repository: opendatacube/datacube-core

Issue:
Non-EPSG WKT projections are slower to create than standard EPSG projections
### Expected behaviour
`CRS` object creation should have near-constant execution times for valid input strings.

### Actual behaviour
`CRS` objects created using a valid WKT string that does not have an equivalent EPSG representation are created ~5x slower due to calling and failing `to_epsg` before attempting the WKT string representation. While the execution time is only different by ~25ms, this code snippet is run for each individual band that's loaded so larger load calls can see significant slowdowns (0.5s difference for a 20 band single MODIS tile for our deployment, a yearly timeseries takes 12mins vs 7mins with the change mentioned below).


### Steps to reproduce the behaviour

This snippet can be used to reproduce the behavior:
```Python
from datacube.utils.geometry import CRS
from pyproj import CRS as _CRS

crs = _CRS.from_user_input(
    'PROJCS["unnamed",GEOGCS["Unknown datum based upon the custom spheroid",DATUM["Not specified (based on custom spheroid)",SPHEROID["Custom spheroid",6371007.181,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Sinusoidal"],PARAMETER["longitude_of_center",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["Meter",1],AXIS["Easting",EAST],AXIS["Northing",NORTH]]'
)
CRS(crs)
```

By updating the [_guess_crs_string](https://github.com/opendatacube/datacube-core/blob/f19a816f1a8f59041cffe5e53a100c6426e11096/datacube/utils/geometry/_base.py#L134) function to move the `to_wkt` check above the `to_epsg` check you can verify the execution time differences. On my local machine the runtimes before/after the update are:
![image](https://user-images.githubusercontent.com/16520854/148820220-c3ac33e1-b9bb-49cd-b93e-385a4b4256c9.png)
 
Projections with an EPSG code remain the same:
![image](https://user-images.githubusercontent.com/16520854/148826365-8a5cfe49-3a3e-41a6-af57-2a9879b59a6f.png)


### Environment information

* Which ``datacube --version`` are you using? `datacube==1.8.6`, `pyproj==3.1.0`
* What datacube deployment/environment are you running against? Local environment. 


> **Note:** Stale issues will be automatically closed after a period of six months with no activity. 
> To ensure critical issues are not closed, tag them with the Github `pinned` tag .


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/opendatacube/datacube-core/pull/1223?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube) Report
> Merging [#1223](https://codecov.io/gh/opendatacube/datacube-core/pull/1223?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube) (2cf031a) into [develop](https://codecov.io/gh/opendatacube/datacube-core/commit/1a7f7b2528381ef78d880c67a58fa66aac9bcd44?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube) (1a7f7b2) will **decrease** coverage by `0.03%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/opendatacube/datacube-core/pull/1223/graphs/tree.svg?width=650&height=150&src=pr&token=TqpPsC1owf&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube)](https://codecov.io/gh/opendatacube/datacube-core/pull/1223?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube)

```diff
@@             Coverage Diff             @@
##           develop    #1223      +/-   ##
===========================================
- Coverage    93.79%   93.75%   -0.04%     
===========================================
  Files          102      102              
  Lines        10406    10406              
===========================================
- Hits          9760     9756       -4     
- Misses         646      650       +4     
```


| [Impacted Files](https://codecov.io/gh/opendatacube/datacube-core/pull/1223?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube) | Coverage Δ | |
|---|---|---|
| [datacube/utils/geometry/\_base.py](https://codecov.io/gh/opendatacube/datacube-core/pull/1223/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube#diff-ZGF0YWN1YmUvdXRpbHMvZ2VvbWV0cnkvX2Jhc2UucHk=) | `99.46% <100.00%> (-0.54%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/opendatacube/datacube-core/pull/1223?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/opendatacube/datacube-core/pull/1223?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube). Last update [1a7f7b2...2cf031a](https://codecov.io/gh/opendatacube/datacube-core/pull/1223?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatacube).


Patch:
diff --git a/datacube/utils/geometry/_base.py b/datacube/utils/geometry/_base.py
index 5c2760c7..43454159 100644
--- a/datacube/utils/geometry/_base.py
+++ b/datacube/utils/geometry/_base.py
@@ -141,12 +141,13 @@ def _guess_crs_str(crs_spec: Any) -> Optional[str]:
     if isinstance(crs_spec, dict):
         crs_spec = _CRS.from_dict(crs_spec)
 
+    if hasattr(crs_spec, 'to_wkt'):
+        return crs_spec.to_wkt()
     if hasattr(crs_spec, 'to_epsg'):
         epsg = crs_spec.to_epsg()
         if epsg is not None:
             return 'EPSG:{}'.format(crs_spec.to_epsg())
-    if hasattr(crs_spec, 'to_wkt'):
-        return crs_spec.to_wkt()
+
     return None
 
 


Test patch:
diff --git a/tests/test_geometry.py b/tests/test_geometry.py
index 8a004270..1c989e20 100644
--- a/tests/test_geometry.py
+++ b/tests/test_geometry.py
@@ -949,7 +949,7 @@ def test_crs():
 
     crs2 = CRS(crs)
     assert crs2 == crs
-    assert crs.proj is crs2.proj
+    assert crs.proj == crs2.proj
 
     assert epsg4326.valid_region == geometry.box(-180, -90, 180, 90, epsg4326)
     assert epsg3857.valid_region.crs == epsg4326
@@ -1475,7 +1475,7 @@ def test_crs_hash():
 
 
 def test_base_internals():
-    assert _guess_crs_str(CRS("epsg:3577")) == 'EPSG:3577'
+    assert _guess_crs_str(CRS("epsg:3577")) == epsg3577.to_wkt()
     no_epsg_crs = CRS(SAMPLE_WKT_WITHOUT_AUTHORITY)
     assert _guess_crs_str(no_epsg_crs) == no_epsg_crs.to_wkt()