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 +2 -1
- yirgacheffe/_core.py +6 -6
- yirgacheffe/_operators.py +93 -27
- yirgacheffe/operators.py +1 -1
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.4.dist-info}/METADATA +33 -2
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.4.dist-info}/RECORD +10 -10
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.4.dist-info}/WHEEL +0 -0
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.4.dist-info}/entry_points.txt +0 -0
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.4.dist-info}/licenses/LICENSE +0 -0
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.4.dist-info}/top_level.txt +0 -0
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,
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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) ->
|
|
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
|
|
398
|
-
cond,
|
|
399
|
-
op.WHERE,
|
|
400
|
-
rhs=a,
|
|
401
|
-
other=b
|
|
402
|
-
)
|
|
403
|
+
return where(cond, a, b)
|
|
403
404
|
|
|
404
405
|
@staticmethod
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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,
|
|
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
|
+
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)
|
|
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=
|
|
2
|
-
yirgacheffe/_core.py,sha256
|
|
3
|
-
yirgacheffe/_operators.py,sha256=
|
|
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=
|
|
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.
|
|
23
|
-
yirgacheffe-1.9.
|
|
24
|
-
yirgacheffe-1.9.
|
|
25
|
-
yirgacheffe-1.9.
|
|
26
|
-
yirgacheffe-1.9.
|
|
27
|
-
yirgacheffe-1.9.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|