yirgacheffe 1.6.0__py3-none-any.whl → 1.7.0__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 +9 -2
- yirgacheffe/_core.py +10 -12
- yirgacheffe/layers/area.py +4 -4
- yirgacheffe/layers/base.py +99 -62
- yirgacheffe/layers/constant.py +3 -3
- yirgacheffe/layers/group.py +5 -7
- yirgacheffe/layers/h3layer.py +17 -17
- yirgacheffe/layers/rasters.py +14 -15
- yirgacheffe/layers/rescaled.py +12 -9
- yirgacheffe/layers/vectors.py +126 -45
- yirgacheffe/operators.py +160 -39
- yirgacheffe/window.py +41 -0
- {yirgacheffe-1.6.0.dist-info → yirgacheffe-1.7.0.dist-info}/METADATA +13 -11
- yirgacheffe-1.7.0.dist-info/RECORD +25 -0
- yirgacheffe-1.6.0.dist-info/RECORD +0 -25
- {yirgacheffe-1.6.0.dist-info → yirgacheffe-1.7.0.dist-info}/WHEEL +0 -0
- {yirgacheffe-1.6.0.dist-info → yirgacheffe-1.7.0.dist-info}/entry_points.txt +0 -0
- {yirgacheffe-1.6.0.dist-info → yirgacheffe-1.7.0.dist-info}/licenses/LICENSE +0 -0
- {yirgacheffe-1.6.0.dist-info → yirgacheffe-1.7.0.dist-info}/top_level.txt +0 -0
yirgacheffe/layers/rescaled.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import Any, Optional, Union
|
|
|
6
6
|
from skimage import transform
|
|
7
7
|
from yirgacheffe.operators import DataType
|
|
8
8
|
|
|
9
|
-
from ..window import PixelScale, Window
|
|
9
|
+
from ..window import MapProjection, PixelScale, Window
|
|
10
10
|
from .rasters import RasterLayer, YirgacheffeLayer
|
|
11
11
|
from .._backends import backend
|
|
12
12
|
|
|
@@ -24,30 +24,33 @@ class RescaledRasterLayer(YirgacheffeLayer):
|
|
|
24
24
|
nearest_neighbour: bool = True,
|
|
25
25
|
) -> RescaledRasterLayer:
|
|
26
26
|
src = RasterLayer.layer_from_file(filename, band=band)
|
|
27
|
-
|
|
27
|
+
source_projection = src.map_projection
|
|
28
|
+
if source_projection is None:
|
|
29
|
+
raise ValueError("Source raster must have projection and scale")
|
|
30
|
+
target_projection = MapProjection(source_projection.name, pixel_scale.xstep, pixel_scale.ystep)
|
|
31
|
+
return RescaledRasterLayer(src, target_projection, nearest_neighbour, src.name)
|
|
28
32
|
|
|
29
33
|
def __init__(
|
|
30
34
|
self,
|
|
31
35
|
src: RasterLayer,
|
|
32
|
-
|
|
36
|
+
target_projection: MapProjection,
|
|
33
37
|
nearest_neighbour: bool = True,
|
|
34
38
|
name: Optional[str] = None,
|
|
35
39
|
):
|
|
36
40
|
super().__init__(
|
|
37
41
|
src.area,
|
|
38
|
-
|
|
39
|
-
projection=src.projection,
|
|
42
|
+
target_projection,
|
|
40
43
|
name=name
|
|
41
44
|
)
|
|
42
45
|
|
|
43
46
|
self._src = src
|
|
44
47
|
self._nearest_neighbour = nearest_neighbour
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
assert
|
|
49
|
+
src_projection = src.map_projection
|
|
50
|
+
assert src_projection # from raster we should always have one
|
|
48
51
|
|
|
49
|
-
self._x_scale =
|
|
50
|
-
self._y_scale =
|
|
52
|
+
self._x_scale = src_projection.xstep / target_projection.xstep
|
|
53
|
+
self._y_scale = src_projection.ystep / target_projection.ystep
|
|
51
54
|
|
|
52
55
|
def close(self):
|
|
53
56
|
self._src.close()
|
yirgacheffe/layers/vectors.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing_extensions import NotRequired
|
|
|
7
7
|
from osgeo import gdal, ogr
|
|
8
8
|
|
|
9
9
|
from ..operators import DataType
|
|
10
|
-
from ..window import Area, PixelScale
|
|
10
|
+
from ..window import Area, MapProjection, PixelScale
|
|
11
11
|
from .base import YirgacheffeLayer
|
|
12
12
|
from .rasters import RasterLayer
|
|
13
13
|
from .._backends import backend
|
|
@@ -84,10 +84,11 @@ class RasteredVectorLayer(RasterLayer):
|
|
|
84
84
|
else:
|
|
85
85
|
datatype_arg = datatype
|
|
86
86
|
|
|
87
|
+
map_projection = MapProjection(projection, scale.xstep, scale.ystep)
|
|
88
|
+
|
|
87
89
|
vector_layer = RasteredVectorLayer(
|
|
88
90
|
layer,
|
|
89
|
-
|
|
90
|
-
projection,
|
|
91
|
+
map_projection,
|
|
91
92
|
datatype=datatype_arg,
|
|
92
93
|
burn_value=burn_value
|
|
93
94
|
)
|
|
@@ -101,8 +102,7 @@ class RasteredVectorLayer(RasterLayer):
|
|
|
101
102
|
def __init__(
|
|
102
103
|
self,
|
|
103
104
|
layer: ogr.Layer,
|
|
104
|
-
|
|
105
|
-
projection: str,
|
|
105
|
+
projection: MapProjection,
|
|
106
106
|
datatype: Union[int, DataType] = DataType.Byte,
|
|
107
107
|
burn_value: Union[int,float,str] = 1,
|
|
108
108
|
):
|
|
@@ -132,7 +132,7 @@ class RasteredVectorLayer(RasterLayer):
|
|
|
132
132
|
# Get the area, but scale it to the pixel resolution that we're using. Note that
|
|
133
133
|
# the pixel scale GDAL uses can have -ve values, but those will mess up the
|
|
134
134
|
# ceil/floor math, so we use absolute versions when trying to round.
|
|
135
|
-
abs_xstep, abs_ystep = abs(
|
|
135
|
+
abs_xstep, abs_ystep = abs(projection.xstep), abs(projection.ystep)
|
|
136
136
|
area = Area(
|
|
137
137
|
left=floor(min(x[0] for x in envelopes) / abs_xstep) * abs_xstep,
|
|
138
138
|
top=ceil(max(x[3] for x in envelopes) / abs_ystep) * abs_ystep,
|
|
@@ -152,8 +152,8 @@ class RasteredVectorLayer(RasterLayer):
|
|
|
152
152
|
if not dataset:
|
|
153
153
|
raise MemoryError('Failed to create memory mask')
|
|
154
154
|
|
|
155
|
-
dataset.SetProjection(projection)
|
|
156
|
-
dataset.SetGeoTransform([area.left,
|
|
155
|
+
dataset.SetProjection(projection.name)
|
|
156
|
+
dataset.SetGeoTransform([area.left, projection.xstep, 0.0, area.top, 0.0, projection.ystep])
|
|
157
157
|
if isinstance(burn_value, (int, float)):
|
|
158
158
|
gdal.RasterizeLayer(dataset, [1], self.layer, burn_values=[burn_value], options=["ALL_TOUCHED=TRUE"])
|
|
159
159
|
elif isinstance(burn_value, str):
|
|
@@ -180,8 +180,9 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
180
180
|
) -> VectorLayer:
|
|
181
181
|
if other_layer is None:
|
|
182
182
|
raise ValueError("like layer can not be None")
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
map_projection = other_layer.map_projection
|
|
184
|
+
if map_projection is None:
|
|
185
|
+
raise ValueError("Reference layer must have projectione")
|
|
185
186
|
|
|
186
187
|
vectors = ogr.Open(filename)
|
|
187
188
|
if vectors is None:
|
|
@@ -196,8 +197,7 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
196
197
|
|
|
197
198
|
vector_layer = VectorLayer(
|
|
198
199
|
layer,
|
|
199
|
-
|
|
200
|
-
other_layer.projection,
|
|
200
|
+
map_projection,
|
|
201
201
|
name=str(filename),
|
|
202
202
|
datatype=datatype if datatype is not None else other_layer.datatype,
|
|
203
203
|
burn_value=burn_value,
|
|
@@ -217,8 +217,34 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
217
217
|
cls,
|
|
218
218
|
filename: Union[Path,str],
|
|
219
219
|
where_filter: Optional[str],
|
|
220
|
-
scale: PixelScale,
|
|
221
|
-
projection: str,
|
|
220
|
+
scale: Optional[PixelScale],
|
|
221
|
+
projection: Optional[str],
|
|
222
|
+
datatype: Optional[Union[int, DataType]] = None,
|
|
223
|
+
burn_value: Union[int,float,str] = 1,
|
|
224
|
+
anchor: Tuple[float,float] = (0.0, 0.0)
|
|
225
|
+
) -> VectorLayer:
|
|
226
|
+
# In 2.0 we need to remove this and migrate to the MapProjection version
|
|
227
|
+
if (projection is None) ^ (scale is None):
|
|
228
|
+
raise ValueError("Either both projection and scale must be provide, or neither")
|
|
229
|
+
if projection is not None and scale is not None:
|
|
230
|
+
map_projection = MapProjection(projection, scale.xstep, scale.ystep)
|
|
231
|
+
else:
|
|
232
|
+
map_projection = None
|
|
233
|
+
return cls._future_layer_from_file(
|
|
234
|
+
filename,
|
|
235
|
+
where_filter,
|
|
236
|
+
map_projection,
|
|
237
|
+
datatype,
|
|
238
|
+
burn_value,
|
|
239
|
+
anchor
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
@classmethod
|
|
243
|
+
def _future_layer_from_file(
|
|
244
|
+
cls,
|
|
245
|
+
filename: Union[Path,str],
|
|
246
|
+
where_filter: Optional[str],
|
|
247
|
+
projection: Optional[MapProjection],
|
|
222
248
|
datatype: Optional[Union[int, DataType]] = None,
|
|
223
249
|
burn_value: Union[int,float,str] = 1,
|
|
224
250
|
anchor: Tuple[float,float] = (0.0, 0.0)
|
|
@@ -243,7 +269,6 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
243
269
|
|
|
244
270
|
vector_layer = VectorLayer(
|
|
245
271
|
layer,
|
|
246
|
-
scale,
|
|
247
272
|
projection,
|
|
248
273
|
name=str(filename),
|
|
249
274
|
datatype=datatype_arg,
|
|
@@ -262,8 +287,7 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
262
287
|
def __init__(
|
|
263
288
|
self,
|
|
264
289
|
layer: ogr.Layer,
|
|
265
|
-
|
|
266
|
-
projection: str,
|
|
290
|
+
projection: Optional[MapProjection],
|
|
267
291
|
name: Optional[str] = None,
|
|
268
292
|
datatype: Union[int,DataType] = DataType.Byte,
|
|
269
293
|
burn_value: Union[int,float,str] = 1,
|
|
@@ -286,6 +310,7 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
286
310
|
self._original = None
|
|
287
311
|
self._dataset_path: Optional[Path] = None
|
|
288
312
|
self._filter: Optional[str] = None
|
|
313
|
+
self._anchor: Tuple[float,float] = (0.0, 0.0)
|
|
289
314
|
|
|
290
315
|
# work out region for mask
|
|
291
316
|
envelopes = []
|
|
@@ -298,30 +323,84 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
298
323
|
feature = layer.GetNextFeature()
|
|
299
324
|
if len(envelopes) == 0:
|
|
300
325
|
raise ValueError('No geometry found')
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
#
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
#
|
|
308
|
-
# (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
326
|
+
self._anchor = anchor
|
|
327
|
+
self._envelopes = envelopes
|
|
328
|
+
|
|
329
|
+
# if projection is not None:
|
|
330
|
+
# # Get the area, but scale it to the pixel resolution that we're using. Note that
|
|
331
|
+
# # the pixel scale GDAL uses can have -ve values, but those will mess up the
|
|
332
|
+
# # ceil/floor math, so we use absolute versions when trying to round.
|
|
333
|
+
# abs_xstep, abs_ystep = abs(projection.xstep), abs(projection.ystep)
|
|
334
|
+
#
|
|
335
|
+
# # Lacking any other reference, we will make the raster align with
|
|
336
|
+
# # (0.0, 0.0), if sometimes we want to align with an existing raster, so if
|
|
337
|
+
# # an anchor is specified, ensure we use that as our pixel space alignment
|
|
338
|
+
# x_anchor = anchor[0]
|
|
339
|
+
# y_anchor = anchor[1]
|
|
340
|
+
# left_shift = x_anchor - abs_xstep
|
|
341
|
+
# right_shift = x_anchor
|
|
342
|
+
# top_shift = y_anchor
|
|
343
|
+
# bottom_shift = y_anchor - abs_ystep
|
|
344
|
+
#
|
|
345
|
+
# area = Area(
|
|
346
|
+
# left=(floor((min(x[0] for x in envelopes) - left_shift) / abs_xstep) * abs_xstep) + left_shift,
|
|
347
|
+
# top=(ceil((max(x[3] for x in envelopes) - top_shift) / abs_ystep) * abs_ystep) + top_shift,
|
|
348
|
+
# right=(ceil((max(x[1] for x in envelopes) - right_shift) / abs_xstep) * abs_xstep) + right_shift,
|
|
349
|
+
# bottom=(floor((min(x[2] for x in envelopes) - bottom_shift) / abs_ystep) * abs_ystep) + bottom_shift,
|
|
350
|
+
# )
|
|
351
|
+
# else:
|
|
352
|
+
# If we don't have a projection just go with the idealised area
|
|
317
353
|
area = Area(
|
|
318
|
-
left=
|
|
319
|
-
top=
|
|
320
|
-
right=
|
|
321
|
-
bottom=
|
|
354
|
+
left=floor(min(x[0] for x in envelopes)),
|
|
355
|
+
top=ceil(max(x[3] for x in envelopes)),
|
|
356
|
+
right=ceil(max(x[1] for x in envelopes)),
|
|
357
|
+
bottom=floor(min(x[2] for x in envelopes)),
|
|
322
358
|
)
|
|
323
359
|
|
|
324
|
-
super().__init__(area,
|
|
360
|
+
super().__init__(area, projection)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _get_operation_area(self, projection: Optional[MapProjection]=None) -> Area:
|
|
364
|
+
if self._projection is not None and projection is not None and self._projection != projection:
|
|
365
|
+
raise ValueError("Calculation projection does not match layer projection")
|
|
366
|
+
|
|
367
|
+
target_projection = projection if projection is not None else self._projection
|
|
368
|
+
|
|
369
|
+
if target_projection is None:
|
|
370
|
+
if self._active_area is not None:
|
|
371
|
+
return self._active_area
|
|
372
|
+
else:
|
|
373
|
+
return self._underlying_area
|
|
374
|
+
else:
|
|
375
|
+
# Get the area, but scale it to the pixel resolution that we're using. Note that
|
|
376
|
+
# the pixel scale GDAL uses can have -ve values, but those will mess up the
|
|
377
|
+
# ceil/floor math, so we use absolute versions when trying to round.
|
|
378
|
+
abs_xstep, abs_ystep = abs(target_projection.xstep), abs(target_projection.ystep)
|
|
379
|
+
|
|
380
|
+
# Lacking any other reference, we will make the raster align with
|
|
381
|
+
# (0.0, 0.0), if sometimes we want to align with an existing raster, so if
|
|
382
|
+
# an anchor is specified, ensure we use that as our pixel space alignment
|
|
383
|
+
x_anchor = self._anchor[0]
|
|
384
|
+
y_anchor = self._anchor[1]
|
|
385
|
+
left_shift = x_anchor - abs_xstep
|
|
386
|
+
right_shift = x_anchor
|
|
387
|
+
top_shift = y_anchor
|
|
388
|
+
bottom_shift = y_anchor - abs_ystep
|
|
389
|
+
|
|
390
|
+
envelopes = self._envelopes
|
|
391
|
+
return Area(
|
|
392
|
+
left=(floor((min(x[0] for x in envelopes) - left_shift) / abs_xstep) * abs_xstep) + left_shift,
|
|
393
|
+
top=(ceil((max(x[3] for x in envelopes) - top_shift) / abs_ystep) * abs_ystep) + top_shift,
|
|
394
|
+
right=(ceil((max(x[1] for x in envelopes) - right_shift) / abs_xstep) * abs_xstep) + right_shift,
|
|
395
|
+
bottom=(floor((min(x[2] for x in envelopes) - bottom_shift) / abs_ystep) * abs_ystep) + bottom_shift,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
@property
|
|
399
|
+
def area(self) -> Area:
|
|
400
|
+
if self._active_area is not None:
|
|
401
|
+
return self._active_area
|
|
402
|
+
else:
|
|
403
|
+
return self._get_operation_area()
|
|
325
404
|
|
|
326
405
|
def __getstate__(self) -> object:
|
|
327
406
|
# Only support pickling on file backed layers (ideally read only ones...)
|
|
@@ -362,12 +441,14 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
362
441
|
def _read_array_for_area(
|
|
363
442
|
self,
|
|
364
443
|
target_area: Area,
|
|
444
|
+
target_projection: Optional[MapProjection],
|
|
365
445
|
x: int,
|
|
366
446
|
y: int,
|
|
367
447
|
width: int,
|
|
368
448
|
height: int,
|
|
369
449
|
) -> Any:
|
|
370
|
-
|
|
450
|
+
projection = target_projection if target_projection is not None else self._projection
|
|
451
|
+
assert projection is not None
|
|
371
452
|
|
|
372
453
|
if self._original is None:
|
|
373
454
|
self._unpark()
|
|
@@ -387,14 +468,14 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
387
468
|
if not dataset:
|
|
388
469
|
raise MemoryError('Failed to create memory mask')
|
|
389
470
|
|
|
390
|
-
dataset.SetProjection(
|
|
471
|
+
dataset.SetProjection(projection.name)
|
|
391
472
|
dataset.SetGeoTransform([
|
|
392
|
-
target_area.left + (x *
|
|
393
|
-
|
|
473
|
+
target_area.left + (x * projection.xstep),
|
|
474
|
+
projection.xstep,
|
|
394
475
|
0.0,
|
|
395
|
-
target_area.top + (y *
|
|
476
|
+
target_area.top + (y * projection.ystep),
|
|
396
477
|
0.0,
|
|
397
|
-
|
|
478
|
+
projection.ystep
|
|
398
479
|
])
|
|
399
480
|
if isinstance(self.burn_value, (int, float)):
|
|
400
481
|
gdal.RasterizeLayer(dataset, [1], self.layer, burn_values=[self.burn_value], options=["ALL_TOUCHED=TRUE"])
|
|
@@ -410,4 +491,4 @@ class VectorLayer(YirgacheffeLayer):
|
|
|
410
491
|
assert NotRequired
|
|
411
492
|
|
|
412
493
|
def read_array(self, x: int, y: int, width: int, height: int) -> Any:
|
|
413
|
-
return self._read_array_for_area(self.
|
|
494
|
+
return self._read_array_for_area(self.area, None, x, y, width, height)
|