eoml 0.9.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.
Files changed (47) hide show
  1. eoml/__init__.py +74 -0
  2. eoml/automation/__init__.py +7 -0
  3. eoml/automation/configuration.py +105 -0
  4. eoml/automation/dag.py +233 -0
  5. eoml/automation/experience.py +618 -0
  6. eoml/automation/tasks.py +825 -0
  7. eoml/bin/__init__.py +6 -0
  8. eoml/bin/clean_checkpoint.py +146 -0
  9. eoml/bin/land_cover_mapping_toml.py +435 -0
  10. eoml/bin/mosaic_images.py +137 -0
  11. eoml/data/__init__.py +7 -0
  12. eoml/data/basic_geo_data.py +214 -0
  13. eoml/data/dataset_utils.py +98 -0
  14. eoml/data/persistence/__init__.py +7 -0
  15. eoml/data/persistence/generic.py +253 -0
  16. eoml/data/persistence/lmdb.py +379 -0
  17. eoml/data/persistence/serializer.py +82 -0
  18. eoml/raster/__init__.py +7 -0
  19. eoml/raster/band.py +141 -0
  20. eoml/raster/dataset/__init__.py +6 -0
  21. eoml/raster/dataset/extractor.py +604 -0
  22. eoml/raster/raster_reader.py +602 -0
  23. eoml/raster/raster_utils.py +116 -0
  24. eoml/torch/__init__.py +7 -0
  25. eoml/torch/cnn/__init__.py +7 -0
  26. eoml/torch/cnn/augmentation.py +150 -0
  27. eoml/torch/cnn/dataset_evaluator.py +68 -0
  28. eoml/torch/cnn/db_dataset.py +605 -0
  29. eoml/torch/cnn/map_dataset.py +579 -0
  30. eoml/torch/cnn/map_dataset_const_mem.py +135 -0
  31. eoml/torch/cnn/outputs_transformer.py +130 -0
  32. eoml/torch/cnn/torch_utils.py +404 -0
  33. eoml/torch/cnn/training_dataset.py +241 -0
  34. eoml/torch/cnn/windows_dataset.py +120 -0
  35. eoml/torch/dataset/__init__.py +6 -0
  36. eoml/torch/dataset/shade_dataset_tester.py +46 -0
  37. eoml/torch/dataset/shade_tree_dataset_creators.py +537 -0
  38. eoml/torch/model_low_use.py +507 -0
  39. eoml/torch/models.py +282 -0
  40. eoml/torch/resnet.py +437 -0
  41. eoml/torch/sample_statistic.py +260 -0
  42. eoml/torch/trainer.py +782 -0
  43. eoml/torch/trainer_v2.py +253 -0
  44. eoml-0.9.0.dist-info/METADATA +93 -0
  45. eoml-0.9.0.dist-info/RECORD +47 -0
  46. eoml-0.9.0.dist-info/WHEEL +4 -0
  47. eoml-0.9.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,602 @@
