ocf-data-sampler 0.5.30__tar.gz → 0.5.32__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ocf-data-sampler might be problematic. Click here for more details.

Files changed (73) hide show
  1. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/PKG-INFO +1 -1
  2. ocf_data_sampler-0.5.32/ocf_data_sampler/select/select_spatial_slice.py +110 -0
  3. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/datasets/site.py +0 -23
  4. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler.egg-info/PKG-INFO +1 -1
  5. ocf_data_sampler-0.5.30/ocf_data_sampler/select/select_spatial_slice.py +0 -217
  6. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/LICENSE +0 -0
  7. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/README.md +0 -0
  8. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/__init__.py +0 -0
  9. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/config/__init__.py +0 -0
  10. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/config/load.py +0 -0
  11. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/config/model.py +0 -0
  12. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/config/save.py +0 -0
  13. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/data/uk_gsp_locations_20220314.csv +0 -0
  14. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/data/uk_gsp_locations_20250109.csv +0 -0
  15. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/__init__.py +0 -0
  16. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/gsp.py +0 -0
  17. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/load_dataset.py +0 -0
  18. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/__init__.py +0 -0
  19. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/nwp.py +0 -0
  20. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/providers/__init__.py +0 -0
  21. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/providers/cloudcasting.py +0 -0
  22. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/providers/ecmwf.py +0 -0
  23. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/providers/gfs.py +0 -0
  24. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/providers/icon.py +0 -0
  25. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/providers/ukv.py +0 -0
  26. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/nwp/providers/utils.py +0 -0
  27. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/open_xarray_tensorstore.py +0 -0
  28. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/satellite.py +0 -0
  29. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/site.py +0 -0
  30. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/load/utils.py +0 -0
  31. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/__init__.py +0 -0
  32. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/collate.py +0 -0
  33. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/common_types.py +0 -0
  34. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/datetime_features.py +0 -0
  35. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/gsp.py +0 -0
  36. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/nwp.py +0 -0
  37. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/satellite.py +0 -0
  38. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/site.py +0 -0
  39. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/numpy_sample/sun_position.py +0 -0
  40. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/__init__.py +0 -0
  41. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/diff_channels.py +0 -0
  42. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/dropout.py +0 -0
  43. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/fill_time_periods.py +0 -0
  44. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/find_contiguous_time_periods.py +0 -0
  45. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/geospatial.py +0 -0
  46. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/location.py +0 -0
  47. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/select/select_time_slice.py +0 -0
  48. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/datasets/__init__.py +0 -0
  49. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/datasets/picklecache.py +0 -0
  50. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/datasets/pvnet_uk.py +0 -0
  51. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/sample/__init__.py +0 -0
  52. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/sample/base.py +0 -0
  53. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/sample/site.py +0 -0
  54. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/sample/uk_regional.py +0 -0
  55. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/__init__.py +0 -0
  56. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/add_alterate_coordinate_projections.py +0 -0
  57. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/config_normalization_values_to_dicts.py +0 -0
  58. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/diff_nwp_data.py +0 -0
  59. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/merge_and_fill_utils.py +0 -0
  60. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/spatial_slice_for_dataset.py +0 -0
  61. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/time_slice_for_dataset.py +0 -0
  62. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/valid_time_periods.py +0 -0
  63. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/torch_datasets/utils/validation_utils.py +0 -0
  64. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler/utils.py +0 -0
  65. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler.egg-info/SOURCES.txt +0 -0
  66. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler.egg-info/dependency_links.txt +0 -0
  67. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler.egg-info/requires.txt +0 -0
  68. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/ocf_data_sampler.egg-info/top_level.txt +0 -0
  69. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/pyproject.toml +0 -0
  70. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/scripts/download_gsp_location_data.py +0 -0
  71. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/scripts/refactor_site.py +0 -0
  72. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/setup.cfg +0 -0
  73. {ocf_data_sampler-0.5.30 → ocf_data_sampler-0.5.32}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ocf-data-sampler
3
- Version: 0.5.30
3
+ Version: 0.5.32
4
4
  Author: James Fulton, Peter Dudfield
5
5
  Author-email: Open Climate Fix team <info@openclimatefix.org>
6
6
  License: MIT License
