yirgacheffe 1.2.0__tar.gz → 1.3.0__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 yirgacheffe might be problematic. Click here for more details.

Files changed (54) hide show
  1. {yirgacheffe-1.2.0/yirgacheffe.egg-info → yirgacheffe-1.3.0}/PKG-INFO +22 -1
  2. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/README.md +21 -0
  3. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/pyproject.toml +1 -1
  4. yirgacheffe-1.3.0/tests/test_datatypes.py +67 -0
  5. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_operators.py +44 -16
  6. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_raster.py +3 -2
  7. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_vectors.py +20 -19
  8. yirgacheffe-1.3.0/yirgacheffe/backends/enumeration.py +58 -0
  9. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/backends/mlx.py +59 -1
  10. yirgacheffe-1.3.0/yirgacheffe/backends/numpy.py +152 -0
  11. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/__init__.py +0 -1
  12. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/base.py +2 -2
  13. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/constant.py +3 -4
  14. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/group.py +3 -2
  15. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/h3layer.py +3 -3
  16. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/rasters.py +16 -7
  17. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/rescaled.py +2 -1
  18. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/vectors.py +26 -9
  19. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/operators.py +18 -1
  20. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0/yirgacheffe.egg-info}/PKG-INFO +22 -1
  21. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/SOURCES.txt +1 -0
  22. yirgacheffe-1.2.0/yirgacheffe/backends/enumeration.py +0 -33
  23. yirgacheffe-1.2.0/yirgacheffe/backends/numpy.py +0 -110
  24. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/LICENSE +0 -0
  25. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/MANIFEST.in +0 -0
  26. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/setup.cfg +0 -0
  27. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_area.py +0 -0
  28. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_auto_windowing.py +0 -0
  29. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_base.py +0 -0
  30. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_group.py +0 -0
  31. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_h3layer.py +0 -0
  32. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_intersection.py +0 -0
  33. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_multiband.py +0 -0
  34. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_optimisation.py +0 -0
  35. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_parallel_operators.py +0 -0
  36. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_pickle.py +0 -0
  37. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_rescaling.py +0 -0
  38. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_rounding.py +0 -0
  39. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_save_with_window.py +0 -0
  40. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_sum_with_window.py +0 -0
  41. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_uniform_area_layer.py +0 -0
  42. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_union.py +0 -0
  43. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/tests/test_window.py +0 -0
  44. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/__init__.py +0 -0
  45. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/backends/__init__.py +0 -0
  46. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/constants.py +0 -0
  47. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/h3layer.py +0 -0
  48. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/layers/area.py +0 -0
  49. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/rounding.py +0 -0
  50. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe/window.py +0 -0
  51. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/dependency_links.txt +0 -0
  52. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/entry_points.txt +0 -0
  53. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/requires.txt +0 -0
  54. {yirgacheffe-1.2.0 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yirgacheffe
3
- Version: 1.2.0
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
@@ -295,6 +303,7 @@ with RasterLayer.layer_from_file('test1.tif') as layer1:
295
303
 
296
304
  The following math operators common to numpy and other libraries are currently supported:
297
305
 
306
+ * abs
298
307
  * clip
299
308
  * exp
300
309
  * exp2
@@ -338,6 +347,18 @@ with RasterLayer.layer_from_file('original.tif') as layer1:
338
347
  calc.save(result)
339
348
  ```
340
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
+
341
362
  ### Apply
342
363
 
343
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.
@@ -11,6 +11,14 @@ Example common use-cases:
11
11
  * 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.
12
12
 
13
13
 
14
+ ## Installation
15
+
16
+ Yirgacheffe is available via pypi, so can be installed with pip for example:
17
+
18
+ ```SystemShell
19
+ $ pip install yirgacheffe
20
+ ```
21
+
14
22
  ## Basic usage
15
23
 
16
24
  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
@@ -271,6 +279,7 @@ with RasterLayer.layer_from_file('test1.tif') as layer1:
271
279
 
272
280
  The following math operators common to numpy and other libraries are currently supported:
273
281
 
282
+ * abs
274
283
  * clip
275
284
  * exp
276
285
  * exp2
@@ -314,6 +323,18 @@ with RasterLayer.layer_from_file('original.tif') as layer1:
314
323
  calc.save(result)
315
324
  ```
316
325
 
326
+ ### Type conversion
327
+
328
+ 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:
329
+
330
+ ```python
331
+ from yirgacheffe.operations import DataType
332
+
333
+
334
+ with RasterLayer.layer_from_file('float_data.tif') as float_layer:
335
+ int_layer = float_layer.astype(DataType.Int32)
336
+ ```
337
+
317
338
  ### Apply
318
339
 
319
340
  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.
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "yirgacheffe"
9
- version = "1.2.0"
9
+ version = "1.3.0"
10
10
  description = "Abstraction of gdal datasets for doing basic math operations"
11
11
  readme = "README.md"
12
12
  authors = [{ name = "Michael Dales", email = "mwd24@cam.ac.uk" }]
@@ -0,0 +1,67 @@
1
+
2
+ import numpy as np
3
+ import pytest
4
+ from osgeo import gdal
5
+
6
+ from yirgacheffe.operators import DataType
7
+ from yirgacheffe.backends import backend, BACKEND
8
+ from yirgacheffe.layers import RasterLayer
9
+
10
+ from helpers import gdal_dataset_with_data
11
+
12
+ @pytest.mark.parametrize("gtype", [
13
+ gdal.GDT_Int8,
14
+ gdal.GDT_Int16,
15
+ gdal.GDT_Int32,
16
+ gdal.GDT_Int64,
17
+ gdal.GDT_Byte,
18
+ gdal.GDT_UInt16,
19
+ gdal.GDT_UInt32,
20
+ gdal.GDT_UInt64,
21
+ gdal.GDT_Float32,
22
+ ])
23
+ def test_round_trip(gtype) -> None:
24
+ ytype = DataType.of_gdal(gtype)
25
+ backend_type = backend.dtype_to_backed(ytype)
26
+ assert backend.backend_to_dtype(backend_type) == ytype
27
+
28
+ @pytest.mark.parametrize("ytype", [
29
+ DataType.Int8,
30
+ DataType.Int16,
31
+ DataType.Int32,
32
+ DataType.Int64,
33
+ DataType.UInt8,
34
+ DataType.UInt16,
35
+ DataType.UInt32,
36
+ DataType.UInt64,
37
+ DataType.Float32,
38
+ DataType.Float64,
39
+ ])
40
+ def test_round_trip_from_gdal(ytype) -> None:
41
+ gtype = ytype.to_gdal()
42
+ assert DataType.of_gdal(gtype) == ytype
43
+
44
+ def test_round_trip_float64() -> None:
45
+ backend_type = backend.dtype_to_backed(DataType.Float64)
46
+ ytype = backend.backend_to_dtype(backend_type)
47
+ print(BACKEND, "sad")
48
+ match BACKEND:
49
+ case "NUMPY":
50
+ assert ytype == DataType.Float64
51
+ case "MLX":
52
+ assert ytype == DataType.Float32
53
+
54
+ def test_float_to_int() -> None:
55
+ data1 = np.array([[1.0, 2.0, 3.0, 4.0], [5.5, 6.5, 7.5, 8.5]])
56
+
57
+ layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
58
+ # Note the float 32 here is to rule out that writing the result to the
59
+ # new dataset was what caused the truncation
60
+ result = RasterLayer.empty_raster_layer_like(layer1, datatype=DataType.Float32)
61
+
62
+ comp = layer1.astype(DataType.UInt8)
63
+ comp.save(result)
64
+
65
+ expected = backend.promote(np.array([[1, 2, 3, 4], [5, 6, 7, 8]]))
66
+ actual = backend.demote_array(result.read_array(0, 0, 4, 2))
67
+ assert (expected == actual).all()
@@ -10,7 +10,7 @@ from osgeo import gdal
10
10
  from helpers import gdal_dataset_with_data
11
11
  import yirgacheffe
12
12
  from yirgacheffe.layers import RasterLayer, ConstantLayer
13
- from yirgacheffe.operators import LayerOperation
13
+ from yirgacheffe.operators import LayerOperation, DataType
14
14
  from yirgacheffe.backends import backend
15
15
 
16
16
  def test_add_byte_layers() -> None:
@@ -21,8 +21,8 @@ def test_add_byte_layers() -> None:
21
21
  layer2 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data2))
