/* proj.c */ /* * Vis5D system for visualizing five dimensional gridded data sets. * Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul, * Dave Santek, and Andre Battaiola. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * As a special exception to the terms of the GNU General Public * License, you are permitted to link Vis5D with (and distribute the * resulting source and executables) the LUI library (copyright by * Stellar Computer Inc. and licensed for distribution with Vis5D), * the McIDAS library, and/or the NetCDF library, where those * libraries are governed by the terms of their own licenses. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "../config.h" /* * Map projection / coordinate transformation */ /* * Map projection parameters: * * IF Projection==PROJ_GENERIC THEN * projargs[0] = northbound * projargs[1] = westbound * projargs[2] = row increment * projargs[3] = column increment * ELSE IF Projection==PROJ_LINEAR THEN * projargs[0] = northbound in degrees * projargs[1] = westbound in deg. * projargs[2] = row increment in deg. * projargs[3] = column increment in deg. * ELSE IF Projection==PROJ_LAMBERT THEN * projargs[0] = Lat1 * projargs[1] = Lat2 * projargs[2] = PoleRow * projargs[3] = PoleColumn * projargs[4] = Central Long. * projargs[5] = Column Increment in km * ELSE IF Projection==PROJ_STEREO THEN * projargs[0] = Central Latitude * projargs[1] = Central Longitude * projargs[2] = Central Row * projargs[3] = Central Column * projargs[4] = Column increment in km * ELSE IF Projection==PROJ_ROTATED THEN * projargs[0] = Latitude on rotated globe of grid row 0 * projargs[1] = Longitude on rotated globe of grid column 0 * projargs[2] = degs of latitude on rotated globe between grid rows * projargs[3] = degs of longitude on rotated globe between grid cols * projargs[4] = Earth latitude of (0, 0) on rotated globe * projargs[5] = Earth longitude of (0, 0) on rotated globe * projargs[6] = Clockwise rotation of rotated globe in degrees * ELSE IF Projection==PROJ_MERCATOR THEN * projargs[0] = Central Latitude * projargs[1] = Central Longitude * projargs[2] = Row Increment in Kilometers * projargs[3] = Column Increment in Kilometers * ELSE IF Projection==PROJ_EPA THEN * projargs[ (R*nc+C) * 2 + 0 ] = latitude of grid point (R,C) * projargs[ (R*nc+C) * 2 + 1 ] = longitude of grid point (R,C) * ENDIF */ /* * Vertical coordinate system parameters: * * IF VertSystem==VERT_GENERIC THEN * vertargs[0] = Bottom bound * vertargs[1] = level increment * ELSE IF VertSystem==VERT_EQUAL_KM THEN * vertargs[0] = bottom bound in km * vertargs[1] = level increment in km * ELSE IF VertSystem==VERT_UNEQUAL_KM THEN * vertargs[n] = height of grid level n. n is in [0..Nl-1] * ELSE IF VertSystem==VERT_UNEQUAL_MB THEN * vertargs[n] = height of grid level n. n is in [0..Nl-1] * ELSE IF VertSystem==VERT_EPA THEN * vertargs[n] = height of grid level n. n is in [0..Nl-1] * ENDIF */ #include #include #include "grid_i.h" #include "proj_i.h" #include "v5d.h" #ifndef M_PI # define M_PI 3.1415926 #endif #define DEG2RAD (M_PI/180.0) #define RAD2DEG (180.0/M_PI) #define RADIUS 6371.23 /* Pete rotated sphere to Earth */ static void pandg_back( float *lat, float *lon, float a, float b, float r ) { float pr, gr, pm, gm; /* NOTE - longitude sign switches - b too! */ pr = DEG2RAD * *lat; gr = -DEG2RAD * *lon; pm = asin( cos(pr) * cos (gr) ); gm = atan2(cos(pr) * sin (gr), -sin(pr) ); *lat = RAD2DEG * asin( sin(a) * sin(pm) - cos(a) * cos(pm) * cos (gm - r) ); *lon = -RAD2DEG * (-b + atan2(cos(pm) * sin (gm - r), sin(a) * cos(pm) * cos (gm - r) + cos(a) * sin(pm))); } /* Earth to Pete rotated sphere */ static void pandg_for( float *lat, float *lon, float a, float b, float r ) { float p1, g1, p, g; /* NOTE - longitude sign switches - b too! */ p1 = DEG2RAD * *lat; g1 = -DEG2RAD * *lon; p = asin( sin(a) * sin(p1) + cos(a) * cos(p1) * cos (g1 + b) ); g = r + atan2(cos(p1) * sin (g1 + b), sin(a) * cos(p1) * cos (g1 + b) - cos(a) * sin(p1) ); *lat = RAD2DEG * asin( -cos(p) * cos (g) ); *lon = -RAD2DEG * atan2(cos(p) * sin (g), sin(p) ); } /**********************************************************************/ /***** Map Projection *****/ /**********************************************************************/ /* * Convert a (row,column) grid coordinate to a (lat,lon) coordinate * according to the projection info. * Return: 1 = ok, 0 = error (out of bounds) */ int rowcol_to_latlon_i( float row, float col, float *lat, float *lon, struct projection *proj ) { switch (proj->Kind) { case PROJ_GENERIC: /* Generic rectilinear */ case PROJ_LINEAR: /* lat/lon rectilinear */ case PROJ_CYLINDRICAL: case PROJ_SPHERICAL: /* FALL-THROUGH */ #define NorthBound proj->Args[0] #define WestBound proj->Args[1] #define RowInc proj->Args[2] #define ColInc proj->Args[3] *lat = NorthBound - row * RowInc; *lon = WestBound - col * ColInc; #undef NorthBound #undef WestBound #undef RowInc #undef ColInc break; case PROJ_LAMBERT: /* Lambert conformal */ #define PoleRow proj->Args[2] #define PoleCol proj->Args[3] #define CentralLon proj->Args[4] #define Hemisphere proj->AuxArgs[0] #define ConeFactor proj->AuxArgs[1] #define Cone proj->AuxArgs[2] { float xldif, xedif, xrlon, radius; xldif = Hemisphere * (row-PoleRow) / ConeFactor; xedif = (PoleCol-col) / ConeFactor; if (xldif==0.0 && xedif==0.0) xrlon = 0.0; else xrlon = atan2( xedif, xldif ); *lon = xrlon / Cone * RAD2DEG + CentralLon; if (*lon > 180.0) *lon -= 360.0; radius = sqrt( xldif*xldif + xedif*xedif ); if (radius < 0.0001) *lat = 90.0 * Hemisphere; /* +/-90 */ else *lat = Hemisphere * (90.0 - 2.0*atan(exp(log(radius)/Cone))*RAD2DEG); } #undef PoleRow #undef PoleCol #undef CentralLon #undef Hemisphere #undef ConeFactor #undef Cone break; case PROJ_MERCATOR: /* Mercator */ #define CentralLat proj->Args[0] #define CentralLon proj->Args[1] #define RowIncKm proj->Args[2] #define ColIncKm proj->Args[3] { float YC, alpha, ic, jc; YC = RADIUS * log((1.0 + sin(DEG2RAD*CentralLat))/cos(DEG2RAD*CentralLat)); ic = (proj->Nr-1)/2.0; jc = (proj->Nc-1)/2.0; alpha = ( (ic-row) * RowIncKm + YC) / RADIUS; *lat = 2 * RAD2DEG * atan( exp(alpha) ) - 90.0; *lon = CentralLon - RAD2DEG * (col-jc) * ColIncKm / RADIUS; } #undef CentralLat #undef CentralLon #undef RowIncKm #undef ColIncKm break; case PROJ_STEREO: /* Polar Stereographic */ #define CentralLat proj->Args[0] #define CentralLon proj->Args[1] #define CentralRow proj->Args[2] #define CentralCol proj->Args[3] #define ColInc proj->Args[4] { float CosCentralLat = cos( CentralLat * DEG2RAD ); float SinCentralLat = sin( CentralLat * DEG2RAD ); float Scale = (2.0 * RADIUS / ColInc); float InvScale = 1.0 / Scale; float xrow, xcol, rho, c, cc, sc; xrow = CentralRow - row; xcol = CentralCol - col; rho = xrow*xrow + xcol*xcol; if (rho<1.0e-20) { *lat = CentralLat; *lon = CentralLon; } else { rho = sqrt( rho ); c = 2.0 * atan( rho * InvScale); cc = cos(c); sc = sin(c); *lat = RAD2DEG * asin( cc*SinCentralLat + xrow*sc*CosCentralLat / rho ); *lon = CentralLon + RAD2DEG * atan2( xcol * sc, (rho * CosCentralLat * cc - xrow * SinCentralLat * sc) ); if (*lon < -180.0) *lon += 360.0; else if (*lon > 180.0) *lon -= 360.0; } } #undef CentralLat #undef CentralLon #undef CentralRow #undef CentralCol #undef ColInc break; case PROJ_ROTATED: /* Rotated */ #define NorthBound proj->Args[0] #define SouthBound (proj->Args[0] - proj->Args[2]*(proj->Nr-1)) #define WestBound proj->Args[1] #define EastBound (proj->Args[1] - proj->Args[3]*(proj->Nc-1)) #define CentralLat proj->Args[4] #define CentralLon proj->Args[5] #define Rotation (proj->Args[6] * DEG2RAD) *lat = NorthBound - row * (NorthBound-SouthBound) / (float) (proj->Nr-1); *lon = WestBound - col * (WestBound-EastBound) / (float) (proj->Nc-1); pandg_back(lat, lon, CentralLat, CentralLon, Rotation); #undef NorthBound #undef SouthBound #undef WestBound #undef EastBound #undef CentralLat #undef CentralLon #undef Rotation break; case PROJ_EPA: /* EPA projection */ #define Latitude(ROW,COL) proj->Args[ ((ROW)*nc+(COL)) * 2 + 0 ] #define Longitude(ROW,COL) proj->Args[ ((ROW)*nc+(COL)) * 2 + 1 ] { /* TODO: interpolate between grid points */ int nr, nc; nr = proj->Nr; nc = proj->Nc; *lat = Latitude( (int) row, (int) col ); *lon = Longitude( (int) row, (int) col ); } #undef Latitude #undef Longitude break; default: printf("Error in rowcol_to_latlon_i: bad projection: %d\n", (int)proj ); break; } return 1; } /* * Find the rectangle in the EPA Lambert conformal grid which contains the * given lat/lon coordinate. * Input: lat, lon - coordinate of interest. * proj - the projection * Output: n, m - row, column of upper-left corner of rectangle. * s, t - coefficients for interpolation. * Return: 1 = found a containing rectangle * 0 = didn't find a rectangle. */ static int find_rectangle( float lat, float lon, int *n, int *m, float *s, float *t, struct projection *proj ) { #define Latitude(ROW,COL) proj->Args[ ((ROW)*nc+(COL)) * 2 + 0 ] #define Longitude(ROW,COL) proj->Args[ ((ROW)*nc+(COL)) * 2 + 1 ] int i, j; float px, py, dx, dy; int nr, nc; /* First two elements of projargs indicate no. of rows and columns */ nr = proj->Nr; nc = proj->Nc; for (i=0;iKind) { case PROJ_GENERIC: /* Generic rectilinear */ case PROJ_LINEAR: /* lat/lon rectilinear */ case PROJ_CYLINDRICAL: case PROJ_SPHERICAL: #define NorthBound proj->Args[0] #define WestBound proj->Args[1] #define RowInc proj->Args[2] #define ColInc proj->Args[3] *row = (NorthBound - lat) / RowInc; *col = (WestBound - lon) / ColInc; if (*row<0.0 || *row>(proj->Nr-1) || *col<0.0 || *col>(proj->Nc-1)) { return 0; } #undef NorthBound #undef WestBound #undef RowInc #undef ColInc break; case PROJ_LAMBERT: /* Lambert conformal */ #define PoleRow proj->Args[2] #define PoleCol proj->Args[3] #define CentralLon proj->Args[4] #define Hemisphere proj->AuxArgs[0] #define ConeFactor proj->AuxArgs[1] #define Cone proj->AuxArgs[2] { float rlon, rlat, r; rlon = lon - CentralLon; rlon = rlon * Cone * DEG2RAD; if (lat<-85.0) { /* infinity */ r = 10000.0; } else { rlat = (90.0 - Hemisphere * lat) * DEG2RAD * 0.5; r = ConeFactor * pow( tan(rlat), Cone ); } *row = PoleRow + r * cos(rlon); *col = PoleCol - r * sin(rlon); if ( *row<0.0 || *row>(proj->Nr-1) || *col<0.0 || *col>(proj->Nc-1)) { return 0; } } #undef PoleRow #undef PoleCol #undef CentralLon #undef Hemisphere #undef ConeFactor #undef Cone break; case PROJ_MERCATOR: /* Mercator */ #define CentralLat proj->Args[0] #define CentralLon proj->Args[1] #define RowIncKm proj->Args[2] #define ColIncKm proj->Args[3] { float X, Y, ic, jc, YC; ic = (proj->Nr-1)/2.0; jc = (proj->Nc-1) / 2.0; YC = RADIUS * log((1.0 + sin(DEG2RAD*CentralLat))/cos(DEG2RAD*CentralLat)); X = RADIUS * (lon-CentralLon) / RAD2DEG; Y = RADIUS * log( (1+sin(DEG2RAD*lat))/cos(DEG2RAD*lat) ); *row = ic - (Y - YC)/RowIncKm; *col = jc - X/ColIncKm; } #undef CentralLat #undef CentralLon #undef RowIncKm #undef ColIncKm break; case PROJ_STEREO: /* Polar Stereographic */ #define CentralLat proj->Args[0] #define CentralLon proj->Args[1] #define CentralRow proj->Args[2] #define CentralCol proj->Args[3] #define ColInc proj->Args[4] { float CosCentralLat = cos( CentralLat * DEG2RAD ); float SinCentralLat = sin( CentralLat * DEG2RAD ); float Scale = (2.0 * RADIUS / ColInc); float rlat, rlon, clon, clat, k; rlat = DEG2RAD * lat; rlon = DEG2RAD * (CentralLon - lon); clon = cos(rlon); clat = cos(rlat); k = Scale / (1.0 + SinCentralLat*sin(rlat) + CosCentralLat*clat*clon); *col = CentralCol + k * clat * sin(rlon); *row = proj->Nr - CentralRow - k * (CosCentralLat * sin(rlat) - SinCentralLat * clat * clon); if ( *row<0.0 || *row>(proj->Nr-1) || *col<0.0 || *col>(proj->Nc-1)) { return 0; } } #undef CentralLat #undef CentralLon #undef CentralRow #undef CentralCol #undef ColInc #undef Nr break; case PROJ_ROTATED: #define NorthBound proj->Args[0] #define SouthBound (proj->Args[0] - proj->Args[2]*(proj->Nr-1)) #define WestBound proj->Args[1] #define EastBound (proj->Args[1] - proj->Args[3]*(proj->Nc-1)) #define CentralLat proj->Args[4] #define CentralLon proj->Args[5] #define Rotation (proj->Args[6] * DEG2RAD) lat0 = lat; lon0 = lon; pandg_for( &lat0, &lon0, CentralLat, CentralLon, Rotation ); *col = (WestBound-lon0)/proj->Args[3]; *row = (NorthBound-lat0) / proj->Args[2]; if ( *row<0.0 || *row>(proj->Nr-1) || *col<0.0 || *col>(proj->Nc-1)) { return 0; } #undef NorthBound #undef SouthBound #undef WestBound #undef EastBound #undef CentralLat #undef CentralLon #undef Rotation break; case PROJ_EPA: /* EPA projection */ { int r, c; float alpha, beta; if (find_rectangle( lat, lon, &r, &c, &alpha, &beta, proj )) { *row = (float) r + alpha; *col = (float) c + beta; } else { return 0; } } break; default: printf("Error in latlon_to_rowcol_i: bad projection: %d\n", (int)proj ); break; } return 1; } /* * Return a floating point value indicitive of the resolution of a * map projection. Low values indicate high-resolution, high values * indicate low-resolution. Typically, this value is the product of * the row and column increments. * Input: proj - the map projection * Return: resolution value */ float proj_resolution( struct projection *proj ) { float res; switch (proj->Kind) { case PROJ_GENERIC: /* Generic rectilinear */ case PROJ_LINEAR: /* lat/lon rectilinear */ case PROJ_CYLINDRICAL: case PROJ_SPHERICAL: #define RowInc proj->Args[2] #define ColInc proj->Args[3] res = RowInc * ColInc; #undef RowInc #undef ColInc break; case PROJ_MERCATOR: #define RowInc proj->Args[2]/111.75 #define ColInc proj->Args[3]/111.75 res = RowInc * ColInc; #undef RowInc #undef ColInc break; case PROJ_ROTATED: #define RowInc proj->Args[2] #define ColInc proj->Args[3] res = RowInc * ColInc; #undef RowInc #undef ColInc break; case PROJ_LAMBERT: /* Lambert conformal */ case PROJ_STEREO: /* Polar Stereographic */ case PROJ_EPA: /* EPA projection */ /* measure distance between a few grid points in center of grid */ { float midrow = proj->Nr / 2.0; float midcol = proj->Nc / 2.0; float lat0, lon0, lat1, lon1, dlat, dlon; rowcol_to_latlon_i( midrow, midcol, &lat0, &lon0, proj ); rowcol_to_latlon_i( midrow+1.0, midcol+1.0, &lat1, &lon1, proj ); dlat = lat1-lat0; dlon = lon1-lon0; res = sqrt( dlat*dlat + dlon*dlon ); } break; default: printf("Error in proj_resolution: bad projection: %d\n", (int)proj ); break; } /* Return absolute value */ if (res<0.0) { return -res; } else { return res; } } /**********************************************************************/ /***** Vertical Coordinate System *****/ /**********************************************************************/ /* * Perform a binary search of the array for the value and return * the index as a float. Return a -1.0 if the value is out of bounds. */ static float binary_search( float value, float array[], int size ) { int low, high, mid; float x; if (value==array[0]) { return 0.0; } else if (valuearray[size-1]) { return -1.0; } else if (value==array[size-1]) { return (float) (size-1); } else { /* do a binary search of array[] for value */ low = 0; high = size-1; while (low<=high) { mid = (low+high)/2; if (valuearray[mid]) low = mid + 1; else return (float) mid; } /* interpolate a value between high and low */ x = (value-array[high]) / (array[low]-array[high]); return high * (1.0-x) + low * x; } } /* * Convert a grid level into a height value. * Input: level - the grid level * vcs - the vertical coordinate system * topo_elev - elevation of topography at the implicit lat/lon * (only used for EPA projection) * Output: height - the height value */ int level_to_height( float level, float *height, struct vcs *vcs, float topo_elev ) { switch (vcs->Kind) { case VERT_GENERIC: *height = vcs->Args[0] + level * vcs->Args[1]; break; case VERT_EQUAL_KM: *height = vcs->Args[0] + level * vcs->Args[1]; break; case VERT_UNEQUAL_MB: /* Unequally spaced mb */ case VERT_UNEQUAL_KM: /* Unequally spaced km */ { int ilevel, ilevel_next; float alpha; ilevel = ((int) level < vcs->Nl) ? ((int) level) : (vcs->Nl-1); ilevel_next = (ilevel+1 < vcs->Nl) ? (ilevel+1) : (vcs->Nl-1); alpha = level - ilevel; *height = vcs->Args[ilevel] * (1.0-alpha) + vcs->Args[ilevel_next] * alpha; } break; case VERT_EPA: #define Sigma(K) vcs->Args[(K)] { float psurf, s, p; psurf = 1012.5 * exp( -topo_elev / 7.2 ); s = Sigma( (int) level ); p = 100.0 + s * (psurf - 100.0); *height = -7.2 * log( p / 1012.5 ); } #undef Sigma break; default: /* error */ printf("Error in level_to_height, can't handle vcs kind %d\n", vcs->Kind); } return 1; } /* * Convert a height to a grid level. * Input: height - the input height value * vcs - the vertical coordinate system * topo_elev - elevation of the topograph at the lat/lon of interest * nl - number of levels in input coordinate system grid * Output: level - the output grid level * Return: 1 = ok, 0 = error (height outside of domain) */ int height_to_level( float height, float *level, struct vcs *vcs, float topo_elev ) { float lev; switch (vcs->Kind) { case VERT_GENERIC: case VERT_EQUAL_KM: lev = (height - vcs->Args[0]) / vcs->Args[1]; break; case VERT_UNEQUAL_MB: /* Unequally spaced mb */ case VERT_UNEQUAL_KM: /* Unequally spaced km */ /* lev will be -1 if out of bounds */ lev = binary_search( height, vcs->Args, vcs->Nl ); break; case VERT_EPA: #define Sigma(K) vcs->Args[(K)] { float hgt[MAXLEVELS]; int ilev; float psurf, s, p; /* compute hgt[ilev] */ psurf = 1012.5 * exp( -topo_elev / 7.2 ); for (ilev=0;ilevNl;ilev++) { s = Sigma( ilev ); p = 100.0 + s * (psurf - 100.0); hgt[ilev] = -7.2 * log( p / 1012.5 ); } /* search hgt[] for height */ /* lev will be -1 if out of bounds */ lev = binary_search( height, hgt, vcs->Nl ); #undef Sigma } break; default: /* error */ printf("Error in height_to_level, can't handle vcs kind %d\n", vcs->Kind); } /* boundary checks: */ if ((lev < vcs->LowLev) || (lev > (vcs->Nl-1))) { return 0; } else { *level = lev; return 1; } }