yirgacheffe 1.7.1__tar.gz → 1.7.3__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 (56) hide show
  1. {yirgacheffe-1.7.1/yirgacheffe.egg-info → yirgacheffe-1.7.3}/PKG-INFO +1 -1
  2. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/pyproject.toml +1 -1
  3. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_intersection.py +36 -3
  4. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_operators.py +46 -34
  5. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_union.py +35 -2
  6. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/base.py +11 -9
  7. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/operators.py +11 -5
  8. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3/yirgacheffe.egg-info}/PKG-INFO +1 -1
  9. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/LICENSE +0 -0
  10. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/MANIFEST.in +0 -0
  11. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/README.md +0 -0
  12. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/setup.cfg +0 -0
  13. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_area.py +0 -0
  14. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_auto_windowing.py +0 -0
  15. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_base.py +0 -0
  16. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_constants.py +0 -0
  17. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_datatypes.py +0 -0
  18. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_group.py +0 -0
  19. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_h3layer.py +0 -0
  20. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_multiband.py +0 -0
  21. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_nodata.py +0 -0
  22. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_openers.py +0 -0
  23. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_optimisation.py +0 -0
  24. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_parallel_operators.py +0 -0
  25. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_pickle.py +0 -0
  26. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_projection.py +0 -0
  27. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_raster.py +0 -0
  28. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_rescaling.py +0 -0
  29. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_rounding.py +0 -0
  30. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_save_with_window.py +0 -0
  31. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_sum_with_window.py +0 -0
  32. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_uniform_area_layer.py +0 -0
  33. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_vectors.py +0 -0
  34. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/tests/test_window.py +0 -0
  35. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/__init__.py +0 -0
  36. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/_backends/__init__.py +0 -0
  37. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/_backends/enumeration.py +0 -0
  38. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/_backends/mlx.py +0 -0
  39. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/_backends/numpy.py +0 -0
  40. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/_core.py +0 -0
  41. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/constants.py +0 -0
  42. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/__init__.py +0 -0
  43. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/area.py +0 -0
  44. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/constant.py +0 -0
  45. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/group.py +0 -0
  46. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/h3layer.py +0 -0
  47. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/rasters.py +0 -0
  48. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/rescaled.py +0 -0
  49. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/layers/vectors.py +0 -0
  50. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/rounding.py +0 -0
  51. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe/window.py +0 -0
  52. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe.egg-info/SOURCES.txt +0 -0
  53. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe.egg-info/dependency_links.txt +0 -0
  54. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe.egg-info/entry_points.txt +0 -0
  55. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/yirgacheffe.egg-info/requires.txt +0 -0
  56. {yirgacheffe-1.7.1 → yirgacheffe-1.7.3}/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.7.1
3
+ Version: 1.7.3
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
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "yirgacheffe"
9
- version = "1.7.1"
9
+ version = "1.7.3"
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" }]
@@ -1,9 +1,12 @@
1
+ import tempfile
2
+ from pathlib import Path
3
+
1
4
  import pytest
2
5
  from osgeo import gdal
3
6
 
4
- from tests.helpers import gdal_dataset_of_region, gdal_empty_dataset_of_region
7
+ from tests.helpers import gdal_dataset_of_region, gdal_empty_dataset_of_region, make_vectors_with_id
5
8
  from yirgacheffe.window import Area, MapProjection, Window
6
- from yirgacheffe.layers import RasterLayer, ConstantLayer, H3CellLayer
9
+ from yirgacheffe.layers import RasterLayer, ConstantLayer, H3CellLayer, VectorLayer
7
10
  from yirgacheffe import WGS_84_PROJECTION
8
11
 
9
12
 
@@ -56,6 +59,36 @@ def test_find_intersection_with_constant() -> None:
56
59
  intersection = RasterLayer.find_intersection(layers)
57
60
  assert intersection == layers[0].area
58
61
 
