Update to Worldwind release 20070920
[worldwind-tracker.git] / gov / nasa / worldwind / globes / EllipsoidalGlobe.java
blob8d59e1b197ef712dd7c5c3961f6fd5c8eb7e30f0
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
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;
13 /**
14 * @author Tom Gaskins
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)
28 if (em == null)
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)
76 if (latLon == null)
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()
88 return this.es;
91 public final double getDiameter()
93 return this.equatorialRadius * 2;
96 public final Vec4 getCenter()
98 return this.center;
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()
123 return this;
126 public boolean intersects(Frustum frustum)
128 if (frustum == null)
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)
150 if (line == null)
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"
161 double m2 = m * m;
162 double n2 = n * n;
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)
178 return null;
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)
201 if (line == null)
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)
213 if (plane == null)
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)
226 if (p == null)
228 String msg = Logging.getMessage("nullValue.PointIsNull");
229 Logging.logger().severe(msg);
230 throw new IllegalArgumentException(msg);
233 p = p.subtract3(this.center);
235 return new Vec4(
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)
284 if (point == null)
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)
296 if (line == null)
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)
305 return 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)
339 if (cart == null)
341 String message = Logging.getMessage("nullValue.PointIsNull");
342 Logging.logger().severe(message);
343 throw new IllegalArgumentException(message);
346 // according to
347 // H. Vermeille,
348 // Direct transformation from geocentric to geodetic ccordinates,
349 // Journal of Geodesy (2002) 76:451-454
350 double ra2 = 1 / (this.equatorialRadius * equatorialRadius);
352 double X = cart.z;
353 //noinspection SuspiciousNameCombination
354 double Y = cart.x;
355 double Z = cart.y;
356 double e2 = this.es;
357 double e4 = e2 * e2;
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)
381 if (this == o)
382 return true;
383 if (o == null || getClass() != o.getClass())
384 return false;
386 EllipsoidalGlobe that = (EllipsoidalGlobe) o;
388 if (Double.compare(that.equatorialRadius, equatorialRadius) != 0)
389 return false;
390 if (Double.compare(that.es, es) != 0)
391 return false;
392 if (Double.compare(that.polarRadius, polarRadius) != 0)
393 return false;
394 if (center != null ? !center.equals(that.center) : that.center != null)
395 return false;
396 //noinspection RedundantIfStatement
397 if (elevationModel != null ? !elevationModel.equals(that.elevationModel) : that.elevationModel != null)
398 return false;
400 return true;
403 public int hashCode()
405 int result;
406 long temp;
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);
415 return result;