kuva-reader 1.0.3__py3-none-any.whl → 1.0.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 kuva-reader might be problematic. Click here for more details.

kuva_reader/__init__.py CHANGED
@@ -28,6 +28,7 @@ from .reader.image import (
28
28
  image_to_dtype_range,
29
29
  image_to_original_range,
30
30
  image_to_uint16_range,
31
+ image_footprint,
31
32
  )
32
33
  from .reader.level0 import Level0Product
33
34
  from .reader.level1 import Level1ABProduct, Level1CProduct
@@ -42,5 +43,6 @@ __all__ = [
42
43
  "image_to_dtype_range",
43
44
  "image_to_original_range",
44
45
  "image_to_uint16_range",
46
+ "image_footprint",
45
47
  "read_product",
46
48
  ]
@@ -4,11 +4,38 @@ from typing import cast, overload
4
4
 
5
5
  import numpy as np
6
6
  import xarray
7
+ from shapely.geometry import box
8
+ from pyproj import Transformer
9
+ from shapely import Polygon
7
10
 
8
11
  # Helper type for image processing purposes. The same operations work both for EO
9
12
  # DataArrays and Numpy arrays.
10
13
  ImageArray_ = np.ndarray | xarray.DataArray
11
14
 
15
+ def image_footprint(image: xarray.DataArray, crs: str = "") -> Polygon:
16
+ """Return a product footprint as a shapely polygon
17
+
18
+ Parameters
19
+ ----------
20
+ image
21
+ The product image
22
+ crs, optional
23
+ CRS to convert to, by default "", keeping the image's CRS
24
+
25
+ Returns
26
+ -------
27
+ A shapely polygon footprint
28
+ """
29
+ if crs:
30
+ transformer = Transformer.from_crs(image.rio.crs, crs, always_xy=True)
31
+ bounds = image.rio.bounds()
32
+ minx, miny = transformer.transform(bounds[0], bounds[1])
33
+ maxx, maxy = transformer.transform(bounds[2], bounds[3])
34
+ footprint = box(minx, miny, maxx, maxy)
35
+ else:
36
+ footprint = box(*image.rio.bounds())
37
+ return footprint
38
+
12
39
 
13
40
  @overload
14
41
  def image_to_dtype_range(
@@ -6,8 +6,9 @@ import rioxarray as rx
6
6
  import xarray
7
7
  from kuva_metadata import MetadataLevel0
8
8
  from pint import UnitRegistry
9
+ from shapely import Polygon
9
10
 
10
- from kuva_reader import image_to_dtype_range, image_to_original_range
11
+ from kuva_reader import image_to_dtype_range, image_to_original_range, image_footprint
11
12
 
12
13
  from .product_base import ProductBase
13
14
 
@@ -80,6 +81,7 @@ class Level0Product(ProductBase[MetadataLevel0]):
80
81
  )
81
82
  for camera, cube in self.metadata.image.data_cubes.items() # type: ignore
82
83
  }
84
+ self.crs = self.images[list(self.images.keys())[0]].rio.crs
83
85
 
84
86
  # Read tags for images and denormalize / renormalize if needed
85
87
  self.data_tags = {camera: img.attrs for camera, img in self.images.items()}
@@ -106,9 +108,9 @@ class Level0Product(ProductBase[MetadataLevel0]):
106
108
  if self.images is not None and len(self.images):
107
109
  return (
108
110
  f"{self.__class__.__name__}"
109
- f"with {len(self.images)} frames of shape {self.images[0].shape} "
110
- f"and CRS '{self.images[0].rio.crs}'. "
111
- f"Loaded from: '{self.image_path}'."
111
+ f"with VIS shape {self.images['vis'].shape} "
112
+ f"and NIR shape {self.images['nir'].shape} "
113
+ f"(CRS '{self.crs}'). Loaded from: '{self.image_path}'."
112
114
  )
113
115
  else:
114
116
  return f"{self.__class__.__name__} loaded from '{self.image_path}'."
@@ -121,6 +123,10 @@ class Level0Product(ProductBase[MetadataLevel0]):
121
123
  """Easy access to the camera keys."""
122
124
  return list(self.images.keys())
123
125
 
126
+ def footprint(self, crs="") -> Polygon:
127
+ """The product footprint as a Shapely polygon."""
128
+ return image_footprint(self.images["vis"], crs)
129
+
124
130
  def _get_data_from_sidecar(
125
131
  self, sidecar_path: Path, target_ureg: UnitRegistry | None = None
126
132
  ) -> MetadataLevel0:
@@ -3,8 +3,10 @@ from typing import cast
3
3
 
4
4
  import rioxarray as rx
5
5
  import xarray
6
+ from kuva_reader import image_footprint
6
7
  from kuva_metadata import MetadataLevel1AB, MetadataLevel1C
7
8
  from pint import UnitRegistry
9
+ from shapely import Polygon
8
10
  from xarray import Dataset
9
11
 
10
12
  from .product_base import ProductBase
