#include "tpers.h" #include #include "common/geodetic/wgs84.h" #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 /* pi/2 */ #endif /* ** This file was adapted and simplified from libproj, and the below ** notice kept as credits. ** ** libproj -- library of cartographic projections ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define EPS10 1.e-10 namespace geodetic { namespace projection { int TPERSProjection::init(double altitude, double longitude, double latitude, double tilt, double azi) { lon_0 = longitude; double omega, gamma; omega = tilt * 0.01745329; // to rads gamma = azi * 0.01745329; // to rads this->tilt = 1; cg = cos(gamma); sg = sin(gamma); cw = cos(omega); sw = sin(omega); height = altitude; phi0 = latitude * 0.01745329; a = WGS84::a* 1000; if (fabs(fabs(phi0) - M_PI_2) < EPS10) mode = phi0 < 0. ? S_POLE : N_POLE; else if (fabs(phi0) < EPS10) mode = EQUIT; else { mode = OBLIQ; sinph0 = sin(phi0); cosph0 = cos(phi0); } pn1 = height / a; // normalize by radius if (pn1 <= 0 || pn1 > 1e10) { // Illegal!! return 1; } p = 1. + pn1; rp = 1. / p; h = 1. / pn1; pfact = (p + 1.) * h; es = 0.; return 0; } int TPERSProjection::forward(double lon, double lat, double &x, double &y) { x = y = 0; // Safety // Shift longitudes to use the sat's as a reference lon -= lon_0; if (lon < -180) lon = lon + 360; if (lon > 180) lon = lon - 360; // To radians double phi = lat * 0.01745329, lam = lon * 0.01745329; double coslam, cosphi, sinphi; sinphi = sin(phi); cosphi = cos(phi); coslam = cos(lam); switch (mode) { case OBLIQ: y = sinph0 * sinphi + cosph0 * cosphi * coslam; break; case EQUIT: y = cosphi * coslam; break; case S_POLE: y = -sinphi; break; case N_POLE: y = sinphi; break; } if (y < rp) { x = y = 2e10; // Trigger error return 1; } y = pn1 / (p - y); x = y * cosphi * sin(lam); switch (mode) { case OBLIQ: y *= (cosph0 * sinphi - sinph0 * cosphi * coslam); break; case EQUIT: y *= sinphi; break; case N_POLE: coslam = -coslam; /*-fallthrough*/ case S_POLE: y *= cosphi * coslam; break; } if (tilt) { double yt, ba; yt = y * cg + x * sg; ba = 1. / (yt * sw * h + cw); x = (x * cg - y * sg) * cw * ba; y = yt * ba; } return 0; } int TPERSProjection::inverse(double x, double y, double &lon, double &lat) { lon = lat = 0.0; double phi = 0, lam = 0; double rh; if (tilt) { double bm, bq, yt; yt = 1. / (pn1 - y * sw); bm = pn1 * x * yt; bq = pn1 * y * cw * yt; x = bm * cg + bq * sg; y = bq * cg - bm * sg; } rh = hypot(x, y); if (fabs(rh) <= EPS10) { lam = 0.; phi = phi0; } else { double cosz, sinz; sinz = 1. - rh * rh * pfact; if (sinz < 0.) { // Illegal lon = lat = 2e10; // Trigger error return 1; } sinz = (p - sqrt(sinz)) / (pn1 / rh + rh / pn1); cosz = sqrt(1. - sinz * sinz); switch (mode) { case OBLIQ: phi = asin(cosz * sinph0 + y * sinz * cosph0 / rh); y = (cosz - sinph0 * sin(phi)) * rh; x *= sinz * cosph0; break; case EQUIT: phi = asin(y * sinz / rh); y = cosz * rh; x *= sinz; break; case N_POLE: phi = asin(cosz); y = -y; break; case S_POLE: phi = -asin(cosz); break; } lam = atan2(x, y); } // To degs lat = phi * 57.29578; lon = lam * 57.29578; // Shift longitudes back to reference 0 lon += lon_0; if (lon < -180) lon = lon + 360; if (lon > 180) lon = lon - 360; return 0; } }; };