[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