mpcaHydro 2.0.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.
- mpcaHydro/WISKI.py +351 -0
- mpcaHydro/__init__.py +0 -0
- mpcaHydro/data_manager.py +321 -0
- mpcaHydro/etlCSG.py +88 -0
- mpcaHydro/etlSWD.py +187 -0
- mpcaHydro/etlWISKI.py +555 -0
- mpcaHydro/etlWPLMN.py +104 -0
- mpcahydro-2.0.0.dist-info/METADATA +15 -0
- mpcahydro-2.0.0.dist-info/RECORD +10 -0
- mpcahydro-2.0.0.dist-info/WHEEL +4 -0
mpcaHydro/WISKI.py
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Mon Jul 10 16:18:03 2023
|
|
4
|
+
|
|
5
|
+
@author: mfratki
|
|
6
|
+
"""
|
|
7
|
+
import requests
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
#TODO: Use this url to make sure web service is working https://wiskiweb01.pca.state.mn.us/
|
|
12
|
+
class Service():
|
|
13
|
+
base_url = 'http://wiskiweb01.pca.state.mn.us/KiWIS/KiWIS?'
|
|
14
|
+
base_dict = {
|
|
15
|
+
'datasource': '0',
|
|
16
|
+
'service': 'kisters',
|
|
17
|
+
'type': 'queryServices',
|
|
18
|
+
'format': 'json'}
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
#TODO: store request types in a file and load them here to avoid making a request when the class is instantiated
|
|
22
|
+
#url = self.url({'request': 'getrequestinfo'})
|
|
23
|
+
#self.requestTypes = requests.get(url).json()[0]
|
|
24
|
+
self._url = None
|
|
25
|
+
self._args = None
|
|
26
|
+
|
|
27
|
+
# def _requestTypes(self):
|
|
28
|
+
# url = self.url({'request': 'getrequestinfo'})
|
|
29
|
+
# self.requestTypes = requests.get(url).json()[0]
|
|
30
|
+
# self._url = None
|
|
31
|
+
# self._args = None
|
|
32
|
+
def _requestTypes(self):
|
|
33
|
+
url = self.url({'request': 'getrequestinfo'})
|
|
34
|
+
return requests.get(url).json()[0]
|
|
35
|
+
|
|
36
|
+
def getRequests(self):
|
|
37
|
+
return list(self._requestTypes()['Requests'].keys())
|
|
38
|
+
|
|
39
|
+
def queryfields(self,request_type):
|
|
40
|
+
return list(self._requestTypes()['Requests'][request_type]['QueryFields']['Content'].keys())
|
|
41
|
+
|
|
42
|
+
def returnfields(self,request_type):
|
|
43
|
+
return list(self._requestTypes()['Requests'][request_type]['Returnfields']['Content'].keys())
|
|
44
|
+
|
|
45
|
+
def optionalfields(self,request_type):
|
|
46
|
+
return list(self._requestTypes()['Requests'][request_type]['Optionalfields']['Content'].keys())
|
|
47
|
+
|
|
48
|
+
def formats(self,request_type):
|
|
49
|
+
return list(self._requestTypes()['Requests'][request_type]['Formats']['Content'].keys())
|
|
50
|
+
|
|
51
|
+
def info(self,request_type):
|
|
52
|
+
url = self.url({'request': 'getrequestinfo'})
|
|
53
|
+
response = requests.get(url)
|
|
54
|
+
get_requests = response.json()
|
|
55
|
+
return get_requests[0]['Requests'].keys()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def url(self,args_dict):
|
|
59
|
+
args_dict = self.base_dict | args_dict
|
|
60
|
+
args = []
|
|
61
|
+
for k,v in args_dict.items():
|
|
62
|
+
if v is None:
|
|
63
|
+
continue
|
|
64
|
+
elif isinstance(v,list):
|
|
65
|
+
v = [str(vv) for vv in v]
|
|
66
|
+
v = ','.join(v)
|
|
67
|
+
args.append(f'{k}={v}')
|
|
68
|
+
args = '&'.join(args)
|
|
69
|
+
|
|
70
|
+
url = self.base_url + args
|
|
71
|
+
self._url = url
|
|
72
|
+
return url
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def df(self,args_dict):
|
|
76
|
+
|
|
77
|
+
# Download request
|
|
78
|
+
# print('Downloading')
|
|
79
|
+
response = requests.get(self.url(args_dict))
|
|
80
|
+
|
|
81
|
+
response.raise_for_status() # raises exception when not a 2xx response
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if response.status_code != 200:
|
|
86
|
+
print('Error: ' + response.json()['message'])
|
|
87
|
+
return 1
|
|
88
|
+
|
|
89
|
+
get_requests = response.json()
|
|
90
|
+
# Convert to dataframe
|
|
91
|
+
if args_dict['request'] in ['getTimeseriesValues']:
|
|
92
|
+
dfs = []
|
|
93
|
+
for get_request in get_requests:
|
|
94
|
+
df = pd.DataFrame(get_request['data'],columns = get_request['columns'].split(','))
|
|
95
|
+
del get_request['data']
|
|
96
|
+
del get_request['rows']
|
|
97
|
+
del get_request['columns']
|
|
98
|
+
for k,v in get_request.items(): df[k] = v
|
|
99
|
+
dfs.append(df)
|
|
100
|
+
df = pd.concat(dfs)
|
|
101
|
+
else:
|
|
102
|
+
df = pd.DataFrame(get_requests[1:], columns = get_requests[0])
|
|
103
|
+
|
|
104
|
+
# print('Done!')
|
|
105
|
+
return df
|
|
106
|
+
|
|
107
|
+
def get(self,args):
|
|
108
|
+
request_type = args['request']
|
|
109
|
+
assert(request_type in self.getRequests())
|
|
110
|
+
_args = {queryfield: None for queryfield in self.queryfields(request_type)} | {optionalfield: None for optionalfield in self.optionalfields(request_type)}
|
|
111
|
+
args = {**_args, **args}
|
|
112
|
+
self._args = args
|
|
113
|
+
return self.df(args)
|
|
114
|
+
|
|
115
|
+
def _filter(self,args):
|
|
116
|
+
|
|
117
|
+
'''
|
|
118
|
+
Filter for ensuring not too many values are requested and determining the proper division
|
|
119
|
+
given the number of timeseries, timeseries length, and timeseries sampling interval
|
|
120
|
+
'''
|
|
121
|
+
'minute','hour','daily'
|
|
122
|
+
|
|
123
|
+
MAX_OUTPUT = 240000 #True max output is 250,000 but giving myself a bit of a buffer
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
n_timeseries = 1
|
|
127
|
+
n_years = 1
|
|
128
|
+
#1 timeseries for 1 year
|
|
129
|
+
n_values = 60*24*365*n_timeseries*n_years
|
|
130
|
+
|
|
131
|
+
if n_values < MAX_OUTPUT :
|
|
132
|
+
return 0
|
|
133
|
+
elif n_timeseries == 1:
|
|
134
|
+
n_values/MAX_OUTPUT
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
'''
|
|
139
|
+
Potential use cases:
|
|
140
|
+
|
|
141
|
+
1. timeseries for a given ts_id
|
|
142
|
+
2. All timeseries for a given station
|
|
143
|
+
3. All timeseries for a given parameter
|
|
144
|
+
4. All timeseries for a given huc_id
|
|
145
|
+
5. All timeseries of a given resolution
|
|
146
|
+
|
|
147
|
+
'''
|
|
148
|
+
|
|
149
|
+
class pyWISK():
|
|
150
|
+
|
|
151
|
+
def __init__(self):
|
|
152
|
+
self.service = Service()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def get(self,args_dict):
|
|
156
|
+
return self.service.get(args_dict)
|
|
157
|
+
|
|
158
|
+
def get_ts(self,
|
|
159
|
+
ts_ids = None,
|
|
160
|
+
huc_id = None,
|
|
161
|
+
station_nos = None,
|
|
162
|
+
parametertype_id = None,
|
|
163
|
+
parameter_no = None,
|
|
164
|
+
start_date = '1996-01-01',
|
|
165
|
+
end_date = '2050-12-31',
|
|
166
|
+
stationgroup_id = None,
|
|
167
|
+
timezone = 'UTC'):
|
|
168
|
+
|
|
169
|
+
if ts_ids is None:
|
|
170
|
+
print('Determing Timeseries IDs')
|
|
171
|
+
ts_ids = self.get_ts_ids(station_nos,huc_id,parametertype_id)
|
|
172
|
+
print('Done!')
|
|
173
|
+
|
|
174
|
+
#print('Downloading Timeseries Data')
|
|
175
|
+
args = {'request':'getTimeseriesValues',
|
|
176
|
+
'ts_id' : ts_ids,
|
|
177
|
+
'from': start_date,
|
|
178
|
+
'to': end_date,
|
|
179
|
+
'returnfields': ['Timestamp', 'Value', 'Quality Code','Quality Code Name'],
|
|
180
|
+
'metadata': 'true',
|
|
181
|
+
'md_returnfields': ['ts_unitsymbol',
|
|
182
|
+
'ts_name',
|
|
183
|
+
'ts_id',
|
|
184
|
+
'station_no',
|
|
185
|
+
'station_name',
|
|
186
|
+
'station_latitude',
|
|
187
|
+
'station_longitude',
|
|
188
|
+
'parametertype_id',
|
|
189
|
+
'parametertype_name',
|
|
190
|
+
'stationparameter_no',
|
|
191
|
+
'stationparameter_name'],
|
|
192
|
+
'timezone':timezone,
|
|
193
|
+
'ca_sta_returnfields': ['stn_HUC12','stn_EQuIS_ID']}
|
|
194
|
+
|
|
195
|
+
df = self.service.get(args)
|
|
196
|
+
#print('Done!')
|
|
197
|
+
return df
|
|
198
|
+
|
|
199
|
+
def get_stations(self,
|
|
200
|
+
huc_id = None,
|
|
201
|
+
parametertype_id = None,
|
|
202
|
+
stationgroup_id = None,
|
|
203
|
+
stationparameter_no = None,
|
|
204
|
+
station_no = None,
|
|
205
|
+
returnfields = []):
|
|
206
|
+
|
|
207
|
+
args = {'request':'getStationList'}
|
|
208
|
+
|
|
209
|
+
returnfields = list(set(['ca_sta','station_no','station_name'] + returnfields))
|
|
210
|
+
|
|
211
|
+
args ={'request': 'getStationList',
|
|
212
|
+
'stationparameter_no': stationparameter_no,
|
|
213
|
+
'stationgroup_id': stationgroup_id,
|
|
214
|
+
'parametertype_id': parametertype_id,
|
|
215
|
+
'station_no': station_no,
|
|
216
|
+
#'object_type': object_type,
|
|
217
|
+
'returnfields': returnfields,
|
|
218
|
+
# 'parametertype_id','parametertype_name',
|
|
219
|
+
# 'station_latitude','station_longitude',
|
|
220
|
+
# 'stationparameter_no','stationparameter_name'],
|
|
221
|
+
'ca_sta_returnfields': ['stn_HUC12','stn_EQuIS_ID','stn_AUID','hydrounit_title','hydrounit_no','NearestTown']
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
df = self.service.get(args)
|
|
226
|
+
if huc_id is not None: df = df.loc[df['stn_HUC12'].str.startswith(huc_id)]
|
|
227
|
+
return df
|
|
228
|
+
|
|
229
|
+
def get_ts_ids(self,
|
|
230
|
+
station_nos=None,
|
|
231
|
+
huc_id = None,
|
|
232
|
+
parametertype_id = None,
|
|
233
|
+
stationparameter_no = None,
|
|
234
|
+
stationgroup_id = None,
|
|
235
|
+
ts_name = None,
|
|
236
|
+
returnfields = None):
|
|
237
|
+
|
|
238
|
+
if station_nos is None:
|
|
239
|
+
station_nos = self.get_stations(huc_id,parametertype_id,stationgroup_id,stationparameter_no)['station_no'].to_list()
|
|
240
|
+
|
|
241
|
+
if returnfields is None:
|
|
242
|
+
returnfields = ['ts_id','ts_name','ca_sta','station_no',
|
|
243
|
+
'ts_unitsymbol',
|
|
244
|
+
'parametertype_id','parametertype_name',
|
|
245
|
+
'station_latitude','station_longitude',
|
|
246
|
+
'stationparameter_no','stationparameter_name',
|
|
247
|
+
'station_no','station_name',
|
|
248
|
+
'coverage','ts_density']
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
args ={'request': 'getTimeseriesList',
|
|
252
|
+
'station_no': station_nos,
|
|
253
|
+
'parametertype_id': parametertype_id,
|
|
254
|
+
'stationparameter_no': stationparameter_no,
|
|
255
|
+
'ts_name' : ts_name,
|
|
256
|
+
'returnfields': returnfields,
|
|
257
|
+
'ca_sta_returnfields': ['stn_HUC12','stn_EQuIS_ID','stn_AUID']}
|
|
258
|
+
|
|
259
|
+
df = self.service.get(args)
|
|
260
|
+
return df
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def get_wplmn(self,station_nos):
|
|
265
|
+
|
|
266
|
+
PARAMETERS_MAP={'5004':'TP Load',
|
|
267
|
+
'5005':'TP Conc',
|
|
268
|
+
'5014':'TSS Load',
|
|
269
|
+
'5015':'TSS Conc',
|
|
270
|
+
'5024':'N Load',
|
|
271
|
+
'5025':'N Conc',
|
|
272
|
+
'5034':'OP Load',
|
|
273
|
+
'5035':'OP Conc',
|
|
274
|
+
'5044':'TKN Load',
|
|
275
|
+
'5045':'TKN Conc',
|
|
276
|
+
'262' :'Flow'}
|
|
277
|
+
|
|
278
|
+
ts_ids = self.get_ts_ids(station_nos = station_nos,
|
|
279
|
+
stationgroup_id = '1319204',
|
|
280
|
+
stationparameter_no = list(PARAMETERS_MAP.keys()),
|
|
281
|
+
ts_name = ['20.Day.Mean'])
|
|
282
|
+
|
|
283
|
+
if len(ts_ids) == 0:
|
|
284
|
+
print('No WPLMN Sites Available')
|
|
285
|
+
return pd.DataFrame()
|
|
286
|
+
|
|
287
|
+
dfs = []
|
|
288
|
+
for ts_id in ts_ids['ts_id']:
|
|
289
|
+
dfs.append(self.get_ts(ts_id))
|
|
290
|
+
time.sleep(1)
|
|
291
|
+
|
|
292
|
+
return pd.concat(dfs)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# CONSTITUENT_NAME_NO = {'Q' :['262'],#,'263'],
|
|
296
|
+
# 'WT' :['450'],# , '451' , '450.42','451.42'],
|
|
297
|
+
# 'OP' :['863' ,'5034' ,'5035'],
|
|
298
|
+
# 'DO' :['865' ,'866' , '867'],
|
|
299
|
+
# 'TP' :['5005' ,'5004'],
|
|
300
|
+
# 'TSS':['5014' ,'5015'],
|
|
301
|
+
# 'N' :['5024' ,'5025'],
|
|
302
|
+
# 'TKN':['5044' ,'5045']}
|
|
303
|
+
|
|
304
|
+
# TS_NAME_SELECTOR = {'Q':{'daily':['20.Day.Mean.Archive','20.Day.Mean'],
|
|
305
|
+
# 'unit': ['15.Rated','08.Provisional.Edited']},
|
|
306
|
+
# 'WT':{'daily':['20.Day.Mean','20.Day.Mean'],
|
|
307
|
+
# 'unit': ['09.Archive','08.Provisional.Edited']},
|
|
308
|
+
# 'TSS':{'daily':['20.Day.Mean','20.Day.Mean'],
|
|
309
|
+
# 'unit': ['09.Archive','08.Provisional.Edited']},
|
|
310
|
+
# 'N':{'daily':['20.Day.Mean','20.Day.Mean'],
|
|
311
|
+
# 'unit': ['09.Archive','08.Provisional.Edited']},
|
|
312
|
+
# 'TKN':{'daily':['20.Day.Mean','20.Day.Mean'],
|
|
313
|
+
# 'unit': ['09.Archive','08.Provisional.Edited']},
|
|
314
|
+
# 'TP':{'daily':['20.Day.Mean','20.Day.Mean'],
|
|
315
|
+
# 'unit': ['09.Archive','08.Provisional.Edited']},
|
|
316
|
+
# 'OP':{'daily':['20.Day.Mean','20.Day.Mean'],
|
|
317
|
+
# 'unit': ['09.Archive','08.Provisional.Edited']},
|
|
318
|
+
# 'DO':{'daily':['20.Day.Mean','20.Day.Mean'],
|
|
319
|
+
# 'unit': ['09.Archive','08.Provisional.Edited']}}
|
|
320
|
+
|
|
321
|
+
# def extract(self,station_nos,constituent,resolution):
|
|
322
|
+
# ts_names = self.TS_NAME_SELECTOR[constituent][resolution]
|
|
323
|
+
# data = self.get_ts_ids(station_no = station_nos,stationparameter_no = self.CONSTITUENT_NAME_NO[constituent],ts_name =ts_names)
|
|
324
|
+
# # Filter by MPCA distinction between internal and external sites and how time series are named
|
|
325
|
+
# ts_ids = pd.concat([data.loc[(data['station_no'].str.startswith('E')) & (data['ts_name'] == ts_names[1])],
|
|
326
|
+
# data.loc[(~data['station_no'].str.startswith('E')) & (data['ts_name'] == ts_names[0])]])
|
|
327
|
+
# dfs = [self.get_ts(ts_ids = ts_id) for ts_id in ts_ids['ts_id']]
|
|
328
|
+
# data = pd.concat(dfs)
|
|
329
|
+
# return data
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# nutrient
|
|
334
|
+
# -N03N02
|
|
335
|
+
# -OP
|
|
336
|
+
# -NH3
|
|
337
|
+
# -TP
|
|
338
|
+
# -DO
|
|
339
|
+
# -CHla
|
|
340
|
+
# temperature
|
|
341
|
+
# flow
|
|
342
|
+
|
|
343
|
+
# test = pyWISK()
|
|
344
|
+
|
|
345
|
+
# df = test.get_ts(ts_ids = 424663010)
|
|
346
|
+
|
|
347
|
+
# df = test.get_ts(station_nos = 'W25060001')
|
|
348
|
+
|
|
349
|
+
# df = test.get_wplmn(huc8_id = '07020005')
|
|
350
|
+
|
|
351
|
+
# df = test.get_ts(huc_id = '07010205',stationgroup_id = '1319204',parametertype_id = 11500)
|
mpcaHydro/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Fri Jun 3 10:01:14 2022
|
|
4
|
+
|
|
5
|
+
@author: mfratki
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
#from abc import abstractmethod
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from mpcaHydro import etlWISKI, etlSWD#, etlEQUIS
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
'''
|
|
16
|
+
Q
|
|
17
|
+
WT
|
|
18
|
+
TSS
|
|
19
|
+
N
|
|
20
|
+
TKN
|
|
21
|
+
OP
|
|
22
|
+
TP
|
|
23
|
+
CHLA
|
|
24
|
+
DO
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Station
|
|
28
|
+
|
|
29
|
+
- id
|
|
30
|
+
- name
|
|
31
|
+
- source
|
|
32
|
+
- data
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
'''
|
|
39
|
+
WISKI_EQUIS_XREF = pd.read_csv(Path(__file__).parent.parent/'WISKI_EQUIS_XREF.csv')
|
|
40
|
+
#WISKI_EQUIS_XREF = pd.read_csv('C:/Users/mfratki/Documents/GitHub/hspf_tools/WISKI_EQUIS_XREF.csv')
|
|
41
|
+
|
|
42
|
+
AGG_DEFAULTS = {'cfs':'mean',
|
|
43
|
+
'mg/l':'mean',
|
|
44
|
+
'degF': 'mean',
|
|
45
|
+
'lb':'sum'}
|
|
46
|
+
|
|
47
|
+
UNIT_DEFAULTS = {'Q': 'cfs',
|
|
48
|
+
'TSS': 'mg/l',
|
|
49
|
+
'TP' : 'mg/l',
|
|
50
|
+
'OP' : 'mg/l',
|
|
51
|
+
'TKN': 'mg/l',
|
|
52
|
+
'N' : 'mg/l',
|
|
53
|
+
'WT' : 'degF',
|
|
54
|
+
'WL' : 'ft'}
|
|
55
|
+
|
|
56
|
+
# VALID_UNITS = {'Q': 'cfs',
|
|
57
|
+
# 'TSS': 'mg/l','lb',
|
|
58
|
+
# 'TP' : 'mg/l',
|
|
59
|
+
# 'OP' : 'mg/l',
|
|
60
|
+
# 'TKN': 'mg/l',
|
|
61
|
+
# 'N' : 'mg/l',
|
|
62
|
+
# 'WT' : 'degF',
|
|
63
|
+
# 'WL' : 'ft'}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def are_lists_identical(nested_list):
|
|
67
|
+
# Sort each sublist
|
|
68
|
+
sorted_sublists = [sorted(sublist) for sublist in nested_list]
|
|
69
|
+
# Compare all sublists to the first one
|
|
70
|
+
return all(sublist == sorted_sublists[0] for sublist in sorted_sublists)
|
|
71
|
+
|
|
72
|
+
class dataManager():
|
|
73
|
+
|
|
74
|
+
def __init__(self,folderpath):
|
|
75
|
+
|
|
76
|
+
self.data = {}
|
|
77
|
+
self.folderpath = Path(folderpath)
|
|
78
|
+
|
|
79
|
+
def get_wiski_stations(self):
|
|
80
|
+
return list(WISKI_EQUIS_XREF['WISKI_STATION_NO'].unique())
|
|
81
|
+
|
|
82
|
+
def get_equis_stations(self):
|
|
83
|
+
return list(WISKI_EQUIS_XREF['EQUIS_STATION_ID'].unique())
|
|
84
|
+
|
|
85
|
+
def wiski_equis_alias(self,wiski_station_id):
|
|
86
|
+
equis_ids = list(set(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['WISKI_STATION_NO'] == wiski_station_id,'WISKI_EQUIS_ID'].to_list()))
|
|
87
|
+
equis_ids = [equis_id for equis_id in equis_ids if not pd.isna(equis_id)]
|
|
88
|
+
if len(equis_ids) == 0:
|
|
89
|
+
return []
|
|
90
|
+
elif len(equis_ids) > 1:
|
|
91
|
+
print(f'Too Many Equis Stations for {wiski_station_id}')
|
|
92
|
+
raise
|
|
93
|
+
else:
|
|
94
|
+
return equis_ids[0]
|
|
95
|
+
|
|
96
|
+
def wiski_equis_associations(self,wiski_station_id):
|
|
97
|
+
equis_ids = list(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['WISKI_STATION_NO'] == wiski_station_id,'EQUIS_STATION_ID'].unique())
|
|
98
|
+
equis_ids = [equis_id for equis_id in equis_ids if not pd.isna(equis_id)]
|
|
99
|
+
if len(equis_ids) == 0:
|
|
100
|
+
return []
|
|
101
|
+
else:
|
|
102
|
+
return equis_ids
|
|
103
|
+
|
|
104
|
+
def equis_wiski_associations(self,equis_station_id):
|
|
105
|
+
wiski_ids = list(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['EQUIS_STATION_ID'] == equis_station_id,'WISKI_STATION_NO'].unique())
|
|
106
|
+
wiski_ids = [wiski_id for wiski_id in wiski_ids if not pd.isna(wiski_id)]
|
|
107
|
+
if len(wiski_ids) == 0:
|
|
108
|
+
return []
|
|
109
|
+
else:
|
|
110
|
+
return wiski_ids
|
|
111
|
+
|
|
112
|
+
def _equis_wiski_associations(self,equis_station_ids):
|
|
113
|
+
wiski_stations = [self.equis_wiski_associations(equis_station_id) for equis_station_id in equis_station_ids]
|
|
114
|
+
if are_lists_identical(wiski_stations):
|
|
115
|
+
return wiski_stations[0]
|
|
116
|
+
else:
|
|
117
|
+
return []
|
|
118
|
+
|
|
119
|
+
def _download_station_data(self,station_id,station_origin,overwrite=False):
|
|
120
|
+
assert(station_origin in ['wiski','equis','swd','wplmn'])
|
|
121
|
+
if station_origin == 'wiski':
|
|
122
|
+
#equis_stations = list(WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['WISKI_STATION_NO'] == station_id,'WISKI_EQUIS_ID'].unique())
|
|
123
|
+
#[self.download_station_data(equis_station,'equis',overwrite = overwrite) for equis_station in equis_stations]
|
|
124
|
+
self.download_station_data(station_id,'wiski',overwrite = overwrite)
|
|
125
|
+
equis_alias = self.wiski_equis_alias(station_id)
|
|
126
|
+
self.download_station_data(equis_alias,'swd',overwrite = overwrite)
|
|
127
|
+
elif station_origin == 'wplmn':
|
|
128
|
+
self.download_station_data(station_id,'wplmn',overwrite = overwrite)
|
|
129
|
+
equis_alias = self.wiski_equis_alias(station_id)
|
|
130
|
+
self.download_station_data(equis_alias,'swd',overwrite = overwrite)
|
|
131
|
+
else:
|
|
132
|
+
wiski_station = self.equis_wiski_associations(station_id)
|
|
133
|
+
#wiski_station = WISKI_EQUIS_XREF.loc[WISKI_EQUIS_XREF['EQUIS_STATION_ID'] == station_id,'WISKI_STATION_NO']
|
|
134
|
+
self.download_station_data(station_id,'equis',overwrite = overwrite)
|
|
135
|
+
self.download_station_data(wiski_station,'wiski',overwrite = overwrite)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def download_station_data(self,station_id,source,folderpath=None,overwrite = False):
|
|
139
|
+
assert(source in ['wiski','equis','swd','wplmn'])
|
|
140
|
+
station_id = str(station_id)
|
|
141
|
+
save_name = station_id
|
|
142
|
+
if source == 'wplmn':
|
|
143
|
+
save_name = station_id + '_wplmn'
|
|
144
|
+
|
|
145
|
+
if folderpath is None:
|
|
146
|
+
folderpath = self.folderpath
|
|
147
|
+
else:
|
|
148
|
+
folderpath = Path(folderpath)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if (folderpath.joinpath(save_name + '.csv').exists()) & (not overwrite):
|
|
152
|
+
print (f'{station_id} data already downloaded')
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
if source == 'wiski':
|
|
156
|
+
data = etlWISKI.download(station_id)
|
|
157
|
+
elif source == 'swd':
|
|
158
|
+
data = etlSWD.download(station_id)
|
|
159
|
+
elif source == 'equis':
|
|
160
|
+
data = etlSWD.download(station_id)
|
|
161
|
+
else:
|
|
162
|
+
data = etlWISKI.download(station_id,wplmn=True)
|
|
163
|
+
#raise NotImplementedError()
|
|
164
|
+
#data = etlEQUIS.download(station_id)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if len(data) > 0:
|
|
169
|
+
data.to_csv(folderpath.joinpath(save_name + '.csv'))
|
|
170
|
+
self.data[station_id] = data
|
|
171
|
+
else:
|
|
172
|
+
print(f'No {source} calibration cata available at Station {station_id}')
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _load(self,station_id):
|
|
176
|
+
df = pd.read_csv(self.folderpath.joinpath(station_id + '.csv'),
|
|
177
|
+
index_col='datetime',
|
|
178
|
+
parse_dates=['datetime'],
|
|
179
|
+
#usecols=['Ts Date','Station number','variable', 'value','reach_id'],
|
|
180
|
+
dtype={'station_id': str, 'value': float, 'variable': str,'constituent':str,'unit':str})
|
|
181
|
+
self.data[station_id] = df
|
|
182
|
+
return df
|
|
183
|
+
|
|
184
|
+
def load(self,station_id):
|
|
185
|
+
try:
|
|
186
|
+
df = self.data[station_id]
|
|
187
|
+
except:
|
|
188
|
+
df = pd.read_csv(self.folderpath.joinpath(station_id + '.csv'),
|
|
189
|
+
index_col='datetime',
|
|
190
|
+
parse_dates=['datetime'],
|
|
191
|
+
#usecols=['Ts Date','Station number','variable', 'value','reach_id'],
|
|
192
|
+
dtype={'station_id': str, 'value': float, 'variable': str,'constituent':str,'unit':str})
|
|
193
|
+
self.data[station_id] = df
|
|
194
|
+
return df
|
|
195
|
+
|
|
196
|
+
def info(self,constituent):
|
|
197
|
+
return pd.concat([self._load(file.stem) for file in self.folderpath.iterdir() if file.suffix == '.csv'])[['station_id','constituent','value']].groupby(by = ['station_id','constituent']).count()
|
|
198
|
+
|
|
199
|
+
def get_wplmn_data(self,station_id,constituent,unit = 'mg/l', agg_period = 'YE', samples_only = True):
|
|
200
|
+
|
|
201
|
+
assert constituent in ['Q','TSS','TP','OP','TKN','N','WT','DO','WL','CHLA']
|
|
202
|
+
station_id = station_id + '_wplmn'
|
|
203
|
+
dfsub = self._load(station_id)
|
|
204
|
+
|
|
205
|
+
if samples_only:
|
|
206
|
+
dfsub = dfsub.loc[dfsub['quality_id'] == 3]
|
|
207
|
+
agg_func = 'mean'
|
|
208
|
+
|
|
209
|
+
dfsub = dfsub.loc[(dfsub['constituent'] == constituent) &
|
|
210
|
+
(dfsub['unit'] == unit),
|
|
211
|
+
['value','data_format','source']]
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
df = dfsub[['value']].resample(agg_period).agg(agg_func)
|
|
215
|
+
|
|
216
|
+
if df.empty:
|
|
217
|
+
dfsub = df
|
|
218
|
+
else:
|
|
219
|
+
|
|
220
|
+
df['data_format'] = dfsub['data_format'].iloc[0]
|
|
221
|
+
df['source'] = dfsub['source'].iloc[0]
|
|
222
|
+
|
|
223
|
+
#if (constituent == 'TSS') & (unit == 'lb'): #convert TSS from lbs to us tons
|
|
224
|
+
# dfsub['value'] = dfsub['value']/2000
|
|
225
|
+
|
|
226
|
+
#dfsub = dfsub.resample('H').mean().dropna()
|
|
227
|
+
|
|
228
|
+
df.attrs['unit'] = unit
|
|
229
|
+
df.attrs['constituent'] = constituent
|
|
230
|
+
return df['value'].to_frame().dropna()
|
|
231
|
+
|
|
232
|
+
def get_data(self,station_id,constituent,agg_period = 'D'):
|
|
233
|
+
return self._get_data([station_id],constituent,agg_period)
|
|
234
|
+
|
|
235
|
+
def _get_data(self,station_ids,constituent,agg_period = 'D'):
|
|
236
|
+
'''
|
|
237
|
+
|
|
238
|
+
Returns the processed observational data associated with the calibration specific id.
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
station_id : STR
|
|
244
|
+
Station ID as a string
|
|
245
|
+
constituent : TYPE
|
|
246
|
+
Constituent abbreviation used for calibration. Valid options:
|
|
247
|
+
'Q',
|
|
248
|
+
'TSS',
|
|
249
|
+
'TP',
|
|
250
|
+
'OP',
|
|
251
|
+
'TKN',
|
|
252
|
+
'N',
|
|
253
|
+
'WT',
|
|
254
|
+
'DO',
|
|
255
|
+
'WL']
|
|
256
|
+
unit : TYPE, optional
|
|
257
|
+
Units of data. The default is 'mg/l'.
|
|
258
|
+
sample_flag : TYPE, optional
|
|
259
|
+
For WPLMN data this flag determines modeled loads are returned. The default is False.
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
dfsub : Pands.Series
|
|
264
|
+
Pandas series of data. Note that no metadata is returned.
|
|
265
|
+
|
|
266
|
+
'''
|
|
267
|
+
|
|
268
|
+
assert constituent in ['Q','TSS','TP','OP','TKN','N','WT','DO','WL','CHLA']
|
|
269
|
+
|
|
270
|
+
unit = UNIT_DEFAULTS[constituent]
|
|
271
|
+
agg_func = AGG_DEFAULTS[unit]
|
|
272
|
+
|
|
273
|
+
dfsub = pd.concat([self.load(station_id) for station_id in station_ids]) # Check cache
|
|
274
|
+
dfsub = dfsub.loc[(dfsub['constituent'] == constituent) &
|
|
275
|
+
(dfsub['unit'] == unit),
|
|
276
|
+
['value','data_format','source']]
|
|
277
|
+
|
|
278
|
+
df = dfsub[['value']].resample(agg_period).agg(agg_func)
|
|
279
|
+
df.attrs['unit'] = unit
|
|
280
|
+
df.attrs['constituent'] = constituent
|
|
281
|
+
|
|
282
|
+
if df.empty:
|
|
283
|
+
|
|
284
|
+
return df
|
|
285
|
+
else:
|
|
286
|
+
|
|
287
|
+
df['data_format'] = dfsub['data_format'].iloc[0]
|
|
288
|
+
df['source'] = dfsub['source'].iloc[0]
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
return df['value'].to_frame().dropna()
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def validate_constituent(constituent):
|
|
295
|
+
assert constituent in ['Q','TSS','TP','OP','TKN','N','WT','DO','WL','CHLA']
|
|
296
|
+
|
|
297
|
+
def validate_unit(unit):
|
|
298
|
+
assert(unit in ['mg/l','lb','cfs','degF'])
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# class database():
|
|
303
|
+
# def __init__(self,db_path):
|
|
304
|
+
# self.dbm = MonitoringDatabase(db_path)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# def get_timeseries(self,station_ds, constituent,agg_period):
|
|
308
|
+
# validate_constituent(constituent)
|
|
309
|
+
# unit = UNIT_DEFAULTS[constituent]
|
|
310
|
+
# agg_func = AGG_DEFAULTS[unit]
|
|
311
|
+
# return odm.get_timeseries(station_id,constituent)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# def get_samples(self,station_ds, constituent,agg_period):
|
|
315
|
+
# validate_constituent(constituent)
|
|
316
|
+
# unit = UNIT_DEFAULTS[constituent]
|
|
317
|
+
# agg_func = AGG_DEFAULTS[unit]
|
|
318
|
+
# return odm.get_sample(station_id,constituent)
|
|
319
|
+
|
|
320
|
+
# def get_samples_and_timeseries(self,station_ds, constituent,agg_period)
|
|
321
|
+
|