yirgacheffe 1.9.3__py3-none-any.whl → 1.9.4__py3-none-any.whl

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/__init__.py CHANGED
@@ -12,12 +12,13 @@ except ModuleNotFoundError:
12
12
  pyproject_data = tomllib.load(f)
13
13
  __version__ = pyproject_data["project"]["version"]
14
14
 
15
+ from .layers import YirgacheffeLayer
15
16
  from ._core import read_raster, read_rasters, read_shape, read_shape_like, constant, read_narrow_raster
16
17
  from .constants import WGS_84_PROJECTION
17
18
  from .window import Area, MapProjection, Window
18
19
  from ._backends.enumeration import dtype as DataType
19
20
 
20
- from ._operators import where, minumum, maximum, clip, log, log2, log10, exp, exp2, nan_to_num, isin, \
21
+ from ._operators import where, minimum, maximum, clip, log, log2, log10, exp, exp2, nan_to_num, isin, \
21
22
  floor, ceil # pylint: disable=W0611
22
23
  from ._operators import abs, round # pylint: disable=W0611,W0622
23
24
 
yirgacheffe/_core.py CHANGED
@@ -16,7 +16,7 @@ def read_raster(
16
16
  filename: Path | str,
17
17
  band: int = 1,
18
18
  ignore_nodata: bool = False,
19
- ) -> RasterLayer:
19
+ ) -> YirgacheffeLayer:
20
20
  """Open a raster file (e.g., GeoTIFF).
21
21
 
22
22
  Args:
@@ -38,7 +38,7 @@ def read_narrow_raster(
38
38
  filename: Path | str,
39
39
  band: int = 1,
40
40
  ignore_nodata: bool = False,
41
- ) -> RasterLayer:
41
+ ) -> YirgacheffeLayer:
42
42
  """Open a 1 pixel wide raster file as a global raster.
43
43
 
44
44
  This exists for the special use case where an area per pixel raster would have the same value per horizontal row
@@ -58,7 +58,7 @@ def read_narrow_raster(
58
58
  def read_rasters(
59
59
  filenames : Sequence[Path | str],
60
60
  tiled: bool=False
61
- ) -> GroupLayer:
61
+ ) -> YirgacheffeLayer:
62
62
  """Open a set of raster files (e.g., GeoTIFFs) as a single layer.
63
63
 
64
64
  Args:
@@ -86,7 +86,7 @@ def read_shape(
86
86
  where_filter: str | None = None,
87
87
  datatype: DataType | None = None,
88
88
  burn_value: int | float | str = 1,
89
- ) -> VectorLayer:
89
+ ) -> YirgacheffeLayer:
90
90
  """Open a polygon file (e.g., GeoJSON, GPKG, or ESRI Shape File).
91
91
 
92
92
  Args:
@@ -124,7 +124,7 @@ def read_shape_like(
124
124
  where_filter: str | None = None,
125
125
  datatype: DataType | None = None,
126
126
  burn_value: int | float | str = 1,
127
- ) -> VectorLayer:
127
+ ) -> YirgacheffeLayer:
128
128
  """Open a polygon file (e.g., GeoJSON, GPKG, or ESRI Shape File).
129
129
 
130
130
  Args:
@@ -146,7 +146,7 @@ def read_shape_like(
146
146
  burn_value,
147
147
  )
148
148
 
149
- def constant(value: int | float) -> ConstantLayer:
149
+ def constant(value: int | float) -> YirgacheffeLayer:
150
150
  """Generate a layer that has the same value in all pixels regardless of scale, projection, and area.
151
151
 
152
152
  Generally this should not be necessary unless you must have the constant as the first term in an
yirgacheffe/_operators.py CHANGED
@@ -10,12 +10,12 @@ import sys
10
10
  import tempfile
11
11
  import time
12
12
  import types
13
+ from collections.abc import Callable
13
14
  from contextlib import ExitStack
14
15
  from enum import Enum
15
16
  from multiprocessing import Semaphore, Process
16
17
  from multiprocessing.managers import SharedMemoryManager
17
18
  from pathlib import Path
18
- from typing import Callable
19
19
 
20
20
  import deprecation
21
21
  import numpy as np
@@ -393,31 +393,34 @@ class LayerMathMixin:
393
393
  class LayerOperation(LayerMathMixin):
394
394
 
395
395
  @staticmethod
396
+ @deprecation.deprecated(
397
+ deprecated_in="1.10",
398
+ removed_in="2.0",
399
+ current_version=__version__,
400
+ details="Use from top level module instead."
401
+ )
396
402
  def where(cond, a, b):
397
- return LayerOperation(
398
- cond,
399
- op.WHERE,
400
- rhs=a,
401
- other=b
402
- )
403
+ return where(cond, a, b)
403
404
 
