/[schmitzm]/branches/2.0-RC1/src/skrueger/geotools/RenderingExecutor.java
ViewVC logotype

Diff of /branches/2.0-RC1/src/skrueger/geotools/RenderingExecutor.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 528 by alfonx, Wed Nov 18 09:56:47 2009 UTC revision 529 by alfonx, Wed Nov 18 20:47:00 2009 UTC
# Line 1  Line 1 
1  package skrueger.geotools;  package skrueger.geotools;
2    
 /*  
  *    GeoTools - The Open Source Java GIS Toolkit  
  *    http://geotools.org  
  *  
  *    (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)  
  *  
  *    This library is free software; you can redistribute it and/or  
  *    modify it under the terms of the GNU Lesser General Public  
  *    License as published by the Free Software Foundation;  
  *    version 2.1 of the License.  
  *  
  *    This library is distributed in the hope that it will be useful,  
  *    but WITHOUT ANY WARRANTY; without even the implied warranty of  
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  
  *    Lesser General Public License for more details.  
  */  
   
   
3  import gtmig.org.geotools.swing.XMapPane;  import gtmig.org.geotools.swing.XMapPane;
4    
5  import java.awt.AlphaComposite;  import java.awt.AlphaComposite;
# Line 25  import java.awt.Color; Line 7  import java.awt.Color;
7  import java.awt.Composite;  import java.awt.Composite;
8  import java.awt.Graphics2D;  import java.awt.Graphics2D;
9  import java.awt.Rectangle;  import java.awt.Rectangle;
10    import java.awt.geom.AffineTransform;
11  import java.util.concurrent.Callable;  import java.util.concurrent.Callable;
12  import java.util.concurrent.CountDownLatch;  import java.util.concurrent.CountDownLatch;
13  import java.util.concurrent.ExecutorService;  import java.util.concurrent.ExecutorService;
# Line 41  import org.geotools.renderer.RenderListe Line 24  import org.geotools.renderer.RenderListe
24  import org.geotools.swing.JMapPane;  import org.geotools.swing.JMapPane;
25  import org.opengis.feature.simple.SimpleFeature;  import org.opengis.feature.simple.SimpleFeature;
26    
 /**  
  * This class is used by {@code JMapPane} to handle the scheduling and running of  
  * rendering tasks on a background thread. It functions as a single thread, non-  
  * queueing executor, ie. only one rendering task can run at any given time and,  
  * while it is running, any other submitted tasks will be rejected.  
  * <p>  
  * Whether a rendering task is accepted or rejected can be tested on submission:  
  * <pre><code>  
  *     ReferencedEnvelope areaToDraw = ...  
  *     Graphics2D graphicsToDrawInto = ...  
  *     boolean accepted = renderingExecutor.submit(areaToDraw, graphicsToDrawInto);  
  * </code></pre>  
  *  
  * The status of the executor can also be checked at any time like this:  
  * <pre><code>  
  *     boolean busy = renderingExecutor.isRunning();  
  * </code></pre>  
  *  
  * While a rendering task is running it is regularly polled to see if it has completed  
  * and, if so, whether it finished normally, was cancelled or failed. The interval between  
  * polling can be adjusted which might be useful to tune the executor for particular  
  * applications:  
  * <pre><code>  
  *     RenderingExecutor re = new RenderingExecutor( mapPane );  
  *     re.setPollingInterval( 150 );  // 150 milliseconds  
  * </code></pre>  
  *  
  * @author Michael Bedward  
  * @since 2.7  
  * @source $URL: http://svn.osgeo.org/geotools/branches/2.6.x/modules/unsupported/swing/src/main/java/org/geotools/swing/RenderingExecutor.java $  
  * @version $Id: RenderingExecutor.java 34285 2009-10-30 10:48:49Z mbedward $  
  *  
  * @see JMapPane  
  */  