1
+ """Raster reader module for geospatial raster data access.
2
+
3
+ This module provides wrapper classes around rasterio for reading and processing
4
+ geotiff raster data. It supports reading single rasters and multiple aligned
5
+ rasters with different resolutions and coordinate systems.
6
+
7
+ The readers are designed to be thread-safe when properly configured and support
8
+ various operations like windowed reading, coordinate-based extraction, and
9
+ on-the-fly resampling.
10
+ """
11
+
12
+ import copy
13
+ import math
14
+ from abc import ABC, abstractmethod
15
+ from pathlib import Path
16
+ from typing import List, Union
17
+
18
+ import numpy
19
+ import rasterio
20
+ from rasterio.transform import TransformMethodsMixin
21
+ from rasterio.windows import Window, WindowMethodsMixin
22
+
23
+ from eoml import get_read_profile
24
+ from eoml.raster.band import Band
25
+
26
+ from rasterio.enums import Resampling
27
+
28
+ from eoml.raster.raster_utils import RasterInfo
29
+
30
+
31
+ def append_raster_reader(r_list,
32
+ reference_index: int = 0,
33
+ read_profile=None,
34
+ sharing=False):
35
+ """Combine multiple raster readers into a single MultiRasterReader.
36
+
37
+ Args:
38
+ r_list: List of RasterReader or MultiRasterReader objects to combine.
39
+ reference_index: Index of the reader to use as spatial reference. Defaults to 0.
40
+ read_profile: Rasterio read profile configuration. Defaults to None.
41
+ sharing: Whether to enable file sharing mode. Defaults to False.
42
+
43
+ Returns:
44
+ MultiRasterReader: Combined reader for all input rasters.
45
+ """
46
+
47
+ paths = []
48
+ bands = []
49
+ transformer = []
50
+ interpolation = []
51
+
52
+ for r in r_list:
53
+
54
+ if isinstance(r, RasterReader):
55
+ paths.append(r.path)
56
+ bands.append(r.bands_list)
57
+ transformer.append(r.transformer)
58
+ interpolation.append(r.interpolation)
59
+
60
+ else:
61
+ paths.extend(r.path)
62
+ bands.extend(r.bands_list)
63
+ transformer.extend(r.transformer)
64
+ interpolation.extend(r.interpolation)
65
+
66
+ return MultiRasterReader(paths, bands, transformer, interpolation, reference_index, read_profile, sharing)
67
+
68
+
69
+ class AbstractRasterReader(ABC):
70
+ """Abstract base class for raster readers.
71
+
72
+ Provides common interface for reading geotiff rasters with support for
73
+ band selection, transformation, and various read operations.
74
+
75
+ Warning:
76
+ This class should either be thread-safe or properly copied for parallel mapping.
77
+
78
+ Attributes:
79
+ bands_list: Band selection configuration.
80
+ transformer: Function to apply to read data.
81
+ interpolation: Resampling method for reading.
82
+ read_profile: Rasterio read configuration.
83
+ path: Path to raster file(s).
84
+ sharing: Whether file sharing is enabled.
85
+ src_info: Raster metadata information.
86
+ """
87
+
88
+ @abstractmethod
89
+ def __init__(self,
90
+ path,
91
+ bands_list: Band,
92
+ transformer,
93
+ interpolation=None,
94
+ read_profile=None,
95
+ sharing=False):
96
+ """Initialize abstract raster reader.
97
+
98
+ Args:
99
+ path: Path to raster file.
100
+ bands_list: Band configuration object.
101
+ transformer: Optional function to transform read data.
102
+ interpolation: Resampling method. Defaults to None (nearest neighbor).
103
+ read_profile: Rasterio read profile. Defaults to None.
104
+ sharing: Enable file sharing mode. Defaults to False.
105
+ """
106
+ self.bands_list = bands_list
107
+ self.transformer = transformer
108
+
109
+ if interpolation is None:
110
+ interpolation = Resampling.nearest
111
+
112
+ self.interpolation = interpolation
113
+ self.read_profile = read_profile
114
+ self.path = path
115
+
116
+ self.sharing = sharing
117
+
118
+ self.src_info = RasterInfo.from_file(path)
119
+
120
+ def __repr__(self):
121
+ return f"AbstractRasterReader({self.path}, {self.bands_list}, {self.transformer}, {self.sharing})"
122
+
123
+ def __copy__(self):
124
+ cls = self.__class__
125
+ result = cls.__new__(cls)
126
+ result.__init__(self.path, self.bands_list, self.transformer, self.interpolation, self.read_profile, self.sharing)
127
+
128
+ return result
129
+
130
+ def __deepcopy__(self, memo):
131
+ cls = self.__class__
132
+ result = cls.__new__(cls)
133
+
134
+ path = copy.deepcopy(self.path)
135
+ bands_list = copy.deepcopy(self.bands_list)
136
+ transformer = copy.deepcopy(self.transformer)
137
+ interpolation = copy.deepcopy(self.interpolation)
138
+ read_profile = copy.deepcopy(self.read_profile)
139
+ result.__init__(path, bands_list, transformer, interpolation, read_profile, self.sharing)
140
+
141
+ return result
142
+
143
+ @abstractmethod
144
+ def ref_raster(self):
145
+ pass
146
+
147
+ @abstractmethod
148
+ def ref_raster_info(self) -> RasterInfo:
149
+ pass
150
+
151
+ @abstractmethod
152
+ def read_windows(self, source_window):
153
+ pass
154
+ @abstractmethod
155
+ def read_windows_around_coordinate(self, center_x, center_y, size, op=math.floor):
156
+ pass
157
+
158
+ @abstractmethod
159
+ def read_bound(self, bounds):
160
+ pass
161
+
162
+ def is_inside(self, windows: Window):
163
+ """Check if a window is fully contained within the raster bounds.
164
+
165
+ Args:
166
+ windows: Rasterio window to check.
167
+
168
+ Returns:
169
+ bool: True if window is fully inside raster bounds, False otherwise.
170
+ """
171
+ return windows.row_off >= 0 and\
172
+ windows.col_off >= 0 and\
173
+ windows.col_off + windows.width <= self.ref_raster_info().width and\
174
+ windows.row_off + windows.height <= self.ref_raster_info().height
175
+
176
+ def windows_for_center(self, center_x, center_y, size, op=math.floor) -> Window:
177
+ """Compute rasterio window centered on specified coordinates.
178
+
179
+ By default uses floor operation, meaning the returned window contains
180
+ the pixel at the specified point.
181
+
182
+ Args:
183
+ center_x: X coordinate (longitude/easting) of center point.
184
+ center_y: Y coordinate (latitude/northing) of center point.
185
+ size: Size of window in pixels (square window).
186
+ op: Operation to apply when converting coordinates to pixels. Defaults to math.floor.
187
+
188
+ Returns:
189
+ Window: Rasterio window centered on the specified coordinates.
190
+ """
191
+ row, col = self.ref_raster_info().index(center_x, center_y, op=op)
192
+ radius = math.floor(size / 2)
193
+
194
+ col = col - radius
195
+ row = row - radius
196
+
197
+ return Window(col, row, size, size)
198
+
199
+
200
+ class RasterReader(AbstractRasterReader):
201
+ """Single raster file reader with band selection and transformation.
202
+
203
+ Wrapper around rasterio for reading geotiff files with support for
204
+ band selection, data transformation, and various reading modes.
205
+
206
+ Warning:
207
+ This class should either be thread-safe or properly copied for parallel mapping.
208
+
209
+ Attributes:
210
+ path: Path to the geotiff file.
211
+ bands_list: Band selection configuration.
212
+ transformer: Function to transform read data.
213
+ interpolation: Resampling method for reading.
214
+ read_profile: Rasterio read configuration.
215
+ n_band: Number of bands to read.
216
+ src_info: Raster metadata information.
217
+ sharing: Whether file sharing is enabled.
218
+ """
219
+
220
+ def __init__(self,
221
+ path: Path,
222
+ bands_list: Band,
223
+ transformer=None,
224
+ interpolation: Union[Resampling, None] = None,
225
+ read_profile=None,
226
+ sharing=False):
227
+ """Initialize single raster reader.
228
+
229
+ Args:
230
+ path: Path to the geotiff file.
231
+ bands_list: Band configuration object specifying which bands to read.
232
+ transformer: Optional function to apply to read data. Defaults to None.
233
+ interpolation: Resampling method for reading. Defaults to None (nearest neighbor).
234
+ read_profile: Rasterio read profile configuration. Defaults to None.
235
+ sharing: Enable file sharing mode. Defaults to False.
236
+ """
237
+
238
+ self.path = path
239
+
240
+ self.transformer = transformer
241
+
242
+ if interpolation is None:
243
+ interpolation = Resampling.nearest
244
+
245
+ self.interpolation = interpolation
246
+
247
+ if read_profile is None:
248
+ self.read_profile = get_read_profile()
249
+ else:
250
+ self.read_profile = read_profile
251
+
252
+ self.n_band = 0
253
+ self.bands_list = bands_list
254
+
255
+ if self.bands_list.selected is None:
256
+ with rasterio.open(path, 'r', **self.read_profile) as reader:
257
+ self.n_band = reader.count
258
+ else:
259
+ self.n_band = len(bands_list)
260
+
261
+ self.src_info = RasterInfo.from_file(path)
262
+
263
+ self.sharing = sharing
264
+ # self.reader = None
265
+
266
+ def __repr__(self):
267
+ return f"RasterReader({self.path}, {self.bands_list}, {self.transformer}, {self.sharing})"
268
+
269
+ def __enter__(self):
270
+ self.reader = rasterio.open(self.path, 'r', **self.read_profile, sharing= self.sharing)
271
+ return self
272
+
273
+ def __exit__(self, exc_type, exc_value, exc_tb):
274
+ self.reader.close()
275
+ # set the reader to none as we can not copy it
276
+ self.reader = None
277
+
278
+ def ref_raster(self):
279
+ """Get the path of the reference raster.
280
+
281
+ Returns:
282
+ str: Path to the raster file.
283
+ """
284
+ return self.path
285
+
286
+ def ref_raster_info(self):
287
+ """Get metadata information of the reference raster.
288
+
289
+ Returns:
290
+ RasterInfo: Raster metadata including CRS, transform, dimensions.
291
+ """
292
+ return self.src_info
293
+
294
+ def read_windows(self, source_window):
295
+ """Read data from a specific window of the raster.
296
+
297
+ Args:
298
+ source_window: Rasterio window defining the area to read.
299
+
300
+ Returns:
301
+ numpy.ndarray: Raster data for the specified window, optionally transformed.
302
+ """
303
+ if self.bands_list.selected1 is not None:
304
+ data = self.reader.read(self.bands_list.selected1, window=source_window, boundless=True)
305
+ else:
306
+ data = self.reader.read(window=source_window, boundless=True)
307
+
308
+ if self.transformer is not None:
309
+ self.transformer(data)
310
+
311
+ return data
312
+
313
+ def read_windows_around_coordinate(self, center_x, center_y, size, op=math.floor):
314
+ """Read a window centered on specified coordinates.
315
+
316
+ Args:
317
+ center_x: X coordinate (longitude/easting) of center point.
318
+ center_y: Y coordinate (latitude/northing) of center point.
319
+ size: Size of window in pixels (square window).
320
+ op: Operation to apply when converting coordinates to pixels. Defaults to math.floor.
321
+
322
+ Returns:
323
+ numpy.ndarray: Raster data for the window around the specified coordinates.
324
+ """
325
+ return self.read_windows(self.windows_for_center(center_x, center_y, size, op))
326
+
327
+ def read_bound(self, bounds):
328
+ """Read data from a bounding box.
329
+
330
+ Args:
331
+ bounds: Tuple of (left, bottom, right, top) coordinates.
332
+
333
+ Returns:
334
+ numpy.ndarray: Raster data for the specified bounding box.
335
+ """
336
+
337
+ windows = self.src_info.window(*bounds)
338
+ return self.read_windows(windows)
339
+
340
+
341
+ class MultiRasterReader(AbstractRasterReader):
342
+ """Multi-raster file reader for reading multiple aligned rasters.
343
+
344
+ Reads multiple raster files as a single combined dataset, with support for
345
+ different resolutions and coordinate systems. Automatically handles
346
+ reprojection and resampling to align all rasters to a reference grid.
347
+
348
+ Warning:
349
+ This class should either be thread-safe or properly copied for parallel mapping.
350
+
351
+ Todo:
352
+ - Check VRT implementation
353
+ - Improve handling of different projections
354
+
355
+ Attributes:
356
+ path: List of paths to geotiff files.
357
+ bands_list: List of band selection configurations for each raster.
358
+ transformer: List of transformation functions for each raster.
359
+ interpolation: List of resampling methods for each raster.
360
+ n_raster: Number of rasters being read.
361
+ reference_index: Index of the reference raster for spatial alignment.
362
+ reader: List of rasterio file readers.
363
+ src_info: List of raster metadata for each file.
364
+ read_profile: Rasterio read configuration.
365
+ n_band: Total number of bands across all rasters.
366
+ sharing: Whether file sharing is enabled.
367
+ """
368
+
369
+ def __init__(self, paths: List[str],
370
+ bands_list: Union[List[Band], None],
371
+ transformer,
372
+ interpolation: Union[List[Union[Resampling, None]], None] = None,
373
+ reference_index: int = 0,
374
+ read_profile=None,
375
+ sharing = False):
376
+ """Initialize multi-raster reader.
377
+
378
+ Args:
379
+ paths: List of paths to geotiff files to read.
380
+ bands_list: List of band configurations, one per raster file.
381
+ transformer: List of transformation functions, one per raster.
382
+ interpolation: List of resampling methods for each raster. Defaults to None (nearest neighbor).
383
+ reference_index: Index of raster to use as spatial reference. Defaults to 0.
384
+ read_profile: Rasterio read profile configuration. Defaults to None.
385
+ sharing: Enable file sharing mode. Defaults to False.
386
+ """
387
+
388
+ super().__init__(paths[reference_index],
389
+ bands_list[reference_index],
390
+ transformer[reference_index],
391
+ interpolation[reference_index],
392
+ read_profile,
393
+ sharing)
394
+
395
+ self.n_raster = len(paths)
396
+
397
+ if interpolation is None:
398
+ interpolation = [Resampling.nearest for _ in range(self.n_raster)]
399
+ else:
400
+ interpolation = [Resampling.nearest if r is None else r for r in interpolation]
401
+
402
+ self.interpolation = interpolation
403
+
404
+ self.bands_list = MultiRasterReader.validate_list(bands_list, "in_bands_list", self.n_raster)
405
+ self.transformer = MultiRasterReader.validate_list(transformer, "normalizer", self.n_raster)
406
+ self.interpolation: Union[List[Union[Resampling, None]], None] = interpolation
407
+
408
+ self.path = paths
409
+
410
+ self.reader = None
411
+ self.reader: list = [None for _ in range(len(paths))]
412
+
413
+ self.src_info = []
414
+ for p in paths:
415
+ self.src_info.append(RasterInfo.from_file(p))
416
+
417
+ if read_profile is None:
418
+ self.read_profile = get_read_profile()
419
+ else:
420
+ self.read_profile = read_profile
421
+
422
+ self.n_band = 0
423
+ self.bands_list = bands_list
424
+ for p, b in zip(paths, self.bands_list):
425
+ if b is None:
426
+ with rasterio.open(p, 'r', **self.read_profile) as reader:
427
+ self.n_band += reader.count
428
+ else:
429
+ self.n_band += len(b)
430
+
431
+ self.reference_index = reference_index
432
+
433
+ def __repr__(self):
434
+ return f"MultiRasterReader({self.path}, {self.bands_list}, {self.transformer}, {self.sharing})"
435
+
436
+ def __copy__(self):
437
+ cls = self.__class__
438
+ result = cls.__new__(cls)
439
+ result.__init__(self.path,
440
+ self.bands_list,
441
+ self.transformer,
442
+ self.interpolation,
443
+ self.reference_index,
444
+ self.read_profile,
445
+ self.sharing)
446
+
447
+ return result
448
+
449
+ def __deepcopy__(self, memo):
450
+ cls = self.__class__
451
+ result = cls.__new__(cls)
452
+
453
+ path = copy.deepcopy(self.path)
454
+ bands_list = copy.deepcopy(self.bands_list)
455
+ transformer = copy.deepcopy(self.transformer)
456
+ interpolation = copy.deepcopy(self.interpolation),
457
+ read_profile = copy.deepcopy(self.read_profile)
458
+ result.__init__(path, bands_list, transformer, interpolation, self.reference_index, read_profile, self.sharing)
459
+
460
+ return result
461
+
462
+ def __enter__(self):
463
+ self.reader = [rasterio.open(p, 'r', **self.read_profile, sharing= self.sharing) for p in self.path]
464
+ return self
465
+
466
+ def __exit__(self, exc_type, exc_value, exc_tb):
467
+ for r in self.reader:
468
+ r.close()
469
+ self.reader = None
470
+
471
+ def ref_raster(self):
472
+ """Get the path of the reference raster.
473
+
474
+ Returns:
475
+ str: Path to the reference raster file.
476
+ """
477
+ return self.path[self.reference_index]
478
+
479
+ def ref_raster_info(self):
480
+ """Get metadata information of the reference raster.
481
+
482
+ Returns:
483
+ RasterInfo: Raster metadata including CRS, transform, dimensions.
484
+ """
485
+ return self.src_info[self.reference_index]
486
+
487
+ @staticmethod
488
+ def validate_list(var, var_name, num):
489
+ """Validate list parameter has correct size.
490
+
491
+ Args:
492
+ var: Variable to validate (list, None, or single value).
493
+ var_name: Name of variable for error messages.
494
+ num: Expected list size.
495
+
496
+ Returns:
497
+ list: Validated list of correct size.
498
+
499
+ Raises:
500
+ ValueError: If var is a list of wrong size or invalid type.
501
+ """
502
+ if hasattr(var, '__len__'):
503
+ if len(var) != num:
504
+ raise ValueError(f"{var_name} should be None or a list of size {num},"
505
+ f" {len(var)} found instead")
506
+ else:
507
+ if var is None:
508
+ var = [None for _ in range(num)]
509
+ else:
510
+ raise ValueError({"None or list expected for the selected band"})
511
+
512
+ return var
513
+
514
+ def read_windows_around_coordinate(self, center_x, center_y, size, op= math.floor):
515
+ """Read a window centered on specified coordinates from all rasters.
516
+
517
+ Args:
518
+ center_x: X coordinate (longitude/easting) of center point.
519
+ center_y: Y coordinate (latitude/northing) of center point.
520
+ size: Size of window in pixels (square window).
521
+ op: Operation to apply when converting coordinates to pixels. Defaults to math.floor.
522
+
523
+ Returns:
524
+ numpy.ndarray: Combined raster data for the window around the specified coordinates.
525
+ """
526
+ return self.read_windows(self.windows_for_center(center_x, center_y, size, math.floor))
527
+
528
+
529
+ def read_windows(self, window: Window):
530
+ """Read data from a specific window across all rasters.
531
+
532
+ Args:
533
+ window: Rasterio window defining the area to read.
534
+
535
+ Returns:
536
+ numpy.ndarray: Combined raster data for the specified window.
537
+ """
538
+ bounds = self.ref_raster_info().window_bounds(window)
539
+ return self.read_bound(bounds)
540
+
541
+
542
+ def _read_windows(self, windows: List[Window]):
543
+ """Internal method to read from multiple windows across all rasters.
544
+
545
+ Args:
546
+ windows: List of windows, one per raster file.
547
+
548
+ Returns:
549
+ numpy.ndarray: Combined and stacked raster data.
550
+ """
551
+ # TODO manage manage resampling
552
+ #rasterio.windows.from_bounds
553
+
554
+ # we read the real window, but change the resolution to "size"
555
+ ref_size = (round(windows[self.reference_index].height), round(windows[self.reference_index].width))
556
+
557
+ data = []
558
+
559
+ for r, b, norm, interpol, source_window in\
560
+ zip(self.reader, self.bands_list, self.transformer, self.interpolation, windows):
561
+ if b.selected1 is not None:
562
+ d = r.read(b.selected1, window=source_window, boundless=True,
563
+ out_shape=ref_size, resampling=interpol)
564
+ else:
565
+ d = r.read(window=source_window, boundless=True,
566
+ out_shape=ref_size, resampling=interpol)
567
+
568
+ if norm is not None:
569
+ norm(d)
570
+
571
+ data.append(d)
572
+
573
+ data = numpy.vstack(data)
574
+
575
+ return data
576
+
577
+ def read_bound(self, bounds):
578
+ """Read data from a bounding box across all rasters.
579
+
580
+ Automatically handles reprojection if rasters have different coordinate
581
+ reference systems.
582
+
583
+ Args:
584
+ bounds: Tuple of (left, bottom, right, top) coordinates in reference CRS.
585
+
586
+ Returns:
587
+ numpy.ndarray: Combined raster data for the specified bounding box.
588
+ """
589
+
590
+ windows = []
591
+ for info in self.src_info:
592
+ if self.ref_raster_info().crs == info.crs:
593
+ other_bounds = bounds
594
+ else:
595
+ other_bounds = rasterio.warp.transform_bounds(self.ref_raster_info().crs, info.crs, *bounds)
596
+
597
+ windows.append(info.window(*other_bounds))
598
+
599
+ return self._read_windows(windows)
600
+
601
+
602
+