1 /******************************************************************************
2  * Project:  SCH Coordinate system
3  * Purpose:  Implementation of SCH Coordinate system
4  * References :
5  *      1. Hensley. Scott. SCH Coordinates and various transformations. June 15, 2000.
6  *      2. Buckley, Sean Monroe. Radar interferometry measurement of land subsidence. 2000..
7  *         PhD Thesis. UT Austin. (Appendix)
8  *      3. Hensley, Scott, Elaine Chapin, and T. Michel. "Improved processing of AIRSAR
9  *         data based on the GeoSAR processor." Airsar earth science and applications
10  *         workshop, March. 2002. (http://airsar.jpl.nasa.gov/documents/workshop2002/papers/T3.pdf)
11  *
12  * Author:   Piyush Agram (piyush.agram@jpl.nasa.gov)
13  * Copyright (c) 2015 California Institute of Technology.
14  * Government sponsorship acknowledged.
15  *
16  * NOTE:  The SCH coordinate system is a sensor aligned coordinate system
17  * developed at JPL for radar mapping missions. Details pertaining to the
18  * coordinate system have been release in the public domain (see references above).
19  * This code is an independent implementation of the SCH coordinate system
20  * that conforms to the PROJ.4 conventions and uses the details presented in these
21  * publicly released documents. All credit for the development of the coordinate
22  * system and its use should be directed towards the original developers at JPL.
23  ******************************************************************************
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  ****************************************************************************/
32 
33 #define PJ_LIB__
34 
35 #include <errno.h>
36 #include <math.h>
37 
38 #include "proj.h"
39 #include "proj_internal.h"
40 
41 namespace { // anonymous namespace
42 struct pj_opaque {
43     double plat; /*Peg Latitude */
44     double plon; /*Peg Longitude*/
45     double phdg; /*Peg heading  */
46     double h0;   /*Average altitude */
47     double transMat[9];
48     double xyzoff[3];
49     double rcurv;
50     PJ* cart;
51     PJ* cart_sph;
52 };
53 } // anonymous namespace
54 
55 PROJ_HEAD(sch, "Spherical Cross-track Height") "\n\tMisc\n\tplat_0= plon_0= phdg_0= [h_0=]";
56 
sch_inverse3d(PJ_XYZ xyz,PJ * P)57 static PJ_LPZ sch_inverse3d(PJ_XYZ xyz, PJ *P) {
58     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
59 
60     PJ_LPZ lpz;
61     lpz.lam = xyz.x * (P->a / Q->rcurv);
62     lpz.phi = xyz.y * (P->a / Q->rcurv);
63     lpz.z = xyz.z;
64     xyz  =  Q->cart_sph->fwd3d (lpz, Q->cart_sph);
65 
66     /* Apply rotation */
67     xyz = {
68         Q->transMat[0] * xyz.x + Q->transMat[1] * xyz.y + Q->transMat[2] * xyz.z,
69         Q->transMat[3] * xyz.x + Q->transMat[4] * xyz.y + Q->transMat[5] * xyz.z,
70         Q->transMat[6] * xyz.x + Q->transMat[7] * xyz.y + Q->transMat[8] * xyz.z
71     };
72 
73     /* Apply offset */
74     xyz.x += Q->xyzoff[0];
75     xyz.y += Q->xyzoff[1];
76     xyz.z += Q->xyzoff[2];
77 
78     /* Convert geocentric coordinates to lat lon */
79     return Q->cart->inv3d (xyz, Q->cart);
80 }
81 
sch_forward3d(PJ_LPZ lpz,PJ * P)82 static PJ_XYZ sch_forward3d(PJ_LPZ lpz, PJ *P) {
83     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
84 
85     /* Convert lat lon to geocentric coordinates */
86     PJ_XYZ xyz  =  Q->cart->fwd3d (lpz, Q->cart);
87 
88     /* Adjust for offset */
89     xyz.x -= Q->xyzoff[0];
90     xyz.y -= Q->xyzoff[1];
91     xyz.z -= Q->xyzoff[2];
92 
93     /* Apply rotation */
94     xyz = {
95         Q->transMat[0] * xyz.x + Q->transMat[3] * xyz.y + Q->transMat[6] * xyz.z,
96         Q->transMat[1] * xyz.x + Q->transMat[4] * xyz.y + Q->transMat[7] * xyz.z,
97         Q->transMat[2] * xyz.x + Q->transMat[5] * xyz.y + Q->transMat[8] * xyz.z
98     };
99 
100     /* Convert to local lat,lon */
101     lpz  =  Q->cart_sph->inv3d (xyz, Q->cart_sph);
102 
103     /* Scale by radius */
104     xyz.x = lpz.lam * (Q->rcurv / P->a);
105     xyz.y = lpz.phi * (Q->rcurv / P->a);
106     xyz.z = lpz.z;
107 
108     return xyz;
109 }
110 
destructor(PJ * P,int errlev)111 static PJ *destructor (PJ *P, int errlev) {
112     if (nullptr==P)
113         return nullptr;
114 
115     auto Q = static_cast<struct pj_opaque*>(P->opaque);
116     if( Q )
117     {
118         if (Q->cart)
119             Q->cart->destructor (Q->cart, errlev);
120         if (Q->cart_sph)
121             Q->cart_sph->destructor (Q->cart_sph, errlev);
122     }
123 
124     return pj_default_destructor(P, errlev);
125 }
126 
setup(PJ * P)127 static PJ *setup(PJ *P) { /* general initialization */
128     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
129 
130     /* Setup original geocentric system */
131     // Pass a dummy ellipsoid definition that will be overridden just afterwards
132     Q->cart = proj_create(P->ctx, "+proj=cart +a=1");
133     if (Q->cart == nullptr)
134         return destructor(P, ENOMEM);
135 
136     /* inherit ellipsoid definition from P to Q->cart */
137     pj_inherit_ellipsoid_def (P, Q->cart);
138 
139     const double clt = cos(Q->plat);
140     const double slt = sin(Q->plat);
141     const double clo = cos(Q->plon);
142     const double slo = sin(Q->plon);
143 
144     /* Estimate the radius of curvature for given peg */
145     const double temp = sqrt(1.0 - (P->es) * slt * slt);
146     const double reast = (P->a)/temp;
147     const double rnorth = (P->a) * (1.0 - (P->es))/pow(temp,3);
148 
149     const double chdg = cos(Q->phdg);
150     const double shdg = sin(Q->phdg);
151 
152     Q->rcurv = Q->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg);
153 
154     /* Set up local sphere at the given peg point */
155     Q->cart_sph = proj_create(P->ctx, "+proj=cart +a=1");
156     if (Q->cart_sph == nullptr)
157         return destructor(P, ENOMEM);
158     pj_calc_ellipsoid_params(Q->cart_sph, Q->rcurv, 0);
159 
160     /* Set up the transformation matrices */
161     Q->transMat[0] = clt * clo;
162     Q->transMat[1] = -shdg*slo - slt*clo * chdg;
163     Q->transMat[2] =  slo*chdg - slt*clo*shdg;
164     Q->transMat[3] =  clt*slo;
165     Q->transMat[4] =  clo*shdg - slt*slo*chdg;
166     Q->transMat[5] = -clo*chdg - slt*slo*shdg;
167     Q->transMat[6] =  slt;
168     Q->transMat[7] =  clt*chdg;
169     Q->transMat[8] =  clt*shdg;
170 
171     PJ_LPZ lpz;
172     lpz.lam = Q->plon;
173     lpz.phi = Q->plat;
174     lpz.z = Q->h0;
175     PJ_XYZ xyz  =  Q->cart->fwd3d (lpz, Q->cart);
176     Q->xyzoff[0] = xyz.x - (Q->rcurv) * clt * clo;
177     Q->xyzoff[1] = xyz.y- (Q->rcurv) * clt * slo;
178     Q->xyzoff[2] = xyz.z - (Q->rcurv) * slt;
179 
180     P->fwd3d = sch_forward3d;
181     P->inv3d = sch_inverse3d;
182     return P;
183 }
184 
185 
PROJECTION(sch)186 PJ *PROJECTION(sch) {
187     struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
188     if (nullptr==Q)
189         return pj_default_destructor(P, ENOMEM);
190     P->opaque = Q;
191     P->destructor = destructor;
192 
193     Q->h0 = 0.0;
194 
195     /* Check if peg latitude was defined */
196     if (pj_param(P->ctx, P->params, "tplat_0").i)
197         Q->plat = pj_param(P->ctx, P->params, "rplat_0").f;
198     else {
199         return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
200     }
201 
202     /* Check if peg longitude was defined */
203     if (pj_param(P->ctx, P->params, "tplon_0").i)
204         Q->plon = pj_param(P->ctx, P->params, "rplon_0").f;
205     else {
206         return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
207     }
208 
209     /* Check if peg heading is defined */
210     if (pj_param(P->ctx, P->params, "tphdg_0").i)
211         Q->phdg = pj_param(P->ctx, P->params, "rphdg_0").f;
212     else {
213         return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
214     }
215 
216 
217     /* Check if average height was defined - If so read it in */
218     if (pj_param(P->ctx, P->params, "th_0").i)
219         Q->h0 = pj_param(P->ctx, P->params, "dh_0").f;
220 
221 
222     return setup(P);
223 }
224