yirgacheffe 1.6.1__py3-none-any.whl → 1.7.1__py3-none-any.whl

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.
yirgacheffe/__init__.py CHANGED
@@ -1,9 +1,16 @@
1
+ from pathlib import Path
2
+
1
3
  from osgeo import gdal
4
+ import tomli as tomllib
5
+
2
6
  try:
3
7
  from importlib import metadata
4
- __version__ = metadata.version(__name__)
8
+ __version__: str = metadata.version(__name__)
5
9
  except ModuleNotFoundError:
6
- __version__ = "unknown"
10
+ pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
11
+ with open(pyproject_path, "rb") as f:
12
+ pyproject_data = tomllib.load(f)
13
+ __version__ = pyproject_data["project"]["version"]
7
14
 
8
15
  from ._core import read_raster, read_rasters, read_shape, read_shape_like
9
16
  from .constants import WGS_84_PROJECTION
yirgacheffe/_core.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from pathlib import Path
2
- from typing import List, Optional, Tuple, Union
2
+ from typing import Optional, Sequence, Tuple, Union
3
3
 
4
4
  from .layers.base import YirgacheffeLayer
5
5
  from .layers.group import GroupLayer, TiledGroupLayer
6
6
  from .layers.rasters import RasterLayer
7
7
  from .layers.vectors import VectorLayer
8
- from .window import PixelScale
8
+ from .window import MapProjection
9
9
  from .operators import DataType
10
10
 
