yirgacheffe 1.2.1__py3-none-any.whl → 1.3.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.

@@ -1,5 +1,7 @@
1
1
  from enum import Enum
2
2
 
3
+ from osgeo import gdal
4
+
3
5
  class operators(Enum):
4
6
  ADD = 1
5
7
  SUB = 2
@@ -32,3 +34,25 @@ class operators(Enum):
32
34
  FLOORDIV = 29
33
35
  CONV2D = 30
34
36
  ABS = 31
37
+ ASTYPE = 32
38
+
39
+
40
+ class dtype(Enum):
41
+ Float32 = gdal.GDT_Float32
42
+ Float64 = gdal.GDT_Float64
43
+ Byte = gdal.GDT_Byte
44
+ Int8 = gdal.GDT_Int8
45
+ Int16 = gdal.GDT_Int16
46
+ Int32 = gdal.GDT_Int32
47
+ Int64 = gdal.GDT_Int64
48
+ UInt8 = gdal.GDT_Byte
49
+ UInt16 = gdal.GDT_UInt16
50
+ UInt32 = gdal.GDT_UInt32
51
+ UInt64 = gdal.GDT_UInt64
52
+
53
+ def to_gdal(self):
54
+ return self.value
55
+
56
+ @classmethod
57
+ def of_gdal(cls, val):
58
+ return cls(val)
@@ -4,6 +4,7 @@ import mlx.core as mx # pylint: disable=E0001,E0611,E0401
4
4
  import mlx.nn
5
5
 
6
6
  from .enumeration import operators as op
7
+ from .enumeration import dtype
7
8
 
8
9
  array_t = mx.array
9
10
  float_t = mx.float32
@@ -121,9 +122,63 @@ def conv2d_op(data, weights):
121
122
  preped_data = mx.array(np.reshape(unshifted_data_shape, conv_data_shape))
122
123
 
123
124
  shifted_res = conv(preped_data)[0]
124
- res = np.reshape(shifted_res, [1] + list(shifted_res.shape)[:-1])
125
+ res = mx.reshape(shifted_res, [1] + list(shifted_res.shape)[:-1])
125
126
  return res[0]
126
127
 