27  public class RenderingExecutor {  public class RenderingExecutor {
28    
29      private final XMapPane mapPane;  private volatile RenderThread task;
30      private final ExecutorService taskExecutor;  private final XMapPane mapPane;
31      private final ScheduledExecutorService watchExecutor;          public RenderingExecutor(XMapPane mapPane) {
32                    this.mapPane = mapPane;
 //    /** The default interval (milliseconds) for polling the result of a rendering task */  
 //    public static final long DEFAULT_POLLING_INTERVAL = 300L;  
   
     private long pollingInterval;  
   
     /*  
      * This latch is used to avoid a race between the cancellation of  
      * a current task and the submittal of a new task  
      */  
     private CountDownLatch cancelLatch;  
   
     /**  
      * Constants to indicate the result of a rendering task  
      */  
     public enum TaskResult {  
         PENDING,  
         COMPLETED,  
         CANCELLED,  
         FAILED;  
     }  
   
     private long numFeatures;  
   
     /**  
      * A rendering task  
      */  
     private class Task implements Callable<TaskResult>, RenderListener {  
   
         private final ReferencedEnvelope envelope;  
         private final Rectangle paintArea;  
         private final Graphics2D graphics;  
   
         private boolean cancelled;  
         private boolean failed;  
                 final private GTRenderer renderer;  
   
         /**  
          * Constructor. Creates a new rendering task  
          *  
          * @param envelope map area to render (world coordinates)  
          * @param paintArea drawing area (image or display coordinates)a  
          * @param graphics graphics object used to draw into the image or display  
          */  
         public Task(final ReferencedEnvelope envelope, final Rectangle paintArea, final Graphics2D graphics, GTRenderer renderer) {  
             this.envelope = envelope;  
             this.paintArea = paintArea;  
             this.graphics = graphics;  
             this.cancelled = false;  
             this.renderer = renderer;  
             failed = false;  
         }  
   
         /**  
          * Called by the executor to run this rendering task.  
          *  
          * @return result of the task: completed, cancelled or failed  
          * @throws Exception  
          */  
         public TaskResult call() throws Exception {  
             if (!cancelled) {  
                   
                 try {  
                     renderer.addRenderListener(this);  
                       
                     Composite composite = graphics.getComposite();  
                     //graphics.setComposite(AlphaComposite.Src);  
                     //graphics.setBackground(Color.WHITE);  
                     graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));  
                     graphics.fill(paintArea);  
                     graphics.setComposite(composite);  
   
   
                     numFeatures = 0;  
                     renderer.paint(graphics, mapPane.getVisibleRect(), envelope, mapPane.getWorldToScreenTransform());  
   
                 } finally {  
                     renderer.removeRenderListener(this);  
                 }  
             }  
   
             if (cancelled) {  
                 return TaskResult.CANCELLED;  
             } else if (failed) {  
                 return TaskResult.FAILED;  
             } else {  
                 return TaskResult.COMPLETED;  
             }  
         }  
   
         /**  
          * Cancel the rendering task if it is running. If called before  
          * being run the task will be abandoned.  
          */  
         public synchronized void cancel() {  
             if (isRunning()) {  
                 cancelled = true;  
                 renderer.stopRendering();  
             }  
         }  
   
         /**  
          * Called by the renderer when each feature is drawn.  
          *  
          * @param feature the feature just drawn  
          */  
         public void featureRenderer(SimpleFeature feature) {  
             // @todo update a progress listener  
             numFeatures++ ;  
         }  
   
         /**  
          * Called by the renderer on error  
          *  
          * @param e cause of the error  
          */  
         public void errorOccurred(Exception e) {  
                 renderingException = e;  
                 graphics.setColor(Color.white);  
                 graphics.drawString(e.getMessage(), 11, 11);  
                 graphics.drawString(e.getMessage(), 9, 9);  
                 graphics.setColor(Color.black);  
                 graphics.drawString(e.getMessage(), 10, 10);  
             failed = true;  
         }  
   
     }  
   
     private AtomicBoolean taskRunning;  
     private Task task;  
     private Future<TaskResult> taskResult;  
     private ScheduledFuture<?> watcher;  
         private Exception renderingException;  
   
     /**  
      * Constructor. Creates a new executor to service the specified map pane.  
      *  
      * @param mapPane the map pane to be serviced  
      * @param l  
      */  
         public RenderingExecutor(final XMapPane mapPane, long pollingInterval) {  
         taskRunning = new AtomicBoolean(false);  
         this.mapPane = mapPane;  
         taskExecutor = Executors.newSingleThreadExecutor();  
         watchExecutor = Executors.newSingleThreadScheduledExecutor();  
         this.pollingInterval = pollingInterval;  
         cancelLatch = new CountDownLatch(0);  
     }  
   
     /**  
      * Get the interval for polling the result of a rendering task  
      *  
      * @return polling interval in milliseconds  
      */  
     public long getPollingInterval() {  
         return pollingInterval;  
33      }      }
34    
     /**  
      * Set the interval for polling the result of a rendering task  
      *  
      * @param interval interval in milliseconds (values {@code <=} 0 are ignored)  
      */  
     public void setPollingInterval(long interval) {  
         if (interval > 0) {  
             pollingInterval = interval;  
         }  
     }  
