anta-database 0.3.4__tar.gz → 0.3.6__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.
Files changed (51) hide show
  1. {anta_database-0.3.4 → anta_database-0.3.6}/PKG-INFO +1 -1
  2. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/compiling/compile.py +22 -9
  3. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/database/database.py +81 -37
  4. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/indexing/index_database.py +28 -9
  5. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/plotting/plotting.py +25 -29
  6. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database.egg-info/PKG-INFO +1 -1
  7. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database.egg-info/SOURCES.txt +1 -0
  8. {anta_database-0.3.4 → anta_database-0.3.6}/book/_toc.yml +1 -0
  9. anta_database-0.3.6/book/advanced.md +154 -0
  10. anta_database-0.3.6/book/pism_example.ipynb +302 -0
  11. {anta_database-0.3.4 → anta_database-0.3.6}/book/quick_start.ipynb +130 -76
  12. anta_database-0.3.4/book/advanced.md +0 -90
  13. {anta_database-0.3.4 → anta_database-0.3.6}/.github/workflows/deploy.yml +0 -0
  14. {anta_database-0.3.4 → anta_database-0.3.6}/.github/workflows/release.yml +0 -0
  15. {anta_database-0.3.4 → anta_database-0.3.6}/.gitignore +0 -0
  16. {anta_database-0.3.4 → anta_database-0.3.6}/LICENSE +0 -0
  17. {anta_database-0.3.4 → anta_database-0.3.6}/README.md +0 -0
  18. {anta_database-0.3.4 → anta_database-0.3.6}/ROADMAP.md +0 -0
  19. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/__init__.py +0 -0
  20. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/anta_database.py +0 -0
  21. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/compiling/__init__.py +0 -0
  22. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/compiling/reconnect.py +0 -0
  23. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/ANT_Basins_IMBIE2_v1.6.cpg +0 -0
  24. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/ANT_Basins_IMBIE2_v1.6.dbf +0 -0
  25. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/ANT_Basins_IMBIE2_v1.6.prj +0 -0
  26. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/ANT_Basins_IMBIE2_v1.6.shp +0 -0
  27. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/ANT_Basins_IMBIE2_v1.6.shx +0 -0
  28. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/GL.pkl +0 -0
  29. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/centeroid_coords_basins.cpg +0 -0
  30. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/centeroid_coords_basins.dbf +0 -0
  31. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/centeroid_coords_basins.prj +0 -0
  32. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/centeroid_coords_basins.shp +0 -0
  33. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/centeroid_coords_basins.shx +0 -0
  34. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/data/site-coords.pkl +0 -0
  35. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/database/__init__.py +0 -0
  36. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/figures/AntA_38ka.png +0 -0
  37. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/indexing/__init__.py +0 -0
  38. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database/plotting/__init__.py +0 -0
  39. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database.egg-info/dependency_links.txt +0 -0
  40. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database.egg-info/requires.txt +0 -0
  41. {anta_database-0.3.4 → anta_database-0.3.6}/anta_database.egg-info/top_level.txt +0 -0
  42. {anta_database-0.3.4 → anta_database-0.3.6}/book/_config.yml +0 -0
  43. {anta_database-0.3.4 → anta_database-0.3.6}/book/contact.md +0 -0
  44. {anta_database-0.3.4 → anta_database-0.3.6}/book/contribute.md +0 -0
  45. {anta_database-0.3.4 → anta_database-0.3.6}/book/figures/Overview_all.png +0 -0
  46. {anta_database-0.3.4 → anta_database-0.3.6}/book/intro.md +0 -0
  47. {anta_database-0.3.4 → anta_database-0.3.6}/book/logo.png +0 -0
  48. {anta_database-0.3.4 → anta_database-0.3.6}/book/references.bib +0 -0
  49. {anta_database-0.3.4 → anta_database-0.3.6}/book/requirements.txt +0 -0
  50. {anta_database-0.3.4 → anta_database-0.3.6}/pyproject.toml +0 -0
  51. {anta_database-0.3.4 → anta_database-0.3.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anta_database
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: SQLite database for the AntArchitecture Community Data
5
5
  Author: Antoine Hermant
6
6
  Author-email: antoine.hermant@etik.com
@@ -24,6 +24,7 @@ class CompileDatabase:
24
24
  firn_correction: Optional[float] = None,
25
25
  extract_data: bool = True,
26
26
  hdf5: bool = True,
27
+ netcdf: bool = False,
27
28
  compute_distance: bool = True,
28
29
  compute_IMBIE_basins: bool = True,
29
30
  compute_IRH_density: bool = True,
@@ -42,6 +43,7 @@ class CompileDatabase:
42
43
  self._compute_IMBIE_basins = compute_IMBIE_basins
43
44
  self._set_attributes = set_attributes
44
45
  self._hdf5 = hdf5
46
+ self._netcdf = netcdf
45
47
  self._shapefiles = shapefiles
46
48
  self._geopackages = geopackages
47
49
  self._break_transects = break_transects
@@ -94,7 +96,7 @@ class CompileDatabase:
94
96
  for file_dict in tqdm(all_files_list, desc="Processing", unit="file"):
95
97
  self.extract(file_dict=file_dict)
96
98
 
97
- if self._hdf5 is True:
99
+ if self._hdf5 is True or self._netcdf is True:
98
100
  all_dirs = []
99
101
  for dir_ in self._dir_list:
100
102
  dirs = [d for d in glob.glob(f"{dir_}/pkl/*") if os.path.isdir(d)]
@@ -104,7 +106,7 @@ class CompileDatabase:
104
106
  num_workers = min(num_tasks, cpus)
105
107
 
106
108
  print('\n',
107
- 'Will start creating the hdf5 for', len(all_dirs), 'traces\n'
109
+ 'Will start creating the hdf5/netcdf for', len(all_dirs), 'traces\n'
108
110
  '\n ', num_workers, 'worker(s) allocated out of', cpu_count(), 'available cpus\n')
109
111
 
110
112
  if num_workers > 1:
@@ -680,14 +682,25 @@ class CompileDatabase:
680
682
  ds[var] = ds[var].chunk({'point': chunk_size_point})
681
683
  encoding[var] = {'zlib': True, 'complevel': 1, 'chunksizes': (chunk_size_point)}
682
684
 
683
- h5_dir = os.path.join(dataset_dir, 'h5')
684
- os.makedirs(h5_dir, exist_ok=True)
685
- if not isinstance(institute, (list, tuple, set)) and institute != 'nan':
686
- h5_file = f'{h5_dir}/{institute}_{flight_id}.h5'
687
- else:
688
- h5_file = f'{h5_dir}/{flight_id}.h5'
685
+ if self._hdf5:
686
+ h5_dir = os.path.join(dataset_dir, 'h5')
687
+ os.makedirs(h5_dir, exist_ok=True)
688
+ if not isinstance(institute, (list, tuple, set)) and institute != 'nan':
689
+ h5_file = f'{h5_dir}/{institute}_{flight_id}.h5'
690
+ else:
691
+ h5_file = f'{h5_dir}/{flight_id}.h5'
692
+
693
+ ds.to_netcdf(h5_file, engine='h5netcdf', encoding=encoding, mode='w')
694
+
695
+ if self._netcdf:
696
+ nc_dir = os.path.join(dataset_dir, 'nc')
697
+ os.makedirs(nc_dir, exist_ok=True)
698
+ if not isinstance(institute, (list, tuple, set)) and institute != 'nan':
699
+ nc_file = f'{nc_dir}/{institute}_{flight_id}.nc'
700
+ else:
701
+ nc_file = f'{nc_dir}/{flight_id}.nc'
689
702
 
690
- ds.to_netcdf(h5_file, engine='h5netcdf', encoding=encoding, mode='w')
703
+ ds.to_netcdf(nc_file, engine='netcdf4', encoding=encoding, mode='w')
691
704
 
692
705
  def do_break_transects(self, h5_file: str) -> None:
693
706
  h5_dir, name = os.path.split(h5_file)
@@ -8,9 +8,10 @@ from typing import Union, List, Dict, Tuple, Optional, Generator
8
8
  from tqdm import tqdm
9
9
 
10
10
  from anta_database.plotting.plotting import Plotting
11
+ from anta_database.indexing.index_database import IndexDatabase
11
12
 
12
13
  class Database:
13
- def __init__(self, database_dir: str, file_db: str = 'AntADatabase.db', include_BEDMAP: bool = False, max_displayed_flight_ids: Optional[int] = 50) -> None:
14
+ def __init__(self, database_dir: str, file_db: str = 'AntADatabase.db', index_database: bool = False, include_BEDMAP: bool = False, max_displayed_flight_ids: Optional[int] = 50) -> None:
14
15
  self._db_dir = database_dir
15
16
  self._file_db = file_db
16
17
  self._file_db_path = os.path.join(self._db_dir, file_db)
@@ -18,6 +19,7 @@ class Database:
18
19
  self._plotting = None
19
20
  self._max_displayed_flight_ids = max_displayed_flight_ids
20
21
  self._include_BM = include_BEDMAP
22
+ self._include_BM = index_database
21
23
  self._disable_tqdm = os.getenv("JUPYTER_BOOK_BUILD", False)
22
24
  self._excluded = {
23
25
  'dataset': [],
@@ -32,6 +34,10 @@ class Database:
32
34
  'radar_instrument': [],
33
35
  }
34
36
 
37
+ if index_database:
38
+ indexing = IndexDatabase(self._db_dir)
39
+ indexing.index_database()
40
+
35
41
  def _build_query_and_params(self,
36
42
  age: Optional[Union[str, List[str]]] = None,
37
43
  var: Optional[Union[str, List[str]]] = None,
@@ -177,38 +183,40 @@ class Database:
177
183
  op, val = self._parse_range_query(field)
178
184
  if op == '>':
179
185
  conditions.append(f"("
180
- f"({column} > ?) OR "
181
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-')+1) AS INTEGER) > ?)"
182
- f")")
186
+ f"CAST({column} AS INTEGER) > ? OR "
187
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-') + 1) AS INTEGER) > ?"
188
+ f")")
183
189
  params.extend([val, val])
184
190
  elif op == '<':
185
191
  conditions.append(f"("
186
- f"({column} < ?) OR "
187
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-')-1) AS INTEGER) < ?)"
188
- f")")
192
+ f"CAST({column} AS INTEGER) < ? OR "
193
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-') - 1) AS INTEGER) < ?"
194
+ f")")
189
195
  params.extend([val, val])
