Repository: regionmask/regionmask

Issue:
lon/ lat offset too small for float32 coordinates?
We add a tiny lon and lat offset to get a consistent edge behaviour for all mask backends:

https://github.com/regionmask/regionmask/blob/ca12afe5924f563b1f3650377718406b97bd6485/regionmask/core/mask.py#L626-L627

However, the offset is _too small_ for `float32` coordinates, which will lead to wrong treatment of borderpoints :-1:

- We can fix this by (1) fiddling around with the offsets (e.g. using `np.spacing(lon).max()`) or (2) converting the coords to `float64`. I suggest to do (2).
- This is less bad if the mask is created with rasterio.


```python
import regionmask
import shapely
import numpy as np

r = regionmask.Regions([shapely.Polygon([[-180, -90], [-180, 90], [180, 90], [180, -90]])])

lon32 = np.arange(-180, 180, dtype=np.float32)
lat32 = np.arange(90, -91, -1, dtype=np.float32)

lon64 = np.arange(-180, 180, dtype=float)
lat64 = np.arange(90, -91, -1, dtype=float)

mask32 = r.mask(lon32, lat32, method="shapely")
mask64 = r.mask(lon64, lat64, method="shapely")

np.testing.assert_equal(mask64.values, mask32.values)

f, axs = plt.subplots(1, 2)
```

```python-traceback
AssertionError
```

```python
f, axs = plt.subplots(1, 2)

mask64.plot(ax=axs[0], add_colorbar=False)
mask32.plot(ax=axs[1], add_colorbar=False)

axs[0].set_title("float64 coords")
axs[1].set_title("float32 coords")

for ax in axs:
    ax.set_aspect("equal")
    ax.set_xlabel("")
    ax.set_ylabel("")
    ax.set_xticks([])
    ax.set_yticks([])
```



![offset_float32](https://github.com/regionmask/regionmask/assets/10194086/11cc77c6-4864-4ff5-ad0c-01892cad3db6)



Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/regionmask/regionmask/pull/493?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) Report
> Merging [#493](https://app.codecov.io/gh/regionmask/regionmask/pull/493?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) (49424ca) into [main](https://app.codecov.io/gh/regionmask/regionmask/commit/e9ddf7aad514c8563662c0323d8a80e8bb35e993?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) (e9ddf7a) will **decrease** coverage by `2.94%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 49424ca differs from pull request most recent head 354e152. Consider uploading reports for the commit 354e152 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main     #493      +/-   ##
==========================================
- Coverage   98.79%   95.86%   -2.94%     
==========================================
  Files          17       17              
  Lines        1330     1329       -1     
==========================================
- Hits         1314     1274      -40     
- Misses         16       55      +39     
```

| [Flag](https://app.codecov.io/gh/regionmask/regionmask/pull/493/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) | Coverage Δ | |
|---|---|---|
| [bare-minimum](https://app.codecov.io/gh/regionmask/regionmask/pull/493/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) | `?` | |
| [min-all-deps](https://app.codecov.io/gh/regionmask/regionmask/pull/493/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) | `?` | |
| [shapely_lt_2](https://app.codecov.io/gh/regionmask/regionmask/pull/493/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) | `?` | |
| [unittests](https://app.codecov.io/gh/regionmask/regionmask/pull/493/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) | `95.86% <100.00%> (-2.94%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files](https://app.codecov.io/gh/regionmask/regionmask/pull/493?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask) | Coverage Δ | |
|---|---|---|
| [regionmask/core/mask.py](https://app.codecov.io/gh/regionmask/regionmask/pull/493?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask#diff-cmVnaW9ubWFzay9jb3JlL21hc2sucHk=) | `90.23% <100.00%> (-7.70%)` | :arrow_down: |

... and [5 files with indirect coverage changes](https://app.codecov.io/gh/regionmask/regionmask/pull/493/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=regionmask)



Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5bcd1f9..71b07ba 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -32,6 +32,7 @@ New regions
 Bug Fixes
 ~~~~~~~~~
 
+- Ensure correct masks are created for `float32` coordinates (:issue:`489`, :pull:`493`).
 - Fixed the default value of ``overlap`` of :py:func:`from_geopandas` to ``None`` (:issue:`453`, :pull:`470`).
 
 Docs
diff --git a/regionmask/core/mask.py b/regionmask/core/mask.py
index cf1f85e..eba2571 100644
--- a/regionmask/core/mask.py
+++ b/regionmask/core/mask.py
@@ -204,8 +204,8 @@ def _mask(
                 "be converted to degree?"
             )
 
-    lon_arr = np.asarray(lon)
-    lat_arr = np.asarray(lat)
+    lon_arr = np.asarray(lon, dtype=float)
+    lat_arr = np.asarray(lat, dtype=float)
 
     # automatically detect whether wrapping is necessary
     if wrap_lon is None:


Test patch:
diff --git a/regionmask/tests/test_mask.py b/regionmask/tests/test_mask.py
index 8996f0e..b279a1c 100644
--- a/regionmask/tests/test_mask.py
+++ b/regionmask/tests/test_mask.py
@@ -964,12 +964,29 @@ def test_mask_whole_grid(method, regions, lon):
     mask = regions.mask(lon, lat, method=method)
 
     assert (mask == 0).all()
+    assert mask.lon.dtype == int
+    assert mask.lat.dtype == int
 
     # with wrap_lon=False the edges are not masked
     mask = regions.mask(lon, lat, method=method, wrap_lon=False)
     assert mask.sel(lat=-90).isnull().all()
 
 
+@pytest.mark.parametrize("method", MASK_METHODS)
+@pytest.mark.parametrize("regions", [r_GLOB_180, r_GLOB_360])
+@pytest.mark.parametrize("lon", [lon180, lon360])
+def test_mask_whole_grid_float32(method, regions, lon):
+
+    lat = np.arange(90, -91, -10, dtype=np.float32)
+    lon = lon.astype(np.float32)  # creates a copy
+    mask = regions.mask(lon, lat, method=method)
+
+    assert (mask == 0).all()
+
+    assert mask.lon.dtype == np.float32
+    assert mask.lat.dtype == np.float32
+
+
 @pytest.mark.parametrize("regions", [r_GLOB_180, r_GLOB_360])
 @pytest.mark.parametrize("lon", [lon180, lon360])
 def test_mask_whole_grid_nan_lon(regions, lon):