35    
36      /**      /**
37       * Submit a new rendering task. If no rendering task is presently running       * Submit a new rendering task. If no rendering task is presently running
# Line 260  public class RenderingExecutor { Line 44  public class RenderingExecutor {
44       * @return true if the rendering task was accepted; false if it was       * @return true if the rendering task was accepted; false if it was
45       *         rejected       *         rejected
46       */       */
47      public synchronized boolean submit(ReferencedEnvelope envelope, Rectangle paintArea, Graphics2D graphics, final GTRenderer renderer) {      public synchronized boolean submit(ReferencedEnvelope envelope, Rectangle paintArea, Graphics2D graphics, final GTRenderer renderer, AffineTransform worldToScreen) {
48          if (!isRunning()) {          System.out.println("submit..:");
49  //            try {          if (task == null || !task.isAlive()) {
50  //                // wait for any cancelled task to finish its shutdown                  System.out.println("is vacant... starting thread!");
51  //                cancelLatch.await();                  
52  //            } catch (InterruptedException ex) {                  task = new RenderThread(paintArea, graphics, renderer, worldToScreen);
53  //                return false;                  task.start();
54  //            }                  
   
             task = new Task(envelope, paintArea, graphics, renderer);  
             taskRunning.set(true);  
             taskResult = taskExecutor.submit(task);  
             watcher = watchExecutor.scheduleAtFixedRate(new Runnable() {  
   
                 public void run() {  
                     pollTaskResult();  
                 }  
             }, pollingInterval, pollingInterval, TimeUnit.MILLISECONDS);  
   
55              return true;              return true;
56            } else {
57                    System.out.println("is busy... requesting stop!");
58                    task.getRenderer().stopRendering();
59          }          }
60    
61          return false;          return false;
62      }      }
63        
64        class RenderThread extends Thread  {
65            
66            private final GTRenderer renderer;
67    //              private final Rectangle paintArea;
68    //              private final AffineTransform worldToScreen;
69    //              private final Graphics2D graphics;
70    
71                    public RenderThread(final Rectangle paintArea, final Graphics2D graphics, GTRenderer renderer, AffineTransform worldToScreen) {
72                            super( new RenderRun(paintArea, graphics, renderer, worldToScreen));
73    //                      this.paintArea = paintArea;
74    //                      this.graphics = graphics;
75                            this.renderer = renderer;
76    //                      this.worldToScreen = worldToScreen;
77                            
78                            System.out.println("starting render thread "+getName());
79                    }
80            
81            public GTRenderer getRenderer() {
82                            return renderer;
83                    }
84    
     /**  
      * Cancel the current rendering task if one is running  
      */  
     public synchronized void cancelTask() {  
         if (isRunning()) {  
             task.cancel();  
               
             // Cancelling to often... can that be the reason?  
             if (cancelLatch.getCount() < 1) {  
                 cancelLatch = new CountDownLatch(1);  
             }  
         }  
     }  
   
     private void pollTaskResult() {  
         if (!taskResult.isDone()) {  
             return;  
         }  
   
         TaskResult result = TaskResult.PENDING;  
   
         try {  
             result = taskResult.get();  
         } catch (Exception ex) {  
             throw new IllegalStateException("When getting rendering result", ex);  
         }  
   
         watcher.cancel(false);  
         taskRunning.set(false);  
   
         switch (result) {  
             case CANCELLED:  
                 cancelLatch.countDown();  
                 mapPane.onRenderingCancelled();  
                 break;  
   
             case COMPLETED:  
                 mapPane.onRenderingCompleted();  
                 break;  
   
             case FAILED:  
                 mapPane.onRenderingFailed(renderingException);  
                 break;  
                   
             case PENDING:  
                 mapPane.onRenderingPending();  
                 break;  
                   
         }  
     }  
   
     public synchronized boolean isRunning() {  
         return taskRunning.get();  
     }  
   
     @Override  
     protected void finalize() throws Throwable {  
         if (this.isRunning()) {  
             taskExecutor.shutdownNow();  
             watchExecutor.shutdownNow();  
         }  
85      }      }
86            
87      public void dispose() {      class RenderRun implements Runnable, RenderListener  {
88          taskExecutor.shutdownNow();          private final Rectangle paintArea;
89          watchExecutor.shutdownNow();                  private final Graphics2D graphics;
90      }                  private final AffineTransform worldToScreen;
91                    private final GTRenderer renderer;
92    
93                    public RenderRun(Rectangle paintArea, Graphics2D graphics,
94                                    GTRenderer renderer, AffineTransform worldToScreen) {
95                                            this.paintArea = paintArea;
96                                            this.graphics = graphics;
97                                            this.renderer = renderer;
98                                            this.worldToScreen = worldToScreen;
99                    }
100                    @Override
101                    public void run() {
102                            try {
103                            renderer.addRenderListener(this);
104                            System.out.println("start rendering...");
105                            try {
106                                            Thread.sleep(1000);
107                                    } catch (InterruptedException e) {
108                                            // TODO Auto-generated catch block
109                                            e.printStackTrace();
110                                    }
111                            renderer.paint(graphics, paintArea, worldToScreen);
112                            
113                            mapPane.onRenderingCompleted();
114                    } finally {
115                            renderer.removeRenderListener(this);
116                    }
117                    }
118                    @Override
119                    public void errorOccurred(Exception e) {
120    //                      System.out.println("rendering error");
121                            mapPane.onRenderingFailed(e);
122                    }
123    
124                    @Override
125                    public void featureRenderer(SimpleFeature feature) {
126                    }
127            
128        }
129    
130            public void cancelTask() {
131                    if (task!=null && task.isAlive()) {
132    //                      System.out.println("request stop for thread " +task.getName());
133                            task.getRenderer().stopRendering();
134                    }
135            }
136    
137            public boolean isRunning() {
138    //              if (task != null)
139    //                      System.out.println("is running "+task.getName()+" = true");
140                    return (task != null && task.isAlive());
141            }
142    
143            public void dispose() {
144                    if (task != null) {
145                            task.renderer.stopRendering();
146                            task = null;
147                    }
148            }
149        
150    
151  }  }
152    

Legend:
Removed from v.528  
changed lines
  Added in v.529

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26