/* Copyright (C) 2001, 2006 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.util; import gov.nasa.worldwind.cache.Cacheable; import gov.nasa.worldwind.geom.*; import java.util.Random; /** * @author tag * @version $Id: Tile.java 5055 2008-04-14 05:19:11Z tgaskins $ */ public class Tile implements Comparable<Tile>, Cacheable { private final Sector sector; private final Level level; private final int row; private final int column; private final TileKey tileKey; private double priority = Double.MAX_VALUE; // Default is minimum priority // The following is late bound because it's only selectively needed and costly to create private String path; public Tile(Sector sector, Level level, int row, int column) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (level == null) { String msg = Logging.getMessage("nullValue.LevelIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (row < 0) { String msg = Logging.getMessage("generic.RowIndexOutOfRange", row); msg += String.valueOf(row); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (column < 0) { String msg = Logging.getMessage("generic.ColumnIndexOutOfRange", column); msg += String.valueOf(row); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } this.sector = sector; this.level = level; this.row = row; this.column = column; this.tileKey = new TileKey(this); this.path = null; } public Tile(Sector sector, Level level) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (level == null) { String msg = Logging.getMessage("nullValue.LevelIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } this.sector = sector; this.level = level; this.row = Tile.computeRow(sector.getDeltaLat(), sector.getMinLatitude()); this.column = Tile.computeColumn(sector.getDeltaLon(), sector.getMinLongitude()); this.tileKey = new TileKey(this); this.path = null; } public Tile(Sector sector) { if (sector == null) { String msg = Logging.getMessage("nullValue.SectorIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } Random random = new Random(); this.sector = sector; this.level = null; this.row = random.nextInt(); this.column = random.nextInt(); this.path = null; this.tileKey = new TileKey(this); } public long getSizeInBytes() { // Return just an approximate size long size = 0; if (this.sector != null) size += this.sector.getSizeInBytes(); if (this.path != null) size += this.getPath().length(); size += 32; // to account for the references and the TileKey size return size; } public String getPath() { if (this.path == null) { this.path = this.level.getPath() + "/" + this.row + "/" + this.row + "_" + this.column; if (!this.level.isEmpty()) path += this.level.getFormatSuffix(); } return this.path; } public String getPathBase() { String path = this.getPath(); return path.substring(0, path.lastIndexOf(".")); } public final Sector getSector() { return sector; } public Level getLevel() { return level; } public final int getLevelNumber() { return this.level != null ? this.level.getLevelNumber() : 0; } public final String getLevelName() { return this.level != null ? this.level.getLevelName() : ""; } public final int getRow() { return row; } public final int getColumn() { return column; } public final String getCacheName() { return this.level != null ? this.level.getCacheName() : null; } public final String getFormatSuffix() { return this.level != null ? this.level.getFormatSuffix() : null; } public final TileKey getTileKey() { return this.tileKey; } public java.net.URL getResourceURL() throws java.net.MalformedURLException { return this.level != null ? this.level.getTileResourceURL(this, null) : null; } public java.net.URL getResourceURL(String imageFormat) throws java.net.MalformedURLException { return this.level != null ? this.level.getTileResourceURL(this, imageFormat) : null; } public String getLabel() { StringBuilder sb = new StringBuilder(); sb.append(this.getLevelNumber()); sb.append("("); sb.append(this.getLevelName()); sb.append(")"); sb.append(", ").append(this.getRow()); sb.append(", ").append(this.getColumn()); return sb.toString(); } public int compareTo(Tile tile) { if (tile == null) { String msg = Logging.getMessage("nullValue.TileIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } // No need to compare Sectors or path because they are redundant with row and column if (tile.getLevelNumber() == this.getLevelNumber() && tile.row == this.row && tile.column == this.column) return 0; if (this.getLevelNumber() < tile.getLevelNumber()) // Lower-res levels compare lower than higher-res return -1; if (this.getLevelNumber() > tile.getLevelNumber()) return 1; if (this.row < tile.row) return -1; if (this.row > tile.row) return 1; if (this.column < tile.column) return -1; return 1; // tile.column must be > this.column because equality was tested above } @Override public boolean equals(Object o) { // Equality based only on the tile key if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Tile tile = (Tile) o; return !(tileKey != null ? !tileKey.equals(tile.tileKey) : tile.tileKey != null); } @Override public int hashCode() { return (tileKey != null ? tileKey.hashCode() : 0); } @Override public String toString() { return this.getPath(); } /** * Computes the row index of a latitude in the global tile grid corresponding to a specified grid interval. * * @param delta the grid interval * @param latitude the latitude for which to compute the row index * @return the row index of the row containing the specified latitude * @throws IllegalArgumentException if <code>delta</code> is null or non-positive, or <code>latitude</code> is null, * greater than positive 90 degrees, or less than negative 90 degrees */ public static int computeRow(Angle delta, Angle latitude) { if (delta == null || latitude == null) { String message = Logging.getMessage("nullValue.AngleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (delta.degrees <= 0d) { String message = Logging.getMessage("generic.DeltaAngleOutOfRange", delta); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (latitude.degrees < -90d || latitude.degrees > 90d) { String message = Logging.getMessage("generic.AngleOutOfRange", latitude); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (latitude.degrees == 90d) return (int) (180d / delta.degrees) - 1; else return (int) ((latitude.degrees + 90d) / delta.degrees); } /** * Computes the column index of a longitude in the global tile grid corresponding to a specified grid interval. * * @param delta the grid interval * @param longitude the longitude for which to compute the column index * @return the column index of the column containing the specified latitude * @throws IllegalArgumentException if <code>delta</code> is null or non-positive, or <code>longitude</code> is * null, greater than positive 180 degrees, or less than negative 180 degrees */ public static int computeColumn(Angle delta, Angle longitude) { if (delta == null || longitude == null) { String message = Logging.getMessage("nullValue.AngleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (delta.degrees <= 0d) { String message = Logging.getMessage("generic.DeltaAngleOutOfRange", delta); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (longitude.degrees < -180d || longitude.degrees > 180d) { String message = Logging.getMessage("generic.AngleOutOfRange", longitude); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (longitude.degrees == 180d) return (int) (360d / delta.degrees) - 1; else return (int) ((longitude.degrees + 180d) / delta.degrees); } /** * Determines the minimum latitude of a row in the global tile grid corresponding to a specified grid interval. * * @param row the row index of the row in question * @param delta the grid interval * @return the minimum latitude of the tile corresponding to the specified row * @throws IllegalArgumentException if the grid interval (<code>delta</code>) is null or zero or the row index is * negative. */ public static Angle computeRowLatitude(int row, Angle delta) { if (delta == null) { String message = Logging.getMessage("nullValue.AngleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (row < 0) { String msg = Logging.getMessage("generic.RowIndexOutOfRange", row); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (delta.degrees <= 0d) { String message = Logging.getMessage("generic.DeltaAngleOutOfRange", delta); Logging.logger().severe(message); throw new IllegalArgumentException(message); } return Angle.fromDegrees(-90d + delta.degrees * row); } /** * Determines the minimum longitude of a column in the global tile grid corresponding to a specified grid interval. * * @param column the row index of the row in question * @param delta the grid interval * @return the minimum longitude of the tile corresponding to the specified column * @throws IllegalArgumentException if the grid interval (<code>delta</code>) is null or zero or the column index is * negative. */ public static Angle computeColumnLongitude(int column, Angle delta) { if (delta == null) { String message = Logging.getMessage("nullValue.AngleIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (column < 0) { String msg = Logging.getMessage("generic.ColumnIndexOutOfRange", column); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (delta.degrees <= 0d) { String message = Logging.getMessage("generic.DeltaAngleOutOfRange", delta); Logging.logger().severe(message); throw new IllegalArgumentException(message); } return Angle.fromDegrees(-180 + delta.degrees * column); } public double getPriority() { return priority; } public void setPriority(double priority) { this.priority = priority; } }