yirgacheffe 1.6.1__py3-none-any.whl → 1.7.0__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.

Potentially problematic release.


This version of yirgacheffe might be problematic. Click here for more details.

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
@@ -5,7 +5,7 @@ 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(
@@ -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,12 @@
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
7
10
 
8
11
  class YirgacheffeLayer(LayerMathMixin):
9
12
  """The common base class for the different layer types. Most still inherit from RasterLayer as deep down
@@ -12,13 +15,15 @@ class YirgacheffeLayer(LayerMathMixin):
12
15
 
13
16
  def __init__(self,
14
17
  area: Area,
15
- pixel_scale: Optional[PixelScale],
16
- projection: str,
18
+ projection: Optional[MapProjection],
17
19
  name: Optional[str] = None
18
20
  ):
19
- self._pixel_scale = pixel_scale
21
+ # This is just to catch code that uses the old private API
22
+ if projection is not None and not isinstance(projection, MapProjection):
23
+ raise TypeError("projection value of wrong type")
24
+
20
25
  self._underlying_area = area
21
- self._active_area = area
26
+ self._active_area: Optional[Area] = None
22
27
  self._projection = projection
23
28
  self._window: Optional[Window] = None
24
29
  self.name = name
@@ -49,16 +54,46 @@ class YirgacheffeLayer(LayerMathMixin):
49
54
  raise NotImplementedError("Must be overridden by subclass")
50
55
 
51
56
  @property
52
- def projection(self) -> str:
53
- return self._projection
57
+ @deprecation.deprecated(
58
+ deprecated_in="1.7",
59
+ removed_in="2.0",
60
+ current_version=__version__,
61
+ details="Use `map_projection` instead."
62
+ )
63
+ def projection(self) -> Optional[str]:
64
+ if self._projection:
65
+ return self._projection.name
66
+ else:
67
+ return None
54
68
 
55
69
  @property
70
+ @deprecation.deprecated(
71
+ deprecated_in="1.7",
72
+ removed_in="2.0",
73
+ current_version=__version__,
74
+ details="Use `map_projection` instead."
75
+ )
56
76
  def pixel_scale(self) -> Optional[PixelScale]:
57
- return self._pixel_scale
77
+ if self._projection:
78
+ return PixelScale(self._projection.xstep, self._projection.ystep)
79
+ else:
80
+ return None
81
+
82
+ @property
83
+ def map_projection(self) -> Optional[MapProjection]:
84
+ return self._projection
58
85
 
59
86
  @property
60
87
  def area(self) -> Area:
61
- return self._active_area
88
+ if self._active_area is not None:
89
+ return self._active_area
90
+ else:
91
+ return self._underlying_area
92
+
93
+ def _get_operation_area(self, projection: Optional[MapProjection]) -> Area:
94
+ if self._projection is not None and projection is not None and self._projection != projection:
95
+ raise ValueError("Calculation projection does not match layer projection")
96
+ return self.area
62
97
 
63
98
  @property
64
99
  def window(self) -> Window:
@@ -77,8 +112,11 @@ class YirgacheffeLayer(LayerMathMixin):
77
112
 
78
113
  # This only makes sense (currently) if all layers
79
114
  # 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")
115
+ projections = [x.map_projection for x in layers if x.map_projection is not None]
116
+ if not projections:
117
+ raise ValueError("No layers have a projection")
118
+ if not all(projections[0] == x for x in projections[1:]):
119
+ raise ValueError("Not all layers are at the same projectin or pixel scale")
82
120
 
83
121
  intersection = Area(
84
122
  left=max(x._underlying_area.left for x in layers),
@@ -97,8 +135,11 @@ class YirgacheffeLayer(LayerMathMixin):
97
135
 
98
136
  # This only makes sense (currently) if all layers
99
137
  # 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")
138
+ projections = [x.map_projection for x in layers if x.map_projection is not None]
139
+ if not projections:
140
+ raise ValueError("No layers have a projection")
141
+ if not all(projections[0] == x for x in projections[1:]):
142
+ raise ValueError("Not all layers are at the same projectin or pixel scale")
102
143
 
103
144
  return Area(
104
145
  left=min(x._underlying_area.left for x in layers),
@@ -109,11 +150,11 @@ class YirgacheffeLayer(LayerMathMixin):
109
150
 
110
151
  @property
111
152
  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")
153
+ if self._projection is None:
154
+ raise AttributeError("No geo transform for layers without explicit pixel scale")
114
155
  return (
115
- self._active_area.left, self._pixel_scale.xstep, 0.0,
116
- self._active_area.top, 0.0, self._pixel_scale.ystep
156
+ self.area.left, self._projection.xstep, 0.0,
157
+ self.area.top, 0.0, self._projection.ystep
117
158
  )
118
159
 
119
160
  def check_pixel_scale(self, scale: PixelScale) -> bool:
@@ -124,21 +165,21 @@ class YirgacheffeLayer(LayerMathMixin):
124
165
  almost_equal(our_scale.ystep, scale.ystep)
125
166
 
126
167
  def set_window_for_intersection(self, new_area: Area) -> None:
127
- if self._pixel_scale is None:
168
+ if self._projection is None:
128
169
  raise ValueError("Can not set Window without explicit pixel scale")
129
170
 
130
171
  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),
172
+ xoff=round_down_pixels((new_area.left - self._underlying_area.left) / self._projection.xstep,
173
+ self._projection.xstep),
174
+ yoff=round_down_pixels((self._underlying_area.top - new_area.top) / (self._projection.ystep * -1.0),
175
+ self._projection.ystep * -1.0),
135
176
  xsize=round_up_pixels(
136
- (new_area.right - new_area.left) / self._pixel_scale.xstep,
137
- self._pixel_scale.xstep
177
+ (new_area.right - new_area.left) / self._projection.xstep,
178
+ self._projection.xstep
138
179
  ),
139
180
  ysize=round_up_pixels(
140
- (new_area.top - new_area.bottom) / (self._pixel_scale.ystep * -1.0),
141
- (self._pixel_scale.ystep * -1.0)
181
+ (new_area.top - new_area.bottom) / (self._projection.ystep * -1.0),
182
+ (self._projection.ystep * -1.0)
142
183
  ),
143
184
  )
144
185
  if (new_window.xoff < 0) or (new_window.yoff < 0):
@@ -156,21 +197,21 @@ class YirgacheffeLayer(LayerMathMixin):
156
197
  self._active_area = new_area
157
198
 
158
199
  def set_window_for_union(self, new_area: Area) -> None:
159
- if self._pixel_scale is None:
200
+ if self._projection is None:
160
201
  raise ValueError("Can not set Window without explicit pixel scale")
161
202
 
162
203
  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),
204
+ xoff=round_down_pixels((new_area.left - self._underlying_area.left) / self._projection.xstep,
205
+ self._projection.xstep),
206
+ yoff=round_down_pixels((self._underlying_area.top - new_area.top) / (self._projection.ystep * -1.0),
207
+ self._projection.ystep * -1.0),
167
208
  xsize=round_up_pixels(
168
- (new_area.right - new_area.left) / self._pixel_scale.xstep,
169
- self._pixel_scale.xstep
209
+ (new_area.right - new_area.left) / self._projection.xstep,
210
+ self._projection.xstep
170
211
  ),
171
212
  ysize=round_up_pixels(
172
- (new_area.top - new_area.bottom) / (self._pixel_scale.ystep * -1.0),
173
- (self._pixel_scale.ystep * -1.0)
213
+ (new_area.top - new_area.bottom) / (self._projection.ystep * -1.0),
214
+ (self._projection.ystep * -1.0)
174
215
  ),
175
216
  )
176
217
  if (new_window.xoff > 0) or (new_window.yoff > 0):
@@ -188,14 +229,14 @@ class YirgacheffeLayer(LayerMathMixin):
188
229
  self._active_area = new_area
189
230
 
190
231
  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)
232
+ self._active_area = None
233
+ if self._projection:
234
+ abs_xstep, abs_ystep = abs(self._projection.xstep), abs(self._projection.ystep)
194
235
  self._window = Window(
195
236
  xoff=0,
196
237
  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),
238
+ xsize=round_up_pixels((self.area.right - self.area.left) / self._projection.xstep, abs_xstep),
239
+ ysize=round_up_pixels((self.area.bottom - self.area.top) / self._projection.ystep, abs_ystep),
199
240
  )
200
241
 
201
242
  def offset_window_by_pixels(self, offset: int) -> None:
@@ -244,25 +285,27 @@ class YirgacheffeLayer(LayerMathMixin):
244
285
  def _read_array_for_area(
245
286
  self,
246
287
  target_area: Area,
288
+ target_projection: MapProjection,
247
289
  x: int,
248
290
  y: int,
249
291
  width: int,
250
292
  height: int,
251
293
  ) -> Any:
252
- assert self._pixel_scale is not None
294
+ assert self._projection is not None
295
+ assert self._projection == target_projection
253
296
 
254
297
  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),
298
+ xoff=round_down_pixels((target_area.left - self._underlying_area.left) / self._projection.xstep,
299
+ self._projection.xstep),
300
+ yoff=round_down_pixels((self._underlying_area.top - target_area.top) / (self._projection.ystep * -1.0),
301
+ self._projection.ystep * -1.0),
259
302
  xsize=round_up_pixels(
260
- (target_area.right - target_area.left) / self._pixel_scale.xstep,
261
- self._pixel_scale.xstep
303
+ (target_area.right - target_area.left) / self._projection.xstep,
304
+ self._projection.xstep
262
305
  ),
263
306
  ysize=round_up_pixels(
264
- (target_area.top - target_area.bottom) / (self._pixel_scale.ystep * -1.0),
265
- (self._pixel_scale.ystep * -1.0)
307
+ (target_area.top - target_area.bottom) / (self._projection.ystep * -1.0),
308
+ (self._projection.ystep * -1.0)
266
309
  ),
267
310
  )
268
311
  return self._read_array_with_window(x, y, width, height, target_window)
@@ -290,25 +333,19 @@ class YirgacheffeLayer(LayerMathMixin):
290
333
 
291
334
  def latlng_for_pixel(self, x_coord: int, y_coord: int) -> Tuple[float,float]:
292
335
  """Get geo coords for pixel. This is relative to the set view window."""
293
- if "WGS 84" not in self.projection:
336
+ if self._projection is None or "WGS 84" not in self._projection.name:
294
337
  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
338
  return (
299
- (y_coord * pixel_scale.ystep) + self.area.top,
300
- (x_coord * pixel_scale.xstep) + self.area.left
339
+ (y_coord * self._projection.ystep) + self.area.top,
340
+ (x_coord * self._projection.xstep) + self.area.left
301
341
  )
302
342
 
303
343
  def pixel_for_latlng(self, lat: float, lng: float) -> Tuple[int,int]:
304
344
  """Get pixel for geo coords. This is relative to the set view window.
305
345
  Result is rounded down to nearest pixel."""
306
- if "WGS 84" not in self.projection:
346
+ if self._projection is None or "WGS 84" not in self._projection.name:
307
347
  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
348
  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)),
349
+ round_down_pixels((lng - self.area.left) / self._projection.xstep, abs(self._projection.xstep)),
350
+ round_down_pixels((lat - self.area.top) / self._projection.ystep, abs(self._projection.ystep)),
314
351
  )
@@ -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,
@@ -7,7 +7,7 @@ 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
@@ -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
@@ -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
@@ -7,7 +7,7 @@ import numpy as np
7
7
  from osgeo import gdal
8
8
 
9
9
  from ..constants import WGS_84_PROJECTION
10
- from ..window import Area, PixelScale, Window
10
+ from ..window import Area, MapProjection, PixelScale, Window
11
11
  from ..rounding import round_up_pixels
12
12
  from .base import YirgacheffeLayer
13
13
  from ..operators import DataType
@@ -107,14 +107,14 @@ class RasterLayer(YirgacheffeLayer):
107
107
  if isinstance(datatype, int):
108
108
  datatype = DataType.of_gdal(datatype)
109
109
 
110
- scale = layer.pixel_scale
111
- if scale is None:
110
+ projection = layer.map_projection
111
+ if projection is None:
112
112
  raise ValueError("Can not work out area without explicit pixel scale")
113
- abs_xstep, abs_ystep = abs(scale.xstep), abs(scale.ystep)
113
+ abs_xstep, abs_ystep = abs(projection.xstep), abs(projection.ystep)
114
114
  width = round_up_pixels((area.right - area.left) / abs_xstep, abs_xstep)
115
115
  height = round_up_pixels((area.top - area.bottom) / abs_ystep, abs_ystep)
116
116
  geo_transform = (
117
- area.left, scale.xstep, 0.0, area.top, 0.0, scale.ystep
117
+ area.left, projection.xstep, 0.0, area.top, 0.0, projection.ystep
118
118
  )
119
119
 
120
120
  if datatype is None:
@@ -150,7 +150,7 @@ class RasterLayer(YirgacheffeLayer):
150
150
  options,
151
151
  )
152
152
  dataset.SetGeoTransform(geo_transform)
153
- dataset.SetProjection(layer.projection)
153
+ dataset.SetProjection(projection.name)
154
154
  if nodata is not None:
155
155
  dataset.GetRasterBand(1).SetNoDataValue(nodata)
156
156
 
@@ -168,11 +168,11 @@ class RasterLayer(YirgacheffeLayer):
168
168
  source_dataset = source._dataset
169
169
  assert source_dataset is not None
170
170
 
171
- old_pixel_scale = source.pixel_scale
172
- assert old_pixel_scale
171
+ old_projection = source.map_projection
172
+ assert old_projection is not None
173
173
 
174
- x_scale = old_pixel_scale.xstep / new_pixel_scale.xstep
175
- y_scale = old_pixel_scale.ystep / new_pixel_scale.ystep
174
+ x_scale = old_projection.xstep / new_pixel_scale.xstep
175
+ y_scale = old_projection.ystep / new_pixel_scale.ystep
176
176
  new_width = round_up_pixels(source_dataset.RasterXSize * x_scale,
177
177
  abs(new_pixel_scale.xstep))
178
178
  new_height = round_up_pixels(source_dataset.RasterYSize * y_scale,
@@ -242,18 +242,17 @@ class RasterLayer(YirgacheffeLayer):
242
242
  raise ValueError("None is not a valid dataset")
243
243
 
244
244
  transform = dataset.GetGeoTransform()
245
- scale = PixelScale(transform[1], transform[5])
245
+ projection = MapProjection(dataset.GetProjection(), transform[1], transform[5])
246
246
  area = Area(
247
247
  left=transform[0],
248
248
  top=transform[3],
249
- right=transform[0] + (dataset.RasterXSize * scale.xstep),
250
- bottom=transform[3] + (dataset.RasterYSize * scale.ystep),
249
+ right=transform[0] + (dataset.RasterXSize * projection.xstep),
250
+ bottom=transform[3] + (dataset.RasterYSize * projection.ystep),
251
251
  )
252
252
 
253
253
  super().__init__(
254
254
  area,
255
- scale,
256
- dataset.GetProjection(),
255
+ projection,
257
256
  name=name
258
257
  )
259
258