Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / formats / rpf / RpfZone.java
blobf04c90119a4baeb7b42c7a69a89942af0517e89a
1 /*
2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
4 All Rights Reserved.
5 */
6 package gov.nasa.worldwind.formats.rpf;
8 import gov.nasa.worldwind.*;
9 import gov.nasa.worldwind.geom.*;
11 import java.util.*;
12 import static java.util.logging.Level.*;
14 /**
15 * @author dcollins
16 * @version $Id: RpfZone.java 1896 2007-05-29 00:16:25Z dcollins $
18 public enum RpfZone
20 /* [Table III, Section 70, MIL-A-89007] */
21 Zone1('1', 369664, 400384, 0, 32),
22 Zone2('2', 302592, 400384, 32, 48),
23 Zone3('3', 245760, 400384, 48, 56),
24 Zone4('4', 199168, 400384, 56, 64),
25 Zone5('5', 163328, 400384, 64, 68),
26 Zone6('6', 137216, 400384, 68, 72),
27 Zone7('7', 110080, 400384, 72, 76),
28 Zone8('8', 82432, 400384, 76, 80),
29 // Zone9('9', 400384, 400384, 80, 90),
30 ZoneA('A', 369664, 400384, 0, -32),
31 ZoneB('B', 302592, 400384, -32, -48),
32 ZoneC('C', 245760, 400384, -48, -56),
33 ZoneD('D', 199168, 400384, -56, -64),
34 ZoneE('E', 163328, 400384, -64, -68),
35 ZoneF('F', 137216, 400384, -68, -72),
36 ZoneG('G', 110080, 400384, -72, -76),
37 ZoneH('H', 82432, 400384, -76, -80),
38 // ZoneJ('J', 400384, 400384, -80, -90),
40 // TODO: computations for polar zones
42 private static class ZoneKey
44 public final RpfZone zone;
45 public final RpfDataSeries dataSeries;
46 private int hashCode;
48 public ZoneKey(RpfZone zone, RpfDataSeries dataSeries)
50 this.zone = zone;
51 this.dataSeries = dataSeries;
52 this.hashCode = this.computeHash();
55 private int computeHash()
57 int hash = 0;
58 if (this.zone != null)
59 hash = 29 * hash + this.zone.ordinal();
60 if (this.dataSeries != null)
61 hash = 29 * hash + this.dataSeries.ordinal();
62 return hash;
65 public boolean equals(Object o)
67 if (this == o)
68 return true;
69 if (o == null || !o.getClass().equals(this.getClass()))
70 return false;
71 final ZoneKey that = (ZoneKey) o;
72 return (this.zone == that.zone) && (this.dataSeries == that.dataSeries);
75 public int hashCode()
77 return this.hashCode;
81 public static class ZoneValues
83 public final RpfZone zone;
84 public final RpfDataSeries dataSeries;
85 public final int eastWestPixelConstant;
86 public final int northSouthPixelConstant;
87 public final Angle equatorwardExtent;
88 public final Angle polewardExtent;
89 public final Sector extent;
90 public final int latitudinalFrames;
91 public final int longitudinalFrames;
92 public final Angle latitudinalFrameExtent;
93 public final Angle longitudinalFrameExtent;
94 public final int maximumFrameNumber;
96 private ZoneValues(RpfZone zone, RpfDataSeries dataSeries)
98 if (zone == null)
100 String message = WorldWind.retrieveErrMsg("nullValue.RpfZoneIsNull");
101 WorldWind.logger().log(FINE, message);
102 throw new IllegalArgumentException(message);
104 if (dataSeries == null)
106 String message = WorldWind.retrieveErrMsg("nullValue.RpfDataSeriesIsNull");
107 WorldWind.logger().log(FINE, message);
108 throw new IllegalArgumentException(message);
111 this.zone = zone;
112 this.dataSeries = dataSeries;
113 this.northSouthPixelConstant = computeNorthSouthPixelConstant(zone.northSouthPixelSpacingConstant,
114 dataSeries);
115 this.eastWestPixelConstant = computeEastWestPixelConstant(zone.eastWestPixelSpacingConstant, dataSeries);
116 this.equatorwardExtent = computeEquatorwardExtent(zone.equatorwardNominalBoundary,
117 this.northSouthPixelConstant, PixelRowsPerFrame);
118 this.polewardExtent = computePolewardExtent(zone.polewardNominalBoundary, this.northSouthPixelConstant,
119 PixelRowsPerFrame);
120 this.extent = computeZoneExtent(this.equatorwardExtent.degrees, this.polewardExtent.degrees);
121 this.latitudinalFrames = computeLatitudinalFrames(this.polewardExtent.degrees,
122 this.equatorwardExtent.degrees, this.northSouthPixelConstant, PixelRowsPerFrame);
123 this.longitudinalFrames = computeLongitudinalFrames(this.eastWestPixelConstant, PixelRowsPerFrame);
124 this.latitudinalFrameExtent = computeLatitudinalFrameExtent(this.polewardExtent.degrees,
125 this.equatorwardExtent.degrees, this.latitudinalFrames);
126 this.longitudinalFrameExtent = computeLongitudinalFrameExtent(this.longitudinalFrames);
127 this.maximumFrameNumber = computeMaximumFrameNumber(this.latitudinalFrames, this.longitudinalFrames);
130 private static double clamp(double x, double min, double max)
132 return (x < min) ? min : ((x > max) ? max : x);
135 // ============== Constant Zone Computations ======================= //
136 // ============== Constant Zone Computations ======================= //
137 // ============== Constant Zone Computations ======================= //
139 private static int computeEastWestPixelConstant(double eastWestPixelSpacingConstant,
140 RpfDataSeries dataSeries)
142 if (dataSeries.rpfDataType.equalsIgnoreCase("CADRG"))
143 return computeEastWestPixelConstantCADRG(eastWestPixelSpacingConstant, dataSeries.scaleOrGSD);
144 else if (dataSeries.rpfDataType.equalsIgnoreCase("CIB"))
145 return computeEastWestPixelConstantCIB(eastWestPixelSpacingConstant, dataSeries.scaleOrGSD);
146 else
148 String message = WorldWind.retrieveErrMsg("RpfZone.UnknownRpfDataType") + dataSeries.rpfDataType;
149 WorldWind.logger().log(FINE, message);
150 throw new IllegalArgumentException(message);
154 /* [Section 60.1.2 MIL-C-89038] */
155 private static int computeEastWestPixelConstantCADRG(double eastWestPixelSpacingConstant, double scale)
157 double S = 1000000d / scale;
158 double tmp = eastWestPixelSpacingConstant * S;
159 tmp = 512d * (int) Math.ceil(tmp / 512d);
160 tmp /= (150d / 100d);
161 return 256 * (int) Math.round(tmp / 256d);
164 /* [Section A.5.1.1, MIL-PRF-89041A] */
165 private static int computeEastWestPixelConstantCIB(double eastWestPixelSpacingConstant,
166 double groundSampleDistance)
168 double S = 100d / groundSampleDistance;
169 double tmp = eastWestPixelSpacingConstant * S;
170 return 512 * (int) Math.ceil(tmp / 512d);
173 /* [Section 60.1.5.c MIL-C-89038] */
174 /* [Section A.5.1.2.c MIL-PRF-89041A] */
175 private static Angle computeEquatorwardExtent(double equatorwardNominalBoundary,
176 double northSouthPixelConstant, double pixelRowsPerFrame)
178 double nsPixelsPerDegree = northSouthPixelConstant / 90d;
179 double degrees = Math.signum(equatorwardNominalBoundary)
180 * clamp((int) (nsPixelsPerDegree * Math.abs(equatorwardNominalBoundary) / pixelRowsPerFrame)
181 * pixelRowsPerFrame / nsPixelsPerDegree, 0, 90);
182 return Angle.fromDegrees(degrees);
185 private static Angle computeLatitudinalFrameExtent(double polewardExtent, double equatorwardExtent,
186 double latitudinalFrames)
188 double degrees = Math.abs(polewardExtent - equatorwardExtent) / latitudinalFrames;
189 return Angle.fromDegrees(degrees);
192 /* [Section 60.1.6 MIL-C-89038] */
193 /* [Section A.5.1.3 MIL-PRF-89041A] */
194 private static int computeLatitudinalFrames(double polewardExtent, double equatorwardExtent,
195 double northSouthPixelConstant, double pixelRowsPerFrame)
197 double nsPixelsPerDegree = northSouthPixelConstant / 90d;
198 double extent = Math.abs(polewardExtent - equatorwardExtent);
199 return (int) Math.rint(extent * nsPixelsPerDegree / pixelRowsPerFrame);
202 public static Angle computeLongitudinalFrameExtent(double longitudinalFrames)
204 double degrees = 360d / longitudinalFrames;
205 return Angle.fromDegrees(degrees);
208 /* [Section 60.1.7 MIL-C-89038] */
209 /* [Section A.5.1.4 MIL-PRF-89041A] */
210 private static int computeLongitudinalFrames(double eastWestPixelConstant, double pixelRowsPerFrame)
212 return (int) Math.ceil(eastWestPixelConstant / pixelRowsPerFrame);
215 /* [Section 30.6 MIL-C-89038] */
216 /* [Section A.3.6, MIL-PRF-89041A] */
217 private static int computeMaximumFrameNumber(int latitudinalFrames, int longitudinalFrames)
219 return (latitudinalFrames * longitudinalFrames) - 1;
222 private static int computeNorthSouthPixelConstant(double northSouthPixelSpacingConstant,
223 RpfDataSeries dataSeries)
225 if (dataSeries.rpfDataType.equalsIgnoreCase("CADRG"))
226 return computeNorthSouthPixelConstantCADRG(northSouthPixelSpacingConstant, dataSeries.scaleOrGSD);
227 else if (dataSeries.rpfDataType.equalsIgnoreCase("CIB"))
228 return computeNorthSouthPixelConstantCIB(northSouthPixelSpacingConstant, dataSeries.scaleOrGSD);
229 else
231 String message = WorldWind.retrieveErrMsg("RpfZone.UnknownRpfDataType") + dataSeries.rpfDataType;
232 WorldWind.logger().log(FINE, message);
233 throw new IllegalArgumentException(message);
237 /* [Section 60.1.1 MIL-C-89038] */
238 private static int computeNorthSouthPixelConstantCADRG(double northSouthPixelConstant, double scale)
240 double S = 1000000d / scale;
241 double tmp = northSouthPixelConstant * S;
242 tmp = 512d * (int) Math.ceil(tmp / 512d);
243 tmp /= 4d;
244 tmp /= (150d / 100d);
245 return 256 * (int) Math.round(tmp / 256d);
248 /* [Section A.5.1.1, MIL-PRF-89041A] */
249 private static int computeNorthSouthPixelConstantCIB(double northSouthPixelSpacingConstant,
250 double groundSampleDistance)
252 double S = 100d / groundSampleDistance;
253 double tmp = northSouthPixelSpacingConstant * S;
254 tmp = 512d * (int) Math.ceil(tmp / 512d);
255 tmp /= 4d;
256 return 256 * (int) Math.round(tmp / 256d);
259 /* [Section 60.1.5.b MIL-C-89038] */
260 /* [Section A.5.1.2.b MIL-PRF-89041A] */
261 private static Angle computePolewardExtent(double polewardNominalBoundary, double northSouthPixelConstant,
262 double pixelRowsPerFrame)
264 double nsPixelsPerDegree = northSouthPixelConstant / 90d;
265 double degrees = Math.signum(polewardNominalBoundary)
266 * clamp(Math.ceil(nsPixelsPerDegree * Math.abs(polewardNominalBoundary) / pixelRowsPerFrame)
267 * pixelRowsPerFrame / nsPixelsPerDegree, 0, 90);
268 return Angle.fromDegrees(degrees);
271 private static Sector computeZoneExtent(double equatorwardExtent, double polewardExtent)
273 double minLatitude, maxLatitude;
274 if (equatorwardExtent < polewardExtent)
276 minLatitude = equatorwardExtent;
277 maxLatitude = polewardExtent;
279 else
281 minLatitude = polewardExtent;
282 maxLatitude = equatorwardExtent;
284 return Sector.fromDegrees(minLatitude, maxLatitude, -180, 180);
287 // ============== Varying Zone Computations ======================= //
288 // ============== Varying Zone Computations ======================= //
289 // ============== Varying Zone Computations ======================= //
291 /* [Section 30.6 MIL-C-89038] */
292 /* [Section A.3.6, MIL-PRF-89041A] */
294 public int frameColumnFromFrameNumber(int frameNumber)
296 int row = this.frameRowFromFrameNumber(frameNumber);
297 return frameNumber - row * this.longitudinalFrames;
300 /* [Section 30.3.2 MIL-C-89038] */
301 /* [Section A.3.3.2, MIL-PRF-89041A] */
302 public int frameColumnFromLongitude(Angle longitude)
304 if (longitude == null)
306 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
307 WorldWind.logger().log(FINE, message);
308 throw new IllegalArgumentException(message);
310 return (int) (((longitude.degrees + 180d) / 360d)
311 * (this.eastWestPixelConstant / (double) PixelRowsPerFrame));
314 public Sector frameExtent(int frameNumber)
316 Angle latOrigin = latitudinalFrameOrigin(this.frameRowFromFrameNumber(frameNumber));
317 Angle lonOrigin = longitudinalFrameOrigin(this.frameColumnFromFrameNumber(frameNumber));
318 return new Sector(
319 latOrigin.subtract(this.latitudinalFrameExtent), latOrigin,
320 lonOrigin, lonOrigin.add(this.longitudinalFrameExtent));
323 /* [Section 30.6 MIL-C-89038] */
324 /* [Section A.3.6, MIL-PRF-89041A] */
325 public int frameNumber(int row, int column)
327 return column + row * this.longitudinalFrames;
330 /* [Section 30.6 MIL-C-89038] */
331 /* [Section A.3.6, MIL-PRF-89041A] */
332 public int frameRowFromFrameNumber(int frameNumber)
334 return (int) (frameNumber / (double) this.longitudinalFrames);
337 /* [Section 30.3.1 MIL-C-89038] */
338 /* [Section A.3.3.1, MIL-PRF-89041A] */
339 public int frameRowFromLatitude(Angle latitude)
341 if (latitude == null)
343 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
344 WorldWind.logger().log(FINE, message);
345 throw new IllegalArgumentException(message);
347 return (int) (((latitude.degrees - this.equatorwardExtent.degrees) / 90d)
348 * (this.northSouthPixelConstant / (double) PixelRowsPerFrame));
351 /* [Section 30.3.1 MIL-C-89038] */
352 /* [Section A.3.3.1, MIL-PRF-89041A] */
353 public Angle latitudinalFrameOrigin(int row)
355 double degrees = (90d / (double) this.northSouthPixelConstant) * ((double) PixelRowsPerFrame) * (row + 1)
356 + this.equatorwardExtent.degrees;
357 return Angle.fromDegrees(degrees);
360 /* [Section 30.3.2 MIL-C-89038] */
361 /* [Section A.3.3.2, MIL-PRF-89041A] */
362 public Angle longitudinalFrameOrigin(int column)
364 double degrees = (360d / (double) this.eastWestPixelConstant) * ((double) PixelRowsPerFrame)
365 * column - 180d;
366 return Angle.fromDegrees(degrees);
370 public static final int PixelRowsPerFrame = 1536;
371 public static final int SubframeRowsPerFrame = 6;
372 public final int eastWestPixelSpacingConstant;
373 public final int equatorwardNominalBoundary;
374 public final int northSouthPixelSpacingConstant;
375 public final int polewardNominalBoundary;
376 public final Character zoneCode;
378 private RpfZone(Character zoneCode, int eastWestPixelSpacingConstant, int northSouthPixelSpacingConstant,
379 int equatorwardNominalBoundary, int polewardNominalBoundary)
381 this.zoneCode = zoneCode;
382 this.eastWestPixelSpacingConstant = eastWestPixelSpacingConstant;
383 this.northSouthPixelSpacingConstant = northSouthPixelSpacingConstant;
384 this.equatorwardNominalBoundary = equatorwardNominalBoundary;
385 this.polewardNominalBoundary = polewardNominalBoundary;
388 private static RpfZone[] enumConstantAlphabet = null;
390 private static RpfZone[] enumConstantAlphabet()
392 if (enumConstantAlphabet == null)
394 RpfZone[] universe = RpfZone.class.getEnumConstants();
395 enumConstantAlphabet = new RpfZone[36];
396 for (RpfZone zone : universe)
398 enumConstantAlphabet[indexFor(zone.zoneCode)] = zone;
401 return enumConstantAlphabet;
404 private static int indexFor(Character zoneCode)
406 if (zoneCode >= '0' && zoneCode <= '9')
407 return zoneCode - '0';
408 else if (zoneCode >= 'A' && zoneCode <= 'Z')
409 return 10 + zoneCode - 'A';
410 return -1;
413 public static boolean isZoneCode(Character zoneCode)
415 if (zoneCode == null)
417 String message = WorldWind.retrieveErrMsg("nullValue.CharacterIsNull");
418 WorldWind.logger().log(FINE, message);
419 throw new IllegalArgumentException(message);
421 RpfZone[] alphabet = enumConstantAlphabet();
422 int index = indexFor(Character.toUpperCase(zoneCode));
423 return (index >= 0) && (index < alphabet.length) && (alphabet[index] != null);
426 private static RpfZone[] northernHemisphereZones = null;
428 private static RpfZone[] northernHemisphereZones()
430 if (northernHemisphereZones == null)
432 RpfZone[] universe = RpfZone.class.getEnumConstants();
433 northernHemisphereZones = new RpfZone[universe.length / 2];
434 int index = 0;
435 for (RpfZone zone : universe)
437 if (zone.polewardNominalBoundary >= 0)
438 northernHemisphereZones[index++] = zone;
441 return northernHemisphereZones;
444 private static RpfZone[] southernHemisphereZones = null;
446 private static RpfZone[] southernHemisphereZones()
448 if (southernHemisphereZones == null)
450 RpfZone[] universe = RpfZone.class.getEnumConstants();
451 southernHemisphereZones = new RpfZone[universe.length / 2];
452 int index = 0;
453 for (RpfZone zone : universe)
455 if (zone.polewardNominalBoundary < 0)
456 southernHemisphereZones[index++] = zone;
459 return southernHemisphereZones;
462 public static RpfZone zoneFor(Character zoneCode)
464 if (zoneCode == null)
466 String message = WorldWind.retrieveErrMsg("nullValue.CharacterIsNull");
467 WorldWind.logger().log(FINE, message);
468 throw new IllegalArgumentException(message);
470 RpfZone zone;
471 RpfZone[] alphabet = enumConstantAlphabet();
472 int index = indexFor(Character.toUpperCase(zoneCode));
473 if (index < 0 || index >= alphabet.length || (zone = alphabet[index]) == null)
475 String message = WorldWind.retrieveErrMsg("generic.EnumNotFound") + zoneCode;
476 WorldWind.logger().log(FINE, message);
477 throw new EnumConstantNotPresentException(RpfZone.class, message);
479 return zone;
482 public static RpfZone zoneFor(RpfDataSeries dataSeries, Angle latitude)
484 if (latitude == null)
486 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
487 WorldWind.logger().log(FINE, message);
488 throw new IllegalArgumentException(message);
490 double latDegrees = latitude.degrees;
491 if (latDegrees < 0)
492 return zoneForSouthernHemisphere(dataSeries, latDegrees);
493 else
494 return zoneForNorthernHemisphere(dataSeries, latDegrees);
497 private static RpfZone zoneForNorthernHemisphere(RpfDataSeries dataSeries, double latitude)
499 for (RpfZone zone : northernHemisphereZones())
501 ZoneValues values = zone.zoneValues(dataSeries);
502 if (latitude >= values.equatorwardExtent.degrees && latitude <= values.polewardExtent.degrees)
503 return zone;
505 return null;
508 private static RpfZone zoneForSouthernHemisphere(RpfDataSeries dataSeries, double latitude)
510 for (RpfZone zone : southernHemisphereZones())
512 ZoneValues values = zone.zoneValues(dataSeries);
513 if (latitude <= values.equatorwardExtent.degrees && latitude >= values.polewardExtent.degrees)
514 return zone;
516 return null;
519 public RpfZone.ZoneValues zoneValues(RpfDataSeries dataSeries)
521 RpfZone.ZoneKey key = new RpfZone.ZoneKey(this, dataSeries);
522 RpfZone.ZoneValues value = zoneValuesDirectory().get(key);
523 if (value == null)
525 value = new RpfZone.ZoneValues(this, dataSeries);
526 zoneValuesDirectory().put(key, value);
528 return value;
531 private static Map<RpfZone.ZoneKey, RpfZone.ZoneValues> zoneValuesDirectory = null;
533 private static Map<RpfZone.ZoneKey, RpfZone.ZoneValues> zoneValuesDirectory()
535 if (zoneValuesDirectory == null)
536 zoneValuesDirectory = new HashMap<RpfZone.ZoneKey, RpfZone.ZoneValues>();
537 return zoneValuesDirectory;