190
196
  elif op == '>=':
191
197
  conditions.append(f"("
192
- f"({column} >= ?) OR "
193
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-')+1) AS INTEGER) >= ?)"
194
- f")")
198
+ f"CAST({column} AS INTEGER) >= ? OR "
199
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-') + 1) AS INTEGER) >= ?"
200
+ f")")
195
201
  params.extend([val, val])
196
202
  elif op == '<=':
197
203
  conditions.append(f"("
198
- f"({column} <= ?) OR "
199
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-')-1) AS INTEGER) <= ?)"
200
- f")")
204
+ f"CAST({column} AS INTEGER) <= ? OR "
205
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-') - 1) AS INTEGER) <= ?"
206
+ f")")
201
207
  params.extend([val, val])
202
208
  elif op == '=':
203
209
  conditions.append(f"("
204
- f"({column} = ?) OR "
210
+ f"CAST({column} AS INTEGER) = ? OR "
205
211
  f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-')-1) AS INTEGER) <= ? AND CAST(SUBSTR({column}, INSTR({column}, '-')+1) AS INTEGER) >= ?)"
206
212
  f")")
207
213
  params.extend([val, val, val])
208
214
  elif self._is_range_value(field):
209
215
  start, end = self._parse_range_value(field)
216
+ start = int(start)
217
+ end = int(end)
210
218
  conditions.append(f"("
211
- f"({column} BETWEEN ? AND ?) OR "
219
+ f"CAST({column} AS INTEGER) >= ? AND CAST({column} AS INTEGER) <= ? OR "
212
220
  f"({column} LIKE '%-%' AND "
213
221
  f"CAST(SUBSTR({column}, 1, INSTR({column}, '-')-1) AS INTEGER) <= ? AND "
214
222
  f"CAST(SUBSTR({column}, INSTR({column}, '-')+1) AS INTEGER) >= ?)"
@@ -240,31 +248,31 @@ class Database:
240
248
  inverted_op = self._invert_range_operator(op)
241
249
  if inverted_op == '>':
242
250
  conditions.append(f"("
243
- f"({column} > ?) OR "
244
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-')+1) AS INTEGER) > ?)"
245
- f")")
251
+ f"CAST({column} AS INTEGER) > ? OR "
252
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-') + 1) AS INTEGER) > ?"
253
+ f")")
246
254
  params.extend([val, val])
