2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
6 package gov
.nasa
.worldwind
.formats
.rpf
;
8 import gov
.nasa
.worldwind
.*;
9 import gov
.nasa
.worldwind
.geom
.*;
12 import static java
.util
.logging
.Level
.*;
16 * @version $Id: RpfZone.java 1896 2007-05-29 00:16:25Z dcollins $
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
;
48 public ZoneKey(RpfZone zone
, RpfDataSeries dataSeries
)
51 this.dataSeries
= dataSeries
;
52 this.hashCode
= this.computeHash();
55 private int computeHash()
58 if (this.zone
!= null)
59 hash
= 29 * hash
+ this.zone
.ordinal();
60 if (this.dataSeries
!= null)
61 hash
= 29 * hash
+ this.dataSeries
.ordinal();
65 public boolean equals(Object o
)
69 if (o
== null || !o
.getClass().equals(this.getClass()))
71 final ZoneKey that
= (ZoneKey
) o
;
72 return (this.zone
== that
.zone
) && (this.dataSeries
== that
.dataSeries
);
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
)
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
);
112 this.dataSeries
= dataSeries
;
113 this.northSouthPixelConstant
= computeNorthSouthPixelConstant(zone
.northSouthPixelSpacingConstant
,
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
,
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
);
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
);
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
);
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
);
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
;
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
));
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
)
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';
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];
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];
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
);
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
);
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
;
492 return zoneForSouthernHemisphere(dataSeries
, latDegrees
);
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
)
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
)
519 public RpfZone
.ZoneValues
zoneValues(RpfDataSeries dataSeries
)
521 RpfZone
.ZoneKey key
= new RpfZone
.ZoneKey(this, dataSeries
);
522 RpfZone
.ZoneValues value
= zoneValuesDirectory().get(key
);
525 value
= new RpfZone
.ZoneValues(this, dataSeries
);
526 zoneValuesDirectory().put(key
, 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
;