int EXP_LVL9 CSmrcatX (Const struct cs_Mrcat_ *mrcat,int cnt,Const double pnts [][3]) { int ii; int rtn_val; double test_val; double dummy; rtn_val = cs_CNVRT_OK; /* All X values are within the domain of the function. However, in the cartesian frame of reference, an absolute value of Y greater than yy_max is outside of the domain. */ for (ii = 0;ii < cnt && rtn_val == cs_CNVRT_OK;ii++) { if (mrcat->quad == 0) { test_val = pnts [ii][YY] - mrcat->y_off; } else { CS_quadI (&test_val,&dummy,pnts [ii],mrcat->x_off, mrcat->y_off, mrcat->quad); } if (fabs (test_val) > mrcat->yy_max) { rtn_val = cs_CNVRT_DOMN; break; } } return (rtn_val); }
static int CSlmtanXP ( Const struct cs_Lmtan_ *lmtan,Const double xy [2],double *gamma) { double my_xx, my_yy; double R; /* Convert to polar coordinates. */ if (lmtan->quad == 0) { my_xx = xy [XX] - lmtan->x_off; my_yy = xy [YY] - lmtan->y_off; } else { CS_quadI (&my_xx,&my_yy,xy,lmtan->x_off,lmtan->y_off,lmtan->quad); } my_yy -= lmtan->R0; R = sqrt (my_xx * my_xx + my_yy * my_yy); if (R < lmtan->one_mm || R > lmtan->max_R) { return (cs_CNVRT_DOMN); } *gamma = atan2 (my_xx,-my_yy); if (fabs (*gamma) > lmtan->max_Gamma) { return (cs_CNVRT_DOMN); } return (cs_CNVRT_OK); }
int EXP_LVL9 CStacylX (Const struct cs_Tacyl_ *tacyl,int cnt,Const double pnts [][3]) { int ii; int rtn_val; double xx, yy; rtn_val = cs_CNVRT_OK; /* Check that all X's and Y's are within the basic range. No special checks are required for lines and/or regions. */ for (ii = 0; ii < cnt; ii++) { if (tacyl->quad == 0) { xx = pnts [ii][XX] - tacyl->x_off; yy = pnts [ii][YY] - tacyl->y_off; } else { CS_quadI (&xx,&yy,pnts [ii],tacyl->x_off,tacyl->y_off,tacyl->quad); } if (fabs (xx) > tacyl->max_xx || fabs (yy) > tacyl->max_yy) { rtn_val = cs_CNVRT_DOMN; break; } } return (rtn_val); }
int EXP_LVL9 CSazmedX (Const struct cs_Azmed_ *azmed,int cnt,Const double pnts [][3]) { int ii; int rtn_val; double dd; double xx, yy; rtn_val = cs_CNVRT_OK; /* Simply check that all X's and Y's are within the maximum radius of the projection. No special checks are required for lines and/or regions. */ for (ii = 0;ii < cnt;ii++) { if (azmed->quad == 0) { xx = pnts [ii][XX] - azmed->x_off; yy = pnts [ii][YY] - azmed->y_off; } else { CS_quadI (&xx,&yy,pnts [ii],azmed->x_off, azmed->y_off, azmed->quad); } dd = sqrt (xx * xx + yy * yy); if (dd > azmed->max_rho) { rtn_val = cs_CNVRT_DOMN; break; } } return (rtn_val); }
int EXP_LVL9 CSazmedI (Const struct cs_Azmed_ *azmed,double ll [2],Const double xy [2]) { extern char csErrnam []; extern double cs_Radian; /* 57.29577..... */ extern double cs_Zero; /* 0.0 */ extern double cs_Half; /* 0.5 */ extern double cs_One; /* 1.0 */ extern double cs_Mone; /*-1.0 */ extern double cs_Three; /* 3.0 */ extern double cs_Six; /* 6.0 */ extern double cs_AnglTest; /* 0.001 seconds of arc, in radians. */ extern double cs_NPTest; /* 0.001 seconds of arc short of the north pole, in radians. */ int rtn_val; int itr_cnt; double x = 0.0; /* initialized to keep the gcc compiler happy */ double y = 0.0; /* initialized to keep the gcc compiler happy */ double c; double lat = 0.0; /* initialized to keep the gcc compiler happy */ double del_lng; double rho; double sin_c; double cos_c; double Az; double sin_Az; double cos_Az; double cos_psi; double last_lat; double A; double B; double D; double D_third; double D_fourth; double E; double E_sq; double E_third; double sin_E; double F; double M; double psi; double tmp1; double tmp2; double my_xy [2]; rtn_val = cs_CNVRT_NRML; /* There are two formulae, one for the sphere and one for the ellipsoid. If the ecentricity of the datum in use is 0.0 exactly, we shall use the spherical formulae. The calculations which immediately follow are required by both cases. First, we remove the false origin, return to the normal X/Y quadrant, and unrotate the coordinates. */ if (azmed->quad == 0) { my_xy [XX] = xy [XX] - azmed->x_off; my_xy [YY] = xy [YY] - azmed->y_off; } else { CS_quadI (&my_xy [XX],&my_xy [YY],xy,azmed->x_off, azmed->y_off, azmed->quad); } x = azmed->cos_Az * my_xy [XX] + azmed->sin_Az * my_xy [YY]; y = azmed->cos_Az * my_xy [YY] - azmed->sin_Az * my_xy [XX]; /* Rho is the radius of the circle whose center is at the origin and goes thorugh the current point. */ rho = sqrt (x * x + y * y); if (rho < azmed->one_mm) { /* The coordinate is essentially the origin. We do the following to avoid a atan2(0,0). If the origin is either pole, the longitude will be undefined. */ if (azmed->aspect == cs_AZMED_NORTH || azmed->aspect == cs_AZMED_SOUTH) { rtn_val = cs_CNVRT_INDF; } ll [LNG] = azmed->org_lng * cs_Radian; ll [LAT] = azmed->org_lat * cs_Radian; return (rtn_val); } if (rho >= azmed->max_rho) { /* Opps!!! The x and/or y is so large it is outside the circle which defines the extent of the coordinate system. Convert xy to lie on the bounding circle at the apropriate azimuth. */ rtn_val = cs_CNVRT_RNG; x *= azmed->max_rho / rho; y *= azmed->max_rho / rho; rho = azmed->max_rho; } /* Now we can convert back to lat/longs. We know that rho is not zero which implies that at least one of x and y is not zero. */ del_lng = cs_Zero; if (azmed->ecent == 0.0) { /* Here for the sphere. The sine and cosine functions shouldnever return a value greater than one. Thus, the asin ()'s here should be OK without any checking. */ c = rho / azmed->ka; sin_c = sin (c); cos_c = cos (c); switch (azmed->aspect) { case cs_AZMED_NORTH: del_lng = atan2 (x,-y); lat = asin (cos_c); break; case cs_AZMED_SOUTH: del_lng = atan2 (x,y); lat = -asin (cos_c); break; case cs_AZMED_EQUATOR: c = rho / azmed->ka; tmp1 = x * sin (c); tmp2 = rho * cos (c); del_lng = atan2 (tmp1,tmp2); tmp1 = (y / rho) * sin_c; if (fabs (tmp1) > cs_One) { tmp1 = (tmp1 >= 0.0) ? cs_One : cs_Mone; } lat = asin (tmp1); break; case cs_AZMED_GUAM: lat = azmed->org_lat; itr_cnt = 16; for (;;) { last_lat = lat; tmp1 = x * x * tan (lat) / azmed->two_ka; lat = azmed->org_lat + (y - tmp1) / azmed->ka; if (fabs (lat - last_lat) < cs_AnglTest) { break; } if (--itr_cnt <= 0) { rtn_val = cs_CNVRT_RNG; break; } } /* Longitude is undefined at the poles. */ if (fabs (lat) < cs_NPTest) { del_lng = x / (azmed->ka * cos (lat)); } break; case cs_AZMED_OBLIQUE: tmp1 = cos_c * azmed->sin_org_lat; tmp1 += (y / rho) * sin_c * azmed->cos_org_lat; if (fabs (tmp1) > cs_One) { tmp1 = (tmp1 >= 0.0) ? cs_One : cs_Mone; } lat = asin (tmp1); tmp1 = x * sin_c; tmp2 = rho * cos_c * azmed->cos_org_lat; tmp2 -= y * sin_c * azmed->sin_org_lat; del_lng = atan2 (tmp1,tmp2); break; default: CS_stncp (csErrnam,"CS_azmed:3",MAXPATH); CS_erpt (cs_ISER); rtn_val = -1; break; } } else { /* Here for the ellisoid. We've already dealt with x and y == 0. */ switch (azmed->aspect) { case cs_AZMED_NORTH: del_lng = atan2 (x,-y); lat = CSmmIcal (&azmed->mmcofI,(azmed->Mp - rho)); break; case cs_AZMED_SOUTH: del_lng = atan2 (x,y); lat = CSmmIcal (&azmed->mmcofI,(rho - azmed->Mp)); break; case cs_AZMED_GUAM: itr_cnt = 16; lat = azmed->org_lat; for (;;) { last_lat = lat; tmp1 = sin (lat); tmp1 = cs_One - azmed->e_sq * tmp1 * tmp1; tmp1 = x * x * tan (lat) * sqrt (tmp1); tmp1 = y - tmp1 / azmed->two_ka; M = azmed->M1 + tmp1; lat = CSmmIcal (&azmed->mmcofI,M); if (fabs (lat - last_lat) < cs_AnglTest) { break; } if (--itr_cnt <= 0) { rtn_val = cs_CNVRT_RNG; break; } } /* Longitude is undefined at the poles. */ if (fabs (lat) < cs_NPTest) { tmp1 = sin (lat); tmp1 = cs_One - azmed->e_sq * tmp1 * tmp1; del_lng = x * sqrt (tmp1) / (azmed->ka * cos (lat)); } break; case cs_AZMED_EQUATOR: case cs_AZMED_OBLIQUE: Az = atan2 (x,y); sin_Az = sin (Az); cos_Az = cos (Az); A = -azmed->e_sq_cos_sq * cos_Az * cos_Az / azmed->one_esq; B = cs_Three * azmed->e_sq * (cs_One - A) * azmed->sin_cos * cos_Az / azmed->one_esq; D = rho / azmed->N1; D_third = D * D * D; D_fourth = D_third * D; E = D; E -= A * (cs_One + A) * D_third / cs_Six; E -= B * (cs_One + cs_Three * A) * D_fourth / 24.0; E_sq = E * E; E_third = E_sq * E; sin_E = sin (E); F = cs_One - (A * E_sq * cs_Half) - (B * E_third / cs_Six); /*lint !e834 */ tmp1 = azmed->sin_org_lat * cos (E); tmp2 = azmed->cos_org_lat * sin_E * cos_Az; psi = asin (tmp1 + tmp2); /* Psi is essentially the latitude of the point adjusted for the affect of the ellipsoid. Thus, it can be zero, and it can be the equivalent of +-90 degrees. In analyzing the following, note that: 1) E is the ellipsoidally adjusted angular distance from the origin to the point; 2) the formula is actually equation 5-4 of Synder, reorganized to produce the delta longitude. Thus, I conclude that delta longitude goes to zero as cos(psi) approaches zero. */ cos_psi = cos (psi); if (fabs (cos_psi) > cs_AnglTest) { del_lng = asin (sin_Az * sin_E / cos (psi)); } /* Now, the latitude. We have a similar problem, but now with sin (psi) == 0. As psi approaches zero, this whole mess reduces to lat = psi, which is zero, of course. */ if (fabs (psi) > cs_AnglTest) { tmp1 = azmed->e_sq * F * azmed->sin_org_lat / sin (psi); tmp2 = tan (psi) / azmed->one_esq; lat = atan ((cs_One - tmp1) * tmp2); } else { lat = cs_Zero; } break; default: CS_stncp (csErrnam,"CS_azmed:",MAXPATH); CS_erpt (cs_ISER); rtn_val = -1; break; } } /* Longitude is undefined, mathematically, at either pole. */ if (rtn_val >= 0) { if (fabs (lat) > cs_NPTest) rtn_val = cs_CNVRT_INDF; /* Longitudes are always normalized for this projection. */ ll [LNG] = (del_lng + azmed->org_lng) * cs_Radian; ll [LAT] = lat * cs_Radian; } return (rtn_val); }
int EXP_LVL9 CSwinklI (Const struct cs_Winkl_ *winkl,double ll [2],Const double xy [2]) { extern double cs_Half; extern double cs_One; extern double cs_Radian; /* 57.29577..... */ extern double cs_3Pi_o_2; /* 3 Pi over 2 */ extern double cs_Pi_o_2; /* Pi over 2 */ extern double cs_AnglTest; extern double cs_Mpi; extern double cs_Pi; static double cnvrgK = 0.90; int rtn_val; int itr_cnt; double xx; double yy; double dd; double lat; double lng; double del_lng; double newX; double newY; double deltaX; double deltaY; rtn_val = cs_CNVRT_NRML; if (winkl->quad == 0) { xx = xy [XX] - winkl->x_off; yy = xy [YY] - winkl->y_off; } else { CS_quadI (&xx,&yy,xy,winkl->x_off,winkl->y_off,winkl->quad); } // Compute a first guess for the latitude and longitude using the // Equirectangular Inverse (which is easy enough) and an approximation of // the difference of the two. lat = (yy / winkl->ka); del_lng = xx / ( winkl->Rcos_ref_lat); // equidistant del_lng *= (cs_One + 0.38 * cos (lat)); // Aitoff adjustment (approx) if (del_lng < cs_Mpi) del_lng = cs_Mpi + cs_AnglTest; else if (del_lng > cs_Pi) del_lng = cs_Pi - cs_AnglTest; itr_cnt = 0; do { rtn_val = CSwinklB (winkl,&newX,&newY,del_lng,lat); deltaX = newX - xx; deltaY = newY - yy; dd = sqrt (deltaX * deltaX + deltaY * deltaY); lat -= (deltaY / winkl->ka) * cnvrgK; del_lng -= (deltaX / winkl->Rcos_ref_lat) * cnvrgK; itr_cnt += 1; } while (itr_cnt < 40 && dd > winkl->one_mm); if (itr_cnt >= 40) { rtn_val = cs_CNVRT_ERR; } lng = (winkl->org_lng + del_lng); if (fabs (lng) >= cs_Pi) { lng = CS_adj2pi (lng); } ll [LNG] = lng * cs_Radian; ll [LAT] = lat * cs_Radian; return rtn_val; }
int EXP_LVL9 CSvdgrnI (Const struct cs_Vdgrn_ *vdgrn,double ll [2],Const double xy [2]) { /* We undefine the following as these identifiers are used as variables in order to match the notation used in Synder. */ extern double cs_Radian; /* 57.29577..... */ extern double cs_Pi; /* 3.14159.... */ extern double cs_Mpi; /*-3.14159.... */ extern double cs_Pi_o_3; /* PI / 3.0 */ extern double cs_Zero; /* 0.0 */ extern double cs_Third; /* 0.33333..... */ extern double cs_One; /* 1.0 */ extern double cs_Mone; /* -1.0 */ extern double cs_Two; /* 2.0 */ extern double cs_Three; /* 3.0 */ extern double cs_Nine; /* 9.0 */ extern double cs_Pi_o_2; /* Pi over 2 */ extern double cs_Mpi_o_2; /* -Pi over 2 */ extern double cs_NPTest; /* 0.001 seconds of arc short of the north pole, in radians. */ int rtn_val; double x; double y; double lat; double del_lng; double xx, xx2; double yy, yy2, two_yy2; double xx2_yy2, xx2_yy2_sq; double c1; double c2, c2_sq, c2_cube; double c3, c3_sq, c3_cube; double dd; double a1; double m1; double theta1; double tmp; rtn_val = cs_CNVRT_NRML; if (vdgrn->quad == 0) { x = xy [0] - vdgrn->x_off; y = xy [1] - vdgrn->y_off; } else { CS_quadI (&x,&y,xy,vdgrn->x_off,vdgrn->y_off,vdgrn->quad); } /* Deal with bogus x and y coordinates. Should do this without the square root for performance purposes. */ tmp = sqrt (x * x + y * y); if (tmp > vdgrn->piR) { rtn_val = cs_CNVRT_RNG; x *= vdgrn->piR / tmp; y *= vdgrn->piR / tmp; } /* Set up all repeated variations of X and Y. */ xx = x / vdgrn->piR; yy = y / vdgrn->piR; xx2 = xx * xx; yy2 = yy * yy; two_yy2 = yy2 + yy2; xx2_yy2 = xx2 + yy2; xx2_yy2_sq = xx2_yy2 * xx2_yy2; /* Compute the internal constants. */ c1 = -(fabs (yy) * (cs_One + xx2_yy2)); c2 = c1 - two_yy2 + xx2; c2_sq = c2 * c2; c2_cube = c2_sq * c2; c3 = cs_One - (cs_Two * c1) + two_yy2 + xx2_yy2_sq; c3_sq = c3 * c3; c3_cube = c3_sq * c3; /* Compute the latitude. The ((3 * dd) / (a1 * m1)) term goes to infinity when Y is zero. Since Y == 0 means the equator for this projection, we punt and simply set lat = 0.0 in this case. */ if (fabs (y) <= vdgrn->one_mm) { lat = cs_Zero; } else { tmp = (cs_Two * c2_cube / c3_cube) - (cs_Nine * c1 * c2 / c3_sq); dd = (yy2 / c3) + (tmp / 27.0); a1 = (c1 - (c2_sq / (cs_Three * c3))) / c3; m1 = cs_Two * sqrt ((-a1) / cs_Three); tmp = (cs_Three * dd) / (a1 * m1); /* tmp, at this point, should never be greater than one. However, noise in the lower bits makes the following necessary. */ if (fabs (tmp) >= cs_One) { tmp = (tmp >= 0.0) ? cs_One : cs_Mone; } theta1 = cs_Third * acos (tmp); tmp = cos (theta1 + cs_Pi_o_3); lat = cs_Pi * ((-m1 * tmp) - (c2 / (cs_Three * c3))); if (y < 0) lat = -lat; } /* If xx is zero, the following falls apart, but longitude is equal to the origin longitude. */ if (fabs (x) < vdgrn->one_mm) { del_lng = cs_Zero; } else { tmp = cs_One + cs_Two * (xx2 - yy2) + xx2_yy2_sq; tmp = xx2_yy2 - cs_One + sqrt (tmp); /* Following divide by 2X causes the blow up. */ del_lng = ((cs_Pi * tmp) / (cs_Two * xx)); } /* Convert the results to degrees. */ if (fabs (del_lng) > cs_Pi) { del_lng = (del_lng > 0.0) ? cs_Pi : cs_Mpi; rtn_val = cs_CNVRT_RNG; } if (fabs (lat) > cs_NPTest) { rtn_val = cs_CNVRT_INDF; if (fabs (lat) > cs_Pi_o_2) { rtn_val = cs_CNVRT_RNG; lat = (lat > 0.0) ? cs_Pi_o_2 : cs_Mpi_o_2; } } ll [LNG] = (del_lng + vdgrn->org_lng) * cs_Radian; ll [LAT] = lat * cs_Radian; return (rtn_val); }
int EXP_LVL9 CSsinusI (Const struct cs_Sinus_ *sinus,double ll [2],Const double xy [2]) { extern double cs_Radian; /* 57.29577..... */ extern double cs_Pi_o_2; /* Pi over 2 */ extern double cs_Mpi_o_2; /* -Pi over 2 */ extern double cs_3Pi_o_2; /* 3 Pi over 2 */ extern double cs_Zero; /* 0.0 */ extern double cs_One; /* 1.0 */ extern double cs_NPTest; /* 0.001 arc seconds short of the north pole in radians. */ int rtn_val; Const struct cs_Zone_ *zp; double xx; double yy; double zn_xx; double zn_yy; double lat; double lng; double del_lng; double cent_lng; /* Local central longitude after adjustment for the appropriate zone. */ double x_off; /* The false easting appropriate for the zone. */ double sin_lat; double cos_lat; double tmp1; rtn_val = cs_CNVRT_NRML; /* Before we can do much with the inverse, we need to determine which zone the coordinate is in, if zones are active. */ if (sinus->zone_cnt <= 0) { /* No zones are active, this is easy. */ cent_lng = sinus->cent_lng; x_off = sinus->x_off; zp = NULL; } else { /* We have a complication introduced by variable quadrants. In order to locate the zone, we must undo the quadrant processing in selected pieces. */ zn_xx = xy [XX]; zn_yy = xy [YY]; if ((sinus->quad & cs_QUAD_SWAP) != 0) { zn_xx = xy [YY]; zn_yy = xy [XX]; } zn_yy -= sinus->y_off; /* We can now use zn_xx and zn_yy to locate the proper zone. */ zp = CS_znlocI (sinus->zones,sinus->zone_cnt,zn_xx,zn_yy); if (zp != NULL) { cent_lng = zp->cent_lng; x_off = zp->x_off; } else { rtn_val = cs_CNVRT_RNG; cent_lng = sinus->cent_lng; x_off = sinus->x_off; } } /* Remove the false origin. */ if (sinus->quad == 0) { xx = xy [XX] - x_off; yy = xy [YY] - sinus->y_off; } else { CS_quadI (&xx,&yy,xy,x_off,sinus->y_off,sinus->quad); } /* Rather sane stuff from here on out. */ if (fabs (yy) > sinus->max_yy) { rtn_val = cs_CNVRT_RNG; yy = (yy >= 0.0) ? sinus->max_yy : -sinus->max_yy; } if (fabs (xx) > sinus->max_xx) { rtn_val = cs_CNVRT_RNG; xx = (xx >= 0.0) ? sinus->max_xx : -sinus->max_xx; } /* We have now compensated for the zone. */ del_lng = cs_Zero; if (sinus->ecent == 0.0) { /* Here for the shpere. Cos (lat) will be zero at the poles. In this case, any longitude will do. */ lat = yy / sinus->ka; if (fabs (lat) < cs_NPTest) { cos_lat = cos (lat); del_lng = xx / (sinus->ka * cos_lat); } else if (rtn_val == cs_CNVRT_NRML) { lat = (lat > 0.0) ? cs_Pi_o_2 : cs_Mpi_o_2; rtn_val = cs_CNVRT_INDF; } } else { /* Here for the ellipsoid. Again, the longitude is indeterminate at the poles, and any longitude will do. */ lat = CSmmIcal (&sinus->mmcofI,yy); if (fabs (lat) < cs_NPTest) { cos_lat = cos (lat); sin_lat = sin (lat); tmp1 = cs_One - (sinus->e_sq * sin_lat * sin_lat); del_lng = (xx * sqrt (tmp1)) / (sinus->ka * cos_lat); } else if (rtn_val == cs_CNVRT_NRML) { lat = (lat > 0.0) ? cs_Pi_o_2 : cs_Mpi_o_2; rtn_val = cs_CNVRT_INDF; } } if (fabs (del_lng) >= cs_3Pi_o_2) { rtn_val = cs_CNVRT_RNG; del_lng = CS_adj2pi (del_lng); } lng = del_lng + cent_lng; /* If the X and Y coordinates are in the void space between the zones, the resulting longitude will end up in a zone other than the one we started with. In this case, we have yet another range error, and we adjust the longitude to the appropriate edge of the zone we started out in. */ if (zp != NULL) { if (lng < zp->west_lng) { rtn_val = cs_CNVRT_RNG; lng = zp->west_lng; } if (lng > zp->east_lng) { rtn_val = cs_CNVRT_RNG; lng = zp->east_lng; } } /* Convert to degrees. */ ll [LNG] = lng * cs_Radian; ll [LAT] = lat * cs_Radian; return (rtn_val); }
int EXP_LVL9 CSkrovkI (Const struct cs_Krovk_ *krovk,double lnglat [2],Const double xy [2]) { extern double cs_Radian; /* 57.29577..... */ extern double cs_Pi; /* Pi */ extern double cs_Pi_o_2; /* Pi over 2 */ extern double cs_Pi_o_4; /* Pi over 4 */ extern double cs_Half; /* 0.5 */ extern double cs_One; /* 1.0 */ extern double cs_Two; /* 2.0 */ int rtn_val; double xx, yy; double rho, theta; double lngO, latO; double lngS, latS; double lngE, latE; double sinLatO, cosLatO; double sinLngS; double sinLatS, cosLatS; double tmp1; double tmp_xy [2]; double deltaXY [2]; rtn_val = cs_CNVRT_NRML; /* We use tmpxy as we don't want to change the provided parameters on the user, that just isn't very nice. */ tmp_xy [XX] = -xy [YY]; tmp_xy [YY] = -xy [XX]; /* Undo the strange stuff we did to get the desired result as far as the axis numbers. See the tail end of the forward function for a description of what's going on here. */ CS_quadI (&xx,&yy,tmp_xy,krovk->x_off,krovk->y_off,krovk->quad); /* Undo the 95 adjustment if appropriate. */ if (krovk->apply95) { /* Note that this does not actually produce a real inverse. The result is close, but not true. A true mathematical inverse does not exist (perhaps why the source doesn't provide one). We'll have to come up with a numerical approximation to the inverse if the creep factor is too large. */ tmp_xy [XX] = xx; tmp_xy [YY] = yy; CSkrovk95 (deltaXY,tmp_xy); xx += deltaXY [XX]; yy += deltaXY [YY]; } /* Convert the X and Y to polar coordinates. */ theta = atan2 (yy,xx); rho = sqrt (yy * yy + xx * xx); /* Check the results for bogus values. */ if (rho < krovk->pole_test) { /* If rho is very small, the point is on the pole and we can simply return that value now. */ lnglat [LNG] = krovk->poleLng * cs_Radian; lnglat [LAT] = krovk->poleLat * cs_Radian; return rtn_val; } if (rho > krovk->infinity) { /* If rho is huge, we respond with a point at the opposite pole. */ lnglat [LNG] = CS_adj2pi (krovk->poleLng - cs_Pi) * cs_Radian; lnglat [LAT] = -krovk->poleLat * cs_Radian; return rtn_val; } if (fabs (theta) >= krovk->theta_max) { /* Opps, the point is in the conic crack. Adjust. */ theta = theta > 0.0 ? krovk->theta_max : -krovk->theta_max; } /* Convert the polar coordinates to lat/long on the oblique sphere. This uses the Lambert Conformal technique, not necessarily the same formulas. */ lngO = theta / krovk->nn; tmp1 = pow (krovk->tanTermI / rho,krovk->one_o_nn); latO = cs_Two * atan (tmp1) - cs_Pi_o_2; /* From the Czech document test case: lngO =? 21.8191895277777; latO =? 77.7249551388888 */ /* Convert from the oblique sphere to the gausian surface. */ sinLatO = sin (latO); cosLatO = cos (latO); sinLatS = krovk->sinLatQ * sinLatO - krovk->cosLatQ * cosLatO * cos (lngO); cosLatS = sqrt (cs_One - sinLatS * sinLatS); latS = atan (sinLatS / cosLatS); sinLngS = (cosLatO / cosLatS) * sin (lngO); lngS = krovk->lngQ - asin (sinLngS); /* Convert from the gausian surface to the ellipsoid. If the reference is a sphere, don't really need to do anything. */ if (krovk->ecent == 0.0) { lngE = lngS; latE = latS; } else { lngE = lngS / krovk->alpha; latE = CSkrovkB3 (krovk,latS); } /* Convert the results to degrees. */ lnglat [LNG] = (lngE + krovk->orgLng) * cs_Radian; lnglat [LAT] = latE * cs_Radian; return (rtn_val); }
int EXP_LVL9 CSorthoI (Const struct cs_Ortho_ *ortho,double ll [2],Const double xy [2]) { extern double cs_Radian; /* 57.29577..... */ extern double cs_Zero; /* 0.0 */ extern double cs_One; /* 1.0 */ extern double cs_AnglTest; /* 0.001 arc seconds in radians. */ extern double cs_NPTest; /* 0.001 arc seconds short of the north pole, in radians. */ extern double cs_SPTest; /* 0.001 arc seconds short of the south pole, in radians. */ int rtn_val; double x; double y; double rho; double lat; double del_lng; double sin_c; double cos_c; double tmp; rtn_val = cs_CNVRT_NRML; if (ortho->quad == 0) { x = xy [XX] - ortho->x_off; y = xy [YY] - ortho->y_off; } else { CS_quadI (&x,&y,xy,ortho->x_off,ortho->y_off,ortho->quad); } del_lng = cs_Zero; rho = sqrt (x * x + y * y); if (rho < ortho->one_mm) { /* This code eliminates many possible glitches in the code below. */ lat = ortho->org_lat; } else { /* The latitude calculation is common to all aspects. c is the angular distance from the origin to the point being converted. Thus, by limiting rho to ka, -pi/4 <= c >= pi/4, and cos_c is always positive (possibly zero), and since we have already handled the case of rho == 0, sin_c will never be zero. */ if (rho > ortho->ka) { rtn_val = cs_CNVRT_RNG; x *= ortho->ka / rho; y *= ortho->ka / rho; rho = ortho->ka; } sin_c = rho / ortho->ka; cos_c = sqrt (cs_One - (sin_c * sin_c)); /* We handled rho == 0 above. */ tmp = (y * sin_c * ortho->cos_org_lat) / rho; tmp = (cos_c * ortho->sin_org_lat) + tmp; lat = asin (tmp); /* The longitude calculation is much faster for the two polar aspects. There is no mathematical reason for having three different cases. Note, we force the values of sin_org_lat and cos_org_lat to hard values in the setup function. */ if (ortho->org_lat >= cs_NPTest) { /* Here for the north polar aspect: cos_org_lat = 0, sin_org_lat = +1. Since rho is not zero, we know that either x or y is not zero. */ del_lng = atan2 (x,-y); } else if (ortho->org_lat <= cs_SPTest) { /* Here for the south polar aspect: cos_org_lat = 0, sin_org_lat = -1 */ del_lng = atan2 (x,y); } else if (fabs (ortho->org_lat) <= cs_AnglTest) { /* Here for the equatorial aspect: cos_org_lat = 1, sin_org_lat = 0. Careful, there is a point at which both args to atan2 here could be zero: x=0,y=ka. If x is zero, the point is on the central meridian. Since this is an equatorial aspect, we need not worry about polar wrap around. */ if (fabs (x) > ortho->one_mm) { del_lng = atan2 (x * sin_c,rho * cos_c); } } else { /* Here for the oblique aspect. Again, if x == 0, it is possible for both arguments to atan2 to be zero. (org_lat=pi/4, c=pi/4). */ if (fabs (x) > ortho->one_mm) { tmp = (rho * ortho->cos_org_lat * cos_c) - ( y * ortho->sin_org_lat * sin_c); del_lng = atan2 (x * sin_c,tmp); } } } if (fabs (lat) > cs_NPTest && rtn_val == cs_CNVRT_NRML) { rtn_val = cs_CNVRT_INDF; } /* Convert the results to degrees. */ ll [LNG] = (del_lng + ortho->org_lng) * cs_Radian; ll [LAT] = lat * cs_Radian; return (rtn_val); }
int EXP_LVL9 CSpstroI (Const struct cs_Pstro_ *pstro,double ll [2],Const double xy [2]) { extern double cs_Radian; /* 57.29577..... */ extern double cs_Two; /* 2.0 */ extern double cs_Pi_o_2; /* PI over 2.0 */ extern double cs_NPTest; /* 0.001 seconds of arc short of the north pole, in radians. */ int rtn_val; double x; double y; double c; double t; double chi; double rho; double cos_c; rtn_val = cs_CNVRT_NRML; /* Adjust for a non-standard quadrant. */ if (pstro->quad == 0) { x = xy [XX] - pstro->x_off; y = xy [YY] - pstro->y_off; } else { CS_quadI (&x,&y,xy,pstro->x_off,pstro->y_off,pstro->quad); } /* If x and y are now both zero, or very close to it, we must just set the result to the origin of the projection. We don't want to divide by rho if it's zero. */ rho = sqrt (x * x + y * y); if (rho <= pstro->one_mm) { /* The coordinate is essentially zero. Return the origin of the coordinate system. */ ll [LNG] = pstro->org_lng * cs_Radian; ll [LAT] = pstro->org_lat * cs_Radian; return (rtn_val); } /* Now we can convert back to lat/longs. */ if (pstro->ecent == 0.0) { /* Here for the sphere. Note, we have already filtered out cases where rho is zero (or very close to zero). Note, rho can approach infinity, although considering values greater than two_ka out of the domain would be generally acceptable. Read c as the angular distance from the origin. */ c = cs_Two * atan (rho / pstro->two_ka); cos_c = cos (c); switch (pstro->aspect) { default: case cs_STERO_NORTH: ll [LNG] = pstro->org_lng + atan2 (x,-y); ll [LAT] = asin (cos_c); break; case cs_STERO_SOUTH: ll [LNG] = -pstro->org_lng + atan2 (-x,y); ll [LAT] = -asin (cos_c); break; } } else { /* Here for the ellisoid. */ switch (pstro->aspect) { default: case cs_STERO_NORTH: ll [LNG] = pstro->org_lng + atan2 (x,-y); t = rho * pstro->e_term / pstro->two_ka; chi = cs_Pi_o_2 - (cs_Two * atan (t)); ll [LAT] = CSchiIcal (&pstro->chicofI,chi); break; case cs_STERO_SOUTH: ll [LNG] = pstro->org_lng - atan2 (-x,y); t = rho * pstro->e_term / pstro->two_ka; chi = (cs_Two * atan (t)) - cs_Pi_o_2; ll [LAT] = CSchiIcal (&pstro->chicofI,chi); break; } } if (fabs (ll [LAT]) > cs_NPTest && rtn_val == cs_CNVRT_NRML) /*lint !e774 */ { rtn_val = cs_CNVRT_INDF; } /* Convert the results to degrees. */ ll [LNG] *= cs_Radian; ll [LAT] *= cs_Radian; return (rtn_val); }
int EXP_LVL9 CSlmtanI (Const struct cs_Lmtan_ *lmtan,double ll [2],Const double xy [2]) { extern double cs_Pi_o_2; /* PI / 2.0 */ extern double cs_Radian; /* 57.29577..... */ extern double cs_Zero; /* 0.0 */ extern double cs_One; /* 1.0 */ extern double cs_Two; /* 2.0 */ extern double cs_AnglTest; /* 0.001 arc seconds, in radians. */ int i_cnt; int rtn_val; double x; double y; double R; double L; double exp_L; double Gamma; double del_lng; double new_lat; double last_lat; double tmp1; rtn_val = cs_CNVRT_NRML; if (lmtan->quad == 0) { x = xy [XX] - lmtan->x_off; y = xy [YY] - lmtan->y_off; } else { CS_quadI (&x,&y,xy,lmtan->x_off,lmtan->y_off,lmtan->quad); } y -= lmtan->R0; /* Set R equal to the length of the line from the origin to the point being converted. Gamma is the angle portion of the polar coordinate system. */ R = sqrt (x * x + y * y); if (R > lmtan->max_R) { /* R is too large, the coordinates provided must be really bogus. */ rtn_val = cs_CNVRT_RNG; R = lmtan->max_R; } /* If R is not zero, then at least one of x and y is not zero, and the atan2 function should be happy. */ if (R > lmtan->one_mm) { Gamma = atan2 (x,-y); } else { /* If R is zero, we are at the focus pole, and any gamma, like zero, will do nicely since longitude is (mathematically) undefined at this point. */ Gamma = cs_Zero; rtn_val = cs_CNVRT_INDF; } if (fabs (Gamma) > lmtan->max_Gamma) { /* Coordinate is in the pie slice opposite the central meridian. */ rtn_val = cs_CNVRT_RNG; Gamma = (Gamma > 0.0) ? lmtan->max_Gamma : -lmtan->max_Gamma; } /* Compute the longitude. sin_org_lat should never be zero as a zero origin latitude is not permitted by CSlmtanQ. If the coordinate system has a south pole focus, sin_org_lat will be negative, thereby effecting the necessary reversal on the sense of gamma. */ del_lng = Gamma / lmtan->sin_org_lat; /* The latitude is a bit more difficult. We first compute the isometric latitude. The use of abs_c here precludes taking the log of a negative number. We've already dealt with the case of R being zero; c and sin_org_lat are never zero. */ L = -(log (R / lmtan->abs_c) / lmtan->sin_org_lat); exp_L = exp (L); new_lat = cs_Two * atan (exp_L) - cs_Pi_o_2; /* We must now iterate to convert the isometric latitude to the geographic latitude that we desire. We could use our CSchiIcal function to calculate the latitude without iteration, but we choose to iterate to duplicate the algorithm published by the French NGI which is the primary user of this projection. If the iteration fails to converge, we assume that we are out of range. Since we limited latitude to reasonable values above, I don't think this can happen. */ i_cnt = -12; do { last_lat = new_lat; tmp1 = lmtan->ecent * sin (last_lat); tmp1 = (cs_One + tmp1) / (cs_One - tmp1); tmp1 = pow (tmp1,lmtan->e_ovr_2); tmp1 *= exp_L; new_lat = cs_Two * atan (tmp1) - cs_Pi_o_2; if (i_cnt++ > 0) { rtn_val = cs_CNVRT_RNG; break; } } while (fabs (new_lat - last_lat) > cs_AnglTest); ll [LNG] = (del_lng + lmtan->org_lng) * cs_Radian; ll [LAT] = new_lat * cs_Radian; return (rtn_val); }
int EXP_LVL9 CSmrcatI (Const struct cs_Mrcat_ *mrcat,double ll [2],Const double xy [2]) { extern double cs_Radian; /* 57.29577... */ extern double cs_Pi_o_2; /* Pi / 2.0 */ extern double cs_3Pi_o_2; /* 3 Pi / 2.0 */ extern double cs_Two; /* 2.0 */ int rtn_val; double xx; double yy; double chi; double lat; double del_lng; double tmp1; rtn_val = cs_CNVRT_NRML; /* Remove whatever offsets are active. */ if (mrcat->quad == 0) { xx = xy [XX] - mrcat->x_off; yy = xy [YY] - mrcat->y_off; } else { CS_quadI (&xx,&yy,xy,mrcat->x_off,mrcat->y_off,mrcat->quad); } /* Check the Y value for range. */ if (fabs (yy) > mrcat->yy_max) { rtn_val = cs_CNVRT_RNG; yy = (yy >= 0.0) ? mrcat->yy_max : -mrcat->yy_max; } /* The longitude calculation is the same for both the spherical and ellipsoidal cases. There may be a slight difference if the standard parallel is not the equator, but tis is taken care of during set up and shows up in the Rfact variable. */ del_lng = xx / mrcat->Rfact; if (fabs (del_lng) >= cs_3Pi_o_2) { rtn_val = cs_CNVRT_RNG; del_lng = CS_adj2pi (del_lng); } /* The following is used for sphere and ellipsoid. */ tmp1 = exp (-yy / mrcat->Rfact); chi = cs_Pi_o_2 - cs_Two * atan (tmp1); /* Finish off the latitude as appropriate. */ if (mrcat->ecent == 0.0) { /* Here for a sphere. */ lat = chi; } else { /* Here for an ellipsoid. This is a series expansion used in other projections, so we have a function to do this for us. */ lat = CSchiIcal (&mrcat->chicofI,chi); } ll [LNG] = (del_lng + mrcat->cent_lng) * cs_Radian; ll [LAT] = lat * cs_Radian; return (rtn_val); }
int EXP_LVL9 CSnzlndI (Const struct cs_Nzlnd_ *nzlnd,double ll [2],Const double xy [2]) { extern double cs_Radian; /* 57.29577..... */ extern double cs_Zero; /* 0.0 */ extern double cs_One; /* 1.0 */ extern double cs_K90; /* 90.0 */ int ii; int rtn_val; double xx; double yy; double rho; double lat; double del_psi,del_lat; struct cs_Cmplx_ zz; struct cs_Cmplx_ theta; struct cs_Cmplx_ theta1; struct cs_Cmplx_ theta2; rtn_val = cs_CNVRT_NRML; if (nzlnd->quad == 0) { xx = xy [XX] - nzlnd->x_off; yy = xy [YY] - nzlnd->y_off; } else { CS_quadI (&xx,&yy,xy,nzlnd->x_off,nzlnd->y_off,nzlnd->quad); } xx /= nzlnd->ka; yy /= nzlnd->ka; if (fabs (xx) > cs_One || fabs (yy) > cs_One) { rtn_val = cs_CNVRT_RNG; rho = sqrt (xx * xx + yy * yy); xx /= rho; yy /= rho; } zz.real = yy; zz.img = xx; CS_iisrs (&zz,nzlnd->C_ary,6,&theta); for (ii = 0;ii < 2;ii++) { CS_iisrs1 (&theta,nzlnd->B_ary,6,&theta1); CS_iisrs0 (&theta,nzlnd->B_ary,6,&theta2); CS_iiadd (&zz,&theta2,&theta2); CS_iidiv (&theta2,&theta1,&theta); } del_psi = theta.real; del_lat = cs_Zero; for (ii = 8;ii >= 0;ii--) { del_lat = (del_lat + nzlnd->D_ary [ii]) * del_psi; } ll [LNG] = CS_adj180 (theta.img * cs_Radian) + nzlnd->org_lng; lat = CS_adj90 (del_lat / nzlnd->lat_kk) + nzlnd->org_lat; if (fabs (lat) > cs_K90) { rtn_val = cs_CNVRT_RNG; lat = CS_adj90 (lat); } ll [LAT] = lat; return (rtn_val); }
int EXP_LVL9 CStacylI (Const struct cs_Tacyl_ *tacyl,double ll [2],Const double xy [2]) { extern double cs_Zero; extern double cs_One; /* 1.0 */ extern double cs_Mone; /* -1.0 */ extern double cs_Radian; /* 57.29577... */ extern double cs_Pi; /* 3.14159... */ extern double cs_Pi_o_2; /* PI / 2.0 */ extern double cs_AnglTest; /* 0.001 arc seconds, in radians. */ int rtn_val; double xx; double yy; double del_lng; double lat; double qq; double Mc; double DD; double beta; double sin_DD; double cos_DD; double latc; double sin_lat; double sin_latc; double cos_latc; double esin_latc; double betac; double sin_betac; double cos_betac; double betap; double cos_betap; double tmp1; double tmp2; double tmp3; double tmp4; rtn_val = cs_CNVRT_NRML; /* Remove the false origin. */ if (tacyl->quad == 0) { xx = xy [XX] - tacyl->x_off; yy = xy [YY] - tacyl->y_off; } else { CS_quadI (&xx,&yy,xy,tacyl->x_off,tacyl->y_off,tacyl->quad); } lat = cs_Zero; del_lng = cs_Zero; if (tacyl->ecent == 0.0) { /* Here for the sphere. There are two special cases to deal with. If X is out of range, we will end up taking the square root of a negative number. In this case, assuming we are not at a pole (cos_DD != 0.0), we set the longitude to be equal to org_lng +- _PI/2 taking the sign of X. If X is in range, but we are at either pole, there is no longitude and we can pick any value, like org_lng. */ DD = yy / tacyl->kah0 + tacyl->org_lat; sin_DD = sin (DD); cos_DD = cos (DD); if (fabs (cos_DD) < cs_AnglTest) { /* Opps!!! We're at a pole. Can't divide by cos_DD. Pick the right pole and leave longitude ar org_lng. */ rtn_val = cs_CNVRT_INDF; if (sin_DD > 0.0) lat = cs_Pi_o_2; else lat = cs_Pi_o_2; } else { /* Not at either pole. Make sure we don't take the square root of a negative number. */ tmp1 = xx * tacyl->h0_o_ka; tmp2 = cs_One - tmp1 * tmp1; if (tmp2 < cs_AnglTest) { if (tmp2 < 0.0) rtn_val = cs_CNVRT_RNG; tmp2 = cs_Zero; } else { tmp2 = sqrt (tmp2); } sin_lat = tmp2 * sin_DD; if (fabs (sin_lat) >= cs_One) { sin_lat = (sin_lat >= 0.0) ? cs_One : cs_Mone; } lat = asin (sin_lat); tmp2 *= cos_DD; /* tmp2 may be zero here, but tmp1 and tmp2 cannot both be zero. Therefore, atan2 should work OK in all cases. */ del_lng = atan2 (tmp1,tmp2); } } else { /* Here for the ellipsoid. The standard equations are indeterminate at the poles. At the current time, the handling of |X| values greater than R/h0 produces different results between the spherical and ellipsoid cases. X isn't supposed to get bigger than this, so it might not make any difference to anyone. */ Mc = tacyl->M0 + yy / tacyl->h0; latc = CSmmIcal (&tacyl->mmcofI,Mc); sin_latc = sin (latc); cos_latc = cos (latc); if (fabs (cos_latc) > cs_AnglTest) { /* Not at a pole. */ esin_latc = tacyl->ecent * sin_latc; tmp1 = (cs_One - esin_latc) / (cs_One + esin_latc); tmp1 = tacyl->one_o_2e * log (tmp1); tmp2 = cs_One - esin_latc * esin_latc; qq = tacyl->one_m_esq * (sin_latc / tmp2 - tmp1); /* Seems a bit inefficient, but we need the normalizing affect of the asin function here. sin_betac should never be more than one, but noise in the low order bits during the calculations require that we check to prevent asin from generating a floating point exception. */ sin_betac = qq / tacyl->qp; if (fabs (sin_betac) >= cs_One) { sin_betac = (sin_betac >= 0.0) ? cs_One : cs_Mone; } betac = asin (sin_betac); sin_betac = sin (betac); cos_betac = cos (betac); /* Note what the following does when |X| is greater than R/h0: Betap, and hence beta, simply wraps back on themselves to computable values. I.e. an X value of R/h0+p is treated the same as the value R/h0-p. */ tmp3 = cos_betac * sqrt (tmp2) / cos_latc; tmp4 = xx * tacyl->h0_o_ka * tmp3; if (fabs (tmp4) > cs_One) { rtn_val = cs_CNVRT_RNG; tmp4 = (tmp4 >= 0.0) ? cs_One : cs_Mone; } betap = -asin (tmp4); cos_betap = cos (betap); beta = asin (cos_betap * sin_betac); /* We don't get here if we are at a pole. Therefore, tan (betap) should be OK, and cos_betac should never be zero. Since cos_betac is always positive, we can use atan just as well as atan2. */ del_lng = -atan (tan (betap) / cos_betac); lat = CSbtIcalPrec (&tacyl->btcofI,beta); } else { /* At a pole, any longitude will do. We'll use the origin longitude by leaving lng set to zero. */ rtn_val = cs_CNVRT_INDF; if (latc > 0.0) lat = cs_Pi_o_2; else lat = cs_Pi_o_2; } } if (fabs (del_lng) > cs_Pi) { del_lng = CS_adj2pi (del_lng); rtn_val = cs_CNVRT_RNG; } ll [LNG] = (del_lng + tacyl->org_lng) * cs_Radian; if (fabs (lat) > cs_Pi_o_2) rtn_val = cs_CNVRT_RNG; ll [LAT] = CS_adj1pi (lat) * cs_Radian; return (rtn_val); }
int EXP_LVL9 CSunityI (Const struct cs_Unity_ *unity,double ll [2],Const double xy [2]) { extern double cs_Zero; /* 0.0 */ extern double cs_K90; /* 90.0 */ extern double cs_Km90; /* -90.0 */ int rtn_val; double lcl_xy [2]; rtn_val = cs_CNVRT_NRML; /* Apply the quadrant processing. This is usually used to make west longitude positive. */ if (unity->quad == 0) { lcl_xy [XX] = xy [XX]; lcl_xy [YY] = xy [YY]; } else { CS_quadI (&lcl_xy [XX],&lcl_xy [YY],xy,cs_Zero,cs_Zero,unity->quad); } /* See if the user supplied values are with the user's specified range. If not, we have a domain error, and we normalize to the user's specified range before proceeding. */ if (lcl_xy [XX] < unity->usr_min || lcl_xy [XX] > unity->usr_max) { rtn_val = cs_CNVRT_DOMN; lcl_xy [XX] -= unity->usr_min; lcl_xy [XX] = fmod (lcl_xy [XX],unity->usr_2pi); lcl_xy [XX] += unity->usr_min; } /* Convert the supplied values to internal units and referencing. */ ll [LNG] = unity->gwo_lng + (lcl_xy [XX] / unity->unit_s); ll [LAT] = lcl_xy [YY] / unity->unit_s; /* Normalize the longitude to internal standards. */ ll [LNG] = CS_adj270 (ll [LNG]); /* If the resulting latitude is out of range, we do have a problem. */ if (ll [LAT] < cs_Km90 || ll [LAT] > cs_K90) { /* Not within the user's expected range. */ rtn_val = cs_CNVRT_RNG; ll [LAT] = CS_adj90 (ll [LAT]); } return (rtn_val); }
int EXP_LVL9 CScsiniI (Const struct cs_Csini_ *csini,double ll [2],Const double xy [2]) { extern double cs_Radian; /* 57.29577... */ extern double cs_Pi; /* 3.14159... */ extern double cs_Pi_o_2; /* pi over 2 */ extern double cs_Zero; /* 0.0 */ extern double cs_Third; /* 0.3333... */ extern double cs_Half; /* 0.5 */ extern double cs_One; /* 1.0 */ extern double cs_Three; /* 3.0 */ extern double cs_AnglTest; /* 0.001 arc seconds, in radians. */ extern double cs_Huge; /* an approximation of infinity, not so large that it can't be used in normal calculations without problems. */ int rtn_val; double x; double y; double lat; double del_lng; double D; double D_2; double D_3; double D_4; double D_5; double N1; double R1; double T1; double phi1; double sin_phi1; double cos_phi1; double tan_phi1; double tmp1; double tmp2; rtn_val = cs_CNVRT_NRML; if (csini->quad == 0) { x = xy [XX] - csini->x_off; y = xy [YY] - csini->y_off; } else { CS_quadI (&x,&y,xy,csini->x_off,csini->y_off,csini->quad); } if (fabs (x) > csini->max_xx) { rtn_val = cs_CNVRT_RNG; x = (x >= 0.0) ? csini->max_xx : -csini->max_xx; } if (y < csini->min_yy || y > csini->max_yy) { rtn_val = cs_CNVRT_RNG; } if (csini->ecent == 0.0) { /* Here for a spherical datum. Cos D is zero at the poles. Tan (tmp1) is also zero when x is zero. */ D = (y / csini->ka) + csini->org_lat; tmp1 = x / csini->ka; tmp2 = sin (D) * cos (tmp1); lat = asin (tmp2); if (fabs (tmp1) > cs_AnglTest) { /* This blows up when both arguments are zero, which will happen only at the poles. At the poles, all values of longitudes are equivalent. */ del_lng = atan2 (tan (tmp1),cos (D)); } else { if (rtn_val == cs_CNVRT_NRML) { rtn_val = cs_CNVRT_INDF; } del_lng = cs_Zero; } } else { /* Here for an ellipsoid. */ phi1 = CSmmIcal (&csini->mmcofI,csini->M0 + y); sin_phi1 = sin (phi1); cos_phi1 = cos (phi1); if (fabs (cos_phi1) > cs_AnglTest) { /* Blows up at the poles, but not really out the useful range of this projection. */ tan_phi1 = sin_phi1/cos_phi1; } else { tan_phi1 = cs_Huge; } T1 = tan_phi1 * tan_phi1; tmp1 = cs_One - (csini->e_sq * (sin_phi1 * sin_phi1)); tmp2 = sqrt (tmp1); N1 = csini->ka / tmp2; R1 = csini->R_term / (tmp1 * tmp2); D = x / N1; D_5 = D * (D_4 = D * (D_3 = D * (D_2 = D * D))); /* Compute the latitude. */ tmp1 = (cs_Half * D_2); tmp1 -= (cs_One + cs_Three * T1) * D_4 * (1.0 / 24.0); tmp1 *= N1 * tan_phi1 / R1; lat = phi1 - tmp1; /* Now we can compute a longitude. */ if (fabs (cos_phi1) > cs_AnglTest) { tmp1 = D - (T1 * (cs_Third * D_3)); tmp1 += (cs_One + cs_Three * T1) * T1 * D_5 * (1.0 / 15.0); del_lng = tmp1 / cos_phi1; /* Blows up at the poles. */ } else { /* Any longitude, like cent_lng, will do just fine. */ if (rtn_val == cs_CNVRT_NRML) { rtn_val = cs_CNVRT_INDF; } del_lng = cs_Zero; } } if (fabs (del_lng) > cs_Pi) { del_lng = CS_adj2pi (del_lng); rtn_val = cs_CNVRT_RNG; } if (fabs (lat) > cs_Pi_o_2) { lat = CS_adj1pi (lat); rtn_val = cs_CNVRT_RNG; } ll [LNG] = (del_lng + csini->cent_lng) * cs_Radian; ll [LAT] = lat * cs_Radian; return (rtn_val); }