merge standard release WRF/WPS V3.0.1.1 into wrffire
[wrffire.git] / wrfv2_fire / external / io_grib_share / gridnav.c
blob48f5f3e261f1eadbcbdc85eb590ad60f7e052a13
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include "gridnav.h"
6 #define MISSING -999
7 #define PI 3.1415927
8 #define RAD_TO_DEG 57.29577951
9 #define EARTH_RAD 6371.200
11 int fill_proj_parms(GridNav *gridnav);
13 /*****************************************************************************
14 * Fills up the gridnav structure using arguments input to the function
16 * input:
17 * central_lat - the central latitude of the projection, in degrees
18 * central_lon - the central longitude of the projection, in degrees
19 * projection - the projection number (defined by #define's in gridnav.h)
20 * truelat1 - the first "true" latitude. Only has an effect with
21 * polar stereographic and lambert projections
22 * truelat2 - the second true latitude. Only has an effect with lambert
23 * projection
24 * num_columns - number of columns in grid
25 * num_rows - number of rows in grid
26 * dx,dy - east-west (dx) and north-south (dy) distance between grid
27 * points, in degrees for latlon (i.e., cylindrical
28 * equidistant) projection, in km for all other projections
29 * (including mercator).
30 * lat_origin - latitude of grid point defined in origin_column, origin_row
31 * lon_origin - longitude of grid point defined in origin_column, origin_row
32 * origin_column - column for lat_origin, long_origin pair
33 * origin_row - row for lat_origin, long_origin pair
35 * output:
36 * gridnav - filled gridnav structure
38 *****************************************************************************/
40 int GRID_init(float central_lat, float central_lon, int projection,
41 float truelat1, float truelat2, int num_columns,
42 int num_rows, float dx, float dy, float lat_origin,
43 float lon_origin, float origin_column, float origin_row,
44 GridNav *gridnav)
47 int status = 1;
49 gridnav->proj.central_lon = central_lon;
50 gridnav->proj.central_lat = central_lat;
51 gridnav->proj.map_proj = projection;
52 gridnav->proj.truelat1 = truelat1;
53 gridnav->proj.truelat2 = truelat2;
55 gridnav->grid.num_columns = num_columns;
56 gridnav->grid.num_rows = num_rows;
57 gridnav->grid.dx = dx;
58 gridnav->grid.dy = dy;
59 gridnav->grid.lat_origin = lat_origin;
60 gridnav->grid.lon_origin = lon_origin;
61 gridnav->grid.origin_column = origin_column;
62 gridnav->grid.origin_row = origin_row;
64 fill_proj_parms(gridnav);
66 return status;
69 /*****************************************************************************
70 * Outputs the latitude and longitude of a grid point.
72 * input:
73 * *gridnav - a pointer to a filled gridnav structure. The gridnav
74 * structure should have been filled using one of the init
75 * functions.
76 * column - the column in the grid to retrieve.
77 * row - the row in the grid to retrieve.
79 * output:
80 * *lat - pointer to output latitude
81 * *lon - pointer to output longitude
83 *****************************************************************************/
85 int GRID_to_latlon(GridNav *gridnav, float column, float row, float *lat,
86 float *lon)
88 /*
89 * Calculate the latitude and longitude for an input grid point location
91 double X, Y, R;
92 int status = 1;
94 switch (gridnav->proj.map_proj)
96 case GRID_MERCATOR:
97 *lat = 2 * RAD_TO_DEG *
98 atan(exp((gridnav->proj_transform.parm2 + gridnav->grid.dx *
99 (row - gridnav->grid.origin_row)) /
100 gridnav->proj_transform.parm1 )) - 90;
101 *lon = gridnav->grid.lon_origin + RAD_TO_DEG * gridnav->grid.dx *
102 (column - gridnav->grid.origin_column) /
103 gridnav->proj_transform.parm1;
104 break;
106 case GRID_LAMCON:
107 X = (column - gridnav->grid.origin_column) * gridnav->grid.dx +
108 gridnav->proj_transform.parm6;
109 Y = (row - gridnav->grid.origin_row) * gridnav->grid.dy +
110 gridnav->proj_transform.parm3 + gridnav->proj_transform.parm7;
111 R = sqrt(X*X + Y*Y);
112 *lat = gridnav->proj_transform.parm5 * 90 -
113 2 * RAD_TO_DEG * atan(gridnav->proj_transform.parm4 *
114 pow(R, 1 /
115 gridnav->proj_transform.parm2));
116 *lon = gridnav->proj.central_lon +
117 RAD_TO_DEG / gridnav->proj_transform.parm2 *
118 atan(X / (gridnav->proj_transform.parm5 * -Y));
119 while (*lon > 180) *lon -= 360;
120 while (*lon <= -180) *lon += 360;
121 break;
123 case GRID_POLSTR:
124 X = (column - gridnav->grid.origin_column)*gridnav->grid.dx;
125 Y = (row - gridnav->grid.origin_row)*gridnav->grid.dy +
126 gridnav->proj_transform.parm3;
127 R = sqrt(X*X + Y*Y);
128 *lat = gridnav->proj_transform.parm5*90 - 2 * RAD_TO_DEG *
129 atan((gridnav->proj_transform.parm5*R/EARTH_RAD)/
130 (1+cos(gridnav->proj_transform.parm1)));
131 *lon = gridnav->grid.lon_origin + RAD_TO_DEG *
132 atan(X/(gridnav->proj_transform.parm5 * -Y));
133 while (*lon > 180) *lon -= 360;
134 while (*lon <= -180) *lon += 360;
135 break;
137 case GRID_LATLON:
138 *lat = (row - gridnav->grid.origin_row)*gridnav->grid.dy +
139 gridnav->grid.lat_origin;
140 *lon = (column - gridnav->grid.origin_column)*gridnav->grid.dx +
141 gridnav->grid.lon_origin;
142 break;
144 default:
146 * Unsupported map projection: set lat-lon to no-data values.
148 fprintf(stderr,"GRID_to_latlon: Unsupport map projection type %d\n",
149 gridnav->proj.map_proj);
150 *lon = -9999;
151 *lat = -9999;
152 status = 0;
153 break;
155 } /* end of switch on map projection type */
158 return status;
162 /*****************************************************************************
163 * Outputs grid point indices, given lat/lon location.
165 * input:
166 * *gridnav - a pointer to a filled gridnav structure. The gridnav
167 * structure should have been filled using one of the init
168 * functions.
169 * lat - latitude for location
170 * lon - longitude for location
171 * output:
172 * *column - pointer to value for column
173 * *row - pointer to value for row
175 *****************************************************************************/
177 int GRID_from_latlon(GridNav *gridnav, float lat, float lon, float *column,
178 float *row)
180 double X, Y, Rs;
181 double lat_rad;
182 int status = 1;
184 switch (gridnav->proj.map_proj)
186 case GRID_MERCATOR:
187 X = gridnav->proj_transform.parm1 *
188 ((lon - gridnav->grid.lon_origin) / RAD_TO_DEG);
189 *column = gridnav->grid.origin_column + X / gridnav->grid.dx;
190 lat_rad = lat / RAD_TO_DEG;
191 Y = gridnav->proj_transform.parm1 * log((1 + sin(lat_rad)) /
192 cos(lat_rad));
193 *row = gridnav->grid.origin_row +
194 ((Y-gridnav->proj_transform.parm2) / gridnav->grid.dy);;
195 break;
197 case GRID_LAMCON:
198 Rs = (EARTH_RAD / gridnav->proj_transform.parm2) *
199 sin(gridnav->proj_transform.parm1) *
200 pow(tan((gridnav->proj_transform.parm5 * 90 - lat) /
201 (2 * RAD_TO_DEG))/
202 tan(gridnav->proj_transform.parm1 / 2),
203 gridnav->proj_transform.parm2);
204 *row = gridnav->grid.origin_row -
205 (1 / gridnav->grid.dy) *
206 (gridnav->proj_transform.parm3 +
207 Rs * cos(gridnav->proj_transform.parm2*
208 (lon - gridnav->proj.central_lon) / RAD_TO_DEG)) -
209 gridnav->proj_transform.parm7 / gridnav->grid.dy;
210 *column = gridnav->grid.origin_column +
211 ( gridnav->proj_transform.parm5*
212 ((Rs / gridnav->grid.dx)*
213 sin(gridnav->proj_transform.parm2 *
214 (lon - gridnav->proj.central_lon) / RAD_TO_DEG) -
215 gridnav->proj_transform.parm6 / gridnav->grid.dx));
216 break;
218 case GRID_POLSTR:
219 Rs = EARTH_RAD * sin((gridnav->proj_transform.parm5 * 90 - lat)
220 / RAD_TO_DEG)*
221 ((1 + cos(gridnav->proj_transform.parm1)) /
222 (1 + cos((gridnav->proj_transform.parm5 * 90 - lat)
223 / RAD_TO_DEG)) );
224 *row = gridnav->grid.origin_row -
225 (1 / gridnav->grid.dy) *
226 (gridnav->proj_transform.parm3 +
227 Rs * cos((lon - gridnav->grid.lon_origin) / RAD_TO_DEG));
228 *column = gridnav->grid.origin_column +
229 gridnav->proj_transform.parm5 *
230 ((Rs / gridnav->grid.dx) *
231 sin((lon - gridnav->grid.lon_origin) / RAD_TO_DEG));
232 break;
234 case GRID_LATLON:
235 *row = ((lat - gridnav->grid.lat_origin) / gridnav->grid.dy) +
236 gridnav->grid.origin_row;
237 /* If lon is negative, make it positive */
238 while (lon < 0) lon += 360.;
239 *column = ((lon - gridnav->grid.lon_origin) / gridnav->grid.dx) +
240 gridnav->grid.origin_column;
241 break;
243 default:
244 fprintf(stderr,"GRID_from_latlon: Unsupported map projection type %d\n",
245 gridnav->proj.map_proj);
246 *column = -9999;
247 *row = -9999;
248 status = 0;
249 break;
251 } /* End of switch on map projection type */
253 return status;
256 int fill_proj_parms(GridNav *gridnav)
258 double orig_lat_rad;
259 double R_orig;
260 int hemifactor;
262 switch (gridnav->proj.map_proj)
264 case GRID_MERCATOR:
265 gridnav->proj_transform.parm1 =
266 EARTH_RAD * cos(gridnav->proj.truelat1 / RAD_TO_DEG);
267 orig_lat_rad = (gridnav->grid.lat_origin) / RAD_TO_DEG;
268 gridnav->proj_transform.parm2 =
269 gridnav->proj_transform.parm1 *
270 log((1. + sin(orig_lat_rad)) / cos(orig_lat_rad));
271 gridnav->proj_transform.parm3 = MISSING;
272 gridnav->proj_transform.parm4 = MISSING;
273 gridnav->proj_transform.parm5 = MISSING;
274 break;
275 case GRID_LAMCON:
276 if (gridnav->proj.truelat1 >= 0)
278 hemifactor = 1;
280 else
282 hemifactor = -1;
284 /* This is Psi1 in MM5 speak */
285 gridnav->proj_transform.parm1 =
286 hemifactor*(PI/2 - fabs(gridnav->proj.truelat1) / RAD_TO_DEG);
287 /* This is Kappa in MM5 speak */
288 if (fabs(gridnav->proj.truelat1 - gridnav->proj.truelat2) < .01)
290 gridnav->proj_transform.parm2 =
291 fabs(sin(gridnav->proj.truelat1 / RAD_TO_DEG));
293 else
295 gridnav->proj_transform.parm2 =
296 (log10(cos(gridnav->proj.truelat1 / RAD_TO_DEG)) -
297 log10(cos(gridnav->proj.truelat2 / RAD_TO_DEG))) /
298 (log10(tan((45 - fabs(gridnav->proj.truelat1) / 2) / RAD_TO_DEG)) -
299 log10(tan((45 - fabs(gridnav->proj.truelat2) / 2) / RAD_TO_DEG)));
301 /* This is Yc in MM5 speak */
302 gridnav->proj_transform.parm3 =
303 -EARTH_RAD/gridnav->proj_transform.parm2 *
304 sin(gridnav->proj_transform.parm1) *
305 pow(
306 (tan((hemifactor * 90 - gridnav->grid.lat_origin) /
307 (RAD_TO_DEG * 2)) /
308 tan(gridnav->proj_transform.parm1 / 2)),
309 gridnav->proj_transform.parm2);
310 gridnav->proj_transform.parm4 =
311 tan(gridnav->proj_transform.parm1 / 2)*
312 pow(hemifactor * (gridnav->proj_transform.parm2)/
313 (EARTH_RAD * sin(gridnav->proj_transform.parm1)),
314 1 / gridnav->proj_transform.parm2);
315 gridnav->proj_transform.parm5 = hemifactor;
316 R_orig = (EARTH_RAD / gridnav->proj_transform.parm2) *
317 sin(gridnav->proj_transform.parm1) *
318 pow(tan((gridnav->proj_transform.parm5 * 90 -
319 gridnav->grid.lat_origin) /
320 (2 * RAD_TO_DEG))/
321 tan(gridnav->proj_transform.parm1 / 2),
322 gridnav->proj_transform.parm2);
323 /* X origin */
324 gridnav->proj_transform.parm6 =
325 R_orig * sin(gridnav->proj_transform.parm2 *
326 (gridnav->grid.lon_origin -
327 gridnav->proj.central_lon) / RAD_TO_DEG);
329 /* Y origin */
330 gridnav->proj_transform.parm7 = -(gridnav->proj_transform.parm3 +
331 R_orig * cos(gridnav->proj_transform.parm2 *
332 (gridnav->grid.lon_origin -
333 gridnav->proj.central_lon) / RAD_TO_DEG));
334 break;
335 case GRID_POLSTR:
336 if (gridnav->proj.truelat1 > 0)
338 hemifactor = 1;
340 else
342 hemifactor = -1;
345 /* This is Psi1 in MM5 speak */
346 gridnav->proj_transform.parm1 =
347 hemifactor * (PI/2 - fabs(gridnav->proj.truelat1) / RAD_TO_DEG);
348 gridnav->proj_transform.parm2 =
349 (1+log10(cos(gridnav->proj.truelat1 / RAD_TO_DEG))) /
350 ( -log10(tan(45 / RAD_TO_DEG - hemifactor *
351 gridnav->proj.truelat1 /
352 (2 * RAD_TO_DEG) )) );
353 /* This is Yc in MM5 speak */
354 gridnav->proj_transform.parm3 =
355 -EARTH_RAD * sin((hemifactor * 90 -
356 gridnav->grid.lat_origin) / RAD_TO_DEG)*
357 ( (1 + cos(gridnav->proj_transform.parm1))/
358 (1 + cos((hemifactor*90 - gridnav->grid.lat_origin) /
359 RAD_TO_DEG)) );
360 gridnav->proj_transform.parm4 = MISSING;
361 gridnav->proj_transform.parm5 = hemifactor;
362 break;
363 case GRID_LATLON:
364 gridnav->proj_transform.parm1 = MISSING;
365 gridnav->proj_transform.parm2 = MISSING;
366 gridnav->proj_transform.parm3 = MISSING;
367 gridnav->proj_transform.parm4 = MISSING;
368 gridnav->proj_transform.parm5 = MISSING;
369 break;
371 default:
372 fprintf(stderr,"GRID_init_mm5data: Invalid Projection\n");
373 return 0;
375 return 1;
378 /*****************************************************************************
379 * Rotates u and v components of wind, from being relative to earth, to
380 * relative to grid.
382 * input:
383 * *gridnav - a pointer to a filled gridnav structure. The gridnav
384 * structure should have been filled using one of the init
385 * functions.
386 * lon - longitude for location
387 * u_earth - the u component of the wind in earth (north-relative)
388 * coordinates.
389 * v_earth - the v component of the wind in earth (north-relative)
390 * coordinates.
391 * output:
392 * *u_grid - pointer to value for u in grid coordinates
393 * *v_grid - pointer to value for v in grid coordinates
395 *****************************************************************************/
397 int GRID_rotate_from_earth_coords(GridNav *gridnav, float lon, float u_earth,
398 float v_earth, float *u_grid, float *v_grid)
400 float speed, dir;
401 float dir_grid;
403 /* Calculate Speed and Direction from u,v */
404 switch (gridnav->proj.map_proj)
406 case GRID_MERCATOR:
407 *u_grid = u_earth;
408 *v_grid = v_earth;
409 break;
410 case GRID_POLSTR: case GRID_LAMCON:
411 speed = sqrt(u_earth * u_earth + v_earth * v_earth);
412 dir = RAD_TO_DEG * atan2(-u_earth,-v_earth);
413 while (dir >= 360) dir -= 360;
414 while (dir < 0) dir += 360;
416 dir_grid = dir - (lon - gridnav->proj.central_lon) *
417 gridnav->proj_transform.parm2;
418 while (dir_grid >= 360) dir_grid -= 360;
419 while (dir_grid < 0) dir_grid += 360;
421 *u_grid = -1. * speed * sin(dir_grid / RAD_TO_DEG);
422 *v_grid = -1. * speed * cos(dir_grid / RAD_TO_DEG);
423 break;
424 default:
425 fprintf(stderr,
426 "GRID_rotate_from_earth_coords: Invalid Projection\n");
427 return 0;
428 } /* End of switch projection */
430 return 1;
433 /*****************************************************************************
434 * Rotates u and v components of wind, from being relative to earth, to
435 * relative to grid.
437 * input:
438 * *gridnav - a pointer to a filled gridnav structure. The gridnav
439 * structure should have been filled using one of the init
440 * functions.
441 * lon - longitude for location
442 * u_grid - the u component of the wind in grid coordinates
443 * v_grid - the v component of the wind in grid coordinates
445 * output:
446 * *u_earth - pointer to value for u in earth-relative coordinates
447 * *v_grid - pointer to value for v in earth-relative coordinates
449 *****************************************************************************/
451 int GRID_rotate_to_earth_coords(GridNav *gridnav, float lon, float u_grid,
452 float v_grid, float *u_earth, float *v_earth)
454 float speed, dir_grid;
455 float dir_earth;
457 /* Calculate Speed and Direction from u,v */
458 switch (gridnav->proj.map_proj)
460 case GRID_MERCATOR:
461 *u_earth = u_grid;
462 *v_earth = v_grid;
463 break;
464 case GRID_POLSTR: case GRID_LAMCON:
465 speed = sqrt(u_grid * u_grid + v_grid * v_grid);
466 dir_grid = RAD_TO_DEG * atan2(-u_grid, -v_grid);
467 while (dir_grid >= 360) dir_grid -= 360;
468 while (dir_grid < 0) dir_grid += 360;
470 dir_earth = dir_grid + (lon - gridnav->proj.central_lon) *
471 gridnav->proj_transform.parm2;
473 while (dir_earth >= 360) dir_earth -= 360;
474 while (dir_earth < 0) dir_earth += 360;
476 *u_earth = -1. * speed * sin(dir_earth / RAD_TO_DEG);
477 *v_earth = -1. * speed * cos(dir_earth / RAD_TO_DEG);
478 break;
479 default:
480 fprintf(stderr,
481 "GRID_rotate_to_earth_coords: Invalid Projection\n");
482 return 0;
483 } /* End of switch projection */
484 return 1;