1
|
|
|
# |
2
|
|
|
# Classes for retrieving soundings based on gridded data from the Data Access |
3
|
|
|
# Framework |
4
|
|
|
# |
5
|
|
|
# |
6
|
|
|
# |
7
|
|
|
# SOFTWARE HISTORY |
8
|
|
|
# |
9
|
|
|
# Date Ticket# Engineer Description |
10
|
|
|
# ------------ ---------- ----------- -------------------------- |
11
|
|
|
# 06/24/15 #4480 dgilling Initial Creation. |
12
|
|
|
# |
13
|
|
|
|
14
|
|
|
from collections import defaultdict |
|
|
|
|
15
|
|
|
from shapely.geometry import Point |
16
|
|
|
|
17
|
|
|
from awips import DateTimeConverter |
|
|
|
|
18
|
|
|
from awips.dataaccess import DataAccessLayer |
19
|
|
|
|
20
|
|
|
from dynamicserialize.dstypes.com.raytheon.uf.common.time import DataTime |
|
|
|
|
21
|
|
|
from dynamicserialize.dstypes.com.raytheon.uf.common.dataplugin.level import Level |
22
|
|
|
|
23
|
|
|
|
24
|
|
View Code Duplication |
def getSounding(modelName, weatherElements, levels, samplePoint, timeRange=None): |
|
|
|
|
25
|
|
|
""" |
26
|
|
|
Performs a series of Data Access Framework requests to retrieve a sounding object |
27
|
|
|
based on the specified request parameters. |
28
|
|
|
|
29
|
|
|
Args: |
30
|
|
|
modelName: the grid model datasetid to use as the basis of the sounding. |
31
|
|
|
weatherElements: a list of parameters to return in the sounding. |
32
|
|
|
levels: a list of levels to sample the given weather elements at |
33
|
|
|
samplePoint: a lat/lon pair to perform the sampling of data at. |
34
|
|
|
timeRange: (optional) a list of times, or a TimeRange to specify |
35
|
|
|
which forecast hours to use. If not specified, will default to all forecast hours. |
36
|
|
|
|
37
|
|
|
Returns: |
38
|
|
|
A _SoundingCube instance, which acts a 3-tiered dictionary, keyed |
39
|
|
|
by DataTime, then by level and finally by weather element. If no |
40
|
|
|
data is available for the given request parameters, None is returned. |
41
|
|
|
|
42
|
|
|
""" |
43
|
|
|
|
44
|
|
|
(locationNames, parameters, levels, envelope, timeRange) = \ |
45
|
|
|
__sanitizeInputs(modelName, weatherElements, levels, samplePoint, timeRange) |
46
|
|
|
|
47
|
|
|
requestArgs = { 'datatype' : 'grid', |
|
|
|
|
48
|
|
|
'locationNames' : locationNames, |
49
|
|
|
'parameters' : parameters, |
50
|
|
|
'levels' : levels, |
51
|
|
|
'envelope' : envelope |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
req = DataAccessLayer.newDataRequest(**requestArgs) |
55
|
|
|
response = DataAccessLayer.getGeometryData(req, timeRange) |
56
|
|
|
soundingObject = _SoundingCube(response) |
57
|
|
|
|
58
|
|
|
return soundingObject |
59
|
|
|
|
60
|
|
|
|
61
|
|
|
def changeEDEXHost(host): |
62
|
|
|
""" |
63
|
|
|
Changes the EDEX host the Data Access Framework is communicating with. |
64
|
|
|
|
65
|
|
|
Args: |
66
|
|
|
host: the EDEX host to connect to |
67
|
|
|
""" |
68
|
|
|
|
69
|
|
|
if host: |
70
|
|
|
DataAccessLayer.changeEDEXHost(str(host)) |
71
|
|
|
|
72
|
|
|
|
73
|
|
|
def __sanitizeInputs(modelName, weatherElements, levels, samplePoint, timeRange): |
74
|
|
|
locationNames = [str(modelName)] |
75
|
|
|
parameters = __buildStringList(weatherElements) |
76
|
|
|
levels = __buildStringList(levels) |
77
|
|
|
envelope = Point(samplePoint) |
78
|
|
|
return locationNames, parameters, levels, envelope, timeRange |
79
|
|
|
|
80
|
|
|
|
81
|
|
|
def __buildStringList(param): |
82
|
|
|
if __notStringIter(param): |
|
|
|
|
83
|
|
|
return [str(item) for item in param] |
84
|
|
|
else: |
85
|
|
|
return [str(param)] |
86
|
|
|
|
87
|
|
|
|
88
|
|
|
def __notStringIter(iterable): |
89
|
|
|
if not isinstance(iterable, str): |
90
|
|
|
try: |
91
|
|
|
iter(iterable) |
92
|
|
|
return True |
93
|
|
|
except TypeError: |
94
|
|
|
return False |
95
|
|
|
|
96
|
|
|
|
97
|
|
View Code Duplication |
class _SoundingCube(object): |
|
|
|
|
98
|
|
|
""" |
99
|
|
|
The top-level sounding object returned when calling ModelSounding.getSounding. |
100
|
|
|
|
101
|
|
|
This object acts as a 3-tiered dict which is keyed by time then level |
102
|
|
|
then parameter name. Calling times() will return all valid keys into this |
103
|
|
|
object. |
104
|
|
|
""" |
105
|
|
|
|
106
|
|
|
def __init__(self, geometryDataObjects): |
107
|
|
|
self._dataDict = {} |
108
|
|
|
self._sortedTimes = [] |
109
|
|
|
if geometryDataObjects: |
110
|
|
|
for geometryData in geometryDataObjects: |
111
|
|
|
dataTime = geometryData.getDataTime() |
112
|
|
|
level = geometryData.getLevel() |
113
|
|
|
for parameter in geometryData.getParameters(): |
114
|
|
|
self.__addItem(parameter, dataTime, level, geometryData.getNumber(parameter)) |
115
|
|
|
|
116
|
|
|
def __addItem(self, parameter, dataTime, level, value): |
117
|
|
|
timeLayer = self._dataDict.get(dataTime, _SoundingTimeLayer(dataTime)) |
118
|
|
|
self._dataDict[dataTime] = timeLayer |
119
|
|
|
timeLayer._addItem(parameter, level, value) |
|
|
|
|
120
|
|
|
if dataTime not in self._sortedTimes: |
121
|
|
|
self._sortedTimes.append(dataTime) |
122
|
|
|
self._sortedTimes.sort() |
123
|
|
|
|
124
|
|
|
def __getitem__(self, key): |
125
|
|
|
return self._dataDict[key] |
126
|
|
|
|
127
|
|
|
def __len__(self): |
128
|
|
|
return len(self._dataDict) |
129
|
|
|
|
130
|
|
|
def times(self): |
131
|
|
|
""" |
132
|
|
|
Returns the valid times for this sounding. |
133
|
|
|
|
134
|
|
|
Returns: |
135
|
|
|
A list containing the valid DataTimes for this sounding in order. |
136
|
|
|
""" |
137
|
|
|
return self._sortedTimes |
138
|
|
|
|
139
|
|
|
|
140
|
|
View Code Duplication |
class _SoundingTimeLayer(object): |
|
|
|
|
141
|
|
|
""" |
142
|
|
|
The second-level sounding object returned when calling ModelSounding.getSounding. |
143
|
|
|
|
144
|
|
|
This object acts as a 2-tiered dict which is keyed by level then parameter |
145
|
|
|
name. Calling levels() will return all valid keys into this |
146
|
|
|
object. Calling time() will return the DataTime for this particular layer. |
147
|
|
|
""" |
148
|
|
|
|
149
|
|
|
def __init__(self, dataTime): |
150
|
|
|
self._dataTime = dataTime |
151
|
|
|
self._dataDict = {} |
152
|
|
|
|
153
|
|
|
def _addItem(self, parameter, level, value): |
154
|
|
|
asString = str(level) |
155
|
|
|
levelLayer = self._dataDict.get(asString, _SoundingTimeAndLevelLayer(self._dataTime, asString)) |
|
|
|
|
156
|
|
|
levelLayer._addItem(parameter, value) |
|
|
|
|
157
|
|
|
self._dataDict[asString] = levelLayer |
158
|
|
|
|
159
|
|
|
def __getitem__(self, key): |
160
|
|
|
asString = str(key) |
161
|
|
|
if asString in self._dataDict: |
162
|
|
|
return self._dataDict[asString] |
163
|
|
|
else: |
164
|
|
|
raise KeyError("Level " + str(key) + " is not a valid level for this sounding.") |
165
|
|
|
|
166
|
|
|
def __len__(self): |
167
|
|
|
return len(self._dataDict) |
168
|
|
|
|
169
|
|
|
def time(self): |
170
|
|
|
""" |
171
|
|
|
Returns the DataTime for this sounding cube layer. |
172
|
|
|
|
173
|
|
|
Returns: |
174
|
|
|
The DataTime for this sounding layer. |
175
|
|
|
""" |
176
|
|
|
return self._dataTime |
177
|
|
|
|
178
|
|
|
def levels(self): |
179
|
|
|
""" |
180
|
|
|
Returns the valid levels for this sounding. |
181
|
|
|
|
182
|
|
|
Returns: |
183
|
|
|
A list containing the valid levels for this sounding in order of |
184
|
|
|
closest to surface to highest from surface. |
185
|
|
|
""" |
186
|
|
|
sortedLevels = [Level(level) for level in list(self._dataDict.keys())] |
187
|
|
|
sortedLevels.sort() |
188
|
|
|
return [str(level) for level in sortedLevels] |
189
|
|
|
|
190
|
|
|
|
191
|
|
View Code Duplication |
class _SoundingTimeAndLevelLayer(object): |
|
|
|
|
192
|
|
|
""" |
193
|
|
|
The bottom-level sounding object returned when calling ModelSounding.getSounding. |
194
|
|
|
|
195
|
|
|
This object acts as a dict which is keyed by parameter name. Calling |
196
|
|
|
parameters() will return all valid keys into this object. Calling time() |
197
|
|
|
will return the DataTime for this particular layer. Calling level() will |
198
|
|
|
return the level for this layer. |
199
|
|
|
""" |
200
|
|
|
|
201
|
|
|
def __init__(self, time, level): |
202
|
|
|
self._time = time |
203
|
|
|
self._level = level |
204
|
|
|
self._parameters = {} |
205
|
|
|
|
206
|
|
|
def _addItem(self, parameter, value): |
207
|
|
|
self._parameters[parameter] = value |
208
|
|
|
|
209
|
|
|
def __getitem__(self, key): |
210
|
|
|
return self._parameters[key] |
211
|
|
|
|
212
|
|
|
def __len__(self): |
213
|
|
|
return len(self._parameters) |
214
|
|
|
|
215
|
|
|
def level(self): |
216
|
|
|
""" |
217
|
|
|
Returns the level for this sounding cube layer. |
218
|
|
|
|
219
|
|
|
Returns: |
220
|
|
|
The level for this sounding layer. |
221
|
|
|
""" |
222
|
|
|
return self._level |
223
|
|
|
|
224
|
|
|
def parameters(self): |
225
|
|
|
""" |
226
|
|
|
Returns the valid parameters for this sounding. |
227
|
|
|
|
228
|
|
|
Returns: |
229
|
|
|
A list containing the valid parameter names. |
230
|
|
|
""" |
231
|
|
|
return list(self._parameters.keys()) |
232
|
|
|
|
233
|
|
|
def time(self): |
234
|
|
|
""" |
235
|
|
|
Returns the DataTime for this sounding cube layer. |
236
|
|
|
|
237
|
|
|
Returns: |
238
|
|
|
The DataTime for this sounding layer. |
239
|
|
|
""" |
240
|
|
|
return self._time |
241
|
|
|
|