Repository: opendatacube/datacube-core

Issue:
Cannot pass `odc.geo` GeoBoxes to `dc.load`
### Expected behaviour
A user may try to use an `odc.geo` GeoBox to load data from `datacube-core`. e.g.:

```
from odc.geo.geobox import GeoBox

gbox = GeoBox.from_bbox(
    [149.036528545, -35.384897602, 149.233835134, -35.198132594], resolution=0.002
)

ds = dc.load(
    product="ga_ls8c_ard_3",
    measurements=["nbart_red", "nbart_green", "nbart_blue"],
    time=("2019-01-10", "2019-01-15"),
    like=gbox,
    resampling="nearest",
    group_by="solar_day",
)
```

### Actual behaviour

However, the code above fails with an `AttributeError`:
![image](https://github.com/opendatacube/datacube-core/assets/17680388/0c9e28c2-951b-4329-b2ce-2643684e5559)

To make it work correctly, the user must add `.compat` to the end of the `odc.geo` GeoBox to get out an older-style `datacube` GeoBox, e.g. `gbox.compat`:

```
from odc.geo.geobox import GeoBox

gbox = GeoBox.from_bbox(
    [149.036528545, -35.384897602, 149.233835134, -35.198132594], resolution=0.002
)

ds = dc.load(
    product="ga_ls8c_ard_3",
    measurements=["nbart_red", "nbart_green", "nbart_blue"],
    time=("2019-01-10", "2019-01-15"),
    like=gbox.compat,
    resampling="nearest",
    group_by="solar_day",
)
```

Even though our timeline for `odc-geo` integration isn't expected until version 1.9, it would be very helpful to support `odc-geo` GeoBoxes in `dc.load` even in version 1.8 to allow users to start writing code that integrates with `odc-geo`.

This could be as simple as detecting `odc-geo` GeoBoxes and converting them via the `.compat` method internally within `datacube-core`.

### Environment information

* Which ``datacube --version`` are you using? `1.8.17`
* What datacube deployment/enviornment are you running against? `DEA prod Sandbox


> **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.
> If you are a community member and not a maintainer please escalate this issue to maintainers via
> [GIS StackExchange](https://gis.stackexchange.com/questions/tagged/open-data-cube) or [Slack](http://slack.opendatacube.org).



Discussion:
Interested in your thoughts @SpacemanPaul @Ariana-B @omad. Would definitely make the product team's lives easier by letting us start using `odc-geo` functionality before it's fully/officially integrated in version 1.9.

Given there's an existing `.compat` method on the `odc-geo` GeoBoxes, I imagine this could be as simple as just calling that method under the hood in `dc.load` if a user passes in an `odc-geo` GeoBox instead of a `datacube-core` one.
Makes sense to me.

Resolving patch:
diff --git a/datacube/api/core.py b/datacube/api/core.py
--- a/datacube/api/core.py
+++ b/datacube/api/core.py
@@ -875,9 +875,16 @@ def output_geobox(like=None, output_crs=None, resolution=None, align=None,
         assert output_crs is None, "'like' and 'output_crs' are not supported together"
         assert resolution is None, "'like' and 'resolution' are not supported together"
         assert align is None, "'like' and 'align' are not supported together"
+
+        # If user passes a GeoBox, return as-is
         if isinstance(like, GeoBox):
             return like
 
+        # For `odc-geo` GeoBox compatibility: use "compat" attribute if
+        # it exists to convert to a Datacube-style GeoBox
+        if isinstance(compat := getattr(like, "compat", None), GeoBox):
+            return compat
+
         return like.geobox
 
     if load_hints:


Test patch:
diff --git a/integration_tests/test_end_to_end.py b/integration_tests/test_end_to_end.py
--- a/integration_tests/test_end_to_end.py
+++ b/integration_tests/test_end_to_end.py
@@ -7,9 +7,11 @@
 import numpy
 import pytest
 import rasterio
+from affine import Affine
 
 from datacube.api.query import query_group_by
 from datacube.api.core import Datacube
+from datacube.utils.geometry import GeoBox
 
 from integration_tests.utils import prepare_test_ingestion_configuration
 
@@ -116,6 +118,7 @@ def test_end_to_end(clirunner, index, testdata_dir, ingest_configs, datacube_env
     check_open_with_dc(index)
     check_open_with_grid_workflow(index)
     check_load_via_dss(index)
+    check_odcgeo_geobox_load(index)
 
 
 def check_open_with_dc(index):
@@ -317,3 +320,45 @@ def check_legacy_open(index):
         xx_lazy = dc.load_data(sources, gbox, mm, dask_chunks={'time': 1})
         assert xx_lazy['blue'].data.dask
         assert xx_lazy.blue[0, :, :].equals(xx.blue[0, :, :])
+
+
+def check_odcgeo_geobox_load(index):
+    """
+    Test that users can use `dc.load(like=...)` by passing an
+    `odc-geo`-style GeoBox.
+    """
+    dc = Datacube(index=index)
+
+    # Create mock odc-geo GeoBox
+    class ODC_geo_geobox:
+        compat = GeoBox(
+            500, 500, Affine(0.002, 0.0, 149.0, 0.0, -0.002, -35.0), "EPSG:4326"
+        )
+        coords = compat.coords
+
+    odc_geo_geobox = ODC_geo_geobox()
+
+    # Load data using .compat method
+    ds_compat = dc.load(
+        product="ls5_nbar_albers",
+        measurements=["blue"],
+        like=odc_geo_geobox.compat,
+    )
+    assert "blue" in ds_compat.data_vars
+
+    # Load data using odc-geo GeoBox directly
+    ds_odcgeo = dc.load(
+        product="ls5_nbar_albers",
+        measurements=["blue"],
+        like=odc_geo_geobox,
+    )
+    assert "blue" in ds_odcgeo.data_vars
+
+    # Like behaves differently when time is specified; make sure this works too
+    ds_odcgeo_time = dc.load(
+        product="ls5_nbar_albers",
+        measurements=["blue"],
+        like=odc_geo_geobox,
+        time="1992-03-23T23:14:25.500000",
+    )
+    assert "blue" in ds_odcgeo_time.data_vars