Package org.locationtech.udig.project.ui.internal

Source Code of org.locationtech.udig.project.ui.internal.TiledRenderManagerDynamic$RenderTileJob

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2004-2008, 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.project.ui.internal;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.locationtech.udig.project.ILayer;
import org.locationtech.udig.project.internal.ContextModelListenerAdapter;
import org.locationtech.udig.project.internal.Layer;
import org.locationtech.udig.project.internal.Map;
import org.locationtech.udig.project.internal.ProjectPackage;
import org.locationtech.udig.project.internal.ProjectPlugin;
import org.locationtech.udig.project.internal.render.CompositeRenderContext;
import org.locationtech.udig.project.internal.render.ExecutorVisitor;
import org.locationtech.udig.project.internal.render.RenderExecutor;
import org.locationtech.udig.project.internal.render.RenderFactory;
import org.locationtech.udig.project.internal.render.RenderPackage;
import org.locationtech.udig.project.internal.render.RendererCreator;
import org.locationtech.udig.project.internal.render.SelectionLayer;
import org.locationtech.udig.project.internal.render.impl.CompositeRenderContextImpl;
import org.locationtech.udig.project.internal.render.impl.CompositeRendererImpl;
import org.locationtech.udig.project.internal.render.impl.RenderContextImpl;
import org.locationtech.udig.project.internal.render.impl.RenderExecutorComposite;
import org.locationtech.udig.project.internal.render.impl.RenderExecutorMultiLayer;
import org.locationtech.udig.project.internal.render.impl.RenderManagerImpl;
import org.locationtech.udig.project.internal.render.impl.TiledCompositeRendererImpl;
import org.locationtech.udig.project.internal.render.impl.TiledRendererCreatorImpl;
import org.locationtech.udig.project.internal.render.impl.UDIGLabelCache;
import org.locationtech.udig.project.internal.render.impl.TiledCompositeRendererImpl.RenderInfo;
import org.locationtech.udig.project.render.AbstractRenderMetrics;
import org.locationtech.udig.project.render.ILabelPainter;
import org.locationtech.udig.project.render.IRenderContext;
import org.locationtech.udig.project.render.IRenderer;
import org.locationtech.udig.project.render.Tile;
import org.locationtech.udig.project.render.TileStateChangedListener;
import org.locationtech.udig.project.render.displayAdapter.IMapDisplay;
import org.locationtech.udig.project.ui.ApplicationGIS;
import org.locationtech.udig.project.ui.internal.render.displayAdapter.impl.ViewportPaneTiledSWT;
import org.locationtech.udig.project.ui.render.displayAdapter.ViewportPane;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.renderer.label.LabelCacheImpl;
import org.geotools.util.ObjectCache;
import org.geotools.util.ObjectCaches;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;


/**
* A IRenderManager that is reacts to events such as viewport model changes and renders
* the screen in tiles.
*
*
* <p>
* These are the events we have found this class pays attention to:
* <ul>
* <li>viewportListner will trigger some kind of refresh when you change view
* size
* <li>renderExecuttorListener does not actually do much; it passes along
* something else that actually does the listening. The composite render
* executor
* <li>contextModelAdapter watches the IMap and notices new layers, deleted
* layers, zorder changing and the occasional style change
* </ul>
* The Map owns one of these things; uses it to scribble on the screen to the
* user.
*
* <p>The tiling system used by this system is based on a tile size of 512x512 pixels and a "center" coordinate
* of (0,0)</p>
*
* @generated
*/
public class TiledRenderManagerDynamic extends RenderManagerImpl {
   
    /**
     *
     * The size of the tiles to use.
     */
    private final static int TILE_SIZE = 512;
      
    /**
     * A "center" coordinate to use for the tile system.
     */
    private Coordinate tileCenter = new Coordinate(0,0);
   
    /**
     * Watches the layer add / delete / change of zorder and style changes.
     */
    ContextModelListenerAdapter contextModelAdapter = RenderManagerAdapters.createContextModelListener(this);
   
    /**
     * Watches the viewport model and class refresh(null) (triggers redrawing of the layers).
     * Examples of viewport model events is the bounds and the crs changing;
     * panning zooming swooshing and other assorted
     * user drive fun.
     */
    Adapter viewportListener = RenderManagerAdapters.createViewportListener(this);

    /**
     * Listens to the viewport model (using the provided viewportListener)
     * and morphing the events into something for the map to be updated with?
     *
     */
    private Adapter viewportModelChangeListener = RenderManagerAdapters.createViewportModelChangeListener(this, viewportListener,contextModelAdapter);

    /**
     * Listens for layer made visible change events and upates
     * tile states accordingly.
     */
    private Adapter visibilityChangedListener = RenderManagerAdapters.createVisibilityChangedAdapater(this);
   
    /**
     * Creates a new RenderExecutorListener which listens to render events
     * and kicks the viewport to updated.
     */
    Adapter renderExecutorListener = RenderManagerAdapters.getRenderExecutorListener(this);


    Adapter selectionListener = RenderManagerAdapters.createLayerListener(this);
   

    /**
     * This is a weak reference to all the tiles.
     *
     */
    ObjectCache tileCache = ObjectCaches.create("soft", 50); //Tiles that are on the screen //$NON-NLS-1$

    /**
     * This is a strong reference to the "important" tiles.  "Important" tiles and tiles
     * classified to be on screen or waiting to be on screen.
     */
    Set<Tile> importantTiles = Collections.synchronizedSet(new HashSet<Tile>());
   
    /**
     * This is our tile state changed listener what is responsible for maintaining
     * the important tiles collections.
     */
    TileStateChangedListener tileStateListener = new TileStateChangedListener(){
       
        public void screenStateChanged(Tile t){
            updateImportantTileList(t);
        }
       
        public void validationStateChanged(Tile t){
            updateImportantTileList(t);
        }
       
        public void renderStateChanged(Tile t){
            //this doesn't effect important tile list
        }
       
        public void contextStateChanged(Tile t){
            //this doesn't affect important tile list
        }
       
        /**
         * Updates the important tile list.  Either adds it or removes
         * it depending on the tile state.
         *
         * @param t
         */
        private void updateImportantTileList( Tile t ) {
            if (t.getScreenState() == Tile.ScreenState.ONSCREEN
                    || t.getTileState() == Tile.ValidatedState.VALIDATED) {
                importantTiles.add(t);
            } else {
                importantTiles.remove(t);
            }
        }
    };
   
    /**
     * Creates a new TiledRenderManagerDynamice and sets the render creator to null.  The render creator will
     * be set to a TiledRenderCreatorImpl when first used.
     *
     * @generated NOT
     */
    public TiledRenderManagerDynamic() {
        super();
        this.rendererCreator = null;
        eAdapters().add(viewportModelChangeListener);
    }
   
