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/operators.py
CHANGED
|
@@ -12,14 +12,15 @@ from multiprocessing.managers import SharedMemoryManager
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Callable, Dict, Optional, Union
|
|
14
14
|
|
|
15
|
+
import deprecation
|
|
15
16
|
import numpy as np
|
|
16
17
|
import numpy.typing as npt
|
|
17
18
|
from osgeo import gdal
|
|
18
19
|
from dill import dumps, loads # type: ignore
|
|
19
20
|
|
|
20
|
-
from . import constants
|
|
21
|
-
from .rounding import
|
|
22
|
-
from .window import Area, PixelScale, Window
|
|
21
|
+
from . import constants, __version__
|
|
22
|
+
from .rounding import round_up_pixels, round_down_pixels
|
|
23
|
+
from .window import Area, PixelScale, MapProjection, Window
|
|
23
24
|
from ._backends import backend
|
|
24
25
|
from ._backends.enumeration import operators as op
|
|
25
26
|
from ._backends.enumeration import dtype as DataType
|
|
@@ -38,14 +39,17 @@ class LayerConstant:
|
|
|
38
39
|
def __init__(self, val):
|
|
39
40
|
self.val = val
|
|
40
41
|
|
|
41
|
-
def __str__(self):
|
|
42
|
+
def __str__(self) -> str:
|
|
42
43
|
return str(self.val)
|
|
43
44
|
|
|
44
|
-
def _eval(self, _area, _index, _step, _target_window):
|
|
45
|
+
def _eval(self, _area, _projection, _index, _step, _target_window):
|
|
45
46
|
return self.val
|
|
46
47
|
|
|
47
48
|
@property
|
|
48
|
-
def area(self):
|
|
49
|
+
def area(self) -> Area:
|
|
50
|
+
return Area.world()
|
|
51
|
+
|
|
52
|
+
def _get_operation_area(self, _projection) -> Area:
|
|
49
53
|
return Area.world()
|
|
50
54
|
|
|
51
55
|
class LayerMathMixin:
|
|
@@ -95,12 +99,26 @@ class LayerMathMixin:
|
|
|
95
99
|
def __or__(self, other):
|
|
96
100
|
return LayerOperation(self, op.OR, other, window_op=WindowOperation.UNION)
|
|
97
101
|
|
|
98
|
-
def _eval(
|
|
102
|
+
def _eval(
|
|
103
|
+
self,
|
|
104
|
+
area,
|
|
105
|
+
projection,
|
|
106
|
+
index,
|
|
107
|
+
step,
|
|
108
|
+
target_window=None
|
|
109
|
+
):
|
|
99
110
|
try:
|
|
100
111
|
window = self.window if target_window is None else target_window
|
|
101
|
-
return self._read_array_for_area(area, 0, index, window.xsize, step)
|
|
112
|
+
return self._read_array_for_area(area, projection, 0, index, window.xsize, step)
|
|
102
113
|
except AttributeError:
|
|
103
|
-
return self._read_array_for_area(
|
|
114
|
+
return self._read_array_for_area(
|
|
115
|
+
area,
|
|
116
|
+
projection,
|
|
117
|
+
0,
|
|
118
|
+
index,
|
|
119
|
+
target_window.xsize if target_window else 1,
|
|
120
|
+
step
|
|
121
|
+
)
|
|
104
122
|
|
|
105
123
|
def nan_to_num(self, nan=0, posinf=None, neginf=None):
|
|
106
124
|
return LayerOperation(
|
|
@@ -229,6 +247,14 @@ class LayerMathMixin:
|
|
|
229
247
|
def parallel_sum(self, callback=None, parallelism=None, band=1):
|
|
230
248
|
return LayerOperation(self).parallel_sum(callback, parallelism, band)
|
|
231
249
|
|
|
250
|
+
def to_geotiff(
|
|
251
|
+
self,
|
|
252
|
+
filename: Union[Path,str],
|
|
253
|
+
and_sum: bool = False,
|
|
254
|
+
parallelism:Optional[int]=None
|
|
255
|
+
) -> Optional[float]:
|
|
256
|
+
return LayerOperation(self).to_geotiff(filename, and_sum, parallelism)
|
|
257
|
+
|
|
232
258
|
def sum(self):
|
|
233
259
|
return LayerOperation(self).sum()
|
|
234
260
|
|
|
@@ -306,7 +332,7 @@ class LayerOperation(LayerMathMixin):
|
|
|
306
332
|
else:
|
|
307
333
|
raise ValueError("Numpy arrays are no allowed")
|
|
308
334
|
else:
|
|
309
|
-
if not
|
|
335
|
+
if not lhs.map_projection == rhs.map_projection:
|
|
310
336
|
raise ValueError("Not all layers are at the same pixel scale")
|
|
311
337
|
self.rhs = rhs
|
|
312
338
|
else:
|
|
@@ -321,7 +347,7 @@ class LayerOperation(LayerMathMixin):
|
|
|
321
347
|
else:
|
|
322
348
|
raise ValueError("Numpy arrays are no allowed")
|
|
323
349
|
else:
|
|
324
|
-
if not
|
|
350
|
+
if not lhs.map_projection == other.map_projection:
|
|
325
351
|
raise ValueError("Not all layers are at the same pixel scale")
|
|
326
352
|
self.other = other
|
|
327
353
|
else:
|
|
@@ -395,7 +421,56 @@ class LayerOperation(LayerMathMixin):
|
|
|
395
421
|
case _:
|
|
396
422
|
assert False, "Should not be reached"
|
|
397
423
|
|
|
424
|
+
def _get_operation_area(self, projection: Optional[MapProjection]) -> Area:
|
|
425
|
+
|
|
426
|
+
# The type().__name__ here is to avoid a circular import dependancy
|
|
427
|
+
lhs_area = self.lhs._get_operation_area(projection)
|
|
428
|
+
try:
|
|
429
|
+
rhs_area = self.rhs._get_operation_area(projection)
|
|
430
|
+
except AttributeError:
|
|
431
|
+
rhs_area = None
|
|
432
|
+
try:
|
|
433
|
+
other_area = self.other._get_operation_area(projection)
|
|
434
|
+
except AttributeError:
|
|
435
|
+
other_area = None
|
|
436
|
+
|
|
437
|
+
all_areas = [x for x in [lhs_area, rhs_area, other_area] if (x is not None) and (not x.is_world)]
|
|
438
|
+
|
|
439
|
+
match self.window_op:
|
|
440
|
+
case WindowOperation.NONE:
|
|
441
|
+
return all_areas[0]
|
|
442
|
+
case WindowOperation.LEFT:
|
|
443
|
+
return lhs_area
|
|
444
|
+
case WindowOperation.RIGHT:
|
|
445
|
+
assert rhs_area is not None
|
|
446
|
+
return rhs_area
|
|
447
|
+
case WindowOperation.INTERSECTION:
|
|
448
|
+
intersection = Area(
|
|
449
|
+
left=max(x.left for x in all_areas),
|
|
450
|
+
top=min(x.top for x in all_areas),
|
|
451
|
+
right=min(x.right for x in all_areas),
|
|
452
|
+
bottom=max(x.bottom for x in all_areas)
|
|
453
|
+
)
|
|
454
|
+
if (intersection.left >= intersection.right) or (intersection.bottom >= intersection.top):
|
|
455
|
+
raise ValueError('No intersection possible')
|
|
456
|
+
return intersection
|
|
457
|
+
case WindowOperation.UNION:
|
|
458
|
+
return Area(
|
|
459
|
+
left=min(x.left for x in all_areas),
|
|
460
|
+
top=max(x.top for x in all_areas),
|
|
461
|
+
right=max(x.right for x in all_areas),
|
|
462
|
+
bottom=min(x.bottom for x in all_areas)
|
|
463
|
+
)
|
|
464
|
+
case _:
|
|
465
|
+
assert False, "Should not be reached"
|
|
466
|
+
|
|
398
467
|
@property
|
|
468
|
+
@deprecation.deprecated(
|
|
469
|
+
deprecated_in="1.7",
|
|
470
|
+
removed_in="2.0",
|
|
471
|
+
current_version=__version__,
|
|
472
|
+
details="Use `map_projection` instead."
|
|
473
|
+
)
|
|
399
474
|
def pixel_scale(self) -> PixelScale:
|
|
400
475
|
# Because we test at construction that pixel scales for RHS/other are roughly equal,
|
|
401
476
|
# I believe this should be sufficient...
|
|
@@ -410,19 +485,22 @@ class LayerOperation(LayerMathMixin):
|
|
|
410
485
|
|
|
411
486
|
@property
|
|
412
487
|
def window(self) -> Window:
|
|
413
|
-
|
|
414
|
-
|
|
488
|
+
projection = self.map_projection
|
|
489
|
+
if projection is None:
|
|
490
|
+
# This can happen if your source layers are say just constants
|
|
491
|
+
raise AttributeError("No window without projection")
|
|
492
|
+
area = self._get_operation_area(projection)
|
|
415
493
|
assert area is not None
|
|
416
494
|
|
|
417
495
|
return Window(
|
|
418
|
-
xoff=round_down_pixels(area.left /
|
|
419
|
-
yoff=round_down_pixels(area.top / (
|
|
496
|
+
xoff=round_down_pixels(area.left / projection.xstep, projection.xstep),
|
|
497
|
+
yoff=round_down_pixels(area.top / (projection.ystep * -1.0), projection.ystep * -1.0),
|
|
420
498
|
xsize=round_up_pixels(
|
|
421
|
-
(area.right - area.left) /
|
|
499
|
+
(area.right - area.left) / projection.xstep, projection.xstep
|
|
422
500
|
),
|
|
423
501
|
ysize=round_up_pixels(
|
|
424
|
-
(area.top - area.bottom) / (
|
|
425
|
-
(
|
|
502
|
+
(area.top - area.bottom) / (projection.ystep * -1.0),
|
|
503
|
+
(projection.ystep * -1.0)
|
|
426
504
|
),
|
|
427
505
|
)
|
|
428
506
|
|
|
@@ -432,23 +510,53 @@ class LayerOperation(LayerMathMixin):
|
|
|
432
510
|
return self.lhs.datatype
|
|
433
511
|
|
|
434
512
|
@property
|
|
513
|
+
@deprecation.deprecated(
|
|
514
|
+
deprecated_in="1.7",
|
|
515
|
+
removed_in="2.0",
|
|
516
|
+
current_version=__version__,
|
|
517
|
+
details="Use `map_projection` instead."
|
|
518
|
+
)
|
|
435
519
|
def projection(self):
|
|
436
520
|
try:
|
|
437
|
-
|
|
521
|
+
projection = self.lhs.projection
|
|
438
522
|
except AttributeError:
|
|
439
|
-
|
|
523
|
+
projection = None
|
|
524
|
+
|
|
525
|
+
if projection is None:
|
|
526
|
+
projection = self.rhs.projection
|
|
527
|
+
return projection
|
|
440
528
|
|
|
441
|
-
|
|
529
|
+
@property
|
|
530
|
+
def map_projection(self) -> Optional[MapProjection]:
|
|
531
|
+
try:
|
|
532
|
+
projection = self.lhs.map_projection
|
|
533
|
+
except AttributeError:
|
|
534
|
+
projection = None
|
|
535
|
+
|
|
536
|
+
if projection is None:
|
|
537
|
+
try:
|
|
538
|
+
projection = self.rhs.map_projection
|
|
539
|
+
except AttributeError:
|
|
540
|
+
pass
|
|
541
|
+
return projection
|
|
542
|
+
|
|
543
|
+
def _eval(
|
|
544
|
+
self,
|
|
545
|
+
area: Area,
|
|
546
|
+
projection: MapProjection,
|
|
547
|
+
index: int,
|
|
548
|
+
step: int,
|
|
549
|
+
target_window:Optional[Window]=None
|
|
550
|
+
):
|
|
442
551
|
|
|
443
552
|
if self.buffer_padding:
|
|
444
553
|
if target_window:
|
|
445
554
|
target_window = target_window.grow(self.buffer_padding)
|
|
446
|
-
|
|
447
|
-
area = area.grow(self.buffer_padding * pixel_scale.xstep)
|
|
555
|
+
area = area.grow(self.buffer_padding * projection.xstep)
|
|
448
556
|
# The index doesn't need updating because we updated area/window
|
|
449
557
|
step += (2 * self.buffer_padding)
|
|
450
558
|
|
|
451
|
-
lhs_data = self.lhs._eval(area, index, step, target_window)
|
|
559
|
+
lhs_data = self.lhs._eval(area, projection, index, step, target_window)
|
|
452
560
|
|
|
453
561
|
if self.operator is None:
|
|
454
562
|
return lhs_data
|
|
@@ -461,12 +569,12 @@ class LayerOperation(LayerMathMixin):
|
|
|
461
569
|
|
|
462
570
|
if self.other is not None:
|
|
463
571
|
assert self.rhs is not None
|
|
464
|
-
rhs_data = self.rhs._eval(area, index, step, target_window)
|
|
465
|
-
other_data = self.other._eval(area, index, step, target_window)
|
|
572
|
+
rhs_data = self.rhs._eval(area, projection, index, step, target_window)
|
|
573
|
+
other_data = self.other._eval(area, projection, index, step, target_window)
|
|
466
574
|
return operator(lhs_data, rhs_data, other_data, **self.kwargs)
|
|
467
575
|
|
|
468
576
|
if self.rhs is not None:
|
|
469
|
-
rhs_data = self.rhs._eval(area, index, step, target_window)
|
|
577
|
+
rhs_data = self.rhs._eval(area, projection, index, step, target_window)
|
|
470
578
|
return operator(lhs_data, rhs_data, **self.kwargs)
|
|
471
579
|
|
|
472
580
|
return operator(lhs_data, **self.kwargs)
|
|
@@ -478,22 +586,24 @@ class LayerOperation(LayerMathMixin):
|
|
|
478
586
|
# of the sum are done in different types.
|
|
479
587
|
res = 0.0
|
|
480
588
|
computation_window = self.window
|
|
589
|
+
projection = self.map_projection
|
|
481
590
|
for yoffset in range(0, computation_window.ysize, self.ystep):
|
|
482
591
|
step=self.ystep
|
|
483
592
|
if yoffset+step > computation_window.ysize:
|
|
484
593
|
step = computation_window.ysize - yoffset
|
|
485
|
-
chunk = self._eval(self.
|
|
594
|
+
chunk = self._eval(self._get_operation_area(projection), projection, yoffset, step, computation_window)
|
|
486
595
|
res += backend.sum_op(chunk)
|
|
487
596
|
return res
|
|
488
597
|
|
|
489
598
|
def min(self):
|
|
490
599
|
res = None
|
|
491
600
|
computation_window = self.window
|
|
601
|
+
projection = self.map_projection
|
|
492
602
|
for yoffset in range(0, computation_window.ysize, self.ystep):
|
|
493
603
|
step=self.ystep
|
|
494
604
|
if yoffset+step > computation_window.ysize:
|
|
495
605
|
step = computation_window.ysize - yoffset
|
|
496
|
-
chunk = self._eval(self.
|
|
606
|
+
chunk = self._eval(self._get_operation_area(projection), projection, yoffset, step, computation_window)
|
|
497
607
|
chunk_min = backend.min_op(chunk)
|
|
498
608
|
if (res is None) or (res > chunk_min):
|
|
499
609
|
res = chunk_min
|
|
@@ -502,11 +612,12 @@ class LayerOperation(LayerMathMixin):
|
|
|
502
612
|
def max(self):
|
|
503
613
|
res = None
|
|
504
614
|
computation_window = self.window
|
|
615
|
+
projection = self.map_projection
|
|
505
616
|
for yoffset in range(0, computation_window.ysize, self.ystep):
|
|
506
617
|
step=self.ystep
|
|
507
618
|
if yoffset+step > computation_window.ysize:
|
|
508
619
|
step = computation_window.ysize - yoffset
|
|
509
|
-
chunk = self._eval(self.
|
|
620
|
+
chunk = self._eval(self._get_operation_area(projection), projection, yoffset, step, computation_window)
|
|
510
621
|
chunk_max = backend.max_op(chunk)
|
|
511
622
|
if (res is None) or (chunk_max > res):
|
|
512
623
|
res = chunk_max
|
|
@@ -525,14 +636,24 @@ class LayerOperation(LayerMathMixin):
|
|
|
525
636
|
except AttributeError as exc:
|
|
526
637
|
raise ValueError("Layer must be a raster backed layer") from exc
|
|
527
638
|
|
|
639
|
+
projection = self.map_projection
|
|
640
|
+
|
|
528
641
|
destination_window = destination_layer.window
|
|
642
|
+
destination_projection = destination_layer.map_projection
|
|
643
|
+
assert destination_projection is not None
|
|
644
|
+
|
|
645
|
+
if projection is None:
|
|
646
|
+
projection = destination_projection
|
|
647
|
+
else:
|
|
648
|
+
if projection != destination_projection:
|
|
649
|
+
raise ValueError("Destination layer and input layers have different projection/scale")
|
|
529
650
|
|
|
530
651
|
# If we're calculating purely from a constant layer, then we don't have a window or area
|
|
531
652
|
# so we should use the destination raster details.
|
|
532
653
|
try:
|
|
533
654
|
computation_window = self.window
|
|
534
|
-
computation_area = self.
|
|
535
|
-
except AttributeError:
|
|
655
|
+
computation_area = self._get_operation_area(projection)
|
|
656
|
+
except (AttributeError, IndexError):
|
|
536
657
|
computation_window = destination_window
|
|
537
658
|
computation_area = destination_layer.area
|
|
538
659
|
|
|
@@ -548,7 +669,7 @@ class LayerOperation(LayerMathMixin):
|
|
|
548
669
|
step=self.ystep
|
|
549
670
|
if yoffset+step > computation_window.ysize:
|
|
550
671
|
step = computation_window.ysize - yoffset
|
|
551
|
-
chunk = self._eval(computation_area, yoffset, step, computation_window)
|
|
672
|
+
chunk = self._eval(computation_area, projection, yoffset, step, computation_window)
|
|
552
673
|
if isinstance(chunk, (float, int)):
|
|
553
674
|
chunk = backend.full((step, destination_window.xsize), chunk)
|
|
554
675
|
band.WriteArray(
|
|
@@ -565,7 +686,7 @@ class LayerOperation(LayerMathMixin):
|
|
|
565
686
|
|
|
566
687
|
def _parallel_worker(self, index, shared_mem, sem, np_dtype, width, input_queue, output_queue, computation_window):
|
|
567
688
|
arr = np.ndarray((self.ystep, width), dtype=np_dtype, buffer=shared_mem.buf)
|
|
568
|
-
|
|
689
|
+
projection = self.map_projection
|
|
569
690
|
try:
|
|
570
691
|
while True:
|
|
571
692
|
# We acquire the lock so we know we have somewhere to put the
|
|
@@ -583,7 +704,7 @@ class LayerOperation(LayerMathMixin):
|
|
|
583
704
|
break
|
|
584
705
|
yoffset, step = task
|
|
585
706
|
|
|
586
|
-
result = self._eval(self.
|
|
707
|
+
result = self._eval(self._get_operation_area(projection), projection, yoffset, step, computation_window)
|
|
587
708
|
backend.eval_op(result)
|
|
588
709
|
|
|
589
710
|
arr[:step] = backend.demote_array(result)
|
|
@@ -620,7 +741,7 @@ class LayerOperation(LayerMathMixin):
|
|
|
620
741
|
assert (destination_layer is not None) or and_sum
|
|
621
742
|
try:
|
|
622
743
|
computation_window = self.window
|
|
623
|
-
except AttributeError:
|
|
744
|
+
except (AttributeError, IndexError):
|
|
624
745
|
# This is most likely because the calculation is on a constant layer (or combination of only constant
|
|
625
746
|
# layers) and there's no real benefit to parallel saving then, so to keep this code from getting yet
|
|
626
747
|
# more complicated just fall back to the single threaded path
|
|
@@ -820,12 +941,12 @@ class LayerOperation(LayerMathMixin):
|
|
|
820
941
|
|
|
821
942
|
class ShaderStyleOperation(LayerOperation):
|
|
822
943
|
|
|
823
|
-
def _eval(self, area, index, step, target_window=None):
|
|
944
|
+
def _eval(self, area, projection, index, step, target_window=None):
|
|
824
945
|
if target_window is None:
|
|
825
946
|
target_window = self.window
|
|
826
|
-
lhs_data = self.lhs._eval(area, index, step, target_window)
|
|
947
|
+
lhs_data = self.lhs._eval(area, projection, index, step, target_window)
|
|
827
948
|
if self.rhs is not None:
|
|
828
|
-
rhs_data = self.rhs._eval(area, index, step, target_window)
|
|
949
|
+
rhs_data = self.rhs._eval(area, projection, index, step, target_window)
|
|
829
950
|
else:
|
|
830
951
|
rhs_data = None
|
|
831
952
|
|
yirgacheffe/window.py
CHANGED
|
@@ -7,6 +7,47 @@ from typing import List, Optional, Tuple
|
|
|
7
7
|
|
|
8
8
|
PixelScale = namedtuple('PixelScale', ['xstep', 'ystep'])
|
|
9
9
|
|
|
10
|
+
@dataclass
|
|
11
|
+
class MapProjection:
|
|
12
|
+
"""Records the map projection and the size of the pixels in a layer.
|
|
13
|
+
|
|
14
|
+
This superceeeds the old PixelScale class, which will be removed in version 2.0.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
name : str
|
|
19
|
+
The map projection used.
|
|
20
|
+
xstep : float
|
|
21
|
+
The number of units horizontal distance a step of one pixel makes in the map projection.
|
|
22
|
+
ystep : float
|
|
23
|
+
The number of units verticle distance a step of one pixel makes in the map projection.
|
|
24
|
+
|
|
25
|
+
Attributes
|
|
26
|
+
----------
|
|
27
|
+
name : str
|
|
28
|
+
The map projection used.
|
|
29
|
+
xstep : float
|
|
30
|
+
The number of units horizontal distance a step of one pixel makes in the map projection.
|
|
31
|
+
ystep : float
|
|
32
|
+
The number of units verticle distance a step of one pixel makes in the map projection.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
name : str
|
|
36
|
+
xstep : float
|
|
37
|
+
ystep : float
|
|
38
|
+
|
|
39
|
+
def __eq__(self, other) -> bool:
|
|
40
|
+
if other is None:
|
|
41
|
+
return True
|
|
42
|
+
# to avoid circular dependancies
|
|
43
|
+
from .rounding import are_pixel_scales_equal_enough # pylint: disable=C0415
|
|
44
|
+
return (self.name == other.name) and \
|
|
45
|
+
are_pixel_scales_equal_enough([self.scale, other.scale])
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def scale(self) -> PixelScale:
|
|
49
|
+
return PixelScale(self.xstep, self.ystep)
|
|
50
|
+
|
|
10
51
|
@dataclass
|
|
11
52
|
class Area:
|
|
12
53
|
"""Class to hold a geospatial area of data in the given projection.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yirgacheffe
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
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
|
|
@@ -14,6 +14,8 @@ Requires-Dist: gdal[numpy]
|
|
|
14
14
|
Requires-Dist: scikit-image
|
|
15
15
|
Requires-Dist: torch
|
|
16
16
|
Requires-Dist: dill
|
|
17
|
+
Requires-Dist: deprecation
|
|
18
|
+
Requires-Dist: tomli
|
|
17
19
|
Provides-Extra: dev
|
|
18
20
|
Requires-Dist: mypy; extra == "dev"
|
|
19
21
|
Requires-Dist: pylint; extra == "dev"
|
|
@@ -57,7 +59,7 @@ import yirgaceffe as yg
|
|
|
57
59
|
|
|
58
60
|
habitat_map = yg.read_raster("habitats.tif")
|
|
59
61
|
elevation_map = yg.read_raster('elevation.tif')
|
|
60
|
-
range_polygon = yg.
|
|
62
|
+
range_polygon = yg.read_shape('species123.geojson')
|
|
61
63
|
area_per_pixel_map = yg.read_raster('area_per_pixel.tif')
|
|
62
64
|
|
|
63
65
|
refined_habitat = habitat_map.isin([...species habitat codes...])
|
|
@@ -71,8 +73,8 @@ print(f'area for species 123: {aoh.sum()}')
|
|
|
71
73
|
Similarly, you could save the result to a new raster layer:
|
|
72
74
|
|
|
73
75
|
```python
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
...
|
|
77
|
+
aoh.to_geotiff("result.tif")
|
|
76
78
|
```
|
|
77
79
|
|
|
78
80
|
Yirgacheffe will automatically infer if you want to do an intersection of maps or a union of the maps based on the operators you use (see below for a full table). You can explicitly override that if you want.
|
|
@@ -177,22 +179,22 @@ with VectorLayer.layer_from_file('range.gpkg', PixelScale(0.001, -0.001), WGS_84
|
|
|
177
179
|
...
|
|
178
180
|
```
|
|
179
181
|
|
|
180
|
-
The new 2.0 way of doing this is:
|
|
182
|
+
The new 2.0 way of doing this is, if you plan to use the vector layer in calculation with other raster layers that will have projection information:
|
|
181
183
|
|
|
182
184
|
```python
|
|
183
185
|
import yirgacheffe as yg
|
|
184
186
|
|
|
185
|
-
with yg.read_shape('range.gpkg'
|
|
187
|
+
with yg.read_shape('range.gpkg') as layer:
|
|
186
188
|
...
|
|
187
189
|
```
|
|
188
190
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
Of if you plan to use the layer on its own and want to specify a rasterisation projection you can do:
|
|
191
192
|
|
|
192
193
|
```python
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
import yirgacheffe as yg
|
|
195
|
+
|
|
196
|
+
with yg.read_shape('range.gpkg', (yg.WGS_84_PROJECTION, (0.001, -0.001))) as layer:
|
|
197
|
+
...
|
|
196
198
|
```
|
|
197
199
|
|
|
198
200
|
### GroupLayer
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
yirgacheffe/__init__.py,sha256=flTXNQQs6k8nboEv9O4eJnlv8kZ11z5zrfWcmUovCLg,537
|
|
2
|
+
yirgacheffe/_core.py,sha256=SjdIqNlIpePo2Ea5A88aqg1WSmqcmrqORRExBPuR-S0,4191
|
|
3
|
+
yirgacheffe/constants.py,sha256=uCWJwec3-ND-zVxYbsk1sdHKANl3ToNCTPg7MZb0j2g,434
|
|
4
|
+
yirgacheffe/operators.py,sha256=fqk61NW_BjNuuBVPx_nvxPbgBs839cx6WztGOxrtiuE,35653
|
|
5
|
+
yirgacheffe/rounding.py,sha256=ggBG4lMyLMtHLW3dBxr3gBCcF2qhRrY5etZiFGlIoqA,2258
|
|
6
|
+
yirgacheffe/window.py,sha256=0Wy3BT4SZLyviDwzLcX8LYOo2MeZ2zXCWAiJNpMbQpc,9505
|
|
7
|
+
yirgacheffe/_backends/__init__.py,sha256=jN-2iRrHStnPI6cNL7XhwhsROtI0EaGfIrbF5c-ECV0,334
|
|
8
|
+
yirgacheffe/_backends/enumeration.py,sha256=pADawllxpW_hW-IVVvZpHWIKzvEMs9aaqfkZRD1zjnY,1003
|
|
9
|
+
yirgacheffe/_backends/mlx.py,sha256=2vOTMqHbQbeqt81Eq_8hxWDXZHaPsDpbXkALRVGEnnw,6130
|
|
10
|
+
yirgacheffe/_backends/numpy.py,sha256=cYO628s4-5K_-Bp3CrnHegzYSZfkt2QC8iE9oOOMtvA,4069
|
|
11
|
+
yirgacheffe/layers/__init__.py,sha256=mYKjw5YTcMNv_hMy7a6K4yRzIuNUbR8WuBTw4WIAmSk,435
|
|
12
|
+
yirgacheffe/layers/area.py,sha256=OFOM1_dMblzXLW29TwEqfdgSecl6aNs04bKJwUydLH0,3914
|
|
13
|
+
yirgacheffe/layers/base.py,sha256=VCqN6oykGeS68IvKZ7my7otZo1lJpymAasIyw_zXY6E,14143
|
|
14
|
+
yirgacheffe/layers/constant.py,sha256=XQ1ibeSckAcUOow-dMUlZiW5S2MKeFquOz_m8Y027GI,1437
|
|
15
|
+
yirgacheffe/layers/group.py,sha256=tSkyJK6sp8VFxB8ieA3hzEeKLkP6cjW6iIOSLmdbL6c,16088
|
|
16
|
+
yirgacheffe/layers/h3layer.py,sha256=Ys6F-e4Jre7lbFBYErF_4oidQx22WkWMKpHpQ7pPDTs,9875
|
|
17
|
+
yirgacheffe/layers/rasters.py,sha256=-yECyz3Odhy1er0ilJ9bfLUseI2cTHfwqhP-H3ImUKo,13365
|
|
18
|
+
yirgacheffe/layers/rescaled.py,sha256=hkvsd7paDCyUViABxrAXdXPOZegdwiphibkdrBuRclk,3366
|
|
19
|
+
yirgacheffe/layers/vectors.py,sha256=40XAgOAmkxY1liGizwgAaeVtYJYdkFCysh9HvzvmNOU,19731
|
|
20
|
+
yirgacheffe-1.7.0.dist-info/licenses/LICENSE,sha256=dNSHwUCJr6axStTKDEdnJtfmDdFqlE3h1NPCveqPfnY,757
|
|
21
|
+
yirgacheffe-1.7.0.dist-info/METADATA,sha256=DORzQDxIzC2lFGZki3xHzOdiCwzTOxJaWIBVjhso6_g,22351
|
|
22
|
+
yirgacheffe-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
yirgacheffe-1.7.0.dist-info/entry_points.txt,sha256=j4KgHXbVGbGyfTySc1ypBdERpfihO4WNjppvCdE9HjE,52
|
|
24
|
+
yirgacheffe-1.7.0.dist-info/top_level.txt,sha256=9DBFlKO2Ld3hG6TuE3qOTd3Tt8ugTiXil4AN4Wr9_y0,12
|
|
25
|
+
yirgacheffe-1.7.0.dist-info/RECORD,,
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
yirgacheffe/__init__.py,sha256=n0v998xPOMMS6hdAQ16UNzmJUdnN5612HRtdudwjYa4,302
|
|
2
|
-
yirgacheffe/_core.py,sha256=0E56yP63vUiwi0g6ntUzmKhOuWQWoh3sSD6Ui8SWWRI,4082
|
|
3
|
-
yirgacheffe/constants.py,sha256=uCWJwec3-ND-zVxYbsk1sdHKANl3ToNCTPg7MZb0j2g,434
|
|
4
|
-
yirgacheffe/operators.py,sha256=03GKaGXn7E2VfA2UKwTUOinNuB7GtAhIO5XZec4jsFg,31364
|
|
5
|
-
yirgacheffe/rounding.py,sha256=ggBG4lMyLMtHLW3dBxr3gBCcF2qhRrY5etZiFGlIoqA,2258
|
|
6
|
-
yirgacheffe/window.py,sha256=kR8sHQ6lcixpndOWC18wLRgbgS8y00tq4Fkh8PLESvM,8209
|
|
7
|
-
yirgacheffe/_backends/__init__.py,sha256=jN-2iRrHStnPI6cNL7XhwhsROtI0EaGfIrbF5c-ECV0,334
|
|
8
|
-
yirgacheffe/_backends/enumeration.py,sha256=pADawllxpW_hW-IVVvZpHWIKzvEMs9aaqfkZRD1zjnY,1003
|
|
9
|
-
yirgacheffe/_backends/mlx.py,sha256=2vOTMqHbQbeqt81Eq_8hxWDXZHaPsDpbXkALRVGEnnw,6130
|
|
10
|
-
yirgacheffe/_backends/numpy.py,sha256=cYO628s4-5K_-Bp3CrnHegzYSZfkt2QC8iE9oOOMtvA,4069
|
|
11
|
-
yirgacheffe/layers/__init__.py,sha256=mYKjw5YTcMNv_hMy7a6K4yRzIuNUbR8WuBTw4WIAmSk,435
|
|
12
|
-
yirgacheffe/layers/area.py,sha256=Qs5N5XMrQwk7StE_Rky94X7BhavRxgRmpC6mXLeu2HQ,3905
|
|
13
|
-
yirgacheffe/layers/base.py,sha256=AgFe1HoWNuJWm2z_CgEE5o-3j-5hdC8HuNpYIM_h8HE,12672
|
|
14
|
-
yirgacheffe/layers/constant.py,sha256=PiRiAgcvOE1Li-fFItt6P_cj8IOlwTf_xi5G_iaqHok,1440
|
|
15
|
-
yirgacheffe/layers/group.py,sha256=e3pyQb35Ma-CPqY0dB4xDjMk3m2DQzzKiMGLSbBMY7c,16292
|
|
16
|
-
yirgacheffe/layers/h3layer.py,sha256=Hy8kJF9-EQnLaRbZlj4TLSRfpq2-dJga8x4jiiPUHYo,9919
|
|
17
|
-
yirgacheffe/layers/rasters.py,sha256=stZOi0sojJqMybvtKpvzfXlFJwRUvW7mA77Q3B4nhzA,13296
|
|
18
|
-
yirgacheffe/layers/rescaled.py,sha256=uam0dsidKpP97gQBig34zXefyNruUbwAnQaeuN68KE4,3104
|
|
19
|
-
yirgacheffe/layers/vectors.py,sha256=4BCGRSzbkFfsGZwLsQS9WelMXV5hDCcLmGGW6mf6MjU,15839
|
|
20
|
-
yirgacheffe-1.6.0.dist-info/licenses/LICENSE,sha256=dNSHwUCJr6axStTKDEdnJtfmDdFqlE3h1NPCveqPfnY,757
|
|
21
|
-
yirgacheffe-1.6.0.dist-info/METADATA,sha256=21WbEajGkbV-TBafy5uAjYLmGW7U0Lr2ur2IG4DXvkM,22410
|
|
22
|
-
yirgacheffe-1.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
-
yirgacheffe-1.6.0.dist-info/entry_points.txt,sha256=j4KgHXbVGbGyfTySc1ypBdERpfihO4WNjppvCdE9HjE,52
|
|
24
|
-
yirgacheffe-1.6.0.dist-info/top_level.txt,sha256=9DBFlKO2Ld3hG6TuE3qOTd3Tt8ugTiXil4AN4Wr9_y0,12
|
|
25
|
-
yirgacheffe-1.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|