@@ -0,0 +1,110 @@
1
+ """Select spatial slices."""
2
+
3
+ import numpy as np
4
+ import xarray as xr
5
+
6
+ from ocf_data_sampler.select.geospatial import find_coord_system
7
+ from ocf_data_sampler.select.location import Location
8
+
9
+
10
+ def _get_pixel_index_location(da: xr.DataArray, location: Location) -> tuple[int, int]:
11
+ """Find pixel index location closest to given Location.
12
+
13
+ Args:
14
+ da: The xarray DataArray.
15
+ location: The Location object representing the point of interest.
16
+
17
+ Returns:
18
+ The pixel indices.
19
+
20
+ Raises:
21
+ ValueError: If the location is outside the bounds of the DataArray.
22
+ """
23
+ target_coords, x_dim, y_dim = find_coord_system(da)
24
+
25
+ x, y = location.in_coord_system(target_coords)
26
+
27
+ x_vals = da[x_dim].values
28
+ y_vals = da[y_dim].values
29
+
30
+ # Check that requested point lies within the data
31
+ if not (x_vals[0] < x < x_vals[-1]):
32
+ raise ValueError(
33
+ f"{x} is not in the interval {x_vals[0]}: {x_vals[-1]}",
34
+ )
35
+ if not (y_vals[0] < y < y_vals[-1]):
36
+ raise ValueError(
37
+ f"{y} is not in the interval {y_vals[0]}: {y_vals[-1]}",
38
+ )
39
+
40
+ closest_x = np.argmin(np.abs(x_vals - x))
41
+ closest_y = np.argmin(np.abs(y_vals - y))
42
+
43
+ return closest_x, closest_y
44
+
45
+
46
+ def select_spatial_slice_pixels(
47
+ da: xr.DataArray,
48
+ location: Location,
49
+ width_pixels: int,
50
+ height_pixels: int,
51
+ ) -> xr.DataArray:
52
+ """Select spatial slice based off pixels from location point of interest.
53
+
54
+ Args:
55
+ da: xarray DataArray to slice from
56
+ location: Location of interest that will be the center of the returned slice
57
+ height_pixels: Height of the slice in pixels
58
+ width_pixels: Width of the slice in pixels
59
+
60
+ Returns:
61
+ The selected DataArray slice.
62
+
63
+ Raises:
64
+ ValueError: If the dimensions are not even or the slice is not allowed
65
+ when padding is required.
66
+ """
67
+ if (width_pixels % 2) != 0:
68
+ raise ValueError("Width must be an even number")
69
+ if (height_pixels % 2) != 0:
70
+ raise ValueError("Height must be an even number")
71
+
72
+ _, x_dim, y_dim = find_coord_system(da)
73
+ center_idx_x, center_idx_y = _get_pixel_index_location(da, location)
74
+
75
+ half_width = width_pixels // 2
76
+ half_height = height_pixels // 2
77
+
78
+ left_idx = int(center_idx_x - half_width)
79
+ right_idx = int(center_idx_x + half_width)
80
+ bottom_idx = int(center_idx_y - half_height)
81
+ top_idx = int(center_idx_y + half_height)
82
+
83
+ data_width_pixels = len(da[x_dim])
84
+ data_height_pixels = len(da[y_dim])
85
+
86
+ # Padding checks
87
+ slice_unavailable = (
88
+ left_idx < 0
89
+ or right_idx > data_width_pixels
90
+ or bottom_idx < 0
91
+ or top_idx > data_height_pixels
92
+ )
93
+
94
+ if slice_unavailable:
95
+ issues = []
96
+ if left_idx < 0:
97
+ issues.append(f"left_idx ({left_idx}) < 0")
98
+ if right_idx > data_width_pixels:
99
+ issues.append(f"right_idx ({right_idx}) > data_width_pixels ({data_width_pixels})")
100
+ if bottom_idx < 0:
101
+ issues.append(f"bottom_idx ({bottom_idx}) < 0")
102
+ if top_idx > data_height_pixels:
103
+ issues.append(f"top_idx ({top_idx}) > data_height_pixels ({data_height_pixels})")
104
+ issue_details = "\n - ".join(issues)
105
+ raise ValueError(f"Window for location {location} not available: \n - {issue_details}")
106
+
107
+ # Standard selection - without padding
108
+ da = da.isel({x_dim: slice(left_idx, right_idx), y_dim: slice(bottom_idx, top_idx)})
109
+
110
+ return da
@@ -1,6 +1,5 @@
1
1
  """Torch dataset for sites."""
2
2
 
3
- import numpy as np
4
3
  import pandas as pd
5
4
  import xarray as xr
6
5
  from torch.utils.data import Dataset
@@ -436,25 +435,3 @@ class SitesDatasetConcurrent(PickleCacheMixin, Dataset):
436
435
  site_samples.append(site_numpy_sample)
437
436
 
438
437
  return stack_np_samples_into_batch(site_samples)