22
22
  result = RasterLayer.empty_raster_layer_like(layer1)
23
23
 
24
- assert layer1.datatype == gdal.GDT_Byte
25
- assert layer2.datatype == gdal.GDT_Byte
24
+ assert layer1.datatype == DataType.Byte
25
+ assert layer2.datatype == DataType.Byte
26
26
 
27
27
  comp = layer1 + layer2
28
28
  comp.save(result)
@@ -64,8 +64,8 @@ def test_add_byte_layers_with_callback(skip, expected_steps) -> None:
64
64
  layer2 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data2))
65
65
  result = RasterLayer.empty_raster_layer_like(layer1)
66
66
 
67
- assert layer1.datatype == gdal.GDT_Byte
68
- assert layer2.datatype == gdal.GDT_Byte
67
+ assert layer1.datatype == DataType.Byte
68
+ assert layer2.datatype == DataType.Byte
69
69
 
70
70
  callback_possitions = []
71
71
 
@@ -632,7 +632,7 @@ def test_save_and_sum_float32(monkeypatch) -> None:
632
632
 
633
633
  data1 = np.array(data, dtype=np.float32)
634
634
  layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
635
- assert layer1.datatype == gdal.GDT_Float32
635
+ assert layer1.datatype == DataType.Float32
636
636
 
637
637
  # Sum forces things to float64
638
638
  expected = np.sum(data1.astype(np.float64))
@@ -660,7 +660,7 @@ def test_parallel_save_and_sum_float32(monkeypatch) -> None:
660
660
  dataset1 = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=path1)
661
661
  dataset1.Close()
662
662
  layer1 = RasterLayer.layer_from_file(path1)
663
- assert layer1.datatype == gdal.GDT_Float32
663
+ assert layer1.datatype == DataType.Float32
664
664
 
665
665
  # Sum forces things to float64
666
666
  expected = np.sum(data1.astype(np.float64))
@@ -684,7 +684,7 @@ def test_sum_float32(monkeypatch) -> None:
684
684
 
685
685
  data1 = np.array(data, dtype=np.float32)
686
686
  layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
687
- assert layer1.datatype == gdal.GDT_Float32
687
+ assert layer1.datatype == DataType.Float32
688
688
 
689
689
  # Sum forces things to float64
690
690
  expected = np.sum(data1.astype(np.float64))
@@ -703,8 +703,8 @@ def test_and_byte_layers() -> None:
703
703
  layer2 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data2))
704
704
  result = RasterLayer.empty_raster_layer_like(layer1)
705
705
 
706
- assert layer1.datatype == gdal.GDT_Byte
707
- assert layer2.datatype == gdal.GDT_Byte
706
+ assert layer1.datatype == DataType.Byte
707
+ assert layer2.datatype == DataType.Byte
708
708
 
709
709
  comp = layer1 & layer2
710
710
  comp.save(result)
@@ -722,8 +722,8 @@ def test_or_byte_layers() -> None:
722
722
  layer2 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data2))
723
723
  result = RasterLayer.empty_raster_layer_like(layer1)
724
724
 
725
- assert layer1.datatype == gdal.GDT_Byte
726
- assert layer2.datatype == gdal.GDT_Byte
725
+ assert layer1.datatype == DataType.Byte
726
+ assert layer2.datatype == DataType.Byte
727
727
 
728
728
  comp = layer1 | layer2
729
729
  comp.save(result)
@@ -741,8 +741,8 @@ def test_and_int_layers() -> None:
741
741
  layer2 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data2))
742
742
  result = RasterLayer.empty_raster_layer_like(layer1)
743
743
 
744
- assert layer1.datatype == gdal.GDT_Int16
745
- assert layer2.datatype == gdal.GDT_Int16
744
+ assert layer1.datatype == DataType.Int16
745
+ assert layer2.datatype == DataType.Int16
746
746
 
747
747
  comp = layer1 & layer2
748
748
  comp.save(result)
@@ -760,8 +760,8 @@ def test_or_int_layers() -> None:
760
760
  layer2 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data2))
761
761
  result = RasterLayer.empty_raster_layer_like(layer1)
762
762
 
763
- assert layer1.datatype == gdal.GDT_Int16
764
- assert layer2.datatype == gdal.GDT_Int16
763
+ assert layer1.datatype == DataType.Int16
764
+ assert layer2.datatype == DataType.Int16
765
765
 
766
766
  comp = layer1 | layer2
767
767
  comp.save(result)
@@ -1197,6 +1197,34 @@ def test_clip_lower_module() -> None:
1197
1197
  actual = result.read_array(0, 0, 4, 2)
1198
1198
  assert (expected == actual).all()
1199
1199
 
