/******************************************************************************* * Copyright 2012 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.worldwind.common.layers.curtain; import static au.gov.ga.earthsci.worldwind.common.util.message.CommonMessageConstants.*; import static au.gov.ga.earthsci.worldwind.common.util.message.MessageSourceAccessor.getMessage; import gov.nasa.worldwind.WWObjectImpl; import gov.nasa.worldwind.avlist.AVKey; import gov.nasa.worldwind.avlist.AVList; import gov.nasa.worldwind.util.Logging; import gov.nasa.worldwind.util.TileKey; import java.awt.Dimension; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import au.gov.ga.earthsci.worldwind.common.util.AVKeyMore; import au.gov.ga.earthsci.worldwind.common.util.transform.URLTransformer; /** * Represents a set of LOD levels for the {@link TiledCurtainLayer}. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class CurtainLevelSet extends WWObjectImpl { private final List<CurtainLevel> levels = new ArrayList<CurtainLevel>(); public CurtainLevelSet(AVList params) { StringBuffer sb = new StringBuffer(); int numLevels = 0; Object o = params.getValue(AVKey.NUM_LEVELS); if (o == null || !(o instanceof Integer) || (numLevels = (Integer) o) < 1) sb.append(Logging.getMessage("term.numLevels")).append(" "); int numEmptyLevels = 0; o = params.getValue(AVKey.NUM_EMPTY_LEVELS); if (o != null && o instanceof Integer && (Integer) o > 0) numEmptyLevels = (Integer) o; String[] inactiveLevels = null; o = params.getValue(AVKey.INACTIVE_LEVELS); if (o != null && !(o instanceof String)) sb.append(Logging.getMessage("term.inactiveLevels")).append(" "); else if (o != null) inactiveLevels = ((String) o).split(","); o = params.getValue(AVKeyMore.FULL_WIDTH); if (o == null || !(o instanceof Integer)) sb.append(getMessage(getTermFullWidthKey())).append(" "); o = params.getValue(AVKeyMore.FULL_HEIGHT); if (o == null || !(o instanceof Integer)) sb.append(getMessage(getTermFullHeightKey())).append(" "); o = params.getValue(AVKeyMore.PATH); if (o == null || !(o instanceof Path)) sb.append(getMessage(getTermPathKey())).append(" "); if (sb.length() > 0) { String message = Logging.getMessage("layers.LevelSet.InvalidLevelDescriptorFields", sb.toString()); Logging.logger().severe(message); throw new IllegalArgumentException(message); } int fullWidth = (Integer) params.getValue(AVKeyMore.FULL_WIDTH); int fullHeight = (Integer) params.getValue(AVKeyMore.FULL_HEIGHT); params = params.copy(); // copy so as not to modify the user's params CurtainTileUrlBuilder tub = (CurtainTileUrlBuilder) params.getValue(AVKey.TILE_URL_BUILDER); if (tub == null) { final String paramsImageFormat = (String) params.getValue(AVKey.IMAGE_FORMAT); params.setValue(AVKey.TILE_URL_BUILDER, new CurtainTileUrlBuilder() { @Override public URL getURL(CurtainTile tile, String imageFormat) throws MalformedURLException { String service = tile.getLevel().getService(); if (service == null || service.length() < 1) return null; service = URLTransformer.transform(service); StringBuffer sb = new StringBuffer(service); if (sb.lastIndexOf("?") < 0) sb.append("?"); else sb.append("&"); sb.append("T="); sb.append(tile.getLevel().getDataset()); sb.append("&L="); sb.append(tile.getLevel().getLevelName()); sb.append("&X="); sb.append(tile.getColumn()); sb.append("&Y="); sb.append(tile.getRow()); String format = imageFormat != null ? imageFormat : paramsImageFormat; if (format != null) sb.append("&F=" + format); return new URL(sb.toString()); } }); } Dimension fullSize = new Dimension(fullWidth, fullHeight); Dimension[] levelSizes = new Dimension[numLevels]; for (int i = numLevels - 1; i >= 0; i--) { levelSizes[i] = fullSize; fullSize = new Dimension((fullSize.width + 1) / 2, (fullSize.height + 1) / 2); } for (int i = 0; i < numLevels; i++) { params.setValue(AVKey.LEVEL_NAME, i < numEmptyLevels ? "" : Integer.toString(i - numEmptyLevels)); params.setValue(AVKey.LEVEL_NUMBER, i); params.setValue(AVKeyMore.LEVEL_WIDTH, levelSizes[i].width); params.setValue(AVKeyMore.LEVEL_HEIGHT, levelSizes[i].height); this.levels.add(new CurtainLevel(params)); } if (inactiveLevels != null) { for (String s : inactiveLevels) { int i = Integer.parseInt(s); this.getLevel(i).setActive(false); } } } public CurtainLevelSet(CurtainLevelSet source) { if (source == null) { String msg = Logging.getMessage("nullValue.LevelSetIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } for (CurtainLevel level : source.levels) { this.levels.add(level); // Levels are final, so it's safe to copy references. } } @Override public Object setValue(String key, Object value) { // Propogate the setting to all levels for (CurtainLevel level : this.levels) { level.setValue(key, value); } return super.setValue(key, value); } @Override public Object getValue(String key) { Object value = super.getValue(key); if (value != null) return value; // See if any level has it for (CurtainLevel level : this.getLevels()) { if (level != null && (value = level.getValue(key)) != null) return value; } return null; } public final List<CurtainLevel> getLevels() { return this.levels; } public final CurtainLevel getLevel(int levelNumber) { return (levelNumber >= 0 && levelNumber < this.levels.size()) ? this.levels.get(levelNumber) : null; } public final int getNumLevels() { return this.levels.size(); } public final CurtainLevel getFirstLevel() { return this.getLevel(0); } public final CurtainLevel getLastLevel() { return this.getLevel(this.getNumLevels() - 1); } public final CurtainLevel getNextToLastLevel() { return this.getLevel(this.getNumLevels() > 1 ? this.getNumLevels() - 2 : 0); } public final boolean isFinalLevel(int levelNum) { return levelNum == this.getNumLevels() - 1; } public final boolean isLevelEmpty(int levelNumber) { return this.levels.get(levelNumber).isEmpty(); } private int numColumnsInLevel(CurtainLevel level) { return level.getColumnCount(); } private long getTileNumber(CurtainTile tile) { return tile.getRow() < 0 ? -1 : tile.getRow() * this.numColumnsInLevel(tile.getLevel()) + tile.getColumn(); } private long getTileNumber(TileKey tileKey) { return tileKey.getRow() < 0 ? -1 : tileKey.getRow() * this.numColumnsInLevel(this.getLevel(tileKey.getLevelNumber())) + tileKey.getColumn(); } /** * Instructs the level set that a tile is likely to be absent. * * @param tile * The tile to mark as having an absent resource. * * @throws IllegalArgumentException * if <code>tile</code> is null */ public final void markResourceAbsent(CurtainTile tile) { if (tile == null) { String msg = Logging.getMessage("nullValue.TileIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } tile.getLevel().markResourceAbsent(this.getTileNumber(tile)); } /** * Indicates whether a tile has been marked as absent. * * @param tileKey * The key of the tile in question. * * @return <code>true</code> if the tile is marked absent, otherwise * <code>false</code>. * * @throws IllegalArgumentException * if <code>tile</code> is null */ public final boolean isResourceAbsent(TileKey tileKey) { if (tileKey == null) { String msg = Logging.getMessage("nullValue.TileKeyIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } CurtainLevel level = this.getLevel(tileKey.getLevelNumber()); return level.isEmpty() || level.isResourceAbsent(this.getTileNumber(tileKey)); } /** * Indicates whether a tile has been marked as absent. * * @param tile * The tile in question. * * @return <code>true</code> if the tile is marked absent, otherwise * <code>false</code>. * * @throws IllegalArgumentException * if <code>tile</code> is null */ public final boolean isResourceAbsent(CurtainTile tile) { if (tile == null) { String msg = Logging.getMessage("nullValue.TileIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } return tile.getLevel().isEmpty() || tile.getLevel().isResourceAbsent(this.getTileNumber(tile)); } /** * Removes the absent-tile mark associated with a tile, if one is * associatied. * * @param tile * The tile to unmark. * * @throws IllegalArgumentException * if <code>tile</code> is null */ public final void unmarkResourceAbsent(CurtainTile tile) { if (tile == null) { String msg = Logging.getMessage("nullValue.TileIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } tile.getLevel().unmarkResourceAbsent(this.getTileNumber(tile)); } // Create the Segment corresponding to a specified key. public Segment computeSegmentForKey(TileKey key) { if (key == null) { String msg = Logging.getMessage("nullValue.KeyIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } CurtainLevel level = this.getLevel(key.getLevelNumber()); return level.computeSegmentForKey(key); } public void setExpiryTime(long expiryTime) { for (CurtainLevel level : this.levels) { level.setExpiryTime(expiryTime); } } }