disdrodb 0.0.21__py3-none-any.whl → 0.1.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 (264) hide show
  1. disdrodb/__init__.py +132 -15
  2. disdrodb/_config.py +4 -2
  3. disdrodb/_version.py +9 -4
  4. disdrodb/api/checks.py +264 -237
  5. disdrodb/api/configs.py +4 -8
  6. disdrodb/api/create_directories.py +235 -290
  7. disdrodb/api/info.py +217 -26
  8. disdrodb/api/io.py +295 -269
  9. disdrodb/api/path.py +597 -173
  10. disdrodb/api/search.py +486 -0
  11. disdrodb/{metadata/scripts → cli}/disdrodb_check_metadata_archive.py +12 -7
  12. disdrodb/{utils/pandas.py → cli/disdrodb_data_archive_directory.py} +9 -18
  13. disdrodb/cli/disdrodb_download_archive.py +86 -0
  14. disdrodb/cli/disdrodb_download_metadata_archive.py +53 -0
  15. disdrodb/cli/disdrodb_download_station.py +84 -0
  16. disdrodb/{api/scripts → cli}/disdrodb_initialize_station.py +22 -10
  17. disdrodb/cli/disdrodb_metadata_archive_directory.py +32 -0
  18. disdrodb/{data_transfer/scripts/disdrodb_download_station.py → cli/disdrodb_open_data_archive.py} +22 -22
  19. disdrodb/cli/disdrodb_open_logs_directory.py +69 -0
  20. disdrodb/{data_transfer/scripts/disdrodb_upload_station.py → cli/disdrodb_open_metadata_archive.py} +22 -24
  21. disdrodb/cli/disdrodb_open_metadata_directory.py +71 -0
  22. disdrodb/cli/disdrodb_open_product_directory.py +74 -0
  23. disdrodb/cli/disdrodb_open_readers_directory.py +32 -0
  24. disdrodb/{l0/scripts → cli}/disdrodb_run_l0.py +38 -31
  25. disdrodb/{l0/scripts → cli}/disdrodb_run_l0_station.py +32 -30
  26. disdrodb/{l0/scripts → cli}/disdrodb_run_l0a.py +30 -21
  27. disdrodb/{l0/scripts → cli}/disdrodb_run_l0a_station.py +24 -33
  28. disdrodb/{l0/scripts → cli}/disdrodb_run_l0b.py +30 -21
  29. disdrodb/{l0/scripts → cli}/disdrodb_run_l0b_station.py +25 -34
  30. disdrodb/cli/disdrodb_run_l0c.py +130 -0
  31. disdrodb/cli/disdrodb_run_l0c_station.py +129 -0
  32. disdrodb/cli/disdrodb_run_l1.py +122 -0
  33. disdrodb/cli/disdrodb_run_l1_station.py +121 -0
  34. disdrodb/cli/disdrodb_run_l2e.py +122 -0
  35. disdrodb/cli/disdrodb_run_l2e_station.py +122 -0
  36. disdrodb/cli/disdrodb_run_l2m.py +122 -0
  37. disdrodb/cli/disdrodb_run_l2m_station.py +122 -0
  38. disdrodb/cli/disdrodb_upload_archive.py +105 -0
  39. disdrodb/cli/disdrodb_upload_station.py +98 -0
  40. disdrodb/configs.py +90 -25
  41. disdrodb/data_transfer/__init__.py +22 -0
  42. disdrodb/data_transfer/download_data.py +87 -90
  43. disdrodb/data_transfer/upload_data.py +64 -37
  44. disdrodb/data_transfer/zenodo.py +15 -18
  45. disdrodb/docs.py +1 -1
  46. disdrodb/issue/__init__.py +17 -4
  47. disdrodb/issue/checks.py +10 -23
  48. disdrodb/issue/reader.py +9 -12
  49. disdrodb/issue/writer.py +14 -17
  50. disdrodb/l0/__init__.py +17 -26
  51. disdrodb/l0/check_configs.py +35 -23
  52. disdrodb/l0/check_standards.py +32 -42
  53. disdrodb/l0/configs/{Thies_LPM → LPM}/bins_diameter.yml +44 -44
  54. disdrodb/l0/configs/{Thies_LPM → LPM}/bins_velocity.yml +40 -40
  55. disdrodb/l0/configs/LPM/l0a_encodings.yml +80 -0
  56. disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_cf_attrs.yml +62 -59
  57. disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_encodings.yml +9 -9
  58. disdrodb/l0/configs/{Thies_LPM → LPM}/raw_data_format.yml +245 -245
  59. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_diameter.yml +66 -66
  60. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_velocity.yml +64 -64
  61. disdrodb/l0/configs/PARSIVEL/l0a_encodings.yml +32 -0
  62. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_cf_attrs.yml +22 -20
  63. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_encodings.yml +17 -17
  64. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/raw_data_format.yml +77 -77
  65. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_diameter.yml +64 -64
  66. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_velocity.yml +64 -64
  67. disdrodb/l0/configs/PARSIVEL2/l0a_encodings.yml +39 -0
  68. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_cf_attrs.yml +24 -22
  69. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_encodings.yml +20 -20
  70. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/raw_data_format.yml +98 -98
  71. disdrodb/l0/configs/{RD_80 → RD80}/bins_diameter.yml +40 -40
  72. disdrodb/l0/configs/RD80/l0a_encodings.yml +16 -0
  73. disdrodb/l0/configs/{RD_80 → RD80}/l0b_cf_attrs.yml +3 -3
  74. disdrodb/l0/configs/RD80/l0b_encodings.yml +135 -0
  75. disdrodb/l0/configs/{RD_80 → RD80}/raw_data_format.yml +48 -48
  76. disdrodb/l0/l0_reader.py +216 -340
  77. disdrodb/l0/l0a_processing.py +237 -208
  78. disdrodb/l0/l0b_nc_processing.py +227 -80
  79. disdrodb/l0/l0b_processing.py +93 -173
  80. disdrodb/l0/l0c_processing.py +627 -0
  81. disdrodb/l0/readers/{ARM → LPM/ARM}/ARM_LPM.py +36 -58
  82. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +226 -0
  83. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +185 -0
  84. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +183 -0
  85. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +179 -0
  86. disdrodb/l0/readers/{UK → LPM/UK}/DIVEN.py +14 -35
  87. disdrodb/l0/readers/PARSIVEL/AUSTRALIA/MELBOURNE_2007_PARSIVEL.py +157 -0
  88. disdrodb/l0/readers/PARSIVEL/CHINA/CHONGQING.py +113 -0
  89. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/ARCTIC_2021.py +40 -57
  90. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/COMMON_2011.py +37 -54
  91. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/DAVOS_2009_2011.py +34 -51
  92. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_2009.py +34 -51
  93. disdrodb/l0/readers/{EPFL/PARADISO_2014.py → PARSIVEL/EPFL/EPFL_ROOF_2008.py} +38 -50
  94. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2010.py +105 -0
  95. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2011.py +34 -51
  96. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2012.py +33 -51
  97. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GENEPI_2007.py +25 -44
  98. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007.py +25 -44
  99. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007_2.py +25 -44
  100. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HPICONET_2010.py +34 -51
  101. disdrodb/l0/readers/{EPFL/EPFL_ROOF_2010.py → PARSIVEL/EPFL/HYMEX_LTE_SOP2.py} +37 -50
  102. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP3.py +111 -0
  103. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HYMEX_LTE_SOP4.py +36 -54
  104. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2018.py +34 -52
  105. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2019.py +38 -56
  106. disdrodb/l0/readers/PARSIVEL/EPFL/PARADISO_2014.py +105 -0
  107. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PARSIVEL_2007.py +27 -45
  108. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PLATO_2019.py +24 -44
  109. disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019.py +140 -0
  110. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RACLETS_2019_WJF.py +41 -59
  111. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RIETHOLZBACH_2011.py +34 -51
  112. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2017.py +117 -0
  113. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2019.py +137 -0
  114. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/UNIL_2022.py +42 -55
  115. disdrodb/l0/readers/PARSIVEL/GPM/IFLOODS.py +104 -0
  116. disdrodb/l0/readers/{GPM → PARSIVEL/GPM}/LPVEX.py +29 -48
  117. disdrodb/l0/readers/PARSIVEL/GPM/MC3E.py +184 -0
  118. disdrodb/l0/readers/PARSIVEL/NCAR/CCOPE_2015.py +113 -0
  119. disdrodb/l0/readers/{NCAR/VORTEX_SE_2016_P1.py → PARSIVEL/NCAR/OWLES_MIPS.py} +46 -72
  120. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +125 -0
  121. disdrodb/l0/readers/{NCAR/OWLES_MIPS.py → PARSIVEL/NCAR/PLOWS_MIPS.py} +45 -64
  122. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +114 -0
  123. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +176 -0
  124. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +183 -0
  125. disdrodb/l0/readers/{ARM/ARM_LD.py → PARSIVEL2/ARM/ARM_PARSIVEL2.py} +27 -50
  126. disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +163 -0
  127. disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +163 -0
  128. disdrodb/l0/readers/{DENMARK → PARSIVEL2/DENMARK}/EROSION_nc.py +14 -35
  129. disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +119 -0
  130. disdrodb/l0/readers/PARSIVEL2/GPM/GCPEX.py +104 -0
  131. disdrodb/l0/readers/PARSIVEL2/GPM/NSSTC.py +176 -0
  132. disdrodb/l0/readers/PARSIVEL2/ITALY/GID_PARSIVEL2.py +32 -0
  133. disdrodb/l0/readers/PARSIVEL2/MEXICO/OH_IIUNAM_nc.py +56 -0
  134. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +120 -0
  135. disdrodb/l0/readers/{NCAR → PARSIVEL2/NCAR}/PECAN_MIPS.py +45 -64
  136. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +181 -0
  137. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +160 -0
  138. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +160 -0
  139. disdrodb/l0/readers/{NCAR/PLOWS_MIPS.py → PARSIVEL2/NCAR/VORTEX_SE_2016_P1.py} +49 -66
  140. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +118 -0
  141. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +152 -0
  142. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT.py +166 -0
  143. disdrodb/l0/readers/{NCAR/RELAMPAGO_RD80.py → RD80/BRAZIL/CHUVA_RD80.py} +36 -60
  144. disdrodb/l0/readers/{BRAZIL → RD80/BRAZIL}/GOAMAZON_RD80.py +36 -55
  145. disdrodb/l0/readers/{NCAR → RD80/NCAR}/CINDY_2011_RD80.py +35 -54
  146. disdrodb/l0/readers/{BRAZIL/CHUVA_RD80.py → RD80/NCAR/RELAMPAGO_RD80.py} +40 -54
  147. disdrodb/l0/readers/template_reader_raw_netcdf_data.py +62 -0
  148. disdrodb/l0/readers/{reader_template.py → template_reader_raw_text_data.py} +20 -44
  149. disdrodb/l0/routines.py +885 -581
  150. disdrodb/l0/standards.py +72 -236
  151. disdrodb/l0/template_tools.py +104 -109
  152. disdrodb/l1/__init__.py +17 -0
  153. disdrodb/l1/beard_model.py +716 -0
  154. disdrodb/l1/encoding_attrs.py +620 -0
  155. disdrodb/l1/fall_velocity.py +260 -0
  156. disdrodb/l1/filters.py +192 -0
  157. disdrodb/l1/processing.py +200 -0
  158. disdrodb/l1/resampling.py +236 -0
  159. disdrodb/l1/routines.py +357 -0
  160. disdrodb/l1_env/__init__.py +17 -0
  161. disdrodb/l1_env/routines.py +38 -0
  162. disdrodb/l2/__init__.py +17 -0
  163. disdrodb/l2/empirical_dsd.py +1735 -0
  164. disdrodb/l2/event.py +388 -0
  165. disdrodb/l2/processing.py +519 -0
  166. disdrodb/l2/processing_options.py +213 -0
  167. disdrodb/l2/routines.py +868 -0
  168. disdrodb/metadata/__init__.py +9 -2
  169. disdrodb/metadata/checks.py +165 -118
  170. disdrodb/metadata/download.py +81 -0
  171. disdrodb/metadata/geolocation.py +146 -0
  172. disdrodb/metadata/info.py +20 -13
  173. disdrodb/metadata/manipulation.py +1 -1
  174. disdrodb/metadata/reader.py +59 -8
  175. disdrodb/metadata/search.py +77 -144
  176. disdrodb/metadata/standards.py +7 -8
  177. disdrodb/metadata/writer.py +8 -14
  178. disdrodb/psd/__init__.py +38 -0
  179. disdrodb/psd/fitting.py +2146 -0
  180. disdrodb/psd/models.py +774 -0
  181. disdrodb/routines.py +1176 -0
  182. disdrodb/scattering/__init__.py +28 -0
  183. disdrodb/scattering/axis_ratio.py +344 -0
  184. disdrodb/scattering/routines.py +456 -0
  185. disdrodb/utils/__init__.py +17 -0
  186. disdrodb/utils/attrs.py +208 -0
  187. disdrodb/utils/cli.py +269 -0
  188. disdrodb/utils/compression.py +60 -42
  189. disdrodb/utils/dask.py +62 -0
  190. disdrodb/utils/decorators.py +110 -0
  191. disdrodb/utils/directories.py +107 -46
  192. disdrodb/utils/encoding.py +127 -0
  193. disdrodb/utils/list.py +29 -0
  194. disdrodb/utils/logger.py +168 -46
  195. disdrodb/utils/time.py +657 -0
  196. disdrodb/utils/warnings.py +30 -0
  197. disdrodb/utils/writer.py +57 -0
  198. disdrodb/utils/xarray.py +138 -47
  199. disdrodb/utils/yaml.py +0 -1
  200. disdrodb/viz/__init__.py +17 -0
  201. disdrodb/viz/plots.py +17 -0
  202. disdrodb-0.1.0.dist-info/METADATA +321 -0
  203. disdrodb-0.1.0.dist-info/RECORD +216 -0
  204. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/WHEEL +1 -1
  205. disdrodb-0.1.0.dist-info/entry_points.txt +30 -0
  206. disdrodb/data_transfer/scripts/disdrodb_download_archive.py +0 -53
  207. disdrodb/data_transfer/scripts/disdrodb_upload_archive.py +0 -57
  208. disdrodb/l0/configs/OTT_Parsivel/l0a_encodings.yml +0 -32
  209. disdrodb/l0/configs/OTT_Parsivel2/l0a_encodings.yml +0 -39
  210. disdrodb/l0/configs/RD_80/l0a_encodings.yml +0 -16
  211. disdrodb/l0/configs/RD_80/l0b_encodings.yml +0 -135
  212. disdrodb/l0/configs/Thies_LPM/l0a_encodings.yml +0 -80
  213. disdrodb/l0/io.py +0 -257
  214. disdrodb/l0/l0_processing.py +0 -1091
  215. disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_OTT.py +0 -178
  216. disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_THIES.py +0 -247
  217. disdrodb/l0/readers/BRAZIL/CHUVA_LPM.py +0 -204
  218. disdrodb/l0/readers/BRAZIL/CHUVA_OTT.py +0 -183
  219. disdrodb/l0/readers/BRAZIL/GOAMAZON_LPM.py +0 -204
  220. disdrodb/l0/readers/BRAZIL/GOAMAZON_OTT.py +0 -183
  221. disdrodb/l0/readers/CHINA/CHONGQING.py +0 -131
  222. disdrodb/l0/readers/EPFL/EPFL_ROOF_2008.py +0 -128
  223. disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP2.py +0 -127
  224. disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP3.py +0 -129
  225. disdrodb/l0/readers/EPFL/RACLETS_2019.py +0 -158
  226. disdrodb/l0/readers/EPFL/SAMOYLOV_2017.py +0 -136
  227. disdrodb/l0/readers/EPFL/SAMOYLOV_2019.py +0 -158
  228. disdrodb/l0/readers/FRANCE/SIRTA_OTT2.py +0 -138
  229. disdrodb/l0/readers/GPM/GCPEX.py +0 -123
  230. disdrodb/l0/readers/GPM/IFLOODS.py +0 -123
  231. disdrodb/l0/readers/GPM/MC3E.py +0 -123
  232. disdrodb/l0/readers/GPM/NSSTC.py +0 -164
  233. disdrodb/l0/readers/ITALY/GID.py +0 -199
  234. disdrodb/l0/readers/MEXICO/OH_IIUNAM_nc.py +0 -92
  235. disdrodb/l0/readers/NCAR/CCOPE_2015.py +0 -133
  236. disdrodb/l0/readers/NCAR/PECAN_FP3.py +0 -137
  237. disdrodb/l0/readers/NCAR/PECAN_MOBILE.py +0 -144
  238. disdrodb/l0/readers/NCAR/RELAMPAGO_OTT.py +0 -195
  239. disdrodb/l0/readers/NCAR/SNOWIE_PJ.py +0 -172
  240. disdrodb/l0/readers/NCAR/SNOWIE_SB.py +0 -179
  241. disdrodb/l0/readers/NCAR/VORTEX2_2009.py +0 -133
  242. disdrodb/l0/readers/NCAR/VORTEX2_2010.py +0 -188
  243. disdrodb/l0/readers/NCAR/VORTEX2_2010_UF.py +0 -191
  244. disdrodb/l0/readers/NCAR/VORTEX_SE_2016_P2.py +0 -135
  245. disdrodb/l0/readers/NCAR/VORTEX_SE_2016_PIPS.py +0 -170
  246. disdrodb/l0/readers/NETHERLANDS/DELFT.py +0 -187
  247. disdrodb/l0/readers/SPAIN/SBEGUERIA.py +0 -179
  248. disdrodb/l0/scripts/disdrodb_run_l0b_concat.py +0 -93
  249. disdrodb/l0/scripts/disdrodb_run_l0b_concat_station.py +0 -85
  250. disdrodb/utils/netcdf.py +0 -452
  251. disdrodb/utils/scripts.py +0 -102
  252. disdrodb-0.0.21.dist-info/AUTHORS.md +0 -18
  253. disdrodb-0.0.21.dist-info/METADATA +0 -186
  254. disdrodb-0.0.21.dist-info/RECORD +0 -168
  255. disdrodb-0.0.21.dist-info/entry_points.txt +0 -15
  256. /disdrodb/l0/configs/{RD_80 → RD80}/bins_velocity.yml +0 -0
  257. /disdrodb/l0/manuals/{Thies_LPM.pdf → LPM.pdf} +0 -0
  258. /disdrodb/l0/manuals/{ODM_470.pdf → ODM470.pdf} +0 -0
  259. /disdrodb/l0/manuals/{OTT_Parsivel.pdf → PARSIVEL.pdf} +0 -0
  260. /disdrodb/l0/manuals/{OTT_Parsivel2.pdf → PARSIVEL2.pdf} +0 -0
  261. /disdrodb/l0/manuals/{PWS_100.pdf → PWS100.pdf} +0 -0
  262. /disdrodb/l0/manuals/{RD_80.pdf → RD80.pdf} +0 -0
  263. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info/licenses}/LICENSE +0 -0
  264. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/top_level.txt +0 -0
