1 | import os |
||
2 | import math |
||
3 | from awips import ThriftClient |
||
4 | from dynamicserialize.dstypes.gov.noaa.nws.ncep.common.dataplugin.gempak.request import GetGridNavRequest |
||
5 | from ctypes import * |
||
6 | |||
7 | EARTH_RADIUS = 6371200.0 |
||
8 | DEG_TO_RAD = math.pi / 180.0 |
||
9 | RAD_TO_DEG = 180.0 / math.pi |
||
10 | TWOPI = math.pi * 2.0 |
||
11 | HALFPI = math.pi / 2.0 |
||
12 | PI4TH = math.pi / 4.0 |
||
13 | PI3RD = math.pi / 3.0 |
||
14 | |||
15 | |||
16 | def createPolar(nsflag, clon, lat1, lon1, dx, dy, unit, nx, ny): |
||
17 | clonr = clon * DEG_TO_RAD |
||
18 | latr = lat1 * DEG_TO_RAD |
||
19 | lonr = lon1 * DEG_TO_RAD |
||
20 | if nsflag == 'N': |
||
21 | x1 = EARTH_RADIUS * math.tan(PI4TH - latr/2.0) * math.sin(lonr-clonr) |
||
22 | y1 = -1 * EARTH_RADIUS * math.tan(PI4TH - latr/2.0) * math.cos(lonr-clonr) |
||
23 | else: |
||
24 | x1 = EARTH_RADIUS * math.tan(PI4TH + latr/2.0) * math.sin(lonr-clonr) |
||
25 | y1 = EARTH_RADIUS * math.tan(PI4TH + latr/2.0) * math.cos(lonr-clonr) |
||
26 | |||
27 | if unit == 'm': |
||
28 | tdx = dx / (1 + math.sin(PI3RD)) |
||
29 | tdy = dy / (1 + math.sin(PI3RD)) |
||
30 | else: |
||
31 | tdx = (dx*1000.0) / (1 + math.sin(PI3RD)) |
||
32 | tdy = (dy*1000.0) / (1 + math.sin(PI3RD)) |
||
33 | |||
34 | x2 = x1 + tdx * (nx-1) |
||
35 | y2 = y1 + tdy * (ny-1) |
||
36 | xll = min(x1, x2) |
||
37 | yll = min(y1, y2) |
||
38 | xur = max(x1, x2) |
||
39 | yur = max(y1, y2) |
||
40 | |||
41 | if nsflag == 'N': |
||
42 | latll = (HALFPI - 2*math.atan2(math.hypot(xll, yll), EARTH_RADIUS)) * RAD_TO_DEG |
||
43 | rtemp = clonr + math.atan2(xll, -yll) |
||
44 | else: |
||
45 | latll = -1 * (HALFPI - 2*math.atan2(math.hypot(xll, yll), EARTH_RADIUS)) * RAD_TO_DEG |
||
46 | rtemp = clonr + math.atan2(xll, yll) |
||
47 | |||
48 | if rtemp > math.pi: |
||
49 | lonll = (rtemp-TWOPI) * RAD_TO_DEG |
||
50 | elif rtemp < -math.pi: |
||
51 | lonll = (rtemp+TWOPI) * RAD_TO_DEG |
||
52 | else: |
||
53 | lonll = rtemp * RAD_TO_DEG |
||
54 | |||
55 | if nsflag == 'N': |
||
56 | latur = (HALFPI - 2*math.atan2(math.hypot(xur, yur), EARTH_RADIUS)) * RAD_TO_DEG |
||
57 | rtemp = clonr + math.atan2(xur, -yur) |
||
58 | else: |
||
59 | latur = -1 * (HALFPI - 2*math.atan2(math.hypot(xur, yur), EARTH_RADIUS)) * RAD_TO_DEG |
||
60 | rtemp = clonr + math.atan2(xur, yur) |
||
61 | |||
62 | if rtemp > math.pi: |
||
63 | lonur = (rtemp-TWOPI) * RAD_TO_DEG |
||
64 | elif rtemp < -math.pi: |
||
65 | lonur = (rtemp+TWOPI) * RAD_TO_DEG |
||
66 | else: |
||
67 | lonur = rtemp * RAD_TO_DEG |
||
68 | |||
69 | return [latll, lonll, latur, lonur] |
||
70 | |||
71 | |||
72 | def createConic(nsflag, clon, lat1, lon1, dx, dy, unit, nx, ny, ang1, ang3): |
||
73 | clonr = clon * DEG_TO_RAD |
||
74 | latr = lat1 * DEG_TO_RAD |
||
75 | lonr = lon1 * DEG_TO_RAD |
||
76 | |||
77 | angle1 = HALFPI - (math.fabs(ang1) * DEG_TO_RAD) |
||
78 | angle2 = HALFPI - (math.fabs(ang3) * DEG_TO_RAD) |
||
79 | |||
80 | if ang1 == ang3: |
||
81 | cc = math.cos(angle1) |
||
82 | else: |
||
83 | cc = (math.log(math.sin(angle2)) - math.log(math.sin(angle1))) \ |
||
84 | / (math.log(math.tan(angle2/2.0)) - math.log(math.tan(angle1/2.0))) |
||
85 | |||
86 | er = EARTH_RADIUS / cc |
||
87 | |||
88 | if nsflag == 'N': |
||
89 | x1 = er * math.pow(math.tan((HALFPI-latr)/2.0), cc) * math.sin(cc*(lonr-clonr)) |
||
90 | y1 = -1.0 * er * math.pow(math.tan((HALFPI-latr)/2.0), cc) * math.cos(cc*(lonr-clonr)) |
||
91 | else: |
||
92 | x1 = er * math.pow(math.tan((HALFPI+latr)/2.0), cc) * math.sin(cc*(lonr-clonr)) |
||
93 | y1 = er * math.pow(math.tan((HALFPI+latr)/2.0), cc) * math.cos(cc*(lonr-clonr)) |
||
94 | |||
95 | alpha = math.pow(math.tan(angle1/2.0), cc) / math.sin(angle1) |
||
96 | |||
97 | if unit == 'm': |
||
98 | x2 = x1 + (nx-1) * alpha * dx |
||
99 | y2 = y1 + (ny-1) * alpha * dy |
||
100 | else: |
||
101 | x2 = x1 + (nx-1) * alpha * (dx*1000.0) |
||
102 | y2 = y1 + (ny-1) * alpha * (dy*1000.0) |
||
103 | |||
104 | xll = min(x1, x2) |
||
105 | yll = min(y1, y2) |
||
106 | xur = max(x1, x2) |
||
107 | yur = max(y1, y2) |
||
108 | |||
109 | if nsflag == 'N': |
||
110 | latll = (HALFPI - 2.0 * math.atan(math.pow(math.hypot(xll, yll)/er, (1/cc)))) * RAD_TO_DEG |
||
111 | rtemp = math.atan2(xll, -yll) * (1/cc) + clonr |
||
112 | else: |
||
113 | latll = (-1.0 * (HALFPI - 2.0 * math.atan(math.pow(math.hypot(xll, yll)/er, (1/cc))))) * RAD_TO_DEG |
||
114 | rtemp = math.atan2(xll, yll) * (1/cc) + clonr |
||
115 | |||
116 | if rtemp > math.pi: |
||
117 | lonll = (rtemp-TWOPI) * RAD_TO_DEG |
||
118 | elif rtemp < -math.pi: |
||
119 | lonll = (rtemp+TWOPI) * RAD_TO_DEG |
||
120 | else: |
||
121 | lonll = rtemp * RAD_TO_DEG |
||
122 | |||
123 | if nsflag == 'N': |
||
124 | latur = (HALFPI - 2.0 * math.atan(math.pow(math.hypot(xur, yur)/er, (1/cc)))) * RAD_TO_DEG |
||
125 | rtemp = math.atan2(xur, -yur) * (1/cc) + clonr |
||
126 | else: |
||
127 | latur = (-1.0 * (HALFPI - 2.0 * math.atan(math.pow(math.hypot(xur, yur)/er, (1/cc))))) * RAD_TO_DEG |
||
128 | rtemp = math.atan2(xur, yur) * (1/cc) + clonr |
||
129 | |||
130 | if rtemp > math.pi: |
||
131 | lonur = (rtemp-TWOPI) * RAD_TO_DEG |
||
132 | elif rtemp < -math.pi: |
||
133 | lonur = (rtemp+TWOPI) * RAD_TO_DEG |
||
134 | else: |
||
135 | lonur = rtemp * RAD_TO_DEG |
||
136 | |||
137 | return [latll, lonll, latur, lonur] |
||
138 | |||
139 | |||
140 | class StringConverter(Union): |
||
141 | _fields_ = [("char", c_char*4), ("int", c_int), ("float", c_float)] |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() Comprehensibility
Best Practice
introduced
by
Comprehensibility
Best Practice
introduced
by
|
|||
142 | |||
143 | |||
144 | class GridNavRetriever: |
||
145 | |||
146 | def __init__(self, server, pluginName, modelId, arrayLen): |
||
147 | self.pluginName = pluginName |
||
148 | self.modelId = modelId |
||
149 | self.arrayLen = arrayLen |
||
150 | self.host = os.getenv("DEFAULT_HOST", server) |
||
151 | self.port = os.getenv("DEFAULT_PORT", "9581") |
||
152 | self.client = ThriftClient.ThriftClient(self.host, self.port) |
||
153 | |||
154 | def getNavBlk(self): |
||
155 | """ Sends ThriftClient request and writes out received files.""" |
||
156 | req = GetGridNavRequest() |
||
157 | req.setPluginName(self.pluginName) |
||
158 | req.setModelId(self.modelId) |
||
159 | resp = self.client.sendRequest(req) |
||
160 | |||
161 | for i, rec in enumerate(resp): |
||
162 | resp[i] = { |
||
163 | key.decode() if isinstance(key, bytes) else key: |
||
164 | val.decode() if isinstance(val, bytes) else val |
||
165 | for key, val in rec.items() |
||
166 | } |
||
167 | |||
168 | nav = [] |
||
169 | |||
170 | for record in resp: |
||
171 | unit = record['spacingunit'] |
||
172 | sk = record['spatialkey'] |
||
173 | skarr = sk.split('/') |
||
174 | |||
175 | nx = float(skarr[1]) |
||
176 | ny = float(skarr[2]) |
||
177 | dx = float(skarr[3]) |
||
178 | dy = float(skarr[4]) |
||
179 | |||
180 | sc = StringConverter() |
||
181 | if record['projtype'] == 'LatLon': |
||
182 | sc.char = 'CED ' |
||
183 | gemproj = 2.0 |
||
184 | ang1 = 0.0 |
||
185 | ang2 = 0.0 |
||
186 | ang3 = 0.0 |
||
187 | |||
188 | lllat = float(record['lowerleftlat']) |
||
189 | lllon = float(record['lowerleftlon']) |
||
190 | urlat = lllat + (dy * (ny-1)) |
||
191 | urlon = lllon + (dx * (nx-1)) |
||
192 | if lllon > 180: |
||
193 | lllon -= 360.0 |
||
194 | if urlon > 180: |
||
195 | urlon -= 360.0 |
||
196 | |||
197 | if record['projtype'] == 'Polar Stereographic': |
||
198 | sc.char = 'STR ' |
||
199 | gemproj = 2.0 |
||
200 | if float(record['standard_parallel_1']) < 0.0: |
||
201 | ang1 = -90.0 |
||
202 | nsflag = 'S' |
||
203 | else: |
||
204 | ang1 = 90.0 |
||
205 | nsflag = 'N' |
||
206 | ang2 = float(record['central_meridian']) |
||
207 | ang3 = 0.0 |
||
208 | |||
209 | lat1 = float(record['lowerleftlat']) |
||
210 | lon1 = float(record['lowerleftlon']) |
||
211 | coords = createPolar(nsflag, ang2, lat1, lon1, dx, dy, unit, nx, ny) |
||
212 | lllat = coords[0] |
||
213 | lllon = coords[1] |
||
214 | urlat = coords[2] |
||
215 | urlon = coords[3] |
||
216 | |||
217 | if record['projtype'] == 'Lambert Conformal': |
||
218 | sc.char = 'LCC ' |
||
219 | gemproj = 2.0 |
||
220 | |||
221 | ang1 = float(skarr[7]) |
||
222 | ang2 = float(record['central_meridian']) |
||
223 | ang3 = float(skarr[8]) |
||
224 | if ang1 < 0.0: |
||
225 | nsflag = 'S' |
||
226 | else: |
||
227 | nsflag = 'N' |
||
228 | |||
229 | lat1 = float(record['lowerleftlat']) |
||
230 | lon1 = float(record['lowerleftlon']) |
||
231 | coords = createConic(nsflag, ang2, lat1, lon1, dx, dy, unit, nx, ny, ang1, ang3) |
||
232 | lllat = coords[0] |
||
233 | lllon = coords[1] |
||
234 | urlat = coords[2] |
||
235 | urlon = coords[3] |
||
236 | |||
237 | # Fill up the output array of floats |
||
238 | nav.append(gemproj) |
||
0 ignored issues
–
show
|
|||
239 | nav.append(sc.float) |
||
240 | nav.append(1.0) |
||
241 | nav.append(1.0) |
||
242 | nav.append(nx) |
||
243 | nav.append(ny) |
||
244 | nav.append(lllat) |
||
0 ignored issues
–
show
|
|||
245 | nav.append(lllon) |
||
0 ignored issues
–
show
|
|||
246 | nav.append(urlat) |
||
0 ignored issues
–
show
|
|||
247 | nav.append(urlon) |
||
0 ignored issues
–
show
|
|||
248 | nav.append(ang1) |
||
0 ignored issues
–
show
|
|||
249 | nav.append(ang2) |
||
0 ignored issues
–
show
|
|||
250 | nav.append(ang3) |
||
0 ignored issues
–
show
|
|||
251 | |||
252 | for i in range(13, int(self.arrayLen)): |
||
253 | nav.append(0.0) |
||
254 | return nav |
||
255 | |||
256 | def getAnlBlk(self): |
||
257 | anl = [] |
||
258 | # Type |
||
259 | anl.append(2.0) |
||
260 | # Delta |
||
261 | anl.append(1.0) |
||
262 | # Extend area |
||
263 | anl.append(0.0) |
||
264 | anl.append(0.0) |
||
265 | anl.append(0.0) |
||
266 | anl.append(0.0) |
||
267 | # Grid area |
||
268 | anl.append(-90.0) |
||
269 | anl.append(-180.0) |
||
270 | anl.append(90.0) |
||
271 | anl.append(180.0) |
||
272 | # Data area |
||
273 | anl.append(-90.0) |
||
274 | anl.append(-180.0) |
||
275 | anl.append(90.0) |
||
276 | anl.append(180.0) |
||
277 | for i in range(18, int(self.arrayLen)): |
||
278 | anl.append(0.0) |
||
279 | return anl |
||
280 | |||
281 | |||
282 | def getnavb(server, table, model, arrlen): |
||
283 | gnr = GridNavRetriever(server, table, model, arrlen) |
||
284 | return gnr.getNavBlk() |
||
285 | |||
286 | |||
287 | def getanlb(server, table, model, arrlen): |
||
288 | gnr = GridNavRetriever(server, table, model, arrlen) |
||
289 | return gnr.getAnlBlk() |
||
290 | |||
291 | |||
292 | # This is the standard boilerplate that runs this script as a main |
||
293 | if __name__ == '__main__': |
||
294 | # Run Test |
||
295 | srv = 'edex-cloud.unidata.ucar.edu' |
||
296 | tbl = 'grid_info' |
||
297 | mdl = 'NAM40' |
||
298 | navlen = '256' |
||
299 | print(getnavb(srv, tbl, mdl, navlen)) |
||
300 | anllen = '128' |
||
301 | print(getanlb(srv, tbl, mdl, anllen)) |
||
302 |