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
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
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
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
,
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
);
69 /*****************************************************************************
70 * Outputs the latitude and longitude of a grid point.
73 * *gridnav - a pointer to a filled gridnav structure. The gridnav
74 * structure should have been filled using one of the init
76 * column - the column in the grid to retrieve.
77 * row - the row in the grid to retrieve.
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
,
89 * Calculate the latitude and longitude for an input grid point location
94 switch (gridnav
->proj
.map_proj
)
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
;
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
;
112 *lat
= gridnav
->proj_transform
.parm5
* 90 -
113 2 * RAD_TO_DEG
* atan(gridnav
->proj_transform
.parm4
*
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;
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
;
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;
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
;
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
);
155 } /* end of switch on map projection type */
162 /*****************************************************************************
163 * Outputs grid point indices, given lat/lon location.
166 * *gridnav - a pointer to a filled gridnav structure. The gridnav
167 * structure should have been filled using one of the init
169 * lat - latitude for location
170 * lon - longitude for location
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
,
184 switch (gridnav
->proj
.map_proj
)
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
)) /
193 *row
= gridnav
->grid
.origin_row
+
194 ((Y
-gridnav
->proj_transform
.parm2
) / gridnav
->grid
.dy
);;
198 Rs
= (EARTH_RAD
/ gridnav
->proj_transform
.parm2
) *
199 sin(gridnav
->proj_transform
.parm1
) *
200 pow(tan((gridnav
->proj_transform
.parm5
* 90 - lat
) /
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
));
219 Rs
= EARTH_RAD
* sin((gridnav
->proj_transform
.parm5
* 90 - lat
)
221 ((1 + cos(gridnav
->proj_transform
.parm1
)) /
222 (1 + cos((gridnav
->proj_transform
.parm5
* 90 - lat
)
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
));
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
;
244 fprintf(stderr
,"GRID_from_latlon: Unsupported map projection type %d\n",
245 gridnav
->proj
.map_proj
);
251 } /* End of switch on map projection type */
256 int fill_proj_parms(GridNav
*gridnav
)
262 switch (gridnav
->proj
.map_proj
)
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
;
276 if (gridnav
->proj
.truelat1
>= 0)
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
));
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
) *
306 (tan((hemifactor
* 90 - gridnav
->grid
.lat_origin
) /
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
) /
321 tan(gridnav
->proj_transform
.parm1
/ 2),
322 gridnav
->proj_transform
.parm2
);
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
);
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
));
336 if (gridnav
->proj
.truelat1
> 0)
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
) /
360 gridnav
->proj_transform
.parm4
= MISSING
;
361 gridnav
->proj_transform
.parm5
= hemifactor
;
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
;
372 fprintf(stderr
,"GRID_init_mm5data: Invalid Projection\n");
378 /*****************************************************************************
379 * Rotates u and v components of wind, from being relative to earth, to
383 * *gridnav - a pointer to a filled gridnav structure. The gridnav
384 * structure should have been filled using one of the init
386 * lon - longitude for location
387 * u_earth - the u component of the wind in earth (north-relative)
389 * v_earth - the v component of the wind in earth (north-relative)
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
)
403 /* Calculate Speed and Direction from u,v */
404 switch (gridnav
->proj
.map_proj
)
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
);
426 "GRID_rotate_from_earth_coords: Invalid Projection\n");
428 } /* End of switch projection */
433 /*****************************************************************************
434 * Rotates u and v components of wind, from being relative to earth, to
438 * *gridnav - a pointer to a filled gridnav structure. The gridnav
439 * structure should have been filled using one of the init
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
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
;
457 /* Calculate Speed and Direction from u,v */
458 switch (gridnav
->proj
.map_proj
)
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
);
481 "GRID_rotate_to_earth_coords: Invalid Projection\n");
483 } /* End of switch projection */