Package org.locationtech.udig.catalog.wmsc.server

Source Code of org.locationtech.udig.catalog.wmsc.server.AbstractTileRange

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2008-2011, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/

package org.locationtech.udig.catalog.wmsc.server;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.commons.collections.MapUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.geotools.data.ows.AbstractOpenWebService;

import com.vividsolutions.jts.geom.Envelope;

/**
* An AbstractTileRange represents a set of Tiles in a given bounds. 
* This TileRange is responsible for fetching the individual tiles if they are not yet
* "complete" and ready to paint, which it does through a TiledWebMapServer.  The
* TileRange really only lives for the cycle of fetching all the tiles.  Once the
* TileRange has finished loading (when all the tiles are complete), its use is
* pretty much done.
*
* Every time a tile is "completed", all listeners will be notified.
*
* @author GDavis
* @since 1.2.0
*/
public abstract class AbstractTileRange implements TileRange {

    protected Map<String, Tile> allTiles;
    protected Envelope bounds;
    protected AbstractOpenWebService<?,?> server;
    protected TileSet tileset;
    protected TileWorkerQueue requestTileWorkQueue; // queue of threads for requesting tiles
    protected boolean using_threadpools = false// set in the constructor
   
    protected final static boolean testing = false// for testing output
    /** keep track of all tile listeners **/
    protected Set<TileListener> tileListeners = new HashSet<TileListener>();   
   
    /**
     * This lock controls access to tilesWaitingToLoad.
     */
    protected ReentrantReadWriteLock tilesWaitingToLoad_lock = new ReentrantReadWriteLock();
   
    /**
     * Map of tiles that are not yet loaded; may be empty.
     */
    protected volatile Map<String, Tile> tilesWaitingToLoad = new HashMap<String, Tile>();
    
    /**
     * A TileRange implementation where you can fill in the fetching protocol.
     * @param server The Server to fetch the tiles from
     * @param tileset TileSet of rendered content
     * @param bounds Optional bounds (may be null) that contain the provided tiles
     * @param tiles The tiles that we wish to fetch; must be from the provided tileset
     * @param requestTileWorkQueue Queue of worker threads that can be used to fetch tiles
     */
    public AbstractTileRange(AbstractOpenWebService<?,?> server, TileSet tileset, Envelope bounds,
            Map<String, Tile> tiles, TileWorkerQueue requestTileWorkQueue) {
        this.server = server;
        this.tileset = tileset;
        if (tiles != null) {
            this.allTiles = tiles;
        }
        else {
            this.allTiles = new HashMap<String, Tile>();
        }
        if (bounds != null) {
            this.bounds = bounds;
        }
        else {
            this.bounds = calculateBounds();
        }
    if (requestTileWorkQueue == null) {
      using_threadpools = false;
      //this.requestTileWorkQueue = new TileWorkerQueue(TileWorkerQueue.defaultWorkingQueueSize);
    }
    else {
      using_threadpools = true;
      this.requestTileWorkQueue = requestTileWorkQueue;
    }       
       
        // calculate what tiles are not yet loaded
        setTilesNotLoaded();
    }
   
    /**
     * Go through every tile and pick out the ones that are not yet loaded and set
     * them in the notLoaded list.
     */
    protected void setTilesNotLoaded() {
        try {
            tilesWaitingToLoad_lock.writeLock().lock();       
          for( Iterator<Entry<String, Tile>> iterator = allTiles.entrySet().iterator(); iterator.hasNext(); ) {
              Entry<String, Tile> tileentry = (Entry<String, Tile>) iterator.next();
              if (tileentry.getValue().getBufferedImage() == null){
                  tilesWaitingToLoad.put(tileentry.getKey(), tileentry.getValue());
              }  
          }
        } finally {
            // unlock the write lock
            tilesWaitingToLoad_lock.writeLock().unlock();
        }         
    }

    /**
     * This is the entry point of this class once it has been created.  It will
     * ensure all the tiles are loaded.  If a tile is not yet loaded it will
     * create a request thread to fetch it, blocking until all the tiles are loaded.
     *
     * @param monitor
     */
    public void loadTiles(IProgressMonitor monitor) {           
        // load each tile not yet loaded.  Lock on the list of
        // tiles to load.
        Set<String> removeTiles = new HashSet<String>();    
        try {
            tilesWaitingToLoad_lock.writeLock().lock()
            // now that we have a lock, check if this has been canceled while waiting
            if (monitor.isCanceled()) {
                dispose();
                return;
            }
           
            Set<Entry<String, Tile>> entrySet = tilesWaitingToLoad.entrySet();
            for( Entry<String, Tile> set : entrySet ) {
                String tileid = set.getKey();
                Tile tile = set.getValue();
               
                // only send a request to get the tile if we don't have it already
                if (tile.getBufferedImage() == null) {
                    try {
                        loadTile(tile, new NullProgressMonitor());
                    } catch (Exception e) {
                        // there was some error getting the tile, so instead of blocking
                        // forever because of the error, set the tile to be removed
                      // from the loading list and continue.
                        e.printStackTrace();
                        removeTiles.add(tileid);
                    }
                }else{
                    //we already have this image; somehow it was filled in for us between when we first
                    //asked for tiles and now.  So we need to notify any listeners
                    tileLoaded(tile);
                    removeTiles.add(tileid); // set tile to be removed from loading list
                }
            }        
            // remove all successfully completed tiles and any error tiles from the list
            // of loading tiles to prevent endless blocking.
            for (String key : removeTiles) {
                tilesWaitingToLoad.remove(key);
            }
        } finally {
            // unlock the write lock
            // until this is released any jobs would get stuck waiting to update it
            tilesWaitingToLoad_lock.writeLock().unlock();
        }
    }
   