62
+ def test_find_intersection_with_vector_unbound() -> None:
63
+ with tempfile.TemporaryDirectory() as tempdir:
64
+ path = Path(tempdir) / "test.gpkg"
65
+ area = Area(left=58, top=74, right=180, bottom=42)
66
+ make_vectors_with_id(42, {area}, path)
67
+ assert path.exists
68
+
69
+ raster = RasterLayer(gdal_dataset_of_region(Area(left=-180.05, top=90.09, right=180.05, bottom=-90.09), 0.13))
70
+ vector = VectorLayer.layer_from_file(path, None, None, None)
71
+ assert vector.area == area
72
+
73
+ layers = [raster, vector]
74
+ intersection = RasterLayer.find_intersection(layers)
75
+ assert intersection == vector.area
76
+
77
+ def test_find_intersection_with_vector_bound() -> None:
78
+ with tempfile.TemporaryDirectory() as tempdir:
79
+ path = Path(tempdir) / "test.gpkg"
80
+ area = Area(left=58, top=74, right=180, bottom=42)
81
+ make_vectors_with_id(42, {area}, path)
82
+ assert path.exists
83
+
84
+ raster = RasterLayer(gdal_dataset_of_region(Area(left=-180.05, top=90.09, right=180.05, bottom=-90.09), 0.13))
85
+ vector = VectorLayer.layer_from_file(path, None, raster.map_projection.scale, raster.map_projection.name)
86
+ assert vector.area != area
87
+
88
+ layers = [raster, vector]
89
+ intersection = RasterLayer.find_intersection(layers)
90
+ assert intersection == vector.area
91
+
59
92
  def test_find_intersection_different_pixel_pitch() -> None:
60
93
  layers = [
61
94
  RasterLayer(gdal_dataset_of_region(Area(-10, 10, 10, -10), 0.02)),
@@ -143,7 +176,7 @@ def test_find_intersection_nearly_same() -> None:
143
176
  assert layers[0].window.xsize == other.window.xsize
144
177
  assert layers[0].window.ysize == other.window.ysize
145
178
 
146
- def test_intersection_stability():
179
+ def test_intersection_stability() -> None:
147
180
  # This test uses h3 tiles as a lazy way to get some bounded regions,
148
181
  # but the bug this test exercises is not h3 specific. This was another case of
149
182
  # a rounding error that causes set_window_for_* methods to wobble depending on how far
@@ -1472,42 +1472,54 @@ def test_to_geotiff_single_thread_and_sum() -> None:
1472
1472
  assert (expected == actual).all()
1473
1473
 
1474
1474
  @pytest.mark.skipif(yirgacheffe._backends.BACKEND != "NUMPY", reason="Only applies for numpy")
1475
- def test_to_geotiff_parallel_thread(monkeypatch) -> None:
1476
- data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
1477
- layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
1478
-
1479
- calc = layer1 * 2
1480
-
1481
- with tempfile.TemporaryDirectory() as tempdir:
1482
- filename = os.path.join(tempdir, "test.tif")
1483
- with monkeypatch.context() as m:
1484
- m.setattr(yirgacheffe.constants, "YSTEP", 1)
1485
- calc.to_geotiff(filename, parallelism=2)
1486
-
1487
- with RasterLayer.layer_from_file(filename) as result:
1488
- expected = data1 * 2
1489
- actual = result.read_array(0, 0, 4, 2)
1490
- assert (expected == actual).all()
1475
+ @pytest.mark.parametrize("parallelism", [
1476
+ 2,
1477
+ True,
1478
+ ])
1479
+ def test_to_geotiff_parallel_thread(monkeypatch, parallelism) -> None:
1480
+ with monkeypatch.context() as m:
1481
+ m.setattr(yirgacheffe.constants, "YSTEP", 1)
1482
+ m.setattr(LayerOperation, "save", None)
1483
+ with tempfile.TemporaryDirectory() as tempdir:
1484
+ data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
1485
+ src_filename = os.path.join("src.tif")
1486
+ dataset = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=src_filename)
1487
+ dataset.Close()
1488
+ with yirgacheffe.read_raster(src_filename) as layer1:
1489
+ calc = layer1 * 2
1490
+ filename = os.path.join(tempdir, "test.tif")
1491
+ calc.to_geotiff(filename, parallelism=parallelism)
1492
+
1493
+ with RasterLayer.layer_from_file(filename) as result:
1494
+ expected = data1 * 2
1495
+ actual = result.read_array(0, 0, 4, 2)
1496
+ assert (expected == actual).all()
1491
1497
 
1492
1498
  @pytest.mark.skipif(yirgacheffe._backends.BACKEND != "NUMPY", reason="Only applies for numpy")
