2424
2525import xarray_grass
2626from xarray_grass .grass_interface import GrassInterface
27- from xarray_grass .grass_backend_array import GrassBackendArray
27+ from xarray_grass .grass_backend_array import GrassSTDSBackendArray
2828
2929
3030class GrassBackendEntrypoint (BackendEntrypoint ):
@@ -265,6 +265,8 @@ def open_grass_maps(
265265 data_array_list .append (data_array )
266266 if raise_on_not_found and any (not_found .values ()):
267267 raise ValueError (f"Objects not found: { not_found } " )
268+
269+ crs_wkt = gi .get_crs_wkt_str ()
268270 finally :
269271 if session is not None :
270272 session .__exit__ (None , None , None )
@@ -277,7 +279,7 @@ def open_grass_maps(
277279 data_array_dict = {da .name : da for da in data_array_list }
278280
279281 attrs = {
280- "crs_wkt" : gi . get_crs_wkt_str () ,
282+ "crs_wkt" : crs_wkt ,
281283 "Conventions" : "CF-1.13-draft" ,
282284 # "title": "",
283285 "history" : f"{ datetime .now (timezone .utc )} : Created with xarray-grass version { xarray_grass .__version__ } " ,
@@ -347,9 +349,7 @@ def open_grass_raster_3d(raster_3d_name: str, grass_i: GrassInterface) -> xr.Dat
347349
348350
349351def open_grass_strds (strds_name : str , grass_i : GrassInterface ) -> xr .DataArray :
350- """must be called from within a grass session
351- TODO: lazy loading
352- """
352+ """Open a STRDS with lazy loading - data is only loaded when accessed"""
353353 strds_id = grass_i .get_id_from_name (strds_name )
354354 strds_name = grass_i .get_name_from_id (strds_id )
355355 x_coords , y_coords , _ = get_coordinates (grass_i , raster_3d = False ).values ()
@@ -360,45 +360,46 @@ def open_grass_strds(strds_name: str, grass_i: GrassInterface) -> xr.DataArray:
360360 time_unit = strds_infos .time_unit
361361 start_time_dim = f"start_time_{ strds_name } "
362362 end_time_dim = f"end_time_{ strds_name } "
363- dims = [start_time_dim , "y" , "x" ]
364- coordinates = dict .fromkeys (dims )
365- coordinates ["x" ] = x_coords
366- coordinates ["y" ] = y_coords
363+
367364 map_list = grass_i .list_maps_in_strds (strds_id )
368365 region = grass_i .get_region ()
369- array_list = []
370- for map_data in map_list :
371- # Lazy load the array
372- backend_array = GrassBackendArray (
373- shape = (region .rows , region .cols ),
374- dtype = map_data .dtype ,
375- map_id = map_data .id ,
376- map_type = "raster" ,
377- grass_interface = grass_i ,
378- )
379- lazy_array = xr .core .indexing .LazilyIndexedArray (backend_array )
380- # add time dimension at the beginning
381- lazy_array_with_time = np .expand_dims (lazy_array , axis = 0 )
382-
383- # ndarray = grass_i.read_raster_map(map_data.id)
384- # # add time dimension at the beginning
385- # ndarray = np.expand_dims(ndarray, axis=0)
386-
387- coordinates [start_time_dim ] = [map_data .start_time ]
388- coordinates [end_time_dim ] = (start_time_dim , [map_data .end_time ])
389-
390- data_array = xr .DataArray (
391- lazy_array_with_time ,
392- coords = coordinates ,
393- dims = dims ,
394- name = strds_name ,
395- )
396- array_list .append (data_array )
397- da_concat = xr .concat (array_list , dim = start_time_dim )
366+
367+ # Create a single backend array for the entire STRDS
368+ backend_array = GrassSTDSBackendArray (
369+ shape = (len (map_list ), region .rows , region .cols ),
370+ dtype = map_list [0 ].dtype ,
371+ map_list = map_list ,
372+ map_type = "raster" ,
373+ grass_interface = grass_i ,
374+ )
375+ lazy_array = xr .core .indexing .LazilyIndexedArray (backend_array )
376+
377+ # Create Variable with lazy array
378+ var = xr .Variable (dims = [start_time_dim , "y" , "x" ], data = lazy_array )
379+
380+ # Extract time coordinates
381+ start_times = [map_data .start_time for map_data in map_list ]
382+ end_times = [map_data .end_time for map_data in map_list ]
383+
384+ # Create coordinates
385+ coordinates = {
386+ "x" : x_coords ,
387+ "y" : y_coords ,
388+ start_time_dim : start_times ,
389+ end_time_dim : (start_time_dim , end_times ),
390+ }
391+
392+ # Convert to DataArray
393+ data_array = xr .DataArray (
394+ var ,
395+ coords = coordinates ,
396+ name = strds_name ,
397+ )
398+
398399 # Add CF attributes
399400 r_infos = grass_i .get_raster_info (map_list [0 ].id )
400401 da_with_attrs = set_cf_coordinates (
401- da_concat ,
402+ data_array ,
402403 gi = grass_i ,
403404 is_3d = False ,
404405 time_dims = [start_time_dim , end_time_dim ],
@@ -414,7 +415,7 @@ def open_grass_strds(strds_name: str, grass_i: GrassInterface) -> xr.DataArray:
414415
415416
416417def open_grass_str3ds (str3ds_name : str , grass_i : GrassInterface ) -> xr .DataArray :
417- """Open a series of 3D raster maps.
418+ """Open a STR3DS with lazy loading - data is only loaded when accessed
418419 TODO: Figure out what to do when the z value of the maps is time."""
419420 str3ds_id = grass_i .get_id_from_name (str3ds_name )
420421 str3ds_name = grass_i .get_name_from_id (str3ds_id )
@@ -426,43 +427,47 @@ def open_grass_str3ds(str3ds_name: str, grass_i: GrassInterface) -> xr.DataArray
426427 time_unit = strds_infos .time_unit
427428 start_time_dim = f"start_time_{ str3ds_name } "
428429 end_time_dim = f"end_time_{ str3ds_name } "
429- dims = [start_time_dim , "z" , "y_3d" , "x_3d" ]
430- coordinates = dict .fromkeys (dims )
431- coordinates ["x_3d" ] = x_coords
432- coordinates ["y_3d" ] = y_coords
433- coordinates ["z" ] = z_coords
430+
434431 map_list = grass_i .list_maps_in_str3ds (str3ds_id )
435432 region = grass_i .get_region ()
436- array_list = []
437- for map_data in map_list :
438- # Lazy load the map
439- backend_array = GrassBackendArray (
440- shape = (region .depths , region .rows3 , region .cols3 ),
441- dtype = map_data .dtype ,
442- map_id = map_data .id ,
443- map_type = "raster3d" ,
444- grass_interface = grass_i ,
445- )
446- lazy_array = xr .core .indexing .LazilyIndexedArray (backend_array )
447- # add time dimension at the beginning
448- lazy_array_with_time = np .expand_dims (lazy_array , axis = 0 )
449-
450- coordinates [start_time_dim ] = [map_data .start_time ]
451- coordinates [end_time_dim ] = (start_time_dim , [map_data .end_time ])
452-
453- data_array = xr .DataArray (
454- lazy_array_with_time ,
455- coords = coordinates ,
456- dims = dims ,
457- name = str3ds_name ,
458- )
459- array_list .append (data_array )
460433
461- da_concat = xr .concat (array_list , dim = start_time_dim )
434+ # Create a single backend array for the entire STR3DS
435+ backend_array = GrassSTDSBackendArray (
436+ shape = (len (map_list ), region .depths , region .rows3 , region .cols3 ),
437+ dtype = map_list [0 ].dtype ,
438+ map_list = map_list ,
439+ map_type = "raster3d" ,
440+ grass_interface = grass_i ,
441+ )
442+ lazy_array = xr .core .indexing .LazilyIndexedArray (backend_array )
443+
444+ # Create Variable with lazy array
445+ var = xr .Variable (dims = [start_time_dim , "z" , "y_3d" , "x_3d" ], data = lazy_array )
446+
447+ # Extract time coordinates
448+ start_times = [map_data .start_time for map_data in map_list ]
449+ end_times = [map_data .end_time for map_data in map_list ]
450+
451+ # Create coordinates
452+ coordinates = {
453+ "x_3d" : x_coords ,
454+ "y_3d" : y_coords ,
455+ "z" : z_coords ,
456+ start_time_dim : start_times ,
457+ end_time_dim : (start_time_dim , end_times ),
458+ }
459+
460+ # Convert to DataArray
461+ data_array = xr .DataArray (
462+ var ,
463+ coords = coordinates ,
464+ name = str3ds_name ,
465+ )
466+
462467 # Add CF attributes
463468 r3_infos = grass_i .get_raster3d_info (map_list [0 ].id )
464469 da_with_attrs = set_cf_coordinates (
465- da_concat ,
470+ data_array ,
466471 gi = grass_i ,
467472 is_3d = True ,
468473 z_unit = r3_infos ["vertical_units" ],
0 commit comments