128
+
129
+ def dtype_to_backed(dt):
130
+ match dt:
131
+ case dtype.Float32:
132
+ return mx.float32
133
+ case dtype.Float64:
134
+ return mx.float32
135
+ case dtype.Byte:
136
+ return mx.uint8
137
+ case dtype.Int8:
138
+ return mx.int8
139
+ case dtype.Int16:
140
+ return mx.int16
141
+ case dtype.Int32:
142
+ return mx.int32
143
+ case dtype.Int64:
144
+ return mx.int64
145
+ case dtype.UInt8:
146
+ return mx.uint8
147
+ case dtype.UInt16:
148
+ return mx.uint16
149
+ case dtype.UInt32:
150
+ return mx.uint32
151
+ case dtype.UInt64:
152
+ return mx.uint64
153
+ case _:
154
+ raise ValueError
155
+
156
+ def backend_to_dtype(val):
157
+ match val:
158
+ case mx.float32:
159
+ return dtype.Float32
160
+ case mx.int8:
161
+ return dtype.Int8
162
+ case mx.int16:
163
+ return dtype.Int16
164
+ case mx.int32:
165
+ return dtype.Int32
166
+ case mx.int64:
167
+ return dtype.Int64
168
+ case mx.uint8:
169
+ return dtype.Byte
170
+ case mx.uint16:
171
+ return dtype.UInt16
172
+ case mx.uint32:
173
+ return dtype.UInt32
174
+ case mx.uint64:
175
+ return dtype.UInt64
176
+ case _:
177
+ raise ValueError
178
+
179
+ def astype_op(data, datatype):
180
+ return data.astype(dtype_to_backed(datatype))
181
+
127
182
  operator_map = {
128
183
  op.ADD: mx.array.__add__,
129
184
  op.SUB: mx.array.__sub__,
@@ -155,4 +210,5 @@ operator_map = {
155
210
  op.FLOORDIV: mx.array.__floordiv__,
156
211
  op.CONV2D: conv2d_op,
157
212
  op.ABS: mx.abs,
213
+ op.ASTYPE: astype_op,
158
214
  }
@@ -3,6 +3,7 @@ import numpy as np
3
3
  import torch
4
4
 
5
5
  from .enumeration import operators as op
6
+ from .enumeration import dtype
6
7
 
7
8
  array_t = np.ndarray
8
9
  float_t = np.float64
@@ -61,52 +62,91 @@ def conv2d_op(data, weights):
61
62
  res = conv(preped_data)
62
63
  return res.detach().numpy()[0][0]
63
64
 
64
- operator_map = {
65
- op.ADD: np.ndarray.__add__,
66
- op.SUB: np.ndarray.__sub__,
67
- op.MUL: np.ndarray.__mul__,
68
- op.TRUEDIV: np.ndarray.__truediv__,
69
- op.POW: np.ndarray.__pow__,
70
- op.EQ: np.ndarray.__eq__,
71
- op.NE: np.ndarray.__ne__,
72
- op.LT: np.ndarray.__lt__,
73
- op.LE: np.ndarray.__le__,
74
- op.GT: np.ndarray.__gt__,
75
- op.GE: np.ndarray.__ge__,
76
- op.AND: np.ndarray.__and__,
77
- op.OR: np.ndarray.__or__,
78
- op.LOG: np.log,
79
- op.LOG2: np.log2,
80
- op.LOG10: np.log10,
81
- op.EXP: np.exp,
82
- op.EXP2: np.exp2,
83
- op.CLIP: np.clip,
84
- op.WHERE: np.where,
85
- op.MIN: np.min,
86
- op.MAX: np.max,
87
- op.MINIMUM: np.minimum,
88
- op.MAXIMUM: np.maximum,
89
- op.NAN_TO_NUM: np.nan_to_num,
90
- op.ISIN: np.isin,
91
- op.REMAINDER: np.ndarray.__mod__,
92
- op.FLOORDIV: np.ndarray.__floordiv__,
93
- op.CONV2D: conv2d_op,
94
- op.ABS: np.abs,
95
- }
65
+ def dtype_to_backed(dt):
66
+ match dt:
67
+ case dtype.Float32:
68
+ return np.float32
69
+ case dtype.Float64:
70
+ return np.float64
71
+ case dtype.Byte:
72
+ return np.uint8
73
+ case dtype.Int8:
74
+ return np.int8
75
+ case dtype.Int16:
76
+ return np.int16
77
+ case dtype.Int32:
78
+ return np.int32
79
+ case dtype.Int64:
80
+ return np.int64
81
+ case dtype.UInt8:
82
+ return np.uint8
83
+ case dtype.UInt16:
84
+ return np.uint16
85
+ case dtype.UInt32:
86
+ return np.uint32
87
+ case dtype.UInt64:
88
+ return np.uint64
89
+ case _:
90
+ raise ValueError
91
+
92
+ def backend_to_dtype(val):
93
+ match val:
94
+ case np.float32:
95
+ return dtype.Float32
96
+ case np.float64:
97
+ return dtype.Float64
98
+ case np.int8:
99
+ return dtype.Int8
100
+ case np.int16:
101
+ return dtype.Int16
102
+ case np.int32:
103
+ return dtype.Int32
104
+ case np.int64:
105
+ return dtype.Int64
106
+ case np.uint8:
107
+ return dtype.Byte
108
+ case np.uint16:
109
+ return dtype.UInt16
110
+ case np.uint32:
111
+ return dtype.UInt32
112
+ case np.uint64:
113
+ return dtype.UInt64
114
+ case _:
115
+ raise ValueError
96
116
 
97
- operator_str_map = {
98
- op.POW: "np.ndarray.__pow__(%s, %s)",
99
- op.LOG: "np.log(%s)",
100
- op.LOG2: "np.log2(%s)",
101
- op.LOG10: "np.log10(%s)",
102
- op.EXP: "np.exp(%s)",
103
- op.EXP2: "np.exp2(%s)",
104
- op.CLIP: "np.clip",
105
- op.WHERE: "np.where(%s, %s, %s)",
106
- op.MIN: "np.min(%s)",
107
- op.MAX: "np.max(%s)",
108
- op.MINIMUM: "np.minimum(%s)",
109
- op.MAXIMUM: "np.maximum(%s)",
110
- op.NAN_TO_NUM: "np.nan_to_num(%s)",
111
- op.ISIN: "np.isin(%s, %s)",
117
+ def astype_op(data, datatype):
118
+ return data.astype(dtype_to_backed(datatype))
119
+
120
+ operator_map = {
121
+ op.ADD: np.ndarray.__add__,
122
+ op.SUB: np.ndarray.__sub__,
123
+ op.MUL: np.ndarray.__mul__,
124
+ op.TRUEDIV: np.ndarray.__truediv__,
125
+ op.POW: np.ndarray.__pow__,
126
+ op.EQ: np.ndarray.__eq__,
127
+ op.NE: np.ndarray.__ne__,
128
+ op.LT: np.ndarray.__lt__,
129
+ op.LE: np.ndarray.__le__,
130
+ op.GT: np.ndarray.__gt__,
131
+ op.GE: np.ndarray.__ge__,
132
+ op.AND: np.ndarray.__and__,
133
+ op.OR: np.ndarray.__or__,
134
+ op.LOG: np.log,
135
+ op.LOG2: np.log2,
136
+ op.LOG10: np.log10,
137
+ op.EXP: np.exp,
138
+ op.EXP2: np.exp2,
139
+ op.CLIP: np.clip,
140
+ op.WHERE: np.where,
141
+ op.MIN: np.min,
142
+ op.MAX: np.max,
143
+ op.MINIMUM: np.minimum,
144
+ op.MAXIMUM: np.maximum,
145
+ op.NAN_TO_NUM: np.nan_to_num,
146
+ op.ISIN: np.isin,
147
+ op.REMAINDER: np.ndarray.__mod__,
148
+ op.FLOORDIV: np.ndarray.__floordiv__,
149
+ op.CONV2D: conv2d_op,
150
+ op.ABS: np.abs,
151
+ op.ASTYPE: astype_op,
112
152
  }
@@ -13,7 +13,6 @@ try:
13
13
  except ModuleNotFoundError:
14
14
  pass
15
15
 
16
-
17
16
  class Layer(RasterLayer):
18
17
  """A place holder for now, at some point I want to replace Layer with RasterLayer."""
19
18
 
@@ -1,7 +1,7 @@
1
1
 
2
2
  from typing import Any, List, Optional, Tuple
3
3
 
4
- from ..operators import LayerMathMixin
4
+ from ..operators import DataType, LayerMathMixin
5
5
  from ..rounding import almost_equal, are_pixel_scales_equal_enough, round_up_pixels, round_down_pixels
6
6
  from ..window import Area, PixelScale, Window
7
7
 
@@ -35,7 +35,7 @@ class YirgacheffeLayer(LayerMathMixin):
35
35
  self.close()
36
36
 
37
37
  @property
38
- def datatype(self) -> int:
38
+ def datatype(self) -> DataType:
39
39
  raise NotImplementedError("Must be overridden by subclass")
40
40
 
41
41
  @property
@@ -1,7 +1,6 @@
1
1
  from typing import Any, Union
2
2
 
3
- from osgeo import gdal
4
-
3
+ from ..operators import DataType
5
4
  from ..window import Area, PixelScale, Window
6
5
  from .base import YirgacheffeLayer
7
6
  from ..backends import backend
@@ -22,8 +21,8 @@ class ConstantLayer(YirgacheffeLayer):
22
21
  self.value = float(value)
23
22
 
24
23
  @property
25
- def datatype(self) -> int:
26
- return gdal.GDT_Float64
24
+ def datatype(self) -> DataType:
25
+ return DataType.Float64
27
26
 
28
27
  def check_pixel_scale(self, _scale: PixelScale) -> bool:
29
28
  return True
@@ -4,6 +4,7 @@ import os
4
4
  from typing import Any, List, Optional, TypeVar
5
5
 
6
6
  import numpy as np
7
+ from yirgacheffe.operators import DataType
7
8
 
8
9
  from ..rounding import are_pixel_scales_equal_enough, round_down_pixels
9
10
  from ..window import Area, Window
@@ -72,8 +73,8 @@ class GroupLayer(YirgacheffeLayer):
72
73
  pass
73
74
 
74
75
  @property
75
- def datatype(self):
76
- return self.layers[0].datatype
76
+ def datatype(self) -> DataType:
77
+ return DataType.of_gdal(self.layers[0].datatype)
77
78
 
78
79
  def set_window_for_intersection(self, new_area: Area) -> None:
79
80
  super().set_window_for_intersection(new_area)
@@ -3,7 +3,7 @@ from typing import Any
3
3
 
4
4
  import h3
5
5
  import numpy as np
6
- from osgeo import gdal
6
+ from yirgacheffe.operators import DataType
7
7
 
8
8
  from ..rounding import round_up_pixels
9
9
  from ..window import Area, PixelScale, Window
@@ -76,8 +76,8 @@ class H3CellLayer(YirgacheffeLayer):
76
76
 
77
77
 
78
78
  @property
79
- def datatype(self) -> int:
80
- return gdal.GDT_Float64
79
+ def datatype(self) -> DataType:
80
+ return DataType.Float64
81
81
 
82
82
  def read_array_with_window(self, xoffset: int, yoffset: int, xsize: int, ysize: int, window: Window) -> Any:
83
83
  if (xsize <= 0) or (ysize <= 0):
@@ -9,6 +9,7 @@ from .. import WGS_84_PROJECTION
9
9
  from ..window import Area, PixelScale, Window
10
10
  from ..rounding import round_up_pixels
11
11
  from .base import YirgacheffeLayer
12
+ from ..operators import DataType
12
13
  from ..backends import backend
13
14
 
14
15
  # Still to early to require Python 3.11 :/
@@ -27,7 +28,7 @@ class RasterLayer(YirgacheffeLayer):
27
28
  def empty_raster_layer(
28
29
  area: Area,
29
30
  scale: PixelScale,
30
- datatype: int,
31
+ datatype: Union[int, DataType],
31
32
  filename: Optional[str]=None,
32
33
  projection: str=WGS_84_PROJECTION,
33
34
  name: Optional[str]=None,
@@ -47,6 +48,10 @@ class RasterLayer(YirgacheffeLayer):
47
48
  bottom=math.floor(area.bottom / abs_ystep) * abs_ystep,
48
49
  )
49
50
 
51
+ # This used to the the GDAL type, so we support that for legacy reasons
52
+ if isinstance(datatype, int):
53
+ datatype = DataType.of_gdal(datatype)
54
+
50
55
  options = []
51
56
  if threads is not None:
52
57
  options.append(f"NUM_THREADS={threads}")
@@ -69,7 +74,7 @@ class RasterLayer(YirgacheffeLayer):
69
74
  round_up_pixels((pixel_friendly_area.right - pixel_friendly_area.left) / abs_xstep, abs_xstep),
70
75
  round_up_pixels((pixel_friendly_area.top - pixel_friendly_area.bottom) / abs_ystep, abs_ystep),
71
76
  bands,
72
- datatype,
77
+ datatype.to_gdal(),
73
78
  options
74
79
  )
75
80
  dataset.SetGeoTransform([
@@ -85,7 +90,7 @@ class RasterLayer(YirgacheffeLayer):
85
90
  layer: Any,
86
91
  filename: Optional[str]=None,
87
92
  area: Optional[Area]=None,
88
- datatype: Optional[int]=None,
93
+ datatype: Optional[Union[int, DataType]]=None,
89
94
  compress: bool=True,
90
95
  nodata: Optional[Union[float,int]]=None,
91
96
  nbits: Optional[int]=None,
@@ -98,6 +103,10 @@ class RasterLayer(YirgacheffeLayer):
98
103
  area = layer.area
99
104
  assert area is not None
100
105
 
106
+ if datatype is not None:
107
+ if isinstance(datatype, int):
108
+ datatype = DataType.of_gdal(datatype)
109
+
101
110
  scale = layer.pixel_scale
102
111
  if scale is None:
103
112
  raise ValueError("Can not work out area without explicit pixel scale")
@@ -130,7 +139,7 @@ class RasterLayer(YirgacheffeLayer):
130
139
  width,
131
140
  height,
132
141
  bands,
133
- datatype if datatype is not None else layer.datatype,
142
+ (datatype if datatype is not None else layer.datatype).to_gdal(),
134
143
  options,
135
144
  )
136
145
  dataset.SetGeoTransform(geo_transform)
@@ -181,7 +190,7 @@ class RasterLayer(YirgacheffeLayer):
181
190
  new_width,
182
191
  new_height,
183
192
  1,
184
- source.datatype,
193
+ source.datatype.to_gdal(),
185
194
  options
186
195
  )
187
196
  dataset.SetGeoTransform((
@@ -279,11 +288,11 @@ class RasterLayer(YirgacheffeLayer):
279
288
  raise FileNotFoundError(f"Failed to open pickled raster {self._dataset_path}") from exc
280
289
 
281
290
  @property
282
- def datatype(self) -> int:
291
+ def datatype(self) -> DataType:
283
292
  if self._dataset is None:
284
293
  self._unpark()
285
294
  assert self._dataset
286
- return self._dataset.GetRasterBand(1).DataType
295
+ return DataType.of_gdal(self._dataset.GetRasterBand(1).DataType)
287
296
 
288
297
  def read_array_with_window(self, xoffset: int, yoffset: int, xsize: int, ysize: int, window: Window) -> Any:
289
298
  if self._dataset is None:
@@ -2,6 +2,7 @@ from math import floor, ceil
2
2
  from typing import Any, Optional
3
3
 
4
4
  from skimage import transform
5
+ from yirgacheffe.operators import DataType
5
6
 
6
7
  from ..window import PixelScale, Window
7
8
  from .rasters import RasterLayer, YirgacheffeLayer
@@ -56,7 +57,7 @@ class RescaledRasterLayer(YirgacheffeLayer):
56
57
  self._src._unpark()
57
58
 
58
59
  @property
59
- def datatype(self) -> int:
60
+ def datatype(self) -> DataType:
60
61
  return self._src.datatype
61
62
 
62
63
  def read_array_with_window(self, xoffset: int, yoffset: int, xsize: int, ysize: int, window: Window) -> Any:
@@ -5,6 +5,7 @@ from typing_extensions import NotRequired
5
5
 
6
6
  from osgeo import gdal, ogr
7
7
 
8
+ from ..operators import DataType
8
9
  from ..window import Area, PixelScale
9
10
  from .base import YirgacheffeLayer
10
11
  from .rasters import RasterLayer
@@ -64,7 +65,7 @@ class RasteredVectorLayer(RasterLayer):
64
65
  where_filter: Optional[str],
65
66
  scale: PixelScale,
66
67
  projection: str,
67
- datatype: Optional[int] = None,
68
+ datatype: Optional[Union[int, DataType]] = None,
68
69
  burn_value: Union[int,float,str] = 1,
69
70
  ): # pylint: disable=W0221
70
71
  vectors = ogr.Open(filename)
@@ -78,6 +79,9 @@ class RasteredVectorLayer(RasterLayer):
78
79
  if datatype is None:
79
80
  datatype = estimated_datatype
80
81
 
82
+ if isinstance(datatype, int):
83
+ datatype = DataType.of_gdal(datatype)
84
+
81
85
  vector_layer = RasteredVectorLayer(
82
86
  layer,
83
87
  scale,
@@ -97,7 +101,7 @@ class RasteredVectorLayer(RasterLayer):
97
101
  layer: ogr.Layer,
98
102
  scale: PixelScale,
99
103
  projection: str,
100
- datatype: int = gdal.GDT_Byte,
104
+ datatype: Union[int, DataType] = DataType.Byte,
101
105
  burn_value: Union[int,float,str] = 1,
102
106
  ):
103
107
  if layer is None:
@@ -106,6 +110,9 @@ class RasteredVectorLayer(RasterLayer):
106
110
 
107
111
  self._original = None
108
112
 
113
+ if isinstance(datatype, int):
114
+ datatype = DataType.of_gdal(datatype)
115
+
109
116
  # work out region for mask
110
117
  envelopes = []
111
118
  layer.ResetReading()
@@ -135,7 +142,7 @@ class RasteredVectorLayer(RasterLayer):
135
142
  round((area.right - area.left) / abs_xstep),
136
143
  round((area.top - area.bottom) / abs_ystep),
137
144
  1,
138
- datatype,
145
+ datatype.to_gdal(),
139
146
  []
140
147
  )
141
148
  if not dataset:
@@ -164,7 +171,7 @@ class VectorLayer(YirgacheffeLayer):
164
171
  filename: str,
165
172
  other_layer: YirgacheffeLayer,
166
173
  where_filter: Optional[str]=None,
167
- datatype: Optional[int] = None,
174
+ datatype: Optional[Union[int, DataType]] = None,
168
175
  burn_value: Union[int,float,str] = 1,
169
176
  ):
