yirgacheffe 1.9.0__tar.gz → 1.9.2__tar.gz

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.

Files changed (58) hide show
  1. {yirgacheffe-1.9.0/yirgacheffe.egg-info → yirgacheffe-1.9.2}/PKG-INFO +3 -1
  2. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/pyproject.toml +4 -1
  3. yirgacheffe-1.9.0/tests/test_base.py → yirgacheffe-1.9.2/tests/test_pixel_coord.py +30 -0
  4. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/_operators.py +102 -0
  5. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/base.py +0 -46
  6. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2/yirgacheffe.egg-info}/PKG-INFO +3 -1
  7. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe.egg-info/SOURCES.txt +1 -1
  8. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe.egg-info/requires.txt +3 -0
  9. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/LICENSE +0 -0
  10. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/MANIFEST.in +0 -0
  11. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/README.md +0 -0
  12. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/setup.cfg +0 -0
  13. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_area.py +0 -0
  14. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_auto_windowing.py +0 -0
  15. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_constants.py +0 -0
  16. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_datatypes.py +0 -0
  17. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_group.py +0 -0
  18. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_h3layer.py +0 -0
  19. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_intersection.py +0 -0
  20. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_multiband.py +0 -0
  21. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_nodata.py +0 -0
  22. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_openers.py +0 -0
  23. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_operators.py +0 -0
  24. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_optimisation.py +0 -0
  25. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_parallel_operators.py +0 -0
  26. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_pickle.py +0 -0
  27. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_projection.py +0 -0
  28. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_raster.py +0 -0
  29. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_rescaling.py +0 -0
  30. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_rounding.py +0 -0
  31. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_save_with_window.py +0 -0
  32. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_sum_with_window.py +0 -0
  33. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_uniform_area_layer.py +0 -0
  34. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_union.py +0 -0
  35. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_vectors.py +0 -0
  36. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/tests/test_window.py +0 -0
  37. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/__init__.py +0 -0
  38. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/_backends/__init__.py +0 -0
  39. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/_backends/enumeration.py +0 -0
  40. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/_backends/mlx.py +0 -0
  41. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/_backends/numpy.py +0 -0
  42. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/_core.py +0 -0
  43. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/constants.py +0 -0
  44. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/__init__.py +0 -0
  45. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/area.py +0 -0
  46. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/constant.py +0 -0
  47. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/group.py +0 -0
  48. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/h3layer.py +0 -0
  49. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/rasters.py +0 -0
  50. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/rescaled.py +0 -0
  51. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/layers/vectors.py +0 -0
  52. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/operators.py +0 -0
  53. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/py.typed +0 -0
  54. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/rounding.py +0 -0
  55. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe/window.py +0 -0
  56. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe.egg-info/dependency_links.txt +0 -0
  57. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe.egg-info/entry_points.txt +0 -0
  58. {yirgacheffe-1.9.0 → yirgacheffe-1.9.2}/yirgacheffe.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yirgacheffe
3
- Version: 1.9.0
3
+ Version: 1.9.2
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
@@ -30,6 +30,8 @@ Requires-Dist: h3
30
30
  Requires-Dist: pyproj
31
31
  Provides-Extra: mlx
32
32
  Requires-Dist: mlx; extra == "mlx"
33
+ Provides-Extra: matplotlib
34
+ Requires-Dist: matplotlib; extra == "matplotlib"
33
35
  Provides-Extra: dev
34
36
  Requires-Dist: mypy; extra == "dev"
35
37
  Requires-Dist: pylint; extra == "dev"
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "yirgacheffe"
9
- version = "1.9.0"
9
+ version = "1.9.2"
10
10
  description = "Abstraction of gdal datasets for doing basic math operations"
11
11
  readme = "README.md"
12
12
  authors = [{ name = "Michael Dales", email = "mwd24@cam.ac.uk" }]
@@ -38,6 +38,9 @@ requires-python = ">=3.10"
38
38
  mlx = [
39
39
  "mlx",
40
40
  ]
