package org.geotools.tile;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.tile.cache.TileRange;
import com.vividsolutions.jts.geom.Envelope;
/**
* Set of tiles at a provided ZoomLevel.
* <p>
* This TIleSet can be understood according to:
* <ul>
* <li>getMap().getBounds() - Envelope including CRS
* <li>getZoomLevel().getRows();
* <li>getZoomLevel().getRows();
* </ul>
*/
public final class TileSet {
TileMap tileMap;
ZoomLevel level; // valid according to metadata
TileDraw draw; // provided from stratagy
private TileServer server;
protected TileSet( TileServer server, TileMap tileMap, ZoomLevel level ){
this.server = server;
this.tileMap = tileMap;
this.level = level;
this.draw = server.protocol.getTileDraw( this );
}
public TileMap getTileMap(){
return tileMap;
}
public ZoomLevel getZoomLevel(){
return level;
}
/**
* Request tiles in a provided range
* @param bbox Understood to match CRS
* @return
*/
public TileRange getTileRange( Rectangle range ){
limitRange( range );
return server.cache.createRange( draw, range );
}
/**
* Request tiles in a provided range
*
* @param bbox Understood to match CRS
* @return
*/
public TileRange getTileRange( Envelope bbox ){
Envelope bounds = tileMap.getInfo().getBounds();
Envelope area = bounds.intersection( bbox );
DirectPosition2D areaMin = new DirectPosition2D( area.getMinX(), area.getMinY() );
DirectPosition2D areaMax = new DirectPosition2D( area.getMaxX(), area.getMaxY() );
Point min = tileMin( areaMin );
Point max = tileMax( areaMax );
Rectangle range = new Rectangle( min );
range.add( max );
limitRange( range );
return getTileRange( range );
}
/** Used to limit the range to 3x3 tiles */
private void limitRange( Rectangle range ) {
if( range.width > 3 ){
range.x += range.width/2-1;
range.width = 3;
}
if( range.height > 3 ){
range.y += range.height/2-1;
range.height = 3;
}
}
/**
* Find the minimum tile with information about the provided pt.
*
* @param pt Position (in CRS)
* @return Point( col, row ) for the provided position
*/
private Point tileMin( DirectPosition2D pt ) {
Point2D ratio = toRatio( pt );
int col = level.getColFromRatio( ratio.getY() );
int row = level.getRowFromRatio( ratio.getX() );
return new Point( col, row );
}
/**
* Find the lower left tile coordinate for the provided pt
*
* @param pt Position (in CRS)
* @return Point( col, row ) for the provided position
*/
private Point tileMax( DirectPosition2D pt ){
Point2D ratio = toRatio( pt );
int col = level.getColFromRatio( ratio.getY() );
int row = level.getRowFromRatio( ratio.getX() );
if( ratio.getX() == (double)col/(double)level.getNumberOfColumns() ){
// we are exactly on the edge
}
else {
// we need to include up to the boundary of the next tile
col += (col< level.getNumberOfColumns() ? 1 : 0);
}
if( ratio.getY() == (double)row/(double)level.getNumberOfRows() ){
// we are exactly on the tile boundary
}
else {
// we need to include up to the boundary of the next tile
row += (row < level.getNumberOfRows() ? 1 : 0);
}
return new Point( col, row );
}
private Point2D toRatio( DirectPosition2D pt ) {
Envelope bounds = tileMap.getInfo().getBounds();
double dx = pt.getX() - bounds.getMinX();
double dy = pt.getY() - bounds.getMinY();
double colRatio = dx / bounds.getWidth();
double rowRatio = dy / bounds.getHeight();
return new Point2D.Double( rowRatio, colRatio );
}
}