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