yirgacheffe 1.9.5__tar.gz → 1.10.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.9.5/yirgacheffe.egg-info → yirgacheffe-1.10.0}/PKG-INFO +2 -1
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/pyproject.toml +2 -1
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_auto_windowing.py +5 -5
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_constants.py +21 -2
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_openers.py +2 -2
- yirgacheffe-1.10.0/tests/test_operator_hashing.py +372 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_operators.py +126 -17
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_pixel_coord.py +29 -26
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_raster.py +33 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_vectors.py +9 -9
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_window.py +10 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/_backends/enumeration.py +7 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/_backends/mlx.py +7 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/_backends/numpy.py +11 -0
- yirgacheffe-1.9.5/yirgacheffe/_operators.py → yirgacheffe-1.10.0/yirgacheffe/_operators/__init__.py +257 -78
- yirgacheffe-1.10.0/yirgacheffe/_operators/cse.py +66 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/base.py +6 -1
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/constant.py +4 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/group.py +4 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/h3layer.py +9 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/rasters.py +12 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/rescaled.py +11 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/vectors.py +13 -2
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/window.py +6 -3
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0/yirgacheffe.egg-info}/PKG-INFO +2 -1
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe.egg-info/SOURCES.txt +3 -1
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe.egg-info/requires.txt +1 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/LICENSE +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/MANIFEST.in +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/README.md +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/setup.cfg +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_area.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_datatypes.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_group.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_h3layer.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_intersection.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_multiband.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_nodata.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_optimisation.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_parallel_operators.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_pickle.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_projection.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_reduce.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_rescaling.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_rounding.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_save_with_window.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_sum_with_window.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_uniform_area_layer.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/tests/test_union.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/__init__.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/_backends/__init__.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/_core.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/constants.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/__init__.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/layers/area.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/operators.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/py.typed +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe/rounding.py +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe.egg-info/dependency_links.txt +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.0}/yirgacheffe.egg-info/entry_points.txt +0 -0
- {yirgacheffe-1.9.5 → yirgacheffe-1.10.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.10.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
|
|
@@ -37,6 +37,7 @@ Requires-Dist: mypy; extra == "dev"
|
|
|
37
37
|
Requires-Dist: pylint; extra == "dev"
|
|
38
38
|
Requires-Dist: pytest; extra == "dev"
|
|
39
39
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
40
41
|
Requires-Dist: build; extra == "dev"
|
|
41
42
|
Requires-Dist: twine; extra == "dev"
|
|
42
43
|
Requires-Dist: mkdocs-material; extra == "dev"
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "yirgacheffe"
|
|
9
|
-
version = "1.
|
|
9
|
+
version = "1.10.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" }]
|
|
@@ -46,6 +46,7 @@ dev = [
|
|
|
46
46
|
"pylint",
|
|
47
47
|
"pytest",
|
|
48
48
|
"pytest-cov",
|
|
49
|
+
"pytest-mock",
|
|
49
50
|
"build",
|
|
50
51
|
"twine",
|
|
51
52
|
"mkdocs-material",
|
|
@@ -5,7 +5,7 @@ import numpy as np
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
7
|
import yirgacheffe as yg
|
|
8
|
-
from tests.helpers import gdal_dataset_with_data,
|
|
8
|
+
from tests.helpers import gdal_dataset_with_data, make_vectors_with_multiple_ids
|
|
9
9
|
from yirgacheffe.layers import ConstantLayer, RasterLayer, VectorLayer
|
|
10
10
|
from yirgacheffe.window import Area
|
|
11
11
|
|
|
@@ -208,7 +208,7 @@ def test_vector_layers_add() -> None:
|
|
|
208
208
|
(Area(-10.0, 10.0, 0.0, 0.0), 42),
|
|
209
209
|
(Area(0.0, 0.0, 10, -10), 43)
|
|
210
210
|
}
|
|
211
|
-
|
|
211
|
+
make_vectors_with_multiple_ids(areas, path)
|
|
212
212
|
|
|
213
213
|
burn_value = 2
|
|
214
214
|
with VectorLayer.layer_from_file(
|
|
@@ -242,7 +242,7 @@ def test_vector_layers_add_unbound_rhs() -> None:
|
|
|
242
242
|
(Area(-10.0, 10.0, 0.0, 0.0), 42),
|
|
243
243
|
(Area(0.0, 0.0, 10, -10), 43)
|
|
244
244
|
}
|
|
245
|
-
|
|
245
|
+
make_vectors_with_multiple_ids(areas, path)
|
|
246
246
|
|
|
247
247
|
burn_value = 2
|
|
248
248
|
with VectorLayer.layer_from_file(path, None, None, None, burn_value=burn_value) as vector_layer:
|
|
@@ -269,7 +269,7 @@ def test_vector_layers_add_unbound_lhs() -> None:
|
|
|
269
269
|
(Area(-10.0, 10.0, 0.0, 0.0), 42),
|
|
270
270
|
(Area(0.0, 0.0, 10, -10), 43)
|
|
271
271
|
}
|
|
272
|
-
|
|
272
|
+
make_vectors_with_multiple_ids(areas, path)
|
|
273
273
|
|
|
274
274
|
burn_value = 2
|
|
275
275
|
with VectorLayer.layer_from_file(path, None, None, None, burn_value=burn_value) as vector_layer:
|
|
@@ -297,7 +297,7 @@ def test_vector_layers_multiply() -> None:
|
|
|
297
297
|
(Area(-10.0, 10.0, 0.0, 0.0), 42),
|
|
298
298
|
(Area(0.0, 0.0, 10, -10), 43)
|
|
299
299
|
}
|
|
300
|
-
|
|
300
|
+
make_vectors_with_multiple_ids(areas, path)
|
|
301
301
|
|
|
302
302
|
burn_value = 2
|
|
303
303
|
layer2 = VectorLayer.layer_from_file(path, None, layer1.pixel_scale, layer1.projection, burn_value=burn_value)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
import numpy as np
|
|
3
|
-
import
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
import yirgacheffe as yg
|
|
4
6
|
from yirgacheffe.layers import RasterLayer, ConstantLayer
|
|
5
7
|
from yirgacheffe.operators import DataType
|
|
6
8
|
from yirgacheffe.window import Area, PixelScale
|
|
@@ -23,10 +25,27 @@ def test_constant_parallel_save(monkeypatch) -> None:
|
|
|
23
25
|
with RasterLayer.empty_raster_layer(area, scale, DataType.Float32) as result:
|
|
24
26
|
with ConstantLayer(42.0) as c:
|
|
25
27
|
with monkeypatch.context() as m:
|
|
26
|
-
m.setattr(
|
|
28
|
+
m.setattr(yg.constants, "YSTEP", 1)
|
|
27
29
|
c.parallel_save(result)
|
|
28
30
|
|
|
29
31
|
expected = np.full((20, 20), 42.0)
|
|
30
32
|
actual = result.read_array(0, 0, 20, 20)
|
|
31
33
|
|
|
32
34
|
assert (expected == actual).all()
|
|
35
|
+
|
|
36
|
+
@pytest.mark.parametrize("lhs,rhs,expected_equal", [
|
|
37
|
+
(1, 2, False),
|
|
38
|
+
(1, 1, True),
|
|
39
|
+
(1.0, 2.0, False),
|
|
40
|
+
(1.0, 1.0, True),
|
|
41
|
+
(1, 1.0, True), # This is Python standard behaviour
|
|
42
|
+
])
|
|
43
|
+
def test_cse_hash(lhs,rhs,expected_equal) -> None:
|
|
44
|
+
a = yg.constant(lhs)
|
|
45
|
+
b = yg.constant(rhs)
|
|
46
|
+
|
|
47
|
+
assert a is not b
|
|
48
|
+
assert a.name != b.name
|
|
49
|
+
|
|
50
|
+
are_hashed_same = a._cse_hash == b._cse_hash
|
|
51
|
+
assert expected_equal == are_hashed_same
|
|
@@ -12,7 +12,7 @@ from yirgacheffe.layers import InvalidRasterBand, RasterLayer
|
|
|
12
12
|
from yirgacheffe.window import Area, MapProjection, Window
|
|
13
13
|
from yirgacheffe.operators import DataType
|
|
14
14
|
from tests.helpers import gdal_dataset_of_region, gdal_multiband_dataset_with_data, \
|
|
15
|
-
make_vectors_with_id,
|
|
15
|
+
make_vectors_with_id, make_vectors_with_multiple_ids
|
|
16
16
|
|
|
17
17
|
def test_raster_from_nonexistent_file() -> None:
|
|
18
18
|
with pytest.raises(FileNotFoundError):
|
|
@@ -136,7 +136,7 @@ def test_open_gpkg_with_filter() -> None:
|
|
|
136
136
|
(Area(-10.0, 10.0, 0.0, 0.0), 42),
|
|
137
137
|
(Area(0.0, 0.0, 10, -10), 43)
|
|
138
138
|
}
|
|
139
|
-
|
|
139
|
+
make_vectors_with_multiple_ids(areas, path)
|
|
140
140
|
|
|
141
141
|
with yg.read_shape(path, (WGS_84_PROJECTION, (1.0, -1.0)), "id_no=42") as layer:
|
|
142
142
|
assert layer.area == Area(-10.0, 10.0, 0.0, 0.0)
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from tests.helpers import make_vectors_with_id, make_vectors_with_multiple_ids, gdal_dataset_of_region, \
|
|
8
|
+
gdal_multiband_dataset_with_data
|
|
9
|
+
import yirgacheffe as yg
|
|
10
|
+
from yirgacheffe.layers import H3CellLayer
|
|
11
|
+
from yirgacheffe._operators.cse import CSECacheTable
|
|
12
|
+
from yirgacheffe._backends import backend
|
|
13
|
+
|
|
14
|
+
def test_simple_constant_expression() -> None:
|
|
15
|
+
with (
|
|
16
|
+
yg.constant(1) as lhs,
|
|
17
|
+
yg.constant(2) as rhs,
|
|
18
|
+
):
|
|
19
|
+
calc0 = lhs + rhs
|
|
20
|
+
calc1 = lhs + rhs
|
|
21
|
+
calc2 = rhs + lhs
|
|
22
|
+
|
|
23
|
+
# One day this test should fail when we take commutative operators into account
|
|
24
|
+
assert calc0 is not calc1
|
|
25
|
+
assert calc0._cse_hash == calc1._cse_hash
|
|
26
|
+
assert calc1._cse_hash != calc2._cse_hash
|
|
27
|
+
|
|
28
|
+
def test_simple_raster_expression() -> None:
|
|
29
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
30
|
+
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
31
|
+
|
|
32
|
+
with(
|
|
33
|
+
yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer1,
|
|
34
|
+
yg.from_array(data2, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer2,
|
|
35
|
+
):
|
|
36
|
+
calc0 = layer1 + layer2
|
|
37
|
+
calc1 = layer1 + layer2
|
|
38
|
+
calc2 = layer1 + layer1
|
|
39
|
+
|
|
40
|
+
assert calc0 is not calc1
|
|
41
|
+
assert calc0._cse_hash == calc1._cse_hash
|
|
42
|
+
assert calc1._cse_hash != calc2._cse_hash
|
|
43
|
+
|
|
44
|
+
def test_raster_different_datatype() -> None:
|
|
45
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
46
|
+
|
|
47
|
+
with(
|
|
48
|
+
yg.from_array(data1.astype(np.int16), (0, 0), ("epsg:4326", (1.0, -1.0))) as layer1,
|
|
49
|
+
yg.from_array(data1.astype(np.float32), (0, 0), ("epsg:4326", (1.0, -1.0))) as layer2,
|
|
50
|
+
):
|
|
51
|
+
assert layer1.datatype == yg.DataType.Int16
|
|
52
|
+
assert layer2.datatype == yg.DataType.Float32
|
|
53
|
+
|
|
54
|
+
assert layer1._cse_hash is not None
|
|
55
|
+
assert layer2._cse_hash is not None
|
|
56
|
+
assert layer1._cse_hash != layer2._cse_hash
|
|
57
|
+
|
|
58
|
+
def test_raster_ignore_nodata() -> None:
|
|
59
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
60
|
+
path = Path(tempdir) / "test.tif"
|
|
61
|
+
area = yg.Area(-10, 10, 10, -10)
|
|
62
|
+
_ = gdal_dataset_of_region(area, 0.02, filename=path)
|
|
63
|
+
|
|
64
|
+
with (
|
|
65
|
+
yg.read_raster(path, ignore_nodata=True) as layer1,
|
|
66
|
+
yg.read_raster(path, ignore_nodata=False) as layer2,
|
|
67
|
+
):
|
|
68
|
+
assert layer1._cse_hash is not None
|
|
69
|
+
assert layer2._cse_hash is not None
|
|
70
|
+
assert layer1._cse_hash != layer2._cse_hash
|
|
71
|
+
|
|
72
|
+
def test_raster_different_bands() -> None:
|
|
73
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
74
|
+
path = Path(tempdir) / "test.tif"
|
|
75
|
+
data1 = np.array([[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]])
|
|
76
|
+
data2 = np.array([[10.0, 20.0, 30.0, 40.0], [50.0, 60.0, 70.0, 80.0]])
|
|
77
|
+
|
|
78
|
+
datas = [data1, data2]
|
|
79
|
+
_ = gdal_multiband_dataset_with_data((0.0, 0.0), 0.02, datas, filename=path)
|
|
80
|
+
|
|
81
|
+
with (
|
|
82
|
+
yg.read_raster(path, band=1) as layer1,
|
|
83
|
+
yg.read_raster(path, band=2) as layer2,
|
|
84
|
+
):
|
|
85
|
+
assert layer1._cse_hash is not None
|
|
86
|
+
assert layer2._cse_hash is not None
|
|
87
|
+
assert layer1._cse_hash != layer2._cse_hash
|
|
88
|
+
|
|
89
|
+
def test_simple_vector_expression() -> None:
|
|
90
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
91
|
+
path = Path(tempdir) / "test.gpkg"
|
|
92
|
+
area = yg.Area(-10.0, 10.0, 10.0, 0.0)
|
|
93
|
+
make_vectors_with_id(42, {area}, path)
|
|
94
|
+
|
|
95
|
+
with yg.read_shape(path) as shape:
|
|
96
|
+
assert shape._cse_hash is not None
|
|
97
|
+
calc = shape * 2
|
|
98
|
+
assert calc._cse_hash is not None
|
|
99
|
+
|
|
100
|
+
def test_vector_different_burn() -> None:
|
|
101
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
102
|
+
path = Path(tempdir) / "test.gpkg"
|
|
103
|
+
area = yg.Area(-10.0, 10.0, 10.0, 0.0)
|
|
104
|
+
make_vectors_with_id(42, {area}, path)
|
|
105
|
+
|
|
106
|
+
with (
|
|
107
|
+
yg.read_shape(path, burn_value=1) as shape1,
|
|
108
|
+
yg.read_shape(path, burn_value=2) as shape2,
|
|
109
|
+
):
|
|
110
|
+
assert shape1._cse_hash is not None
|
|
111
|
+
assert shape2._cse_hash is not None
|
|
112
|
+
assert shape1._cse_hash != shape2._cse_hash
|
|
113
|
+
|
|
114
|
+
def test_vector_different_where() -> None:
|
|
115
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
116
|
+
path = Path(tempdir) / "test.gpkg"
|
|
117
|
+
areas = {
|
|
118
|
+
(yg.Area(0.0, 0.0, 10, -10), 42),
|
|
119
|
+
(yg.Area(0.0, 0.0, 10, -10), 43)
|
|
120
|
+
}
|
|
121
|
+
make_vectors_with_multiple_ids(areas, path)
|
|
122
|
+
|
|
123
|
+
with (
|
|
124
|
+
yg.read_shape(path) as shape0,
|
|
125
|
+
yg.read_shape(path, where_filter="id_no=42") as shape1,
|
|
126
|
+
yg.read_shape(path, where_filter="id_no=43") as shape2,
|
|
127
|
+
):
|
|
128
|
+
assert shape0._cse_hash is not None
|
|
129
|
+
assert shape1._cse_hash is not None
|
|
130
|
+
assert shape2._cse_hash is not None
|
|
131
|
+
assert shape0._cse_hash != shape1._cse_hash
|
|
132
|
+
assert shape1._cse_hash != shape2._cse_hash
|
|
133
|
+
assert shape0._cse_hash != shape2._cse_hash
|
|
134
|
+
|
|
135
|
+
def test_vector_different_datatype() -> None:
|
|
136
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
137
|
+
path = Path(tempdir) / "test.gpkg"
|
|
138
|
+
area = yg.Area(-10.0, 10.0, 10.0, 0.0)
|
|
139
|
+
make_vectors_with_id(42, {area}, path)
|
|
140
|
+
|
|
141
|
+
with (
|
|
142
|
+
yg.read_shape(path, datatype=yg.DataType.Int16) as shape1,
|
|
143
|
+
yg.read_shape(path, datatype=yg.DataType.Float32) as shape2,
|
|
144
|
+
):
|
|
145
|
+
assert shape1._cse_hash is not None
|
|
146
|
+
assert shape2._cse_hash is not None
|
|
147
|
+
assert shape1._cse_hash != shape2._cse_hash
|
|
148
|
+
|
|
149
|
+
def test_simple_group_layer() -> None:
|
|
150
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
151
|
+
path1 = Path(tempdir) / "1.tif"
|
|
152
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
153
|
+
with yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer:
|
|
154
|
+
layer.to_geotiff(path1)
|
|
155
|
+
|
|
156
|
+
path2 = Path(tempdir) / "2.tif"
|
|
157
|
+
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])
|
|
158
|
+
with yg.from_array(data2, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer:
|
|
159
|
+
layer.to_geotiff(path2)
|
|
160
|
+
|
|
161
|
+
with (
|
|
162
|
+
yg.read_rasters([path1, path2]) as group0,
|
|
163
|
+
yg.read_rasters([path1, path2]) as group1,
|
|
164
|
+
yg.read_rasters([path1]) as group2,
|
|
165
|
+
):
|
|
166
|
+
assert group0._cse_hash is not None
|
|
167
|
+
assert group1._cse_hash is not None
|
|
168
|
+
assert group2._cse_hash is not None
|
|
169
|
+
assert group0._cse_hash == group1._cse_hash
|
|
170
|
+
assert group1._cse_hash != group2._cse_hash
|
|
171
|
+
|
|
172
|
+
def test_simple_h3_layers() -> None:
|
|
173
|
+
with (
|
|
174
|
+
H3CellLayer("88972eac11fffff", yg.MapProjection("epsg:4326", 0.001, -0.001)) as layer0,
|
|
175
|
+
H3CellLayer("88972eac11fffff", yg.MapProjection("epsg:4326", 0.001, -0.001)) as layer1,
|
|
176
|
+
H3CellLayer("88972eac19fffff", yg.MapProjection("epsg:4326", 0.001, -0.001)) as layer2,
|
|
177
|
+
H3CellLayer("88972eac11fffff", yg.MapProjection("epsg:4326", 0.002, -0.002)) as layer3,
|
|
178
|
+
):
|
|
179
|
+
assert layer0._cse_hash is not None
|
|
180
|
+
assert layer1._cse_hash is not None
|
|
181
|
+
assert layer2._cse_hash is not None
|
|
182
|
+
assert layer3._cse_hash is not None
|
|
183
|
+
assert layer0._cse_hash == layer1._cse_hash
|
|
184
|
+
assert layer1._cse_hash != layer2._cse_hash
|
|
185
|
+
assert layer1._cse_hash != layer3._cse_hash
|
|
186
|
+
|
|
187
|
+
def test_mixed_raster_constant() -> None:
|
|
188
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
189
|
+
|
|
190
|
+
with(
|
|
191
|
+
yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer1,
|
|
192
|
+
yg.constant(2) as layer2,
|
|
193
|
+
yg.constant(2.0) as layer3,
|
|
194
|
+
):
|
|
195
|
+
calc0 = layer1 + layer2
|
|
196
|
+
calc1 = layer1 + layer2
|
|
197
|
+
calc2 = layer1 * layer2
|
|
198
|
+
calc3 = layer1 + layer3
|
|
199
|
+
|
|
200
|
+
assert calc0 is not calc1
|
|
201
|
+
assert layer2 is not layer3
|
|
202
|
+
assert calc0._cse_hash == calc1._cse_hash
|
|
203
|
+
assert calc1._cse_hash != calc2._cse_hash
|
|
204
|
+
assert calc1._cse_hash == calc3._cse_hash
|
|
205
|
+
|
|
206
|
+
def test_cse_simple(mocker, monkeypatch) -> None:
|
|
207
|
+
with monkeypatch.context() as m:
|
|
208
|
+
m.setattr(yg.constants, "YSTEP", 1)
|
|
209
|
+
|
|
210
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
211
|
+
with (
|
|
212
|
+
yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as lhs,
|
|
213
|
+
yg.constant(3) as rhs,
|
|
214
|
+
):
|
|
215
|
+
calc = (lhs + rhs) * (lhs + rhs)
|
|
216
|
+
|
|
217
|
+
# this is an API violation, but let's check the table used for CSE
|
|
218
|
+
hash_table = CSECacheTable(calc, calc.window)
|
|
219
|
+
|
|
220
|
+
assert len(hash_table) == 4
|
|
221
|
+
|
|
222
|
+
top_level_hash = calc._cse_hash
|
|
223
|
+
common_term_hash = (lhs + rhs)._cse_hash
|
|
224
|
+
lhs_hash = lhs._cse_hash
|
|
225
|
+
rhs_hash = rhs._cse_hash
|
|
226
|
+
|
|
227
|
+
assert hash_table._table[(top_level_hash, calc.window)] == (1, None)
|
|
228
|
+
assert hash_table._table[(common_term_hash, calc.window)] == (2, None)
|
|
229
|
+
assert hash_table._table[(lhs_hash, calc.window)] == (1, None)
|
|
230
|
+
assert hash_table._table[(rhs_hash, calc.window)] == (1, None)
|
|
231
|
+
|
|
232
|
+
lhs_spy = mocker.spy(lhs, '_read_array_with_window')
|
|
233
|
+
rhs_spy = mocker.spy(rhs, '_read_array_for_area')
|
|
234
|
+
|
|
235
|
+
expected = (data1 + 3) * (data1 + 3)
|
|
236
|
+
actual = calc.read_array(0, 0, 4, 2)
|
|
237
|
+
assert (expected == actual).all()
|
|
238
|
+
|
|
239
|
+
assert lhs_spy.call_count == 2
|
|
240
|
+
assert rhs_spy.call_count == 2
|
|
241
|
+
|
|
242
|
+
def test_simple_aoh_style_range_check(mocker, monkeypatch) -> None:
|
|
243
|
+
with monkeypatch.context() as m:
|
|
244
|
+
m.setattr(yg.constants, "YSTEP", 1)
|
|
245
|
+
|
|
246
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
247
|
+
with yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as lhs:
|
|
248
|
+
calc = (lhs > 2) & (lhs < 7)
|
|
249
|
+
|
|
250
|
+
# this is an API violation, but let's check the table used for CSE
|
|
251
|
+
hash_table = CSECacheTable(calc, calc.window)
|
|
252
|
+
|
|
253
|
+
assert len(hash_table) == 6
|
|
254
|
+
assert hash_table._table[(lhs._cse_hash, calc.window)] == (2, None)
|
|
255
|
+
for k, v in hash_table._table.items():
|
|
256
|
+
if k == (lhs._cse_hash, calc.window):
|
|
257
|
+
continue
|
|
258
|
+
assert v == (1, None)
|
|
259
|
+
|
|
260
|
+
lhs_spy = mocker.spy(lhs, '_read_array_with_window')
|
|
261
|
+
|
|
262
|
+
expected = (data1 > 2) & (data1 < 7)
|
|
263
|
+
|
|
264
|
+
actual = calc.read_array(0, 0, 4, 2)
|
|
265
|
+
assert (expected == actual).all()
|
|
266
|
+
|
|
267
|
+
assert lhs_spy.call_count == 2
|
|
268
|
+
|
|
269
|
+
def test_caching_versus_boundary_expansion(monkeypatch) -> None:
|
|
270
|
+
# If you have a layer that is the source for a convolution, then the window read from it is
|
|
271
|
+
# expanded, so if we have the same layer in and out a convolution, it'll be read at different
|
|
272
|
+
# window sizes, and cause a mess with caching
|
|
273
|
+
with monkeypatch.context() as m:
|
|
274
|
+
m.setattr(yg.constants, "YSTEP", 1)
|
|
275
|
+
|
|
276
|
+
matrix = np.array([[0.0, 1.0, 0.0], [1.0, 1.0, 1.0], [0.0, 1.0, 0.0]])
|
|
277
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [12, 13, 14, 15]]).astype(np.float32)
|
|
278
|
+
with yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as lhs:
|
|
279
|
+
calc = lhs.conv2d(matrix) * lhs
|
|
280
|
+
|
|
281
|
+
# this is an API violation, but let's check the table used for CSE
|
|
282
|
+
hash_table = CSECacheTable(calc, calc.window)
|
|
283
|
+
assert len(hash_table) == 4
|
|
284
|
+
for val in hash_table._table.values():
|
|
285
|
+
assert val == (1, None) # i.e., the two lhs values did not get put in the same hash table row
|
|
286
|
+
|
|
287
|
+
@pytest.mark.parametrize("sequence", [
|
|
288
|
+
[1, 2, 3],
|
|
289
|
+
(1, 2, 3),
|
|
290
|
+
{1, 2, 3},
|
|
291
|
+
])
|
|
292
|
+
def test_isin_hashable(sequence) -> None:
|
|
293
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
294
|
+
with yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer:
|
|
295
|
+
calc = layer.isin(sequence)
|
|
296
|
+
|
|
297
|
+
assert calc._cse_hash is not None
|
|
298
|
+
|
|
299
|
+
expected = np.isin(data1, list(sequence))
|
|
300
|
+
actual = calc.read_array(0, 0, 4, 2)
|
|
301
|
+
assert (expected == actual).all()
|
|
302
|
+
|
|
303
|
+
def test_nan_to_num_hashable() -> None:
|
|
304
|
+
data1 = np.array([[1, float("nan"), float("inf"), float("-inf")], [5, 6, 7, 8]])
|
|
305
|
+
with yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer:
|
|
306
|
+
calc = layer.nan_to_num(2, 3, 4)
|
|
307
|
+
|
|
308
|
+
assert calc._cse_hash is not None
|
|
309
|
+
|
|
310
|
+
expected = np.nan_to_num(data1, nan=2, posinf=3, neginf=4)
|
|
311
|
+
actual = calc.read_array(0, 0, 4, 2)
|
|
312
|
+
assert (expected == actual).all()
|
|
313
|
+
|
|
314
|
+
def test_unary_numpy_apply_hashable() -> None:
|
|
315
|
+
data1 = np.array([[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]])
|
|
316
|
+
with yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as layer:
|
|
317
|
+
|
|
318
|
+
def simple_add(chunk):
|
|
319
|
+
return chunk + 1.0
|
|
320
|
+
|
|
321
|
+
comp = layer.numpy_apply(simple_add)
|
|
322
|
+
|
|
323
|
+
assert comp._cse_hash is not None
|
|
324
|
+
|
|
325
|
+
def test_cse_cache_table_reset() -> None:
|
|
326
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
327
|
+
with (
|
|
328
|
+
yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as lhs,
|
|
329
|
+
yg.constant(3) as rhs,
|
|
330
|
+
):
|
|
331
|
+
calc = (lhs + rhs) * (lhs + rhs)
|
|
332
|
+
cse_cache = CSECacheTable(calc, calc.window)
|
|
333
|
+
|
|
334
|
+
term = lhs + rhs
|
|
335
|
+
term_hash = term._cse_hash
|
|
336
|
+
|
|
337
|
+
assert cse_cache.get_data(term_hash, calc.window) is None
|
|
338
|
+
|
|
339
|
+
cse_cache.set_data(term_hash, calc.window, backend.promote(data1))
|
|
340
|
+
|
|
341
|
+
cache_result = cse_cache.get_data(term_hash, calc.window)
|
|
342
|
+
assert cache_result is not None
|
|
343
|
+
assert (backend.demote_array(cache_result) == data1).all()
|
|
344
|
+
|
|
345
|
+
cse_cache.reset_cache()
|
|
346
|
+
|
|
347
|
+
assert cse_cache.get_data(term_hash, calc.window) is None
|
|
348
|
+
|
|
349
|
+
def test_cse_cache_table_cache_miss_on_different_window_size() -> None:
|
|
350
|
+
data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
|
|
351
|
+
with (
|
|
352
|
+
yg.from_array(data1, (0, 0), ("epsg:4326", (1.0, -1.0))) as lhs,
|
|
353
|
+
yg.constant(3) as rhs,
|
|
354
|
+
):
|
|
355
|
+
calc = (lhs + rhs) * (lhs + rhs)
|
|
356
|
+
cse_cache = CSECacheTable(calc, calc.window)
|
|
357
|
+
|
|
358
|
+
term = lhs + rhs
|
|
359
|
+
term_hash = term._cse_hash
|
|
360
|
+
|
|
361
|
+
assert cse_cache.get_data(term_hash, calc.window) is None
|
|
362
|
+
|
|
363
|
+
cse_cache.set_data(term_hash, calc.window, backend.promote(data1))
|
|
364
|
+
|
|
365
|
+
cache_result = cse_cache.get_data(term_hash, calc.window)
|
|
366
|
+
assert (backend.demote_array(cache_result) == data1).all()
|
|
367
|
+
|
|
368
|
+
assert cse_cache.get_data(term_hash, calc.window.grow(1)) is None
|
|
369
|
+
# This just tests that we're not stuck on id(window) check, we do test proper window equality
|
|
370
|
+
cache_result = cse_cache.get_data(term_hash, calc.window.grow(0))
|
|
371
|
+
assert cache_result is not None
|
|
372
|
+
assert (backend.demote_array(cache_result) == data1).all()
|