001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI for
003: * visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or modify it under
008: * the terms of the GNU General Public License as published by the Free Software
009: * Foundation; either version 2 of the License, or (at your option) any later
010: * version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
014: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
015: * details.
016: *
017: * You should have received a copy of the GNU General Public License along with
018: * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
019: * Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.workbench.ui.renderer;
034:
035: import java.awt.Graphics2D;
036: import java.awt.event.ActionEvent;
037: import java.awt.event.ActionListener;
038: import java.util.ArrayList;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.Map;
043:
044: import javax.swing.Timer;
045:
046: import com.vividsolutions.jts.util.Assert;
047: import com.vividsolutions.jump.util.OrderedMap;
048: import com.vividsolutions.jump.workbench.model.Layer;
049: import com.vividsolutions.jump.workbench.model.Layerable;
050: import com.vividsolutions.jump.workbench.model.WMSLayer;
051: import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
052:
053: //[sstein] : 30.07.2005 added variable maxFeatures with getters and setters
054:
055: public class RenderingManager {
056: private LayerViewPanel panel;
057: private int maxFeatures = 100; //this variable will be used for
058: //LayerRenderer.class which extends
059: //FeatureCollectionRenderer.class
060: //default in FeatureCollectionRenderer is 100 features.
061:
062: /**
063: * @see multiRendererThreadQueue
064: */
065: public static final String USE_MULTI_RENDERING_THREAD_QUEUE_KEY = RenderingManager.class
066: .getName()
067: + " - USE MULTI RENDERING THREAD QUEUE";
068:
069: private Map contentIDToRendererMap = new OrderedMap();
070:
071: private OrderedMap contentIDToLowRendererFactoryMap = new OrderedMap();
072:
073: private OrderedMap contentIDToHighRendererFactoryMap = new OrderedMap();
074:
075: /**
076: * There's no performance advantage to rendering dozens of non-WMS or
077: * non-database layers in parallel. In fact, it will make the GUI less
078: * responsive. [Jon Aquino]
079: */
080: private ThreadQueue defaultRendererThreadQueue = new ThreadQueue(1);
081:
082: /**
083: * WMS and database processing are done on the server side, so allow these
084: * queries to be done in parallel. But not too many, as each Thread consumes
085: * 1 MB of memory (see http://mindprod.com/jglossthread.html). The Threads
086: * may pile up if the server is down. [Jon Aquino]
087: */
088: private ThreadQueue multiRendererThreadQueue = new ThreadQueue(20);
089:
090: //250 ms wasn't as good as 1 s because less got painted on each repaint,
091: //making rendering appear to be slower. [Jon Aquino]
092: //LDB: 400 ms is better when using mouse wheel zooming
093: private Timer repaintTimer = new Timer(400, new ActionListener() {
094: public void actionPerformed(ActionEvent e) {
095: for (Iterator i = contentIDToRendererMap.values()
096: .iterator(); i.hasNext();) {
097: Renderer renderer = (Renderer) i.next();
098: if (renderer.isRendering()) {
099: repaintPanel();
100: return;
101: }
102: }
103:
104: repaintTimer.stop();
105: repaintPanel();
106: }
107: });
108:
109: private boolean paintingEnabled = true;
110:
111: //[sstein: 20.01.2006] added for Ole
112: protected static HashMap layerableClassToRendererFactoryMap = new HashMap();
113:
114: public RenderingManager(final LayerViewPanel panel) {
115: this .panel = panel;
116: repaintTimer.setCoalesce(true);
117: putAboveLayerables(SelectionBackgroundRenderer.CONTENT_ID,
118: new Renderer.Factory() {
119: public Renderer create() {
120: return new SelectionBackgroundRenderer(panel);
121: }
122: });
123: putAboveLayerables(FeatureSelectionRenderer.CONTENT_ID,
124: new Renderer.Factory() {
125: public Renderer create() {
126: return new FeatureSelectionRenderer(panel);
127: }
128: });
129: putAboveLayerables(LineStringSelectionRenderer.CONTENT_ID,
130: new Renderer.Factory() {
131: public Renderer create() {
132: return new LineStringSelectionRenderer(panel);
133: }
134: });
135: putAboveLayerables(PartSelectionRenderer.CONTENT_ID,
136: new Renderer.Factory() {
137: public Renderer create() {
138: return new PartSelectionRenderer(panel);
139: }
140: });
141: }
142:
143: public void putBelowLayerables(Object contentID,
144: Renderer.Factory factory) {
145: contentIDToLowRendererFactoryMap.put(contentID, factory);
146: }
147:
148: public void putAboveLayerables(Object contentID,
149: Renderer.Factory factory) {
150: contentIDToHighRendererFactoryMap.put(contentID, factory);
151: }
152:
153: public void renderAll() {
154: defaultRendererThreadQueue.clear();
155: multiRendererThreadQueue.clear();
156:
157: for (Iterator i = contentIDs().iterator(); i.hasNext();) {
158: Object contentID = i.next();
159: render(contentID);
160: }
161: }
162:
163: protected List contentIDs() {
164: ArrayList contentIDs = new ArrayList();
165: contentIDs.addAll(contentIDToLowRendererFactoryMap.keyList());
166: for (Iterator i = panel.getLayerManager().reverseIterator(
167: Layerable.class); i.hasNext();) {
168: Layerable layerable = (Layerable) i.next();
169: contentIDs.add(layerable);
170: }
171:
172: contentIDs.addAll(contentIDToHighRendererFactoryMap.keyList());
173:
174: return contentIDs;
175: }
176:
177: public Renderer getRenderer(Object contentID) {
178: return (Renderer) contentIDToRendererMap.get(contentID);
179: }
180:
181: private void setRenderer(Object contentID, Renderer renderer) {
182: contentIDToRendererMap.put(contentID, renderer);
183: }
184:
185: public void render(Object contentID) {
186: render(contentID, true);
187: }
188:
189: public void render(Object contentID, boolean clearImageCache) {
190:
191: if (getRenderer(contentID) == null) {
192: setRenderer(contentID, createRenderer(contentID));
193: }
194:
195: if (getRenderer(contentID).isRendering()) {
196: getRenderer(contentID).cancel();
197:
198: //It might not cancel immediately, so create a new Renderer [Jon
199: // Aquino]
200: setRenderer(contentID, createRenderer(contentID));
201: }
202:
203: if (clearImageCache) {
204: getRenderer(contentID).clearImageCache();
205: }
206: Runnable runnable = getRenderer(contentID).createRunnable();
207: if (runnable != null) {
208: // Before I would create threads that did nothing. Now I never do
209: // that -- I just return null. A dozen threads that do nothing make
210: // the system sluggish. [Jon Aquino]
211: ((contentID instanceof Layerable && ((Layerable) contentID)
212: .getBlackboard()
213: .get(USE_MULTI_RENDERING_THREAD_QUEUE_KEY, false)) ? multiRendererThreadQueue
214: : defaultRendererThreadQueue).add(runnable);
215: }
216:
217: if (!repaintTimer.isRunning()) {
218: repaintPanel();
219: repaintTimer.start();
220: }
221: }
222:
223: public void repaintPanel() {
224: if (!paintingEnabled) {
225: return;
226: }
227:
228: panel.super Repaint();
229: }
230:
231: //[sstein: 20.01.2006]
232: // Start: added by Ole
233: // everything is static to make it useable before a LayerManager instance
234: // (containing a RenderingManager) is created
235: // which is the case, at the time the PlugIns are initialized and to have one map
236: // for all RenderingManager
237: public static Renderer.ContentDependendFactory getRenderFactoryForLayerable(
238: Class clss) {
239: if (layerableClassToRendererFactoryMap.containsKey(clss)) {
240: return (Renderer.ContentDependendFactory) layerableClassToRendererFactoryMap
241: .get(clss);
242: }
243: return null;
244: }
245:
246: public static void putRendererForLayerable(Class clss,
247: Renderer.ContentDependendFactory rendererFactory) {
248: if (!layerableClassToRendererFactoryMap.containsKey(clss)) {
249: layerableClassToRendererFactoryMap.put(clss,
250: rendererFactory);
251: }
252: }
253:
254: // End: added by Ole*
255:
256: //this method is called by method render();
257: protected Renderer createRenderer(Object contentID) {
258: if (contentID instanceof Layer) {
259: //[sstein] new
260: LayerRenderer lr = new LayerRenderer((Layer) contentID,
261: this .panel);
262: lr.setMaxFeatures(this .maxFeatures);
263: return lr;
264: //[sstein] old
265: //return new LayerRenderer((Layer) contentID, panel);
266: }
267: if (contentID instanceof WMSLayer) {
268: return new WMSLayerRenderer((WMSLayer) contentID, panel);
269: }
270: //[sstein: 20.01.2006] Start: added by Ole
271: if (RenderingManager.getRenderFactoryForLayerable(contentID
272: .getClass()) != null) {
273: return RenderingManager.getRenderFactoryForLayerable(
274: contentID.getClass()).create(contentID);
275: }
276: //End: added by Ole*
277: if (contentIDToLowRendererFactoryMap.containsKey(contentID)) {
278: return ((Renderer.Factory) contentIDToLowRendererFactoryMap
279: .get(contentID)).create();
280: }
281: if (contentIDToHighRendererFactoryMap.containsKey(contentID)) {
282: return ((Renderer.Factory) contentIDToHighRendererFactoryMap
283: .get(contentID)).create();
284: }
285: Assert.shouldNeverReachHere(contentID.toString());
286: return null;
287: }
288:
289: public void setPaintingEnabled(boolean paintingEnabled) {
290: this .paintingEnabled = paintingEnabled;
291: }
292:
293: public void copyTo(Graphics2D destination) {
294: for (Iterator i = contentIDs().iterator(); i.hasNext();) {
295: Object contentID = i.next();
296:
297: if (getRenderer(contentID) != null) {
298: getRenderer(contentID).copyTo(destination);
299: }
300: }
301: }
302:
303: public ThreadQueue getDefaultRendererThreadQueue() {
304: return defaultRendererThreadQueue;
305: }
306:
307: public void dispose() {
308: repaintTimer.stop();
309: defaultRendererThreadQueue.dispose();
310: multiRendererThreadQueue.dispose();
311: //The ThreadSafeImage cached in each Renderer consumes 1 MB of memory,
312: //according to OptimizeIt [Jon Aquino]
313: contentIDToRendererMap.clear();
314: }
315:
316: public LayerViewPanel getPanel() {
317: return panel;
318: }
319:
320: //[sstein] added 30.07.2005
321:
322: /**
323: * @return Returns the number of maxFeatures to render
324: * as vector graphic.
325: */
326: public int getMaxFeatures() {
327: return maxFeatures;
328: }
329:
330: /**
331: * @param maxFeatures The maximum number of Features to render
332: * as vector graphic.<p>
333: * Use this method before using method render(Object contentID) or render(Object contentID, boolean clearImageCache)
334: */
335: public void setMaxFeatures(int maxFeatures) {
336: this .maxFeatures = maxFeatures;
337: }
338:
339: /**
340: * Remove the LayerRenderer when a Layer is removed (helps to free the memory)
341: * Added on 2007-05-21 [Michael Michaud and Larry Becker]
342: * Called by LayerManager
343: * @param layer layer to remove
344: */
345: public void removeLayerRenderer(Object contentID) {
346: contentIDToRendererMap.remove(contentID);
347: }
348:
349: }
|