gov.nasa.worldwind.util.Tile Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 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;
/**
* Large images and most imagery and elevation-data sets are subdivided in order to display visible portions quickly and
* without excessive memory usage. Each subdivision is called a tile, and a collections of adjacent tiles corresponding
* to a common spatial resolution is typically maintained in a {@link Level}. A collection of levels of progressive
* resolutions are maintained in a {@link LevelSet}. The Tile
class represents a single tile of a
* subdivided image or elevation raster.
*
* Individual tiles are identified by the level, row and column of the tile within its containing level set.
*
* @author tag
* @version $Id: Tile.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class Tile implements Comparable, Cacheable
{
private final Sector sector;
private final Level level;
private final int row;
private final int column;
/** An optional cache name. Overrides the Level's cache name when non-null. */
private final String cacheName;
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;
/**
* Constructs a tile for a given sector, level, row and column of the tile's containing tile set.
*
* @param sector the sector corresponding with the tile.
* @param level the tile's level within a containing level set.
* @param row the row index (0 origin) of the tile within the indicated level.
* @param column the column index (0 origin) of the tile within the indicated level.
*
* @throws IllegalArgumentException if sector
or level
is null.
*/
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);
}
// // Allow negative row/col IDs to be used as a signal to prevent their use for non-arranged tiles
// 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.cacheName = null;
this.tileKey = new TileKey(this);
this.path = null;
}
/**
* Constructs a tile for a given sector, level, row and column of the tile's containing tile set. If the cache name
* is non-null, it overrides the level's cache name and is returned by {@link #getCacheName()}. Otherwise, the
* level's cache name is used.
*
* @param sector the sector corresponding with the tile.
* @param level the tile's level within a containing level set.
* @param row the row index (0 origin) of the tile within the indicated level.
* @param column the column index (0 origin) of the tile within the indicated level.
* @param cacheName optional cache name to override the Level's cache name. May be null.
*
* @throws IllegalArgumentException if sector
or level
is null.
*/
public Tile(Sector sector, Level level, int row, int column, String cacheName)
{
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 = row;
this.column = column;
this.cacheName = cacheName;
this.tileKey = new TileKey(this);
this.path = null;
}
/**
* Constructs a texture tile for a given sector and level, and with a default row and column.
*
* @param sector the sector to create the tile for.
* @param level the level to associate the tile with
*
* @throws IllegalArgumentException if sector or level are 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(), Angle.NEG90);
this.column = Tile.computeColumn(sector.getDeltaLon(), sector.getMinLongitude(), Angle.NEG180);
this.cacheName = null;
this.tileKey = new TileKey(this);
this.path = null;
}
/**
* Constructs a texture tile for a given sector with a default level, row and column.
*
* @param sector the sector to create the tile for.
*/
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.cacheName = null;
this.tileKey = new TileKey(this);
this.path = null;
}
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.contains(".") ? path.substring(0, path.lastIndexOf(".")) : path;
}
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;
}
/**
* Returns the tile's cache name. If a non-null cache name was specified at construction, that name is returned.
* Otherwise this returns the level's cache name.
*
* @return the tile's cache name.
*/
public final String getCacheName()
{
if (this.cacheName != null)
return this.cacheName;
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 getWidth()
{
return this.getLevel().getTileWidth();
}
public int getHeight()
{
return this.getLevel().getTileHeight();
}
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
* @param origin the origin of the grid
*
* @return the row index of the row containing the specified latitude
*
* @throws IllegalArgumentException if delta
is null or non-positive, or latitude
is null,
* greater than positive 90 degrees, or less than negative 90 degrees
*/
public static int computeRow(Angle delta, Angle latitude, Angle origin)
{
if (delta == null || latitude == null || origin == 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);
}
int row = (int) ((latitude.degrees - origin.degrees) / delta.degrees);
// Latitude is at the end of the grid. Subtract 1 from the computed row to return the last row.
if ((latitude.degrees - origin.degrees) == 180d)
row = row - 1;
return row;
}
/**
* 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
* @param origin the origin of the grid
*
* @return the column index of the column containing the specified latitude
*
* @throws IllegalArgumentException if delta
is null or non-positive, or longitude
is
* null, greater than positive 180 degrees, or less than negative 180 degrees
*/
public static int computeColumn(Angle delta, Angle longitude, Angle origin)
{
if (delta == null || longitude == null || origin == 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);
}
// Compute the longitude relative to the grid. The grid provides 360 degrees of longitude from the grid origin.
// We wrap grid longitude values so that the grid begins and ends at the origin.
double gridLongitude = longitude.degrees - origin.degrees;
if (gridLongitude < 0.0)
gridLongitude = 360d + gridLongitude;
int col = (int) (gridLongitude / delta.degrees);
// Longitude is at the end of the grid. Subtract 1 from the computed column to return the last column.
if ((longitude.degrees - origin.degrees) == 360d)
col = col - 1;
return col;
}
/**
* 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
* @param origin the origin of the grid
*
* @return the minimum latitude of the tile corresponding to the specified row
*
* @throws IllegalArgumentException if the grid interval (delta
) is null or zero or the row index is
* negative.
*/
public static Angle computeRowLatitude(int row, Angle delta, Angle origin)
{
if (delta == null || origin == 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);
}
double latDegrees = origin.degrees + (row * delta.degrees);
return Angle.fromDegrees(latDegrees);
}
/**
* 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
* @param origin the origin of the grid
*
* @return the minimum longitude of the tile corresponding to the specified column
*
* @throws IllegalArgumentException if the grid interval (delta
) is null or zero or the column index is
* negative.
*/
public static Angle computeColumnLongitude(int column, Angle delta, Angle origin)
{
if (delta == null || origin == 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);
}
double lonDegrees = origin.degrees + (column * delta.degrees);
return Angle.fromDegrees(lonDegrees);
}
public double getPriority()
{
return priority;
}
public void setPriority(double priority)
{
this.priority = priority;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy