yirgacheffe 1.2.1__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.
- {yirgacheffe-1.2.1/yirgacheffe.egg-info → yirgacheffe-1.3.0}/PKG-INFO +21 -1
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/README.md +20 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/pyproject.toml +1 -1
- yirgacheffe-1.3.0/tests/test_datatypes.py +67 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_operators.py +16 -16
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_raster.py +3 -2
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_vectors.py +20 -19
- yirgacheffe-1.3.0/yirgacheffe/backends/enumeration.py +58 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/backends/mlx.py +57 -1
- yirgacheffe-1.3.0/yirgacheffe/backends/numpy.py +152 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/__init__.py +0 -1
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/base.py +2 -2
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/constant.py +3 -4
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/group.py +3 -2
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/h3layer.py +3 -3
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/rasters.py +16 -7
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/rescaled.py +2 -1
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/vectors.py +26 -9
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/operators.py +10 -1
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0/yirgacheffe.egg-info}/PKG-INFO +21 -1
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/SOURCES.txt +1 -0
- yirgacheffe-1.2.1/yirgacheffe/backends/enumeration.py +0 -34
- yirgacheffe-1.2.1/yirgacheffe/backends/numpy.py +0 -112
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/LICENSE +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/MANIFEST.in +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/setup.cfg +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_area.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_auto_windowing.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_base.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_group.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_h3layer.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_intersection.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_multiband.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_optimisation.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_parallel_operators.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_pickle.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_rescaling.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_rounding.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_save_with_window.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_sum_with_window.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_uniform_area_layer.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_union.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/tests/test_window.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/__init__.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/backends/__init__.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/constants.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/h3layer.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/layers/area.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/rounding.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe/window.py +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/dependency_links.txt +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/entry_points.txt +0 -0
- {yirgacheffe-1.2.1 → yirgacheffe-1.3.0}/yirgacheffe.egg-info/requires.txt +0 -0
- {yirgacheffe-1.2.1 → 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.
|
|
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.
|
|
@@ -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
|
|
@@ -315,6 +323,18 @@ with RasterLayer.layer_from_file('original.tif') as layer1:
|
|
|
315
323
|
calc.save(result)
|
|
316
324
|
```
|
|
317
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
|
+
|
|
318
338
|
### Apply
|
|
319
339
|
|
|
320
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.
|
|
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 ==
|
|
25
|
-
assert layer2.datatype ==
|
|
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 ==
|
|
68
|
-
assert layer2.datatype ==
|
|
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 ==
|
|
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 ==
|
|
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 ==
|
|
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 ==
|
|
707
|
-
assert layer2.datatype ==
|
|
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 ==
|
|
726
|
-
assert layer2.datatype ==
|
|
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 ==
|
|
745
|
-
assert layer2.datatype ==
|
|
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 ==
|
|
764
|
-
assert layer2.datatype ==
|
|
763
|
+
assert layer1.datatype == DataType.Int16
|
|
764
|
+
assert layer2.datatype == DataType.Int16
|
|
765
765
|
|
|
766
766
|
comp = layer1 | layer2
|
|
767
767
|
comp.save(result)
|
|
@@ -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 ==
|
|
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 ==
|
|
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 ==
|
|
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,
|
|
260
|
-
(42,
|
|
261
|
-
(-1,
|
|
262
|
-
(1024,
|
|
263
|
-
(1024*1024,
|
|
264
|
-
(-1024*1024,
|
|
265
|
-
(1.0,
|
|
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,
|
|
298
|
-
(42,
|
|
299
|
-
(1,
|
|
300
|
-
(42,
|
|
301
|
-
(1024,
|
|
302
|
-
(1.0,
|
|
303
|
-
(0.5,
|
|
304
|
-
(1.0,
|
|
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,
|
|
338
|
-
(1.0,
|
|
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 ==
|
|
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
|
|
@@ -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 =
|
|
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
|
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
|
|
2
|
+
import numpy as np
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
from .enumeration import operators as op
|
|
6
|
+
from .enumeration import dtype
|
|
7
|
+
|
|
8
|
+
array_t = np.ndarray
|
|
9
|
+
float_t = np.float64
|
|
10
|
+
|
|
11
|
+
promote = lambda a: a
|
|
12
|
+
demote_array = lambda a: a
|
|
13
|
+
demote_scalar = lambda a: a
|
|
14
|
+
eval_op = lambda a: a
|
|
15
|
+
|
|
16
|
+
add_op = np.ndarray.__add__
|
|
17
|
+
sub_op = np.ndarray.__sub__
|
|
18
|
+
mul_op = np.ndarray.__mul__
|
|
19
|
+
truediv_op = np.ndarray.__truediv__
|
|
20
|
+
pow_op = np.ndarray.__pow__
|
|
21
|
+
eq_op = np.ndarray.__eq__
|
|
22
|
+
ne_op = np.ndarray.__ne__
|
|
23
|
+
lt_op = np.ndarray.__lt__
|
|
24
|
+
le_op = np.ndarray.__le__
|
|
25
|
+
gt_op = np.ndarray.__gt__
|
|
26
|
+
ge_op = np.ndarray.__ge__
|
|
27
|
+
and_op = np.ndarray.__and__
|
|
28
|
+
or_op = np.ndarray.__or__
|
|
29
|
+
nan_to_num = np.nan_to_num
|
|
30
|
+
isin = np.isin
|
|
31
|
+
log = np.log
|
|
32
|
+
log2 = np.log2
|
|
33
|
+
log10 = np.log10
|
|
34
|
+
exp = np.exp
|
|
35
|
+
exp2 = np.exp2
|
|
36
|
+
clip = np.clip
|
|
37
|
+
where = np.where
|
|
38
|
+
min_op = np.min
|
|
39
|
+
max_op = np.max
|
|
40
|
+
maximum = np.maximum
|
|
41
|
+
minimum = np.minimum
|
|
42
|
+
zeros = np.zeros
|
|
43
|
+
pad = np.pad
|
|
44
|
+
sum_op = lambda a: np.sum(a.astype(np.float64))
|
|
45
|
+
isscalar = np.isscalar
|
|
46
|
+
full = np.full
|
|
47
|
+
allclose = np.allclose
|
|
48
|
+
remainder_op = np.ndarray.__mod__
|
|
49
|
+
floordiv_op = np.ndarray.__floordiv__
|
|
50
|
+
abs_op = np.abs
|
|
51
|
+
|
|
52
|
+
def conv2d_op(data, weights):
|
|
53
|
+
# torch wants to process dimensions of channels of width of height
|
|
54
|
+
# Which is why both the data and weights get nested into two arrays here,
|
|
55
|
+
# and then we have to unpack it from that nesting.
|
|
56
|
+
|
|
57
|
+
preped_weights = np.array([[weights]])
|
|
58
|
+
conv = torch.nn.Conv2d(1, 1, weights.shape, bias=False)
|
|
59
|
+
conv.weight = torch.nn.Parameter(torch.from_numpy(preped_weights))
|
|
60
|
+
preped_data = torch.from_numpy(np.array([[data]]))
|
|
61
|
+
|
|
62
|
+
res = conv(preped_data)
|
|
63
|
+
return res.detach().numpy()[0][0]
|
|
64
|
+
|
|
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
|
|
116
|
+
|
|
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,
|
|
152
|
+
}
|
|
@@ -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) ->
|
|
38
|
+
def datatype(self) -> DataType:
|
|
39
39
|
raise NotImplementedError("Must be overridden by subclass")
|
|
40
40
|
|
|
41
41
|
@property
|