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 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 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 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); }