disdrodb/api/info.py CHANGED
@@ -19,19 +19,31 @@
19
19
  """Retrieve file information from DISDRODB products file names and filepaths."""
20
20
 
21
21
  import os
22
+ from collections import defaultdict
22
23
  from pathlib import Path
23
24
 
24
25
  import numpy as np
25
26
  from trollsift import Parser
26
27
 
28
+ from disdrodb.utils.time import acronym_to_seconds
29
+
27
30
  ####---------------------------------------------------------------------------
28
31
  ########################
29
32
  #### FNAME PATTERNS ####
30
33
  ########################
31
- DISDRODB_FNAME_PATTERN = (
34
+ DISDRODB_FNAME_L0_PATTERN = (
32
35
  "{product:s}.{campaign_name:s}.{station_name:s}.s{start_time:%Y%m%d%H%M%S}.e{end_time:%Y%m%d%H%M%S}"
33
36
  ".{version:s}.{data_format:s}"
34
37
  )
38
+ DISDRODB_FNAME_L2E_PATTERN = ( # also L0C and L1 --> accumulation_acronym = sample_interval
39
+ "{product:s}.{accumulation_acronym}.{campaign_name:s}.{station_name:s}.s{start_time:%Y%m%d%H%M%S}.e{end_time:%Y%m%d%H%M%S}"
40
+ ".{version:s}.{data_format:s}"
41
+ )
42
+
43
+ DISDRODB_FNAME_L2M_PATTERN = (
44
+ "{product:s}_{subproduct:s}.{accumulation_acronym}.{campaign_name:s}.{station_name:s}.s{start_time:%Y%m%d%H%M%S}.e{end_time:%Y%m%d%H%M%S}"
45
+ ".{version:s}.{data_format:s}"
46
+ )
35
47
 
36
48
  ####---------------------------------------------------------------------------.
37
49
  ##########################
@@ -41,9 +53,17 @@ DISDRODB_FNAME_PATTERN = (
41
53
 
42
54
  def _parse_filename(filename):
43
55
  """Parse the filename with trollsift."""
44
- # Retrieve information from filename
45
- p = Parser(DISDRODB_FNAME_PATTERN)
46
- info_dict = p.parse(filename)
56
+ if filename.startswith("L0A") or filename.startswith("L0B"):
57
+ p = Parser(DISDRODB_FNAME_L0_PATTERN)
58
+ info_dict = p.parse(filename)
59
+ elif filename.startswith("L2E") or filename.startswith("L1") or filename.startswith("L0C"):
60
+ p = Parser(DISDRODB_FNAME_L2E_PATTERN)
61
+ info_dict = p.parse(filename)
62
+ elif filename.startswith("L2M"):
63
+ p = Parser(DISDRODB_FNAME_L2M_PATTERN)
64
+ info_dict = p.parse(filename)
65
+ else:
66
+ raise ValueError("Not a DISDRODB product file.")
47
67
  return info_dict
48
68
 
49
69
 
@@ -54,6 +74,11 @@ def _get_info_from_filename(filename):
54
74
  info_dict = _parse_filename(filename)
55
75
  except ValueError:
56
76
  raise ValueError(f"{filename} can not be parsed. Report the issue.")
77
+
78
+ # Add additional information to info dictionary
79
+ if "accumulation_acronym" in info_dict:
80
+ info_dict["sample_interval"] = acronym_to_seconds(info_dict["accumulation_acronym"])
81
+
57
82
  # Return info dictionary
58
83
  return info_dict
59
84
 
@@ -91,6 +116,7 @@ def _get_version_from_filepath(filepath):
91
116
 
92
117
 
93
118
  def get_version_from_filepaths(filepaths):
119
+ """Return the DISDROB product version of the specified files."""
94
120
  if isinstance(filepaths, str):
95
121
  filepaths = [filepaths]
96
122
  list_version = [_get_version_from_filepath(filepath) for filepath in filepaths]
@@ -98,34 +124,47 @@ def get_version_from_filepaths(filepaths):
98
124
 
99
125
 
100
126
  def get_campaign_name_from_filepaths(filepaths):
127
+ """Return the DISDROB campaign name of the specified files."""
101
128
  list_id = get_key_from_filepaths(filepaths, key="campaign_name")
102
129
  return list_id
103
130
 
104
131
 
105
132
  def get_station_name_from_filepaths(filepaths):
133
+ """Return the DISDROB station name of the specified files."""
106
134
  list_id = get_key_from_filepaths(filepaths, key="station_name")
107
135
  return list_id
108
136
 
109
137
 
110
138
  def get_product_from_filepaths(filepaths):
139
+ """Return the DISDROB product name of the specified files."""
111
140
  list_id = get_key_from_filepaths(filepaths, key="product")
112
141
  return list_id
113
142
 
114
143
 
115
144
  def get_start_time_from_filepaths(filepaths):
145
+ """Return the start time of the specified files."""
116
146
  list_start_time = get_key_from_filepaths(filepaths, key="start_time")
117
147
  return list_start_time
118
148
 
119
149
 
120
150
  def get_end_time_from_filepaths(filepaths):
151
+ """Return the end time of the specified files."""
121
152
  list_end_time = get_key_from_filepaths(filepaths, key="end_time")
122
153
  return list_end_time
123
154
 
124
155
 
125
156
  def get_start_end_time_from_filepaths(filepaths):
157
+ """Return the start and end time of the specified files."""
126
158
  list_start_time = get_key_from_filepaths(filepaths, key="start_time")
127
159
  list_end_time = get_key_from_filepaths(filepaths, key="end_time")
128
- return np.array(list_start_time), np.array(list_end_time)
160
+ return np.array(list_start_time).astype("M8[s]"), np.array(list_end_time).astype("M8[s]")
161
+
162
+
163
+ def get_sample_interval_from_filepaths(filepaths):
164
+ """Return the sample interval of the specified files."""
165
+ list_accumulation_acronym = get_key_from_filepaths(filepaths, key="accumulation_acronym")
166
+ list_sample_interval = [acronym_to_seconds(s) for s in list_accumulation_acronym]
167
+ return list_sample_interval
129
168
 
130
169
 
131
170
  ####--------------------------------------------------------------------------.
@@ -135,18 +174,18 @@ def get_start_end_time_from_filepaths(filepaths):
135
174
 
136
175
 
137
176
  def infer_disdrodb_tree_path_components(path: str) -> list:
138
- """Return a list with the component of the disdrodb_path.
177
+ """Return a list with the component of a DISDRODB path ``disdrodb_path``.
139
178
 
140
179
  Parameters
141
180
  ----------
142
181
  path : str
143
- `path` can be a campaign_dir ('raw_dir' or 'processed_dir'), or a DISDRODB file path.
182
+ Directory or file path within the DISDRODB archive.
144
183
 
145
184
  Returns
146
185
  -------
147
186
  list
148
187
  Path element of the DISDRODB archive.
149
- Format: [<base_dir>, <Raw or Processed>, <DATA_SOURCE>, <CAMPAIGN_NAME>, ...]
188
+ Format: [``data_archive_dir``, ``product_version``, ``data_source`, ``campaign_name``, ...]
150
189
  """
151
190
  # Retrieve path elements (os-specific)
152
191
  p = Path(path)
@@ -158,60 +197,79 @@ def infer_disdrodb_tree_path_components(path: str) -> list:
158
197
  raise ValueError(f"The DISDRODB directory is not present in the path '{path}'")
159
198
  # Find the rightermost occurrence
160
199
  right_most_occurrence = max(idx_occurrence)
161
- # Define base_dir and tree components
162
- base_dir = os.path.join(*list_path_elements[: right_most_occurrence + 1])
200
+ # Define archive_dir and tree components
201
+ archive_dir = os.path.join(*list_path_elements[: right_most_occurrence + 1])
163
202
  tree_components = list_path_elements[right_most_occurrence + 1 :]
164
203
  # Return components
165
- components = [base_dir] + tree_components
204
+ components = [archive_dir, *tree_components]
166
205
  return components
167
206
 
168
207
 
169
208
  def infer_path_info_dict(path: str) -> dict:
170
- """Return a dictionary with the base_dir, data_source and campaign_name of the disdrodb_path.
209
+ """Return a dictionary with the ``data_archive_dir``, ``data_source`` and ``campaign_name`` of the disdrodb_path.
171
210
 
172
211
  Parameters
173
212
  ----------
174
213
  path : str
175
- `path` can be a campaign_dir ('raw_dir' or 'processed_dir'), or a DISDRODB file path.
214
+ Directory or file path within the DISDRODB archive.
176
215
 
177
216
  Returns
178
217
  -------
179
- list
218
+ dict
180
219
  Dictionary with the path element of the DISDRODB archive.
181
- Valid keys: "base_dir", "data_source", "campaign_name"
220
+ Valid keys: ``"data_archive_dir"``, ``"data_source"``, ``"campaign_name"``
182
221
  """
183
222
  components = infer_disdrodb_tree_path_components(path=path)
184
223
  if len(components) <= 3:
185
224
  raise ValueError(f"Impossible to determine data_source and campaign_name from {path}")
186
225
  path_dict = {}
187
- path_dict["base_dir"] = components[0]
226
+ path_dict["data_archive_dir"] = components[0]
188
227
  path_dict["data_source"] = components[2]
189
228
  path_dict["campaign_name"] = components[3]
190
229
  return path_dict
191
230
 
192
231
 
232
+ def infer_path_info_tuple(path: str) -> tuple:
233
+ """Return a tuple with the ``data_archive_dir``, ``data_source`` and ``campaign_name`` of the disdrodb_path.
234
+
235
+ Parameters
236
+ ----------
237
+ path : str
238
+ Directory or file path within the DISDRODB archive.
239
+
240
+ Returns
241
+ -------
242
+ tuple
243
+ Dictionary with the path element of the DISDRODB archive.
244
+ Valid keys: ``"data_archive_dir"``, ``"data_source"``, ``"campaign_name"``
245
+ """
246
+ path_dict = infer_path_info_dict(path)
247
+ return path_dict["data_archive_dir"], path_dict["data_source"], path_dict["campaign_name"]
248
+
249
+
193
250
  def infer_disdrodb_tree_path(path: str) -> str:
194
- """Return the directory tree path from the base_dir directory.
251
+ """Return the directory tree path from the archive directory.
195
252
 
196
- Current assumption: no data_source, campaign_name, station_name or file contain the word DISDRODB!
253
+ Current assumption: no ``data_source``, ``campaign_name``, ``station_name`` or file contain the word DISDRODB!
197
254
 
198
255
  Parameters
199
256
  ----------
200
257
  path : str
201
- `path` can be a campaign_dir ('raw_dir' or 'processed_dir'), or a DISDRODB file path.
258
+ Directory or file path within the DISDRODB archive.
202
259
 
203
260
  Returns
204
261
  -------
205
262
  str
206
263
  Path inside the DISDRODB archive.
207
- Format: DISDRODB/<Raw or Processed>/<DATA_SOURCE>/...
264
+ Format: ``DISDRODB/RAW/<DATA_SOURCE>/<CAMPAIGN_NAME>/...``
265
+ Format: ``DISDRODB/<ARCHIVE_VERSION>/<DATA_SOURCE>/<CAMPAIGN_NAME>/...``
208
266
  """
209
267
  components = infer_disdrodb_tree_path_components(path=path)
210
268
  tree_filepath = os.path.join("DISDRODB", *components[1:])
211
269
  return tree_filepath
212
270
 
213
271
 
214
- def infer_base_dir_from_path(path: str) -> str:
272
+ def infer_archive_dir_from_path(path: str) -> str:
215
273
  """Return the disdrodb base directory from a file or directory path.
216
274
 
217
275
  Assumption: no data_source, campaign_name, station_name or file contain the word DISDRODB!
@@ -219,7 +277,7 @@ def infer_base_dir_from_path(path: str) -> str:
219
277
  Parameters
220
278
  ----------
221
279
  path : str
222
- `path` can be a campaign_dir ('raw_dir' or 'processed_dir'), or a DISDRODB file path.
280
+ Directory or file path within the DISDRODB archive.
223
281
 
224
282
  Returns
225
283
  -------
@@ -232,12 +290,12 @@ def infer_base_dir_from_path(path: str) -> str:
232
290
  def infer_campaign_name_from_path(path: str) -> str:
233
291
  """Return the campaign name from a file or directory path.
234
292
 
235
- Assumption: no data_source, campaign_name, station_name or file contain the word DISDRODB!
293
+ Assumption: no ``data_source``, ``campaign_name``, ``station_name`` or file contain the word DISDRODB!
236
294
 
237
295
  Parameters
238
296
  ----------
239
297
  path : str
240
- `path` can be a campaign_dir ('raw_dir' or 'processed_dir'), or a DISDRODB file path.
298
+ Directory or file path within the DISDRODB archive.
241
299
 
242
300
  Returns
243
301
  -------
@@ -254,12 +312,12 @@ def infer_campaign_name_from_path(path: str) -> str:
254
312
  def infer_data_source_from_path(path: str) -> str:
255
313
  """Return the data_source from a file or directory path.
256
314
 
257
- Assumption: no data_source, campaign_name, station_name or file contain the word DISDRODB!
315
+ Assumption: no ``data_source``, ``campaign_name``, ``station_name`` or file contain the word DISDRODB!
258
316
 
259
317
  Parameters
260
318
  ----------
261
319
  path : str
262
- `path` can be a campaign_dir ('raw_dir' or 'processed_dir'), or a DISDRODB file path.
320
+ Directory or file path within the DISDRODB archive.
263
321
 
264
322
  Returns
265
323
  -------
@@ -274,3 +332,136 @@ def infer_data_source_from_path(path: str) -> str:
274
332
 
275
333
 
276
334
  ####--------------------------------------------------------------------------.
335
+ #######################
336
+ #### Group utility ####
337
+ #######################
338
+
339
+
340
+ FILE_KEYS = [
341
+ "product",
342
+ "subproduct",
343
+ "campaign_name",
344
+ "station_name",
345
+ "start_time",
346
+ "end_time",
347
+ "data_format",
348
+ "accumulation_acronym",
349
+ "sample_interval",
350
+ ]
351
+
352
+
353
+ TIME_KEYS = [
354
+ "year",
355
+ "month",
356
+ "month_name",
357
+ "quarter",
358
+ "season",
359
+ "day",
360
+ "doy",
361
+ "dow",
362
+ "hour",
363
+ "minute",
364
+ "second",
365
+ ]
366
+
367
+
368
+ def check_groups(groups):
369
+ """Check groups validity."""
370
+ if not isinstance(groups, (str, list)):
371
+ raise TypeError("'groups' must be a list (or a string if a single group is specified.")
372
+ if isinstance(groups, str):
373
+ groups = [groups]
374
+ groups = np.array(groups)
375
+ valid_keys = FILE_KEYS + TIME_KEYS
376
+ invalid_keys = groups[np.isin(groups, valid_keys, invert=True)]
377
+ if len(invalid_keys) > 0:
378
+ raise ValueError(f"The following group keys are invalid: {invalid_keys}. Valid values are {valid_keys}.")
379
+ return groups.tolist()
380
+
381
+
382
+ def get_season(time):
383
+ """Get season from `datetime.datetime` or `datetime.date` object."""
384
+ month = time.month
385
+ if month in [12, 1, 2]:
386
+ return "DJF" # Winter (December, January, February)
387
+ if month in [3, 4, 5]:
388
+ return "MAM" # Spring (March, April, May)
389
+ if month in [6, 7, 8]:
390
+ return "JJA" # Summer (June, July, August)
391
+ return "SON" # Autumn (September, October, November)
392
+
393
+
394
+ def get_time_component(time, component):
395
+ """Get time component from `datetime.datetime` object."""
396
+ func_dict = {
397
+ "year": lambda time: time.year,
398
+ "month": lambda time: time.month,
399
+ "day": lambda time: time.day,
400
+ "doy": lambda time: time.timetuple().tm_yday, # Day of year
401
+ "dow": lambda time: time.weekday(), # Day of week (0=Monday, 6=Sunday)
402
+ "hour": lambda time: time.hour,
403
+ "minute": lambda time: time.minute,
404
+ "second": lambda time: time.second,
405
+ # Additional
406
+ "month_name": lambda time: time.strftime("%B"), # Full month name
407
+ "quarter": lambda time: (time.month - 1) // 3 + 1, # Quarter (1-4)
408
+ "season": lambda time: get_season(time), # Season (DJF, MAM, JJA, SON)
409
+ }
410
+ return str(func_dict[component](time))
411
+
412
+
413
+ def _get_groups_value(groups, filepath):
414
+ """Return the value associated to the groups keys.
415
+
416
+ If multiple keys are specified, the value returned is a string of format: ``<group_value_1>/<group_value_2>/...``
417
+
418
+ If a single key is specified and is ``start_time`` or ``end_time``, the function
419
+ returns a :py:class:`datetime.datetime` object.
420
+ """
421
+ single_key = len(groups) == 1
422
+ info_dict = get_info_from_filepath(filepath)
423
+ start_time = info_dict["start_time"]
424
+ list_key_values = []
425
+ for key in groups:
426
+ if key in TIME_KEYS:
427
+ list_key_values.append(get_time_component(start_time, component=key))
428
+ else:
429
+ value = info_dict.get(key, f"{key}=None")
430
+ list_key_values.append(value if single_key else str(value))
431
+ if single_key:
432
+ return list_key_values[0]
433
+ return "/".join(list_key_values)
434
+
435
+
436
+ def group_filepaths(filepaths, groups=None):
437
+ """
438
+ Group filepaths in a dictionary if groups are specified.
439
+
440
+ Parameters
441
+ ----------
442
+ filepaths : list
443
+ List of filepaths.
444
+ groups: list or str
445
+ The group keys by which to group the filepaths.
446
+ Valid group keys are ``product``, ``subproduct``, ``campaign_name``, ``station_name``,
447
+ ``start_time``, ``end_time``,``accumulation_acronym``,``sample_interval``,
448
+ ``data_format``,
449
+ ``year``, ``month``, ``day``, ``doy``, ``dow``, ``hour``, ``minute``, ``second``,
450
+ ``month_name``, ``quarter``, ``season``.
451
+ The time components are extracted from ``start_time`` !
452
+ If groups is ``None`` returns the input filepaths list.
453
+ The default value is ``None``.
454
+
455
+ Returns
456
+ -------
457
+ dict or list
458
+ Either a dictionary of format ``{<group_value>: <list_filepaths>}``.
459
+ or the original input filepaths (if ``groups=None``)
460
+
461
+ """
462
+ if groups is None:
463
+ return filepaths
464
+ groups = check_groups(groups)
465
+ filepaths_dict = defaultdict(list)
466
+ _ = [filepaths_dict[_get_groups_value(groups, filepath)].append(filepath) for filepath in filepaths]
467
+ return dict(filepaths_dict)