eotdl 2025.4.22.post2__py3-none-any.whl → 2025.5.26__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.
eotdl/files/metadata.py CHANGED
@@ -35,7 +35,8 @@ class Metadata(BaseModel):
35
35
  f.write(f"name: {self.name}\n")
36
36
  f.write(f"license: {self.license}\n")
37
37
  f.write(f"source: {self.source}\n")
38
- f.write(f"thumbnail: {self.thumbnail}\n")
38
+ if self.thumbnail:
39
+ f.write(f"thumbnail: {self.thumbnail}\n")
39
40
  f.write(f"authors:\n")
40
41
  for author in self.authors:
41
42
  f.write(f" - {author}\n")
eotdl/models/ingest.py CHANGED
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from ..repos import ModelsAPIRepo
4
4
  from ..files.ingest import prep_ingest_stac, prep_ingest_folder, ingest, ingest_virtual, ingest_catalog
5
5
 
6
- def retrieve_model(metadata, user):
6
+ def retrieve_model(metadata, user, private=False):
7
7
  repo = ModelsAPIRepo()
8
8
  data, error = repo.retrieve_model(metadata.name)
9
9
  # print(data, error)
@@ -22,6 +22,7 @@ def ingest_model(
22
22
  logger=print,
23
23
  force_metadata_update=False,
24
24
  sync_metadata=False,
25
+ private=False,
25
26
  ):
26
27
  path = Path(path)
27
28
  if not path.is_dir():
