yirgacheffe 1.9.3__py3-none-any.whl → 1.9.5__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 +3 -2
- yirgacheffe/_backends/enumeration.py +30 -1
- yirgacheffe/_core.py +56 -7
- yirgacheffe/_operators.py +93 -27
- yirgacheffe/layers/group.py +1 -1
- yirgacheffe/operators.py +1 -1
- yirgacheffe/window.py +9 -0
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.5.dist-info}/METADATA +33 -2
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.5.dist-info}/RECORD +13 -13
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.5.dist-info}/WHEEL +0 -0
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.5.dist-info}/entry_points.txt +0 -0
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.5.dist-info}/licenses/LICENSE +0 -0
- {yirgacheffe-1.9.3.dist-info → yirgacheffe-1.9.5.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 .
|
|
15
|
+
from .layers import YirgacheffeLayer
|
|
16
|
+
from ._core import read_raster, read_rasters, read_shape, read_shape_like, constant, read_narrow_raster, from_array
|
|
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
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from enum import Enum
|
|
2
4
|
|
|
5
|
+
import numpy as np
|
|
3
6
|
from osgeo import gdal
|
|
4
7
|
|
|
5
8
|
class operators(Enum):
|
|
@@ -77,5 +80,31 @@ class dtype(Enum):
|
|
|
77
80
|
return self.value
|
|
78
81
|
|
|
79
82
|
@classmethod
|
|
80
|
-
def of_gdal(cls, val):
|
|
83
|
+
def of_gdal(cls, val: int) -> dtype:
|
|
81
84
|
return cls(val)
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def of_array(cls, val: np.ndarray) -> dtype:
|
|
88
|
+
match val.dtype:
|
|
89
|
+
case np.float32:
|
|
90
|
+
return dtype.Float32
|
|
91
|
+
case np.float64:
|
|
92
|
+
return dtype.Float64
|
|
93
|
+
case np.int8:
|
|
94
|
+
return dtype.Int8
|
|
95
|
+
case np.int16:
|
|
96
|
+
return dtype.Int16
|
|
97
|
+
case np.int32:
|
|
98
|
+
return dtype.Int32
|
|
99
|
+
case np.int64:
|
|
100
|
+
return dtype.Int64
|
|
101
|
+
case np.uint8:
|
|
102
|
+
return dtype.UInt8
|
|
103
|
+
case np.uint16:
|
|
104
|
+
return dtype.UInt16
|
|
105
|
+
case np.uint32:
|
|
106
|
+
return dtype.UInt32
|
|
107
|
+
case np.uint64:
|
|
108
|
+
return dtype.UInt64
|
|
109
|
+
case _:
|
|
110
|
+
raise ValueError
|
yirgacheffe/_core.py
CHANGED
|
@@ -3,20 +3,22 @@ from __future__ import annotations
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Sequence
|
|
5
5
|
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
6
8
|
from .layers.area import UniformAreaLayer
|
|
7
9
|
from .layers.base import YirgacheffeLayer
|
|
8
10
|
from .layers.constant import ConstantLayer
|
|
9
11
|
from .layers.group import GroupLayer, TiledGroupLayer
|
|
10
12
|
from .layers.rasters import RasterLayer
|
|
11
13
|
from .layers.vectors import VectorLayer
|
|
12
|
-
from .window import MapProjection
|
|
14
|
+
from .window import Area, MapProjection
|
|
13
15
|
from ._backends.enumeration import dtype as DataType
|
|
14
16
|
|
|
15
17
|
def read_raster(
|
|
16
18
|
filename: Path | str,
|
|
17
19
|
band: int = 1,
|
|
18
20
|
ignore_nodata: bool = False,
|
|
19
|
-
) ->
|
|
21
|
+
) -> YirgacheffeLayer:
|
|
20
22
|
"""Open a raster file (e.g., GeoTIFF).
|
|
21
23
|
|
|
22
24
|
Args:
|
|
@@ -38,7 +40,7 @@ def read_narrow_raster(
|
|
|
38
40
|
filename: Path | str,
|
|
39
41
|
band: int = 1,
|
|
40
42
|
ignore_nodata: bool = False,
|
|
41
|
-
) ->
|
|
43
|
+
) -> YirgacheffeLayer:
|
|
42
44
|
"""Open a 1 pixel wide raster file as a global raster.
|
|
43
45
|
|
|
44
46
|
This exists for the special use case where an area per pixel raster would have the same value per horizontal row
|
|
@@ -58,7 +60,7 @@ def read_narrow_raster(
|
|
|
58
60
|
def read_rasters(
|
|
59
61
|
filenames : Sequence[Path | str],
|
|
60
62
|
tiled: bool=False
|
|
61
|
-
) ->
|
|
63
|
+
) -> YirgacheffeLayer:
|
|
62
64
|
"""Open a set of raster files (e.g., GeoTIFFs) as a single layer.
|
|
63
65
|
|
|
64
66
|
Args:
|
|
@@ -86,7 +88,7 @@ def read_shape(
|
|
|
86
88
|
where_filter: str | None = None,
|
|
87
89
|
datatype: DataType | None = None,
|
|
88
90
|
burn_value: int | float | str = 1,
|
|
89
|
-
) ->
|
|
91
|
+
) -> YirgacheffeLayer:
|
|
90
92
|
"""Open a polygon file (e.g., GeoJSON, GPKG, or ESRI Shape File).
|
|
91
93
|
|
|
92
94
|
Args:
|
|
@@ -124,7 +126,7 @@ def read_shape_like(
|
|
|
124
126
|
where_filter: str | None = None,
|
|
125
127
|
datatype: DataType | None = None,
|
|
126
128
|
burn_value: int | float | str = 1,
|
|
127
|
-
) ->
|
|
129
|
+
) -> YirgacheffeLayer:
|
|
128
130
|
"""Open a polygon file (e.g., GeoJSON, GPKG, or ESRI Shape File).
|
|
129
131
|
|
|
130
132
|
Args:
|
|
@@ -146,7 +148,7 @@ def read_shape_like(
|
|
|
146
148
|
burn_value,
|
|
147
149
|
)
|
|
148
150
|
|
|
149
|
-
def constant(value: int | float) ->
|
|
151
|
+
def constant(value: int | float) -> YirgacheffeLayer:
|
|
150
152
|
"""Generate a layer that has the same value in all pixels regardless of scale, projection, and area.
|
|
151
153
|
|
|
152
154
|
Generally this should not be necessary unless you must have the constant as the first term in an
|
|
@@ -161,3 +163,50 @@ def constant(value: int | float) -> ConstantLayer:
|
|
|
161
163
|
A constant layer of the provided value.
|
|
162
164
|
"""
|
|
163
165
|
return ConstantLayer(value)
|
|
166
|
+
|
|
167
|
+
def from_array(
|
|
168
|
+
values: np.ndarray,
|
|
169
|
+
origin: tuple[float, float],
|
|
170
|
+
projection: MapProjection | tuple[str, tuple[float, float]],
|
|
171
|
+
) -> YirgacheffeLayer:
|
|
172
|
+
"""Creates an in-memory layer from a numerical array.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
values: a 2D array of data values, with Y on the first dimension, X on
|
|
176
|
+
the second dimension.
|
|
177
|
+
origin: the position of the top left pixel in the geospatial space
|
|
178
|
+
projection: the map projection and pixel scale to use.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
A geospatial layer that uses the provided data for its values.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
if projection is None:
|
|
185
|
+
raise ValueError("Projection must not be none")
|
|
186
|
+
|
|
187
|
+
if not isinstance(projection, MapProjection):
|
|
188
|
+
projection_name, scale_tuple = projection
|
|
189
|
+
projection = MapProjection(projection_name, scale_tuple[0], scale_tuple[1])
|
|
190
|
+
|
|
191
|
+
dims = values.shape
|
|
192
|
+
|
|
193
|
+
area = Area(
|
|
194
|
+
left=origin[0],
|
|
195
|
+
top=origin[1],
|
|
196
|
+
right=origin[0] + (projection.xstep * dims[1]),
|
|
197
|
+
bottom=origin[1] + (projection.ystep * dims[0])
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
layer = RasterLayer.empty_raster_layer(
|
|
201
|
+
area,
|
|
202
|
+
scale=projection.scale,
|
|
203
|
+
datatype=DataType.of_array(values),
|
|
204
|
+
filename=None,
|
|
205
|
+
projection=projection.name,
|
|
206
|
+
)
|
|
207
|
+
assert layer._dataset
|
|
208
|
+
assert layer._dataset.RasterXSize == dims[1]
|
|
209
|
+
assert layer._dataset.RasterYSize == dims[0]
|
|
210
|
+
layer._dataset.GetRasterBand(1).WriteArray(values, 0, 0)
|
|
211
|
+
|
|
212
|
+
return layer
|
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/layers/group.py
CHANGED
|
@@ -81,7 +81,7 @@ class GroupLayer(YirgacheffeLayer):
|
|
|
81
81
|
|
|
82
82
|
@property
|
|
83
83
|
def datatype(self) -> DataType:
|
|
84
|
-
return
|
|
84
|
+
return self.layers[0].datatype
|
|
85
85
|
|
|
86
86
|
def set_window_for_intersection(self, new_area: Area) -> None:
|
|
87
87
|
super().set_window_for_intersection(new_area)
|
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
|
yirgacheffe/window.py
CHANGED
|
@@ -22,6 +22,15 @@ class MapProjection:
|
|
|
22
22
|
name: The map projection used in WKT format.
|
|
23
23
|
xstep: The number of units horizontal distance a step of one pixel makes in the map projection.
|
|
24
24
|
ystep: The number of units vertical distance a step of one pixel makes in the map projection.
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
Create a map projection using an EPSG code:
|
|
28
|
+
|
|
29
|
+
>>> proj_wgs84 = MapProjection("epsg:4326", 0.001, -0.001)
|
|
30
|
+
|
|
31
|
+
Create a projection using an ESRI code:
|
|
32
|
+
|
|
33
|
+
>>> proj_esri = MapProjection("esri:54030", 1000, -1000)
|
|
25
34
|
"""
|
|
26
35
|
|
|
27
36
|
def __init__(self, projection_string: str, xstep: float, ystep: float) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yirgacheffe
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.5
|
|
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,27 +1,27 @@
|
|
|
1
|
-
yirgacheffe/__init__.py,sha256=
|
|
2
|
-
yirgacheffe/_core.py,sha256=
|
|
3
|
-
yirgacheffe/_operators.py,sha256=
|
|
1
|
+
yirgacheffe/__init__.py,sha256=Ps6W8A1TRriVNxZEF3jW1_KOLEtji4ffyoGRmQXne8g,927
|
|
2
|
+
yirgacheffe/_core.py,sha256=Tr6RAiRZOO3vbtiTjLoNRjjd1DkXYZOgpDqrjs7jCBw,7231
|
|
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
|
-
yirgacheffe/window.py,sha256=
|
|
8
|
+
yirgacheffe/window.py,sha256=kAAsE3t49DbffrW78DLZuGwgT-MCpM3WBJQ-5MO5ciM,9755
|
|
9
9
|
yirgacheffe/_backends/__init__.py,sha256=jN-2iRrHStnPI6cNL7XhwhsROtI0EaGfIrbF5c-ECV0,334
|
|
10
|
-
yirgacheffe/_backends/enumeration.py,sha256=
|
|
10
|
+
yirgacheffe/_backends/enumeration.py,sha256=Clb-oRha65po_dED_lECXjnih-n77CtUg18-34xX6nA,2652
|
|
11
11
|
yirgacheffe/_backends/mlx.py,sha256=U1gl1lK1mZXLEET6ylF1TNs6WJ0PBEvfSk7ppn28n8w,6203
|
|
12
12
|
yirgacheffe/_backends/numpy.py,sha256=Gxx49JJH79GFEkKIpV6IyjCUcdtN5-qLlzRfylzKhS4,4142
|
|
13
13
|
yirgacheffe/layers/__init__.py,sha256=mYKjw5YTcMNv_hMy7a6K4yRzIuNUbR8WuBTw4WIAmSk,435
|
|
14
14
|
yirgacheffe/layers/area.py,sha256=wJcMHbLJBaXS4BeFbu5rYeKfgu3gvaE9hwQ5j6aw-y4,3976
|
|
15
15
|
yirgacheffe/layers/base.py,sha256=7b4WXuvnmCv8mR0iyCIuSEolnV8D3f2vtCaYlcJCIa8,13201
|
|
16
16
|
yirgacheffe/layers/constant.py,sha256=gtkQ98Z01CYYDgFElswtRZY4ZG3UnS5NIAoIVue5ufk,1481
|
|
17
|
-
yirgacheffe/layers/group.py,sha256=
|
|
17
|
+
yirgacheffe/layers/group.py,sha256=jFJ60YcbkLNeD-2w3QOLwbcYEWdgicrXMClIo2vz97Q,16139
|
|
18
18
|
yirgacheffe/layers/h3layer.py,sha256=Rq1bFo7CApIh5NdBcV7hSj3hm-DszY79nhYsTRAvJ_g,9916
|
|
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.5.dist-info/licenses/LICENSE,sha256=dNSHwUCJr6axStTKDEdnJtfmDdFqlE3h1NPCveqPfnY,757
|
|
23
|
+
yirgacheffe-1.9.5.dist-info/METADATA,sha256=TvX6Nwvdp_u80s9PXHCvSoxndvp4mjOBE1wRcn-neL0,7407
|
|
24
|
+
yirgacheffe-1.9.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
25
|
+
yirgacheffe-1.9.5.dist-info/entry_points.txt,sha256=j4KgHXbVGbGyfTySc1ypBdERpfihO4WNjppvCdE9HjE,52
|
|
26
|
+
yirgacheffe-1.9.5.dist-info/top_level.txt,sha256=9DBFlKO2Ld3hG6TuE3qOTd3Tt8ugTiXil4AN4Wr9_y0,12
|
|
27
|
+
yirgacheffe-1.9.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|