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