@@ -30,7 +31,7 @@ def ingest_model(
30
31
  prep_ingest_stac(path, logger)
31
32
  else:
32
33
  prep_ingest_folder(path, verbose, logger, force_metadata_update, sync_metadata)
33
- return ingest(path, ModelsAPIRepo(), retrieve_model, 'models')
34
+ return ingest(path, ModelsAPIRepo(), retrieve_model, 'models', private)
34
35
 
35
36
  def ingest_virtual_model( # could work for a list of paths with minimal changes...
36
37
  path,
eotdl/models/update.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from ..repos import ModelsAPIRepo
2
-
3
-
2
+ from ..auth import with_auth
3
+ from .retrieve import retrieve_model
4
4
  def update_model(model_id, metadata, content, user):
5
5
  repo = ModelsAPIRepo()
6
6
  data, error = repo.update_model(
@@ -16,9 +16,11 @@ def update_model(model_id, metadata, content, user):
16
16
  raise Exception(error)
17
17
  return data
18
18
 
19
- def deactivate_model(model_id):
19
+ @with_auth
20
+ def deactivate_model(model_name, user):
21
+ model = retrieve_model(model_name)
20
22
  repo = ModelsAPIRepo()
21
- data, error = repo.deactivate_model(model_id)
23
+ data, error = repo.deactivate_model(model['id'], user)
22
24
  if error:
23
25
  raise Exception(error)
24
26
  return data
@@ -18,15 +18,26 @@ class DatasetsAPIRepo(APIRepo):
18
18
  response = requests.get(url)
19
19
  return self.format_response(response)
20
20
 
21
+ def retrieve_private_datasets(self, user):
22
+ url = self.url + "datasets/private"
23
+ response = requests.get(url, headers=self.generate_headers(user))
24
+ return self.format_response(response)
25
+
21
26
  def retrieve_dataset(self, name):
22
27
  response = requests.get(self.url + "datasets?name=" + name)
23
28
  return self.format_response(response)
24
29
 
30
+ def retrieve_private_dataset(self, name, user):
31
+ response = requests.get(self.url + "datasets/private?name=" + name, headers=self.generate_headers(user))
32
+ return self.format_response(response)
33
+
25
34
  def get_dataset_by_id(self, dataset_id):
26
35
  response = requests.get(self.url + "datasets/" + dataset_id)
27
36
  return self.format_response(response)
28
37
 
29
- def create_dataset(self, metadata, user):
38
+ def create_dataset(self, metadata, user, private=False):
39
+ if private:
40
+ metadata["visibility"] = "private"
30
41
  response = requests.post(
31
42
  self.url + "datasets",
32
43
  json=metadata,
@@ -42,8 +53,9 @@ class DatasetsAPIRepo(APIRepo):
42
53
  )
43
54
  return self.format_response(response)
44
55
 
45
- def deactivate_dataset(self, dataset_name):
56
+ def deactivate_dataset(self, dataset_name, user):
46
57
  response = requests.patch(
47
- self.url + "datasets/deactivate/" + dataset_name
58
+ self.url + "datasets/deactivate/" + dataset_name,
59
+ headers=self.generate_headers(user),
48
60
  )
49
61
  return self.format_response(response)
@@ -0,0 +1,50 @@
1
+ import requests
2
+
3
+ from ..repos import APIRepo
4
+
5
+ class FEAPIRepo(APIRepo):
6
+ def __init__(self, url=None):
7
+ super().__init__(url)
8
+
9
+ def retrieve_pipelines(self, name, limit):
10
+ url = self.url + "pipelines"
11
+ if name is not None:
12
+ url += "?match=" + name
13
+ if limit is not None:
14
+ if name is None:
15
+ url += "?limit=" + str(limit)
16
+ else:
17
+ url += "&limit=" + str(limit)
18
+ response = requests.get(url)
19
+ return self.format_response(response)
20
+
21
+ def retrieve_pipeline(self, name):
22
+ response = requests.get(self.url + "pipelines?name=" + name)
23
+ return self.format_response(response)
24
+
25
+ def get_pipeline_by_id(self, pipeline_id):
26
+ response = requests.get(self.url + "pipelines/" + pipeline_id)
27
+ return self.format_response(response)
28
+
29
+ def create_pipeline(self, metadata, user):
30
+ response = requests.post(
31
+ self.url + "pipelines",
32
+ json=metadata,
33
+ headers=self.generate_headers(user),
34
+ )
35
+ return self.format_response(response)
36
+
37
+ def complete_ingestion(self, pipeline_id, version, size, user):
38
+ response = requests.post(
39
+ self.url + "pipelines/complete/" + pipeline_id,
40
+ json={"version": version, "size": size},
41
+ headers=self.generate_headers(user),
42
+ )
43
+ return self.format_response(response)
44
+
45
+ def deactivate_pipeline(self, pipeline_id, user):
46
+ response = requests.patch(
47
+ self.url + "pipelines/deactivate/" + pipeline_id,
48
+ headers=self.generate_headers(user),
49
+ )
50
+ return self.format_response(response)
@@ -63,7 +63,6 @@ class FilesAPIRepo(APIRepo):
63
63
  # url += "?version=" + str(file_version)
64
64
  return self.stage_file_url(url, path, user)
65
65
 
66
-
67
66
  def stage_file_url(
68
67
  self,
69
68
  url,
@@ -38,8 +38,9 @@ class ModelsAPIRepo(APIRepo):
38
38
  )
39
39
  return self.format_response(response)
40
40
 
41
- def deactivate_model(self, model_name):
41
+ def deactivate_model(self, model_name, user):
42
42
  response = requests.patch(
43
- self.url + "models/deactivate/" + model_name
43
+ self.url + "models/deactivate/" + model_name,
44
+ headers=self.generate_headers(user),
44
45
  )
45
46
  return self.format_response(response)
eotdl/repos/__init__.py CHANGED
@@ -4,4 +4,5 @@ from .AuthAPIRepo import AuthAPIRepo
4
4
  from .DatasetsAPIRepo import DatasetsAPIRepo
5
5
  from .FilesAPIRepo import FilesAPIRepo
6
6
  from .ModelsAPIRepo import ModelsAPIRepo
7
- from .STACAPIRepo import STACAPIRepo
7
+ from .STACAPIRepo import STACAPIRepo
8
+ from .FEAPIRepo import FEAPIRepo
@@ -0,0 +1,273 @@
1
+ import json
2
+ import numpy as np
3
+ import pandas as pd
4
+ import rasterio
5
+ import rasterio.features
6
+ from scipy import signal
7
+ from shapely import affinity
8
+ from shapely.geometry import Polygon
9
+ from pathlib import Path
10
+ import geopandas as gpd
11
+
12
+
13
+ def convert_shape_to_wgs84(poly, transform):
14
+ a,b,c,d,e,f = transform[:6]
15
+ mat = (a,b,d,e,c,f)
16
+ return affinity.affine_transform(poly, mat)
17
+
18
+ def get_vessel_shape(center, width, length, theta):
19
+ """ Get the bounding box for a ship
20
+ Args:
21
+ center (float): Ship's center index (subpixel)
22
+ width (float): Ship's width in pixel
23
+ length (float): Ship's length in pixel
24
+ theta (float): Ship's heading angle in radian
25
+ Returns:
26
+ (Shapely Polygon): Bounding box
27
+ """
28
+ width = width/2
29
+ length = length/2
30
+ poly = Polygon([(-width, -length), (-width, length), (width, length),
31
+ (width, -length)])
32
+
33
+ poly = affinity.rotate(poly, theta, 'center', use_radians=True)
34
+ poly = affinity.translate(poly, xoff=center[0], yoff=center[1])
35
+ return poly
36
+
37
+
38
+ def center_cross_correlation(data, width, length, theta, upsample_factor):
39
+ """ Approximate the center of the boat with a cross correlation of the data
40
+ and a gaussian model
41
+ Args:
42
+ data (Rasterio DatasetReader): tif file reader
43
+ width (float): Ship's width in pixel
44
+ length (float): Ship's length in pixel
45
+ theta (float): Ship's heading angle in radian
46
+ upsample_factor (uint): up sampling factor
47
+ Returns:
48
+ (Numpy Array): centered mask
49
+ (Numpy Array): Correlations with each bands of the image
50
+ (Numpy Array): Coordinates of the center of the boat
51
+ """
52
+ # Up sampling
53
+ up_sampled_data = np.repeat(np.repeat(data, upsample_factor, axis=1),
54
+ upsample_factor, axis=0)
55
+
56
+ # Rectangular mask
57
+ size = (data.shape[0]*upsample_factor, data.shape[1]*upsample_factor)
58
+ shape = get_vessel_shape([size[0]/2, size[1]/2], width * upsample_factor, length*upsample_factor, theta)
59
+ centered_mask = rasterio.features.rasterize([shape], out_shape=(size), all_touched=True).astype(np.int8)
60
+
61
+ # Remove null edges of the mask
62
+ border_padding_size = 2 * upsample_factor
63
+ max_x = int(np.max(shape.exterior.coords.xy[1])) +3 + border_padding_size
64
+ min_x = int(np.min(shape.exterior.coords.xy[1])) -2 - border_padding_size
65
+ max_y = int(np.max(shape.exterior.coords.xy[0])) +3 + border_padding_size
66
+ min_y = int(np.min(shape.exterior.coords.xy[0])) -2 - border_padding_size
67
+
68
+ if (max_x - min_x)%2 == 0:
69
+ max_x += 1
70
+ if (max_y - min_y)%2 == 0:
71
+ max_y += 1
72
+
73
+ cropped_mask = centered_mask[min_x:max_x, min_y:max_y]
74
+ background_value = -1
75
+
76
+ ####
77
+ background_value = 0
78
+ cropped_mask[cropped_mask == 0 ] = -1
79
+ ####
80
+
81
+
82
+
83
+ model_mask = cropped_mask.copy()
84
+ model_mask[0:border_padding_size] = background_value
85
+ model_mask[-border_padding_size:] = background_value
86
+ model_mask[:,0:border_padding_size] = background_value
87
+ model_mask[:,-border_padding_size:] = background_value
88
+ for i in range(2,model_mask.shape[0]-2):
89
+ for j in range(2, model_mask.shape[1]-2):
90
+ window = cropped_mask[i-2:i+3,j-2:j+3]
91
+ if np.all(window != 1):
92
+ model_mask[i,j] = background_value
93
+
94
+ # Model and data must have the same dimensions parity to avoid an offset after the cross correlation
95
+ if model_mask.shape[0] %2 == 0:
96
+ model_mask = np.append(model_mask, -1* np.ones((1, model_mask.shape[1])), axis=0)
97
+ if model_mask.shape[1] %2 == 0:
98
+ model_mask = np.append(model_mask, -1* np.ones((model_mask.shape[0],1)), axis=1)
99
+
100
+ # Compute the coordinates of the maximum correlation for each band
101
+ mean_filter = np.ones(cropped_mask.shape)/np.size(cropped_mask)
102
+ correlations = np.zeros(up_sampled_data.shape)
103
+ for i in range(data.shape[2]):
104
+ correlation_band_i = signal.correlate2d(up_sampled_data[:, :, i],
105
+ model_mask, mode='valid', boundary='fill', fillvalue=0)
106
+
107
+ # Mean filter convolution (removes abnormal high values)
108
+ average_map = signal.convolve2d(up_sampled_data[:, :, i],
109
+ mean_filter, mode='valid', boundary='fill', fillvalue=np.inf)
110
+ correlation_band_i = np.divide(correlation_band_i, average_map,
111
+ out=np.zeros(correlation_band_i.shape), where=average_map!=0)
112
+ correlation_band_i -= np.min(correlation_band_i)
113
+
114
+ # resize the output
115
+ nb = correlations.shape[0]
116
+ na = correlation_band_i.shape[0]
117
+ lowerx = (nb) // 2 - (na // 2)
118
+ upperx = (nb // 2) + (na // 2)
119
+ nb = correlations.shape[1]
120
+ na = correlation_band_i.shape[1]
121
+ lowery = (nb) // 2 - (na // 2)
122
+ uppery = (nb // 2) + (na // 2)
123
+ correlations[lowerx:upperx, lowery:uppery,i] = correlation_band_i
124
+
125
+
126
+ window1d = np.abs(np.hamming(correlations.shape[0]))
127
+ window2d = np.sqrt(np.outer(window1d,window1d))
128
+ correlations *= np.repeat(window2d[:,:,None],correlations.shape[2],axis=2)
129
+
130
+ somme = np.sum(correlations, axis = 2)
131
+
132
+ ##########################################################
133
+
134
+
135
+
136
+ center = np.argwhere(somme == np.max(somme))[0] / upsample_factor
137
+
138
+ # swap to the right coordinate system
139
+ center[0], center[1] = center[1], center[0]
140
+
141
+ return centered_mask, correlations, center
142
+
143
+
144
+ def generate_mask(file_path, output_dir, row_vessel, upsample_factor=1, output_type="raster", dilatation=5):
145
+ """ Get the entries for the csv file for an image without a mask
146
+ Args:
147
+ file_path (String): path to the image
148
+ output_dir (String): path to the output folder
149
+ row_vessel (Pandas Dataframe): boat's informations
150
+ upsample_factor (Int): upsampling factor for the model and the cross correlation
151
+ output_type (String): can be a 'raster' (.tif) or a 'vector' (.geojson) output
152
+ dilatation (Int): size of the dilation for the length and width of the mask in meters
153
+ Returns:
154
+ (Dict): csv entries for the image
155
+ """
156
+
157
+ # Load the source tile
158
+ with rasterio.open(file_path, 'r') as src:
159
+
160
+ # All 10m channels except blue
161
+ data = src.read([2,3,4,11,12]).transpose(1, 2, 0)
162
+
163
+ # Get the dimensions of the boat
164
+ # pixel_res = abs(src.transform[4])
165
+ pixel_res = 10
166
+ width_px = row_vessel["Width"] / pixel_res
167
+ length_px = row_vessel["Length"] / pixel_res
168
+
169
+ # Get the center of the mask
170
+ model, correlations, center = center_cross_correlation(data,
171
+ width_px, length_px, row_vessel['Heading'], upsample_factor)
172
+
173
+ # Rectangular bounding box
174
+ mask_width_px = (row_vessel["Width"] + dilatation) / pixel_res
175
+ mask_length_px = (row_vessel["Length"] + dilatation) / pixel_res
176
+ shape = get_vessel_shape(center, mask_width_px, mask_length_px, row_vessel['Heading'])
177
+ mask = rasterio.features.rasterize([shape], out_shape=data.shape[0:2], all_touched=True)
178
+
179
+ if output_type == "vector":
180
+ # generate Geojson
181
+ shape_wgs84 = convert_shape_to_wgs84(shape, src.transform)
182
+ gdr = gpd.GeoDataFrame({'geometry': [shape_wgs84],
183
+ "labels": [["Boat"]],
184
+ "tasks":[["segmentation"]]},
185
+ crs='EPSG:4326')
186
+
187
+ # Write to geojson
188
+ label_geojson = gdr.to_json(drop_id=True)
189
+ out_mask_path = Path(file_path).parent / f"{file_path.stem}_labels.geojson"
190
+
191
+ with open(out_mask_path, "w") as dst:
192
+ dst.write(label_geojson)
193
+
194
+ elif output_type == "raster":
195
+ # Create the profile for the .tif
196
+ profile = {
197
+ 'driver': 'GTiff',
198
+ 'height': src.height,
199
+ 'width': src.width,
200
+ 'count': 1,
201
+ 'dtype': rasterio.uint8,
202
+ 'crs': src.crs,
203
+ 'transform': src.transform,
204
+ 'nodata': None,
205
+ 'compress': 'lzw'
206
+ }
207
+
208
+ # Write the mask
209
+ out_mask_path = Path(output_dir) / f"mask_{Path(file_path).name}"
210
+ with rasterio.open(out_mask_path, 'w', **profile) as dst:
211
+ dst.write(mask, indexes=1)
212
+
213
+
214
+ def process_directoy(dir_name, output_dir, tiles_df, upsample_factor, output_type):
215
+
216
+ assert output_type == "raster" or output_type == "vector"
217
+ print(f"Processing tiles from {dir_name}")
218
+
219
+ # Prepare outputpath
220
+ output_dir_name = Path(output_dir)
221
+
222
+ if output_type =="vector":
223
+
224
+ labels_json = {"labels": [{"name": "Boat", "color": "#ff8000"}]}
225
+ json_path = Path(dir_name) / "labels.json"
226
+
227
+ with open(json_path, "w") as dst:
228
+ dst.write(json.dumps(labels_json))
229
+
230
+ # Generate the mask for each vessel
231
+ list_nan = []
232
+
233
+ # Convert angles in radian
234
+ tiles_df["Heading"] *= np.pi/180
235
+
236
+ for i in range(len(tiles_df)):
237
+ row_vessel = tiles_df.iloc[i]
238
+ img_path = Path(dir_name) / f"{row_vessel['ImageId']}.tiff"
239
+
240
+ # Get mmsi from filename
241
+ vessel = int(row_vessel["ImageId"].split('_')[1])
242
+
243
+ if row_vessel.isnull().values.any(): # Tiles with Nan data
244
+ list_nan.append(vessel)
245
+
246
+ else:
247
+ # Get the mask of the boat
248
+ generate_mask(img_path, output_dir_name, row_vessel, upsample_factor, output_type)
249
+
250
+ if list_nan:
251
+ print(f"No mask saved for the vessels {list_nan}, NaN values found in the AIS data")
252
+
253
+
254
+ def process_db(images_path, output_dir, upsample_factor, output_type = "raster"):
255
+ """ Get the entries for the csv file for an image without a mask
256
+ Args:
257
+ csv_tiles (String): path to the csv folder of the tiles
258
+ output_dir (String): path to the output folder
259
+ upsample_factor (Int): upsampling factor for the model and the cross correlation
260
+ output_type (String): can be a 'raster' (.tif) or a 'vector' (.geojson) output
261
+ """
262
+ # Prepare the output file
263
+ csv_paths = Path(images_path).glob("*.csv")
264
+ for csv_tiles in csv_paths:
265
+ # read the tiles csv
266
+ tiles_df = pd.read_csv(csv_tiles)
267
+ # Get the path to the tiles
268
+ tiles_path = Path(csv_tiles).parent
269
+ process_directoy(tiles_path,
270
+ output_dir=output_dir,
271
+ tiles_df=tiles_df,
272
+ upsample_factor=upsample_factor,
273
+ output_type=output_type)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eotdl
3
- Version: 2025.4.22.post2
3
+ Version: 2025.5.26
4
4
  Summary: Earth Observation Training Data Lab
5
5
  Author-email: earthpulse <it@earthpulse.es>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
- eotdl/__init__.py,sha256=-O7BvxY9Mzl3TA3wOKD1uu7C0__yrYkBF7J9kAF4jts,29
2
- eotdl/cli.py,sha256=BAI3HuDf1WohaWeA5y1bUJMABDkqmfttOCa7FY5P3Hk,701
1
+ eotdl/__init__.py,sha256=7OVkF8SLpGQOJkvOiT6XHXrkE9hp4JDvF9-vFYP8Ytg,27
2
+ eotdl/cli.py,sha256=MgRmnBcnPtRTW_nuvtH41y7MSjmVMzr1JOt9X-oDnt4,759
3
3
  eotdl/access/__init__.py,sha256=k-zmTwB6VLoWt_AsXx9CnEKdtONBZAaC8T6vqPMPSjk,436
4
4
  eotdl/access/download.py,sha256=e5H8LUkCfIVkFxJFM5EwCMG-R5DHVSHDGLvuNM5DNc8,2815
5
5
  eotdl/access/search.py,sha256=1indipTfna4VAfGlKb8gkaYyHAELdHR4cm1mVIDW69s,1415
@@ -18,40 +18,56 @@ eotdl/auth/errors.py,sha256=E1lv3Igk--J-SOgNH18i8Xx9bXrrMyBSHKt_CAUmGPo,308
18
18
  eotdl/auth/is_logged.py,sha256=QREuhkoDnarZoUZwCxVCNoESGb_Yukh0lJo1pXvrV9Q,115
19
19
  eotdl/auth/logout.py,sha256=P_Sp6WmVvnG3R9V1L9541KNyHFko9DtQPqAKD2vaguw,161
20
20
  eotdl/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- eotdl/commands/auth.py,sha256=WzA0aFGRoscy7fPKQTxiphBc0LJztJxBBl7rjDBRVfI,1544
22
- eotdl/commands/datasets.py,sha256=gE9CcHq4KyMsBhj28j1TfkLsMVG2CPBqvAANqAvxn0Y,5503
23
- eotdl/commands/models.py,sha256=iXy9XNgrYvi0_vZamDFbF69OQ-ApxSTCxmKeIvp9Q0g,5145
21
+ eotdl/commands/auth.py,sha256=sSfawOpht6ntToFXDJrOu11IATV9vN03Bqyg5LNGb1s,1646
22
+ eotdl/commands/datasets.py,sha256=1ksdUYSalEY8ts0_ak3-nhV4WyygkNl33uiZ6K2GUaM,5508
23
+ eotdl/commands/models.py,sha256=a6ozJC0MH7CsK2-ts6sN_9EFbT6bzoaJne8v83JjTzg,4942
24
+ eotdl/commands/pipelines.py,sha256=uvdS7NFzxG95IiKKFf5ha4TgGZsN-LO7Pucmp0GWgnw,4396
24
25
  eotdl/commands/stac.py,sha256=jgjjkGw9tIvuovyAwfEu6B4ZMoMvEj5lrj_lO-9IqrE,1444
25
26
  eotdl/curation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
27
  eotdl/curation/stac/__init__.py,sha256=FMi-qzd6v1sdsqZsBRRnedccaJFXJGCPCH3uTctyLYU,37
27
28
  eotdl/curation/stac/api.py,sha256=5XW9yzSxiuML70bBbzJ0DGx0GmplmhYtgeGZg029Qjk,1428
28
29
  eotdl/curation/stac/stac.py,sha256=4f7xrh2CcXTkTs3or1UMVxiFfwtVfTqH4YwTGsbi6No,1013
29
- eotdl/datasets/__init__.py,sha256=g2WBJa_EVVXtktxqZRJEAQPHADU0qVaLkjDYZnojDgc,256
30
- eotdl/datasets/ingest.py,sha256=7LI4eENRxhGDjgfAlgLqTY96jiX5iRB0_SlU63NboOM,1414
31
- eotdl/datasets/retrieve.py,sha256=dhNbBJu0vE0l-LsGQNQF5Vc_WzZDRbXPzvd66GNlV6U,691
32
- eotdl/datasets/stage.py,sha256=pcU1AsjbczzMHdhCxjKfCuuuLo1OZMMWNAUqj-3SxKc,2162
33
- eotdl/datasets/update.py,sha256=pHHutyGf0ALTuGmq2TYSyJ3VaLmY0WMNgHw_pKUn2zI,567
34
- eotdl/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ eotdl/datasets/__init__.py,sha256=3FtdIDmSaCg4P15oI5d-7DDomuUEz4E3PjK78Ofvm3g,283
31
+ eotdl/datasets/ingest.py,sha256=sxtnaRTUZxtg5Wfs8uei7M63xszpd6eWHH-KvDqPW5c,1750
32
+ eotdl/datasets/retrieve.py,sha256=vQzociVOcD1A1ZTfFC42jzgEYVcc_y_HcvlALBqpAd8,1210
33
+ eotdl/datasets/stage.py,sha256=dbmEV5VpaM1DEHhpCWTEgYAuCNUGaW0Ghm_oP8_COEg,2168
34
+ eotdl/datasets/update.py,sha256=QmhXZwAOqz5ysc_VpCjFFDo5tpt0EKG44G1jYeQmbkQ,706
35
+ eotdl/fe/__init__.py,sha256=nwm7HHfh_UrCnMMYaO2vqQ6y4ziVzAf6zt_RuvDSVF4,167
36
+ eotdl/fe/ingest.py,sha256=JkVxPkHbltmvlRP6fmPKZu5lx8HJGOp9dSxrzkCv8tQ,1489
37
+ eotdl/fe/retrieve.py,sha256=xrtmYcG14xgknu9yQJEdSjJMtSMoZkDU____95Yd6YY,448
38
+ eotdl/fe/stage.py,sha256=wxmX_uyNc2kLvU_bF2SvyBBr05sPlTA7xYq0T4pSzEo,2001
39
+ eotdl/fe/update.py,sha256=GO9ep8tAneGQrOseKdZnrADvNs1x9uhG4Q7Y5UhcLVI,354
40
+ eotdl/fe/openeo/__init__.py,sha256=oag2hTyLcRKF-7SGkY-ZcbuftUtABZMNtn1-8gOYDik,108
41
+ eotdl/fe/openeo/advanced_patch_extraction.py,sha256=jyfkHT8Co53xey2qbWimzYeB5kLwCEG_aCyK3OUefW4,5778
42
+ eotdl/fe/openeo/basic_point_extraction.py,sha256=pUFZ34J9Ob21V9Ur6GLO3d3HGKYU5o6vjBtF3eR3JXs,4105
43
+ eotdl/fe/openeo/dataframe_utils.py,sha256=QYG4xkJ6LER8q9lDD8ovd_pPcBb9dX2hbg4y5ZMMfW4,7981
44
+ eotdl/fe/openeo/s3proxy_utils.py,sha256=AH8cKyf8D8qM4UYXgBgZX7I2seKxhkhNjxQJpcqtxZI,6406
45
+ eotdl/fe/openeo/spatial_utils.py,sha256=E1FfPnzgfBMhFd4XAjRl9FEZO3QnHf27tJjkZzze75Q,1008
46
+ eotdl/fe/openeo/temporal_utils.py,sha256=R6epJqyhNH-c-wXVg8GgV3O_1DStk9hms5Oon4lPLH8,570
47
+ eotdl/files/__init__.py,sha256=3guFFgXq6WEQ6X1VsVnEZEzya2kpQajaDvLpNSQboVA,33
48
+ eotdl/files/get_url.py,sha256=gcD6E6cj9tsEsF2T5WdrFKK7rBjjjcGmWtyUc8IQEBo,772
35
49
  eotdl/files/ingest.bck,sha256=dgjZfd-ACCKradDo2B02CPahwEhFtWvnKvTm372K5eo,6185
36
- eotdl/files/ingest.py,sha256=mepe-RmweWaM_4fUZjyGWpwjrg6EIlc_B7GwxRR7Br4,9379
37
- eotdl/files/metadata.py,sha256=C-NDr-zjM58fP8QcHB1N1QyLRUeYyMbT6wPPnxGk8LI,1370
50
+ eotdl/files/ingest.py,sha256=RwQ3WazTm3b600QXEiHsTDApIegxAxW-Vhw9GkGQFP0,9427
51
+ eotdl/files/metadata.py,sha256=MGrIvcgNr3AMI3j9Jcdyp5Q3Jcuth8m6Z14BCDxF0xI,1405
38
52
  eotdl/models/__init__.py,sha256=b6t1Z377On1F56c-GSy7FM_nBWRLHh1Ph2R24rPFiVY,239
39
53
  eotdl/models/download.py,sha256=rRT3fG-qS3-SXfzFdqy0cuiDnOIV9Du74JCnsbbA9Ps,3475
40
- eotdl/models/ingest.py,sha256=xpDoY4Tn0y94bIDXgk2No6fGLydPbKYcc9NsPj-yFms,1372
54
+ eotdl/models/ingest.py,sha256=cvkxH_KZO1v2a6VSsd0Mady1kPQY7WGn0YBXaY90_Xc,1412
41
55
  eotdl/models/retrieve.py,sha256=-Ij7dT4J1p7MW4n13OlPB9OW4tBaBXPwk9dW8IuCZPc,664
42
56
  eotdl/models/stage.py,sha256=rvWN8vcBz7qHhu0TzJ90atw1kEr3JPKF0k2S-Sv-JVs,1944
43
- eotdl/models/update.py,sha256=0N6S_n_DXWaS2A56lHjukBgLLZDxygB8jN2WfZyJwaQ,545
57
+ eotdl/models/update.py,sha256=PIdPbPrdul-HSAAL21SadAkZT2kcnJyV2uTmi8dlK5k,676
44
58
  eotdl/repos/APIRepo.py,sha256=s9w29Cnk5Pu4NvjyHAfcpM69a2lODICJ-Gn_iosxsPg,791
45
59
  eotdl/repos/AuthAPIRepo.py,sha256=vYCqFawe3xUm2cx4SqVXCvzl8J_sr9rs_MkipYC0bXE,957
46
60
  eotdl/repos/AuthRepo.py,sha256=jpzzhINCcDZHRCyrPDsp49h17IlXp2HvX3BB3f5cnb4,1154
47
- eotdl/repos/DatasetsAPIRepo.py,sha256=Wx8xkQHT53h7AZATNSCLaDmkHYMoSGDVoyo_beqpgGo,1639
48
- eotdl/repos/FilesAPIRepo.py,sha256=PNNS5LGxksZpkg-49_r3r2H0iQWkCeb7iiL6j-S4wLY,4757
49
- eotdl/repos/ModelsAPIRepo.py,sha256=i2T1mJwlWu2mpexUVOE9ha5JRyHdf6_6n36TraL3-BU,1438
61
+ eotdl/repos/DatasetsAPIRepo.py,sha256=bJnMWQVQ-t25MHwisMKeq8yeAVxmbDB77TXtYdTwV70,2209
62
+ eotdl/repos/FEAPIRepo.py,sha256=nbyk1Wt3hzCYOm6zvZ1DA5UQ_rmY8G3cpSKs0rLem4I,1702
63
+ eotdl/repos/FilesAPIRepo.py,sha256=iJWT2_7TIg8gTMAihui5mBbcUWF5THh-AuuLcfyMHGk,4756
64
+ eotdl/repos/ModelsAPIRepo.py,sha256=hgnU8wkwESsrzfL6Wxpwwt7ejlnoOBeeNa4IrV4hwRo,1494
50
65
  eotdl/repos/STACAPIRepo.py,sha256=GJIrLkgVB-donToJlgOmaJbxDmXzIuwlmCb9R2yoRIA,1387
51
- eotdl/repos/__init__.py,sha256=GIzk62681dvNzYgVzvJgrMzVRhrep4-kJH6lTOtfnT8,258
66
+ eotdl/repos/__init__.py,sha256=I4_friwwZ4sSQxmguYY2uBNWvpKLaHUEN1i6Zib_WWU,291
52
67
  eotdl/shared/__init__.py,sha256=mF7doJC8Z5eTPmB01UQvPivThZac32DRY33T6qshXfg,41
53
68
  eotdl/shared/checksum.py,sha256=4IB6N9jRO0chMDNJzpdnFDhC9wcFF9bO5oHq2HodcHw,479
54
69
  eotdl/tools/__init__.py,sha256=_p3n2dw3ulwyr1OlVw5d_jMV64cNYfajQMUbzFfvIpU,178
70
+ eotdl/tools/ais_labelling.py,sha256=2KGbNGjN3R_Y26SoXPNBuXB7HSpUpyXViIByM-ABDvo,10655
55
71
  eotdl/tools/geo_utils.py,sha256=JKHUAnqkwiIrvh5voDclWAW-i57qVqH2FUjeOt1TQf4,7547
56
72
  eotdl/tools/metadata.py,sha256=RvNmoMdfEKoo-DzhEAqL-f9ZCjIe_bsdHQwACMk6w1E,1664
57
73
  eotdl/tools/paths.py,sha256=yWhOtVxX4NxrDrrBX2fuye5N1mAqrxXFy_eA7dffd84,1152
@@ -60,7 +76,7 @@ eotdl/tools/time_utils.py,sha256=JHrQ3PxXkhwor8zcOFccf26zOG9WBtb9xHb6j-Fqa9k,466
60
76
  eotdl/tools/tools.py,sha256=Tl4_v2ejkQo_zyZek8oofJwoYcdVosdOwW1C0lvWaNM,6354
61
77
  eotdl/wrappers/__init__.py,sha256=IY3DK_5LMbc5bIQFleQA9kzFbPhWuTLesJ8dwfvpkdA,32
62
78
  eotdl/wrappers/models.py,sha256=kNO4pYw9KKKmElE7bZWWHGs7FIThNUXj8XciKh_3rNw,6432
63
- eotdl-2025.4.22.post2.dist-info/METADATA,sha256=CB8WifWJtYRbDtitqT85iGfIiEe0VD52Mh2EYqHNsq0,3371
64
- eotdl-2025.4.22.post2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
65
- eotdl-2025.4.22.post2.dist-info/entry_points.txt,sha256=FV4dFIZ5zdWj1q1nUEEip29n3sAgbviVOizEz00gEF0,40
66
- eotdl-2025.4.22.post2.dist-info/RECORD,,
79
+ eotdl-2025.5.26.dist-info/METADATA,sha256=ifJ4T20QsC202xJpv8Cwn-4vBHQOF9e_l0Jdgb1rZ-Y,3365
80
+ eotdl-2025.5.26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
81
+ eotdl-2025.5.26.dist-info/entry_points.txt,sha256=FV4dFIZ5zdWj1q1nUEEip29n3sAgbviVOizEz00gEF0,40
82
+ eotdl-2025.5.26.dist-info/RECORD,,