package org.geotools.tile.cache;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.geometry.Envelope2D;
import org.geotools.tile.TileDraw;
import org.geotools.util.SimpleInternationalString;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import com.vividsolutions.jts.geom.Envelope;
/**
* Default that simply caches on TileRange.
* <p>
* </p>
* @author Jody Garnett, Refractions Research Inc.
*/
public class SimpleTileCache implements TileCache {
DirectTileRange cached = null;
public TileRange createRange( TileDraw draw, Rectangle range ) {
Set loaded = cacheHits( draw, range );
List tiles = createClearRange(draw, range);
DirectTileRange tileRange = new DirectTileRange( draw, range, tiles, loaded );
if( tileRange.equals( cached )){
return cached;
}
else {
cached = tileRange;
}
return tileRange;
}
synchronized GridCoverage2D cacheHit( String name ){
if( cached == null ) return null;
for( Iterator i = cached.getTiles().iterator(); i.hasNext(); ){
GridCoverage2D tile = (GridCoverage2D) i.next();
if( name.equals( tile.getName().toString() )){
return tile;
}
}
return null;
}
synchronized Set cacheHits( TileDraw draw, Rectangle range ) {
if( cached == null ) return Collections.EMPTY_SET;
Set hits = new HashSet( range.width*range.height);
for( int col = (int)range.x; col<range.getMaxX(); col++){
for( int row = (int)range.y; row<range.getMaxY(); row++){
String name = draw.name(row, col);
if( cached.loaded.contains(name) ){
hits.add( name );
}
}
}
return hits;
}
synchronized List createClearRange( TileDraw draw, Rectangle range ) {
List clear = new ArrayList( range.width*range.height);
for( int col = (int)range.x; col<range.getMaxX(); col++){
for( int row = (int)range.y; row<range.getMaxY(); row++){
String name = draw.name(row, col);
GridCoverage2D hit = cacheHit( name );
if( hit != null ){
clear.add( hit );
}
else {
clear.add( draw.drawPlaceholder( row, col ) );
}
}
}
return clear;
}
public void flushTiles( TileDraw draw ) {
cached = null;
}
public void retireTiles( TileDraw draw ) {
cached = null;
}
public void close() {
cached = null;
}
class DirectTileRange implements TileRange {
private TileDraw draw;
private Rectangle range;
/**
* List of GridCoverage2D defined by this TileRange.
* <p>
* List is used in order to provide the following mapping:
* tiles.get(
* </p>
*/
private List tiles;
/** names of tiles already loaded */
Set loaded;
boolean isLoaded;
/**
* Tile range will be created with "placeholders"; to retrive content
* use the load method.
*
* @param draw Stratagy object used to produce GridCoverages
* @param range Range of tiles to produce
*/
DirectTileRange( TileDraw draw, Rectangle range, List tiles, Set loaded ){
this.draw = draw;
this.range = range;
this.isLoaded = false;
this.tiles = tiles;
this.loaded = loaded.isEmpty() ? new HashSet() : loaded;
}
public Rectangle getRange() {
return range;
}
public Envelope getBounds() {
Envelope bounds = new Envelope();
double x,y;
for( Iterator i=tiles.iterator();i.hasNext();){
GridCoverage2D tile = (GridCoverage2D) i.next();
Envelope2D area = tile.getEnvelope2D();
//System.out.println( tile.getName() + " --> "+ area );
x = area.getMinX();
y = area.getMinY();
bounds.expandToInclude( x,y );
x = area.getMaxX();
y = area.getMaxY();
bounds.expandToInclude( x, y );
}
//System.out.println( range + " --> "+bounds );
return bounds;
}
public Envelope2D getEnvelope2D() {
Envelope2D bounds = null;
for( Iterator i=tiles.iterator();i.hasNext();){
GridCoverage2D tile = (GridCoverage2D) i.next();
if( bounds == null){
bounds = new Envelope2D( tile.getEnvelope() );
}
else {
bounds.add( tile.getEnvelope2D() );
}
}
return bounds;
}
public String toString() {
return "DirectTileRange("+range+")";
}
public boolean equals( Object obj ) {
if( obj == this ) return true;
if( obj == null || !(obj instanceof DirectTileRange)){
return false;
}
DirectTileRange other = (DirectTileRange) obj;
return this.draw == other.draw && this.range.equals( other.range );
}
public int hashCode() {
return draw.hashCode() | range.hashCode() << 3;
}
/**
* Set up available GridCoverage2d.
* <p>
* An entry is provided for every tile, even if just a placeholder.
*/
public Set getTiles() {
HashSet set = new HashSet( tiles );
return Collections.unmodifiableSet( set);
}
public boolean isLoaded() {
return isLoaded;
}
/**
* Load tiles; this will replace existing placeholders.
*/
public void load( IProgressMonitor monitor ) {
fetchTiles(monitor, "Loading " );
this.isLoaded = true;
}
/**
* Refresh tiles; this will update existing contents.
*/
public void refresh( IProgressMonitor monitor ) {
if( isLoaded ){
loaded.clear();
fetchTiles(monitor, "Refresh" );
}
else {
if( monitor == null ) monitor = new NullProgressMonitor();
monitor.setTaskName( "Load already in progress" );
monitor.isCanceled();
}
}
private void fetchTiles( IProgressMonitor monitor, String message ) {
if( monitor == null ) monitor = new NullProgressMonitor();
int count =0;
int total = range.width * range.height;
monitor.beginTask("Fetch Tiles", total );
try {
for( int col = (int)range.x; col<range.getMaxX(); col++){
for( int row = (int)range.y; row<range.getMaxY(); row++){
if( monitor.isCanceled() ) {
return;
}
String name = draw.name(row, col);
if( loaded.contains( name )){
count++;
monitor.worked( 1 );
}
else {
monitor.setTaskName(name);
GridCoverage2D tile = draw.drawTile(row, col );
tiles.set( count, tile );
loaded.add(name);
count++;
monitor.worked( 1 );
}
}
}
} finally{
monitor.done();
}
}
}
}