1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  SCH Coordinate system
5  * Purpose:  Implementation of SCH Coordinate system
6  * References :
7  *      1. Hensley. Scott. SCH Coordinates and various transformations. June 15, 2000.
8  *      2. Buckley, Sean Monroe. Radar interferometry measurement of land subsidence. 2000..
9  *         PhD Thesis. UT Austin. (Appendix)
10  *      3. Hensley, Scott, Elaine Chapin, and T. Michel. "Improved processing of AIRSAR
11  *         data based on the GeoSAR processor." Airsar earth science and applications
12  *         workshop, March. 2002. (http://airsar.jpl.nasa.gov/documents/workshop2002/papers/T3.pdf)
13  *
14  * Author:   Piyush Agram (piyush.agram@jpl.nasa.gov)
15  * Copyright (c) 2015 California Institute of Technology.
16  * Government sponsorship acknowledged.
17  *
18  * NOTE:  The SCH coordinate system is a sensor aligned coordinate system
19  * developed at JPL for radar mapping missions. Details pertaining to the
20  * coordinate system have been release in the public domain (see references above).
21  * This code is an independent implementation of the SCH coordinate system
22  * that conforms to the PROJ.4 conventions and uses the details presented in these
23  * publicly released documents. All credit for the development of the coordinate
24  * system and its use should be directed towards the original developers at JPL.
25  ******************************************************************************
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32  * DEALINGS IN THE SOFTWARE.
33  ****************************************************************************/
34 
35 #define PJ_LIB__
36 #include <projects.h>
37 #include "geocent.h"
38 
39 struct pj_opaque {
40     double plat; /*Peg Latitude */
41     double plon; /*Peg Longitude*/
42     double phdg; /*Peg heading  */
43     double h0;   /*Average altitude */
44     double transMat[9];
45     double xyzoff[3];
46     double rcurv;
47     GeocentricInfo sph;
48     GeocentricInfo elp_0;
49 };
50 
51 PROJ_HEAD(sch, "Spherical Cross-track Height") "\n\tMisc\n\tplat_0 = ,plon_0 = , phdg_0 = ,[h_0 = ]";
52 
inverse3d(XYZ xyz,PJ * P)53 static LPZ inverse3d(XYZ xyz, PJ *P) {
54     LPZ lpz = {0.0, 0.0, 0.0};
55     struct pj_opaque *Q = P->opaque;
56     double temp[3];
57     double pxyz[3];
58 
59     /* Local lat,lon using radius */
60     pxyz[0] = xyz.y * P->a / Q->rcurv;
61     pxyz[1] = xyz.x * P->a / Q->rcurv;
62     pxyz[2] = xyz.z;
63 
64     if( pj_Convert_Geodetic_To_Geocentric( &(Q->sph), pxyz[0], pxyz[1], pxyz[2],
65                 temp, temp+1, temp+2) != 0)
66             I3_ERROR;
67 
68     /* Apply rotation */
69     pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[1] * temp[1] + Q->transMat[2] * temp[2];
70     pxyz[1] = Q->transMat[3] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[5] * temp[2];
71     pxyz[2] = Q->transMat[6] * temp[0] + Q->transMat[7] * temp[1] + Q->transMat[8] * temp[2];
72 
73     /* Apply offset */
74     pxyz[0] += Q->xyzoff[0];
75     pxyz[1] += Q->xyzoff[1];
76     pxyz[2] += Q->xyzoff[2];
77 
78     /* Convert geocentric coordinates to lat lon */
79     pj_Convert_Geocentric_To_Geodetic( &(Q->elp_0), pxyz[0], pxyz[1], pxyz[2],
80             temp, temp+1, temp+2);
81 
82 
83     lpz.lam = temp[1] ;
84     lpz.phi = temp[0] ;
85     lpz.z = temp[2];
86 
87 #if 0
88     printf("INVERSE: \n");
89     printf("XYZ: %f %f %f \n", xyz.x, xyz.y, xyz.z);
90     printf("LPZ: %f %f %f \n", lpz.lam, lpz.phi, lpz.z);
91 #endif
92     return lpz;
93 }
94 
forward3d(LPZ lpz,PJ * P)95 static XYZ forward3d(LPZ lpz, PJ *P) {
96     XYZ xyz = {0.0, 0.0, 0.0};
97     struct pj_opaque *Q = P->opaque;
98     double temp[3];
99     double pxyz[3];
100 
101 
102     /* Convert lat lon to geocentric coordinates */
103     if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), lpz.phi, lpz.lam, lpz.z,
104                 temp, temp+1, temp+2 ) != 0 )
105         F3_ERROR;
106 
107 
108     /* Adjust for offset */
109     temp[0] -= Q->xyzoff[0];
110     temp[1] -= Q->xyzoff[1];
111     temp[2] -= Q->xyzoff[2];
112 
113 
114     /* Apply rotation */
115     pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[3] * temp[1] + Q->transMat[6] * temp[2];
116     pxyz[1] = Q->transMat[1] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[7] * temp[2];
117     pxyz[2] = Q->transMat[2] * temp[0] + Q->transMat[5] * temp[1] + Q->transMat[8] * temp[2];
118 
119     /* Convert to local lat,lon */
120     pj_Convert_Geocentric_To_Geodetic( &(Q->sph), pxyz[0], pxyz[1], pxyz[2],
121             temp, temp+1, temp+2);
122 
123 
124     /* Scale by radius */
125     xyz.x = temp[1] * Q->rcurv / P->a;
126     xyz.y = temp[0] * Q->rcurv / P->a;
127     xyz.z = temp[2];
128 
129 #if 0
130     printf("FORWARD: \n");
131     printf("LPZ: %f %f %f \n", lpz.lam, lpz.phi, lpz.z);
132     printf("XYZ: %f %f %f \n", xyz.x, xyz.y, xyz.z);
133 #endif
134     return xyz;
135 }
136 
137 
freeup_new(PJ * P)138 static void *freeup_new (PJ *P) {                       /* Destructor */
139     if (0==P)
140         return 0;
141     if (0==P->opaque)
142         return pj_dealloc (P);
143 
144     pj_dealloc (P->opaque);
145     return pj_dealloc(P);
146 }
147 
freeup(PJ * P)148 static void freeup (PJ *P) {
149     freeup_new (P);
150     return;
151 }
152 
setup(PJ * P)153 static PJ *setup(PJ *P) { /* general initialization */
154     struct pj_opaque *Q = P->opaque;
155     double reast, rnorth;
156     double chdg, shdg;
157     double clt, slt;
158     double clo, slo;
159     double temp;
160     double pxyz[3];
161 
162     temp = P->a * sqrt(1.0 - P->es);
163 
164     /* Setup original geocentric system */
165     if ( pj_Set_Geocentric_Parameters(&(Q->elp_0), P->a, temp) != 0)
166             E_ERROR(-37);
167 
168     clt = cos(Q->plat);
169     slt = sin(Q->plat);
170     clo = cos(Q->plon);
171     slo = sin(Q->plon);
172 
173     /* Estimate the radius of curvature for given peg */
174     temp = sqrt(1.0 - (P->es) * slt * slt);
175     reast = (P->a)/temp;
176     rnorth = (P->a) * (1.0 - (P->es))/pow(temp,3);
177 
178     chdg = cos(Q->phdg);
179     shdg = sin(Q->phdg);
180 
181     Q->rcurv = Q->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg);
182 
183 #if 0
184     printf("North Radius: %f \n", rnorth);
185     printf("East Radius: %f \n", reast);
186     printf("Effective Radius: %f \n", Q->rcurv);
187 #endif
188 
189     /* Set up local sphere at the given peg point */
190     if ( pj_Set_Geocentric_Parameters(&(Q->sph), Q->rcurv, Q->rcurv) != 0)
191         E_ERROR(-37);
192 
193     /* Set up the transformation matrices */
194     Q->transMat[0] = clt * clo;
195     Q->transMat[1] = -shdg*slo - slt*clo * chdg;
196     Q->transMat[2] =  slo*chdg - slt*clo*shdg;
197     Q->transMat[3] =  clt*slo;
198     Q->transMat[4] =  clo*shdg - slt*slo*chdg;
199     Q->transMat[5] = -clo*chdg - slt*slo*shdg;
200     Q->transMat[6] =  slt;
201     Q->transMat[7] =  clt*chdg;
202     Q->transMat[8] =  clt*shdg;
203 
204 
205     if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), Q->plat, Q->plon, Q->h0,
206                                            pxyz, pxyz+1, pxyz+2 ) != 0 )
207     {
208         E_ERROR(-14)
209     }
210 
211 
212     Q->xyzoff[0] = pxyz[0] - (Q->rcurv) * clt * clo;
213     Q->xyzoff[1] = pxyz[1] - (Q->rcurv) * clt * slo;
214     Q->xyzoff[2] = pxyz[2] - (Q->rcurv) * slt;
215 
216 #if 0
217     printf("Offset: %f %f %f \n", Q->xyzoff[0], Q->xyzoff[1], Q->xyzoff[2]);
218 #endif
219 
220     P->fwd3d = forward3d;
221     P->inv3d = inverse3d;
222     return P;
223 }
224 
225 
PROJECTION(sch)226 PJ *PROJECTION(sch) {
227     struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque));
228     if (0==Q)
229         return freeup_new (P);
230     P->opaque = Q;
231 
232     Q->h0 = 0.0;
233 
234     /* Check if peg latitude was defined */
235     if (pj_param(P->ctx, P->params, "tplat_0").i)
236         Q->plat = pj_param(P->ctx, P->params, "rplat_0").f;
237     else
238         E_ERROR(-37);
239 
240     /* Check if peg longitude was defined */
241     if (pj_param(P->ctx, P->params, "tplon_0").i)
242         Q->plon = pj_param(P->ctx, P->params, "rplon_0").f;
243     else
244         E_ERROR(-37);
245 
246     /* Check if peg latitude is defined */
247     if (pj_param(P->ctx, P->params, "tphdg_0").i)
248         Q->phdg = pj_param(P->ctx, P->params, "rphdg_0").f;
249     else
250         E_ERROR(-37);
251 
252 
253     /* Check if average height was defined - If so read it in */
254     if (pj_param(P->ctx, P->params, "th_0").i)
255         Q->h0 = pj_param(P->ctx, P->params, "dh_0").f;
256 
257     /* Completed reading in the projection parameters */
258 #if 0
259     printf("PSA: Lat = %f Lon = %f Hdg = %f \n", Q->plat, Q->plon, Q->phdg);
260 #endif
261 
262     return setup(P);
263 }
264 
265 /* Skipping sef-test since the test system is not capable of handling
266  * 3D coordinate systems for the time being. Relying on tests in ../nad/
267  */
pj_sch_selftest(void)268 int pj_sch_selftest (void) {return 0;}
269