Update to Worldwind release 20070920
[worldwind-tracker.git] / gov / nasa / worldwind / util / LevelSet.java
blobfb789e88dbc601aa6cb5e090976d8b5bd75670ad
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.util;
9 import gov.nasa.worldwind.WWObjectImpl;
10 import gov.nasa.worldwind.avlist.*;
11 import gov.nasa.worldwind.geom.*;
13 import java.net.*;
14 import java.util.*;
16 /**
17 * @author tag
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;
30 this.sector = sector;
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"));
52 int numLevels = 0;
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
66 else if (o != null)
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"));
75 else if (o != null)
77 sectorLimits = (SectorResolution[]) o;
78 for (SectorResolution sr : sectorLimits)
80 if (sr.levelNumber > numLevels - 1)
82 String message =
83 Logging.getMessage("LevelSet.sectorResolutionLimitsTooHigh", sr.levelNumber, numLevels - 1);
84 Logging.logger().warning(message);
85 break;
89 this.sectorLevelLimits = sectorLimits;
91 if (sb.length() > 0)
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);
104 if (tub == null)
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)
112 sb.append("?");
113 sb.append("T=");
114 sb.append(tile.getLevel().getDataset());
115 sb.append("&L=");
116 sb.append(tile.getLevel().getLevelName());
117 sb.append("&X=");
118 sb.append(tile.getColumn());
119 sb.append("&Y=");
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)
166 if (source == null)
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()
186 return this.sector;
189 public final LatLon getLevelZeroTileDelta()
191 return this.levelZeroTileDelta;
194 public final ArrayList<Level> getLevels()
196 return this.levels;
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);
229 break;
233 return level;
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);
246 break;
250 return level;
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)
283 if (tile == null)
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)
302 if (tile == null)
304 String msg = Logging.getMessage("nullValue.TileIsNull");
305 Logging.logger().severe(msg);
306 throw new IllegalArgumentException(msg);
309 if (tile.getLevel().isEmpty())
310 return true;
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)
324 if (tile == null)
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)
337 if (key == null)
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)
359 if (key == null)
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());