    public RendererCreator getRendererCreator() {
        checkState();
        if (rendererCreator == null){
            initRenderCreator();
        }
        return rendererCreator;
    }

    /**
     * Creates a new render creator and initializes it with the current layers on the map.
     *
     */
    private void initRenderCreator() {
        this.rendererCreator = new TiledRendererCreatorImpl(getTileSize(), this);
        List<Layer> layers = getMapInternal().getLayersInternal();
        // make sure renderer creator is initialized.
        ENotificationImpl notification = new ENotificationImpl(this,
                Notification.ADD_MANY, ProjectPackage.CONTEXT_MODEL__LAYERS,
                null, layers);
        getRendererCreator().changed(notification);
    }

   
    /**
     * Gets a tile from the cached list.  If the tile
     * does not exist a new tile is created
     * and added to the cached list.
     *

     *
     *
     * @param env
     * @return
     */
    private Tile getOrCreateTile(ReferencedEnvelope env){
       
        Tile tile = (Tile) tileCache.get(env);
        if (tile == null) {
            try {
                tileCache.writeLock(env);
                tile = (Tile) tileCache.peek(env);
                if (tile == null) {
                    tile = createTile(env);
                    tileCache.put(env, tile);          
                }
            } finally {
                tileCache.writeUnLock(env);
            }
        }
        return tile;
    }
   
    /**
     * Creates a new tile adding the listeners and putting it in the important tiles list.
     *
     * @param key
     * @return
     */
    private Tile createTile(ReferencedEnvelope key){
        RenderExecutorComposite newRE = createRenderExecutor(key);
        Tile tile = new Tile(key, newRE, getTileSize());
        tile.setStateChangedListener(tileStateListener);
        importantTiles.add(tile);
        ((RenderContextImpl)newRE.getContext()).setLabelPainterLocal(newRE.getContext().getLabelPainter());
        return tile;
    }
    /**
     * Refreshes all tiles within the given bounds for a particular layer.
     *
     * If refreshing this layer causes other layers to need refreshing then the other layers will be refreshed
     * too.  An example is that if a wms layer has been moved a refresh may result in multiple wms contexts in the rendering
     * stack; each which need to be refreshed.
     * @see org.locationtech.udig.project.render.impl.RenderManagerImpl#refresh(org.locationtech.udig.project.Layer)
     */
    public void refresh(final ILayer layer, Envelope bounds) {
        checkState();
        if (!renderingEnabled) {
            return;
        }

        if (getMapDisplay() == null || getMapDisplay().getWidth() < 1 || getMapDisplay().getHeight() < 1) {
            return; // we are not set up to renderer yet!
        }
       
        final SelectionLayer selectionLayer = getRendererCreator().findSelectionLayer(layer);
       
        //get only the tile in the bounds in the area of interest
        ReferencedEnvelope areaOfInterest = null;
        if (bounds == null){
            areaOfInterest = getMap().getViewportModel().getBounds();
        }else{
            areaOfInterest = new ReferencedEnvelope(bounds, getMap().getViewportModel().getCRS());
        }
        ReferencedEnvelope viewportbounds = getMap().getViewportModel().getBounds();
        double resolution = viewportbounds.getWidth() / getMapDisplay().getWidth();
       
        //get only the tile in the bounds in the area of interest
        Collection<ReferencedEnvelope> tileBounds = computeTileBounds(areaOfInterest, resolution);
        setTileStateToOld(tileBounds);
       
        for( ReferencedEnvelope env : tileBounds ) {
            //need to render the entire tile otherwise new additions to tile doesn't work properly
            final Envelope bbox = env;
            if (bbox.isNull()){
                //nothing to do; the bounding box of the tile does not intersect the area of intersect
                //this shouldn't occur but it does???  (maybe a double precision thing)
                continue;
            }
           
            Tile tile = getOrCreateTile(env);
           
            tile.setTileState(Tile.ValidatedState.VALIDATED);
            tile.setRenderState(Tile.RenderState.RENDERED);
           
            RenderExecutorComposite re = tile.getRenderExecutor();
           
            //validate the context if necessary
            List<AbstractRenderMetrics> torefresh = new ArrayList<AbstractRenderMetrics>();
            if (tile.getContextState() == Tile.ContextState.INVALID){
                torefresh = validateRendererConfiguration(tile);
                tile.setContextState(Tile.ContextState.OKAY);
            }
           
            //determine which layers need to be updated
            Collection<ILayer> layersToUpdate = new ArrayList<ILayer>();
            layersToUpdate.add(layer);
            if (selectionLayer != null) layersToUpdate.add(selectionLayer);
            if (torefresh != null){
                for( AbstractRenderMetrics metrics : torefresh ) {
                    layersToUpdate.add(metrics.getRenderContext().getLayer());
                }
            }
         
            for( final ILayer newlayer : layersToUpdate) {
                re.visit(new ExecutorVisitor(){
                    public void visit( RenderExecutor executor ) {
                        if (executor.getContext().getLayer() == newlayer) {
                            // layer to refresh
                            executor.getRenderer().setRenderBounds(bbox);
                            // register request
                            executor.getRenderer().setState(IRenderer.RENDER_REQUEST);
                        }
                    }

                    public void visit( RenderExecutorMultiLayer executor ) {
                        if (executor.getContext().getLayers().contains(newlayer)) {
                            // why no need to set the bounds here???
                            // register request
                            executor.getRenderer().setState(IRenderer.RENDER_REQUEST);
                        }
                    }

                    public void visit( RenderExecutorComposite executor ) {
                        // visit all the children executors
                        List<RenderExecutor> executors = new ArrayList<RenderExecutor>(executor
                                .getRenderer().getRenderExecutors());
                        for( RenderExecutor child : executors ) {
                            child.visit(this);
                        }
                    }
                });
            }
        }
    }
   
   