11
11
  def read_raster(
@@ -32,7 +32,7 @@ def read_raster(
32
32
  return RasterLayer.layer_from_file(filename, band, ignore_nodata)
33
33
 
34
34
  def read_rasters(
35
- filenames : Union[List[Path],List[str]],
35
+ filenames : Sequence[Union[Path,str]],
36
36
  tiled: bool=False
37
37
  ) -> GroupLayer:
38
38
  """Open a set of raster files (e.g., GeoTIFFs) as a single layer.
@@ -58,8 +58,7 @@ def read_rasters(
58
58
 
59
59
  def read_shape(
60
60
  filename: Union[Path,str],
61
- scale: Union[PixelScale, Tuple[float,float]],
62
- projection: str,
61
+ projection: Union[Optional[MapProjection],Optional[Tuple[str,Tuple[float,float]]]]=None,
63
62
  where_filter: Optional[str] = None,
64
63
  datatype: Optional[DataType] = None,
65
64
  burn_value: Union[int,float,str] = 1,
@@ -70,10 +69,8 @@ def read_shape(
70
69
  ----------
71
70
  filename : Path
72
71
  Path of raster file to open.
73
- scale: PixelScale or tuple of float
74
- The dimensions of each pixel.
75
- projection: str
76
- The map projection to use
72
+ projection: MapProjection or tuple, optional
73
+ The map projection to use,
77
74
  where_filter : str, optional
78
75
  For use with files with many entries (e.g., GPKG), applies this filter to the data.
79
76
  datatype: DataType, default=DataType.Byte
@@ -87,16 +84,17 @@ def read_shape(
87
84
  Returns an layer representing the vector data.
88
85
  """
89
86
 
90
- if not isinstance(scale, PixelScale):
91
- scale = PixelScale(scale[0], scale[1])
87
+ if projection is not None:
88
+ if not isinstance(projection, MapProjection):
89
+ projection_name, scale_tuple = projection
90
+ projection = MapProjection(projection_name, scale_tuple[0], scale_tuple[1])
92
91
 
93
- return VectorLayer.layer_from_file(
92
+ return VectorLayer._future_layer_from_file(
94
93
  filename,
95
94
  where_filter,
96
- scale,
97
95
  projection,
98
96
  datatype,
99
- burn_value
97
+ burn_value,
100
98
  )
101
99
 
102
100
  def read_shape_like(
@@ -65,13 +65,13 @@ class UniformAreaLayer(RasterLayer):
65
65
 
66
66
  transform = dataset.GetGeoTransform()
67
67
 
68
- pixel_scale = self.pixel_scale
69
- assert pixel_scale # from raster we should always have one
68
+ projection = self.map_projection
69
+ assert projection is not None # from raster we should always have one
70
70
 
71
71
  self._underlying_area = Area(
72
- floor(-180 / pixel_scale.xstep) * pixel_scale.xstep,
72
+ floor(-180 / projection.xstep) * projection.xstep,
73
73
  self.area.top,
74
- ceil(180 / pixel_scale.xstep) * pixel_scale.xstep,
74
+ ceil(180 / projection.xstep) * projection.xstep,
75
75
  self.area.bottom
76
76
  )
77
77
  self._active_area = self._underlying_area
@@ -1,9 +1,13 @@
1
1
  from __future__ import annotations
2
2
  from typing import Any, Optional, Sequence, Tuple
3
3
 
4
+ import deprecation
5
+
6
+ from .. import __version__
4
7
  from ..operators import DataType, LayerMathMixin
5
- from ..rounding import almost_equal, are_pixel_scales_equal_enough, round_up_pixels, round_down_pixels
6
- from ..window import Area, PixelScale, Window
8
+ from ..rounding import almost_equal, round_up_pixels, round_down_pixels
9
+ from ..window import Area, MapProjection, PixelScale, Window
10
+ from .._backends import backend
7
11
 
8
12
  class YirgacheffeLayer(LayerMathMixin):
9
13
  """The common base class for the different layer types. Most still inherit from RasterLayer as deep down
@@ -12,13 +16,15 @@ class YirgacheffeLayer(LayerMathMixin):
12
16
 
13
17
  def __init__(self,
14
18
  area: Area,
15
- pixel_scale: Optional[PixelScale],
16
- projection: str,
19
+ projection: Optional[MapProjection],
17
20
  name: Optional[str] = None
18
21
  ):
19
- self._pixel_scale = pixel_scale
22
+ # This is just to catch code that uses the old private API
23
+ if projection is not None and not isinstance(projection, MapProjection):
24
+ raise TypeError("projection value of wrong type")
25
+
20
26
  self._underlying_area = area
21
- self._active_area = area
27
+ self._active_area: Optional[Area] = None
22
28
  self._projection = projection
23
29
  self._window: Optional[Window] = None
24
30
  self.name = name
@@ -49,16 +55,46 @@ class YirgacheffeLayer(LayerMathMixin):
49
55
  raise NotImplementedError("Must be overridden by subclass")
50
56
 
51
57
  @property
52
- def projection(self) -> str:
53
- return self._projection
58
+ @deprecation.deprecated(
59
+ deprecated_in="1.7",
60
+ removed_in="2.0",
61
+ current_version=__version__,
62
+ details="Use `map_projection` instead."
63
+ )
64
+ def projection(self) -> Optional[str]:
65
+ if self._projection:
66
+ return self._projection.name
67
+ else:
68
+ return None
54
69
 
55
70
  @property
71
+ @deprecation.deprecated(
72
+ deprecated_in="1.7",
73
+ removed_in="2.0",
74
+ current_version=__version__,
75
+ details="Use `map_projection` instead."
76
+ )
56
77
  def pixel_scale(self) -> Optional[PixelScale]:
57
- return self._pixel_scale
78
+ if self._projection:
79
+ return PixelScale(self._projection.xstep, self._projection.ystep)
80
+ else:
81
+ return None
82
+
83
+ @property
84
+ def map_projection(self) -> Optional[MapProjection]:
85
+ return self._projection
58
86
 
59
87
  @property
60
88
  def area(self) -> Area:
61
- return self._active_area
89
+ if self._active_area is not None:
90
+ return self._active_area
91
+ else:
92
+ return self._underlying_area
93
+
94
+ def _get_operation_area(self, projection: Optional[MapProjection]) -> Area:
95
+ if self._projection is not None and projection is not None and self._projection != projection:
96
+ raise ValueError("Calculation projection does not match layer projection")
97
+ return self.area
62
98
 
63
99
  @property
64
100
  def window(self) -> Window:
@@ -77,8 +113,11 @@ class YirgacheffeLayer(LayerMathMixin):
77
113
 
78
114
  # This only makes sense (currently) if all layers
79
115
  # have the same pixel pitch (modulo desired accuracy)
80
- if not are_pixel_scales_equal_enough([x.pixel_scale for x in layers]):
81
- raise ValueError("Not all layers are at the same pixel scale")
116
+ projections = [x.map_projection for x in layers if x.map_projection is not None]
117
+ if not projections:
118
+ raise ValueError("No layers have a projection")
119
+ if not all(projections[0] == x for x in projections[1:]):
120
+ raise ValueError("Not all layers are at the same projectin or pixel scale")
82
121
 
83
122
  intersection = Area(
84
123
  left=max(x._underlying_area.left for x in layers),
@@ -97,8 +136,11 @@ class YirgacheffeLayer(LayerMathMixin):
97
136
 
98
137
  # This only makes sense (currently) if all layers
99
138
  # have the same pixel pitch (modulo desired accuracy)
100
- if not are_pixel_scales_equal_enough([x.pixel_scale for x in layers]):
101
- raise ValueError("Not all layers are at the same pixel scale")
139
+ projections = [x.map_projection for x in layers if x.map_projection is not None]
140
+ if not projections:
141
+ raise ValueError("No layers have a projection")
142
+ if not all(projections[0] == x for x in projections[1:]):
143
+ raise ValueError("Not all layers are at the same projectin or pixel scale")
102
144
 
103
145
  return Area(
104
146
  left=min(x._underlying_area.left for x in layers),
@@ -109,11 +151,11 @@ class YirgacheffeLayer(LayerMathMixin):
109
151
 
110
152
  @property
111
153
  def geo_transform(self) -> Tuple[float, float, float, float, float, float]:
112
- if self._pixel_scale is None:
113
- raise ValueError("No geo transform for layers without explicit pixel scale")
154
+ if self._projection is None:
155
+ raise AttributeError("No geo transform for layers without explicit pixel scale")
114
156
  return (
115
- self._active_area.left, self._pixel_scale.xstep, 0.0,
116
- self._active_area.top, 0.0, self._pixel_scale.ystep
157
+ self.area.left, self._projection.xstep, 0.0,
158
+ self.area.top, 0.0, self._projection.ystep
117
159
  )
118
160
 
119
161
  def check_pixel_scale(self, scale: PixelScale) -> bool:
@@ -124,21 +166,21 @@ class YirgacheffeLayer(LayerMathMixin):
124
166
  almost_equal(our_scale.ystep, scale.ystep)
125
167
 
126
168
  def set_window_for_intersection(self, new_area: Area) -> None:
127
- if self._pixel_scale is None:
169
+ if self._projection is None:
128
170
  raise ValueError("Can not set Window without explicit pixel scale")
129
171
 
130
172
  new_window = Window(
131
- xoff=round_down_pixels((new_area.left - self._underlying_area.left) / self._pixel_scale.xstep,
132
- self._pixel_scale.xstep),
133
- yoff=round_down_pixels((self._underlying_area.top - new_area.top) / (self._pixel_scale.ystep * -1.0),
134
- self._pixel_scale.ystep * -1.0),
173
+ xoff=round_down_pixels((new_area.left - self._underlying_area.left) / self._projection.xstep,
174
+ self._projection.xstep),
175
+ yoff=round_down_pixels((self._underlying_area.top - new_area.top) / (self._projection.ystep * -1.0),
176
+ self._projection.ystep * -1.0),
135
177
  xsize=round_up_pixels(
136
- (new_area.right - new_area.left) / self._pixel_scale.xstep,
137
- self._pixel_scale.xstep
178
+ (new_area.right - new_area.left) / self._projection.xstep,
179
+ self._projection.xstep
138
180
  ),
139
181
  ysize=round_up_pixels(
140
- (new_area.top - new_area.bottom) / (self._pixel_scale.ystep * -1.0),
141
- (self._pixel_scale.ystep * -1.0)
182
+ (new_area.top - new_area.bottom) / (self._projection.ystep * -1.0),
183
+ (self._projection.ystep * -1.0)
142
184
  ),
143
185
  )
144
186
  if (new_window.xoff < 0) or (new_window.yoff < 0):
@@ -156,21 +198,21 @@ class YirgacheffeLayer(LayerMathMixin):
156
198
  self._active_area = new_area
157
199
 
158
200
  def set_window_for_union(self, new_area: Area) -> None:
159
- if self._pixel_scale is None:
201
+ if self._projection is None:
160
202
  raise ValueError("Can not set Window without explicit pixel scale")
161
203
 
162
204
  new_window = Window(
163
- xoff=round_down_pixels((new_area.left - self._underlying_area.left) / self._pixel_scale.xstep,
164
- self._pixel_scale.xstep),
165
- yoff=round_down_pixels((self._underlying_area.top - new_area.top) / (self._pixel_scale.ystep * -1.0),
166
- self._pixel_scale.ystep * -1.0),
205
+ xoff=round_down_pixels((new_area.left - self._underlying_area.left) / self._projection.xstep,
206
+ self._projection.xstep),
207
+ yoff=round_down_pixels((self._underlying_area.top - new_area.top) / (self._projection.ystep * -1.0),
208
+ self._projection.ystep * -1.0),
167
209
  xsize=round_up_pixels(
168
- (new_area.right - new_area.left) / self._pixel_scale.xstep,
169
- self._pixel_scale.xstep
210
+ (new_area.right - new_area.left) / self._projection.xstep,
211
+ self._projection.xstep
170
212
  ),
171
213
  ysize=round_up_pixels(
172
- (new_area.top - new_area.bottom) / (self._pixel_scale.ystep * -1.0),
173
- (self._pixel_scale.ystep * -1.0)
214
+ (new_area.top - new_area.bottom) / (self._projection.ystep * -1.0),
215
+ (self._projection.ystep * -1.0)
174
216
  ),
175
217
  )
176
218
  if (new_window.xoff > 0) or (new_window.yoff > 0):
@@ -188,14 +230,14 @@ class YirgacheffeLayer(LayerMathMixin):
188
230
  self._active_area = new_area
189
231
 
190
232
  def reset_window(self) -> None:
191
- self._active_area = self._underlying_area
192
- if self._pixel_scale:
193
- abs_xstep, abs_ystep = abs(self._pixel_scale.xstep), abs(self._pixel_scale.ystep)
233
+ self._active_area = None
234
+ if self._projection:
235
+ abs_xstep, abs_ystep = abs(self._projection.xstep), abs(self._projection.ystep)
194
236
  self._window = Window(
195
237
  xoff=0,
196
238
  yoff=0,
197
- xsize=round_up_pixels((self.area.right - self.area.left) / self._pixel_scale.xstep, abs_xstep),
198
- ysize=round_up_pixels((self.area.bottom - self.area.top) / self._pixel_scale.ystep, abs_ystep),
239
+ xsize=round_up_pixels((self.area.right - self.area.left) / self._projection.xstep, abs_xstep),
240
+ ysize=round_up_pixels((self.area.bottom - self.area.top) / self._projection.ystep, abs_ystep),
199
241
  )
200
242
 
201
243
  def offset_window_by_pixels(self, offset: int) -> None:
@@ -244,29 +286,34 @@ class YirgacheffeLayer(LayerMathMixin):
244
286
  def _read_array_for_area(
245
287
  self,
246
288
  target_area: Area,
289
+ target_projection: MapProjection,
247
290
  x: int,
248
291
  y: int,
249
292
  width: int,
250
293
  height: int,
251
294
  ) -> Any:
252
- assert self._pixel_scale is not None
295
+ assert self._projection is not None
296
+ assert self._projection == target_projection
253
297
 
254
298
  target_window = Window(
255
- xoff=round_down_pixels((target_area.left - self._underlying_area.left) / self._pixel_scale.xstep,
256
- self._pixel_scale.xstep),
257
- yoff=round_down_pixels((self._underlying_area.top - target_area.top) / (self._pixel_scale.ystep * -1.0),
258
- self._pixel_scale.ystep * -1.0),
299
+ xoff=round_down_pixels((target_area.left - self._underlying_area.left) / self._projection.xstep,
300
+ self._projection.xstep),
301
+ yoff=round_down_pixels((self._underlying_area.top - target_area.top) / (self._projection.ystep * -1.0),
302
+ self._projection.ystep * -1.0),
259
303
  xsize=round_up_pixels(
260
- (target_area.right - target_area.left) / self._pixel_scale.xstep,
261
- self._pixel_scale.xstep
304
+ (target_area.right - target_area.left) / self._projection.xstep,
305
+ self._projection.xstep
262
306
  ),
263
307
  ysize=round_up_pixels(
264
- (target_area.top - target_area.bottom) / (self._pixel_scale.ystep * -1.0),
265
- (self._pixel_scale.ystep * -1.0)
308
+ (target_area.top - target_area.bottom) / (self._projection.ystep * -1.0),
309
+ (self._projection.ystep * -1.0)
266
310
  ),
267
311
  )
268
312
  return self._read_array_with_window(x, y, width, height, target_window)
269
313
 
314
+ def _read_array(self, x: int, y: int, width: int, height: int) -> Any:
315
+ return self._read_array_with_window(x, y, width, height, self.window)
316
+
270
317
  def read_array(self, x: int, y: int, width: int, height: int) -> Any:
271
318
  """Reads data from the layer based on the current reference window.
272
319
 
@@ -286,29 +333,24 @@ class YirgacheffeLayer(LayerMathMixin):
286
333
  Any
287
334
  An array of values from the layer.
288
335
  """
289
- return self._read_array_with_window(x, y, width, height, self.window)
336
+ res = self._read_array(x, y, width, height)
337
+ return backend.demote_array(res)
290
338
 
291
339
  def latlng_for_pixel(self, x_coord: int, y_coord: int) -> Tuple[float,float]:
292
340
  """Get geo coords for pixel. This is relative to the set view window."""
293
- if "WGS 84" not in self.projection:
341
+ if self._projection is None or "WGS 84" not in self._projection.name:
294
342
  raise NotImplementedError("Not yet supported for other projections")
295
- pixel_scale = self.pixel_scale
296
- if pixel_scale is None:
297
- raise ValueError("Layer has no pixel scale")
298
343
  return (
299
- (y_coord * pixel_scale.ystep) + self.area.top,
300
- (x_coord * pixel_scale.xstep) + self.area.left
344
+ (y_coord * self._projection.ystep) + self.area.top,
345
+ (x_coord * self._projection.xstep) + self.area.left
301
346
  )
302
347
 
303
348
  def pixel_for_latlng(self, lat: float, lng: float) -> Tuple[int,int]:
304
349
  """Get pixel for geo coords. This is relative to the set view window.
305
350
  Result is rounded down to nearest pixel."""
306
- if "WGS 84" not in self.projection:
351
+ if self._projection is None or "WGS 84" not in self._projection.name:
307
352
  raise NotImplementedError("Not yet supported for other projections")
308
- pixel_scale = self.pixel_scale
309
- if pixel_scale is None:
310
- raise ValueError("Layer has no pixel scale")
311
353
  return (
312
- round_down_pixels((lng - self.area.left) / pixel_scale.xstep, abs(pixel_scale.xstep)),
313
- round_down_pixels((lat - self.area.top) / pixel_scale.ystep, abs(pixel_scale.ystep)),
354
+ round_down_pixels((lng - self.area.left) / self._projection.xstep, abs(self._projection.xstep)),
355
+ round_down_pixels((lat - self.area.top) / self._projection.ystep, abs(self._projection.ystep)),
314
356
  )
@@ -1,10 +1,9 @@
1
1
  from typing import Any, Union
2
2
 
3
3
  from ..operators import DataType
4
- from ..window import Area, PixelScale, Window
4
+ from ..window import Area, MapProjection, PixelScale, Window
5
5
  from .base import YirgacheffeLayer
6
6
  from .._backends import backend
7
- from ..constants import WGS_84_PROJECTION
8
7
 
9
8
 
10
9
  class ConstantLayer(YirgacheffeLayer):
@@ -12,7 +11,7 @@ class ConstantLayer(YirgacheffeLayer):
12
11
  missing (e.g., area) without having the calculation full of branches."""
13
12
  def __init__(self, value: Union[int,float]): # pylint: disable=W0231
14
13
  area = Area.world()
15
- super().__init__(area, None, WGS_84_PROJECTION)
14
+ super().__init__(area, None)
16
15
  self.value = float(value)
17
16
 
18
17
  @property
@@ -41,6 +40,7 @@ class ConstantLayer(YirgacheffeLayer):
41
40
  def _read_array_for_area(
42
41
  self,
43
42
  _target_area: Area,
43
+ _target_projection: MapProjection,
44
44
  x: int,
45
45
  y: int,
46
46
  width: int,
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
  import copy
3
3
  from pathlib import Path
4
- from typing import Any, List, Optional, Union
4
+ from typing import Any, List, Optional, Sequence, Union
5
5
 
6
6
  import numpy as np
7
7
  from numpy import ma
8
8
 
9
9
  from ..operators import DataType
10
- from ..rounding import are_pixel_scales_equal_enough, round_down_pixels
10
+ from ..rounding import round_down_pixels
11
11
  from ..window import Area, Window
12
12
  from .base import YirgacheffeLayer
13
13
  from .rasters import RasterLayer
@@ -39,14 +39,14 @@ class GroupLayer(YirgacheffeLayer):
39
39
  @classmethod
40
40
  def layer_from_files(
41
41
  cls,
42
- filenames: Union[List[Path],List[str]],
42
+ filenames: Sequence[Union[Path,str]],
43
43
  name: Optional[str] = None
44
44
  ) -> GroupLayer:
45
45
  if filenames is None:
46
46
  raise ValueError("filenames argument is None")
47
- if len(filenames) < 1:
48
- raise GroupLayerEmpty("No files found")
49
47
  rasters: List[YirgacheffeLayer] = [RasterLayer.layer_from_file(x) for x in filenames]
48
+ if len(rasters) < 1:
49
+ raise GroupLayerEmpty("No files found")
50
50
  return cls(rasters, name)
51
51
 
52
52
  def __init__(
@@ -56,17 +56,15 @@ class GroupLayer(YirgacheffeLayer):
56
56
  ) -> None:
57
57
  if not layers:
58
58
  raise GroupLayerEmpty("Expected one or more layers")
59
- if not are_pixel_scales_equal_enough([x.pixel_scale for x in layers]):
60
- raise ValueError("Not all layers are at the same pixel scale")
61
- if not all(x.projection == layers[0].projection for x in layers):
62
- raise ValueError("Not all layers are the same projection")
59
+ if not all(x.map_projection == layers[0].map_projection for x in layers):
60
+ raise ValueError("Not all layers are the same projection/scale")
63
61
  for layer in layers:
64
- if layer._active_area != layer._underlying_area:
62
+ if layer._active_area is not None:
65
63
  raise ValueError("Layers can not currently be constrained")
66
64
 
67
65
  # area/window are superset of all tiles
68
66
  union = YirgacheffeLayer.find_union(layers)
69
- super().__init__(union, layers[0].pixel_scale, layers[0].projection, name=name)
67
+ super().__init__(union, layers[0].map_projection, name=name)
70
68
 
71
69
  # We store them in reverse order so that from the user's perspective
72
70
  # the first layer in the list will be the most important in terms
@@ -146,7 +144,7 @@ class GroupLayer(YirgacheffeLayer):
146
144
  if len(contributing_layers) == 1:
147
145
  layer, adjusted_layer_window, intersection = contributing_layers[0]
148
146
  if target_window == intersection:
149
- data = layer.read_array(
147
+ data = layer._read_array(
150
148
  intersection.xoff - adjusted_layer_window.xoff,
151
149
  intersection.yoff - adjusted_layer_window.yoff,
152
150
  intersection.xsize,
@@ -158,11 +156,11 @@ class GroupLayer(YirgacheffeLayer):
158
156
 
159
157
  result = np.zeros((ysize, xsize), dtype=float)
160
158
  for layer, adjusted_layer_window, intersection in contributing_layers:
161
- data = layer.read_array(
159
+ data = layer._read_array(
162
160
  intersection.xoff - adjusted_layer_window.xoff,
163
161
  intersection.yoff - adjusted_layer_window.yoff,
164
162
  intersection.xsize,
165
- intersection.ysize,
163
+ intersection.ysize
166
164
  )
167
165
  result_x_offset = (intersection.xoff - xoffset) - window.xoff
168
166
  result_y_offset = (intersection.yoff - yoffset) - window.yoff
@@ -271,7 +269,7 @@ class TiledGroupLayer(GroupLayer):
271
269
  intersection = Window.find_intersection_no_throw([target_window, adjusted_layer_window])
272
270
  if intersection is None:
273
271
  continue
274
- data = layer.read_array(
272
+ data = layer._read_array(
275
273
  intersection.xoff - adjusted_layer_window.xoff,
276
274
  intersection.yoff - adjusted_layer_window.yoff,
277
275
  intersection.xsize,
@@ -6,13 +6,13 @@ import numpy as np
6
6
  from yirgacheffe.operators import DataType
7
7
 
8
8
  from ..rounding import round_up_pixels
9
- from ..window import Area, PixelScale, Window
9
+ from ..window import Area, MapProjection, Window
10
10
  from .base import YirgacheffeLayer
11
11
  from .._backends import backend
12
12
 
13
13
  class H3CellLayer(YirgacheffeLayer):
14
14
 
15
- def __init__(self, cell_id: str, scale: PixelScale, projection: str):
15
+ def __init__(self, cell_id: str, projection: MapProjection):
16
16
  if not h3.is_valid_cell(cell_id):
17
17
  raise ValueError(f"{cell_id} is not a valid H3 cell identifier")
18
18
  self.cell_id = cell_id
@@ -20,7 +20,7 @@ class H3CellLayer(YirgacheffeLayer):
20
20
 
21
21
  self.cell_boundary = h3.cell_to_boundary(cell_id)
22
22
 
23
- abs_xstep, abs_ystep = abs(scale.xstep), abs(scale.ystep)
23
+ abs_xstep, abs_ystep = abs(projection.xstep), abs(projection.ystep)
24
24
  area = Area(
25
25
  left=(floor(min(x[1] for x in self.cell_boundary) / abs_xstep) * abs_xstep),
26
26
  top=(ceil(max(x[0] for x in self.cell_boundary) / abs_ystep) * abs_ystep),
@@ -56,12 +56,12 @@ class H3CellLayer(YirgacheffeLayer):
56
56
  bottom=area.bottom,
57
57
  )
58
58
 
59
- super().__init__(area, scale, projection)
59
+ super().__init__(area, projection)
60
60
  self._window = Window(
61
61
  xoff=0,
62
62
  yoff=0,
63
- xsize=round_up_pixels((area.right - area.left) / scale.xstep, abs_xstep),
64
- ysize=round_up_pixels((area.bottom - area.top) / scale.ystep, abs_ystep),
63
+ xsize=round_up_pixels((area.right - area.left) / projection.xstep, abs_xstep),
64
+ ysize=round_up_pixels((area.bottom - area.top) / projection.ystep, abs_ystep),
65
65
  )
66
66
  self._raster_xsize = self.window.xsize
67
67
  self._raster_ysize = self.window.ysize
@@ -90,7 +90,7 @@ class H3CellLayer(YirgacheffeLayer):
90
90
  ysize: int,
91
91
  window: Window,
92
92
  ) -> Any:
93
- assert self._pixel_scale is not None
93
+ assert self._projection is not None
94
94
 
95
95
  if (xsize <= 0) or (ysize <= 0):
96
96
  raise ValueError("Request dimensions must be positive and non-zero")
@@ -119,8 +119,8 @@ class H3CellLayer(YirgacheffeLayer):
119
119
 
120
120
  subset = np.zeros((intersection.ysize, intersection.xsize))
121
121
 
122
- start_x = self._active_area.left + ((intersection.xoff - self.window.xoff) * self._pixel_scale.xstep)
123
- start_y = self._active_area.top + ((intersection.yoff - self.window.yoff) * self._pixel_scale.ystep)
122
+ start_x = self.area.left + ((intersection.xoff - self.window.xoff) * self._projection.xstep)
123
+ start_y = self.area.top + ((intersection.yoff - self.window.yoff) * self._projection.ystep)
124
124
 
125
125
  for ypixel in range(intersection.ysize):
126
126
  # The latlng_to_cell is quite expensive, so ideally we want to avoid
@@ -144,7 +144,7 @@ class H3CellLayer(YirgacheffeLayer):
144
144
  # only a tiny number of cells that have convex edges, so in future it'd be
145
145
  # interesting to see if we can infer when that is.
146
146
 
147
- lat = start_y + (ypixel * self._pixel_scale.ystep)
147
+ lat = start_y + (ypixel * self._projection.ystep)
148
148
 
149
149
  if self._raster_safe_bounds[0] < lat < self._raster_safe_bounds[1]:
150
150
  # In "safe" zone, try to be clever
@@ -152,14 +152,14 @@ class H3CellLayer(YirgacheffeLayer):
152
152
  right_most = -1
153
153
 
154
154
  for xpixel in range(intersection.xsize):
155
- lng = start_x + (xpixel * self._pixel_scale.xstep)
155
+ lng = start_x + (xpixel * self._projection.xstep)
156
156
  this_cell = h3.latlng_to_cell(lat, lng, self.zoom)
157
157
  if this_cell == self.cell_id:
158
158
  left_most = xpixel
159
159
  break
160
160
 
161
161
  for xpixel in range(intersection.xsize - 1, left_most - 1, -1):
162
- lng = start_x + (xpixel * self._pixel_scale.xstep)
162
+ lng = start_x + (xpixel * self._projection.xstep)
163
163
  this_cell = h3.latlng_to_cell(lat, lng, self.zoom)
164
164
  if this_cell == self.cell_id:
165
165
  right_most = xpixel
@@ -170,7 +170,7 @@ class H3CellLayer(YirgacheffeLayer):
170
170
  else:
171
171
  # Not in safe zone, be diligent.
172
172
  for xpixel in range(intersection.xsize):
173
- lng = start_x + (xpixel * self._pixel_scale.xstep)
173
+ lng = start_x + (xpixel * self._projection.xstep)
174
174
  this_cell = h3.latlng_to_cell(lat, lng, self.zoom)
175
175
  if this_cell == self.cell_id:
176
176
  subset[ypixel][xpixel] = 1.0
@@ -196,19 +196,19 @@ class H3CellLayer(YirgacheffeLayer):
196
196
  left = min(x[1] for x in self.cell_boundary if x[1] > 0.0)
197
197
  right = max(x[1] for x in self.cell_boundary if x[1] < 0.0) + 360.0
198
198
  max_width_projection = right - left
199
- max_width = ceil(max_width_projection / self._pixel_scale.xstep)
199
+ max_width = ceil(max_width_projection / self._projection.xstep)
200
200
 
201
201
  for ypixel in range(yoffset, yoffset + ysize):
202
- lat = self._active_area.top + (ypixel * self._pixel_scale.ystep)
202
+ lat = self.area.top + (ypixel * self._projection.ystep)
203
203
 
204
204
  for xpixel in range(xoffset, min(xoffset + xsize, max_width)):
205
- lng = self._active_area.left + (xpixel * self._pixel_scale.xstep)
205
+ lng = self.area.left + (xpixel * self._projection.xstep)
206
206
  this_cell = h3.latlng_to_cell(lat, lng, self.zoom)
207
207
  if this_cell == self.cell_id:
208
208
  res[ypixel - yoffset][xpixel - xoffset] = 1.0
209
209
 
210
210
  for xpixel in range(xoffset + xsize - 1, xoffset + xsize - max_width, -1):
211
- lng = self._active_area.left + (xpixel * self._pixel_scale.xstep)
211
+ lng = self.area.left + (xpixel * self._projection.xstep)
212
212
  this_cell = h3.latlng_to_cell(lat, lng, self.zoom)
213
213
  if this_cell == self.cell_id:
214
214
  res[ypixel - yoffset][xpixel - xoffset] = 1.0