170
177
  if other_layer is None:
@@ -179,6 +186,10 @@ class VectorLayer(YirgacheffeLayer):
179
186
  if where_filter is not None:
180
187
  layer.SetAttributeFilter(where_filter)
181
188
 
189
+ if datatype is not None:
190
+ if isinstance(datatype, int):
191
+ datatype = DataType.of_gdal(datatype)
192
+
182
193
  vector_layer = VectorLayer(
183
194
  layer,
184
195
  other_layer.pixel_scale,
@@ -205,7 +216,7 @@ class VectorLayer(YirgacheffeLayer):
205
216
  where_filter: Optional[str],
206
217
  scale: PixelScale,
207
218
  projection: str,
208
- datatype: Optional[int] = None,
219
+ datatype: Optional[Union[int, DataType]] = None,
209
220
  burn_value: Union[int,float,str] = 1,
210
221
  anchor: Tuple[float,float] = (0.0, 0.0)
211
222
  ):
@@ -220,6 +231,9 @@ class VectorLayer(YirgacheffeLayer):
220
231
  if datatype is None:
221
232
  datatype = estimated_datatype
222
233
 
234
+ if isinstance(datatype, int):
235
+ datatype = DataType.of_gdal(datatype)
236
+
223
237
  vector_layer = VectorLayer(
224
238
  layer,
225
239
  scale,
@@ -244,7 +258,7 @@ class VectorLayer(YirgacheffeLayer):
244
258
  scale: PixelScale,
245
259
  projection: str,
246
260
  name: Optional[str] = None,
247
- datatype: int = gdal.GDT_Byte,
261
+ datatype: Union[int,DataType] = DataType.Byte,
248
262
  burn_value: Union[int,float,str] = 1,
249
263
  anchor: Tuple[float,float] = (0.0, 0.0)
250
264
  ):
