/[schmitzm]/branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java
ViewVC logotype

Contents of /branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 513 - (show annotations)
Mon Nov 9 11:17:34 2009 UTC (15 years, 3 months ago) by alfonx
File size: 11172 byte(s)
Fixed an error in the zoom-out code of XMapPane

1 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 = 300L;
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 * @param l
222 */
223 public RenderingExecutor(final XMapPane mapPane, long pollingInterval) {
224 taskRunning = new AtomicBoolean(false);
225 this.mapPane = mapPane;
226 taskExecutor = Executors.newSingleThreadExecutor();
227 watchExecutor = Executors.newSingleThreadScheduledExecutor();
228 this.pollingInterval = pollingInterval;
229 cancelLatch = new CountDownLatch(0);
230 }
231
232 /**
233 * Get the interval for polling the result of a rendering task
234 *
235 * @return polling interval in milliseconds
236 */
237 public long getPollingInterval() {
238 return pollingInterval;
239 }
240
241 /**
242 * Set the interval for polling the result of a rendering task
243 *
244 * @param interval interval in milliseconds (values {@code <=} 0 are ignored)
245 */
246 public void setPollingInterval(long interval) {
247 if (interval > 0) {
248 pollingInterval = interval;
249 }
250 }
251
252 /**
253 * Submit a new rendering task. If no rendering task is presently running
254 * this new task will be accepted; otherwise it will be rejected (ie. there
255 * is no task queue).
256 *
257 * @param envelope the map area (world coordinates) to be rendered
258 * @param graphics the graphics object to draw on
259 *
260 * @return true if the rendering task was accepted; false if it was
261 * rejected
262 */
263 public synchronized boolean submit(ReferencedEnvelope envelope, Rectangle paintArea, Graphics2D graphics, final GTRenderer renderer) {
264 if (!isRunning() || cancelLatch.getCount() > 0) {
265 try {
266 // wait for any cancelled task to finish its shutdown
267 cancelLatch.await();
268 } catch (InterruptedException ex) {
269 return false;
270 }
271
272 task = new Task(envelope, paintArea, graphics, renderer);
273 taskRunning.set(true);
274 taskResult = taskExecutor.submit(task);
275 watcher = watchExecutor.scheduleAtFixedRate(new Runnable() {
276
277 public void run() {
278 pollTaskResult();
279 }
280 }, pollingInterval, pollingInterval, TimeUnit.MILLISECONDS);
281
282 return true;
283 }
284
285 return false;
286 }
287
288 /**
289 * Cancel the current rendering task if one is running
290 */
291 public synchronized void cancelTask() {
292 if (isRunning()) {
293 task.cancel();
294 cancelLatch = new CountDownLatch(1);
295 }
296 }
297
298 private void pollTaskResult() {
299 if (!taskResult.isDone()) {
300 return;
301 }
302
303 TaskResult result = TaskResult.PENDING;
304
305 try {
306 result = taskResult.get();
307 } catch (Exception ex) {
308 throw new IllegalStateException("When getting rendering result", ex);
309 }
310
311 watcher.cancel(false);
312 taskRunning.set(false);
313
314 switch (result) {
315 case CANCELLED:
316 cancelLatch.countDown();
317 mapPane.onRenderingCancelled();
318 break;
319
320 case COMPLETED:
321 mapPane.onRenderingCompleted();
322 break;
323
324 case FAILED:
325 mapPane.onRenderingFailed(renderingException);
326 break;
327
328 case PENDING:
329 mapPane.onRenderingPending();
330 break;
331
332 }
333 }
334
335 public synchronized boolean isRunning() {
336 return taskRunning.get();
337 }
338
339 @Override
340 protected void finalize() throws Throwable {
341 if (this.isRunning()) {
342 taskExecutor.shutdownNow();
343 watchExecutor.shutdownNow();
344 }
345 }
346 }
347

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26