package org.geotools.tile;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.tile.cache.TileRange;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Envelope;
/**
* Represents tiled content avaialble at a range of zoom levels.
* <p>
* Each zoom level has a corrasponding TileSet.
* </p>
* @author jgarnett
*/
public final class TileMap {
private TileMapInfo info;
private Map map; // from ZoomLevel to TileSet
private TileServer server;
public TileMap( TileServer server, TileMapInfo info ){
this.server = server;
this.info = info;
this.map = new HashMap();
}
CoordinateReferenceSystem getCRS(){
return info.getCRS();
}
public TileMapInfo getInfo(){
return info;
}
public synchronized TileSet getTileSet( ZoomLevel zoom ){
if( map.containsKey( zoom )){
return (TileSet) map.get( zoom );
}
TileSet tileset = new TileSet( server, this, zoom );
map.put( zoom, tileset );
return tileset;
}
public String toString() {
if( info != null ){
return "TileMap ("+info.getName()+")";
}
return "TileMap no data";
}
ZoomLevel getAppropriateScale( double scaleDenominator ){
// Sorted set so we can stop when our scaleDenominator is exceeded
int level = 0;
ZoomLevel zoomLevel= null;
for( Iterator i=info.getZoomLevels().iterator(); i.hasNext(); level++){
zoomLevel = (ZoomLevel) i.next();
double scale = zoomLevel.getScaleDenominator();
if( scaleDenominator >= scale ) {
return zoomLevel;
}
}
return zoomLevel; // most detailed level we have
}
/**
* Retrive data from the most appropriate TileSet for the provided scaleDenominator.
* <p>
* This supports "free form" zooming, the resulting TileSet will need resampling
* to match your display.
* <p>
* This method will try and choose a tile range from a tile set at a greater
* level of details then the scaleDenominator provided. This setting
* works perfectly for raster data; text labels or line work will suffer. As
* a work around you can "back off" your scaleDenominator a bit and work
* with a pixelated result.
* </p>
*
* @param zoom
* @return TileSet
*/
public TileRange getTileRange( Envelope bounds, double scaleDenominator ) {
ZoomLevel bestFit = getAppropriateScale( scaleDenominator );
if( bestFit == null ) return TileRange.EMPTY;
// TODO confirm behavior of scaledenominator
//bestFit = (ZoomLevel) info.getZoomLevels().iterator().next();
TileSet tileSet = getTileSet( bestFit );
return tileSet.getTileRange( bounds );
}
}