2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
7 package gov
.nasa
.worldwind
.globes
;
9 import gov
.nasa
.worldwind
.WWObjectImpl
;
10 import gov
.nasa
.worldwind
.geom
.*;
11 import gov
.nasa
.worldwind
.util
.Logging
;
15 * @version $Id: EllipsoidalGlobe.java 2835 2007-09-13 19:29:22Z dcollins $
17 public class EllipsoidalGlobe
extends WWObjectImpl
implements Globe
19 private final double equatorialRadius
;
20 private final double polarRadius
;
21 private final double es
;
22 private final Vec4 center
;
24 private final ElevationModel elevationModel
;
26 public EllipsoidalGlobe(double equatorialRadius
, double polarRadius
, double es
, ElevationModel em
)
30 String msg
= Logging
.getMessage("nullValue.ElevationModelIsNull");
31 Logging
.logger().severe(msg
);
32 throw new IllegalArgumentException(msg
);
35 this.equatorialRadius
= equatorialRadius
;
36 this.polarRadius
= polarRadius
;
37 this.es
= es
; // assume it's consistent with the two radii
38 this.center
= Vec4
.ZERO
;
39 this.elevationModel
= em
;
42 public final double getRadius()
44 return this.equatorialRadius
;
47 public final double getEquatorialRadius()
49 return this.equatorialRadius
;
52 public final double getPolarRadius()
54 return this.polarRadius
;
57 public double getMaximumRadius()
59 return this.equatorialRadius
;
62 public double getRadiusAt(Angle latitude
, Angle longitude
)
64 if (latitude
== null || longitude
== null)
66 String msg
= Logging
.getMessage("nullValue.AngleIsNull");
67 Logging
.logger().severe(msg
);
68 throw new IllegalArgumentException(msg
);
71 return this.computePointFromPosition(latitude
, longitude
, 0d
).getLength3();
74 public double getRadiusAt(LatLon latLon
)
78 String msg
= Logging
.getMessage("nullValue.LatLonIsNull");
79 Logging
.logger().severe(msg
);
80 throw new IllegalArgumentException(msg
);
83 return this.computePointFromPosition(latLon
.getLatitude(), latLon
.getLongitude(), 0d
).getLength3();
86 public double getEccentricitySquared()
91 public final double getDiameter()
93 return this.equatorialRadius
* 2;
96 public final Vec4
getCenter()
101 public double getMaxElevation()
103 return this.elevationModel
.getMaxElevation();
106 public double getMinElevation()
108 return this.elevationModel
.getMinElevation();
111 public double getMaxElevation(Sector sector
)
113 return this.elevationModel
.getMaxElevation(sector
);
116 public double getMinElevation(Sector sector
)
118 return this.elevationModel
.getMinElevation(sector
);
121 public final Extent
getExtent()
126 public boolean intersects(Frustum frustum
)
130 String message
= Logging
.getMessage("nullValue.FrustumIsNull");
131 Logging
.logger().severe(message
);
132 throw new IllegalArgumentException(message
);
135 return frustum
.intersects(this);
138 public Intersection
[] intersect(Line line
)
140 return this.intersect(line
, this.equatorialRadius
, this.polarRadius
);
143 public Intersection
[] intersect(Line line
, double altitude
)
145 return this.intersect(line
, this.equatorialRadius
+ altitude
, this.polarRadius
+ altitude
);
148 private Intersection
[] intersect(Line line
, double equRadius
, double polRadius
)
152 String message
= Logging
.getMessage("nullValue.LineIsNull");
153 Logging
.logger().severe(message
);
154 throw new IllegalArgumentException(message
);
157 // Taken from Lengyel, 2Ed., Section 5.2.3, page 148.
159 double m
= equRadius
/ polRadius
; // "ratio of the x semi-axis length to the y semi-axis length"
160 double n
= 1d
; // "ratio of the x semi-axis length to the z semi-axis length"
163 double r2
= equRadius
* equRadius
; // nominal radius squared //equRadius * polRadius;
165 double vx
= line
.getDirection().x
;
166 double vy
= line
.getDirection().y
;
167 double vz
= line
.getDirection().z
;
168 double sx
= line
.getOrigin().x
;
169 double sy
= line
.getOrigin().y
;
170 double sz
= line
.getOrigin().z
;
172 double a
= vx
* vx
+ m2
* vy
* vy
+ n2
* vz
* vz
;
173 double b
= 2d
* (sx
* vx
+ m2
* sy
* vy
+ n2
* sz
* vz
);
174 double c
= sx
* sx
+ m2
* sy
* sy
+ n2
* sz
* sz
- r2
;
176 double discriminant
= discriminant(a
, b
, c
);
177 if (discriminant
< 0)
180 double discriminantRoot
= Math
.sqrt(discriminant
);
181 if (discriminant
== 0)
183 Vec4 p
= line
.getPointAt((-b
- discriminantRoot
) / (2 * a
));
184 return new Intersection
[] {new Intersection(p
, true)};
186 else // (discriminant > 0)
188 Vec4 near
= line
.getPointAt((-b
- discriminantRoot
) / (2 * a
));
189 Vec4 far
= line
.getPointAt((-b
+ discriminantRoot
) / (2 * a
));
190 return new Intersection
[] {new Intersection(near
, false), new Intersection(far
, false)};
194 static private double discriminant(double a
, double b
, double c
)
196 return b
* b
- 4 * a
* c
;
199 public boolean intersects(Line line
)
203 String msg
= Logging
.getMessage("nullValue.LineIsNull");
204 Logging
.logger().severe(msg
);
205 throw new IllegalArgumentException(msg
);
208 return line
.distanceTo(this.center
) <= this.equatorialRadius
;
211 public boolean intersects(Plane plane
)
215 String msg
= Logging
.getMessage("nullValue.PlaneIsNull");
216 Logging
.logger().severe(msg
);
217 throw new IllegalArgumentException(msg
);
220 double dq1
= plane
.dot(this.center
);
221 return dq1
<= this.equatorialRadius
;
224 public Vec4
computeSurfaceNormalAtPoint(Vec4 p
)
228 String msg
= Logging
.getMessage("nullValue.PointIsNull");
229 Logging
.logger().severe(msg
);
230 throw new IllegalArgumentException(msg
);
233 p
= p
.subtract3(this.center
);
236 p
.x
/ (this.equatorialRadius
* this.equatorialRadius
),
237 p
.y
/ (this.polarRadius
* this.polarRadius
),
238 p
.z
/ (this.equatorialRadius
* this.equatorialRadius
)).normalize3();
241 public final ElevationModel
getElevationModel()
243 return this.elevationModel
;
246 public final double getElevation(Angle latitude
, Angle longitude
)
248 if (latitude
== null || longitude
== null)
250 String message
= Logging
.getMessage("nullValue.LatitudeOrLongitudeIsNull");
251 Logging
.logger().severe(message
);
252 throw new IllegalArgumentException(message
);
255 return this.elevationModel
!= null ?
this.elevationModel
.getElevation(latitude
, longitude
) : 0;
258 public final Vec4
computePointFromPosition(Position position
)
260 if (position
== null)
262 String message
= Logging
.getMessage("nullValue.PositionIsNull");
263 Logging
.logger().severe(message
);
264 throw new IllegalArgumentException(message
);
267 return this.geodeticToCartesian(position
.getLatitude(), position
.getLongitude(), position
.getElevation());
270 public final Vec4
computePointFromPosition(Angle latitude
, Angle longitude
, double metersElevation
)
272 if (latitude
== null || longitude
== null)
274 String message
= Logging
.getMessage("nullValue.LatitudeOrLongitudeIsNull");
275 Logging
.logger().severe(message
);
276 throw new IllegalArgumentException(message
);
279 return this.geodeticToCartesian(latitude
, longitude
, metersElevation
);
282 public final Position
computePositionFromPoint(Vec4 point
)
286 String message
= Logging
.getMessage("nullValue.PointIsNull");
287 Logging
.logger().severe(message
);
288 throw new IllegalArgumentException(message
);
291 return this.cartesianToGeodetic(point
);
294 public final Position
getIntersectionPosition(Line line
)
298 String msg
= Logging
.getMessage("nullValue.LineIsNull");
299 Logging
.logger().severe(msg
);
300 throw new IllegalArgumentException(msg
);
303 Intersection
[] intersections
= this.intersect(line
);
304 if (intersections
== null)
307 return this.computePositionFromPoint(intersections
[0].getIntersectionPoint());
310 // The code below maps latitude / longitude position to globe-centered Cartesian coordinates.
311 // The Y axis points to the north pole. The Z axis points to the intersection of the prime
312 // meridian and the equator, in the equatorial plane. The X axis completes a right-handed
313 // coordinate system, and is 90 degrees east of the Z axis and also in the equatorial plane.
315 private Vec4
geodeticToCartesian(Angle latitude
, Angle longitude
, double metersElevation
)
317 if (latitude
== null || longitude
== null)
319 String message
= Logging
.getMessage("nullValue.LatitudeOrLongitudeIsNull");
320 Logging
.logger().severe(message
);
321 throw new IllegalArgumentException(message
);
324 double cosLat
= latitude
.cos();
325 double sinLat
= latitude
.sin();
327 double rpm
= // getRadius (in meters) of vertical in prime meridian
328 this.equatorialRadius
/ Math
.sqrt(1.0 - this.es
* sinLat
* sinLat
);
330 double x
= (rpm
+ metersElevation
) * cosLat
* longitude
.sin();
331 double y
= (rpm
* (1.0 - this.es
) + metersElevation
) * sinLat
;
332 double z
= (rpm
+ metersElevation
) * cosLat
* longitude
.cos();
334 return new Vec4(x
, y
, z
);
337 private Position
cartesianToGeodetic(Vec4 cart
)
341 String message
= Logging
.getMessage("nullValue.PointIsNull");
342 Logging
.logger().severe(message
);
343 throw new IllegalArgumentException(message
);
348 // Direct transformation from geocentric to geodetic ccordinates,
349 // Journal of Geodesy (2002) 76:451-454
350 double ra2
= 1 / (this.equatorialRadius
* equatorialRadius
);
353 //noinspection SuspiciousNameCombination
359 double XXpYY
= X
* X
+ Y
* Y
;
360 double sqrtXXpYY
= Math
.sqrt(XXpYY
);
361 double p
= XXpYY
* ra2
;
362 double q
= Z
* Z
* (1 - e2
) * ra2
;
363 double r
= 1 / 6.0 * (p
+ q
- e4
);
364 double s
= e4
* p
* q
/ (4 * r
* r
* r
);
365 double t
= Math
.pow(1 + s
+ Math
.sqrt(s
* (2 + s
)), 1 / 3.0);
366 double u
= r
* (1 + t
+ 1 / t
);
367 double v
= Math
.sqrt(u
* u
+ e4
* q
);
368 double w
= e2
* (u
+ v
- q
) / (2 * v
);
369 double k
= Math
.sqrt(u
+ v
+ w
* w
) - w
;
370 double D
= k
* sqrtXXpYY
/ (k
+ e2
);
371 double lon
= 2 * Math
.atan2(Y
, X
+ sqrtXXpYY
);
372 double sqrtDDpZZ
= Math
.sqrt(D
* D
+ Z
* Z
);
373 double lat
= 2 * Math
.atan2(Z
, D
+ sqrtDDpZZ
);
374 double elevation
= (k
+ e2
- 1) * sqrtDDpZZ
/ k
;
376 return Position
.fromRadians(lat
, lon
, elevation
);
379 public boolean equals(Object o
)
383 if (o
== null || getClass() != o
.getClass())
386 EllipsoidalGlobe that
= (EllipsoidalGlobe
) o
;
388 if (Double
.compare(that
.equatorialRadius
, equatorialRadius
) != 0)
390 if (Double
.compare(that
.es
, es
) != 0)
392 if (Double
.compare(that
.polarRadius
, polarRadius
) != 0)
394 if (center
!= null ?
!center
.equals(that
.center
) : that
.center
!= null)
396 //noinspection RedundantIfStatement
397 if (elevationModel
!= null ?
!elevationModel
.equals(that
.elevationModel
) : that
.elevationModel
!= null)
403 public int hashCode()
407 temp
= equatorialRadius
!= +0.0d ? Double
.doubleToLongBits(equatorialRadius
) : 0L;
408 result
= (int) (temp ^
(temp
>>> 32));
409 temp
= polarRadius
!= +0.0d ? Double
.doubleToLongBits(polarRadius
) : 0L;
410 result
= 31 * result
+ (int) (temp ^
(temp
>>> 32));
411 temp
= es
!= +0.0d ? Double
.doubleToLongBits(es
) : 0L;
412 result
= 31 * result
+ (int) (temp ^
(temp
>>> 32));
413 result
= 31 * result
+ (center
!= null ? center
.hashCode() : 0);
414 result
= 31 * result
+ (elevationModel
!= null ? elevationModel
.hashCode() : 0);