yirgacheffe 1.8.0__tar.gz → 1.8.1__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.
- {yirgacheffe-1.8.0/yirgacheffe.egg-info → yirgacheffe-1.8.1}/PKG-INFO +1 -1
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/pyproject.toml +1 -1
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_area.py +3 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_auto_windowing.py +2 -2
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_intersection.py +30 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_union.py +11 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/__init__.py +1 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/_backends/enumeration.py +20 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/vectors.py +29 -29
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/window.py +3 -1
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1/yirgacheffe.egg-info}/PKG-INFO +1 -1
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/LICENSE +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/MANIFEST.in +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/README.md +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/setup.cfg +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_base.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_constants.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_datatypes.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_group.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_h3layer.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_multiband.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_nodata.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_openers.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_operators.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_optimisation.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_parallel_operators.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_pickle.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_projection.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_raster.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_rescaling.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_rounding.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_save_with_window.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_sum_with_window.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_uniform_area_layer.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_vectors.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/tests/test_window.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/_backends/__init__.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/_backends/mlx.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/_backends/numpy.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/_core.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/_operators.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/constants.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/__init__.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/area.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/base.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/constant.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/group.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/h3layer.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/rasters.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/layers/rescaled.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/operators.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/py.typed +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe/rounding.py +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe.egg-info/SOURCES.txt +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe.egg-info/dependency_links.txt +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe.egg-info/entry_points.txt +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe.egg-info/requires.txt +0 -0
- {yirgacheffe-1.8.0 → yirgacheffe-1.8.1}/yirgacheffe.egg-info/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "yirgacheffe"
|
|
9
|
-
version = "1.8.
|
|
9
|
+
version = "1.8.1"
|
|
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" }]
|
|
@@ -4,7 +4,7 @@ import tempfile
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
import yirgacheffe
|
|
7
|
+
import yirgacheffe as yg
|
|
8
8
|
from tests.helpers import gdal_dataset_with_data, make_vectors_with_mutlile_ids
|
|
9
9
|
from yirgacheffe.layers import ConstantLayer, RasterLayer, VectorLayer
|
|
10
10
|
from yirgacheffe.window import Area
|
|
@@ -315,7 +315,7 @@ def test_vector_layers_multiply() -> None:
|
|
|
315
315
|
expected = np.array([[2, 0], [0, 8]])
|
|
316
316
|
assert (expected == actual).all()
|
|
317
317
|
|
|
318
|
-
@pytest.mark.skipif(
|
|
318
|
+
@pytest.mark.skipif(yg._backends.BACKEND != "NUMPY", reason="Only applies for numpy")
|
|
319
319
|
def test_parallel_save_windows() -> None:
|
|
320
320
|
data1 = np.array([[1, 2], [3, 4]])
|
|
321
321
|
data2 = np.array([[10, 20, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120], [130, 140, 150, 160]])
|
|
@@ -59,6 +59,9 @@ def test_find_intersection_with_constant() -> None:
|
|
|
59
59
|
intersection = RasterLayer.find_intersection(layers)
|
|
60
60
|
assert intersection == layers[0].area
|
|
61
61
|
|
|
62
|
+
for layer in layers:
|
|
63
|
+
layer.set_window_for_intersection(intersection)
|
|
64
|
+
|
|
62
65
|
def test_find_intersection_with_vector_unbound() -> None:
|
|
63
66
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
64
67
|
path = Path(tempdir) / "test.gpkg"
|
|
@@ -74,6 +77,10 @@ def test_find_intersection_with_vector_unbound() -> None:
|
|
|
74
77
|
intersection = RasterLayer.find_intersection(layers)
|
|
75
78
|
assert intersection == vector.area
|
|
76
79
|
|
|
80
|
+
raster.set_window_for_intersection(intersection)
|
|
81
|
+
with pytest.raises(ValueError):
|
|
82
|
+
vector.set_window_for_intersection(intersection)
|
|
83
|
+
|
|
77
84
|
def test_find_intersection_with_vector_bound() -> None:
|
|
78
85
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
79
86
|
path = Path(tempdir) / "test.gpkg"
|
|
@@ -89,6 +96,29 @@ def test_find_intersection_with_vector_bound() -> None:
|
|
|
89
96
|
intersection = RasterLayer.find_intersection(layers)
|
|
90
97
|
assert intersection == vector.area
|
|
91
98
|
|
|
99
|
+
for layer in layers:
|
|
100
|
+
layer.set_window_for_intersection(intersection)
|
|
101
|
+
|
|
102
|
+
def test_find_intersection_with_vector_awkward_rounding() -> None:
|
|
103
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
104
|
+
path = Path(tempdir) / "test.gpkg"
|
|
105
|
+
area = Area(left=-90, top=45, right=90, bottom=-45)
|
|
106
|
+
make_vectors_with_id(42, {area}, path)
|
|
107
|
+
assert path.exists
|
|
108
|
+
|
|
109
|
+
raster = RasterLayer(gdal_dataset_of_region(Area(left=-180, top=90, right=180, bottom=-90), 18.0))
|
|
110
|
+
vector = VectorLayer.layer_from_file(path, None, raster.map_projection.scale, raster.map_projection.name)
|
|
111
|
+
|
|
112
|
+
rounded_area = Area(left=-90, top=54, right=90, bottom=-54)
|
|
113
|
+
assert vector.area == rounded_area
|
|
114
|
+
|
|
115
|
+
layers = [raster, vector]
|
|
116
|
+
intersection = RasterLayer.find_intersection(layers)
|
|
117
|
+
assert intersection == vector.area
|
|
118
|
+
|
|
119
|
+
for layer in layers:
|
|
120
|
+
layer.set_window_for_intersection(intersection)
|
|
121
|
+
|
|
92
122
|
def test_find_intersection_different_pixel_pitch() -> None:
|
|
93
123
|
layers = [
|
|
94
124
|
RasterLayer(gdal_dataset_of_region(Area(-10, 10, 10, -10), 0.02)),
|
|
@@ -49,6 +49,9 @@ def test_find_union_distinct() -> None:
|
|
|
49
49
|
union = RasterLayer.find_union(layers)
|
|
50
50
|
assert union == Area(-110, 10, 110, -10)
|
|
51
51
|
|
|
52
|
+
for layer in layers:
|
|
53
|
+
layer.set_window_for_union(union)
|
|
54
|
+
|
|
52
55
|
def test_find_union_with_null() -> None:
|
|
53
56
|
layers = [
|
|
54
57
|
RasterLayer(gdal_dataset_of_region(Area(-10, 10, 10, -10), 0.02)),
|
|
@@ -80,6 +83,11 @@ def test_find_union_with_vector_unbound() -> None:
|
|
|
80
83
|
union = RasterLayer.find_union(layers)
|
|
81
84
|
assert union == vector.area
|
|
82
85
|
|
|
86
|
+
raster.set_window_for_union(union)
|
|
87
|
+
with pytest.raises(ValueError):
|
|
88
|
+
vector.set_window_for_union(union)
|
|
89
|
+
|
|
90
|
+
|
|
83
91
|
def test_find_union_with_vector_bound() -> None:
|
|
84
92
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
85
93
|
path = Path(tempdir) / "test.gpkg"
|
|
@@ -95,6 +103,9 @@ def test_find_union_with_vector_bound() -> None:
|
|
|
95
103
|
union = RasterLayer.find_union(layers)
|
|
96
104
|
assert union == vector.area
|
|
97
105
|
|
|
106
|
+
for layer in layers:
|
|
107
|
+
layer.set_window_for_union(union)
|
|
108
|
+
|
|
98
109
|
@pytest.mark.parametrize("scale", [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09])
|
|
99
110
|
def test_set_union_self(scale) -> None:
|
|
100
111
|
layer = RasterLayer(gdal_dataset_of_region(Area(-10, 10, 10, -10), scale))
|
|
@@ -15,5 +15,6 @@ except ModuleNotFoundError:
|
|
|
15
15
|
from ._core import read_raster, read_rasters, read_shape, read_shape_like, constant, read_narrow_raster
|
|
16
16
|
from .constants import WGS_84_PROJECTION
|
|
17
17
|
from .window import Area, MapProjection, Window
|
|
18
|
+
from ._backends.enumeration import dtype as DataType
|
|
18
19
|
|
|
19
20
|
gdal.UseExceptions()
|
|
@@ -41,6 +41,26 @@ class operators(Enum):
|
|
|
41
41
|
ISNAN = 36
|
|
42
42
|
|
|
43
43
|
class dtype(Enum):
|
|
44
|
+
"""Represents the type of data returned by a layer.
|
|
45
|
+
|
|
46
|
+
This enumeration defines the valid data types supported by Yirgacheffe, and is
|
|
47
|
+
what is returned by calling `datatype` on a layer or expression, and can be
|
|
48
|
+
passed to `astype` to convert values between types.
|
|
49
|
+
|
|
50
|
+
Attributes:
|
|
51
|
+
Float32: 32 bit floating point value
|
|
52
|
+
Float64: 64 bit floating point value
|
|
53
|
+
Byte: Unsigned 8 bit integer value
|
|
54
|
+
Int8: Signed 8 bit integer value
|
|
55
|
+
Int16: Signed 16 bit integer value
|
|
56
|
+
Int32: Signed 32 bit integer value
|
|
57
|
+
Int64: Signed 64 bit integer value
|
|
58
|
+
UInt8: Unsigned 8 bit integer value
|
|
59
|
+
UInt16: Unsigned 16 bit integer value
|
|
60
|
+
UInt32: Unsigned 32 bit integer value
|
|
61
|
+
UInt64: Unsigned 64 bit integer value
|
|
62
|
+
"""
|
|
63
|
+
|
|
44
64
|
Float32 = gdal.GDT_Float32
|
|
45
65
|
Float64 = gdal.GDT_Float64
|
|
46
66
|
Byte = gdal.GDT_Byte
|
|
@@ -333,36 +333,36 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
333
333
|
self._anchor = anchor
|
|
334
334
|
self._envelopes = envelopes
|
|
335
335
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
336
|
+
if projection is not None:
|
|
337
|
+
# Get the area, but scale it to the pixel resolution that we're using. Note that
|
|
338
|
+
# the pixel scale GDAL uses can have -ve values, but those will mess up the
|
|
339
|
+
# ceil/floor math, so we use absolute versions when trying to round.
|
|
340
|
+
abs_xstep, abs_ystep = abs(projection.xstep), abs(projection.ystep)
|
|
341
|
+
|
|
342
|
+
# Lacking any other reference, we will make the raster align with
|
|
343
|
+
# (0.0, 0.0), if sometimes we want to align with an existing raster, so if
|
|
344
|
+
# an anchor is specified, ensure we use that as our pixel space alignment
|
|
345
|
+
x_anchor = anchor[0]
|
|
346
|
+
y_anchor = anchor[1]
|
|
347
|
+
left_shift = x_anchor - abs_xstep
|
|
348
|
+
right_shift = x_anchor
|
|
349
|
+
top_shift = y_anchor
|
|
350
|
+
bottom_shift = y_anchor - abs_ystep
|
|
351
|
+
|
|
352
|
+
area = Area(
|
|
353
|
+
left=(floor((min(x[0] for x in envelopes) - left_shift) / abs_xstep) * abs_xstep) + left_shift,
|
|
354
|
+
top=(ceil((max(x[3] for x in envelopes) - top_shift) / abs_ystep) * abs_ystep) + top_shift,
|
|
355
|
+
right=(ceil((max(x[1] for x in envelopes) - right_shift) / abs_xstep) * abs_xstep) + right_shift,
|
|
356
|
+
bottom=(floor((min(x[2] for x in envelopes) - bottom_shift) / abs_ystep) * abs_ystep) + bottom_shift,
|
|
357
|
+
)
|
|
358
|
+
else:
|
|
359
359
|
# If we don't have a projection just go with the idealised area
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
360
|
+
area = Area(
|
|
361
|
+
left=floor(min(x[0] for x in envelopes)),
|
|
362
|
+
top=ceil(max(x[3] for x in envelopes)),
|
|
363
|
+
right=ceil(max(x[1] for x in envelopes)),
|
|
364
|
+
bottom=floor(min(x[2] for x in envelopes)),
|
|
365
|
+
)
|
|
366
366
|
|
|
367
367
|
super().__init__(area, projection)
|
|
368
368
|
|
|
@@ -72,7 +72,9 @@ class Area:
|
|
|
72
72
|
def __hash__(self):
|
|
73
73
|
return (self.left, self.top, self.right, self.bottom).__hash__()
|
|
74
74
|
|
|
75
|
-
def __eq__(self, other) -> bool:
|
|
75
|
+
def __eq__(self, other: object) -> bool:
|
|
76
|
+
if not isinstance(other, Area):
|
|
77
|
+
return False
|
|
76
78
|
return math.isclose(self.left, other.left, abs_tol=1e-09) and \
|
|
77
79
|
math.isclose(self.right, other.right, abs_tol=1e-09) and \
|
|
78
80
|
math.isclose(self.top, other.top, abs_tol=1e-09) and \
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|