Repository: scverse/spatialdata

Issue:
`rasterize` converts labels to images
**Describe the bug**

When applying `transform_to_data_extent` on a SpatialData with labels, they are merged into images.

**To Reproduce**
Steps to reproduce the behavior:

```python
from spatialdata.datasets import blobs
sdata = blobs()
sdata_rasterized = transform_to_data_extent(sdata, coordinate_system="global", target_unit_to_pixels=1)
assert len(sdata_rasterized.labels) == len(sdata.labels)
```
```pycon
In [61]: sdata_rasterized
Out[61]: 
SpatialData object
├── Images
│     ├── 'blobs_image': DataArray[cyx] (3, 512, 512)
│     ├── 'blobs_labels': DataArray[cyx] (1, 512, 512)
│     ├── 'blobs_multiscale_image': DataArray[cyx] (3, 512, 512)
│     └── 'blobs_multiscale_labels': DataArray[cyx] (1, 512, 512)
├── Points
│     └── 'blobs_points': DataFrame with shape: (<Delayed>, 4) (2D points)
├── Shapes
│     ├── 'blobs_circles': GeoDataFrame shape: (5, 2) (2D shapes)
│     ├── 'blobs_multipolygons': GeoDataFrame shape: (2, 1) (2D shapes)
│     └── 'blobs_polygons': GeoDataFrame shape: (5, 1) (2D shapes)
└── Tables
      └── 'table': AnnData (26, 3)
with coordinate systems:
    ▸ 'global', with elements:
        blobs_image (Images), blobs_labels (Images), blobs_multiscale_image (Images), blobs_multiscale_labels (Images), blobs_points (Points), blobs_circles (Shapes), blobs_multipolygons (Shapes), blobs_polygons (Shapes)

```

**Expected behavior**
All element types should be preserved. Labels should stay in labels.

**Additional context**
This is caused by the following line:
https://github.com/scverse/spatialdata/blob/6694c91d6b23cdcc8a6e57b21bcbe25d9423763f/src/spatialdata/_core/operations/rasterize.py#L330-L331

The behavior is undocumented and not intuitive. As it was intentionally introduced, there seems to exist a use case, but in other use cases it is a potential source of bugs. Some control over it is already provided through the flag `return_regions_as_labels=False`.
- We should **document** it.
- We should rethink whether it is safer to change the **default** of `return_regions_as_labels`, especially if it is for internal use.
- We must either make sure all functions calling `rasterize` **pass the correct value** for `return_regions_as_labels` that is appropriate in each case, or further expose the flag to their callers.
  A possible fix would be to make `transform_to_data_extent` call `rasterize(…, return_regions_as_labels=True)` https://github.com/scverse/spatialdata/blob/6694c91d6b23cdcc8a6e57b21bcbe25d9423763f/src/spatialdata/_core/operations/_utils.py#L118-L128


Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/scverse/spatialdata/pull/791?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scverse) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 91.84%. Comparing base [(`6694c91`)](https://app.codecov.io/gh/scverse/spatialdata/commit/6694c91d6b23cdcc8a6e57b21bcbe25d9423763f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scverse) to head [(`014eb37`)](https://app.codecov.io/gh/scverse/spatialdata/commit/014eb379e78592435c272a4f2f38c8cc34e72fb7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scverse).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #791   +/-   ##
=======================================
  Coverage   91.84%   91.84%           
=======================================
  Files          45       45           
  Lines        6893     6893           
=======================================
  Hits         6331     6331           
  Misses        562      562           
```

| [Files with missing lines](https://app.codecov.io/gh/scverse/spatialdata/pull/791?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scverse) | Coverage Δ | |
|---|---|---|
| [src/spatialdata/\_core/operations/\_utils.py](https://app.codecov.io/gh/scverse/spatialdata/pull/791?src=pr&el=tree&filepath=src%2Fspatialdata%2F_core%2Foperations%2F_utils.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scverse#diff-c3JjL3NwYXRpYWxkYXRhL19jb3JlL29wZXJhdGlvbnMvX3V0aWxzLnB5) | `92.15% <ø> (ø)` | |

</details>
----
🚨 Try these New Features:

- [Flaky Tests Detection](https://docs.codecov.com/docs/test-result-ingestion-beta) - Detect and resolve failed and flaky tests
aeisenbarth: CI is failing, probably due to a mismatch of Zarr and AnnData versions ("cannot import name 'Array' from 'zarr.core'").

Patch:
diff --git a/src/spatialdata/_core/operations/_utils.py b/src/spatialdata/_core/operations/_utils.py
index 00b6550..2372227 100644
--- a/src/spatialdata/_core/operations/_utils.py
+++ b/src/spatialdata/_core/operations/_utils.py
@@ -125,6 +125,7 @@ def transform_to_data_extent(
                 target_width=target_width,
                 target_height=None,
                 target_depth=None,
+                return_regions_as_labels=True,
             )
             sdata_to_return_elements[element_name] = rasterized
         else:


Test patch:
diff --git a/tests/core/operations/test_spatialdata_operations.py b/tests/core/operations/test_spatialdata_operations.py
index bf63fc4..95b567b 100644
--- a/tests/core/operations/test_spatialdata_operations.py
+++ b/tests/core/operations/test_spatialdata_operations.py
@@ -17,6 +17,7 @@ from spatialdata.models import (
     PointsModel,
     ShapesModel,
     TableModel,
+    get_model,
     get_table_keys,
 )
 from spatialdata.testing import assert_elements_dict_are_identical, assert_spatial_data_objects_are_identical
@@ -474,6 +475,7 @@ def test_transform_to_data_extent(full_sdata: SpatialData, maintain_positioning:
         for element in elements:
             before = full_sdata[element]
             after = sdata[element]
+            assert get_model(after) == get_model(before)
             data_extent_before = get_extent(before, coordinate_system="global")
             data_extent_after = get_extent(after, coordinate_system="global")
             # huge tolerance because of the bug with pixel perfectness