Skip to content

Commit 1d37c68

Browse files
authored
Remove the grass session management from the package (#8)
* remove dependency on grass_session. GRASS session should be set by the user * More robust support for ovewrite flag * update readme
1 parent f19b8b2 commit 1d37c68

File tree

8 files changed

+530
-752
lines changed

8 files changed

+530
-752
lines changed

README.md

Lines changed: 61 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
A [GRASS](https://grass.osgeo.org/) backend for [Xarray](https://xarray.dev/).
88
Explore all your GRASS rasters with Xarray.
9+
Import zarr or NetCDF into your GRASS database.
910

1011
## Installation
1112

@@ -18,44 +19,65 @@ You need to install GRASS independently.
1819
## Loading GRASS data as an Xarray Dataset
1920

2021
```python
21-
>>> import xarray as xr
22-
>>> test_ds = xr.open_dataset("/home/lc/grassdata/nc_spm_08_grass7/PERMANENT/", raster=["boundary_county_500m", "elevation"])
23-
>>> test_ds
24-
<xarray.Dataset> Size: 253kB
25-
Dimensions: (y: 150, x: 140)
22+
import xarray as xr
23+
import grass_session
24+
25+
import grass.script as gscript
26+
27+
28+
with grass_session.Session(
29+
gisdb="/home/laurent/data/grassdata/",
30+
location="nc_spm_08_grass7_test",
31+
mapset="PERMANENT",
32+
):
33+
# Make modis_lst mapset accessible
34+
gscript.run_command("g.mapsets", mapset="modis_lst", operation="add")
35+
test_ds = xr.open_dataset(
36+
"", # No need to pass a path, the information is taken from the active grass session
37+
engine="xarray_grass", # If no path is given, then the engine must be specified
38+
raster=["boundary_county_500m", "elevation"],
39+
strds="LST_Day_monthly@modis_lst",
40+
)
41+
42+
print(test_ds)
43+
```
44+
45+
```
46+
Search path not modified
47+
<xarray.Dataset> Size: 6MB
48+
Dimensions: (y: 165, x: 179, start_time_LST_Day_monthly: 24)
2649
Coordinates:
27-
* y (y) float32 600B 2.2e+05 2.2e+05 ... 2.207e+05
28-
* x (x) float32 560B 6.383e+05 6.383e+05 ... 6.39e+05
50+
* y (y) float32 660B 2.196e+05 ... 2.212e+05
51+
* x (x) float32 716B 6.377e+05 ... 6.395e+05
52+
* start_time_LST_Day_monthly (start_time_LST_Day_monthly) datetime64[ns] 192B ...
53+
end_time_LST_Day_monthly (start_time_LST_Day_monthly) datetime64[ns] 192B ...
2954
Data variables:
30-
boundary_county_500m (y, x) float64 168kB ...
31-
elevation (y, x) float32 84kB ...
55+
boundary_county_500m (y, x) float64 236kB ...
56+
elevation (y, x) float32 118kB ...
57+
LST_Day_monthly (start_time_LST_Day_monthly, y, x) int64 6MB ...
3258
Attributes:
3359
crs_wkt: PROJCRS["NAD83(HARN) / North Carolina",BASEGEOGCRS["NAD83(H...
3460
Conventions: CF-1.13-draft
35-
history: 2025-10-31 18:22:16.644873+00:00: Created with xarray-grass...
36-
source: GRASS database. project: <nc_spm_08_grass7>, mapset:<PERMAN...
37-
61+
history: 2025-11-02 23:51:57.141257+00:00: Created with xarray-grass...
62+
source: GRASS database. project: , mapset:
3863
```
3964

65+
xarray-grass requires an active GRASS session to work.
66+
Here we're using the [grass-session](https://github.com/zarch/grass-session) package to set it.
67+
4068
You can choose which maps you want to load with the `raster`, `raster_3d`, `strds` and `str3ds` parameters to `open_dataset`.
4169
Those accept either a single string or an iterable.
4270
If none of those are specified, the whole mapset will be loaded, ignoring single maps that are already registered in either a `strds` or `str3ds`;
43-
those maps will be loaded into the Xarray Dataset for being part of the GRASS Space Time Dataset.
71+
those maps will be loaded into the Xarray Dataset as part of the GRASS Space Time Dataset.
4472
Any time-stamp associated to a single map not registered in a stds is ignored.
4573

4674
The extent and resolution of the resulting `Dataset` is defined by the region setting of GRASS, set with the `g.region` GRASS tool.
4775
Note that in GRASS the 3D resolution is independent from the 2D resolution.
4876
Therefore, 2D and 3D maps loaded in Xarray will not share the same dimensions and coordinates.
4977
The coordinates in the Xarray `Dataset` correspond to the center of the GRASS cell.
5078

51-
If run from outside a GRASS session, `xarray-grass` will automatically create a session in the requested project and mapset.
52-
If run from within GRASS, only maps from accessible mapsets could be loaded.
53-
You can list the accessible mapsets with `g.mapsets` from GRASS.
54-
5579
In GRASS, the time dimension of various STDSs is not homogeneous, as it is for the spatial coordinates.
56-
To reflect this, xarray-grass will create one time dimension for each STDS loaded.
57-
58-
From within a grass session, it is possible to access various mapsets.
80+
To account for this, xarray-grass will create one time dimension for each STDS loaded.
5981

6082
## CF conventions attributes mapping
6183

@@ -80,35 +102,29 @@ The attributes set at the dataset level are:
80102

81103
## Writing an Xarray Dataset or DataArray to GRASS
82104

105+
Continuing the script fro above, we can now write back the STRDS to GRASS.
83106

84-
```python
85-
import xarray as xr
86-
from xarray_grass import to_grass
87-
88-
mapset = "/home/lc/grassdata/nc_spm_08_grass7/PERMANENT/" # Or pathlib.Path
107+
There are two requirements of note to write to GRASS using xarray-grass:
108+
- The Dataset or SataArray needs a `crs_wkt` attribute with the CRS information in the WKT format.
109+
- The `dims` parameter needs to map every dimensions which is non standard to its standard name.
110+
The standard names are [x, y, z and start_time]
89111

90-
test_ds = xr.open_dataset(
91-
mapset,
92-
raster=["boundary_county_500m", "elevation"],
93-
strds="LST_Day_monthly@modis_lst",
94-
)
95-
96-
print(test_ds)
112+
```python
113+
from xarray_grass import to_grass
97114

98-
# Let's write the modis time series back into the PERMANENT mapset
115+
# Let's write the modis time series back into the current (PERMANENT) mapset
99116

100-
da_modis = test_ds["LST_Day_monthly"]
101-
# xarray-grass needs the CRS information to write to GRASS
102-
da_modis.attrs["crs_wkt"] = test_ds.attrs["crs_wkt"]
117+
da_modis = test_ds["LST_Day_monthly"]
118+
# xarray-grass needs the CRS information to write to GRASS
119+
da_modis.attrs["crs_wkt"] = test_ds.attrs["crs_wkt"]
103120

104-
to_grass(
105-
dataset=da_modis,
106-
mapset=mapset,
107-
dims={
108-
"LST_Day_monthly": {"start_time": "start_time_LST_Day_monthly"},
109-
},
110-
overwrite=False
111-
)
121+
to_grass(
122+
dataset=da_modis,
123+
dims={
124+
"LST_Day_monthly": {"start_time": "start_time_LST_Day_monthly"},
125+
},
126+
overwrite=False,
127+
)
112128
```
113129

114130
The above `print` statement should return this:
@@ -151,12 +167,11 @@ Attributes:
151167
- [x] Transpose if dimensions are not in the expected order
152168
- [x] Support time units for relative time
153169
- [ ] Support `end_time`
154-
- [ ] Accept writing into a specific mapset (GRASS 8.5)
155170
- [ ] Accept non homogeneous 3D resolution in NS and EW dimensions (GRASS 8.5)
156171
- [x] Lazy loading of STDS on the time dimension
157172
- [ ] Properly test with lat-lon location
158173

159174
### Stretch goals
160175

161-
- [ ] Load all mapsets from a GRASS project (ex location)
162176
- [ ] Read CRS definitions from CF compatible fields
177+
- [ ] Lazy load on the spatial dimension

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ readme = "README.md"
1313
license ="GPL-2.0-or-later"
1414
requires-python = ">=3.11"
1515
dependencies = [
16-
"grass-session>=0.5",
1716
"numpy>=2.2.5",
1817
"pyproj>=3.7.1",
1918
"pandas>=2.2.3",
@@ -28,6 +27,7 @@ classifiers = [
2827
"Programming Language :: Python :: 3.11",
2928
"Programming Language :: Python :: 3.12",
3029
"Programming Language :: Python :: 3.13",
30+
"Programming Language :: Python :: 3.14",
3131
"Topic :: Scientific/Engineering",
3232
]
3333

@@ -39,6 +39,7 @@ Issues = "https://github.com/lrntct/xarray-grass/issues"
3939

4040
[dependency-groups]
4141
dev = [
42+
"grass-session>=0.5",
4243
"pre-commit>=4.2.0",
4344
"pytest>=8.3.5",
4445
"pytest-random-order>=1.1.1",

src/xarray_grass/grass_interface.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626

2727
# Needed to import grass modules
28-
import grass_session # noqa: F401
2928
import grass.script as gs
3029
from grass.script import array as garray
3130
import grass.pygrass.utils as gutils
@@ -98,6 +97,10 @@ def __init__(self, overwrite: bool = False):
9897
if "GISRC" not in os.environ:
9998
raise RuntimeError("GRASS session not set.")
10099
self.overwrite = overwrite
100+
if self.overwrite:
101+
os.environ["GRASS_OVERWRITE"] = "1"
102+
else:
103+
os.environ["GRASS_OVERWRITE"] = "0"
101104
tgis.init()
102105

103106
@staticmethod
@@ -505,3 +508,33 @@ def register_maps_in_stds(
505508
unit=t_unit,
506509
)
507510
return self
511+
512+
def get_coordinates(self, raster_3d: bool) -> dict[str : np.ndarray]:
513+
"""return np.ndarray of coordinates from the GRASS region."""
514+
current_region = self.get_region()
515+
lim_e = current_region.e
516+
lim_w = current_region.w
517+
lim_n = current_region.n
518+
lim_s = current_region.s
519+
lim_t = current_region.t
520+
lim_b = current_region.b
521+
dz = current_region.tbres
522+
if raster_3d:
523+
dx = current_region.ewres3
524+
dy = current_region.nsres3
525+
else:
526+
dx = current_region.ewres
527+
dy = current_region.nsres
528+
# GRASS limits are at the edge of the region.
529+
# In the exported arrays, coordinates are at the center of the cell
530+
# Stop not changed to include it in the range
531+
start_w = lim_w + dx / 2
532+
stop_e = lim_e
533+
start_s = lim_s + dy / 2
534+
stop_n = lim_n
535+
start_b = lim_b + dz / 2
536+
stop_t = lim_t
537+
x_coords = np.arange(start=start_w, stop=stop_e, step=dx, dtype=np.float32)
538+
y_coords = np.arange(start=start_s, stop=stop_n, step=dy, dtype=np.float32)
539+
z_coords = np.arange(start=start_b, stop=stop_t, step=dz, dtype=np.float32)
540+
return {"x": x_coords, "y": y_coords, "z": z_coords}

0 commit comments

Comments
 (0)