    /**
     *
     * Called when a selection layer is refreshed.
     * @see org.locationtech.udig.project.render.impl.RenderManagerImpl#refreshSelection(com.vividsolutions.jts.geom.Envelope)
     */
    public void refreshSelection(final ILayer layer, final Envelope bounds) {
       
        checkState();
        if (!renderingEnabled) {
            return;
        }

        if (getMapDisplay() == null || getMapDisplay().getWidth() < 1 || getMapDisplay().getHeight() < 1) {
            return; // we are not set up to renderer yet!
        }
       
        //find selection layer
        final SelectionLayer selectionLayer = getRendererCreator().findSelectionLayer(layer);
        if (selectionLayer == null) return;
               
        //redraw the ones we care about for now
        //determine area of interest
        ReferencedEnvelope areaOfInterest = null;
        if (bounds == null){
            areaOfInterest = getMap().getViewportModel().getBounds();
        }else{
            areaOfInterest= new ReferencedEnvelope(bounds, getMap().getViewportModel().getCRS());
        }
        ReferencedEnvelope viewportbounds = getMap().getViewportModel().getBounds();
        double resolution = viewportbounds.getWidth() / getMapDisplay().getWidth();
       
        //we need to re-render all tiles
        invalidateAllTilesRenderState();
       
        //get only the tile in the bounds in the area of interest
        Collection<ReferencedEnvelope> tileBounds = computeTileBounds(areaOfInterest, resolution);
        setTileStateToOld(tileBounds);
       
        for( ReferencedEnvelope env : tileBounds ) {
            Tile tile = getOrCreateTile(env);
           
            tile.setRenderState(Tile.RenderState.RENDERED);
            tile.setTileState(Tile.ValidatedState.VALIDATED);
            RenderExecutorComposite re = tile.getRenderExecutor();
        
            // creates the contexts
            if (tile.getContextState() == Tile.ContextState.INVALID){
                validateRendererConfiguration(tile);
                tile.setContextState(Tile.ContextState.OKAY);
            }

            final Envelope bbox = env;

            //set render requests for the selection layer
            re.visit(new ExecutorVisitor(){

                public void visit( RenderExecutor executor ) {
                    IRenderContext context = executor.getContext();
                    if (selectionLayer == context.getLayer()) {
                        executor.getRenderer().setRenderBounds(bbox);
                        // only clear the part of the image in the bounds??
                        context.clearImage();
                        executor.getRenderer().setState(IRenderer.RENDER_REQUEST);
                    }
                }

                public void visit( RenderExecutorMultiLayer executor ) {
                    CompositeRenderContext contexts = executor.getContext();
                    for( IRenderContext context : contexts.getContexts() ) {
                        if (context.getLayer() == selectionLayer) {
                            executor.getRenderer().setState(IRenderer.RENDER_REQUEST);
                            return;
                        }
                    }
                }

                public void visit( RenderExecutorComposite executor ) {
                    for( RenderExecutor child : executor.getRenderer().getRenderExecutors() ) {
                        child.visit(this);
                    }

                }
            });
        }  
    }

  
    /**
     * Clears the selection layer
     * @see org.locationtech.udig.project.render.IRenderManager#clearSelection(ILayer)
     */
    public void clearSelection(ILayer layer) {
        checkState();
        if (getMapDisplay() == null)
            return;
       
        //find selection layer
        final Layer selectionLayer = getRendererCreator().findSelectionLayer(layer);
        if (selectionLayer == null)
            return;     
       
        //tiles need to be re-rendered
        invalidateAllTilesRenderState();
       
        // refresh image
        refreshImage();
    }
   
    /**
     * Updates the validated state of all tiles whose key is not in the validTiles
     * list to ValidatedState.OLD.
     *
     * @param validTiles
     */
    private void setTileStateToOld( Collection<ReferencedEnvelope> validTiles ) {
        // mark all tiles as offscreen
        for( Iterator<Object> iterator = tileCache.getKeys().iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope key = (ReferencedEnvelope) iterator.next();
            Tile t = (Tile) tileCache.get(key);
            if (t != null && !validTiles.contains(key)) {
                t.setTileState(Tile.ValidatedState.OLD);
            }
        }
    }

    /**
     * Renders the collection of tiles in a separate different thread.
     *
     * <p>Currently this is used by the scroll panning tool
     * to render tiles as we pan around.</p>
     *
     * @param tiles
     */
    RenderTileJob tileRenderJob = null;
    public void renderTiles(Collection<Tile> tiles){
        if (tiles == null || tiles.isEmpty()) return;
        if (tileRenderJob == null){
            tileRenderJob = new RenderTileJob(this);
        }
        tileRenderJob.addTiles(tiles);
        tileRenderJob.schedule();
    }
    /**
     * Cancels all render jobs that are running in the background; that
     * were started by calling the renderTiles function.
     */
    public void cancelTileRenderJobs(){
        if (tileRenderJob != null){
            tileRenderJob.clear();
            tileRenderJob.cancel();
        }
    }
   
