ooi-data-explorations 0.2.3__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.
- __init__.py +0 -0
- examples/__init__.py +0 -0
- examples/camds/uncabled.ipynb +412 -0
- examples/ctdbp/__init__.py +0 -0
- examples/ctdbp/combine_mooring_ctdbp.py +212 -0
- examples/ctdbp/recovered_host_ctdbp.py +52 -0
- examples/ctdbp/recovered_inst_ctdbp.py +51 -0
- examples/ctdbp/telemetered_ctdbp.py +52 -0
- examples/ctdpf/__init__.py +0 -0
- examples/ctdpf/cspp_ctdpf.py +47 -0
- examples/dosta/__init__.py +0 -0
- examples/dosta/combine_mooring_dosta.py +210 -0
- examples/dosta/recovered_host_ctdbp_dosta.py +52 -0
- examples/dosta/recovered_host_solo_dosta.py +52 -0
- examples/dosta/recovered_inst_ctdbp_dosta.py +52 -0
- examples/dosta/telemetered_ctdbp_dosta.py +52 -0
- examples/dosta/telemetered_solo_dosta.py +52 -0
- examples/flort/__init__.py +0 -0
- examples/flort/cabled_profiler_flort.py +39 -0
- examples/flort/cspp_flort.py +50 -0
- examples/flort/recovered_host_flort.py +51 -0
- examples/flort/recovered_inst_flort.py +51 -0
- examples/flort/telemetered_flort.py +51 -0
- examples/flort/wfp_flort.py +50 -0
- examples/metbk/__init__.py +0 -0
- examples/metbk/recovered_host_metbk.py +48 -0
- examples/metbk/telemetered_bulk_flux.py +50 -0
- examples/metbk/telemetered_metbk.py +50 -0
- examples/notebooks/dosta/ooiea_cabled_dosta_processing.ipynb +1784 -0
- examples/notebooks/dosta/ooiea_dosta_annotations_hitl_qc.ipynb +220 -0
- examples/notebooks/dosta/ooiea_dosta_quality_assessments.ipynb +786 -0
- examples/notebooks/dosta/ooiea_uncabled_dosta_data.ipynb +368 -0
- examples/notebooks/fdchp/.gitignore +1 -0
- examples/notebooks/fdchp/FdchpExample.ipynb +552 -0
- examples/notebooks/fdchp/ProcessFdchpRaw.ipynb +6235 -0
- examples/notebooks/ifcb/combining_ifcb_and_environmental.ipynb +1619 -0
- examples/notebooks/m2m_explorations.ipynb +2313 -0
- examples/notebooks/optaa/acs137_20210302_pre_deployment_adat.csv +84 -0
- examples/notebooks/optaa/acs137_20210302_pre_deployment_cdat.csv +84 -0
- examples/notebooks/optaa/process_kdata_cabled_profiler_ce04osps.ipynb +3863 -0
- examples/notebooks/optaa/process_kdata_cabled_profiler_rs01sbps.ipynb +3902 -0
- examples/notebooks/optaa/process_kdata_optaa.ipynb +2283 -0
- examples/notebooks/optaa/process_thredds_optaa.ipynb +2292 -0
- examples/notebooks/optaa/process_thredds_optaa_cspp.ipynb +1078 -0
- examples/notebooks/optaa/processing_workflow_using_the_cspp_optaa.ipynb +5364 -0
- examples/notebooks/optaa/when_things_go_bad.ipynb +2087 -0
- examples/notebooks/pco2a/carbonate_climatologies.ipynb +686 -0
- examples/notebooks/pco2a/regional_pco2_comparisons.png +0 -0
- examples/notebooks/pco2a/regional_ph_comparisons.png +0 -0
- examples/notebooks/pco2w/PCO2_Calculations.ipynb +2880 -0
- examples/notebooks/pco2w/ce01issm_pco2w_C0119_converted_2019-10-31.csv +2357 -0
- examples/notebooks/phsen/creating_annotations.ipynb +1983 -0
- examples/nutnr/__init__.py +0 -0
- examples/nutnr/cspp_nutnr.py +47 -0
- examples/nutnr/recovered_host_nutnr.py +52 -0
- examples/nutnr/recovered_inst_nutnr.py +52 -0
- examples/nutnr/telemetered_nutnr.py +52 -0
- examples/optaa/__init__.py +0 -0
- examples/optaa/cabled_benthic_optaa.py +48 -0
- examples/optaa/cabled_profiler_optaa.py +48 -0
- examples/optaa/cspp_optaa.py +49 -0
- examples/optaa/recovered_host_optaa.py +49 -0
- examples/optaa/telemetered_optaa.py +54 -0
- examples/pco2a/__init__.py +0 -0
- examples/pco2a/recovered_host_pco2a.py +59 -0
- examples/pco2a/telemetered_pco2a.py +57 -0
- examples/pco2w/__init__.py +0 -0
- examples/pco2w/combine_mooring_pco2w.py +195 -0
- examples/pco2w/recovered_host_pco2w.py +52 -0
- examples/pco2w/recovered_inst_pco2w.py +51 -0
- examples/pco2w/streamed_pco2w.py +52 -0
- examples/pco2w/telemetered_pco2w.py +52 -0
- examples/phsen/__init__.py +0 -0
- examples/phsen/combine_mooring_phsen.py +180 -0
- examples/phsen/imodem_phsen.py +51 -0
- examples/phsen/recovered_host_phsen.py +52 -0
- examples/phsen/recovered_inst_phsen.py +51 -0
- examples/phsen/streamed_phsen.py +52 -0
- examples/phsen/telemetered_phsen.py +52 -0
- examples/spkir/__init__.py +0 -0
- examples/spkir/cspp_spkir.py +47 -0
- examples/spkir/recovered_host_spkir.py +47 -0
- examples/spkir/telemetered_spkir.py +45 -0
- examples/tensorflow/sealion_detection_example.ipynb +708 -0
- examples/tensorflow/sealion_training_example.ipynb +371 -0
- examples/vel3d/__init__.py +0 -0
- examples/vel3d/recovered_host_vel3d.py +48 -0
- examples/vel3d/recovered_wfp_vel3d.py +42 -0
- examples/vel3d/telemetered_vel3d.py +47 -0
- examples/vel3d/telemetered_wfp_vel3d.py +42 -0
- examples/velpt/__init__.py +0 -0
- examples/velpt/cspp_velpt.py +47 -0
- examples/velpt/recovered_host_velpt.py +45 -0
- examples/velpt/recovered_inst_velpt.py +39 -0
- examples/velpt/telemetered_velpt.py +45 -0
- examples/water_quality/__init__.py +0 -0
- examples/water_quality/wqx_ctdbp.py +147 -0
- examples/water_quality/wqx_dosta.py +145 -0
- examples/water_quality/wqx_flort.py +158 -0
- examples/water_quality/wqx_nutnr.py +146 -0
- examples/water_quality/wqx_phsen.py +144 -0
- examples/wavss/__init__.py +0 -0
- examples/wavss/recovered_host_wavss_directional.py +38 -0
- examples/wavss/recovered_host_wavss_summary.py +37 -0
- examples/wavss/telemetered_wavss_directional.py +42 -0
- examples/wavss/telemetered_wavss_summary.py +42 -0
- ooi_data_explorations/__init__.py +7 -0
- ooi_data_explorations/bottles.py +264 -0
- ooi_data_explorations/cabled/__init__.py +0 -0
- ooi_data_explorations/cabled/process_flort.py +157 -0
- ooi_data_explorations/cabled/process_optaa.py +531 -0
- ooi_data_explorations/cabled/process_phsen.py +176 -0
- ooi_data_explorations/calibrations.py +64 -0
- ooi_data_explorations/combine_data.py +238 -0
- ooi_data_explorations/common.py +1381 -0
- ooi_data_explorations/construct_urls.py +210 -0
- ooi_data_explorations/data_request.py +272 -0
- ooi_data_explorations/m2m_urls.yml +12186 -0
- ooi_data_explorations/profilers.py +296 -0
- ooi_data_explorations/qartod/__init__.py +0 -0
- ooi_data_explorations/qartod/cgsn/scripts/CTDBP.ipynb +2878 -0
- ooi_data_explorations/qartod/cgsn/scripts/CTDMO.ipynb +2894 -0
- ooi_data_explorations/qartod/cgsn/scripts/CTDPF.ipynb +1129 -0
- ooi_data_explorations/qartod/cgsn/scripts/DOSTA.ipynb +1156 -0
- ooi_data_explorations/qartod/cgsn/scripts/FLORD.ipynb +1267 -0
- ooi_data_explorations/qartod/cgsn/scripts/FLORT.ipynb +1320 -0
- ooi_data_explorations/qartod/cgsn/scripts/METBK.ipynb +1299 -0
- ooi_data_explorations/qartod/cgsn/scripts/NUTNR.ipynb +1550 -0
- ooi_data_explorations/qartod/cgsn/scripts/PAR.ipynb +1101 -0
- ooi_data_explorations/qartod/cgsn/scripts/PCO2A.ipynb +1069 -0
- ooi_data_explorations/qartod/cgsn/scripts/PCO2W.ipynb +1090 -0
- ooi_data_explorations/qartod/cgsn/scripts/PHSEN.ipynb +1099 -0
- ooi_data_explorations/qartod/cgsn/scripts/WAVSS.ipynb +1301 -0
- ooi_data_explorations/qartod/cgsn/scripts/wavss_sensor_ranges.yaml +41 -0
- ooi_data_explorations/qartod/climatology.py +211 -0
- ooi_data_explorations/qartod/discrete_samples.py +196 -0
- ooi_data_explorations/qartod/endurance/__init__.py +0 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_ctdbp.py +205 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_ctdpf.py +195 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_dofst.py +199 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_dosta.py +271 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_flort.py +324 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_gliders.py +135 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_metbk.py +258 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_nutnr.py +287 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_parad.py +209 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_pco2a.py +218 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_pco2w.py +166 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_phsen.py +194 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_presf.py +193 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_spkir.py +231 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_vel3d.py +298 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_velpt.py +239 -0
- ooi_data_explorations/qartod/endurance/qartod_ce_wavss.py +187 -0
- ooi_data_explorations/qartod/endurance/reports/__init__.py +0 -0
- ooi_data_explorations/qartod/endurance/reports/reports_ce_ctdbp.py +397 -0
- ooi_data_explorations/qartod/endurance/reports/reports_ce_nutnr.py +395 -0
- ooi_data_explorations/qartod/endurance/reports/reports_ce_phsen.py +406 -0
- ooi_data_explorations/qartod/gross_range.py +281 -0
- ooi_data_explorations/qartod/plotting.py +145 -0
- ooi_data_explorations/qartod/qc_processing.py +668 -0
- ooi_data_explorations/qartod/rca/decimate.py +211 -0
- ooi_data_explorations/qartod/rca/parameterMap.csv +13 -0
- ooi_data_explorations/qartod/rca/qartodTests.csv +3 -0
- ooi_data_explorations/qartod/rca/qartod_rca.py +344 -0
- ooi_data_explorations/qartod/rca/siteParameters.csv +41 -0
- ooi_data_explorations/qartod/reporting.py +92 -0
- ooi_data_explorations/qartod/table_checker.py +270 -0
- ooi_data_explorations/uncabled/__init__.py +0 -0
- ooi_data_explorations/uncabled/ooim2m_cspp.py +280 -0
- ooi_data_explorations/uncabled/process_adcp.py +452 -0
- ooi_data_explorations/uncabled/process_ctdbp.py +216 -0
- ooi_data_explorations/uncabled/process_ctdpf.py +144 -0
- ooi_data_explorations/uncabled/process_dofst.py +215 -0
- ooi_data_explorations/uncabled/process_dosta.py +547 -0
- ooi_data_explorations/uncabled/process_fdchp.py +1141 -0
- ooi_data_explorations/uncabled/process_flord.py +306 -0
- ooi_data_explorations/uncabled/process_flort.py +607 -0
- ooi_data_explorations/uncabled/process_metbk.py +384 -0
- ooi_data_explorations/uncabled/process_mopak.py +1621 -0
- ooi_data_explorations/uncabled/process_nutnr.py +835 -0
- ooi_data_explorations/uncabled/process_optaa.py +843 -0
- ooi_data_explorations/uncabled/process_parad.py +259 -0
- ooi_data_explorations/uncabled/process_pco2a.py +289 -0
- ooi_data_explorations/uncabled/process_pco2w.py +491 -0
- ooi_data_explorations/uncabled/process_phsen.py +595 -0
- ooi_data_explorations/uncabled/process_spkir.py +394 -0
- ooi_data_explorations/uncabled/process_turbd.py +99 -0
- ooi_data_explorations/uncabled/process_vel3d.py +1347 -0
- ooi_data_explorations/uncabled/process_velpt.py +521 -0
- ooi_data_explorations/uncabled/process_wavss.py +540 -0
- ooi_data_explorations-0.2.3.dist-info/METADATA +457 -0
- ooi_data_explorations-0.2.3.dist-info/RECORD +197 -0
- ooi_data_explorations-0.2.3.dist-info/WHEEL +4 -0
- ooi_data_explorations-0.2.3.dist-info/licenses/LICENSE.txt +21 -0
- setup.py +59 -0
- version.py +1 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
@author Christopher Wingard
|
|
5
|
+
@brief Creates the YAML file with the information needed to construct URLs for M2M requests
|
|
6
|
+
"""
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from ooi_data_explorations.common import AUTH, BASE_URL
|
|
10
|
+
from ooi_data_explorations.common import list_nodes, list_sensors, list_methods, list_streams
|
|
11
|
+
|
|
12
|
+
# Organize the nodes into assemblies common across all the arrays. Helps to better organize the data, taking all the
|
|
13
|
+
# different names used in OOI and collapsing them down to a more coherent, and logical grouping. This list is
|
|
14
|
+
# deliberately non-inclusive of all possible nodes, as some are either specific to control systems or have data not
|
|
15
|
+
# readily accessed through the M2M system.
|
|
16
|
+
ASSEMBLY = {
|
|
17
|
+
'buoy': ['SBD11', 'SBD12', 'SBD17'],
|
|
18
|
+
'midwater': ['PC01A', 'PC01B', 'PC03A', 'RID16', 'RID26', 'RID27', 'RII01', 'RII11', 'RIM01', 'RIS01'],
|
|
19
|
+
'seafloor': [
|
|
20
|
+
'LJ01A', 'LJ01B', 'LJ01C', 'LJ01D', 'LJ03A', 'MFD35', 'MFD37', 'MJ01A', 'MJ01B', 'MJ03A', 'MJ03B',
|
|
21
|
+
'MJ03C', 'MJ03D', 'MJ03E', 'MJ03F', 'PN03B'],
|
|
22
|
+
'glider': [
|
|
23
|
+
'GL247', 'GL276', 'GL311', 'GL312', 'GL319', 'GL320', 'GL326', 'GL327', 'GL335', 'GL336', 'GL339',
|
|
24
|
+
'GL340', 'GL361', 'GL362', 'GL363', 'GL364', 'GL365', 'GL374', 'GL375', 'GL376', 'GL379', 'GL380',
|
|
25
|
+
'GL381', 'GL382', 'GL383', 'GL384', 'GL386', 'GL387', 'GL388', 'GL389', 'GL453', 'GL469', 'GL470',
|
|
26
|
+
'GL477', 'GL478', 'GL484', 'GL485', 'GL486', 'GL493', 'GL494', 'GL495', 'GL496', 'GL514', 'GL523',
|
|
27
|
+
'GL524', 'GL525', 'GL537', 'GL538', 'GL559', 'GL560', 'GL561', 'PG514', 'PG515', 'PG528', 'PG562',
|
|
28
|
+
'PG563', 'PG564', 'PG565', 'PG566', 'PG575', 'PG578', 'PG580', 'PG583'
|
|
29
|
+
],
|
|
30
|
+
'auv': ['A6263', 'A6264'],
|
|
31
|
+
'profiler': ['WFP01', 'WFP02', 'WFP03', 'SP001', 'SF01A', 'SF01B', 'SF03A', 'DP01A', 'DP01B', 'DP03A']
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Assemblies can be further categorized by a sub-assembly type if needed. Otherwise, the code and assembly type are
|
|
35
|
+
# the same. This helps to differentiate times where more than one assembly type is present at a site and we need to
|
|
36
|
+
# further distinguish between them.
|
|
37
|
+
SUBASSEMBLY = {
|
|
38
|
+
'nsif': ['RID16', 'RID26', 'RID27'],
|
|
39
|
+
'riser': ['RII01', 'RII11', 'RIM01'],
|
|
40
|
+
'sphere': ['RIS01'],
|
|
41
|
+
'200m': ['PC01A', 'PC01B', 'PC03A'],
|
|
42
|
+
'mfn': ['MFD35', 'MFD37'],
|
|
43
|
+
'bep': ['LJ01C', 'LJ01D'],
|
|
44
|
+
'low-power': ['LJ01A', 'LJ01B', 'LJ03A'],
|
|
45
|
+
'medium-power': ['MJ01A', 'MJ01B', 'MJ03A', 'MJ03B', 'MJ03C', 'MJ03D', 'MJ03E', 'MJ03F'],
|
|
46
|
+
'coastal-glider': [
|
|
47
|
+
'GL247', 'GL276', 'GL311', 'GL312', 'GL319', 'GL320', 'GL326', 'GL327', 'GL335', 'GL336', 'GL339', 'GL340',
|
|
48
|
+
'GL361', 'GL362', 'GL363', 'GL364', 'GL365', 'GL374', 'GL375', 'GL376', 'GL379', 'GL380', 'GL381', 'GL382',
|
|
49
|
+
'GL383', 'GL384', 'GL386', 'GL387', 'GL388', 'GL389', 'GL514',
|
|
50
|
+
],
|
|
51
|
+
'global-glider': [
|
|
52
|
+
'GL453', 'GL469', 'GL470', 'GL477', 'GL478', 'GL484', 'GL485', 'GL486', 'GL493', 'GL494', 'GL495', 'GL496',
|
|
53
|
+
'GL523', 'GL524', 'GL525', 'GL537', 'GL538', 'GL559', 'GL560', 'GL561'
|
|
54
|
+
],
|
|
55
|
+
'profiling-glider': [
|
|
56
|
+
'PG514', 'PG515', 'PG528', 'PG562', 'PG563', 'PG564', 'PG565', 'PG566', 'PG575', 'PG578', 'PG580', 'PG583'
|
|
57
|
+
],
|
|
58
|
+
'cspp': ['SP001'],
|
|
59
|
+
'coastal-wfp': ['WFP01'],
|
|
60
|
+
'global-wfp': ['WFP02', 'WFP03'],
|
|
61
|
+
'cabled-wfp': ['DP01A', 'DP01B', 'DP03A'],
|
|
62
|
+
'shallow-profiler': ['SF01A', 'SF01B', 'SF03A']
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# List of allowed methods (there are some called bad_*, ignoring those)
|
|
66
|
+
METHODS = ['streamed', 'telemetered', 'recovered_host', 'recovered_inst', 'recovered_cspp', 'recovered_wfp']
|
|
67
|
+
|
|
68
|
+
# Sensor codes to exclude. These are either not science sensors (e.g. 00-DCLENG000), or not really accessible from the
|
|
69
|
+
# M2M system (e.g. cameras, hydrophones or seismometers).
|
|
70
|
+
SENSOR_EXCLUDES = [
|
|
71
|
+
'AOABP', 'CAM', 'COVIS', 'ENG', 'FLOBN', 'HPIES', 'HYD', 'MASSP', 'OBS', 'OSMOI', 'OVRSR', 'PPSDNA', 'QNTSR',
|
|
72
|
+
'RASFL', 'SCPRA', 'SCTAA', 'ZPLSCB',
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
# Several streams were defined within OOI that are beyond core science needs. The terms below filter out those streams.
|
|
76
|
+
STREAM_EXCLUDES = [
|
|
77
|
+
'metadata', 'diagnostics', 'abc_power', 'dcl_power', 'config', 'wave_burst', 'imodem_status',
|
|
78
|
+
'imodem_start_time', 'cspp_dark_instrument', 'instrument_blank', 'adcp_engineering', 'adcp_ancillary',
|
|
79
|
+
'adcp_transmit', 'dcl_fourier', 'dcl_mean_directional', 'dcl_motion', 'dcl_non_directional', 'echogram_data',
|
|
80
|
+
'calibration', 'status', 'settings', 'hardware', 'adcp_pd0_beam_parsed', 'identification_string', 'clock_data',
|
|
81
|
+
'battery_voltage', 'thermistor_voltage', 'dev1_data', 'data_record_cal', 'dark_sample', 'imodem_control',
|
|
82
|
+
'imodem_power', 'offset', 'adcp_velocity_glider', 'nutnr_b_dcl_dark', 'nutnr_b_metadata', 'nutnr_b_dark',
|
|
83
|
+
'prest_event_counter', 'prest_reference_oscillator', 'vel3d_b_engineering', 'tmpsf_engineering',
|
|
84
|
+
'vadcp_5thbeam_pd0_beam_parsed', 'nutnr_a_test'
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# grab the entire OOI vocabulary dictionary
|
|
89
|
+
def get_vocabulary():
|
|
90
|
+
"""
|
|
91
|
+
Uses the an API call to grab the entire vocabulary dictionary for all OOI sites, nodes and sensors
|
|
92
|
+
|
|
93
|
+
:return: json object with the site-node-sensor specific vocabulary
|
|
94
|
+
"""
|
|
95
|
+
r = requests.get(BASE_URL + '12586/vocab', auth=(AUTH[0], AUTH[2]))
|
|
96
|
+
if r.status_code == requests.codes.ok:
|
|
97
|
+
return r.json()
|
|
98
|
+
else:
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
VOCAB = get_vocabulary()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def add_site(site, url_file):
|
|
106
|
+
"""
|
|
107
|
+
For an OOI site, assemble a curated list of all the instruments and data streams that are available for data
|
|
108
|
+
explorations. This file will create a YAML file per site. Additional HITL work is required to further clean-up and
|
|
109
|
+
check the list, and pruning streams and methods down to the core set of science sensors. The results of this work
|
|
110
|
+
is used to create the m2m_urls.yml file used in this python toolbox.
|
|
111
|
+
|
|
112
|
+
:param site: OOI 8 character site designation (e.g. CE01ISSM)
|
|
113
|
+
:param url_file: data file to save the results in.
|
|
114
|
+
:return: None, creates the data file.
|
|
115
|
+
"""
|
|
116
|
+
sp = ' ' # create an explicit two-space indent string for the YAML file
|
|
117
|
+
# open the YAML file to store the results
|
|
118
|
+
with open(url_file, 'w') as f:
|
|
119
|
+
# create the header portion of the YAML, getting the array and site names from the API
|
|
120
|
+
site = site.upper()
|
|
121
|
+
for site_vocab in VOCAB:
|
|
122
|
+
if site_vocab.get('refdes') == site:
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
f.write('---\n{site}:\n'.format(site=site))
|
|
126
|
+
f.write('{sp}array: {array}\n'.format(sp=sp*1, array=site_vocab['tocL1']))
|
|
127
|
+
f.write('{sp}name: {name}\n'.format(sp=sp*1, name=site_vocab['tocL2']))
|
|
128
|
+
f.write('{sp}assembly:\n'.format(sp=sp*1))
|
|
129
|
+
|
|
130
|
+
# create a list of nodes for this site
|
|
131
|
+
nodes = list_nodes(site)
|
|
132
|
+
|
|
133
|
+
# for each node, if it is one of interest as defined above, create an assembly entry
|
|
134
|
+
for k, v in ASSEMBLY.items():
|
|
135
|
+
# find the nodes that correspond to this assembly
|
|
136
|
+
assembly = k
|
|
137
|
+
node = sorted(set(v) & set(nodes))
|
|
138
|
+
|
|
139
|
+
# skip if we don't have any nodes in this assembly
|
|
140
|
+
if not node:
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
# if we have nodes, create the assembly entry
|
|
144
|
+
for node_vocab in VOCAB:
|
|
145
|
+
if node_vocab.get('refdes') == site + '-' + node[0]:
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
# for each node, create the list of sensors
|
|
149
|
+
for n in node:
|
|
150
|
+
f.write('{sp}- type: {assembly}\n'.format(sp=sp * 2, assembly=assembly))
|
|
151
|
+
f.write('{sp}name: {name}\n'.format(sp=sp * 3, name=node_vocab['tocL3']))
|
|
152
|
+
# if we need to further distinguish, add the assembly code name
|
|
153
|
+
for l, w in SUBASSEMBLY.items():
|
|
154
|
+
if n in w:
|
|
155
|
+
f.write('{sp}subassembly: {name}\n'.format(sp=sp * 3, name=l))
|
|
156
|
+
f.write('{sp}instrument:\n'.format(sp=sp * 3))
|
|
157
|
+
sensors = list_sensors(site, n)
|
|
158
|
+
sensors = filter_stream(sensors, SENSOR_EXCLUDES) # remove sensors of no interest
|
|
159
|
+
|
|
160
|
+
if not sensors:
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
for sensor in sensors:
|
|
164
|
+
for sensor_vocab in VOCAB:
|
|
165
|
+
if sensor_vocab.get('refdes') == site + '-' + n + '-' + sensor:
|
|
166
|
+
break
|
|
167
|
+
|
|
168
|
+
instrument = (sensor[3:8]).lower()
|
|
169
|
+
f.write('{sp}- class: {instrument}\n'.format(sp=sp*4, instrument=instrument))
|
|
170
|
+
f.write('{sp}instrument_name: {name}\n'.format(sp=sp*5, name=sensor_vocab['instrument']))
|
|
171
|
+
f.write('{sp}instrument_model: {model}\n'.format(sp=sp*5, model=sensor_vocab['model']))
|
|
172
|
+
f.write('{sp}instrument_manufacturer: {manu}\n'.format(sp=sp*5, manu=sensor_vocab['manufacturer']))
|
|
173
|
+
f.write('{sp}mindepth: {mindepth}\n'.format(sp=sp*5, mindepth=sensor_vocab['mindepth']))
|
|
174
|
+
f.write('{sp}maxdepth: {maxdepth}\n'.format(sp=sp*5, maxdepth=sensor_vocab['maxdepth']))
|
|
175
|
+
f.write('{sp}node: {node}\n'.format(sp=sp*5, node=n))
|
|
176
|
+
f.write('{sp}sensor: {sensor}\n'.format(sp=sp*5, sensor=sensor))
|
|
177
|
+
f.write('{sp}stream:\n'.format(sp=sp*5))
|
|
178
|
+
|
|
179
|
+
methods = list_methods(site, n, sensor)
|
|
180
|
+
if not methods:
|
|
181
|
+
f.write('{sp}unknown: null\n'.format(sp=sp*6,))
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
for method in methods:
|
|
185
|
+
if method in METHODS:
|
|
186
|
+
streams = list_streams(site, n, sensor, method)
|
|
187
|
+
streams = filter_stream(streams, STREAM_EXCLUDES)
|
|
188
|
+
if len(streams) == 1:
|
|
189
|
+
f.write('{sp}{method}: {streams}\n'.format(sp=sp*6, method=method,
|
|
190
|
+
streams=streams[0]))
|
|
191
|
+
else:
|
|
192
|
+
f.write('{sp}{method}:\n'.format(sp=sp*6, method=method))
|
|
193
|
+
for stream in streams:
|
|
194
|
+
f.write('{sp}- {stream}\n'.format(sp=sp*7, stream=stream))
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def filter_stream(streams, excludes):
|
|
198
|
+
"""
|
|
199
|
+
Uses a list of keywords to remove sensors or streams from the list returned by OOI Net.
|
|
200
|
+
|
|
201
|
+
:param streams: list of sensor or streams returned from OOI Net
|
|
202
|
+
:param excludes: list of keywords to use in pruning the list
|
|
203
|
+
:return: a cleaned, pruned list
|
|
204
|
+
"""
|
|
205
|
+
clean = []
|
|
206
|
+
for stream in streams:
|
|
207
|
+
if not any(sub in stream for sub in excludes):
|
|
208
|
+
clean.append(stream)
|
|
209
|
+
|
|
210
|
+
return clean
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
import os
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pytz
|
|
6
|
+
import xarray as xr
|
|
7
|
+
import dateutil.parser as parser
|
|
8
|
+
|
|
9
|
+
from ooi_data_explorations import M2M_URLS
|
|
10
|
+
from ooi_data_explorations.common import dr_inputs, m2m_request, m2m_collect, get_deployment_dates
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def filter_urls(site, assembly, instrument, method):
|
|
14
|
+
"""
|
|
15
|
+
Takes the M2M_URLS dictionary and searches for the instrument of interest
|
|
16
|
+
as defined by the site code, assembly type, instrument class, and data
|
|
17
|
+
delivery method to return the OOI specific site, node and stream names
|
|
18
|
+
needed to request the data.
|
|
19
|
+
|
|
20
|
+
:param site: OOI eight letter site code (e.g. CE04OSPS for the Oregon
|
|
21
|
+
Offshore Shallow Profiler)
|
|
22
|
+
:param assembly: Assembly grouping name (e.g. midwater for the 200 m
|
|
23
|
+
Platform)
|
|
24
|
+
:param instrument: The instrument class name (e.g. phsen for the
|
|
25
|
+
SAMI2-pH sensor)
|
|
26
|
+
:param method: The data delivery method (e.g. streamed for cabled
|
|
27
|
+
streaming data)
|
|
28
|
+
|
|
29
|
+
:return node: The OOI specific node code(s) for the assembly
|
|
30
|
+
:return sensor: The OOI specific sensor code(s) for the instrument class
|
|
31
|
+
:return stream: The OOI specific stream name(s) for the site, node, sensor
|
|
32
|
+
and delivery method combination
|
|
33
|
+
"""
|
|
34
|
+
# Use the site, assembly, instrument, and data delivery method to determine the OOI specific site, node, sensor,
|
|
35
|
+
# method and stream
|
|
36
|
+
node = []
|
|
37
|
+
sensor = []
|
|
38
|
+
stream = []
|
|
39
|
+
|
|
40
|
+
# pare the larger dictionary down to the site of interest and check if a valid site was used
|
|
41
|
+
m2m_urls = M2M_URLS.get(site.upper())
|
|
42
|
+
if not m2m_urls:
|
|
43
|
+
raise SyntaxError('Unknown site code: %s' % site)
|
|
44
|
+
|
|
45
|
+
# make sure the correct data delivery method was specified
|
|
46
|
+
if method not in ['streamed', 'telemetered', 'recovered_host', 'recovered_inst', 'recovered_cspp', 'recovered_wfp']:
|
|
47
|
+
raise SyntaxError('Unknown data delivery method: %s' % method)
|
|
48
|
+
|
|
49
|
+
# find the instrument(s) of interest in the assembly group
|
|
50
|
+
for grouping in m2m_urls.get('assembly'):
|
|
51
|
+
if grouping.get('type') == assembly or grouping.get('subassembly') == assembly:
|
|
52
|
+
for instrmt in grouping['instrument']:
|
|
53
|
+
if instrmt['class'] == instrument:
|
|
54
|
+
node.append(instrmt.node)
|
|
55
|
+
sensor.append(instrmt.sensor)
|
|
56
|
+
stream.append(instrmt.stream.get(method))
|
|
57
|
+
|
|
58
|
+
# check to see if we were able to find the system of interest
|
|
59
|
+
if not stream:
|
|
60
|
+
raise RuntimeWarning('Instrument defined by %s-%s-%s-%s cannot be found.' % (site, assembly, instrument,
|
|
61
|
+
method))
|
|
62
|
+
|
|
63
|
+
# return the OOI specific names for the node(s), sensor(s) and stream(s)
|
|
64
|
+
return node, sensor, stream
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def data_request(site, assembly, instrument, method, **kwargs):
|
|
68
|
+
"""
|
|
69
|
+
Requests data via the OOI M2M API using the site code, assembly type,
|
|
70
|
+
instrument class and data delivery method as defined in the m2m_urls.yml to
|
|
71
|
+
construct the OOI specific data request.
|
|
72
|
+
|
|
73
|
+
:param site: OOI site code as an 8 character string
|
|
74
|
+
:param assembly: The assembly type where the instrument is located
|
|
75
|
+
:param instrument: the OOI instrument class name for the instrument of
|
|
76
|
+
interest
|
|
77
|
+
:param method: The data delivery method for the system of interest
|
|
78
|
+
:param kwargs: Takes the following optional keyword arguments:
|
|
79
|
+
|
|
80
|
+
start: Starting date/time for the data request in a dateutil.parser
|
|
81
|
+
recognizable form. If ``None``, the default, the beginning of the
|
|
82
|
+
data record will be used
|
|
83
|
+
stop: Ending date/time for the data request in a dateutil.parser
|
|
84
|
+
recognizable form If ``None``, the default, the end of the data
|
|
85
|
+
record will be used
|
|
86
|
+
deploy: Use the deployment number, entered as an integer, to set the
|
|
87
|
+
starting and ending dates If ``None``, the default, the starting
|
|
88
|
+
and ending dates are used. If you enter both, the deployment number
|
|
89
|
+
will take priority in setting the start and end dates
|
|
90
|
+
aggregate: In cases where more than one instance of an instrument class
|
|
91
|
+
is part of an assembly, will collect all of the data if the integer
|
|
92
|
+
value entered is ``0``, or the specific instance of the instrument
|
|
93
|
+
is requested if any value greater than ``0`` is used. If ``None``,
|
|
94
|
+
the default, the first instance of an instrument will be used.
|
|
95
|
+
|
|
96
|
+
:return data: Returns the request data as an xarray dataset for further analysis
|
|
97
|
+
"""
|
|
98
|
+
# setup inputs to the function, make sure case is correct
|
|
99
|
+
site = site.upper()
|
|
100
|
+
assembly = assembly.lower()
|
|
101
|
+
instrument = instrument.lower()
|
|
102
|
+
method = method.lower()
|
|
103
|
+
|
|
104
|
+
# parse the keyword arguments
|
|
105
|
+
start = None
|
|
106
|
+
stop = None
|
|
107
|
+
deploy = None
|
|
108
|
+
aggregate = None
|
|
109
|
+
for key, value in kwargs.items():
|
|
110
|
+
if key not in ['start', 'stop', 'deploy', 'aggregate']:
|
|
111
|
+
raise KeyError('Unknown keyword (%s) argument.' % key)
|
|
112
|
+
else:
|
|
113
|
+
if key == 'start':
|
|
114
|
+
start = value
|
|
115
|
+
if key == 'stop':
|
|
116
|
+
stop = value
|
|
117
|
+
if key == 'deploy':
|
|
118
|
+
deploy = value
|
|
119
|
+
if key == 'aggregate':
|
|
120
|
+
aggregate = value
|
|
121
|
+
|
|
122
|
+
# use the assembly, instrument and data delivery methods to find the system of interest
|
|
123
|
+
node, sensor, stream = filter_urls(site, assembly, instrument, method)
|
|
124
|
+
|
|
125
|
+
# check the formatting of the start and end dates. We need to be able to parse and convert to an ISO format.
|
|
126
|
+
if start:
|
|
127
|
+
# check the formatting of the start date string and convert to the ISO format used by the M2M API
|
|
128
|
+
try:
|
|
129
|
+
start = parser.parse(start)
|
|
130
|
+
start = start.astimezone(pytz.utc)
|
|
131
|
+
start = start.strftime('%Y-%m-%dT%H:%M:%S.000Z')
|
|
132
|
+
except parser.ParserError:
|
|
133
|
+
raise SyntaxError('Formatting of the starting date string needs to be in a recognizable format')
|
|
134
|
+
|
|
135
|
+
if stop:
|
|
136
|
+
# check the formatting of the stop date string and convert to the ISO format used by the M2M API
|
|
137
|
+
try:
|
|
138
|
+
stop = parser.parse(stop)
|
|
139
|
+
stop = stop.astimezone(pytz.utc)
|
|
140
|
+
stop = stop.strftime('%Y-%m-%dT%H:%M:%S.000Z')
|
|
141
|
+
except parser.ParserError:
|
|
142
|
+
raise SyntaxError('Formatting of the ending date string needs to be in a recognizable format')
|
|
143
|
+
|
|
144
|
+
if deploy:
|
|
145
|
+
# Determine start and end dates based on the deployment number
|
|
146
|
+
start, stop = get_deployment_dates(site, node[0], sensor[0], deploy)
|
|
147
|
+
if not start or not stop:
|
|
148
|
+
exit_text = ('Deployment dates are unavailable for %s-%s-%s-%s, deployment %02d.' % (site.lower(), assembly,
|
|
149
|
+
instrument, method,
|
|
150
|
+
deploy))
|
|
151
|
+
raise RuntimeWarning(exit_text)
|
|
152
|
+
|
|
153
|
+
# for some cases, there maybe more than 1 stream, but in general, we only want the first one
|
|
154
|
+
if isinstance(stream[0], list):
|
|
155
|
+
stream = stream[0][0]
|
|
156
|
+
else:
|
|
157
|
+
stream = stream[0]
|
|
158
|
+
|
|
159
|
+
tag = ('.*{instrument}.*\\.nc$'.format(instrument=instrument.upper())) # set regex tag to use when downloading
|
|
160
|
+
data = None # setup the default data set
|
|
161
|
+
|
|
162
|
+
# check if there are multiple instances of this instrument class on the assembly
|
|
163
|
+
if len(node) > 1:
|
|
164
|
+
print('There are multiple instances of the instrument %s under %s-%s.' % (instrument, site.lower(), assembly))
|
|
165
|
+
|
|
166
|
+
# check if we are aggregating the multiple instruments into a single data set
|
|
167
|
+
if isinstance(aggregate, int):
|
|
168
|
+
if aggregate == 0:
|
|
169
|
+
# request all of the instruments associated with this site, assembly, instrument and method
|
|
170
|
+
print(('Requesting all %d instances of this instrument. Data sets will be concatenated\n'
|
|
171
|
+
'and a new variable called `sensor_count` will be added to help distinguish the \n'
|
|
172
|
+
'instruments for later processing.') % len(node))
|
|
173
|
+
for i in range(len(node)):
|
|
174
|
+
r = m2m_request(site, node[i], sensor[i], method, stream, start, stop)
|
|
175
|
+
if r:
|
|
176
|
+
temp = m2m_collect(r, tag)
|
|
177
|
+
temp['sensor_count'] = temp['deployment'] * 0 + i + 1
|
|
178
|
+
if not data:
|
|
179
|
+
data = temp
|
|
180
|
+
else:
|
|
181
|
+
data = xr.concat([data, temp], dim='time')
|
|
182
|
+
else:
|
|
183
|
+
# request a specific instrument of the multiple instruments associated with this site, assembly,
|
|
184
|
+
# instrument and method.
|
|
185
|
+
if aggregate > len(node):
|
|
186
|
+
raise SyntaxError('Only %d instruments available, you selected %d' % (len(node), aggregate))
|
|
187
|
+
|
|
188
|
+
print('Requesting instrument %d out of %d.' % (aggregate, len(node)))
|
|
189
|
+
i = aggregate - 1
|
|
190
|
+
r = m2m_request(site, node[i], sensor[i], method, stream, start, stop)
|
|
191
|
+
if r:
|
|
192
|
+
data = m2m_collect(r, tag)
|
|
193
|
+
else:
|
|
194
|
+
r = m2m_request(site, node[0], sensor[0], method, stream, start, stop)
|
|
195
|
+
if r:
|
|
196
|
+
data = m2m_collect(r, tag)
|
|
197
|
+
|
|
198
|
+
if not data:
|
|
199
|
+
raise RuntimeWarning('Data unavailable for %s-%s-%s-%s.' % (site.lower(), assembly, instrument, method))
|
|
200
|
+
|
|
201
|
+
# return the resulting data, which is an xarray.Dataset object
|
|
202
|
+
for v in data.variables:
|
|
203
|
+
# first convert strings with data types set as objects or S64 with binary encoding
|
|
204
|
+
if data[v].dtype == np.dtype('O') or data[v].dtype == np.dtype('S64'):
|
|
205
|
+
data[v] = data[v].astype(np.str)
|
|
206
|
+
|
|
207
|
+
return data
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def main(argv=None):
|
|
211
|
+
"""
|
|
212
|
+
CLI interface to the data_request function. Requests data via the OOI M2M
|
|
213
|
+
API using the site code, assembly type, instrument class and data delivery
|
|
214
|
+
method as defined in the m2m_urls.yml to construct the OOI specific data
|
|
215
|
+
request.
|
|
216
|
+
|
|
217
|
+
:param argv: Command line inputs used by the function are:
|
|
218
|
+
|
|
219
|
+
site (-s, --site): OOI site code as an 8 character string (required)
|
|
220
|
+
assembly (-a, --assembly): Assembly or subassembly type where the
|
|
221
|
+
instrument is located (required)
|
|
222
|
+
instrument (-i, --instrument): OOI instrument class name for the
|
|
223
|
+
instrument of interest (required)
|
|
224
|
+
method (-m, --method): The data delivery method for the system of
|
|
225
|
+
interest (required)
|
|
226
|
+
outfile (-o, --outfile): An absolute or relative path and file name
|
|
227
|
+
for where the resulting data should be saved as a NetCDF file
|
|
228
|
+
(required)
|
|
229
|
+
|
|
230
|
+
start (-bt, --beginDT): Starting or beginning date/time for the data
|
|
231
|
+
request in a dateutil.parser recognizable form. If ``None``, the
|
|
232
|
+
default, the beginning of the data record will be used (optional)
|
|
233
|
+
stop (-et, --endDT): Ending date/time for the data request in a
|
|
234
|
+
dateutil.parser recognizable form If ``None``, the default, the end
|
|
235
|
+
date of the data record will be used (optional)
|
|
236
|
+
deploy (-dp, --deploy): Use the deployment number, entered as an
|
|
237
|
+
integer, to set the beginning and ending dates If ``None``, the
|
|
238
|
+
default, the beginning and ending dates are used. If you enter
|
|
239
|
+
both, the deployment number will take priority in setting the start
|
|
240
|
+
and end dates (optional)
|
|
241
|
+
aggregate (-ag, --aggregate): In cases where more than one instance of
|
|
242
|
+
an instrument class is part of an assembly, collect all of the data
|
|
243
|
+
if the integer value entered is ``0``, or the specific instance of
|
|
244
|
+
the instrument is requested if any value greater than ``0`` is
|
|
245
|
+
used. If ``None``, the default, the first instance of an instrument
|
|
246
|
+
will be requested. (optional)
|
|
247
|
+
|
|
248
|
+
:return: Saves the data to disk as a NetCDF file.
|
|
249
|
+
"""
|
|
250
|
+
args = dr_inputs(argv)
|
|
251
|
+
site = args.site
|
|
252
|
+
assembly = args.assembly
|
|
253
|
+
instrument = args.instrument
|
|
254
|
+
method = args.method
|
|
255
|
+
outfile = os.path.abspath(args.outfile)
|
|
256
|
+
deploy = args.deploy
|
|
257
|
+
start = args.start
|
|
258
|
+
stop = args.stop
|
|
259
|
+
aggregate = args.aggregate
|
|
260
|
+
|
|
261
|
+
# request the data
|
|
262
|
+
data = data_request(site, assembly, instrument, method, start=start, stop=stop, deploy=deploy, aggregate=aggregate)
|
|
263
|
+
|
|
264
|
+
# save the data to disk
|
|
265
|
+
if not os.path.exists(os.path.dirname(outfile)):
|
|
266
|
+
os.makedirs(os.path.dirname(outfile))
|
|
267
|
+
|
|
268
|
+
data.to_netcdf(outfile, mode='w', format='NETCDF4', engine='h5netcdf')
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == '__main__':
|
|
272
|
+
main()
|