41
+ matplotlib = [
42
+ "matplotlib",
43
+ ]
41
44
  dev = [
42
45
  "mypy",
43
46
  "pylint",
@@ -83,6 +83,21 @@ def test_latlng_for_pixel(
83
83
  assert math.isclose(result[0], expected[0])
84
84
  assert math.isclose(result[1], expected[1])
85
85
 
86
+ def test_latlng_for_pixel_on_operator() -> None:
87
+ layer1 = YirgacheffeLayer(
88
+ Area(-10, 10, 10, -10),
89
+ MapProjection("epsg:4326", 0.2, -0.2),
90
+ )
91
+ layer2 = YirgacheffeLayer(
92
+ Area(-10, 10, 10, -10),
93
+ MapProjection("epsg:4326", 0.2, -0.2),
94
+ )
95
+ calc = layer1 + layer2
96
+ result = calc.latlng_for_pixel(0, 0)
97
+ expected = (10.0, -10.0)
98
+ assert math.isclose(result[0], expected[0])
99
+ assert math.isclose(result[1], expected[1])
100
+
86
101
  @pytest.mark.parametrize(
87
102
  "area,projection,coord,expected",
88
103
  [
@@ -119,6 +134,21 @@ def test_pixel_for_latlng(
119
134
  result = layer.pixel_for_latlng(*coord)
120
135
  assert result == expected
121
136
 
137
+ def test_pixel_for_latlng_on_operator() -> None:
138
+ layer1 = YirgacheffeLayer(
139
+ Area(-10, 10, 10, -10),
140
+ MapProjection("epsg:4326", 0.2, -0.2),
141
+ )
142
+ layer2 = YirgacheffeLayer(
143
+ Area(-10, 10, 10, -10),
144
+ MapProjection("epsg:4326", 0.2, -0.2),
145
+ )
146
+ calc = layer1 + layer2
147
+ result = calc.pixel_for_latlng(10, -10)
148
+ expected = (0, 0)
149
+ assert math.isclose(result[0], expected[0])
150
+ assert math.isclose(result[1], expected[1])
151
+
122
152
  @pytest.mark.parametrize(
123
153
  "area,window,projection,pixel,expected",
124
154
  [
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import builtins
3
4
  import logging
4
5
  import math
5
6
  import multiprocessing
@@ -21,6 +22,7 @@ import numpy as np
21
22
  import numpy.typing as npt
22
23
  from osgeo import gdal
23
24
  from dill import dumps, loads # type: ignore
25
+ from pyproj import Transformer
24
26
 
25
27
  from . import constants, __version__
26
28
  from .rounding import round_up_pixels, round_down_pixels
@@ -290,6 +292,102 @@ class LayerMathMixin:
290
292
  datatype=datatype
291
293
  )
292
294
 
295
+ def latlng_for_pixel(self, x: int, y: int) -> tuple[float, float]:
296
+ """Get geo coords for pixel. This is relative to the set view window.
297
+
298
+ Args:
299
+ x: X axis position within raster
300
+ y: Y axis position within raster
301
+
302
+ Returns:
303
+ A tuple containing the (latitude, longitude).
304
+ """
305
+ projection = self.map_projection # type: ignore[attr-defined]
306
+ area = self.area # type: ignore[attr-defined]
307
+ if projection is None:
308
+ raise ValueError("Map has not projection space")
309
+ pixel_scale = projection.scale
310
+ coord_in_raster_space = (
311
+ (y * pixel_scale.ystep) + area.top,
312
+ (x * pixel_scale.xstep) + area.left,
313
+ )
314
+ transformer = Transformer.from_crs(projection.name, "EPSG:4326")
315
+ return transformer.transform(*coord_in_raster_space)
316
+
317
+ def pixel_for_latlng(self, lat: float, lng: float) -> tuple[int, int]:
318
+ """Get pixel for geo coords. This is relative to the set view window.
319
+ Result is rounded down to nearest pixel.
320
+
321
+ Args:
322
+ lat: Geospatial latitude in WGS84
323
+ lng: Geospatial longitude in WGS84
324
+
325
+ Returns:
326
+ A tuple containing the x, y coordinates in pixel space.
327
+ """
328
+ projection = self.map_projection # type: ignore[attr-defined]
329
+ area = self.area # type: ignore[attr-defined]
330
+ if projection is None:
331
+ raise ValueError("Map has not projection space")
332
+
333
+ transformer = Transformer.from_crs("EPSG:4326", projection.name)
334
+ x, y = transformer.transform(lng,lat)
335
+
336
+ pixel_scale = projection.scale
337
+ return (
338
+ round_down_pixels((x - area.left) / pixel_scale.xstep, builtins.abs(pixel_scale.xstep)),
339
+ round_down_pixels((y - area.top) / pixel_scale.ystep, builtins.abs(pixel_scale.ystep)),
340
+ )
341
+
342
+ @property
343
+ def window(self) -> Window:
344
+ raise NotImplementedError("Must be overridden by subclass")
345
+
346
+ @property
347
+ def area(self) -> Area:
348
+ raise NotImplementedError("Must be overridden by subclass")
349
+
350
+ def read_array(self, _x, _y, _w, _h):
351
+ raise NotImplementedError("Must be overridden by subclass")
352
+
353
+ def show(self, ax=None, max_pixels: int | None =1000, **kwargs):
354
+ """Display data using matplotlib.
355
+
356
+ Args:
357
+ ax: Matplotlib axes object. If not provided, the default matplotlib context will be used.
358
+ max_pixels: How many pixels to downsample to. If None, raw pixels will be used.
359
+ **kwargs: Passed to matplotlib imshow.
360
+
361
+ Returns:
362
+ A matplotlib image.
363
+ """
364
+ import matplotlib.pyplot as plt # pylint: disable=C0415
365
+
366
+ if ax is None:
367
+ ax = plt.gca()
368
+
369
+ window = self.window
370
+
371
+ raw_data = self.read_array(
372
+ 0,
373
+ 0,
374
+ window.xsize,
375
+ window.ysize
376
+ )
377
+ if max_pixels:
378
+ downsample = max(window.xsize, window.ysize) // max_pixels
379
+ data = raw_data[::downsample,::downsample]
380
+ else:
381
+ data = raw_data
382
+ area = self.area
383
+ extent = [
384
+ area.left,
385
+ area.right,
386
+ area.bottom,
387
+ area.top,
388
+ ]
389
+ return ax.imshow(data, extent=extent, **kwargs)
390
+
293
391
 
294
392
  class LayerOperation(LayerMathMixin):
295
393
 
@@ -334,6 +432,7 @@ class LayerOperation(LayerMathMixin):
334
432
  self.kwargs = kwargs
335
433
  self.window_op = window_op
336
434
  self.buffer_padding = buffer_padding
435
+ self._forced_area = None
337
436
 
338
437
  if lhs is None:
339
438
  raise ValueError("LHS on operation should not be none")
@@ -401,6 +500,9 @@ class LayerOperation(LayerMathMixin):
401
500
  return self._get_operation_area(self.map_projection)
402
501
 
403
502
  def _get_operation_area(self, projection: MapProjection | None) -> Area:
503
+ if self._forced_area is not None:
504
+ return self._forced_area
505
+
404
506
  lhs_area = self.lhs._get_operation_area(projection)
405
507
  try:
406
508
  rhs_area = self.rhs._get_operation_area(projection)
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
  from typing import Any, Sequence
3
3
 
4
4
  import deprecation
5
- from pyproj import Transformer
6
5
 
7
6
  from .. import __version__
8
7
  from .._operators import LayerMathMixin
@@ -332,48 +331,3 @@ class YirgacheffeLayer(LayerMathMixin):
332
331
  """
333
332
  res = self._read_array(x, y, width, height)
334
333
  return backend.demote_array(res)
335
-
336
- def latlng_for_pixel(self, x: int, y: int) -> tuple[float, float]:
337
- """Get geo coords for pixel. This is relative to the set view window.
338
-
339
- Args:
340
- x: X axis position within raster
341
- y: Y axis position within raster
342
-
343
- Returns:
344
- A tuple containing the (latitude, longitude).
345
- """
346
- projection = self.map_projection
347
- if projection is None:
348
- raise ValueError("Map has not projection space")
349
- pixel_scale = projection.scale
350
- coord_in_raster_space = (
351
- (y * pixel_scale.ystep) + self.area.top,
352
- (x * pixel_scale.xstep) + self.area.left,
353
- )
354
- transformer = Transformer.from_crs(projection.name, "EPSG:4326")
355
- return transformer.transform(*coord_in_raster_space)
356
-
357
- def pixel_for_latlng(self, lat: float, lng: float) -> tuple[int, int]:
358
- """Get pixel for geo coords. This is relative to the set view window.
359
- Result is rounded down to nearest pixel.
360
-
361
- Args:
362
- lat: Geospatial latitude in WGS84
363
- lng: Geospatial longitude in WGS84
364
-
365
- Returns:
366
- A tuple containing the x, y coordinates in pixel space.
367
- """
368
- projection = self.map_projection
369
- if projection is None:
370
- raise ValueError("Map has not projection space")
371
-
372
- transformer = Transformer.from_crs("EPSG:4326", projection.name)
373
- x, y = transformer.transform(lng,lat)
374
-
375
- pixel_scale = projection.scale
376
- return (
377
- round_down_pixels((x - self.area.left) / pixel_scale.xstep, abs(pixel_scale.xstep)),
378
- round_down_pixels((y - self.area.top) / pixel_scale.ystep, abs(pixel_scale.ystep)),
379
- )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yirgacheffe
3
- Version: 1.9.0
3
+ Version: 1.9.2
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
@@ -30,6 +30,8 @@ Requires-Dist: h3
30
30
  Requires-Dist: pyproj
31
31
  Provides-Extra: mlx
32
32
  Requires-Dist: mlx; extra == "mlx"
33
+ Provides-Extra: matplotlib
34
+ Requires-Dist: matplotlib; extra == "matplotlib"
33
35
  Provides-Extra: dev
34
36
  Requires-Dist: mypy; extra == "dev"
35
37
  Requires-Dist: pylint; extra == "dev"
@@ -4,7 +4,6 @@ README.md
4
4
  pyproject.toml
5
5
  tests/test_area.py
6
6
  tests/test_auto_windowing.py
7
- tests/test_base.py
8
7
  tests/test_constants.py
9
8
  tests/test_datatypes.py
10
9
  tests/test_group.py
@@ -17,6 +16,7 @@ tests/test_operators.py
17
16
  tests/test_optimisation.py
18
17
  tests/test_parallel_operators.py
19
18
  tests/test_pickle.py
19
+ tests/test_pixel_coord.py
20
20
  tests/test_projection.py
21
21
  tests/test_raster.py
22
22
  tests/test_rescaling.py
@@ -20,5 +20,8 @@ mkdocstrings-python
20
20
  mike
21
21
  mkdocs-gen-files
22
22
 
23
+ [matplotlib]
24
+ matplotlib
25
+
23
26
  [mlx]
24
27
  mlx
File without changes
File without changes
File without changes
File without changes