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
.util
;
9 import gov
.nasa
.worldwind
.WWObjectImpl
;
10 import gov
.nasa
.worldwind
.avlist
.*;
11 import gov
.nasa
.worldwind
.geom
.*;
18 * @version $Id: LevelSet.java 2786 2007-09-10 17:41:00Z tgaskins $
20 public class LevelSet
extends WWObjectImpl
22 public static final class SectorResolution
24 private final int levelNumber
;
25 private final Sector sector
;
27 public SectorResolution(Sector sector
, int levelNumber
)
29 this.levelNumber
= levelNumber
;
34 private final Sector sector
;
35 private final LatLon levelZeroTileDelta
;
36 private final int numLevelZeroColumns
;
37 private final java
.util
.ArrayList
<Level
> levels
= new java
.util
.ArrayList
<Level
>();
38 private final SectorResolution
[] sectorLevelLimits
;
40 public LevelSet(AVList params
)
42 StringBuffer sb
= new StringBuffer();
44 Object o
= params
.getValue(AVKey
.LEVEL_ZERO_TILE_DELTA
);
45 if (o
== null || !(o
instanceof LatLon
))
46 sb
.append(Logging
.getMessage("term.tileDelta"));
48 o
= params
.getValue(AVKey
.SECTOR
);
49 if (o
== null || !(o
instanceof Sector
))
50 sb
.append(Logging
.getMessage("term.sector"));
53 o
= params
.getValue(AVKey
.NUM_LEVELS
);
54 if (o
== null || !(o
instanceof Integer
) || (numLevels
= (Integer
) o
) < 1)
55 sb
.append(Logging
.getMessage("term.numLevels"));
57 int numEmptyLevels
= 0;
58 o
= params
.getValue(AVKey
.NUM_EMPTY_LEVELS
);
59 if (o
== null || !(o
instanceof Integer
) || (numEmptyLevels
= (Integer
) o
) < 0)
60 sb
.append(Logging
.getMessage("term.numEMptyLevels"));
62 String
[] inactiveLevels
= null;
63 o
= params
.getValue(AVKey
.INACTIVE_LEVELS
);
64 if (o
!= null && !(o
instanceof String
))
65 sb
.append(Logging
.getMessage("term.sector")); // TODO field name
67 inactiveLevels
= ((String
) o
).split(",");
69 SectorResolution
[] sectorLimits
= null;
70 o
= params
.getValue(AVKey
.SECTOR_RESOLUTION_LIMITS
);
71 if (o
!= null && !(o
instanceof SectorResolution
[]))
73 sb
.append(Logging
.getMessage("term.sectorResolutionLimits"));
77 sectorLimits
= (SectorResolution
[]) o
;
78 for (SectorResolution sr
: sectorLimits
)
80 if (sr
.levelNumber
> numLevels
- 1)
83 Logging
.getMessage("LevelSet.sectorResolutionLimitsTooHigh", sr
.levelNumber
, numLevels
- 1);
84 Logging
.logger().warning(message
);
89 this.sectorLevelLimits
= sectorLimits
;
93 String message
= Logging
.getMessage("layers.LevelSet.InvalidLevelDescriptorFields", sb
.toString());
94 Logging
.logger().severe(message
);
95 throw new IllegalArgumentException(message
);
98 this.levelZeroTileDelta
= (LatLon
) params
.getValue(AVKey
.LEVEL_ZERO_TILE_DELTA
);
99 this.sector
= (Sector
) params
.getValue(AVKey
.SECTOR
);
101 params
= params
.copy(); // copy so as not to modify the user's params
103 TileUrlBuilder tub
= (TileUrlBuilder
) params
.getValue(AVKey
.TILE_URL_BUILDER
);
106 params
.setValue(AVKey
.TILE_URL_BUILDER
, new TileUrlBuilder()
108 public URL
getURL(Tile tile
) throws MalformedURLException
110 StringBuffer sb
= new StringBuffer(tile
.getLevel().getService());
111 if (sb
.lastIndexOf("?") != sb
.length() - 1)
114 sb
.append(tile
.getLevel().getDataset());
116 sb
.append(tile
.getLevel().getLevelName());
118 sb
.append(tile
.getColumn());
120 sb
.append(tile
.getRow());
122 return new URL(sb
.toString());
127 for (int i
= 0; i
< numLevels
; i
++)
129 params
.setValue(AVKey
.LEVEL_NAME
, i
< numEmptyLevels ?
"" : Integer
.toString(i
- numEmptyLevels
));
130 params
.setValue(AVKey
.LEVEL_NUMBER
, i
);
132 Angle latDelta
= this.levelZeroTileDelta
.getLatitude().divide(Math
.pow(2, i
));
133 Angle lonDelta
= this.levelZeroTileDelta
.getLongitude().divide(Math
.pow(2, i
));
134 params
.setValue(AVKey
.TILE_DELTA
, new LatLon(latDelta
, lonDelta
));
136 this.levels
.add(new Level(params
));
139 if (inactiveLevels
!= null)
141 for (String s
: inactiveLevels
)
143 int i
= Integer
.parseInt(s
);
144 this.getLevel(i
).setActive(false);
148 if (this.sectorLevelLimits
!= null)
150 Arrays
.sort(this.sectorLevelLimits
, new Comparator
<SectorResolution
>()
152 public int compare(SectorResolution sra
, SectorResolution srb
)
154 // sort order is deliberately backwards in order to list higher-resolution sectors first
155 return sra
.levelNumber
< srb
.levelNumber ?
1 : sra
.levelNumber
== srb
.levelNumber ?
0 : -1;
160 this.numLevelZeroColumns
=
161 (int) Math
.round(this.sector
.getDeltaLon().divide(this.levelZeroTileDelta
.getLongitude()));
164 public LevelSet(LevelSet source
)
168 String msg
= Logging
.getMessage("nullValue.LevelSetIsNull");
169 Logging
.logger().severe(msg
);
170 throw new IllegalArgumentException(msg
);
173 this.levelZeroTileDelta
= source
.levelZeroTileDelta
;
174 this.sector
= source
.sector
;
175 this.numLevelZeroColumns
= source
.numLevelZeroColumns
;
176 this.sectorLevelLimits
= source
.sectorLevelLimits
;
178 for (Level level
: source
.levels
)
180 this.levels
.add(level
); // Levels are final, so it's safe to copy references.
184 public final Sector
getSector()
189 public final LatLon
getLevelZeroTileDelta()
191 return this.levelZeroTileDelta
;
194 public final ArrayList
<Level
> getLevels()
199 public final Level
getLevel(int levelNumber
)
201 return (levelNumber
>= 0 && levelNumber
< this.levels
.size()) ?
this.levels
.get(levelNumber
) : null;
204 public final int getNumLevels()
206 return this.levels
.size();
209 public final Level
getFirstLevel()
211 return this.getLevel(0);
214 public final Level
getLastLevel()
216 return this.getLevel(this.getNumLevels() - 1);
219 public final Level
getLastLevel(Sector sector
)
221 Level level
= this.getLevel(this.getNumLevels() - 1);
223 if (this.sectorLevelLimits
!= null)
224 for (SectorResolution sr
: this.sectorLevelLimits
)
226 if (sr
.sector
.intersects(sector
) && sr
.levelNumber
<= level
.getLevelNumber())
228 level
= this.getLevel(sr
.levelNumber
);
236 public final Level
getLastLevel(Angle latitude
, Angle longitude
)
238 Level level
= this.getLevel(this.getNumLevels() - 1);
240 if (this.sectorLevelLimits
!= null)
241 for (SectorResolution sr
: this.sectorLevelLimits
)
243 if (sr
.sector
.contains(latitude
, longitude
) && sr
.levelNumber
<= level
.getLevelNumber())
245 level
= this.getLevel(sr
.levelNumber
);
253 public final boolean isFinalLevel(int levelNum
)
255 return levelNum
== this.getNumLevels() - 1;
258 public final boolean isLevelEmpty(int levelNumber
)
260 return this.levels
.get(levelNumber
).isEmpty();
263 private int numColumnsInLevel(Level level
)
265 int levelDelta
= level
.getLevelNumber() - this.getFirstLevel().getLevelNumber();
266 double twoToTheN
= Math
.pow(2, levelDelta
);
267 return (int) (twoToTheN
* this.numLevelZeroColumns
);
270 private long getTileNumber(Tile tile
)
272 return tile
.getRow() * this.numColumnsInLevel(tile
.getLevel()) + tile
.getColumn();
276 * Instructs the level set that a tile is likely to be absent.
278 * @param tile The tile to mark as having an absent resource.
279 * @throws IllegalArgumentException if <code>tile</code> is null
281 public final void markResourceAbsent(Tile tile
)
285 String msg
= Logging
.getMessage("nullValue.TileIsNull");
286 Logging
.logger().severe(msg
);
287 throw new IllegalArgumentException(msg
);
290 tile
.getLevel().markResourceAbsent(this.getTileNumber(tile
));
294 * Indicates whether a tile has been marked as absent.
296 * @param tile The tile in question.
297 * @return <code>true</code> if the tile is marked absent, otherwise <code>false</code>.
298 * @throws IllegalArgumentException if <code>tile</code> is null
300 public final boolean isResourceAbsent(Tile tile
)
304 String msg
= Logging
.getMessage("nullValue.TileIsNull");
305 Logging
.logger().severe(msg
);
306 throw new IllegalArgumentException(msg
);
309 if (tile
.getLevel().isEmpty())
312 int tileNumber
= tile
.getRow() * this.numColumnsInLevel(tile
.getLevel()) + tile
.getColumn();
313 return tile
.getLevel().isResourceAbsent(tileNumber
);
317 * Removes the absent-tile mark associated with a tile, if one is associatied.
319 * @param tile The tile to unmark.
320 * @throws IllegalArgumentException if <code>tile</code> is null
322 public final void unmarkResourceAbsent(Tile tile
)
326 String msg
= Logging
.getMessage("nullValue.TileIsNull");
327 Logging
.logger().severe(msg
);
328 throw new IllegalArgumentException(msg
);
331 tile
.getLevel().unmarkResourceAbsent(this.getTileNumber(tile
));
334 // Create the tile corresponding to a specified key.
335 public Sector
computeSectorForKey(TileKey key
)
339 String msg
= Logging
.getMessage("nullValue.KeyIsNull");
340 Logging
.logger().severe(msg
);
341 throw new IllegalArgumentException(msg
);
344 Level level
= this.getLevel(key
.getLevelNumber());
346 // Compute the tile's SW lat/lon based on its row/col in the level's data set.
347 Angle dLat
= level
.getTileDelta().getLatitude();
348 Angle dLon
= level
.getTileDelta().getLongitude();
350 Angle minLatitude
= Tile
.computeRowLatitude(key
.getRow(), dLat
);
351 Angle minLongitude
= Tile
.computeColumnLongitude(key
.getColumn(), dLon
);
353 return new Sector(minLatitude
, minLatitude
.add(dLat
), minLongitude
, minLongitude
.add(dLon
));
356 // Create the tile corresponding to a specified key.
357 public Tile
createTile(TileKey key
)
361 String msg
= Logging
.getMessage("nullValue.KeyIsNull");
362 Logging
.logger().severe(msg
);
363 throw new IllegalArgumentException(msg
);
366 Level level
= this.getLevel(key
.getLevelNumber());
368 // Compute the tile's SW lat/lon based on its row/col in the level's data set.
369 Angle dLat
= level
.getTileDelta().getLatitude();
370 Angle dLon
= level
.getTileDelta().getLongitude();
372 Angle minLatitude
= Tile
.computeRowLatitude(key
.getRow(), dLat
);
373 Angle minLongitude
= Tile
.computeColumnLongitude(key
.getColumn(), dLon
);
375 Sector tileSector
= new Sector(minLatitude
, minLatitude
.add(dLat
), minLongitude
, minLongitude
.add(dLon
));
377 return new Tile(tileSector
, level
, key
.getRow(), key
.getColumn());