    /**
     * Renders a specific tile in the calling thread.
     * <p>The tile is only rendered if necessary (determined by the render
     * and context state).  The context
     * is also validated and updated if necessary.
     * </p>
     *
     * @param tile
     */
    public void renderTile( Tile tile ) {
        boolean rendertile = (tile.getRenderState() == Tile.RenderState.INVALID || tile.getRenderState() == Tile.RenderState.NEW);
        tile.setTileState(Tile.ValidatedState.VALIDATED);
        RenderExecutorComposite re = tile.getRenderExecutor();

        // creates the contexts - this function returns the "new" contexts; these are the ones
        // that need to be refreshed.
        List<AbstractRenderMetrics> forrefresh = null;
        if (tile.getContextState() == Tile.ContextState.INVALID) {
            forrefresh = validateRendererConfiguration(tile);
            tile.setContextState(Tile.ContextState.OKAY);
            if (forrefresh == null) {
                rendertile = true;
            }
        }
        if (!rendertile && forrefresh != null) {
            // only refresh the layers that have changed
            for( AbstractRenderMetrics context : forrefresh ) {

                // kick the child renderer that has been updated
                List<TiledCompositeRendererImpl.RenderInfo> kids = ((TiledCompositeRendererImpl) re.getRenderer()).getChildren();
                for( TiledCompositeRendererImpl.RenderInfo r : kids ) {
                    if (r.getMetrics().getRenderContext().equals(context.getRenderContext())) {
                        // we need to render this layer
                        // layer to refresh
                        r.getExecutor().getRenderer().setRenderBounds(tile.getReferencedEnvelope());
                        r.getExecutor().getRenderer().setState(IRenderer.RENDER_REQUEST);
                    }
                }
            }
        } else if (rendertile) {
            // refresh all layers 
            re.setRenderBounds(tile.getReferencedEnvelope());
            re.render();
        }
        tile.setRenderState(Tile.RenderState.RENDERED);
        refreshImage();
    }
    /**
     * Refreshes the map display without removing the existing tile contexts.
     *
     * @param bounds
     */
    public void softRefresh( final Envelope bounds) {
        checkState();
        if (!renderingEnabled) {
            return;
        }
        getViewportModelInternal().setInitialized(true);

        if (getMapDisplay() == null) {
            return;
        }

        if (getMapDisplay().getWidth() < 1 || getMapDisplay().getHeight() < 1) {
            return;
        }

        // only worry about tiles in the area of interest as specified by the bounds.
        // if the bounds is null then we are interested in the entire viewport
        ReferencedEnvelope areaOfInterest = null;
        if (bounds == null) {
            areaOfInterest = getMap().getViewportModel().getBounds();
        } else {
            areaOfInterest = new ReferencedEnvelope(bounds, getMap().getViewportModel().getCRS());
        }
       
        ReferencedEnvelope viewportbounds = getMap().getViewportModel().getBounds();
        double resolution = viewportbounds.getWidth() / getMapDisplay().getWidth();

        Collection<ReferencedEnvelope> tileBounds = computeTileBounds(areaOfInterest, resolution);
        //update tile states
        setTileStateToOld(tileBounds);
        for( Iterator<ReferencedEnvelope> iterator = tileBounds.iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope referencedEnvelope = (ReferencedEnvelope) iterator.next();
           
            Tile tile = getOrCreateTile(referencedEnvelope);
            boolean rendertile = (tile.getRenderState() == Tile.RenderState.INVALID || tile.getRenderState() == Tile.RenderState.NEW);
          
            tile.setTileState(Tile.ValidatedState.VALIDATED);
            RenderExecutorComposite re = tile.getRenderExecutor();

            // creates the contexts - this function returns the "new" contexts; these are the ones
            //that need to be refreshed.
            List<AbstractRenderMetrics> forrefresh = null;
            if (tile.getContextState() == Tile.ContextState.INVALID){
                forrefresh = validateRendererConfiguration(tile);
                tile.setContextState(Tile.ContextState.OKAY);
                if (forrefresh == null){
                    rendertile = true;
                }
            }
                       
            if (!rendertile && forrefresh != null ){
                //only refresh the layers that have changed
                for(AbstractRenderMetrics context : forrefresh){
                   
                    //kick the child renderer that has been updated
                    List<TiledCompositeRendererImpl.RenderInfo> kids = ((TiledCompositeRendererImpl)re.getRenderer()).getChildren();
                    for (TiledCompositeRendererImpl.RenderInfo r : kids){
                        if (r.getMetrics().getRenderContext().equals(context.getRenderContext())){
                            //we need to render this layer
                            // layer to refresh
                            r.getExecutor().getRenderer().setRenderBounds(referencedEnvelope);
                            r.getExecutor().getRenderer().setState(IRenderer.RENDER_REQUEST);
                        }
                    }
                }
            }else if (rendertile){
//                if (re.getState() != IRenderer.STARTING) { // && re.getState() != IRenderer.RENDERING){
//                    re.setState(IRenderer.STARTING);
//                    re.setRenderBounds(referencedEnvelope);
//                    re.render();
                    List<TiledCompositeRendererImpl.RenderInfo> kids = ((TiledCompositeRendererImpl)re.getRenderer()).getChildren();
                    for (TiledCompositeRendererImpl.RenderInfo r : kids){
                            // layer to refresh
                            r.getExecutor().getRenderer().setRenderBounds(referencedEnvelope);
                            r.getExecutor().getRenderer().setState(IRenderer.RENDER_REQUEST);
                    }
                    re.setState(IRenderer.RENDER_REQUEST);
//                }               
            }
        }
       
        if (!getMapInternal().getContextModel().eAdapters().contains(contextModelAdapter)){
            getMapInternal().getContextModel().eAdapters().add(contextModelAdapter);
        }

        refreshImage();
    }

    /**
     * Performs a "hard refresh" - this removes any existing tiles then recreates them.
     *
     * @generated NOT
     */
    public void refresh(final Envelope bounds) {
        //clear and existing configurations
        if (getRendererCreator() != null){
            ((TiledRendererCreatorImpl)getRendererCreator()).reset();
        }

        if (getMapDisplay() instanceof ViewportPaneTiledSWT){
            //clear the tiles from the viewport pane as well otherwise the tiles will
            //be out of sync
            ViewportPaneTiledSWT tiledpane = (ViewportPaneTiledSWT) getMapDisplay();
            tiledpane.clearCachedTiles();
            tiledpane.clearReadyTiles();
        }
       
        //remove all existing images so everything will be recreated
        disposeAllTiles();
        tileCache.clear();
        importantTiles.clear();
       
        //now recreate everything
        softRefresh(bounds);
    }

   
    /**
     * Re composes all the images of all the tiles in the viewport. 
     * <p>
     * This function does not verify the rendering stack; however it
     * does verify that render state and re-render if necessary.
     *
     */
    @Override
    public void refreshImage() {       
        //only worry about tiles in the area of interest as specified by the viewport
        ReferencedEnvelope areaOfInterest = getMap().getViewportModel().getBounds();
       
        ReferencedEnvelope viewportbounds = getMap().getViewportModel().getBounds();
        double resolution = viewportbounds.getWidth() / getMapDisplay().getWidth();
       
        Collection<ReferencedEnvelope> tileBounds = computeTileBounds(areaOfInterest, resolution);

        for( Iterator<ReferencedEnvelope> iterator = tileBounds.iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope referencedEnvelope = (ReferencedEnvelope) iterator.next();

            Tile tile = getOrCreateTile(referencedEnvelope);

            RenderExecutorComposite re = tile.getRenderExecutor();
            if (tile.getRenderState() == Tile.RenderState.INVALID) {
                re.setRenderBounds(tile.getReferencedEnvelope());
                re.getRenderer().setState(IRenderer.RENDER_REQUEST);
                re.render();
            } else {
                //kick the renderer to refresh the image
               re.getRenderer().setState(IRenderer.DONE);
            }
            tile.setRenderState(Tile.RenderState.RENDERED);
        }
    }
   

    /**
     * Returns the size of the tiles
     *
     * @return
     */
    public int getTileSize(){
        return TILE_SIZE;
    }
   
