1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.bodies;
18
19 import java.io.Serializable;
20
21 import org.hipparchus.CalculusFieldElement;
22 import org.hipparchus.Field;
23 import org.hipparchus.analysis.differentiation.DerivativeStructure;
24 import org.hipparchus.geometry.euclidean.threed.FieldLine;
25 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26 import org.hipparchus.geometry.euclidean.threed.Line;
27 import org.hipparchus.geometry.euclidean.threed.Vector3D;
28 import org.hipparchus.geometry.euclidean.twod.Vector2D;
29 import org.hipparchus.util.FastMath;
30 import org.hipparchus.util.FieldSinCos;
31 import org.hipparchus.util.MathArrays;
32 import org.hipparchus.util.MathUtils;
33 import org.hipparchus.util.SinCos;
34 import org.orekit.frames.FieldTransform;
35 import org.orekit.frames.Frame;
36 import org.orekit.frames.StaticTransform;
37 import org.orekit.frames.Transform;
38 import org.orekit.time.AbsoluteDate;
39 import org.orekit.time.FieldAbsoluteDate;
40 import org.orekit.utils.PVCoordinates;
41 import org.orekit.utils.TimeStampedPVCoordinates;
42
43
44
45
46
47
48
49
50
51
52
53
54 public class OneAxisEllipsoid extends Ellipsoid implements BodyShape {
55
56
57 private static final long serialVersionUID = 20130518L;
58
59
60 private static final double ANGULAR_THRESHOLD = 1.0e-4;
61
62
63 private final Frame bodyFrame;
64
65
66 private final double ae2;
67
68
69 private final double ap2;
70
71
72 private final double f;
73
74
75 private final double e;
76
77
78 private final double e2;
79
80
81 private final double g;
82
83
84 private final double g2;
85
86
87 private double angularThreshold;
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public OneAxisEllipsoid(final double ae, final double f,
116 final Frame bodyFrame) {
117 super(bodyFrame, ae, ae, ae * (1.0 - f));
118 this.f = f;
119 this.ae2 = ae * ae;
120 this.e2 = f * (2.0 - f);
121 this.e = FastMath.sqrt(e2);
122 this.g = 1.0 - f;
123 this.g2 = g * g;
124 this.ap2 = ae2 * g2;
125 setAngularThreshold(1.0e-12);
126 this.bodyFrame = bodyFrame;
127 }
128
129
130
131
132
133
134
135
136
137
138 public void setAngularThreshold(final double angularThreshold) {
139 this.angularThreshold = angularThreshold;
140 }
141
142
143
144
145 public double getEquatorialRadius() {
146 return getA();
147 }
148
149
150
151
152 public double getFlattening() {
153 return f;
154 }
155
156
157
158
159 public double getEccentricitySquared() {
160 return e2;
161 }
162
163
164
165
166 public double getEccentricity() {
167 return e;
168 }
169
170
171 public Frame getBodyFrame() {
172 return bodyFrame;
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 public Vector3D getCartesianIntersectionPoint(final Line line, final Vector3D close,
190 final Frame frame, final AbsoluteDate date) {
191
192
193 final StaticTransform frameToBodyFrame =
194 frame.getStaticTransformTo(bodyFrame, date);
195 final Line lineInBodyFrame = frameToBodyFrame.transformLine(line);
196
197
198 final Vector3D point = lineInBodyFrame.getOrigin();
199 final double x = point.getX();
200 final double y = point.getY();
201 final double z = point.getZ();
202 final double z2 = z * z;
203 final double r2 = x * x + y * y;
204
205 final Vector3D direction = lineInBodyFrame.getDirection();
206 final double dx = direction.getX();
207 final double dy = direction.getY();
208 final double dz = direction.getZ();
209 final double cz2 = dx * dx + dy * dy;
210
211
212
213 final double a = 1.0 - e2 * cz2;
214 final double b = -(g2 * (x * dx + y * dy) + z * dz);
215 final double c = g2 * (r2 - ae2) + z2;
216 final double b2 = b * b;
217 final double ac = a * c;
218 if (b2 < ac) {
219 return null;
220 }
221 final double s = FastMath.sqrt(b2 - ac);
222 final double k1 = (b < 0) ? (b - s) / a : c / (b + s);
223 final double k2 = c / (a * k1);
224
225
226 final Vector3D closeInBodyFrame = frameToBodyFrame.transformPosition(close);
227 final double closeAbscissa = lineInBodyFrame.getAbscissa(closeInBodyFrame);
228 final double k =
229 (FastMath.abs(k1 - closeAbscissa) < FastMath.abs(k2 - closeAbscissa)) ? k1 : k2;
230 return lineInBodyFrame.pointAt(k);
231
232 }
233
234
235 public GeodeticPoint getIntersectionPoint(final Line line, final Vector3D close,
236 final Frame frame, final AbsoluteDate date) {
237
238 final Vector3D intersection = getCartesianIntersectionPoint(line, close, frame, date);
239 if (intersection == null) {
240 return null;
241 }
242 final double ix = intersection.getX();
243 final double iy = intersection.getY();
244 final double iz = intersection.getZ();
245
246 final double lambda = FastMath.atan2(iy, ix);
247 final double phi = FastMath.atan2(iz, g2 * FastMath.sqrt(ix * ix + iy * iy));
248 return new GeodeticPoint(phi, lambda, 0.0);
249
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 public <T extends CalculusFieldElement<T>> FieldVector3D<T> getCartesianIntersectionPoint(final FieldLine<T> line,
268 final FieldVector3D<T> close,
269 final Frame frame,
270 final FieldAbsoluteDate<T> date) {
271
272
273 final FieldTransform<T> frameToBodyFrame = frame.getTransformTo(bodyFrame, date);
274 final FieldLine<T> lineInBodyFrame = frameToBodyFrame.transformLine(line);
275
276
277 final FieldVector3D<T> point = lineInBodyFrame.getOrigin();
278 final T x = point.getX();
279 final T y = point.getY();
280 final T z = point.getZ();
281 final T z2 = z.multiply(z);
282 final T r2 = x.multiply(x).add(y.multiply(y));
283
284 final FieldVector3D<T> direction = lineInBodyFrame.getDirection();
285 final T dx = direction.getX();
286 final T dy = direction.getY();
287 final T dz = direction.getZ();
288 final T cz2 = dx.multiply(dx).add(dy.multiply(dy));
289
290
291
292 final T a = cz2.multiply(e2).subtract(1.0).negate();
293 final T b = x.multiply(dx).add(y.multiply(dy)).multiply(g2).add(z.multiply(dz)).negate();
294 final T c = r2.subtract(ae2).multiply(g2).add(z2);
295 final T b2 = b.multiply(b);
296 final T ac = a.multiply(c);
297 if (b2.getReal() < ac.getReal()) {
298 return null;
299 }
300 final T s = b2.subtract(ac).sqrt();
301 final T k1 = (b.getReal() < 0) ? b.subtract(s).divide(a) : c.divide(b.add(s));
302 final T k2 = c.divide(a.multiply(k1));
303
304
305 final FieldVector3D<T> closeInBodyFrame = frameToBodyFrame.transformPosition(close);
306 final T closeAbscissa = lineInBodyFrame.getAbscissa(closeInBodyFrame);
307 final T k = (FastMath.abs(k1.getReal() - closeAbscissa.getReal()) < FastMath.abs(k2.getReal() - closeAbscissa.getReal())) ?
308 k1 : k2;
309 return lineInBodyFrame.pointAt(k);
310 }
311
312
313 public <T extends CalculusFieldElement<T>> FieldGeodeticPoint<T> getIntersectionPoint(final FieldLine<T> line,
314 final FieldVector3D<T> close,
315 final Frame frame,
316 final FieldAbsoluteDate<T> date) {
317
318 final FieldVector3D<T> intersection = getCartesianIntersectionPoint(line, close, frame, date);
319 if (intersection == null) {
320 return null;
321 }
322 final T ix = intersection.getX();
323 final T iy = intersection.getY();
324 final T iz = intersection.getZ();
325
326 final T lambda = iy.atan2(ix);
327 final T phi = iz.atan2(ix.multiply(ix).add(iy.multiply(iy)).sqrt().multiply(g2));
328 return new FieldGeodeticPoint<>(phi, lambda, phi.getField().getZero());
329
330 }
331
332
333 public Vector3D transform(final GeodeticPoint point) {
334 final double longitude = point.getLongitude();
335 final SinCos scLambda = FastMath.sinCos(longitude);
336 final double latitude = point.getLatitude();
337 final SinCos scPhi = FastMath.sinCos(latitude);
338 final double h = point.getAltitude();
339 final double n = getA() / FastMath.sqrt(1.0 - e2 * scPhi.sin() * scPhi.sin());
340 final double r = (n + h) * scPhi.cos();
341 return new Vector3D(r * scLambda.cos(), r * scLambda.sin(), (g2 * n + h) * scPhi.sin());
342 }
343
344
345 public <T extends CalculusFieldElement<T>> FieldVector3D<T> transform(final FieldGeodeticPoint<T> point) {
346
347 final T latitude = point.getLatitude();
348 final T longitude = point.getLongitude();
349 final T altitude = point.getAltitude();
350
351 final FieldSinCos<T> scLambda = FastMath.sinCos(longitude);
352 final FieldSinCos<T> scPhi = FastMath.sinCos(latitude);
353 final T cLambda = scLambda.cos();
354 final T sLambda = scLambda.sin();
355 final T cPhi = scPhi.cos();
356 final T sPhi = scPhi.sin();
357 final T n = sPhi.multiply(sPhi).multiply(e2).subtract(1.0).negate().sqrt().reciprocal().multiply(getA());
358 final T r = n.add(altitude).multiply(cPhi);
359
360 return new FieldVector3D<>(r.multiply(cLambda),
361 r.multiply(sLambda),
362 sPhi.multiply(altitude.add(n.multiply(g2))));
363 }
364
365
366 public Vector3D projectToGround(final Vector3D point, final AbsoluteDate date, final Frame frame) {
367
368
369 final StaticTransform toBody = frame.getStaticTransformTo(bodyFrame, date);
370 final Vector3D p = toBody.transformPosition(point);
371 final double z = p.getZ();
372 final double r = FastMath.hypot(p.getX(), p.getY());
373
374
375 final Ellipse meridian = new Ellipse(Vector3D.ZERO,
376 r == 0 ? Vector3D.PLUS_I : new Vector3D(p.getX() / r, p.getY() / r, 0),
377 Vector3D.PLUS_K,
378 getA(), getC(), bodyFrame);
379
380
381 final Vector3D groundPoint = meridian.toSpace(meridian.projectToEllipse(new Vector2D(r, z)));
382
383
384 return toBody.getInverse().transformPosition(groundPoint);
385
386 }
387
388
389 public TimeStampedPVCoordinates projectToGround(final TimeStampedPVCoordinates pv, final Frame frame) {
390
391
392 final Transform toBody = frame.getTransformTo(bodyFrame, pv.getDate());
393 final TimeStampedPVCoordinates pvInBodyFrame = toBody.transformPVCoordinates(pv);
394 final Vector3D p = pvInBodyFrame.getPosition();
395 final double r = FastMath.hypot(p.getX(), p.getY());
396
397
398 final Vector3D meridian = r == 0 ? Vector3D.PLUS_I : new Vector3D(p.getX() / r, p.getY() / r, 0);
399 final Ellipse firstPrincipalCurvature =
400 new Ellipse(Vector3D.ZERO, meridian, Vector3D.PLUS_K, getA(), getC(), bodyFrame);
401
402
403 final TimeStampedPVCoordinates gpFirst = firstPrincipalCurvature.projectToEllipse(pvInBodyFrame);
404 final Vector3D gpP = gpFirst.getPosition();
405 final double gr = MathArrays.linearCombination(gpP.getX(), meridian.getX(),
406 gpP.getY(), meridian.getY());
407 final double gz = gpP.getZ();
408
409
410 final Vector3D east = new Vector3D(-meridian.getY(), meridian.getX(), 0);
411 final Vector3D zenith = new Vector3D(gr * getC() / getA(), meridian, gz * getA() / getC(), Vector3D.PLUS_K).normalize();
412 final Vector3D north = Vector3D.crossProduct(zenith, east);
413
414
415 final Ellipse secondPrincipalCurvature = getPlaneSection(gpP, north);
416 final TimeStampedPVCoordinates gpSecond = secondPrincipalCurvature.projectToEllipse(pvInBodyFrame);
417
418 final Vector3D gpV = gpFirst.getVelocity().add(gpSecond.getVelocity());
419 final Vector3D gpA = gpFirst.getAcceleration().add(gpSecond.getAcceleration());
420
421
422 final TimeStampedPVCoordinates groundPV =
423 new TimeStampedPVCoordinates(pv.getDate(), gpP, gpV, gpA);
424
425
426 return toBody.getInverse().transformPVCoordinates(groundPV);
427
428 }
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445 public GeodeticPoint transform(final Vector3D point, final Frame frame, final AbsoluteDate date) {
446
447
448 final Vector3D pointInBodyFrame = frame.getStaticTransformTo(bodyFrame, date)
449 .transformPosition(point);
450 final double r2 = pointInBodyFrame.getX() * pointInBodyFrame.getX() +
451 pointInBodyFrame.getY() * pointInBodyFrame.getY();
452 final double r = FastMath.sqrt(r2);
453 final double z = pointInBodyFrame.getZ();
454
455 final double lambda = FastMath.atan2(pointInBodyFrame.getY(), pointInBodyFrame.getX());
456
457 double h;
458 double phi;
459 if (r <= ANGULAR_THRESHOLD * FastMath.abs(z)) {
460
461
462 final double osculatingRadius = ae2 / getC();
463 final double evoluteCuspZ = FastMath.copySign(getA() * e2 / g, -z);
464 final double deltaZ = z - evoluteCuspZ;
465
466 phi = FastMath.copySign(0.5 * FastMath.PI - FastMath.atan(r / FastMath.abs(deltaZ)), deltaZ);
467 h = FastMath.hypot(deltaZ, r) - osculatingRadius;
468 } else if (FastMath.abs(z) <= ANGULAR_THRESHOLD * r) {
469
470
471 final double osculatingRadius = ap2 / getA();
472 final double evoluteCuspR = getA() * e2;
473 final double deltaR = r - evoluteCuspR;
474 if (deltaR >= 0) {
475
476
477 phi = (deltaR == 0) ? 0.0 : FastMath.atan(z / deltaR);
478 h = FastMath.hypot(deltaR, z) - osculatingRadius;
479 } else {
480
481
482 final double rClose = r / e2;
483 final double zClose = FastMath.copySign(g * FastMath.sqrt(ae2 - rClose * rClose), z);
484 phi = FastMath.atan((zClose - z) / (rClose - r));
485 h = -FastMath.hypot(r - rClose, z - zClose);
486 }
487
488 } else {
489
490 final double epsPhi = 1.0e-15;
491 final double epsH = 1.0e-14 * FastMath.max(getA(), FastMath.sqrt(r2 + z * z));
492 final double c = getA() * e2;
493 final double absZ = FastMath.abs(z);
494 final double zc = g * absZ;
495 double sn = absZ;
496 double sn2 = sn * sn;
497 double cn = g * r;
498 double cn2 = cn * cn;
499 double an2 = cn2 + sn2;
500 double an = FastMath.sqrt(an2);
501 double bn = 0;
502 phi = Double.POSITIVE_INFINITY;
503 h = Double.POSITIVE_INFINITY;
504 for (int i = 0; i < 10; ++i) {
505 final double oldSn = sn;
506 final double oldCn = cn;
507 final double oldPhi = phi;
508 final double oldH = h;
509 final double an3 = an2 * an;
510 final double csncn = c * sn * cn;
511 bn = 1.5 * csncn * ((r * sn - zc * cn) * an - csncn);
512 sn = (zc * an3 + c * sn2 * sn) * an3 - bn * sn;
513 cn = (r * an3 - c * cn2 * cn) * an3 - bn * cn;
514 if (sn * oldSn < 0 || cn < 0) {
515
516 while (sn * oldSn < 0 || cn < 0) {
517 sn = (sn + oldSn) / 2;
518 cn = (cn + oldCn) / 2;
519 }
520 } else {
521
522
523 final int exp = (FastMath.getExponent(sn) + FastMath.getExponent(cn)) / 2;
524 sn = FastMath.scalb(sn, -exp);
525 cn = FastMath.scalb(cn, -exp);
526
527 sn2 = sn * sn;
528 cn2 = cn * cn;
529 an2 = cn2 + sn2;
530 an = FastMath.sqrt(an2);
531
532 final double cc = g * cn;
533 h = (r * cc + absZ * sn - getA() * g * an) / FastMath.sqrt(an2 - e2 * cn2);
534 if (FastMath.abs(oldH - h) < epsH) {
535 phi = FastMath.copySign(FastMath.atan(sn / cc), z);
536 if (FastMath.abs(oldPhi - phi) < epsPhi) {
537 break;
538 }
539 }
540
541 }
542
543 }
544 }
545
546 return new GeodeticPoint(phi, lambda, h);
547
548 }
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565 public <T extends CalculusFieldElement<T>> FieldGeodeticPoint<T> transform(final FieldVector3D<T> point,
566 final Frame frame,
567 final FieldAbsoluteDate<T> date) {
568
569
570 final FieldVector3D<T> pointInBodyFrame = frame.getTransformTo(bodyFrame, date).transformPosition(point);
571 final T r2 = pointInBodyFrame.getX().multiply(pointInBodyFrame.getX()).
572 add(pointInBodyFrame.getY().multiply(pointInBodyFrame.getY()));
573 final T r = r2.sqrt();
574 final T z = pointInBodyFrame.getZ();
575
576 final T lambda = pointInBodyFrame.getY().atan2(pointInBodyFrame.getX());
577
578 T h;
579 T phi;
580 if (r.getReal() <= ANGULAR_THRESHOLD * FastMath.abs(z.getReal())) {
581
582
583 final double osculatingRadius = ae2 / getC();
584 final double evoluteCuspZ = FastMath.copySign(getA() * e2 / g, -z.getReal());
585 final T deltaZ = z.subtract(evoluteCuspZ);
586
587 phi = r.divide(deltaZ.abs()).atan().negate().add(r.getPi().multiply(0.5)).copySign(deltaZ);
588 h = deltaZ.hypot(r).subtract(osculatingRadius);
589 } else if (FastMath.abs(z.getReal()) <= ANGULAR_THRESHOLD * r.getReal()) {
590
591
592 final double osculatingRadius = ap2 / getA();
593 final double evoluteCuspR = getA() * e2;
594 final T deltaR = r.subtract(evoluteCuspR);
595 if (deltaR.getReal() >= 0) {
596
597
598 phi = (deltaR.getReal() == 0) ? z.getField().getZero() : z.divide(deltaR).atan();
599 h = deltaR.hypot(z).subtract(osculatingRadius);
600 } else {
601
602
603 final T rClose = r.divide(e2);
604 final T zClose = rClose.multiply(rClose).negate().add(ae2).sqrt().multiply(g).copySign(z);
605 phi = zClose.subtract(z).divide(rClose.subtract(r)).atan();
606 h = r.subtract(rClose).hypot(z.subtract(zClose)).negate();
607 }
608
609 } else {
610
611 final double epsPhi = 1.0e-15;
612 final double epsH = 1.0e-14 * getA();
613 final double c = getA() * e2;
614 final T absZ = z.abs();
615 final T zc = absZ.multiply(g);
616 T sn = absZ;
617 T sn2 = sn.multiply(sn);
618 T cn = r.multiply(g);
619 T cn2 = cn.multiply(cn);
620 T an2 = cn2.add(sn2);
621 T an = an2.sqrt();
622 T bn = an.getField().getZero();
623 phi = an.getField().getZero().add(Double.POSITIVE_INFINITY);
624 h = an.getField().getZero().add(Double.POSITIVE_INFINITY);
625 for (int i = 0; i < 10; ++i) {
626 final T oldSn = sn;
627 final T oldCn = cn;
628 final T oldPhi = phi;
629 final T oldH = h;
630 final T an3 = an2.multiply(an);
631 final T csncn = sn.multiply(cn).multiply(c);
632 bn = csncn.multiply(1.5).multiply((r.multiply(sn).subtract(zc.multiply(cn))).multiply(an).subtract(csncn));
633 sn = zc.multiply(an3).add(sn2.multiply(sn).multiply(c)).multiply(an3).subtract(bn.multiply(sn));
634 cn = r.multiply(an3).subtract(cn2.multiply(cn).multiply(c)).multiply(an3).subtract(bn.multiply(cn));
635 if (sn.getReal() * oldSn.getReal() < 0 || cn.getReal() < 0) {
636
637 while (sn.getReal() * oldSn.getReal() < 0 || cn.getReal() < 0) {
638 sn = sn.add(oldSn).multiply(0.5);
639 cn = cn.add(oldCn).multiply(0.5);
640 }
641 } else {
642
643
644 final int exp = (FastMath.getExponent(sn.getReal()) + FastMath.getExponent(cn.getReal())) / 2;
645 sn = sn.scalb(-exp);
646 cn = cn.scalb(-exp);
647
648 sn2 = sn.multiply(sn);
649 cn2 = cn.multiply(cn);
650 an2 = cn2.add(sn2);
651 an = an2.sqrt();
652
653 final T cc = cn.multiply(g);
654 h = r.multiply(cc).add(absZ.multiply(sn)).subtract(an.multiply(getA() * g)).divide(an2.subtract(cn2.multiply(e2)).sqrt());
655 if (FastMath.abs(oldH.getReal() - h.getReal()) < epsH) {
656 phi = sn.divide(cc).atan().copySign(z);
657 if (FastMath.abs(oldPhi.getReal() - phi.getReal()) < epsPhi) {
658 break;
659 }
660 }
661
662 }
663
664 }
665 }
666
667 return new FieldGeodeticPoint<>(phi, lambda, h);
668
669 }
670
671
672
673
674
675
676
677
678 public FieldGeodeticPoint<DerivativeStructure> transform(final PVCoordinates point,
679 final Frame frame, final AbsoluteDate date) {
680
681
682 final Transform toBody = frame.getTransformTo(bodyFrame, date);
683 final PVCoordinates pointInBodyFrame = toBody.transformPVCoordinates(point);
684 final FieldVector3D<DerivativeStructure> p = pointInBodyFrame.toDerivativeStructureVector(2);
685 final DerivativeStructure pr2 = p.getX().multiply(p.getX()).add(p.getY().multiply(p.getY()));
686 final DerivativeStructure pr = pr2.sqrt();
687 final DerivativeStructure pz = p.getZ();
688
689
690 final TimeStampedPVCoordinates groundPoint = projectToGround(new TimeStampedPVCoordinates(date, pointInBodyFrame),
691 bodyFrame);
692 final FieldVector3D<DerivativeStructure> gp = groundPoint.toDerivativeStructureVector(2);
693 final DerivativeStructure gpr2 = gp.getX().multiply(gp.getX()).add(gp.getY().multiply(gp.getY()));
694 final DerivativeStructure gpr = gpr2.sqrt();
695 final DerivativeStructure gpz = gp.getZ();
696
697
698 final DerivativeStructure dr = pr.subtract(gpr);
699 final DerivativeStructure dz = pz.subtract(gpz);
700 final double insideIfNegative = g2 * (pr2.getReal() - ae2) + pz.getReal() * pz.getReal();
701
702 return new FieldGeodeticPoint<>(DerivativeStructure.atan2(gpz, gpr.multiply(g2)),
703 DerivativeStructure.atan2(p.getY(), p.getX()),
704 DerivativeStructure.hypot(dr, dz).copySign(insideIfNegative));
705 }
706
707
708
709
710
711
712
713
714
715
716
717 public double azimuthBetweenPoints(final GeodeticPoint origin, final GeodeticPoint destination) {
718 final double dLon = MathUtils.normalizeAngle(destination.getLongitude(), origin.getLongitude()) - origin.getLongitude();
719 final double originIsoLat = geodeticToIsometricLatitude(origin.getLatitude());
720 final double destIsoLat = geodeticToIsometricLatitude(destination.getLatitude());
721
722 final double az = FastMath.atan2(dLon, destIsoLat - originIsoLat);
723 if (az < 0.) {
724 return az + MathUtils.TWO_PI;
725 }
726 return az;
727 }
728
729
730
731
732
733
734
735
736
737
738
739
740 public <T extends CalculusFieldElement<T>> T azimuthBetweenPoints(final FieldGeodeticPoint<T> origin, final FieldGeodeticPoint<T> destination) {
741 final T dLon = MathUtils.normalizeAngle(destination.getLongitude().subtract(origin.getLongitude()), origin.getLongitude().getField().getZero());
742 final T originIsoLat = geodeticToIsometricLatitude(origin.getLatitude());
743 final T destIsoLat = geodeticToIsometricLatitude(destination.getLatitude());
744
745 final T az = FastMath.atan2(dLon, destIsoLat.subtract(originIsoLat));
746 if (az.getReal() < 0.) {
747 return az.add(az.getPi().multiply(2));
748 }
749 return az;
750 }
751
752
753
754
755
756
757
758
759 public double geodeticToIsometricLatitude(final double geodeticLatitude) {
760 if (FastMath.abs(geodeticLatitude) <= angularThreshold) {
761 return 0.;
762 }
763
764 final double eSinLat = e * FastMath.sin(geodeticLatitude);
765
766
767 final double a = FastMath.log(FastMath.tan(FastMath.PI / 4. + geodeticLatitude / 2.));
768
769 final double b = (e / 2.) * FastMath.log((1. - eSinLat) / (1. + eSinLat));
770
771 return a + b;
772 }
773
774
775
776
777
778
779
780
781
782 public <T extends CalculusFieldElement<T>> T geodeticToIsometricLatitude(final T geodeticLatitude) {
783 if (geodeticLatitude.abs().getReal() <= angularThreshold) {
784 return geodeticLatitude.getField().getZero();
785 }
786 final Field<T> field = geodeticLatitude.getField();
787 final T ecc = geodeticLatitude.newInstance(e);
788 final T eSinLat = ecc.multiply(geodeticLatitude.sin());
789
790
791 final T a = FastMath.log(FastMath.tan(geodeticLatitude.getPi().divide(4.).add(geodeticLatitude.divide(2.))));
792
793 final T b = ecc.divide(2.).multiply(FastMath.log(field.getOne().subtract(eSinLat).divide(field.getOne().add(eSinLat))));
794
795 return a.add(b);
796 }
797
798
799
800
801
802
803
804
805 private Object writeReplace() {
806 return new DataTransferObject(getA(), f, bodyFrame, angularThreshold);
807 }
808
809
810 private static class DataTransferObject implements Serializable {
811
812
813 private static final long serialVersionUID = 20130518L;
814
815
816 private final double ae;
817
818
819 private final double f;
820
821
822 private final Frame bodyFrame;
823
824
825 private final double angularThreshold;
826
827
828
829
830
831
832
833 DataTransferObject(final double ae, final double f,
834 final Frame bodyFrame, final double angularThreshold) {
835 this.ae = ae;
836 this.f = f;
837 this.bodyFrame = bodyFrame;
838 this.angularThreshold = angularThreshold;
839 }
840
841
842
843
844
845 private Object readResolve() {
846 final OneAxisEllipsoid ellipsoid = new OneAxisEllipsoid(ae, f, bodyFrame);
847 ellipsoid.setAngularThreshold(angularThreshold);
848 return ellipsoid;
849 }
850
851 }
852
853 }