247
255
  elif inverted_op == '<':
248
256
  conditions.append(f"("
249
- f"({column} < ?) OR "
250
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-')-1) AS INTEGER) < ?)"
251
- f")")
257
+ f"CAST({column} AS INTEGER) < ? OR "
258
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-') - 1) AS INTEGER) < ?"
259
+ f")")
252
260
  params.extend([val, val])
253
261
  elif inverted_op == '>=':
254
262
  conditions.append(f"("
255
- f"({column} >= ?) OR "
256
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-')+1) AS INTEGER) >= ?)"
257
- f")")
263
+ f"CAST({column} AS INTEGER) >= ? OR "
264
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, INSTR({column}, '-') + 1) AS INTEGER) >= ?"
265
+ f")")
258
266
  params.extend([val, val])
259
267
  elif inverted_op == '<=':
260
268
  conditions.append(f"("
261
- f"({column} <= ?) OR "
262
- f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-')-1) AS INTEGER) <= ?)"
263
- f")")
269
+ f"CAST({column} AS INTEGER) <= ? OR "
270
+ f"{column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-') - 1) AS INTEGER) <= ?"
271
+ f")")
264
272
  params.extend([val, val])
265
273
  elif inverted_op == '=':
266
274
  conditions.append(f"("
267
- f"({column} = ?) OR "
275
+ f"CAST({column} AS INTEGER) = ? OR "
268
276
  f"({column} LIKE '%-%' AND CAST(SUBSTR({column}, 1, INSTR({column}, '-')-1) AS INTEGER) <= ? AND CAST(SUBSTR({column}, INSTR({column}, '-')+1) AS INTEGER) >= ?)"
269
277
  f")")