    /**
     * Computes the tiles associated with a bounds and resolution
     *
     * @param viewBounds                bounds to find tiles for
     * @param worldunitsperpixel        resolution
     *
     * @return  Collection of referenced envelopes that represent the tiles in the bounds
     */
    @Override
    public Collection<ReferencedEnvelope> computeTileBounds(ReferencedEnvelope viewBounds, double worldunitsperpixel){
       
        double unittilesize = worldunitsperpixel * getTileSize();
        double minx = viewBounds.getMinX();
        double minxtile = Math.floor((minx - tileCenter.x) / unittilesize) * unittilesize + tileCenter.x;
       
        double maxx = viewBounds.getMaxX();
        double maxxtile = (Math.floor((maxx - tileCenter.x) / unittilesize) + 1) * unittilesize + tileCenter.x;
       
        double miny = viewBounds.getMinY();
        double minytile = Math.floor((miny - tileCenter.y) / unittilesize) * unittilesize + tileCenter.y;
       
        double maxy = viewBounds.getMaxY();
        double maxytile = (Math.floor((maxy - tileCenter.y) / unittilesize) + 1) * unittilesize + tileCenter.y;

        ArrayList<ReferencedEnvelope> tileBounds = new ArrayList<ReferencedEnvelope>();
        int numberx = (int)Math.round( (maxxtile - minxtile)/unittilesize );
        int numbery = (int)Math.round( (maxytile - minytile)/unittilesize);
       
        for (int x = 0; x < numberx; x ++){
            double xvalue = x * unittilesize + minxtile;
            double xvaluemax = (x + 1) * unittilesize + minxtile;
            xvalue = roundDouble(xvalue);
            xvaluemax = roundDouble(xvaluemax);
            for (int y = 0; y < numbery; y ++){
                double yvalue = y * unittilesize + minytile;
                double yvaluemax = (y + 1) * unittilesize + minytile;
                yvalue = roundDouble(yvalue);
                yvaluemax = roundDouble(yvaluemax);
               
                ReferencedEnvelope env = new ReferencedEnvelope(xvalue, xvaluemax, yvalue, yvaluemax, viewBounds.getCoordinateReferenceSystem());
                tileBounds.add(env);
            }
        }       
        return tileBounds;
    }
   
    /**
     * This function takes the last two digits (8 bits) of a double and 0's them.
     *
     *
     * @param number
     * @return
     */
    private static double roundDouble(double number){
       
        Long xBits = Double.doubleToLongBits(number);

        //zeroLowerBits
        int nBits = 8;
        long invMask = (1L << nBits) - 1L;
       
        long mask =~ invMask;
       
        xBits &= mask;  
        return Double.longBitsToDouble(xBits);
    }
   
   
    /**
     * Disposes of this render manager and all associated tiles.
     *
     * @generated NOT
     */
    public void dispose() {
        Set<EObject> set = new HashSet<EObject>();
        set.add(getMapInternal());
        Iterator<EObject> iter = getMapInternal().eAllContents();
        while (iter.hasNext()) {
            EObject obj = iter.next();
            removeAdapters(obj);
        }
        try{
            getMapInternal().removeDeepAdapter(visibilityChangedListener);
        }catch (Exception ex){
            ex.printStackTrace();
        }
        //remove Adapters from all render executors
        ((ViewportPane) mapDisplay).setRenderManager(null);
    
        //dispose of all tiles
        disposeAllTiles();
        tileCache.clear();
        importantTiles.clear();

        super.dispose();
    }
   
    /**
     * Disposes all tiles
     */
    private void disposeAllTiles(){
        for( Iterator<Object> iterator = tileCache.getKeys().iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope key = (ReferencedEnvelope) iterator.next();
            Tile t = (Tile)tileCache.get(key);
            if (t != null){
                removeAdapters(t.getRenderExecutor());
                t.dispose();
            }
        }   
    }
   
    /**
     * @param obj
     */
    private void removeAdapters(EObject obj) {
        obj.eAdapters().remove(this.viewportListener);
        obj.eAdapters().remove(this.viewportModelChangeListener);
        obj.eAdapters().remove(this.contextModelAdapter);
        obj.eAdapters().remove(this.renderExecutorListener);
        obj.eAdapters().remove(this.selectionListener);
    }

    /**
     *
     * Sets the map display to the new value.
     *
     * @generated
     */
    public void setDisplayGen(IMapDisplay newDisplay) {
        IMapDisplay oldDisplay = mapDisplay;
        mapDisplay = newDisplay;
        if (eNotificationRequired())
            eNotify(new ENotificationImpl(this, Notification.SET,
                    RenderPackage.RENDER_MANAGER__MAP_DISPLAY, oldDisplay,
                    mapDisplay));
    }

    /**
     *
     * Sets the map display to the new value and updates
     * the render manager associated with the map display.
     *
     * @see org.locationtech.udig.project.render.RenderManager#setDisplay(org.locationtech.udig.project.render.displayAdapter.IMapDisplay)
     * @param value
     */
    public void setDisplay(IMapDisplay value) {
        checkState();
        ((ViewportPane) value).setRenderManager(this);
        setDisplayGen(value);
    }


    /**
     * @see org.locationtech.udig.project.render.impl.RenderManagerImpl#basicSetMap(org.locationtech.udig.project.Map,
     *      org.eclipse.emf.common.notify.NotificationChain)
     */
    @Override
    public NotificationChain basicSetMapInternal(Map newMap,
            NotificationChain msgs) {
        if (getMapInternal() != null) {
            getMapInternal().eAdapters().remove(selectionListener);
            getMapInternal().removeDeepAdapter(selectionListener);
        }
        if (newMap != null) {
            newMap.eAdapters().add(selectionListener);
            newMap.addDeepAdapter(selectionListener);
        }
        NotificationChain change = super.basicSetMapInternal(newMap, msgs);
        if (getMapInternal() != null){
            getMapInternal().addDeepAdapter(this.visibilityChangedListener);
        }
       
        return change;
    }

   
    /**
     * Ensures that the current configuration of renderer is a valid choice.
     *
     * <p>This function takes the current "configuration" represented by the tile and compares it to a newly
     * created configuration that is created based on the current state of the system.  It then removes and contexts from the current
     * configuration that are not in the new configuration and adds any newly created configurations.
     * </p>
     * <p>
     * The function will return null if the entire context is new; otherwise it will return a set of
     * render metrics that represent the newly created contexts.  These new contexts are the context that need to be re-rendered.
     * </p>
     *
     * @return a list of added contexts; null if all contexts are new;
     *
     */
    private List<AbstractRenderMetrics> validateRendererConfiguration(Tile t){
       
        checkState();
   
        if (rendererCreator == null){
            initRenderCreator();
        }
        RenderExecutorComposite re = t.getRenderExecutor();
        TiledCompositeRendererImpl renderer = null;
        try{
            renderer = (TiledCompositeRendererImpl)re.getRenderer();
        }catch (Exception ex){
            ProjectPlugin.log("Error creating context for tile - Tile does not have a renderer.", ex); //$NON-NLS-1$
            return null;
        }
       
        if (renderer == null){
            //the tile doesn't have a renderer there is some error;
            ProjectPlugin.log("Error creating context for tile - Tile does not have a renderer."); //$NON-NLS-1$
            return null;
        }
        Collection<AbstractRenderMetrics> currentconfiguration;
        synchronized (this) {
            if (renderer.getChildren().size() == 0){
                currentconfiguration = null;
            }else{
                currentconfiguration = renderer.getChildrenMetrics();
            }
        }
      
       ArrayList<AbstractRenderMetrics> newcontexts = new ArrayList<AbstractRenderMetrics>();
       ILabelPainter tilelabelpainter = ((RenderContextImpl)t.getRenderExecutor().getContext()).getLabelPainter();
       if (currentconfiguration == null){
            //get new configurations
            Collection<AbstractRenderMetrics> newconfig = ((TiledRendererCreatorImpl)getRendererCreator()).getConfiguration(t.getReferencedEnvelope());
            // need this because this is taking place in a non-synchronized
            // block so it is possible for
            // this code to be executed twice. I want the second run to be
            // accurate.
            // might need to be thought about more.
            renderer.removeAllChildren();
            renderer.addChildren(newconfig);
            newcontexts = null;
            for( Iterator<AbstractRenderMetrics> iterator = newconfig.iterator(); iterator.hasNext(); ) {
                AbstractRenderMetrics abstractRenderMetrics = (AbstractRenderMetrics) iterator.next();
                //setup label painter
                ((RenderContextImpl)abstractRenderMetrics.getRenderContext()).setLabelPainterLocal(tilelabelpainter);
            }
        }else{
            //we have an existing configuration; lets recycle as many of the contexts as possible
            //lets get the new configuration
            Collection <AbstractRenderMetrics> newconfig = ((TiledRendererCreatorImpl) getRendererCreator()).getConfiguration(t.getReferencedEnvelope());
           
            //items to add/remove from the existing configuration
            List<AbstractRenderMetrics> removeList = new ArrayList<AbstractRenderMetrics>();
            List<AbstractRenderMetrics> addList = new ArrayList<AbstractRenderMetrics>();
           
            for(AbstractRenderMetrics metric: currentconfiguration){
                if (!newconfig.contains(metric)){
                    //this is an existing context the doesn't exist in the new set
                    removeList.add(metric);
                }
            }
            for (AbstractRenderMetrics metric: newconfig){
                if (!currentconfiguration.contains(metric)){
                    //this is in the new configuration but not the only one therefore we need to add it
                    addList.add(metric);
                    //setup label painter
                    ((RenderContextImpl)metric.getRenderContext()).setLabelPainterLocal(tilelabelpainter);
                }
            }
           
            newcontexts.addAll(addList);
           
            renderer.removeChildren(removeList);
           
            if (!addList.isEmpty()){
                renderer.removeChildren(addList);
            }
            renderer.addChildren(addList);
           
            //let's properly dispose of anything in the remove List
            for( Iterator<AbstractRenderMetrics> iterator = removeList.iterator(); iterator.hasNext(); ) {
                AbstractRenderMetrics metric = (AbstractRenderMetrics) iterator.next();
                ((RenderContextImpl)metric.getRenderContext()).dispose();
            }
       }
       return newcontexts;
    }

