/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI for
* visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.workbench.ui.renderer;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.Timer;
import com.vividsolutions.jump.util.OrderedMap;
import com.vividsolutions.jump.workbench.model.Layerable;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
public class RenderingManager {
// [sstein: 20.01.2006] added for Ole
/** @deprecated */
protected static HashMap layerableClassToRendererFactoryMap = new HashMap();
private static final Map<Class, RendererFactory> CLASS_RENDERER_FACTORY_MAP = new HashMap<Class, RendererFactory>();
private LayerViewPanel panel;
/**
* this variable will be used for {@link LayerRenderer} which extends
* {@link FeatureCollectionRenderer} default in FeatureCollectionRenderer is
* 100 features.
*/
private int maxFeatures = 100; // this variable will be used for
// LayerRenderer.class which extends
// FeatureCollectionRenderer.class
// default in FeatureCollectionRenderer is 100 features.
/**
* @see ThreadQueue
*/
public static final String USE_MULTI_RENDERING_THREAD_QUEUE_KEY = RenderingManager.class.getName()
+ " - USE MULTI RENDERING THREAD QUEUE";
private Map contentIDToRendererMap = new OrderedMap();
private OrderedMap contentIDToLowRendererFactoryMap = new OrderedMap();
private OrderedMap contentIDToHighRendererFactoryMap = new OrderedMap();
/**
* There's no performance advantage to rendering dozens of non-WMS or
* non-database layers in parallel. In fact, it will make the GUI less
* responsive. [Jon Aquino]
*/
private ThreadQueue defaultRendererThreadQueue = new ThreadQueue(1);
/**
* WMS and database processing are done on the server side, so allow these
* queries to be done in parallel. But not too many, as each Thread consumes 1
* MB of memory (see http://mindprod.com/jglossthread.html). The Threads may
* pile up if the server is down. [Jon Aquino]
*/
private ThreadQueue multiRendererThreadQueue = new ThreadQueue(20);
// 250 ms wasn't as good as 1 s because less got painted on each repaint,
// making rendering appear to be slower. [Jon Aquino]
// LDB: changed from 400 to 800 ms after raster stretch mouse wheel zooming
private Timer repaintTimer = new Timer(800, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (Iterator i = contentIDToRendererMap.values().iterator(); i.hasNext();) {
Renderer renderer = (Renderer)i.next();
if (renderer.isRendering()) {
repaintPanel();
return;
}
}
repaintTimer.stop();
repaintPanel();
}
});
private boolean paintingEnabled = true;
public RenderingManager(final LayerViewPanel panel) {
this.panel = panel;
repaintTimer.setCoalesce(true);
putAboveLayerables(SelectionBackgroundRenderer.CONTENT_ID,
new Renderer.Factory() {
public Renderer create() {
return new SelectionBackgroundRenderer(panel);
}
});
putAboveLayerables(FeatureSelectionRenderer.CONTENT_ID,
new Renderer.Factory() {
public Renderer create() {
return new FeatureSelectionRenderer(panel);
}
});
putAboveLayerables(LineStringSelectionRenderer.CONTENT_ID,
new Renderer.Factory() {
public Renderer create() {
return new LineStringSelectionRenderer(panel);
}
});
putAboveLayerables(PartSelectionRenderer.CONTENT_ID,
new Renderer.Factory() {
public Renderer create() {
return new PartSelectionRenderer(panel);
}
});
}
public void putBelowLayerables(Object contentID, Renderer.Factory factory) {
contentIDToLowRendererFactoryMap.put(contentID, factory);
}
public void putAboveLayerables(Object contentID, Renderer.Factory factory) {
contentIDToHighRendererFactoryMap.put(contentID, factory);
}
public void renderAll() {
defaultRendererThreadQueue.clear();
multiRendererThreadQueue.clear();
for (Iterator i = contentIDs().iterator(); i.hasNext();) {
Object contentID = i.next();
render(contentID);
}
}
protected List contentIDs() {
ArrayList contentIDs = new ArrayList();
contentIDs.addAll(contentIDToLowRendererFactoryMap.keyList());
for (Iterator i = panel.getLayerManager().reverseIterator(Layerable.class); i.hasNext();) {
Layerable layerable = (Layerable)i.next();
contentIDs.add(layerable);
}
contentIDs.addAll(contentIDToHighRendererFactoryMap.keyList());
return contentIDs;
}
public Renderer getRenderer(Object contentID) {
return (Renderer)contentIDToRendererMap.get(contentID);
}
private void setRenderer(Object contentID, Renderer renderer) {
contentIDToRendererMap.put(contentID, renderer);
}
public void render(Object contentID) {
render(contentID, true);
}
public void render(Object contentID, boolean clearImageCache) {
if (getRenderer(contentID) == null) {
setRenderer(contentID, createRenderer(contentID));
}
if (getRenderer(contentID).isRendering()) {
getRenderer(contentID).cancel();
// It might not cancel immediately, so create a new Renderer [Jon
// Aquino]
setRenderer(contentID, createRenderer(contentID));
}
if (clearImageCache) {
getRenderer(contentID).clearImageCache();
}
Runnable runnable = getRenderer(contentID).createRunnable();
if (runnable != null) {
// Before I would create threads that did nothing. Now I never do
// that -- I just return null. A dozen threads that do nothing make
// the system sluggish. [Jon Aquino]
((contentID instanceof Layerable && ((Layerable)contentID).getBlackboard()
.get(USE_MULTI_RENDERING_THREAD_QUEUE_KEY, false)) ? multiRendererThreadQueue
: defaultRendererThreadQueue).add(runnable);
}
if (!repaintTimer.isRunning()) {
repaintPanel();
repaintTimer.start();
}
}
public void repaintPanel() {
if (!paintingEnabled) {
return;
}
panel.superRepaint();
}
// [sstein: 20.01.2006]
// Start: added by Ole
// everything is static to make it useable before a LayerManager instance
// (containing a RenderingManager) is created
// which is the case, at the time the PlugIns are initialized and to have one
// map
// for all RenderingManager
/**
* @deprecated see {@link #getRendererFactory(Class)}
*/
public static Renderer.ContentDependendFactory getRenderFactoryForLayerable(
Class clss) {
if (layerableClassToRendererFactoryMap.containsKey(clss)) {
return (Renderer.ContentDependendFactory)layerableClassToRendererFactoryMap.get(clss);
}
return null;
}
/**
* @param clss
* @param rendererFactory
* @deprecated see {@link #setRendererFactory(Class, RendererFactory)}
*/
public static void putRendererForLayerable(Class clss,
Renderer.ContentDependendFactory rendererFactory) {
if (!layerableClassToRendererFactoryMap.containsKey(clss)) {
layerableClassToRendererFactoryMap.put(clss, rendererFactory);
}
}
// End: added by Ole*
// this method is called by method render();
protected Renderer createRenderer(Object contentID) {
RendererFactory rendererFactory = getRendererFactory(contentID.getClass());
if (rendererFactory != null) {
return rendererFactory.create(contentID, panel, maxFeatures);
}
// p_d_austin: The following items should be removed when all renderers
// are migrated to the RendererFactory framework.
// [sstein: 20.01.2006] Start: added by Ole
if (RenderingManager.getRenderFactoryForLayerable(contentID.getClass()) != null) {
return RenderingManager.getRenderFactoryForLayerable(contentID.getClass())
.create(contentID);
}
// End: added by Ole*
if (contentIDToLowRendererFactoryMap.containsKey(contentID)) {
return ((Renderer.Factory)contentIDToLowRendererFactoryMap.get(contentID)).create();
}
if (contentIDToHighRendererFactoryMap.containsKey(contentID)) {
return ((Renderer.Factory)contentIDToHighRendererFactoryMap.get(contentID)).create();
}
throw new IllegalArgumentException("No renderer defined for layerable: "
+ contentID);
}
public void setPaintingEnabled(boolean paintingEnabled) {
this.paintingEnabled = paintingEnabled;
}
public void copyTo(Graphics2D destination) {
for (Iterator i = contentIDs().iterator(); i.hasNext();) {
Object contentID = i.next();
if (getRenderer(contentID) != null) {
getRenderer(contentID).copyTo(destination);
}
}
}
public ThreadQueue getDefaultRendererThreadQueue() {
return defaultRendererThreadQueue;
}
public void dispose() {
repaintTimer.stop();
defaultRendererThreadQueue.dispose();
multiRendererThreadQueue.dispose();
// The ThreadSafeImage cached in each Renderer consumes 1 MB of memory,
// according to OptimizeIt [Jon Aquino]
contentIDToRendererMap.clear();
}
public LayerViewPanel getPanel() {
return panel;
}
// [sstein] added 30.07.2005
/**
* @return Returns the number of maxFeatures to render as vector graphic.
*/
public int getMaxFeatures() {
return maxFeatures;
}
/**
* @param maxFeatures The maximum number of Features to render as vector
* graphic.
* <p>
* Use this method before using method render(Object contentID) or
* render(Object contentID, boolean clearImageCache)
*/
public void setMaxFeatures(int maxFeatures) {
this.maxFeatures = maxFeatures;
}
/**
* Remove the LayerRenderer when a Layer is removed (helps to free the memory)
* Added on 2007-05-21 [Michael Michaud and Larry Becker] Called by
* LayerManager
*
* @param contentID layer to remove
*/
public void removeLayerRenderer(Object contentID) {
contentIDToRendererMap.remove(contentID);
}
/**
* Get the renderer factory for the class.
*
* @param clazz The class
* @return The renderer factory.
*/
public static RendererFactory getRendererFactory(Class clazz) {
if (clazz == null) {
return null;
} else {
RendererFactory factory = CLASS_RENDERER_FACTORY_MAP.get(clazz);
if (factory == null) {
return getRendererFactory(clazz.getSuperclass());
}
return factory;
}
}
/**
* Set the renderer factory for the class.
*
* @param clazz The class
* @param factory The renderer factory.
*/
public static void setRendererFactory(Class clazz, RendererFactory factory) {
CLASS_RENDERER_FACTORY_MAP.put(clazz, factory);
}
}