[Schmitzm-commits] r510 - branches/1.0-gt2-2.6/src/skrueger/geotools
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Thu Nov 5 18:39:38 CET 2009
Author: alfonx
Date: 2009-11-05 18:39:37 +0100 (Thu, 05 Nov 2009)
New Revision: 510
Added:
branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java
Log:
my own version of geotool's RenderingExecutor, because theirs runs agains JMapPane only. We use XMapPane
Added: branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java
===================================================================
--- branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java 2009-11-05 08:51:33 UTC (rev 509)
+++ branches/1.0-gt2-2.6/src/skrueger/geotools/RenderingExecutor.java 2009-11-05 17:39:37 UTC (rev 510)
@@ -0,0 +1,341 @@
+package skrueger.geotools;
+
+/*
+ * 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.
+ */
+
+
+import gtmig.org.geotools.swing.XMapPane;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.renderer.GTRenderer;
+import org.geotools.renderer.RenderListener;
+import org.geotools.swing.JMapPane;
+import org.opengis.feature.simple.SimpleFeature;
+
+/**
+ * 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
+ */
+public class RenderingExecutor {
+
+ private final XMapPane mapPane;
+ private final ExecutorService taskExecutor;
+ private final ScheduledExecutorService watchExecutor;
+
+ /** The default interval (milliseconds) for polling the result of a rendering task */
+ public static final long DEFAULT_POLLING_INTERVAL = 100L;
+
+ 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
+ */
+ public RenderingExecutor(final XMapPane mapPane) {
+ taskRunning = new AtomicBoolean(false);
+ this.mapPane = mapPane;
+ taskExecutor = Executors.newSingleThreadExecutor();
+ watchExecutor = Executors.newSingleThreadScheduledExecutor();
+ pollingInterval = DEFAULT_POLLING_INTERVAL;
+ 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;
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * Submit a new rendering task. If no rendering task is presently running
+ * this new task will be accepted; otherwise it will be rejected (ie. there
+ * is no task queue).
+ *
+ * @param envelope the map area (world coordinates) to be rendered
+ * @param graphics the graphics object to draw on
+ *
+ * @return true if the rendering task was accepted; false if it was
+ * rejected
+ */
+ public synchronized boolean submit(ReferencedEnvelope envelope, Rectangle paintArea, Graphics2D graphics, final GTRenderer renderer) {
+ if (!isRunning() || cancelLatch.getCount() > 0) {
+ try {
+ // wait for any cancelled task to finish its shutdown
+ cancelLatch.await();
+ } catch (InterruptedException ex) {
+ return false;
+ }
+
+ task = new Task(envelope, paintArea, graphics, renderer);
+ taskRunning.set(true);
+ taskResult = taskExecutor.submit(task);
+ watcher = watchExecutor.scheduleAtFixedRate(new Runnable() {
+
+ public void run() {
+ pollTaskResult();
+ }
+ }, DEFAULT_POLLING_INTERVAL, DEFAULT_POLLING_INTERVAL, TimeUnit.MILLISECONDS);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Cancel the current rendering task if one is running
+ */
+ public synchronized void cancelTask() {
+ if (isRunning()) {
+ task.cancel();
+ 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;
+ }
+ }
+
+ public synchronized boolean isRunning() {
+ return taskRunning.get();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (this.isRunning()) {
+ taskExecutor.shutdownNow();
+ watchExecutor.shutdownNow();
+ }
+ }
+}
+
More information about the Schmitzm-commits
mailing list