404
405
  @staticmethod
405
- def maximum(a, b):
406
- return LayerOperation(
407
- a,
408
- op.MAXIMUM,
409
- b,
410
- window_op=WindowOperation.UNION,
411
- )
406
+ @deprecation.deprecated(
407
+ deprecated_in="1.10",
408
+ removed_in="2.0",
409
+ current_version=__version__,
410
+ details="Use from top level module instead."
411
+ )
412
+ def minimum(a, b):
413
+ return minimum(a, b)
412
414
 
413
415
  @staticmethod
414
- def minimum(a, b):
415
- return LayerOperation(
416
- a,
417
- op.MINIMUM,
418
- rhs=b,
419
- window_op=WindowOperation.UNION,
420
- )
416
+ @deprecation.deprecated(
417
+ deprecated_in="1.10",
418
+ removed_in="2.0",
419
+ current_version=__version__,
420
+ details="Use from top level module instead."
421
+ )
422
+ def maximum(a, b):
423
+ return maximum(a, b)
421
424
 
422
425
  def __init__(
423
426
  self,
@@ -999,7 +1002,8 @@ class LayerOperation(LayerMathMixin):
999
1002
  self,
1000
1003
  filename: Path | str,
1001
1004
  and_sum: bool = False,
1002
- parallelism: int | bool | None = None
1005
+ parallelism: int | bool | None = None,
1006
+ callback: Callable[[float], None] | None = None,
1003
1007
  ) -> float | None:
1004
1008
  """Saves a calculation to a raster file, optionally also returning the sum of pixels.
1005
1009
 
@@ -1009,6 +1013,8 @@ class LayerOperation(LayerMathMixin):
1009
1013
  that value.
1010
1014
  parallelism: If passed, attempt to use multiple CPU cores up to the number provided, or if set to True,
1011
1015
  yirgacheffe will pick a sensible value.
1016
+ callback: If passed, this callback will be called periodically with a progress update for the saving,
1017
+ with a value between 0.0 and 1.0.
1012
1018
 
1013
1019
  Returns:
1014
1020
  Either returns None, or the sum of the pixels in the resulting raster if `and_sum` was specified.
@@ -1026,12 +1032,12 @@ class LayerOperation(LayerMathMixin):
1026
1032
  from yirgacheffe.layers.rasters import RasterLayer # type: ignore # pylint: disable=C0415
1027
1033
  with RasterLayer.empty_raster_layer_like(self, filename=tempory_file.name) as layer:
1028
1034
  if parallelism is None:
1029
- result = self.save(layer, and_sum=and_sum)
1035
+ result = self.save(layer, and_sum=and_sum, callback=callback)
1030
1036
  else:
1031
1037
  if isinstance(parallelism, bool):
1032
1038
  # Parallel save treats None as "work it out"
1033
1039
  parallelism = None
1034
- result = self.parallel_save(layer, and_sum=and_sum, parallelism=parallelism)
1040
+ result = self.parallel_save(layer, and_sum=and_sum, callback=callback, parallelism=parallelism)
1035
1041
 
1036
1042
  os.makedirs(target_dir, exist_ok=True)
1037
1043
  os.rename(src=tempory_file.name, dst=filename)
@@ -1121,10 +1127,70 @@ class ShaderStyleOperation(LayerOperation):
1121
1127
 
1122
1128
  return result
1123
1129
 
1130
+ def where(cond, a, b):
1131
+ """Return elements chosen from `a` or `b` depending on `cond`.
1132
+
1133
+ Behaves like numpy.where(condition, x, y), returning a layer operation
1134
+ where elements from `a` are selected where `cond` is True, and elements
1135
+ from `b` are selected where `cond` is False.
1136
+
1137
+ Args:
1138
+ cond: Layer or constant used as condition. Where True, yield `a`, otherwise yield `b`.
1139
+ a: Layer or constant with values from which to choose where `cond` is True.
1140
+ b: Layer or constant with values from which to choose where `cond` is False.
1141
+
1142
+ Returns:
1143
+ New layer representing the conditional selection.
1144
+ """
1145
+ return LayerOperation(
1146
+ cond,
1147
+ op.WHERE,
1148
+ rhs=a,
1149
+ other=b
1150
+ )
1151
+
1152
+ def maximum(a, b):
1153
+ """Element-wise maximum of layer elements.
1154
+
1155
+ Behaves like numpy.maximum(x1, x2), comparing two layers element-by-element
1156
+ and returning a new layer with the maximum values.
1157
+
1158
+ Args:
1159
+ a: First layer or constant to compare.
1160
+ b: Second layer or constant to compare.
1161
+
1162
+ Returns:
1163
+ New layer representing the element-wise maximum of the inputs.
1164
+ """
1165
+ return LayerOperation(
1166
+ a,
1167
+ op.MAXIMUM,
1168
+ b,
1169
+ window_op=WindowOperation.UNION,
1170
+ )
1171
+
1172
+ def minimum(a, b):
1173
+ """Element-wise minimum of layer elements.
1174
+
1175
+ Behaves like numpy.minimum(x1, x2), comparing two layers element-by-element
1176
+ and returning a new layer with the minimum values.
1177
+
1178
+ Args:
1179
+ a: First layer or constant to compare.
1180
+ b: Second layer or constant to compare.
1181
+
1182
+ Returns:
1183
+ New layer representing the element-wise minimum of the inputs.
1184
+ """
1185
+ return LayerOperation(
1186
+ a,
1187
+ op.MINIMUM,
1188
+ rhs=b,
1189
+ window_op=WindowOperation.UNION,
1190
+ )
1191
+
1192
+
1124
1193
  # We provide these module level accessors as it's often nicer to write `log(x/y)` rather than `(x/y).log()`
1125
- where = LayerOperation.where
1126
- minumum = LayerOperation.minimum
1127
- maximum = LayerOperation.maximum
1128
1194
  clip = LayerOperation.clip
1129
1195
  log = LayerOperation.log
1130
1196
  log2 = LayerOperation.log2
yirgacheffe/operators.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # Eventually all this should be moved to the top level in 2.0, but for backwards compatibility in 1.x needs
2
2
  # to remain here
3
3
 
4
- from ._operators import where, minumum, maximum, clip, log, log2, log10, exp, exp2, nan_to_num, isin, \
4
+ from ._operators import where, minimum, maximum, clip, log, log2, log10, exp, exp2, nan_to_num, isin, \
5
5
  floor, ceil # pylint: disable=W0611
6
6
  from ._operators import abs, round # pylint: disable=W0611,W0622
7
7
  from ._backends.enumeration import dtype as DataType # pylint: disable=W0611
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yirgacheffe
3
- Version: 1.9.3
3
+ Version: 1.9.4
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
@@ -106,11 +106,42 @@ with (
106
106
  yg.read_shape('species123.geojson') as range_map,
107
107
  ):
108
108
  refined_habitat = habitat_map.isin([...species habitat codes...])
109
- refined_elevation = (elevation_map >= species_min) && (elevation_map <= species_max)
109
+ refined_elevation = (elevation_map >= species_min) & (elevation_map <= species_max)
110
110
  aoh = refined_habitat * refined_elevation * range_polygon * area_per_pixel_map
111
111
  print(f'Area of habitat: {aoh.sum()}')
112
112
  ```
113
113
 
114
+ ## Citation
115
+
116
+ If you use Yirgacheffe in your research, please cite our paper:
117
+
118
+ > Michael Winston Dales, Alison Eyres, Patrick Ferris, Francesca A. Ridley, Simon Tarr, and Anil Madhavapeddy. 2025. Yirgacheffe: A Declarative Approach to Geospatial Data. In *Proceedings of the 2nd ACM SIGPLAN International Workshop on Programming for the Planet* (PROPL '25). Association for Computing Machinery, New York, NY, USA, 47–54. https://doi.org/10.1145/3759536.3763806
119
+
120
+ <details>
121
+ <summary>BibTeX</summary>
122
+
123
+ ```bibtex
124
+ @inproceedings{10.1145/3759536.3763806,
125
+ author = {Dales, Michael Winston and Eyres, Alison and Ferris, Patrick and Ridley, Francesca A. and Tarr, Simon and Madhavapeddy, Anil},
126
+ title = {Yirgacheffe: A Declarative Approach to Geospatial Data},
127
+ year = {2025},
128
+ isbn = {9798400721618},
129
+ publisher = {Association for Computing Machinery},
130
+ address = {New York, NY, USA},
131
+ url = {https://doi.org/10.1145/3759536.3763806},
132
+ doi = {10.1145/3759536.3763806},
133
+ abstract = {We present Yirgacheffe, a declarative geospatial library that allows spatial algorithms to be implemented concisely, supports parallel execution, and avoids common errors by automatically handling data (large geospatial rasters) and resources (cores, memory, GPUs). Our primary user domain comprises ecologists, where a typical problem involves cleaning messy occurrence data, overlaying it over tiled rasters, combining layers, and deriving actionable insights from the results. We describe the successes of this approach towards driving key pipelines related to global biodiversity and describe the capability gaps that remain, hoping to motivate more research into geospatial domain-specific languages.},
134
+ booktitle = {Proceedings of the 2nd ACM SIGPLAN International Workshop on Programming for the Planet},
135
+ pages = {47–54},
136
+ numpages = {8},
137
+ keywords = {Biodiversity, Declarative, Geospatial, Python},
138
+ location = {Singapore, Singapore},
139
+ series = {PROPL '25}
140
+ }
141
+ ```
142
+
143
+ </details>
144
+
114
145
  ## Thanks
115
146
 
116
147
  Thanks to discussion and feedback from my colleagues, particularly Alison Eyres, Patrick Ferris, Amelia Holcomb, and Anil Madhavapeddy.
@@ -1,8 +1,8 @@
1
- yirgacheffe/__init__.py,sha256=hDiVV0R4WCaRvYU_sBkL8Q6XO2PxFBM07fzNHYUGVJ0,878
2
- yirgacheffe/_core.py,sha256=AU6tlqovBV_l1dNZs6AlHSw59Z0U6pStUaQZvJGiLhM,5721
3
- yirgacheffe/_operators.py,sha256=cEjURX3GxI2kaNJwTy7JknaFsXNZTvRJj6yNLFvWTm0,41252
1
+ yirgacheffe/__init__.py,sha256=JWQOJP_RPlBmN_N3vPGH-9FByjRQKCWUSnsh4fPNtW8,915
2
+ yirgacheffe/_core.py,sha256=-X6wTjAKagcjWcH14IKp75Nf-kANO_Nsd8wKm5XEQzE,5750
3
+ yirgacheffe/_operators.py,sha256=VGQ9AOOJP6cxsr_G2kxdaPaXqKi7K1csocxsudlRwVc,43440
4
4
  yirgacheffe/constants.py,sha256=bKUjOGNj19zwggV79lJgK7tiv51DH2-rgNOKswl2gvQ,293
5
- yirgacheffe/operators.py,sha256=nw-BpnAwTjCwFtjosa8wKd2MGUuC0PJR5jACFdLhqCg,412
5
+ yirgacheffe/operators.py,sha256=Y1KkNt79N1elR4ZplQaQngx29wdf2QFF_5la4PI3EhI,412
6
6
  yirgacheffe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  yirgacheffe/rounding.py,sha256=Jzd9qlLnLpigT95GbQTByvYOo639Nfq4LBEVyvhYdoc,2289
8
8
  yirgacheffe/window.py,sha256=QuyBLOwKFI0XkEQ4Bd2hdELPbJSfHL7mt5KSi7CIHcE,9505
@@ -19,9 +19,9 @@ yirgacheffe/layers/h3layer.py,sha256=Rq1bFo7CApIh5NdBcV7hSj3hm-DszY79nhYsTRAvJ_g
19
19
  yirgacheffe/layers/rasters.py,sha256=zBE9uXm6LvAQF2_XdQzcOgJQOQWGmuPflY5JNDrUf3k,13527
20
20
  yirgacheffe/layers/rescaled.py,sha256=gEFbXeYxX1nVn7eQYmbGww90_yc5ENmgQrD_WxXxpQE,3352
21
21
  yirgacheffe/layers/vectors.py,sha256=A27kuTr0C9BZhHG0-cplNEa7aSNcse37Pm9xTjEzv-c,19990
22
- yirgacheffe-1.9.3.dist-info/licenses/LICENSE,sha256=dNSHwUCJr6axStTKDEdnJtfmDdFqlE3h1NPCveqPfnY,757
23
- yirgacheffe-1.9.3.dist-info/METADATA,sha256=g6QH8LpSDGzWwQZ33Swkb0lt61jbI95kTFM-VklAQZ8,5429
24
- yirgacheffe-1.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
- yirgacheffe-1.9.3.dist-info/entry_points.txt,sha256=j4KgHXbVGbGyfTySc1ypBdERpfihO4WNjppvCdE9HjE,52
26
- yirgacheffe-1.9.3.dist-info/top_level.txt,sha256=9DBFlKO2Ld3hG6TuE3qOTd3Tt8ugTiXil4AN4Wr9_y0,12
27
- yirgacheffe-1.9.3.dist-info/RECORD,,
22
+ yirgacheffe-1.9.4.dist-info/licenses/LICENSE,sha256=dNSHwUCJr6axStTKDEdnJtfmDdFqlE3h1NPCveqPfnY,757
23
+ yirgacheffe-1.9.4.dist-info/METADATA,sha256=cUAABIwPG9_RTO0ylH25O_7usbtQneZwTUCsfk_SLIs,7407
24
+ yirgacheffe-1.9.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
+ yirgacheffe-1.9.4.dist-info/entry_points.txt,sha256=j4KgHXbVGbGyfTySc1ypBdERpfihO4WNjppvCdE9HjE,52
26
+ yirgacheffe-1.9.4.dist-info/top_level.txt,sha256=9DBFlKO2Ld3hG6TuE3qOTd3Tt8ugTiXil4AN4Wr9_y0,12
27
+ yirgacheffe-1.9.4.dist-info/RECORD,,