@@ -253,7 +267,10 @@ class VectorLayer(YirgacheffeLayer):
253
267
  self.layer = layer
254
268
  self.name = name
255
269
 
256
- self._datatype = datatype
270
+ if isinstance(datatype, int):
271
+ self._datatype = DataType.of_gdal(datatype)
272
+ else:
273
+ self._datatype = datatype
257
274
 
258
275
  # If the burn value is a number, use it directly, if it's a string
259
276
  # then assume it is a column name in the dataset
@@ -332,7 +349,7 @@ class VectorLayer(YirgacheffeLayer):
332
349
  self.layer.SetAttributeFilter(self._filter)
333
350
 
334
351
  @property
335
- def datatype(self) -> int:
352
+ def datatype(self) -> DataType:
336
353
  return self._datatype
337
354
 
338
355
  def read_array_for_area(self, target_area: Area, x: int, y: int, width: int, height: int) -> Any:
@@ -348,7 +365,7 @@ class VectorLayer(YirgacheffeLayer):
348
365
  width,
349
366
  height,
350
367
  1,
351
- self.datatype,
368
+ self.datatype.to_gdal(),
352
369
  []
353
370
  )
354
371
  if not dataset:
yirgacheffe/operators.py CHANGED
@@ -17,6 +17,7 @@ from .rounding import are_pixel_scales_equal_enough, round_up_pixels, round_down
17
17
  from .window import Area, PixelScale, Window
18
18
  from .backends import backend
19
19
  from .backends.enumeration import operators as op
20
+ from .backends.enumeration import dtype as DataType
20
21
 
21
22
  logger = logging.getLogger(__name__)
22
23
  logger.setLevel(logging.WARNING)
@@ -208,6 +209,14 @@ class LayerMathMixin:
208
209
  def max(self):
209
210
  return LayerOperation(self).max()
210
211
 
212
+ def astype(self, datatype):
213
+ return LayerOperation(
214
+ self,
215
+ op.ASTYPE,
216
+ window_op=WindowOperation.NONE,
217
+ datatype=datatype
218
+ )
219
+
211
220
 
212
221
  class LayerOperation(LayerMathMixin):
213
222
 
@@ -392,7 +401,7 @@ class LayerOperation(LayerMathMixin):
392
401
  )
393
402
 
394
403
  @property
395
- def datatype(self):
404
+ def datatype(self) -> DataType:
396
405
  # TODO: Work out how to indicate type promotion via numpy
397
406
  return self.lhs.datatype
398
407
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yirgacheffe
3
- Version: 1.2.1
3
+ Version: 1.3.0
4
4
  Summary: Abstraction of gdal datasets for doing basic math operations
5
5
  Author-email: Michael Dales <mwd24@cam.ac.uk>