270
278
  params.extend([val, val, val])
@@ -642,21 +650,48 @@ class Database:
642
650
  seen.add(age)
643
651
  unique_pairs.append((age, unc))
644
652
 
653
+ unique_institutes = set()
654
+ for institutes in institutes_list:
655
+ for institute in institutes.split(', '):
656
+ unique_institutes.add(institute.strip())
657
+
658
+ unique_projects = set()
659
+ for projects in projects_list:
660
+ for project in projects.split(', '):
661
+ unique_projects.add(project.strip())
662
+
663
+ unique_radar_instruments = set()
664
+ for radar_instruments in radar_instruments_list:
665
+ for radar_instrument in radar_instruments.split(', '):
666
+ unique_radar_instruments.add(radar_instrument.strip())
667
+
668
+ unique_basins = set()
669
+ for basins in basin_list:
670
+ for basin in basins.split(', '):
671
+ unique_basins.add(basin.strip())
672
+
673
+ df_dataset_sorted = pd.DataFrame({
674
+ 'dataset': metadata['dataset'],
675
+ 'reference': metadata['reference'],
676
+ 'DOI_dataset': metadata['DOI_dataset'],
677
+ 'DOI_publication': metadata['DOI_publication']
678
+ }).sort_values('dataset')
679
+
645
680
  sorted_ages, sorted_age_unc = zip(*unique_pairs) if unique_pairs else ([], [])
646
681
  metadata['age'] = [str(age) for age in sorted_ages]
647
682
  metadata['age_unc'] = [str(age_unc) for age_unc in sorted_age_unc]
648
683
  metadata['var'] = sorted(set(vars_list))
649
- metadata['institute'] = sorted(set(institutes_list))
650
- metadata['project'] = sorted(set(projects_list))
684
+ metadata['institute'] = sorted(set(unique_institutes))
685
+ metadata['project'] = sorted(set(unique_projects))
651
686
  metadata['acquisition_year'] = sorted(set(acq_years_list))
652
- metadata['dataset'] = list(dict.fromkeys(metadata['dataset']))
653
- metadata['reference'] = list(dict.fromkeys(metadata['reference']))
654
- metadata['DOI_dataset'] = list(dict.fromkeys(metadata['DOI_dataset']))
655
- metadata['DOI_publication'] = list(dict.fromkeys(metadata['DOI_publication']))
687
+ metadata['dataset'] = list(dict.fromkeys(df_dataset_sorted['dataset']))
688
+ metadata['reference'] = list(dict.fromkeys(df_dataset_sorted['reference']))
689
+ metadata['DOI_dataset'] = list(dict.fromkeys(df_dataset_sorted['DOI_dataset']))
690
+ metadata['DOI_publication'] = list(dict.fromkeys(df_dataset_sorted['DOI_publication']))
656
691
  metadata['flight_id'] = list(set(metadata['flight_id']))
657
- metadata['radar_instrument'] = sorted(set(radar_instruments_list))
692
+ metadata['radar_instrument'] = sorted(set(unique_radar_instruments))
658
693
  metadata['region'] = sorted(set(region_list))
659
- metadata['IMBIE_basin'] = sorted(set(basin_list))
694
+ metadata['IMBIE_basin'] = sorted(set(unique_basins))
660
695
 
661
696
  if retain_query:
662
697
  self._md = metadata
@@ -717,6 +752,7 @@ class Database:
717
752
  data_dir: Optional[str] = None,
718
753
  downsampling_factor: Optional[str] = None,
719
754
  disable_tqdm: bool = False,
755
+ fraction_depth: Optional[bool] = False,
720
756
  ) -> Generator[Tuple[pd.DataFrame, Dict]]:
721
757
  """
722
758
  Generates xarray Datasets from HDF5 files, one at a time, with lazy loading.
@@ -770,13 +806,21 @@ class Database:
770
806
  irh_index = irh_index[0]
771
807
  age_impl.append(age)
772
808
 
773
- df[int(age)] = ds[var][::downsampling_factor, irh_index]
809
+ df[age] = ds[var][::downsampling_factor, irh_index]
810
+ if fraction_depth:
811
+ if 'ICE_THK' in ds.keys():
812
+ df[age] *= 100/ds['ICE_THK'][::downsampling_factor]
813
+ else:
814
+ df[age] = np.nan
815
+ # print(f'WARNING: flight line {file_md['flight_id']} in {file_md['dataset']} does not have ICE_THK, cannot compute fraction depth, will return NaN')
774
816
 
775
817
  else:
776
818
  if var in ds.keys():
777
819
  var_impl.append(var)
778
820
  df[var] = ds[var][::downsampling_factor]
779
821
 
822
+ if 0 < len(age_impl) <= 1:
823
+ age_impl = list(age_impl)[0]
780
824
  metadata = {
781
825
  'dataset': file_md['dataset'],
782
826
  'var': var_impl,
@@ -3,23 +3,42 @@ from tqdm import tqdm
3
3
  import glob
4
4
  import pandas as pd
5
5
  import numpy as np
6
- import xarray as xr
7
6
  import sqlite3
8
7
  import h5py
9
8
  import json
10
9
  import ast
11
10
 
12
11
  class IndexDatabase:
13
- def __init__(self, database_dir: str, file_db: str = 'AntADatabase.db', index: str = 'references.json'):
12
+ def __init__(self, database_dir: str, file_db: str = 'AntADatabase.db') -> None:
14
13
  self._db_dir = database_dir
15
14
  self._file_db = os.path.join(self._db_dir, file_db)
16
- self._file_index = index
17
- with open(os.path.join(self._db_dir, self._file_index), 'r', encoding='utf-8') as f:
18
- self._index = json.load(f)
15
+ self._disable_tqdm = os.getenv("JUPYTER_BOOK_BUILD", False)
19
16
 
20
17
  def index_database(self):
21
18
  h5_files = []
22
- for ref in self._index:
19
+
20
+ dirs = glob.glob(f'{self._db_dir}/*/')
21
+ self._dataset_list = []
22
+
23
+ for d in dirs:
24
+ with open(f'{d}/raw_md.json', 'r', encoding='utf-8') as f:
25
+ unique_data = []
26
+ seen = set()
27
+ index = json.load(f)
28
+ for item in index:
29
+ combo = (item['dataset'], item['DOI publication'], item['DOI dataset'])
30
+ if combo not in seen:
31
+ seen.add(combo)
32
+ unique_data.append({
33
+ "dataset": item['dataset'],
34
+ "citation": item['citation'],
35
+ "DOI publication": item['DOI publication'],
36
+ "DOI dataset": item['DOI dataset']
37
+ })
38
+ for unique in unique_data:
39
+ self._dataset_list.append(unique)
40
+
41
+ for ref in self._dataset_list:
23
42
  found_files = glob.glob(f'{self._db_dir}/{ref['dataset']}/h5/*.h5', recursive=False)
24
43
  if found_files:
25
44
  h5_files.extend(found_files)
@@ -44,11 +63,11 @@ class IndexDatabase:
44
63
  )
45
64
  ''')
46
65
 
47
- for row in self._index:
66
+ for row in self._dataset_list:
48
67
  try:
49
68
  cursor.execute(
50
69
  'INSERT INTO sources (name, citation, DOI_dataset, DOI_publication) VALUES (?, ?, ?, ?)',
51
- (row['dataset'], row['citation'], row['DOI_dataset'], row['DOI_publication'])
70
+ (row['dataset'], row['citation'], row['DOI dataset'], row['DOI publication'])
52
71
  )
53
72
  except sqlite3.IntegrityError:
54
73
  # dataset already exists, skip
@@ -172,7 +191,7 @@ class IndexDatabase:
172
191
  )
173
192
  ''')
174
193
 
175
- for f in tqdm(h5_files, desc="Indexing files"):
194
+ for f in tqdm(h5_files, desc="Indexing files", disable=self._disable_tqdm):
176
195
  _, file_name = os.path.split(f)
177
196
 
178
197
  with h5py.File(f, 'r') as f:
@@ -177,6 +177,7 @@ class Plotting:
177
177
  self,
178
178
  metadata: Union[None, Dict, 'MetadataResult'] = None,
179
179
  downsampling_factor: Optional[int] = None,
180
+ fraction_depth: Optional[bool] = False,
180
181
  title: Optional[str] = None,
181
182
  xlim: Optional[tuple] = (None, None),
182
183
  ylim: Optional[tuple] = (None, None),
@@ -197,6 +198,7 @@ class Plotting:
197
198
  color_by='var',
198
199
  metadata=metadata,
199
200
  downsampling_factor=downsampling_factor,
201
+ fraction_depth=fraction_depth,
200
202
  title=title,
201
203
  xlim=xlim,
202
204
  ylim=ylim,
@@ -261,6 +263,7 @@ class Plotting:
261
263
  self,
262
264
  metadata: Union[None, Dict, 'MetadataResult'] = None,
263
265
  elevation: Optional[bool] = False,
266
+ fraction_depth: Optional[bool] = False,
264
267
  downsampling_factor: Optional[int] = None,
265
268
  title: Optional[str] = None,
266
269
  xlim: Optional[tuple] = (None, None),
@@ -402,9 +405,8 @@ class Plotting:
402
405
  else:
403
406
  var = var[0]
404
407
 
405
-
406
408
  all_dfs = []
407
- for ds, _ in tqdm(self._db.data_generator(metadata, downsampling_factor=downsampling_factor, disable_tqdm=True), desc="Plotting", total=total_traces, unit='trace', disable=self._disable_tqdm):
409
+ for ds, _ in tqdm(self._db.data_generator(metadata, downsampling_factor=downsampling_factor, disable_tqdm=True, fraction_depth=fraction_depth), desc="Plotting", total=total_traces, unit='trace', disable=self._disable_tqdm):
408
410
  all_dfs.append(ds)
409
411
  df = pd.concat(all_dfs)
410
412
 
@@ -478,37 +480,31 @@ class Plotting:
478
480
  title = f'AntADatabase Basal Unit'
479
481
  scatter = plt.scatter(df['PSX']/1000, df['PSY']/1000, c=df[var], cmap=cmap, s=marker_size, vmin=vmin, vmax=vmax, linewidths=0, rasterized=True)
480
482
 
481
- elif var in ['IRH_DEPTH', 'IRH_FRAC_DEPTH']:
482
- if var == 'IRH_DEPTH':
483
- label = f'IRH Depth [m]'
484
- if cmap == None:
485
- cmap = cmaps.torch_r
486
- extend = 'both'
487
- age = list(metadata['age'])
488
- if not title:
483
+ elif var in ['IRH_DEPTH']:
484
+ if cmap == None:
485
+ cmap = cmaps.torch_r
486
+ extend = 'both'
487
+ age = list(metadata['age'])
488
+ if not title:
489
+ if fraction_depth:
490
+ title = f'AntADatabase IRH Fraction Depth'
491
+ else:
489
492
  title = f'AntADatabase IRH Depth'
490
- if len(age) > 1:
491
- print('WARNING: Multiple layers provided: ', age,
492
- '\nSelect a unique age for better results')
493
- elif len(age) == 0:
494
- print('No layer provided, please provide a valid age.')
495
- return
496
- elif var == 'IRH_FRAC_DEPTH':
497
- if cmap == None:
498
- cmap = cmaps.torch_r
499
- extend = 'both'
500
- age = list(metadata['age'])
501
- if len(age) > 1:
502
- print('WARNING: Multiple layers provided: ', age,
503
- '\nSelect a unique age for better results')
504
- elif len(age) == 0:
505
- print('No layer provided, please provide a valid age.')
506
- return
507
- age = age[0]
493
+ if len(age) > 1:
494
+ print('WARNING: Multiple layers provided: ', age,
495
+ '\nSelect a unique age for better results')
496
+ elif len(age) == 0:
497
+ print('No layer provided, please provide a valid age.')
498
+ return
499
+ if fraction_depth:
508
500
  label = r'IRH Fractional Depth [\%]'
501
+ if vmin is None: vmin = 0
502
+ if vmax is None: vmax = 100
503
+ else:
504
+ label = f'IRH Depth [m]'
509
505
 
510
506
  for age in metadata['age']:
511
- scatter = plt.scatter(df['PSX']/1000, df['PSY']/1000, c=df[int(age)], cmap=cmap, s=marker_size, vmin=vmin, vmax=vmax, linewidths=0, rasterized=True)
507
+ scatter = plt.scatter(df['PSX']/1000, df['PSY']/1000, c=df[age], cmap=cmap, s=marker_size, vmin=vmin, vmax=vmax, linewidths=0, rasterized=True)
512
508
 
513
509
  if color_by == 'transect_1D':
514
510
  flight_id = list(metadata['flight_id'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anta_database
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: SQLite database for the AntArchitecture Community Data
5
5
  Author: Antoine Hermant
6
6
  Author-email: antoine.hermant@etik.com
@@ -41,6 +41,7 @@ book/contact.md
41
41
  book/contribute.md
42
42
  book/intro.md
43
43
  book/logo.png
44
+ book/pism_example.ipynb
44
45
  book/quick_start.ipynb
45
46
  book/references.bib
46
47
  book/requirements.txt
@@ -5,6 +5,7 @@ format: jb-book
5
5
  root: intro
6
6
  chapters:
7
7
  - file: quick_start
8
+ - file: pism_example
8
9
  - file: advanced
9
10
  - file: contact
10
11
  - file: contribute
@@ -0,0 +1,154 @@
1
+ ---
2
+ jupytext:
3
+ formats: md:myst
4
+ text_representation:
5
+ extension: .md
6
+ format_name: myst
7
+ format_version: 0.13
8
+ jupytext_version: 1.11.5
9
+ kernelspec:
10
+ display_name: Python 3
11
+ language: python
12
+ name: python3
13
+ ---
14
+
15
+ # Advanced: Managing the database
16
+
17
+ ## Reindexing
18
+
19
+ You may want for example to update the age of the layers in a particular dataset. For this, you just need to modify the ages in the file called `raw_md.json` located under a dataset directory. Then, reindex with the `IndexDatabase` class:
20
+
21
+ ```
22
+ from anta_database import IndexDatabase
23
+
24
+ db_path = '/home/anthe/documents/data/isochrones/AntADatabase/'
25
+ indexing = IndexDatabase(db_path)
26
+ indexing.index_database()
27
+ ```
28
+
29
+ Or simply use the `index_database=True` argument when initializing the `Database`, which runs the IndexDatabase class itself:
30
+ ```
31
+ from anta_database import Database
32
+
33
+ db_path = '/home/anthe/documents/data/isochrones/AntADatabase/'
34
+ db = Database(db_path, index_database=True)
35
+ ```
36
+
37
+ ## (Re)compile the database
38
+
39
+ You can (re)compile the database, if for example you modify some data in the raw directories or if you add a dataset. For this, make sure to follow the structure:
40
+
41
+ ```
42
+ AntADatabase/
43
+ ├── AntADatabase.db
44
+ ├── FirstAuthor_YYYY
45
+ ├── raw_md.json # file containing information about the dataset, take example on an existing dataset and see the section below
46
+ ├── original_new_column_names.csv # first row: names of columns to keep from raw files, second row: how the columns should be renamed (see section below)
47
+ ├── raw/ #Directory with the original files to process
48
+ └── h5/ # Directory were the processed files will be written (it will be created in the process)
49
+ ```
50
+
51
+ Note that the header information in the raw files should be commented out with '#'. The column names of the dataframe should be kept.
52
+ Then use the `CompileDatabase class to compile the database:
53
+
54
+ ```
55
+ from anta_database import CompileDatabase
56
+
57
+ dir_path_list = [ # list of the dataset subdirectories to compile
58
+ './Winter_2018',
59
+ './Sanderson_2024',
60
+ './Franke_2025',
61
+ './Cavitte_2020',
62
+ './Beem_2021',
63
+ './Bodart_2021/',
64
+ './Muldoon_2023/',
65
+ './Ashmore_2020/',
66
+ ]
67
+
68
+ compiler = CompileDatabase(dir_path_list)
69
+ compiler.compile()
70
+ ```
71
+
72
+ Then reindex (see above). Furthermore, if the depth is not given in meters but TWT, you should set the wave\_speed (units should match values in the file) for conversion and firn\_correction (meters):
73
+
74
+ ```
75
+ dir_path = './Wang_2023'
76
+ compiler = CompileDatabase(dir_path, wave_speed=0.1685, firn_correction=15.5)
77
+ compiler.compile()
78
+ ```
79
+
80
+ ## The JSON file
81
+
82
+ The `raw_md.json` defines all the metadata of a raw file which we be associated to the corresponding data in the database. This will allow to query the database with all the implemented filters. One should provide all the information that exists if possible:
83
+ One important field is the `data type`, which specifies if the raw files are organised per layers or per flight lines. There are then two possibilities:
84
+
85
+ `data type`: "layer"
86
+ | field | Description | Example Values |
87
+ |-------------------|-------------------------------------------------------------------------------------------------|-------------------------------------|
88
+ | `data type` | `layer` | `'layer'` |
89
+ | `raw file` | Name of the raw file in the raw folder with the correct extension (csv, txt, tab). | `'OIA_EDC_IRH1.csv'` |
90
+ | `dataset` | Name of the dataset corresponding dataset (usually same as the folder name) | `'Cavitte_2020'` |
91
+ | `institute` | Institute(s) that produced the data. | `'BAS'`, `['AWI', 'NASA']` |
92
+ | `project` | Project(s) under which the data were collected. | `'OIB'` |
93
+ | `acquisition year`| Year(s) in which the radar data were acquired. Can be a range. | `'2000-2010'`, `'2005'` |
94
+ | `age` | Age in years before present of the layer. | `'10000'` |
95
+ | `age uncertainty` | Uncertainty on the age of the layer | `'800'` |
96
+ | `citation` | How the dataset should be cited | `'Cavitte et al. 2020'` |
97
+ | `DOI dataset` | DOI that directly links to the original dataset (e.g zenodo, USAP-DC...) | `'https://doi.org/10.15784/601411'` |
98
+ | `DOI publication` | DOI of the associated publication that published the data | `'https://doi.org/10.5194/essd-13-4759-2021'` |
99
+ Example of such a dataset is Cavitte_2020
100
+
101
+ `data type`: "flight line"
102
+ Replace the `raw file` entry by `IRH name` as they are named in the raw files. Then the compilation will use the file name for flight ID.
103
+ | field | Description | Example Values |
104
+ |-------------------|-------------------------------------------------------------------------------------------------|-------------------------------------|
105
+ | `data type` | `flight line` | `'flight line'` |
106
+ | `IRH name` | Name of the IRH layer as is it named in the raw file (e.g. column name) | `'IRH1'` |
107
+ | `age` | Age in years before present of the layer. | `'10000'` |
108
+ Example of such a dataset is Wang_2023
109
+
110
+ ## The CSV column file
111
+
112
+ The original_new_column_names.csv pairs the columns in the raw file with associated variables in the database. The minimal information in the file should be:
113
+ - Polar Stereographic coordinates PSX and PSY or lon, lat (will be converted to PSX, PSY)
114
+ - The IRH depth
115
+
116
+ More information can be provided such as flight ID, Ice thickness, acquisition year etc. But the columns should be renamed following the database convention:
117
+ | variable | variable name convention in the Database |
118
+ |-------------------|-------------------------------------------------------------------------------------------------|
119
+ | IRH depth | `IRH_DEPTH` |
120
+ | Ice thickness | `ICE_THK` |
121
+ | Surface elevation | `SURF_ELEV` |
122
+ | Bed elevation | `BED_ELEV` |
123
+ | Distance along transect | `DIST` |
124
+ | `acquisition year`| `acq_year` |
125
+ | `Flight ID` | `Flight_ID` |
126
+
127
+ Note if the acquisition year column is not directly a year YYYY but a string that contains the year, add a third row to provide the pattern to extract the acquisition year from the string.
128
+
129
+ Example of original_new_column_names.csv file:
130
+ ```
131
+ timestamp,x,y,thk_m,bedelv_mABSL,IRHdepth_m,line
132
+ acq_year,PSX,PSY,ICE_THK,BED_ELEV,IRH_DEPTH,Flight_ID
133
+ YYYYxxxxxxxxxxxxxxxxxxxxxx
134
+ ```
135
+
136
+ For a dataset organized by flight lines, so many IRH depth in the raw files, the original_new_column_names.csv should look like:
137
+ ```
138
+ Longitude,Latitude,TWT [ns] (of bed),TWT [ns] (of H1),TWT [ns] (of H2),TWT [ns] (of H3),TWT [ns] (of H4),TWT [ns] (of H5),TWT [ns] (of H6),TWT [ns] (of H7)
139
+ lon,lat,ICE_THK,IRH1,IRH2,IRH3,IRH4,IRH5,IRH6,IRH7
140
+ ```
141
+ Where the new column names for the layers correspond to the names you provided in the JSON file.
142
+
143
+
144
+ ## Notes
145
+ ### Multiprocessing
146
+ The compilation uses multiprocessing tools for parallel processing. It first finds all the raw files to process and then distribute the processes on multiple processors. By default, it uses all available cpus on the machine minus 1 (to not completely freeze the machine). However, if there are fewer tasks than cpus (fewer files to process), it will use only as many cpus as there are tasks.
147
+ To manually fix the number of cpus used during the compilation:
148
+
149
+ ```
150
+ compiler.compile(cpus=2) # Or any integer of choice
151
+ ```
152
+
153
+ ### Compilation process
154
+ Needs to be updated.