@@ -56,6 +58,25 @@ class Level1ABProduct(ProductBase[MetadataLevel1AB]):
56
58
  rx.open_rasterio(self.image_path / "L1B.tif"),
57
59
  )
58
60
  self.data_tags = self.image.attrs
61
+ self.wavelengths = [
62
+ b.wavelength.to("nm").magnitude for b in self.metadata.image.bands
63
+ ]
64
+ self.crs = self.image.rio.crs
65
+
66
+ def __repr__(self):
67
+ """Pretty printing of the object with the most important info"""
68
+ if self.image is not None:
69
+ return (
70
+ f"{self.__class__.__name__} with shape {self.image.shape} "
71
+ f"and wavelengths {self.wavelengths} (CRS: '{self.crs}'). "
72
+ f"Loaded from: '{self.image_path}'."
73
+ )
74
+ else:
75
+ return f"{self.__class__.__name__} loaded from '{self.image_path}'"
76
+
77
+ def footprint(self, crs="") -> Polygon:
78
+ """The product footprint as a Shapely polygon."""
79
+ return image_footprint(self.image, crs)
59
80
 
60
81
  def _get_data_from_sidecar(
61
82
  self, sidecar_path: Path, target_ureg: UnitRegistry | None = None
@@ -163,18 +184,23 @@ class Level1CProduct(ProductBase[MetadataLevel1C]):
163
184
  self.wavelengths = [
164
185
  b.wavelength.to("nm").magnitude for b in self.metadata.image.bands
165
186
  ]
187
+ self.crs = self.image.rio.crs
166
188
 
167
189
  def __repr__(self):
168
190
  """Pretty printing of the object with the most important info"""
169
191
  if self.image is not None:
170
192
  return (
171
193
  f"{self.__class__.__name__} with shape {self.image.shape} "
172
- f"and wavelengths {self.wavelengths} (CRS: '{self.image.rio.crs}'). "
194
+ f"and wavelengths {self.wavelengths} (CRS: '{self.crs}'). "
173
195
  f"Loaded from: '{self.image_path}'."
174
196
  )
175
197
  else:
176
198
  return f"{self.__class__.__name__} loaded from '{self.image_path}'"
177
199
 
200
+ def footprint(self, crs="") -> Polygon:
201
+ """The product footprint as a Shapely polygon."""
202
+ return image_footprint(self.image, crs)
203
+
178
204
  def _get_data_from_sidecar(
179
205
  self, sidecar_path: Path, target_ureg: UnitRegistry | None = None
180
206
  ) -> MetadataLevel1C:
@@ -2,8 +2,10 @@ from pathlib import Path
2
2
  from typing import cast
3
3
 
4
4
  import rioxarray as rx
5
+ from kuva_reader import image_footprint
5
6
  from kuva_metadata import MetadataLevel2A
6
7
  from pint import UnitRegistry
8
+ from shapely import Polygon
7
9
  from xarray import Dataset
8
10
 
9
11
  from .product_base import ProductBase
@@ -55,18 +57,23 @@ class Level2AProduct(ProductBase[MetadataLevel2A]):
55
57
  self.wavelengths = [
56
58
  b.wavelength.to("nm").magnitude for b in self.metadata.image.bands
57
59
  ]
60
+ self.crs = self.image.rio.crs
58
61
 
59
62
  def __repr__(self):
60
63
  """Pretty printing of the object with the most important info"""
61
64
  if self.image is not None:
62
65
  return (
63
66
  f"{self.__class__.__name__} with shape {self.image.shape} "
64
- f"and wavelengths {self.wavelengths} (CRS: '{self.image.rio.crs}'). "
67
+ f"and wavelengths {self.wavelengths} (CRS: '{self.crs}'). "
65
68
  f"Loaded from: '{self.image_path}'."
66
69
  )
67
70
  else:
68
71
  return f"{self.__class__.__name__} loaded from '{self.image_path}'"
69
72
 
73
+ def footprint(self, crs="") -> Polygon:
74
+ """The product footprint as a Shapely polygon."""
75
+ return image_footprint(self.image, crs)
76
+
70
77
  def _get_data_from_sidecar(
71
78
  self, sidecar_path: Path, target_ureg: UnitRegistry | None = None
72
79
  ) -> MetadataLevel2A:
@@ -25,7 +25,7 @@ def read_product(
25
25
 
26
26
  product_map = {
27
27
  "L0": Level0Product,
28
- "L1AB": Level1ABProduct,
28
+ "L1B": Level1ABProduct,
29
29
  "L1C": Level1CProduct,
30
30
  "L2A": Level2AProduct,
31
31
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kuva-reader
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: Manipulate the Kuva Space image and metadata formats
5
5
  License: MIT
6
6
  Author: Guillem Ballesteros
@@ -61,8 +61,8 @@ The loaded product is stored in a `rioxarray` object, which contains extensive G
61
61
  ```python
62
62
  from kuva_reader import read_product
63
63
 
64
- l2a_product = read_product("my_data_folder/hyperfield1a_L2A_20250105T092548")
65
- print(l2a_product) # Will show some main information such as image shape and CRS
64
+ product = read_product("my_data_folder/hyperfield1a_L2A_20250105T092548")
65
+ print(product) # Will show some main information such as image shape and CRS
66
66
  ```
67
67
 
68
68
  This assumes a mostly untouched folder after distributing. Otherwise, you may need to
@@ -75,7 +75,21 @@ l2a_product = Level2AProduct("your/l2a/folder")
75
75
  ```
76
76
 
77
77
  The actual raster image is stored and can be analysed in `product.image`, while metadata
78
- information of the product is in `product.metadata`.
78
+ information of the product is in `product.metadata`.
79
+
80
+ ## Other tips
81
+
82
+ The product object attributes and methods allow the retrieval of other interesting information as well:
83
+
84
+ ```python
85
+ from kuva_reader import read_product
86
+
87
+ product = read_product("your/product/folder")
88
+ product.footprint(crs="EPSG:4326") # Footprint with option to transform CRS
89
+ product.image.shape # The image attribute contains all the image data
90
+ product.wavelengths # Wavelengths corresponding to image bands
91
+ product.crs # CRS
92
+ ```
79
93
 
80
94
  ## Processing levels
81
95
 
@@ -0,0 +1,15 @@
1
+ kuva_reader/__init__.py,sha256=CdpOxA1S2rbwwOZX6oNwfDeta_DoiIayEMhm9tDLizA,1553
2
+ kuva_reader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ kuva_reader/reader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ kuva_reader/reader/image.py,sha256=-3Wsng_is0WRek2Fzi2UY3NCtO6PLsBkFFWTI9fC7xE,5468
5
+ kuva_reader/reader/level0.py,sha256=DyiHRWz_lj2gbxkt3SqYvEaPPo_Sg6Fz7dP4-4XW1qQ,10646
6
+ kuva_reader/reader/level1.py,sha256=VR2s9U4BiA2k8ZvRlbiPhh8nnBIrM82rgwVKN4J8ZVE,8836
7
+ kuva_reader/reader/level2.py,sha256=M6QnlWwb0FdtlBuhn0Ibe9hIdoXT-RFa9p9PSd4ZFMA,4338
8
+ kuva_reader/reader/product_base.py,sha256=_O96DACi2bWEXhNJVuIU328RJ1y1gs7_rMyyEaMo4aA,4294
9
+ kuva_reader/reader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ kuva_reader/reader/read.py,sha256=b5hGFbeJ-7sg-iilSJn29Hcqj4e7kiWT2S0BH2JwcDE,1761
11
+ kuva_reader/reader/utils.py,sha256=oZ1G43nm8lCxzfbdqBs0KhKaXWe_uf-78uBXWvmZigs,1566
12
+ kuva_reader-1.0.4.dist-info/METADATA,sha256=kCQLBjP1xD8QiASHd2xgFsSAbkdL-viFjsCWZ9c_rM4,5053
13
+ kuva_reader-1.0.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
14
+ kuva_reader-1.0.4.dist-info/entry_points.txt,sha256=YJysY9EChfOb1W_IEht2oE5Z8Y9jA0J6-kofaPv-NdI,149
15
+ kuva_reader-1.0.4.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- kuva_reader/__init__.py,sha256=qqsgtVNCkqXFuXOXf5v51ZT_Dcipj1ty0mGUw8fU3sU,1509
2
- kuva_reader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- kuva_reader/reader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- kuva_reader/reader/image.py,sha256=Ep54Tzila3hKF2I_u-54xnFUf8WqP2fiKAJnbyZxasA,4647
5
- kuva_reader/reader/level0.py,sha256=-ONYaBH9YFHGaFvJDiDRlmXUWTYPQyQt0XZeuv5sEL4,10370
6
- kuva_reader/reader/level1.py,sha256=V1gNjz9y185nCJkYpQVcXrmAOQVMaFwIPeIhQJBzaI4,7815
7
- kuva_reader/reader/level2.py,sha256=lyEbVSLkBxz32ZIEnJih4ln-F_d8h6FTOu1Npt7VHg0,4091
8
- kuva_reader/reader/product_base.py,sha256=_O96DACi2bWEXhNJVuIU328RJ1y1gs7_rMyyEaMo4aA,4294
9
- kuva_reader/reader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- kuva_reader/reader/read.py,sha256=l2x-vlOLP8AUMzqSjHLaT7xt0YJtYUjiYWm6btV-rqo,1762
11
- kuva_reader/reader/utils.py,sha256=oZ1G43nm8lCxzfbdqBs0KhKaXWe_uf-78uBXWvmZigs,1566
12
- kuva_reader-1.0.3.dist-info/METADATA,sha256=XDxxOJM_4pUzFthDkE8vLYlLTbXtSCRzkOvCCTxYECs,4612
13
- kuva_reader-1.0.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
14
- kuva_reader-1.0.3.dist-info/entry_points.txt,sha256=YJysY9EChfOb1W_IEht2oE5Z8Y9jA0J6-kofaPv-NdI,149
15
- kuva_reader-1.0.3.dist-info/RECORD,,