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

Annotation of /branches/2.0-RC2/src/skrueger/geotools/RenderingExecutor.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 510 - (hide annotations)
Thu Nov 5 17:39:37 2009 UTC (15 years, 3 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java
File size: 11025 byte(s)
my own version of geotool's RenderingExecutor, because theirs runs agains JMapPane only. We use XMapPane

1 alfonx 510 package skrueger.geotools;
2    
3     /*
4     * GeoTools - The Open Source Java GIS Toolkit
5     * http://geotools.org
6     *
7     * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
8     *
9     * This library is free software; you can redistribute it and/or
10     * modify it under the terms of the GNU Lesser General Public
11     * License as published by the Free Software Foundation;
12     * version 2.1 of the License.
13     *
14     * This library is distributed in the hope that it will be useful,
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17     * Lesser General Public License for more details.
18     */
19    
20    
21     import gtmig.org.geotools.swing.XMapPane;
22    
23     import java.awt.AlphaComposite;
24     import java.awt.Color;
25     import java.awt.Composite;
26     import java.awt.Graphics2D;
27     import java.awt.Rectangle;
28     import java.util.concurrent.Callable;
29     import java.util.concurrent.CountDownLatch;
30     import java.util.concurrent.ExecutorService;
31     import java.util.concurrent.Executors;
32     import java.util.concurrent.Future;
33     import java.util.concurrent.ScheduledExecutorService;
34     import java.util.concurrent.ScheduledFuture;
35     import java.util.concurrent.TimeUnit;
36     import java.util.concurrent.atomic.AtomicBoolean;
37    
38     import org.geotools.geometry.jts.ReferencedEnvelope;
39     import org.geotools.renderer.GTRenderer;
40     import org.geotools.renderer.RenderListener;
41     import org.geotools.swing.JMapPane;
42     import org.opengis.feature.simple.SimpleFeature;
43    
44     /**
45     * This class is used by {@code JMapPane} to handle the scheduling and running of
46     * rendering tasks on a background thread. It functions as a single thread, non-
47     * queueing executor, ie. only one rendering task can run at any given time and,
48     * while it is running, any other submitted tasks will be rejected.
49     * <p>
50     * Whether a rendering task is accepted or rejected can be tested on submission:
51     * <pre><code>
52     * ReferencedEnvelope areaToDraw = ...
53     * Graphics2D graphicsToDrawInto = ...
54     * boolean accepted = renderingExecutor.submit(areaToDraw, graphicsToDrawInto);
55     * </code></pre>
56     *
57     * The status of the executor can also be checked at any time like this:
58     * <pre><code>
59     * boolean busy = renderingExecutor.isRunning();
60     * </code></pre>
61     *
62     * While a rendering task is running it is regularly polled to see if it has completed
63     * and, if so, whether it finished normally, was cancelled or failed. The interval between
64     * polling can be adjusted which might be useful to tune the executor for particular
65     * applications:
66     * <pre><code>
67     * RenderingExecutor re = new RenderingExecutor( mapPane );
68     * re.setPollingInterval( 150 ); // 150 milliseconds
69     * </code></pre>
70     *
71     * @author Michael Bedward
72     * @since 2.7
73     * @source $URL: http://svn.osgeo.org/geotools/branches/2.6.x/modules/unsupported/swing/src/main/java/org/geotools/swing/RenderingExecutor.java $
74     * @version $Id: RenderingExecutor.java 34285 2009-10-30 10:48:49Z mbedward $
75     *
76     * @see JMapPane
77     */
78     public class RenderingExecutor {
79    
80     private final XMapPane mapPane;
81     private final ExecutorService taskExecutor;
82     private final ScheduledExecutorService watchExecutor;
83    
84     /** The default interval (milliseconds) for polling the result of a rendering task */
85     public static final long DEFAULT_POLLING_INTERVAL = 100L;
86    
87     private long pollingInterval;
88    
89     /*
90     * This latch is used to avoid a race between the cancellation of
91     * a current task and the submittal of a new task
92     */
93     private CountDownLatch cancelLatch;
94    
95     /**
96     * Constants to indicate the result of a rendering task
97     */
98     public enum TaskResult {
99     PENDING,
100     COMPLETED,
101     CANCELLED,
102     FAILED;
103     }
104    
105     private long numFeatures;
106    
107     /**
108     * A rendering task
109     */
110     private class Task implements Callable<TaskResult>, RenderListener {
111    
112     private final ReferencedEnvelope envelope;
113     private final Rectangle paintArea;
114     private final Graphics2D graphics;
115    
116     private boolean cancelled;
117     private boolean failed;
118     final private GTRenderer renderer;
119    
120     /**
121     * Constructor. Creates a new rendering task
122     *
123     * @param envelope map area to render (world coordinates)
124     * @param paintArea drawing area (image or display coordinates)a
125     * @param graphics graphics object used to draw into the image or display
126     */
127     public Task(final ReferencedEnvelope envelope, final Rectangle paintArea, final Graphics2D graphics, GTRenderer renderer) {
128     this.envelope = envelope;
129     this.paintArea = paintArea;
130     this.graphics = graphics;
131     this.cancelled = false;
132     this.renderer = renderer;
133     failed = false;
134     }
135    
136     /**
137     * Called by the executor to run this rendering task.
138     *
139     * @return result of the task: completed, cancelled or failed
140     * @throws Exception
141     */
142     public TaskResult call() throws Exception {
143     if (!cancelled) {
144    
145     try {
146     renderer.addRenderListener(this);
147    
148     Composite composite = graphics.getComposite();
149     //graphics.setComposite(AlphaComposite.Src);
150     //graphics.setBackground(Color.WHITE);
151     graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
152     graphics.fill(paintArea);
153     graphics.setComposite(composite);
154    
155    
156     numFeatures = 0;
157     renderer.paint(graphics, mapPane.getVisibleRect(), envelope, mapPane.getWorldToScreenTransform());
158    
159     } finally {
160     renderer.removeRenderListener(this);
161     }
162     }
163    
164     if (cancelled) {
165     return TaskResult.CANCELLED;
166     } else if (failed) {
167     return TaskResult.FAILED;
168     } else {
169     return TaskResult.COMPLETED;
170     }
171     }
172    
173     /**
174     * Cancel the rendering task if it is running. If called before
175     * being run the task will be abandoned.
176     */
177     public synchronized void cancel() {
178     if (isRunning()) {
179     cancelled = true;
180     renderer.stopRendering();
181     }
182     }
183    
184     /**
185     * Called by the renderer when each feature is drawn.
186     *
187     * @param feature the feature just drawn
188     */
189     public void featureRenderer(SimpleFeature feature) {
190     // @todo update a progress listener
191     numFeatures++ ;
192     }
193    
194     /**
195     * Called by the renderer on error
196     *
197     * @param e cause of the error
198     */
199     public void errorOccurred(Exception e) {
200     renderingException = e;
201     graphics.setColor(Color.white);
202     graphics.drawString(e.getMessage(), 11, 11);
203     graphics.drawString(e.getMessage(), 9, 9);
204     graphics.setColor(Color.black);
205     graphics.drawString(e.getMessage(), 10, 10);
206     failed = true;
207     }
208    
209     }
210    
211     private AtomicBoolean taskRunning;
212     private Task task;
213     private Future<TaskResult> taskResult;
214     private ScheduledFuture<?> watcher;
215     private Exception renderingException;
216    
217     /**
218     * Constructor. Creates a new executor to service the specified map pane.
219     *
220     * @param mapPane the map pane to be serviced
221     */
222     public RenderingExecutor(final XMapPane mapPane) {
223     taskRunning = new AtomicBoolean(false);
224     this.mapPane = mapPane;
225     taskExecutor = Executors.newSingleThreadExecutor();
226     watchExecutor = Executors.newSingleThreadScheduledExecutor();
227     pollingInterval = DEFAULT_POLLING_INTERVAL;
228     cancelLatch = new CountDownLatch(0);
229     }
230    
231     /**
232     * Get the interval for polling the result of a rendering task
233     *
234     * @return polling interval in milliseconds
235     */
236     public long getPollingInterval() {
237     return pollingInterval;
238     }
239    
240     /**
241     * Set the interval for polling the result of a rendering task
242     *
243     * @param interval interval in milliseconds (values {@code <=} 0 are ignored)
244     */
245     public void setPollingInterval(long interval) {
246     if (interval > 0) {
247     pollingInterval = interval;
248     }
249     }
250    
251     /**
252     * Submit a new rendering task. If no rendering task is presently running
253     * this new task will be accepted; otherwise it will be rejected (ie. there
254     * is no task queue).
255     *
256     * @param envelope the map area (world coordinates) to be rendered
257     * @param graphics the graphics object to draw on
258     *
259     * @return true if the rendering task was accepted; false if it was
260     * rejected
261     */
262     public synchronized boolean submit(ReferencedEnvelope envelope, Rectangle paintArea, Graphics2D graphics, final GTRenderer renderer) {
263     if (!isRunning() || cancelLatch.getCount() > 0) {
264     try {
265     // wait for any cancelled task to finish its shutdown
266     cancelLatch.await();
267     } catch (InterruptedException ex) {
268     return false;
269     }
270    
271     task = new Task(envelope, paintArea, graphics, renderer);
272     taskRunning.set(true);
273     taskResult = taskExecutor.submit(task);
274     watcher = watchExecutor.scheduleAtFixedRate(new Runnable() {
275    
276     public void run() {
277     pollTaskResult();
278     }
279     }, DEFAULT_POLLING_INTERVAL, DEFAULT_POLLING_INTERVAL, TimeUnit.MILLISECONDS);
280    
281     return true;
282     }
283    
284     return false;
285     }
286    
287     /**
288     * Cancel the current rendering task if one is running
289     */
290     public synchronized void cancelTask() {
291     if (isRunning()) {
292     task.cancel();
293     cancelLatch = new CountDownLatch(1);
294     }
295     }
296    
297     private void pollTaskResult() {
298     if (!taskResult.isDone()) {
299     return;
300     }
301    
302     TaskResult result = TaskResult.PENDING;
303    
304     try {
305     result = taskResult.get();
306     } catch (Exception ex) {
307     throw new IllegalStateException("When getting rendering result", ex);
308     }
309    
310     watcher.cancel(false);
311     taskRunning.set(false);
312    
313     switch (result) {
314     case CANCELLED:
315     cancelLatch.countDown();
316     mapPane.onRenderingCancelled();
317     break;
318    
319     case COMPLETED:
320     mapPane.onRenderingCompleted();
321     break;
322    
323     case FAILED:
324     mapPane.onRenderingFailed(renderingException);
325     break;
326     }
327     }
328    
329     public synchronized boolean isRunning() {
330     return taskRunning.get();
331     }
332    
333     @Override
334     protected void finalize() throws Throwable {
335     if (this.isRunning()) {
336     taskExecutor.shutdownNow();
337     watchExecutor.shutdownNow();
338     }
339     }
340     }
341    

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26