    /**
     * Here we'll need a render executor and renderer for each tile.
     *
     * This creates a render executor composite.
     */
    private RenderExecutorComposite createRenderExecutor(ReferencedEnvelope bounds) {
        checkState();
       
        CompositeRendererImpl renderer = (CompositeRendererImpl)RenderFactory.eINSTANCE.createTiledCompositeRenderer();

        CompositeRenderContext context = new CompositeRenderContextImpl() {
            @Override
            public synchronized BufferedImage getImage(int width, int height) {
                if (image == null || image.getWidth() < width
                        || image.getHeight() < height) {
                    image = new BufferedImage(width, height,
                            BufferedImage.TYPE_3BYTE_BGR);
                    Graphics2D g = image.createGraphics();
                    g.setBackground(Color.WHITE);
                    g.clearRect(0, 0, width, height);
                    g.dispose();
                }

                return image;
            }

            @Override
            public synchronized void clearImage(Rectangle paintArea) {
                if (image == null)
                    return;
                Graphics2D g = image.createGraphics();
                g.setBackground(Color.WHITE);
                g.clearRect(paintArea.x, paintArea.y, paintArea.width,
                        paintArea.height);
                g.dispose();
            }

        };
       
        context.setMapInternal(getMapInternal());
        context.setRenderManagerInternal(this);
        context.setImageBounds(bounds);
        context.setImageSize(new Dimension(getTileSize(), getTileSize()));
       
        ((CompositeRenderContextImpl)context).setLabelPainterLocal(new UDIGLabelCache(new LabelCacheImpl()));
        renderer.setContext(context);
       
        renderer.setName(Messages.RenderManagerDynamic_allLayers);
       
        RenderExecutorComposite renderExecutor = (RenderExecutorComposite)RenderFactory.eINSTANCE.createRenderExecutor(renderer);
        renderExecutor.setName("Tiled (" + bounds.hashCode() + "): " + bounds.toString()); //$NON-NLS-1$ //$NON-NLS-2$
        renderExecutor.eAdapters().add(renderExecutorListener);
       
        return renderExecutor;
    }

  
   
    /**
     *
     * Returns true if these two layers are related in any way.  Two layers are related if:
     * <ul>
     *   <li>They are the same (layer = contained)
     *   <li>Layer is a part of a composite context and contained is also part of that context.</li>
     * </ul>
     *
     * @param layer
     * @param contained
     *
     * @returns true if the two layers are part of the same context
     */
    public boolean areLayersRelatedByContext(ILayer layer, ILayer contained){
       
       if (tileCache.getKeys().size() == 0){
            //no contexts built to check against so return false;
            return false;
        }

        //they are the same; so they are related
        if (layer == contained) return true;
       
       
        Tile t = null;
        Iterator<Object> keys = tileCache.getKeys().iterator();
        while( t == null && keys.hasNext()){
            t = (Tile)tileCache.get((ReferencedEnvelope)keys.next());
        }
        if (t == null){
            //no tile found
            return false;
        }

        RenderExecutorComposite parent  = t.getRenderExecutor();
        Collection<TiledCompositeRendererImpl.RenderInfo>  kids = ((TiledCompositeRendererImpl)parent.getRenderer()).getChildren();
       
        for( Iterator<TiledCompositeRendererImpl.RenderInfo> iterator = kids.iterator(); iterator.hasNext(); ) {
            TiledCompositeRendererImpl.RenderInfo kid = iterator.next();
            IRenderContext childcontext = kid.getRenderer().getContext()
            ;
            if (childcontext instanceof CompositeRenderContext){
                if (((CompositeRenderContext) childcontext).getLayers().contains(layer)){
                    if( ((CompositeRenderContext) childcontext).getLayers().contains(contained)){
                        //composite context and contains layer + contained
                        //these layers are related through a composite context
                        return true;
                    }else{
                        //composite context that contains layer but not contained
                        //cannot be related
                        return false;
                    }
                }
            }else{
                if (childcontext.getLayer() == layer){
                    //layer is not part of a composite context and cannot be related in
                    //any way to contained
                    return false;
                }
            }
           
        }
        return false;
    }
   