    /**
     * remove all listeners and do any other cleanup
     */
    public void dispose() {
        // remove all listeners
        for (TileListener listener : tileListeners) {
            removeListener(listener);
        }
    }
   
    /**
     * Notify any listeners and update the tile list that the given
     * tile is now loaded
     *
     * @param tile
     */
    public void tileLoaded( Tile tile ) {
        // notify any listeners that the tile is ready
        notifyListenersTileReady(tile);
    }

    /**
     * Remove the given tile from the loading list and ensure the lock is obtained before
     * modifying it.
     *
     * @return
     */
    protected void removeTileFromLoadingList( Tile tile) {
      // get a write lock on the tile loading list to remove this tile
        // from the loading list
        try {
          tilesWaitingToLoad_lock.writeLock().lock();
          tilesWaitingToLoad.remove(tile.getId());
        } finally {
          tilesWaitingToLoad_lock.writeLock().unlock();
        }       
    }

    protected Envelope calculateBounds() {
        Envelope newBounds = new Envelope();
        for (String key : allTiles.keySet()) {
            newBounds.expandToInclude(allTiles.get(key).getBounds());
        }
        return newBounds;
    }
   
    public Tile getTile(String tileId) {
        if (tileId == null) {
            return null;
        }
        return allTiles.get(tileId);
    }
   
    /**
     * Returns the tiles in the given range as an unmodifiable
     * map.
     *
     * @return
     */
    @SuppressWarnings("unchecked")
    public Map<String, Tile> getTiles() {
        return MapUtils.unmodifiableMap(allTiles);
    }   
   
    public int getTileCount() {
        return allTiles.keySet().size();
    }
   
    /**
     * Returns whether every tile in the range is complete and ready
     * to paint
     *
     * @return boolean
     */
    public boolean isComplete() {
        for( Iterator<Entry<String, Tile>> iterator = allTiles.entrySet().iterator(); iterator.hasNext(); ) {
            Entry<String, Tile> value = (Entry<String, Tile>) iterator.next();
            if (value.getValue().getBufferedImage() == null){
                return false;
            }
        }
        return true;
    }
   
    public Envelope getRangeBounds() {
        return bounds;
    }
   
    /**
     * This method will see if we are using thread pools and either create a runnable
     * to load the tile using the pool or create a single thread to do it.  It will
     * lock on the tile. 
     *
     * @param tile
     * @param monitor
     * @throws Exception
     */
    protected void loadTile( final Tile tile, final IProgressMonitor monitor ) throws Exception {

      if (using_threadpools) {
        Runnable r = new Runnable() {
              public void run() {
                internalLoadTile(tile, monitor);
              }
          };
          requestTileWorkQueue.execute(r);
      }
      else {
        Thread t = new Thread() {
          @Override
          public void run() {
            internalLoadTile(tile, monitor);
          }
        };
        t.start();
      }
    }
   
    /**
     * Do the work of loading a tile
     *
     * @param tile
     * @param monitor
     */
    private void internalLoadTile(final Tile tile, final IProgressMonitor monitor) {
      tile.loadTile(monitor);

        // now that the tile lock is unlocked, notify any listeners and
        // update the not loaded list (we want to do this whether we got
        // the tile or not because we don't want the blocking queue
        // in the rendering to wait blocked forever for missing tiles)
        tileLoaded(tile);
        removeTileFromLoadingList(tile);
        cacheTile(tile)// cache the tile with whatever method is setup
    }
   
    protected BufferedImage createErrorImage(){
        BufferedImage bf = new BufferedImage(tileset.getWidth(), tileset.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = bf.createGraphics();
        g.setColor(Color.RED);
        g.drawLine(0, 0, tileset.getWidth(), tileset.getHeight());
        g.drawLine(0, tileset.getHeight(), tileset.getWidth(), 0);
        return bf;
    }
   
    /**
     * Notify all tile listeners that a tile can now be drawn
     *
     * @param tile to draw
     */
    protected void notifyListenersTileReady( Tile tile ) {
        for (TileListener listener : tileListeners) {
            listener.notifyTileReady(tile);
        }
    }   
   
    public void addListener(TileListener listener) {
        tileListeners.add(listener);
    }
   
    public void removeListener(TileListener listener) {
        tileListeners.remove(listener);
    }      

}
TOP

Related Classes of org.locationtech.udig.catalog.wmsc.server.AbstractTileRange

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.