1200
+ def test_abs_method() -> None:
1201
+ data1 = np.array([[1.0, 2.0, 3.0, 4.0], [-1.0, -2.0, -3.0, -4.0]])
1202
+ layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
1203
+ result = RasterLayer.empty_raster_layer_like(layer1)
1204
+
1205
+ comp = layer1.abs()
1206
+ comp.save(result)
1207
+
1208
+ expected = backend.abs_op(backend.promote(data1))
1209
+ backend.eval_op(expected)
1210
+
1211
+ actual = result.read_array(0, 0, 4, 2)
1212
+ assert (expected == actual).all()
1213
+
1214
+ def test_abs_module() -> None:
1215
+ data1 = np.array([[1.0, 2.0, 3.0, 4.0], [-1.0, -2.0, -3.0, -4.0]])
1216
+ layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
1217
+ result = RasterLayer.empty_raster_layer_like(layer1)
1218
+
1219
+ comp = LayerOperation.abs(layer1)
1220
+ comp.save(result)
1221
+
1222
+ expected = backend.abs_op(backend.promote(data1))
1223
+ backend.eval_op(expected)
1224
+
1225
+ actual = result.read_array(0, 0, 4, 2)
1226
+ assert (expected == actual).all()
1227
+
1200
1228
  @pytest.mark.parametrize("skip", [
1201
1229
  1,
1202
1230
  2,
@@ -9,6 +9,7 @@ from helpers import gdal_dataset_of_region, gdal_multiband_dataset_with_data
9
9
  from yirgacheffe.window import Area, PixelScale, Window
10
10
  from yirgacheffe.layers import RasterLayer, InvalidRasterBand
11
11
  from yirgacheffe.rounding import round_up_pixels
12
+ from yirgacheffe.operators import DataType
12
13
 
13
14
 
14
15
  # There is a lot of "del" in this file, due to a combination of gdal having no way
@@ -128,12 +129,12 @@ def test_empty_layer_from_raster_with_new_smaller_area():
128
129
 
129
130
  def test_empty_layer_from_raster_new_datatype():
130
131
  source = RasterLayer(gdal_dataset_of_region(Area(-10, 10, 10, -10), 0.02))
131
- assert source.datatype == gdal.GDT_Byte
132
+ assert source.datatype == DataType.Byte
132
133
  empty = RasterLayer.empty_raster_layer_like(source, datatype=gdal.GDT_Float64)
133
134
  assert empty.pixel_scale == source.pixel_scale
134
135
  assert empty.projection == source.projection
135
136
  assert empty.window == source.window
136
- assert empty.datatype == gdal.GDT_Float64
137
+ assert empty.datatype == DataType.Float64
137
138
 
138
139
  def test_empty_layer_from_raster_with_window():
139
140
  source = RasterLayer(gdal_dataset_of_region(Area(-10, 10, 10, -10), 0.02))
@@ -8,6 +8,7 @@ from helpers import make_vectors_with_mutlile_ids, make_vectors_with_id, make_ve
8
8
  from yirgacheffe import WGS_84_PROJECTION
9
9
  from yirgacheffe.layers import RasterLayer, RasteredVectorLayer, VectorLayer, VectorRangeLayer, DynamicVectorRangeLayer
10
10
  from yirgacheffe.window import Area, PixelScale, Window
11
+ from yirgacheffe.operators import DataType
11
12
 
12
13
  def test_basic_vector_layer_no_filter_match() -> None:
13
14
  with tempfile.TemporaryDirectory() as tempdir:
@@ -139,7 +140,7 @@ def test_vector_layers_with_default_burn_value(klass) -> None:
139
140
  assert layer.area == Area(-10.0, 10.0, 10.0, -10.0)
140
141
  assert layer.geo_transform == (-10.0, 1.0, 0.0, 10.0, 0.0, -1.0)
141
142
  assert layer.window == Window(0, 0, 20, 20)
142
- assert layer.datatype == gdal.GDT_Byte
143
+ assert layer.datatype == DataType.Byte
143
144
 
144
145
  # The default burn value is 1, so check that if we sum the area
145
146
  # we get half and half
@@ -256,13 +257,13 @@ def test_vector_layers_with_field_value(klass) -> None:
256
257
  @pytest.mark.parametrize(
257
258
  "value,expected",
258
259
  [
259
- (1, gdal.GDT_Byte),
260
- (42, gdal.GDT_Byte),
261
- (-1, gdal.GDT_Int16),
262
- (1024, gdal.GDT_UInt16),
263
- (1024*1024, gdal.GDT_UInt32),
264
- (-1024*1024, gdal.GDT_Int32),
265
- (1.0, gdal.GDT_Float64),
260
+ (1, DataType.Byte),
261
+ (42, DataType.Byte),
262
+ (-1, DataType.Int16),
263
+ (1024, DataType.UInt16),
264
+ (1024*1024, DataType.UInt32),
265
+ (-1024*1024, DataType.Int32),
266
+ (1.0, DataType.Float64),
266
267
  ]
267
268
  )
268
269
  def test_vector_layers_with_guessed_type_burn_value(value, expected) -> None:
@@ -294,14 +295,14 @@ def test_vector_layers_with_guessed_type_burn_value(value, expected) -> None:
294
295
  @pytest.mark.parametrize(
295
296
  "value,datatype",
296
297
  [
297
- (1, gdal.GDT_Byte),
298
- (42, gdal.GDT_Byte),
299
- (1, gdal.GDT_Int16),
300
- (42, gdal.GDT_Int16),
301
- (1024, gdal.GDT_Int16),
302
- (1.0, gdal.GDT_Float32),
303
- (0.5, gdal.GDT_Float32),
304
- (1.0, gdal.GDT_Float64),
298
+ (1, DataType.Byte),
299
+ (42, DataType.Byte),
300
+ (1, DataType.Int16),
301
+ (42, DataType.Int16),
302
+ (1024, DataType.Int16),
303
+ (1.0, DataType.Float32),
304
+ (0.5, DataType.Float32),
305
+ (1.0, DataType.Float64),
305
306
  ]
306
307
  )
307
308
  def test_vector_layers_with_different_type_burn_value(value, datatype) -> None:
@@ -334,8 +335,8 @@ def test_vector_layers_with_different_type_burn_value(value, datatype) -> None:
334
335
  @pytest.mark.parametrize(
335
336
  "value,expected",
336
337
  [
337
- (1, gdal.GDT_Int64),
338
- (1.0, gdal.GDT_Float64),
338
+ (1, DataType.Int64),
339
+ (1.0, DataType.Float64),
339
340
  ]
340
341
  )
341
342
  def test_vector_layers_with_guess_field_type_burn_value(value, expected) -> None:
@@ -448,7 +449,7 @@ def test_vector_layers_with_empty_features(klass) -> None:
448
449
  assert layer.area == Area(-10.0, 10.0, 10.0, -10.0)
449
450
  assert layer.geo_transform == (-10.0, 1.0, 0.0, 10.0, 0.0, -1.0)
450
451
  assert layer.window == Window(0, 0, 20, 20)
451
- assert layer.datatype == gdal.GDT_Byte
452
+ assert layer.datatype == DataType.Byte
452
453
 
453
454
  # The default burn value is 1, so check that if we sum the area
454
455
  # we get half and half
@@ -0,0 +1,58 @@
1
+ from enum import Enum
2
+
3
+ from osgeo import gdal
4
+
5
+ class operators(Enum):
6
+ ADD = 1
7
+ SUB = 2
8
+ MUL = 3
9
+ TRUEDIV = 4
10
+ POW = 5
11
+ EQ = 6
12
+ NE = 7
13
+ LT = 8
14
+ LE = 9
15
+ GT = 10
16
+ GE = 11
17
+ AND = 12
18
+ OR = 13
19
+ LOG = 14
20
+ LOG2 = 15
21
+ LOG10 = 16
22
+ EXP = 17
23
+ EXP2 = 18
24
+ CLIP = 19
25
+ WHERE = 20
26
+ MIN = 21
27
+ MAX = 22
28
+ SUM = 23
29
+ MINIMUM = 24
30
+ MAXIMUM = 25
31
+ NAN_TO_NUM = 26
32
+ ISIN = 27
33
+ REMAINDER = 28
34
+ FLOORDIV = 29
35
+ CONV2D = 30
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
@@ -43,6 +44,7 @@ full = mx.full
43
44
  allclose = mx.allclose
44
45
  remainder_op = mx.remainder
45
46
  floordiv_op = mx.array.__floordiv__
47
+ abs_op = mx.abs
46
48
 
47
49
  def sum_op(a):
48
50
  # There are weird issues around how MLX overflows int8, so just promote the data ahead of summing
@@ -120,9 +122,63 @@ def conv2d_op(data, weights):
120
122
  preped_data = mx.array(np.reshape(unshifted_data_shape, conv_data_shape))
121
123
 
122
124
  shifted_res = conv(preped_data)[0]
123
- res = np.reshape(shifted_res, [1] + list(shifted_res.shape)[:-1])
125
+ res = mx.reshape(shifted_res, [1] + list(shifted_res.shape)[:-1])
124
126
  return res[0]
125
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
+
126
182
  operator_map = {
127
183
  op.ADD: mx.array.__add__,
128
184
  op.SUB: mx.array.__sub__,
@@ -153,4 +209,6 @@ operator_map = {
153
209
  op.REMAINDER: mx.remainder,
154
210
  op.FLOORDIV: mx.array.__floordiv__,
155
211
  op.CONV2D: conv2d_op,
212
+ op.ABS: mx.abs,
213
+ op.ASTYPE: astype_op,
156
214
  }