    /**
     * @return null as there is no render executor for this; there are multiple render executors
     */
    public RenderExecutor getRenderExecutor() {
        return null;
    }
   
    /**
     * @see org.locationtech.udig.project.render.impl.RenderManagerImpl#setRenderExecutor(org.locationtech.udig.project.render.RenderExecutor)
     */
    public void setRenderExecutor(RenderExecutor newRenderExecutor) {
        throw new UnsupportedOperationException("This operation is not supported for a Tiled Render Manager Dynamic."); //$NON-NLS-1$
    }

    /**
     * Throws an unsupported operation exception.
     *
     * <p>The tiled rendering system does not have
     * a single set of renderers.</p>
     *
     * @returns
     */
    @Override
    public List<IRenderer> getRenderers(){
        throw new UnsupportedOperationException("Invalid operation for a Tiled RenderManager Dynamic."); //$NON-NLS-1$
    }

    /**
     * Returns a list of tiles that match the given tile bounds.  If the given tile
     * bounds are null, they will be calculated from the viewport bounds.  The returned
     * list of tiles can then be cached for faster painting/panning.
     *
     * This is assumed to be called by the viewport.  As a result any tile withing the bounds provided
     * is marked as on-screen; others will be marked as offscreen; which means it is possible for them to be
     * removed.
     *
     */
    @Override
    public java.util.Map<ReferencedEnvelope, Tile> getTiles(
      Collection<ReferencedEnvelope> bounds) {
      // if no tile bounds were passed, calculate them from the current viewport bounds
      if (bounds == null) {
        ReferencedEnvelope viewportbounds = getViewportModelInternal().getBounds();
        bounds = computeTileBounds(viewportbounds, viewportbounds.getWidth() / getMapDisplay().getWidth());
      }
     
      // loop through the tile bounds and return a tile for each
        java.util.Map<ReferencedEnvelope, Tile> newTiles = new HashMap<ReferencedEnvelope, Tile>();
        try {
            for( Iterator<ReferencedEnvelope> iterator = bounds.iterator(); iterator.hasNext(); ) {
                ReferencedEnvelope env = (ReferencedEnvelope) iterator.next();

                // get the tile
                Tile tile = getOrCreateTile(env);
                tile.setScreenState(Tile.ScreenState.ONSCREEN);
                if (tile.getRenderState() == Tile.RenderState.INVALID){
                    tile.getRenderExecutor().setRenderBounds(tile.getReferencedEnvelope());
                    tile.getRenderExecutor().setState(IRenderer.RENDER_REQUEST);
                }
                newTiles.put(env, tile);
            }
           
            //update the other tiles to an offscreen state
            for( Iterator<Object> iterator = tileCache.getKeys().iterator(); iterator.hasNext(); ) {
                ReferencedEnvelope referencedEnvelope = (ReferencedEnvelope) iterator.next();
                Tile t = (Tile)tileCache.get(referencedEnvelope);
                if (t != null && !newTiles.containsKey(referencedEnvelope)){
                    t.setScreenState(Tile.ScreenState.OFFSCREEN);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
       
        return newTiles;
    }   
   
    /**
     * Sets the rendered state on all tiles to invalid.  This means that next time
     * this tile is access it will be re-rendered.
     *
     */
    private void invalidateAllTilesRenderState(){
        for( Iterator<Object> iterator = tileCache.getKeys().iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope key = (ReferencedEnvelope)iterator.next();
            Tile t = (Tile)tileCache.get(key);
            if (t != null){
                t.setRenderState(Tile.RenderState.INVALID);
            }
        }
    }
   
    /**
     * Sets the context state on all tiles to be invalid.  This means next time this tile
     * is used for anything the context (rendering state) will be re-validated against the current stack
     * and modifications made as necessary.
     */
    private void invalidateAllTileContext(){
        for( Iterator<Object> iterator = tileCache.getKeys().iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope key = (ReferencedEnvelope)iterator.next();
            Tile t = (Tile)tileCache.get(key);
            if (t != null){
                t.setContextState(Tile.ContextState.INVALID);
            }
        }
    }

   
    /* The section below only exists for testing against a regular viewport */

    private BufferedImage screen = null;
   
    /**
     * This is the image that is to be drawn on the screen.
     *
     * So for now we get the images necessary to form the tiles and compile into a single
     * image for the viewport.
     */
    @Override
    public RenderedImage getImage() {
        ReferencedEnvelope bounds = getViewportModelInternal().getBounds();
       
        Collection<ReferencedEnvelope> tileBounds = computeTileBounds(bounds, bounds.getWidth() / getMapDisplay().getWidth());
   
        if (screen == null || screen.getWidth() != getMapDisplay().getWidth() || screen.getHeight() != getMapDisplay().getHeight()){
            screen = new BufferedImage(getMapDisplay().getWidth(), getMapDisplay().getHeight(), BufferedImage.TYPE_INT_ARGB);  
        }
       
        Graphics2D g = screen.createGraphics();

        //clear existing image
        g.setBackground(new Color(0, 0, 0, 0));
        g.clearRect(0,0, getMapDisplay().getWidth(), getMapDisplay().getHeight());
       
        //update screen tile states
        for( Iterator<Object> iterator = tileCache.getKeys().iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope referencedEnvelope = (ReferencedEnvelope) iterator.next();
            Tile t = (Tile)tileCache.get(referencedEnvelope);
            if (t != null && !tileBounds.contains(referencedEnvelope)){
                t.setScreenState(Tile.ScreenState.OFFSCREEN);
            }
        }
       
        try {
            for( Iterator<ReferencedEnvelope> iterator = tileBounds.iterator(); iterator.hasNext(); ) {
                ReferencedEnvelope env = (ReferencedEnvelope) iterator.next();

                // get the tile image
                Tile tile = getOrCreateTile(env);
                tile.setScreenState(Tile.ScreenState.ONSCREEN);
               
                BufferedImage tileimage = tile.getBufferedImage();

                //write the image to the screen image
                Point a = getViewportModelInternal().worldToPixel(new Coordinate(env.getMinX(), env.getMinY()));
                Point b = getViewportModelInternal().worldToPixel(new Coordinate(env.getMaxX(), env.getMaxY()));
               
                g.drawImage(tileimage, a.x, b.y, b.x - a.x, a.y - b.y, null);
               
                //draw a border around the tile for debugging
                g.setColor(Color.BLACK);
               
                g.drawLine(a.x, a.y, a.x, b.y);
                g.drawLine(a.x, b.y, b.x, b.y);
                g.drawLine(b.x, b.y, b.x, a.y);
                g.drawLine(b.x, a.y, a.x, a.y);
               
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        g.dispose();

        return screen;
    }
   
   
    /* The functions below are designed to handle events that affect the rendering system.
     * This includes such things as layers added/removed and zorder changes; crs changes.
     *
     * It does not include things such as viewport bounds changing as those such events
     * should not affect the rendering stack
     */
   
    /**
     * To be called when the order of layers has changed
     * <p>
     * This function invalidates all tiles and contexts.  The
     * contexts much be invalidated because the WMS layers may have been
     * split which means instead of a single context we now need two (or merged).
     * </p>
     */
    public void zorderChanged( Notification msg ){
        getRendererCreator().changed(msg);
       
        invalidateAllTilesRenderState();
        invalidateAllTileContext();
       
        //refresh this layer
        Layer newV=(Layer) msg.getNewValue();
        refresh(newV, null);
    }
   
    /**
     * To be called when layers have been added.
     * <p>
     * This function invalidates all tiles and associated contexts then calls
     * refresh on the layers that have been added to re-render those layers
     * </p>
     */
    @SuppressWarnings("unchecked")
    public void layersAdded(Notification msg){
        getRendererCreator().changed(msg);
       
        invalidateAllTileContext();
       
        //lets only call refresh on the added tiles
        ArrayList<Layer> addedLayers = new ArrayList<Layer>();
        switch( msg.getEventType() ) {
        case Notification.ADD: {
            Layer layer = (Layer) msg.getNewValue();
            addedLayers.add(layer);
            break;
        }
        case Notification.ADD_MANY: {
            for( Layer layer : (Collection< ? extends Layer>) msg.getNewValue() ) {
                addedLayers.add(layer);
            }
        }}
        //refresh the layers that have been added
        for( Layer layer : addedLayers ) {
            refresh(layer, null);
        }
    }
   
    /**
     * To be called when layer(s) have been removed.
     * <p>
     * This function invalidates the context and render state of all tiles; then re-renders
     * the tiles in the current viewport.
     *
     */
    public void layersRemoved(Notification msg){
        getRendererCreator().changed(msg);
       
        invalidateAllTileContext();
        invalidateAllTilesRenderState();
       
        softRefresh(null);
    }
   
   
    /**
     * Called when the CRS has changed.
     * <p>
     * This function removes all tiles and rebuilds the ones that are in the
     * current viewport.
     * </p>
     */
    public void crsChanged(Notification msg){
        //remove all existing images so everything will be recreated
        disposeAllTiles();
        tileCache.clear();
        importantTiles.clear();
       
        softRefresh(null);
    }
   
    /**
     * Called when the viewport changes.
     *
     * <p>This function creates and renders the tiles for the new viewport</p>
     *
     * @param msg
     */
    public void viewportChanged(Notification msg){
        if (getViewportModelInternal().isBoundsChanging()){
            if (getMapDisplay() instanceof ViewportPaneTiledSWT){
                Collection<Tile> newtiles = ((ViewportPaneTiledSWT)getMapDisplay()).updateReadyTiles();
                renderTiles(newtiles);
            }
        }else{
            if (getMapDisplay() instanceof ViewportPaneTiledSWT){
                cancelTileRenderJobs();
            }
            if (ApplicationGIS.getActiveMap() != null
                    && ApplicationGIS.getVisibleMaps().contains(
                            ApplicationGIS.getActiveMap())) {

                // perform a soft refresh with re-rendering any of the data
                softRefresh(null);
            }
        }
       
    }
   
    /**
     * Called when a layer blackboard entry has been changed.
     *
     * <p>
     * If the layer is not a selection layer this function invalidates all tile render states and
     * class refresh(layer, null) to re-render the layer.
     * </p>
     *
     * @param layer  the layer whose blackboard changed.
     */
    public void blackBoardChanged(Layer layer){
        if (layer instanceof SelectionLayer){
            return;
        }
        //all tiles need to be re-rendered
        //Maybe we should be invalidating the contexts here?  Does a blackboard change
        //mean a different renderer should be used?
        invalidateAllTilesRenderState();
        refresh(layer, null);
   
   
   
    /**
     * This function should be called when a layer is made visible.
     * <p>
     * The tiles that are off screen will have there render state set
     * to invalid because we don't know if the layer has been rendered for them or not.
     * </p>
     * <p>If layers are made invisible we don't need to call this as refreshImage() is called
     * and that will deal with re-composing the image and removing the hidden layer.</p>
     *
     */
    public void layerMadeVisible(Layer layer){
        //tiles on the screen can have there contexts invalidated
        ReferencedEnvelope areaOfInterest = getMap().getViewportModel().getBounds();
        ReferencedEnvelope viewportbounds = getMap().getViewportModel().getBounds();
        double resolution = viewportbounds.getWidth() / getMapDisplay().getWidth();
        Collection<ReferencedEnvelope> tileBounds = computeTileBounds(areaOfInterest, resolution);
       
        for( Iterator<Object> iterator = tileCache.getKeys().iterator(); iterator.hasNext(); ) {
            ReferencedEnvelope key = (ReferencedEnvelope)iterator.next();
            Tile t = (Tile)tileCache.get(key);
            if (!tileBounds.contains(key)){
                //we can't assume that all layers have been rendered outside of the current tile bounds
                //really we only need to do this when tiles are made visible...
                t.setRenderState(Tile.RenderState.INVALID);
            }
        }
    }
   
    /**
     * This is job to render tiles in a separate thread.
     *
     * <p>This is currently used to render tiles outside the display thread
     * as we zoom around.
     * </p>
     * @author Emily Gouge (Refractions Research, Inc.)
     * @since 1.2.0
     */
    class RenderTileJob extends Job{

        private List<Tile> tilestorender = new ArrayList<Tile>();
        private TiledRenderManagerDynamic manager;
       
        /**
         * Creates a new job from a given manager
         * @param manager
         */
        public RenderTileJob( TiledRenderManagerDynamic manager ) {
            super("Pan Tile Rendering Job"); //$NON-NLS-1$
            this.manager = manager;
        }
       
        /**
         * Adds tiles to the list of tiles to be processed.
         *
         * @param tiles
         */
        public void addTiles(Collection<Tile> tiles){
            tilestorender.addAll(tiles);
        }

        /**
         * Removes all tiles from the list to be rendered
         */
        public void clear(){
            tilestorender.clear();
        }
        /**
         * Renders each tile in the list; until the list is empty
         */
        @Override
        protected IStatus run( IProgressMonitor monitor ) {
            if (tilestorender.size() == 0) return Status.OK_STATUS;
            Tile t = tilestorender.remove(0);
            manager.renderTile(t);
            if (tilestorender.size() > 0){
                schedule();
            }
            return Status.OK_STATUS;
        }
       
    }   
}
TOP

Related Classes of org.locationtech.udig.project.ui.internal.TiledRenderManagerDynamic$RenderTileJob

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.