1493
- def test_to_geotiff_parallel_thread_and_sum(monkeypatch) -> None:
1494
- data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
1495
- layer1 = RasterLayer(gdal_dataset_with_data((0.0, 0.0), 0.02, data1))
1496
-
1497
- calc = layer1 * 2
1498
-
1499
- with tempfile.TemporaryDirectory() as tempdir:
1500
- filename = os.path.join(tempdir, "test.tif")
1501
- with monkeypatch.context() as m:
1502
- m.setattr(yirgacheffe.constants, "YSTEP", 1)
1503
- actual_sum = calc.to_geotiff(filename, and_sum=True)
1504
-
1505
- assert (data1.sum() * 2) == actual_sum
1506
-
1507
- with RasterLayer.layer_from_file(filename) as result:
1508
- expected = data1 * 2
1509
- actual = result.read_array(0, 0, 4, 2)
1510
- assert (expected == actual).all()
1499
+ @pytest.mark.parametrize("parallelism", [
1500
+ 2,
1501
+ True,
1502
+ ])
1503
+ def test_to_geotiff_parallel_thread_and_sum(monkeypatch, parallelism) -> None:
1504
+ with monkeypatch.context() as m:
1505
+ m.setattr(yirgacheffe.constants, "YSTEP", 1)
1506
+ m.setattr(LayerOperation, "save", None)
1507
+ with tempfile.TemporaryDirectory() as tempdir:
1508
+ data1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
1509
+ src_filename = os.path.join("src.tif")
1510
+ dataset = gdal_dataset_with_data((0.0, 0.0), 0.02, data1, filename=src_filename)
1511
+ dataset.Close()
1512
+ with yirgacheffe.read_raster(src_filename) as layer1:
1513
+ filename = os.path.join(tempdir, "test.tif")
1514
+ calc = layer1 * 2
1515
+ actual_sum = calc.to_geotiff(filename, and_sum=True, parallelism=parallelism)
1516
+
1517
+ assert (data1.sum() * 2) == actual_sum
1518
+
1519
+ with RasterLayer.layer_from_file(filename) as result:
1520
+ expected = data1 * 2
1521
+ actual = result.read_array(0, 0, 4, 2)
1522
+ assert (expected == actual).all()
1511
1523
 
1512
1524
  def test_raster_and_vector() -> None:
1513
1525
  with tempfile.TemporaryDirectory() as tempdir:
@@ -1,8 +1,11 @@
1
+ import tempfile
2
+ from pathlib import Path
3
+
1
4
  import pytest
2
5
 
3
- from tests.helpers import gdal_dataset_of_region
6
+ from tests.helpers import gdal_dataset_of_region, make_vectors_with_id
4
7
  from yirgacheffe.window import Area, Window
5
- from yirgacheffe.layers import ConstantLayer, RasterLayer
8
+ from yirgacheffe.layers import ConstantLayer, RasterLayer, VectorLayer
6
9
 
7
10
 
8
11
  def test_find_union_empty_list() -> None:
@@ -62,6 +65,36 @@ def test_find_union_different_pixel_pitch() -> None:
62
65
  with pytest.raises(ValueError):
63
66
  _ = RasterLayer.find_union(layers)
64
67
 
68
+ def test_find_union_with_vector_unbound() -> None:
69
+ with tempfile.TemporaryDirectory() as tempdir:
70
+ path = Path(tempdir) / "test.gpkg"
71
+ area = Area(left=58, top=74, right=180, bottom=42)
72
+ make_vectors_with_id(42, {area}, path)
73
+ assert path.exists
74
+
75
+ raster = RasterLayer(gdal_dataset_of_region(Area(left=59.93, top=70.07, right=170.04, bottom=44.98), 0.13))
76
+ vector = VectorLayer.layer_from_file(path, None, None, None)
77
+ assert vector.area == area
78
+
79
+ layers = [raster, vector]
80
+ union = RasterLayer.find_union(layers)
81
+ assert union == vector.area
82
+
83
+ def test_find_union_with_vector_bound() -> None:
84
+ with tempfile.TemporaryDirectory() as tempdir:
85
+ path = Path(tempdir) / "test.gpkg"
86
+ area = Area(left=58, top=74, right=180, bottom=42)
87
+ make_vectors_with_id(42, {area}, path)
88
+ assert path.exists
89
+
90
+ raster = RasterLayer(gdal_dataset_of_region(Area(left=59.93, top=70.07, right=170.04, bottom=44.98), 0.13))
91
+ vector = VectorLayer.layer_from_file(path, None, raster.map_projection.scale, raster.map_projection.name)
92
+ assert vector.area != area
93
+
94
+ layers = [raster, vector]
95
+ union = RasterLayer.find_union(layers)
96
+ assert union == vector.area
97
+
65
98
  @pytest.mark.parametrize("scale", [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09])
66
99
  def test_set_union_self(scale) -> None:
67
100
  layer = RasterLayer(gdal_dataset_of_region(Area(-10, 10, 10, -10), scale))
@@ -91,7 +91,7 @@ class YirgacheffeLayer(LayerMathMixin):
91
91
  else:
92
92
  return self._underlying_area
93
93
 
94
- def _get_operation_area(self, projection: Optional[MapProjection]) -> Area:
94
+ def _get_operation_area(self, projection: Optional[MapProjection]=None) -> Area:
95
95
  if self._projection is not None and projection is not None and self._projection != projection:
96
96
  raise ValueError("Calculation projection does not match layer projection")
97
97
  return self.area
@@ -119,11 +119,12 @@ class YirgacheffeLayer(LayerMathMixin):
119
119
  if not all(projections[0] == x for x in projections[1:]):
120
120
  raise ValueError("Not all layers are at the same projectin or pixel scale")
121
121
 
122
+ layer_areas = [x._get_operation_area() for x in layers]
122
123
  intersection = Area(
123
- left=max(x._underlying_area.left for x in layers),
124
- top=min(x._underlying_area.top for x in layers),
125
- right=min(x._underlying_area.right for x in layers),
126
- bottom=max(x._underlying_area.bottom for x in layers)
124
+ left=max(x.left for x in layer_areas),
125
+ top=min(x.top for x in layer_areas),
126
+ right=min(x.right for x in layer_areas),
127
+ bottom=max(x.bottom for x in layer_areas)
127
128
  )
128
129
  if (intersection.left >= intersection.right) or (intersection.bottom >= intersection.top):
129
130
  raise ValueError('No intersection possible')
@@ -142,11 +143,12 @@ class YirgacheffeLayer(LayerMathMixin):
142
143
  if not all(projections[0] == x for x in projections[1:]):
143
144
  raise ValueError("Not all layers are at the same projectin or pixel scale")
144
145
 
146
+ layer_areas = [x._get_operation_area() for x in layers]
145
147
  return Area(
146
- left=min(x._underlying_area.left for x in layers),
147
- top=max(x._underlying_area.top for x in layers),
148
- right=max(x._underlying_area.right for x in layers),
149
- bottom=min(x._underlying_area.bottom for x in layers)
148
+ left=min(x.left for x in layer_areas),
149
+ top=max(x.top for x in layer_areas),
150
+ right=max(x.right for x in layer_areas),
151
+ bottom=min(x.bottom for x in layer_areas)
150
152
  )
151
153
 
152
154
  @property
@@ -251,7 +251,7 @@ class LayerMathMixin:
251
251
  self,
252
252
  filename: Union[Path,str],
253
253
  and_sum: bool = False,
254
- parallelism:Optional[int]=None
254
+ parallelism:Optional[Union[int,bool]]=None
255
255
  ) -> Optional[float]:
256
256
  return LayerOperation(self).to_geotiff(filename, and_sum, parallelism)
257
257
 
@@ -659,7 +659,9 @@ class LayerOperation(LayerMathMixin):
659
659
 
660
660
  if (computation_window.xsize != destination_window.xsize) \
661
661
  or (computation_window.ysize != destination_window.ysize):
662
- raise ValueError("Destination raster window size does not match input raster window size.")
662
+ raise ValueError((f"Destination raster window size does not match input raster window size: "
663
+ f"{(destination_window.xsize, destination_window.ysize)} vs "
664
+ f"{(computation_window.xsize, computation_window.ysize)}"))
663
665
 
664
666
  total = 0.0
665
667
 
@@ -899,7 +901,7 @@ class LayerOperation(LayerMathMixin):
899
901
  self,
900
902
  filename: Union[Path,str],
901
903
  and_sum: bool = False,
902
- parallelism:Optional[int]=None
904
+ parallelism:Optional[Union[int,bool]] = None
903
905
  ) -> Optional[float]:
904
906
  """Saves a calculation to a raster file, optionally also returning the sum of pixels.
905
907
 
@@ -909,8 +911,9 @@ class LayerOperation(LayerMathMixin):
909
911
  Path of the raster to save the result to.
910
912
  and_sum : bool, default=False
911
913
  If true then the function will also calculate the sum of the raster as it goes and return that value.
912
- parallelism : int, optional, default=None
913
- If passed, attempt to use multiple CPU cores up to the number provided.
914
+ parallelism : int or bool, optional, default=None
915
+ If passed, attempt to use multiple CPU cores up to the number provided, or if set to True, yirgacheffe
916
+ will pick a sensible value.
914
917
 
915
918
  Returns
916
919
  -------
@@ -932,6 +935,9 @@ class LayerOperation(LayerMathMixin):
932
935
  if parallelism is None:
933
936
  result = self.save(layer, and_sum=and_sum)
934
937
  else:
938
+ if isinstance(parallelism, bool):
939
+ # Parallel save treats None as "work it out"
940
+ parallelism = None
935
941
  result = self.parallel_save(layer, and_sum=and_sum, parallelism=parallelism)
936
942
 
937
943
  os.makedirs(target_dir, exist_ok=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yirgacheffe
3
- Version: 1.7.1
3
+ Version: 1.7.3
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
File without changes
File without changes
File without changes
File without changes