439
-
440
-
441
- def coarsen_data(xr_data: xr.Dataset, coarsen_to_deg: float = 0.1) -> xr.Dataset:
442
- """Coarsen the data to a specified resolution in degrees.
443
-
444
- Args:
445
- xr_data: xarray dataset to coarsen
446
- coarsen_to_deg: resolution to coarsen to in degrees
447
- """
448
- if "latitude" in xr_data.coords and "longitude" in xr_data.coords:
449
- step = np.abs(xr_data.latitude.values[1] - xr_data.latitude.values[0])
450
- step = np.round(step, 4)
451
- coarsen_factor = int(coarsen_to_deg / step)
452
- if coarsen_factor > 1:
453
- xr_data = xr_data.coarsen(
454
- latitude=coarsen_factor,
455
- longitude=coarsen_factor,
456
- boundary="pad",
457
- coord_func="min",
458
- ).mean()
459
-
460
- return xr_data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ocf-data-sampler
3
- Version: 0.5.30
3
+ Version: 0.5.32
4
4
  Author: James Fulton, Peter Dudfield
5
5
  Author-email: Open Climate Fix team <info@openclimatefix.org>
6
6
  License: MIT License
@@ -1,217 +0,0 @@
1
- """Select spatial slices."""
2
-
3
- import numpy as np
4
- import xarray as xr
5
-
6
- from ocf_data_sampler.select.geospatial import find_coord_system
7
- from ocf_data_sampler.select.location import Location
8
-
9
-
10
- def _get_pixel_index_location(da: xr.DataArray, location: Location) -> tuple[int, int]:
11
- """Find pixel index location closest to given Location.
12
-
13
- Args:
14
- da: The xarray DataArray.
15
- location: The Location object representing the point of interest.
16
-
17
- Returns:
18
- The pixel indices.
19
-
20
- Raises:
21
- ValueError: If the location is outside the bounds of the DataArray.
22
- """
23
- target_coords, x_dim, y_dim = find_coord_system(da)
24
-
25
- x, y = location.in_coord_system(target_coords)
26
-
27
- x_vals = da[x_dim].values
28
- y_vals = da[y_dim].values
29
-
30
- # Check that requested point lies within the data
31
- if not (x_vals[0] < x < x_vals[-1]):
32
- raise ValueError(
33
- f"{x} is not in the interval {x_vals[0]}: {x_vals[-1]}",
34
- )
35
- if not (y_vals[0] < y < y_vals[-1]):
36
- raise ValueError(
37
- f"{y} is not in the interval {y_vals[0]}: {y_vals[-1]}",
38
- )
39
-
40
- closest_x = np.argmin(np.abs(x_vals - x))
41
- closest_y = np.argmin(np.abs(y_vals - y))
42
-
43
- return closest_x, closest_y
44
-
45
-
46
- def _select_padded_slice(
47
- da: xr.DataArray,
48
- left_idx: int,
49
- right_idx: int,
50
- bottom_idx: int,
51
- top_idx: int,
52
- x_dim: str,
53
- y_dim: str,
54
- ) -> xr.DataArray:
55
- """Selects spatial slice - padding where necessary if indices are out of bounds.
56
-
57
- Args:
58
- da: xarray DataArray.
59
- left_idx: The leftmost index of the slice.
60
- right_idx: The rightmost index of the slice.
61
- bottom_idx: The bottommost index of the slice.
62
- top_idx: The topmost index of the slice.
63
- x_dim: Name of the x dimension.
64
- y_dim: Name of the y dimension.
65
-
66
- Returns:
67
- An xarray DataArray with padding, if necessary.
68
- """
69
- data_width_pixels = len(da[x_dim])
70
- data_height_pixels = len(da[y_dim])
71
-
72
- left_pad_pixels = max(0, -left_idx)
73
- right_pad_pixels = max(0, right_idx - data_width_pixels)
74
- bottom_pad_pixels = max(0, -bottom_idx)
75
- top_pad_pixels = max(0, top_idx - data_height_pixels)
76
-
77
- if (left_pad_pixels > 0 and right_pad_pixels > 0) or (
78
- bottom_pad_pixels > 0 and top_pad_pixels > 0
79
- ):
80
- raise ValueError("Cannot pad both sides of the window")
81
-
82
- dx = np.median(np.diff(da[x_dim].values))
83
- dy = np.median(np.diff(da[y_dim].values))
84
-
85
- # Create a new DataArray which has indices which go outside
86
- # the original DataArray
87
- # Pad the left of the window
88
- if left_pad_pixels > 0:
89
- x_sel = np.concatenate(
90
- [
91
- da[x_dim].values[0] + np.arange(-left_pad_pixels, 0) * dx,
92
- da[x_dim].values[0:right_idx],
93
- ],
94
- )
95
- da = da.isel({x_dim: slice(0, right_idx)}).reindex({x_dim: x_sel})
96
-
97
- # Pad the right of the window
98
- elif right_pad_pixels > 0:
99
- x_sel = np.concatenate(
100
- [
101
- da[x_dim].values[left_idx:],
102
- da[x_dim].values[-1] + np.arange(1, right_pad_pixels + 1) * dx,
103
- ],
104
- )
105
- da = da.isel({x_dim: slice(left_idx, None)}).reindex({x_dim: x_sel})
106
-
107
- # No left-right padding required
108
- else:
109
- da = da.isel({x_dim: slice(left_idx, right_idx)})
110
-
111
- # Pad the bottom of the window
112
- if bottom_pad_pixels > 0:
113
- y_sel = np.concatenate(
114
- [
115
- da[y_dim].values[0] + np.arange(-bottom_pad_pixels, 0) * dy,
116
- da[y_dim].values[0:top_idx],
117
- ],
118
- )
119
- da = da.isel({y_dim: slice(0, top_idx)}).reindex({y_dim: y_sel})
120
-
121
- # Pad the top of the window
122
- elif top_pad_pixels > 0:
123
- y_sel = np.concatenate(
124
- [
125
- da[y_dim].values[bottom_idx:],
126
- da[y_dim].values[-1] + np.arange(1, top_pad_pixels + 1) * dy,
127
- ],
128
- )
129
- da = da.isel({y_dim: slice(bottom_idx, None)}).reindex({y_dim: y_sel})
130
-
131
- # No bottom-top padding required
132
- else:
133
- da = da.isel({y_dim: slice(bottom_idx, top_idx)})
134
-
135
- return da
136
-
137
-
138
- def select_spatial_slice_pixels(
139
- da: xr.DataArray,
140
- location: Location,
141
- width_pixels: int,
142
- height_pixels: int,
143
- allow_partial_slice: bool = False,
144
- ) -> xr.DataArray:
145
- """Select spatial slice based off pixels from location point of interest.
146
-
147
- Args:
148
- da: xarray DataArray to slice from
149
- location: Location of interest that will be the center of the returned slice
150
- height_pixels: Height of the slice in pixels
151
- width_pixels: Width of the slice in pixels
152
- allow_partial_slice: Whether to allow a partial slice.
153
-
154
- Returns:
155
- The selected DataArray slice.
156
-
157
- Raises:
158
- ValueError: If the dimensions are not even or the slice is not allowed
159
- when padding is required.
160
-
161
- """
162
- if (width_pixels % 2) != 0:
163
- raise ValueError("Width must be an even number")
164
- if (height_pixels % 2) != 0:
165
- raise ValueError("Height must be an even number")
166
-
167
- _, x_dim, y_dim = find_coord_system(da)
168
- center_idx_x, center_idx_y = _get_pixel_index_location(da, location)
169
-
170
- half_width = width_pixels // 2
171
- half_height = height_pixels // 2
172
-
173
- left_idx = int(center_idx_x - half_width)
174
- right_idx = int(center_idx_x + half_width)
175
- bottom_idx = int(center_idx_y - half_height)
176
- top_idx = int(center_idx_y + half_height)
177
-
178
- data_width_pixels = len(da[x_dim])
179
- data_height_pixels = len(da[y_dim])
180
-
181
- # Padding checks
182
- pad_required = (
183
- left_idx < 0
184
- or right_idx > data_width_pixels
185
- or bottom_idx < 0
186
- or top_idx > data_height_pixels
187
- )
188
-
189
- if pad_required:
190
- if allow_partial_slice:
191
- da = _select_padded_slice(da, left_idx, right_idx, bottom_idx, top_idx, x_dim, y_dim)
192
- else:
193
- issues = []
194
- if left_idx < 0:
195
- issues.append(f"left_idx ({left_idx}) < 0")
196
- if right_idx > data_width_pixels:
197
- issues.append(f"right_idx ({right_idx}) > data_width_pixels ({data_width_pixels})")
198
- if bottom_idx < 0:
199
- issues.append(f"bottom_idx ({bottom_idx}) < 0")
200
- if top_idx > data_height_pixels:
201
- issues.append(f"top_idx ({top_idx}) > data_height_pixels ({data_height_pixels})")
202
- issue_details = "\n".join(issues)
203
- raise ValueError(
204
- f"Window for location {location} not available. Padding required due to: \n"
205
- f"{issue_details}\n"
206
- "You may wish to set `allow_partial_slice=True`",
207
- )
208
- else:
209
- # Standard selection - without padding
210
- da = da.isel({x_dim: slice(left_idx, right_idx), y_dim: slice(bottom_idx, top_idx)})
211
-
212
- if len(da[x_dim]) != width_pixels:
213
- raise ValueError(f"x-dim has size {len(da[x_dim])}, expected {width_pixels}")
214
- if len(da[y_dim]) != height_pixels:
215
- raise ValueError(f"y-dim has size {len(da[y_dim])}, expected {height_pixels}")
216
-
217
- return da