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