6
6
  License-Expression: ISC
@@ -35,6 +35,14 @@ Example common use-cases:
35
35
  * Do the raster layers get big and take up large amounts of memory? Yirgacheffe will let you do simple numerical operations with layers directly and then worry about the memory management behind the scenes for you.
36
36
 
37
37
 
38
+ ## Installation
39
+
40
+ Yirgacheffe is available via pypi, so can be installed with pip for example:
41
+
42
+ ```SystemShell
43
+ $ pip install yirgacheffe
44
+ ```
45
+
38
46
  ## Basic usage
39
47
 
40
48
  They main unit of data in Yirgacheffe is a "layer", which wraps either a raster dataset or polygon data, and then you can do work on layers without having to worry (unless you choose to) about how they align - Yirgacheffe will work out all the details around overlapping
@@ -339,6 +347,18 @@ with RasterLayer.layer_from_file('original.tif') as layer1:
339
347
  calc.save(result)
340
348
  ```
341
349
 
350
+ ### Type conversion
351
+
352
+ Similar to numpy and other Python numerical libraries, Yirgacheffe will automatically deal with simple type conversion where possible, however sometimes explicit conversion is either necessary or desired. Similar to numpy, there is an `astype` operator that lets you set the conversion:
353
+
354
+ ```python
355
+ from yirgacheffe.operations import DataType
356
+
357
+
358
+ with RasterLayer.layer_from_file('float_data.tif') as float_layer:
359
+ int_layer = float_layer.astype(DataType.Int32)
360
+ ```
361
+
342
362
  ### Apply
343
363
 
344
364
  You can specify a function that takes either data from one layer or from two layers, and returns the processed data. There's two version of this: one that lets you specify a numpy function that'll be applied to the layer data as an array, or one that is more shader like that lets you do pixel wise processing.
@@ -0,0 +1,25 @@
1
+ yirgacheffe/__init__.py,sha256=U5AoPk_iWreSCexG2ID-tmSXiJz4_9Lvzbf42DMvT7k,658
2
+ yirgacheffe/constants.py,sha256=9ZcFLcir4GhPBBDG2rzLhDtJ0pMzpZYuzE9f-CFeA44,12
3
+ yirgacheffe/h3layer.py,sha256=MT2hm6n64hzHSeRPvjn-CwErru937ntKXbEU7CIlPSU,91
4
+ yirgacheffe/operators.py,sha256=dOzScup0rYKU2GbY3j5zFJJ0JMQomq6hN0c1nH1_igs,27198
5
+ yirgacheffe/rounding.py,sha256=ggBG4lMyLMtHLW3dBxr3gBCcF2qhRrY5etZiFGlIoqA,2258
6
+ yirgacheffe/window.py,sha256=0XZdwD4mz0bRU9eBhFY1Xk1hQt6FqCKp3BnUgxZup3c,5224
7
+ yirgacheffe/backends/__init__.py,sha256=149-fg1PVXC36cgyuSZsU8SYOm65fzUmYN_MHZtEyrY,313
8
+ yirgacheffe/backends/enumeration.py,sha256=v1CsWSq97LNS4QXgUc59OyaBQWF5dAG5cDrMY2NWx1o,960
9
+ yirgacheffe/backends/mlx.py,sha256=Bt0Jlt96ECHZmBwI7kqJ6kSVsLDU-6rq0E7oUt7EzOg,5969
10
+ yirgacheffe/backends/numpy.py,sha256=t8rPDqkghE5UfkJi8r6ZUg14f1nL2lsPp61FGqUJwCg,3887
11
+ yirgacheffe/layers/__init__.py,sha256=GR_TJlhPKDK1212CG2T99X2FdBlNo_G8q2zQ6nJbiO4,1534
12
+ yirgacheffe/layers/area.py,sha256=yIRXzeeLi3MMyuh4LG_VgZrKNWe5xwZgDGdgaoYRpP0,3805
13
+ yirgacheffe/layers/base.py,sha256=vVxumZgFLTYXFtVxJEiBVV1QqtQWa5kwL10ckUF-ykE,11617
14
+ yirgacheffe/layers/constant.py,sha256=5YWSdr48Cw3DhryLBx45PNnPi4NzcVuQVin_YmOSWTg,1426
15
+ yirgacheffe/layers/group.py,sha256=2hRta24Mn5JwfDOB4-gJfroa_lVJSUFgSZ8FMeWy_FM,15127
16
+ yirgacheffe/layers/h3layer.py,sha256=Cyrw_6nXc1a_Twsb0FfexKhHAbegKXStL3J5LLk2AX8,9687
17
+ yirgacheffe/layers/rasters.py,sha256=8mAbH49RtKIoA3h9mxWNbPzo8NJWDSfoRPT7fpkTJ4I,12328
18
+ yirgacheffe/layers/rescaled.py,sha256=kWJlu7DuUB3nRDt3VtbJKiqBDQb2Ba8xzIIXTvOGdK8,2945
19
+ yirgacheffe/layers/vectors.py,sha256=JOqM7Ym53ZdxWKpBJac-opPp2N_ynMfet1Xtsi85dpM,15087
20
+ yirgacheffe-1.3.0.dist-info/licenses/LICENSE,sha256=dNSHwUCJr6axStTKDEdnJtfmDdFqlE3h1NPCveqPfnY,757
21
+ yirgacheffe-1.3.0.dist-info/METADATA,sha256=jt79WCW-SqvTFkzqnHQC6VQoK0rJwo6R3uMePZD4CMc,20473
22
+ yirgacheffe-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ yirgacheffe-1.3.0.dist-info/entry_points.txt,sha256=j4KgHXbVGbGyfTySc1ypBdERpfihO4WNjppvCdE9HjE,52
24
+ yirgacheffe-1.3.0.dist-info/top_level.txt,sha256=9DBFlKO2Ld3hG6TuE3qOTd3Tt8ugTiXil4AN4Wr9_y0,12
25
+ yirgacheffe-1.3.0.dist-info/RECORD,,
@@ -1,25 +0,0 @@
1
- yirgacheffe/__init__.py,sha256=U5AoPk_iWreSCexG2ID-tmSXiJz4_9Lvzbf42DMvT7k,658
2
- yirgacheffe/constants.py,sha256=9ZcFLcir4GhPBBDG2rzLhDtJ0pMzpZYuzE9f-CFeA44,12
3
- yirgacheffe/h3layer.py,sha256=MT2hm6n64hzHSeRPvjn-CwErru937ntKXbEU7CIlPSU,91
4
- yirgacheffe/operators.py,sha256=eGa_y5fXXaEb4DkQ7b899bbhzl639yyIVLfVRbkD7vE,26945
5
- yirgacheffe/rounding.py,sha256=ggBG4lMyLMtHLW3dBxr3gBCcF2qhRrY5etZiFGlIoqA,2258
6
- yirgacheffe/window.py,sha256=0XZdwD4mz0bRU9eBhFY1Xk1hQt6FqCKp3BnUgxZup3c,5224
7
- yirgacheffe/backends/__init__.py,sha256=149-fg1PVXC36cgyuSZsU8SYOm65fzUmYN_MHZtEyrY,313
8
- yirgacheffe/backends/enumeration.py,sha256=lddqmD7Z2z2g2SIPkmgNOiNSSZer3rIuBG3qHwy7t_g,474
9
- yirgacheffe/backends/mlx.py,sha256=wJ5kUNWdhpE-DLrgsvxpLeiXm7fUlKvrqik976jM9kc,4558
10
- yirgacheffe/backends/numpy.py,sha256=-y3oLi3BOUuS5l9IJJ3orl08YrPbo9mideW9M1Re6lA,2745
11
- yirgacheffe/layers/__init__.py,sha256=TrKSHpd-TF7wgC371hka6paFXBnqvDtjJYblproNKZs,1535
12
- yirgacheffe/layers/area.py,sha256=yIRXzeeLi3MMyuh4LG_VgZrKNWe5xwZgDGdgaoYRpP0,3805
13
- yirgacheffe/layers/base.py,sha256=pWnmmohGqI4rtH_CruDWfEfOiNcyns-sU2FcNw55ng4,11602
14
- yirgacheffe/layers/constant.py,sha256=Fm76YLZobSlNJjbpJOX8VDDkifYPVtg55zdUc8kt7xc,1412
15
- yirgacheffe/layers/group.py,sha256=RXl6sGa4OagudIb8seveLfnvCMC9_wm-5hrGq6LYgnw,15054
16
- yirgacheffe/layers/h3layer.py,sha256=O4u-tGO32SQaEsgNLlgGCOFd4bjm1mt2coZDL23F_HI,9662
17
- yirgacheffe/layers/rasters.py,sha256=8kT2ETplWTJNb7F9HsmqRMz7APolByvIkZuBB0mRduk,11907
18
- yirgacheffe/layers/rescaled.py,sha256=hEj6BnrJm8o7dMVND5KMpVfg-HVVExovcKRnUl64dRI,2897
19
- yirgacheffe/layers/vectors.py,sha256=OxXxnV5pss9vSmeiqvmx68ZGmC7iX6Iet0y4b4ZEMcc,14436
20
- yirgacheffe-1.2.1.dist-info/licenses/LICENSE,sha256=dNSHwUCJr6axStTKDEdnJtfmDdFqlE3h1NPCveqPfnY,757
21
- yirgacheffe-1.2.1.dist-info/METADATA,sha256=bmztQnEJWScy1ig9svcz_vyy6-I8XIvRESPuf5WgEHc,19844
22
- yirgacheffe-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- yirgacheffe-1.2.1.dist-info/entry_points.txt,sha256=j4KgHXbVGbGyfTySc1ypBdERpfihO4WNjppvCdE9HjE,52
24
- yirgacheffe-1.2.1.dist-info/top_level.txt,sha256=9DBFlKO2Ld3hG6TuE3qOTd3Tt8ugTiXil4AN4Wr9_y0,12
25
- yirgacheffe-1.2.1.dist-info/RECORD,,