kuva-reader 1.1.1__tar.gz → 1.1.3__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 kuva-reader might be problematic. Click here for more details.

@@ -0,0 +1,134 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ *.ipynb
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *.cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+
51
+ # Translations
52
+ *.mo
53
+ *.pot
54
+
55
+ # Django stuff:
56
+ *.log
57
+ local_settings.py
58
+ db.sqlite3
59
+
60
+ # Flask stuff:
61
+ instance/
62
+ .webassets-cache
63
+
64
+ # Torch stuff
65
+ lightning_logs/
66
+
67
+ # Scrapy stuff:
68
+ .scrapy
69
+
70
+ # Sphinx documentation
71
+ docs/_build/
72
+
73
+ # PyBuilder
74
+ target/
75
+
76
+ # Jupyter Notebook
77
+ .ipynb_checkpoints
78
+
79
+ # VSCode
80
+ .vscode
81
+
82
+ # pyenv
83
+ .python-version
84
+
85
+ # celery beat schedule file
86
+ celerybeat-schedule
87
+
88
+ # SageMath parsed files
89
+ *.sage.py
90
+
91
+ # Environments
92
+ .env
93
+ .venv
94
+ env/
95
+ venv/
96
+ ENV/
97
+ env.bak/
98
+ venv.bak/
99
+
100
+ # Spyder project settings
101
+ .spyderproject
102
+ .spyproject
103
+
104
+ # Rope project settings
105
+ .ropeproject
106
+
107
+ # mkdocs documentation
108
+ /site
109
+
110
+ # mypy
111
+ .mypy_cache/
112
+
113
+ # Direnv stuff
114
+ .direnv/
115
+ .envrc
116
+
117
+ # Poetry
118
+ # poetry.lock
119
+
120
+ # Supervisord
121
+ supervisord.log
122
+ supervisord.pid
123
+
124
+ # Debug folders
125
+ _debug/
126
+
127
+ # Kuva data
128
+ *.tif
129
+ *.tiff
130
+ *.npy
131
+ hyperfield*.json
132
+
133
+ # Do not ignore Kuva files in the test_data directory
134
+ !kuva-reader/tests/test_data/**
@@ -1,22 +1,16 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: kuva-reader
3
- Version: 1.1.1
3
+ Version: 1.1.3
4
4
  Summary: Manipulate the Kuva Space image and metadata formats
5
- License: MIT
6
- Author: Guillem Ballesteros
7
- Author-email: guillem@kuvaspace.com
8
- Requires-Python: >=3.10,<=3.13
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Programming Language :: Python :: 3.12
14
- Requires-Dist: kuva-geometry (>=1.0.1,<2.0.0)
15
- Requires-Dist: kuva-metadata (>=1.1.1,<2.0.0)
16
- Requires-Dist: numpy (>=1.26.4,<2.0.0)
17
- Requires-Dist: numpy-quaternion (>=2022.4.4,<2023.0.0)
18
- Requires-Dist: pint (>=0.22,<0.23)
19
- Requires-Dist: rasterio (>=1.4.1,<2.0.0)
5
+ Author-email: Guillem Ballesteros <guillem@kuvaspace.com>, Lennert Antson <lennert.antson@kuvaspace.com>, Arthur Vandenhoeke <arthur.vandenhoeke@kuvaspace.com>, Olli Eloranta <olli.eloranta@kuvaspace.com>
6
+ License-Expression: MIT
7
+ Requires-Python: <=3.13,>=3.10
8
+ Requires-Dist: kuva-geometry<2.0.0,>=1.0.1
9
+ Requires-Dist: kuva-metadata<2.0.0,>=1.1.1
10
+ Requires-Dist: numpy-quaternion>=2023.4.4
11
+ Requires-Dist: numpy>=1.26.4
12
+ Requires-Dist: pint<1.0.0,>=0.22
13
+ Requires-Dist: rasterio<2,>=1.4.3
20
14
  Description-Content-Type: text/markdown
21
15
 
22
16
  <div align="center">
@@ -128,4 +122,3 @@ The `kuva-reader` project software is under the [MIT license](https://github.com
128
122
  # Status of unit tests
129
123
 
130
124
  [![Unit tests for kuva-reader](https://github.com/KuvaSpace/kuva-data-processing/actions/workflows/test-kuva-reader.yml/badge.svg)](https://github.com/KuvaSpace/kuva-data-processing/actions/workflows/test-kuva-reader.yml)
131
-
@@ -22,7 +22,7 @@ Dependencies
22
22
  visualization.
23
23
  """
24
24
 
25
- __version__ = "0.1.0"
25
+ __version__ = "1.1.2"
26
26
 
27
27
  from .reader.image import (
28
28
  image_footprint,
@@ -61,11 +61,12 @@ class Level0Product(ProductBase[MetadataLevel0]):
61
61
  ) -> None:
62
62
  super().__init__(image_path, metadata, target_ureg)
63
63
 
64
- self.images = {
64
+ self._images = {
65
65
  camera: cast(
66
66
  rio.DatasetReader,
67
67
  rio.open(
68
68
  self.image_path / (cube.camera.name + ".tif"),
69
+ num_threads='16',
69
70
  ),
70
71
  )
71
72
  for camera, cube in self.metadata.image.data_cubes.items() # type: ignore
@@ -97,6 +98,12 @@ class Level0Product(ProductBase[MetadataLevel0]):
97
98
  """Return the datarray for the chosen camera."""
98
99
  return self.images[camera]
99
100
 
101
+ @property
102
+ def images(self) -> dict[str, rio.DatasetReader]:
103
+ if self._images is None:
104
+ raise RuntimeError("Images has been released.")
105
+ return self._images
106
+
100
107
  def keys(self) -> list[str]:
101
108
  """Easy access to the camera keys."""
102
109
  return list(self.images.keys())
@@ -230,11 +237,14 @@ class Level0Product(ProductBase[MetadataLevel0]):
230
237
  """Explicitely closes the Rasterio DatasetReaders and releases the memory of
231
238
  the `images` variable.
232
239
  """
233
- for k in self.images.keys():
234
- self.images[k].close()
235
-
236
- del self.images
237
- self.images = None
240
+ if self._images is not None:
241
+ for k in self._images.keys():
242
+ self._images[k].close()
243
+
244
+ del self._images
245
+ # We know that images are not None as long as somebody doesn't call
246
+ # this function beforehand....
247
+ self._images = None
238
248
 
239
249
 
240
250
  def generate_level_0_metafile():
@@ -50,9 +50,9 @@ class Level1ABProduct(ProductBase[MetadataLevel1AB]):
50
50
  ) -> None:
51
51
  super().__init__(image_path, metadata, target_ureg)
52
52
 
53
- self.image = cast(
53
+ self._image = cast(
54
54
  rio.DatasetReader,
55
- rio.open(self.image_path / "L1B.tif"),
55
+ rio.open(self.image_path / "L1B.tif", num_threads='16'),
56
56
  )
57
57
 
58
58
  self.data_tags = self.image.tags()
@@ -73,6 +73,12 @@ class Level1ABProduct(ProductBase[MetadataLevel1AB]):
73
73
  else:
74
74
  return f"{self.__class__.__name__} loaded from '{self.image_path}'"
75
75
 
76
+ @property
77
+ def image(self) -> rio.DatasetReader:
78
+ if self._image is None:
79
+ raise RuntimeError("Images has been released.")
80
+ return self._image
81
+
76
82
  def footprint(self, crs="") -> Polygon:
77
83
  """The product footprint as a Shapely polygon."""
78
84
  return image_footprint(self.image, crs)
@@ -130,8 +136,35 @@ class Level1ABProduct(ProductBase[MetadataLevel1AB]):
130
136
  """Explicitely closes the Rasterio DatasetReader and releases the memory of
131
137
  the `image` variable.
132
138
  """
133
- del self.image
134
- self.image = None
139
+ if self._image is not None:
140
+ self._image.close()
141
+ del self._image
142
+ self._image = None
143
+
144
+ def generate_metadata_file(self) -> None:
145
+ """Write the sidecar files next to the product."""
146
+ metadata_file_name = self.image_path.name + ".json"
147
+
148
+ with rio.open(self.image_path / "L1B.tif") as src:
149
+ shape = (src.height, src.width)
150
+ crs_epsg = src.crs.to_epsg()
151
+ geotransform = src.transform
152
+ gsd_w, gsd_h = src.res
153
+
154
+
155
+ with (self.image_path / metadata_file_name).open("w") as fh:
156
+ fh.write(
157
+ self.metadata.model_dump_json(
158
+ indent=2,
159
+ context={
160
+ "shape": shape,
161
+ "epsg": crs_epsg,
162
+ "transform": geotransform,
163
+ "gsd_w": gsd_w,
164
+ "gsd_h": gsd_h,
165
+ },
166
+ )
167
+ )
135
168
 
136
169
 
137
170
  class Level1CProduct(ProductBase[MetadataLevel1C]):
@@ -170,9 +203,9 @@ class Level1CProduct(ProductBase[MetadataLevel1C]):
170
203
  ) -> None:
171
204
  super().__init__(image_path, metadata, target_ureg)
172
205
 
173
- self.image = cast(
206
+ self._image = cast(
174
207
  rio.DatasetReader,
175
- rio.open(self.image_path / "L1C.tif"),
208
+ rio.open(self.image_path / "L1C.tif", num_threads='16'),
176
209
  )
177
210
  self.data_tags = self.image.tags()
178
211
 
@@ -193,6 +226,12 @@ class Level1CProduct(ProductBase[MetadataLevel1C]):
193
226
  else:
194
227
  return f"{self.__class__.__name__} loaded from '{self.image_path}'"
195
228
 
229
+ @property
230
+ def image(self) -> rio.DatasetReader:
231
+ if self._image is None:
232
+ raise RuntimeError("Images has been released.")
233
+ return self._image
234
+
196
235
  def footprint(self, crs="") -> Polygon:
197
236
  """The product footprint as a Shapely polygon."""
198
237
  return image_footprint(self.image, crs)
@@ -231,9 +270,35 @@ class Level1CProduct(ProductBase[MetadataLevel1C]):
231
270
  """Explicitely closes the Rasterio DatasetReader and releases the memory of
232
271
  the `image` variable.
233
272
  """
234
- self.image.close()
235
- del self.image
236
- self.image = None
273
+ if self._image is not None:
274
+ self._image.close()
275
+ del self._image
276
+ self._image = None
277
+
278
+ def generate_metadata_file(self) -> None:
279
+ """Write the sidecar files next to the product."""
280
+ metadata_file_name = self.image_path.name + ".json"
281
+
282
+ with rio.open(self.image_path / "L1C.tif") as src:
283
+ shape = (src.height, src.width)
284
+ crs_epsg = src.crs.to_epsg()
285
+ geotransform = src.transform
286
+ gsd_w, gsd_h = src.res
287
+
288
+
289
+ with (self.image_path / metadata_file_name).open("w") as fh:
290
+ fh.write(
291
+ self.metadata.model_dump_json(
292
+ indent=2,
293
+ context={
294
+ "shape": shape,
295
+ "epsg": crs_epsg,
296
+ "transform": geotransform,
297
+ "gsd_w": gsd_w,
298
+ "gsd_h": gsd_h,
299
+ },
300
+ )
301
+ )
237
302
 
238
303
 
239
304
  def generate_level_1_metafile():
@@ -47,9 +47,9 @@ class Level2AProduct(ProductBase[MetadataLevel2A]):
47
47
  ) -> None:
48
48
  super().__init__(image_path, metadata, target_ureg)
49
49
 
50
- self.image = cast(
50
+ self._image = cast(
51
51
  rio.DatasetReader,
52
- rio.open(self.image_path / "L2A.tif"),
52
+ rio.open(self.image_path / "L2A.tif", num_threads='16'),
53
53
  )
54
54
  self.data_tags = self.image.tags()
55
55
 
@@ -70,6 +70,12 @@ class Level2AProduct(ProductBase[MetadataLevel2A]):
70
70
  else:
71
71
  return f"{self.__class__.__name__} loaded from '{self.image_path}'"
72
72
 
73
+ @property
74
+ def image(self) -> rio.DatasetReader:
75
+ if self._image is None:
76
+ raise RuntimeError("Images has been released.")
77
+ return self._image
78
+
73
79
  def footprint(self, crs="") -> Polygon:
74
80
  """The product footprint as a Shapely polygon."""
75
81
  return image_footprint(self.image, crs)
@@ -108,9 +114,35 @@ class Level2AProduct(ProductBase[MetadataLevel2A]):
108
114
  """Explicitely closes the Rasterio DatasetReader and releases the memory of
109
115
  the `image` variable.
110
116
  """
111
- self.image.close()
112
- del self.image
113
- self.image = None
117
+ if self._image is not None:
118
+ self._image.close()
119
+ del self._image
120
+ self._image = None
121
+
122
+ def generate_metadata_file(self) -> None:
123
+ """Write the sidecar files next to the product."""
124
+ metadata_file_name = self.image_path.name + ".json"
125
+
126
+ with rio.open(self.image_path / "L2A.tif") as src:
127
+ shape = (src.height, src.width)
128
+ crs_epsg = src.crs.to_epsg()
129
+ geotransform = src.transform
130
+ gsd_w, gsd_h = src.res
131
+
132
+
133
+ with (self.image_path / metadata_file_name).open("w") as fh:
134
+ fh.write(
135
+ self.metadata.model_dump_json(
136
+ indent=2,
137
+ context={
138
+ "shape": shape,
139
+ "epsg": crs_epsg,
140
+ "transform": geotransform,
141
+ "gsd_w": gsd_w,
142
+ "gsd_h": gsd_h,
143
+ },
144
+ )
145
+ )
114
146
 
115
147
 
116
148
  def generate_level_2_metafile():
@@ -35,7 +35,7 @@ class ProductBase(Generic[TMetadata], metaclass=ABCMeta):
35
35
  def __init__(
36
36
  self,
37
37
  image_path: Path,
38
- metadata: MetadataBase | None = None,
38
+ metadata: TMetadata | None = None,
39
39
  target_ureg: UnitRegistry | None = None,
40
40
  ):
41
41
  self.image_path = Path(image_path)