001: /*
002: * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004, Refractions Research Inc. This
003: * library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
004: * License as published by the Free Software Foundation; version 2.1 of the License. This library is distributed in the
005: * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
006: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
007: */
008: package net.refractions.udig.render.internal.feature.basic;
009:
010: import java.awt.Color;
011: import java.awt.Graphics2D;
012: import java.awt.Point;
013: import java.awt.Rectangle;
014: import java.awt.RenderingHints;
015: import java.io.IOException;
016: import java.util.HashMap;
017: import java.util.logging.ConsoleHandler;
018: import java.util.logging.Level;
019: import java.util.logging.Logger;
020:
021: import net.refractions.udig.core.TransparencyRemovingVisitor;
022: import net.refractions.udig.project.ILayer;
023: import net.refractions.udig.project.internal.ProjectPlugin;
024: import net.refractions.udig.project.internal.StyleBlackboard;
025: import net.refractions.udig.project.internal.render.SelectionLayer;
026: import net.refractions.udig.project.internal.render.impl.RendererImpl;
027: import net.refractions.udig.project.internal.render.impl.Styling;
028: import net.refractions.udig.project.preferences.PreferenceConstants;
029: import net.refractions.udig.project.render.ILabelPainter;
030: import net.refractions.udig.project.render.IRenderContext;
031: import net.refractions.udig.project.render.RenderException;
032: import net.refractions.udig.render.feature.basic.internal.Messages;
033: import net.refractions.udig.ui.ProgressManager;
034:
035: import org.eclipse.core.runtime.IProgressMonitor;
036: import org.eclipse.core.runtime.SubProgressMonitor;
037: import org.eclipse.jface.preference.IPreferenceStore;
038: import org.geotools.data.FeatureSource;
039: import org.geotools.data.Query;
040: import org.geotools.feature.Feature;
041: import org.geotools.filter.FilterFactoryFinder;
042: import org.geotools.geometry.jts.ReferencedEnvelope;
043: import org.geotools.map.DefaultMapContext;
044: import org.geotools.map.DefaultMapLayer;
045: import org.geotools.map.MapContext;
046: import org.geotools.map.MapLayer;
047: import org.geotools.renderer.GTRenderer;
048: import org.geotools.renderer.RenderListener;
049: import org.geotools.renderer.lite.StreamingRenderer;
050: import org.geotools.styling.Style;
051: import org.geotools.styling.StyleFactoryFinder;
052: import org.geotools.styling.visitor.DuplicatorStyleVisitor;
053: import org.opengis.referencing.FactoryException;
054: import org.opengis.referencing.crs.CoordinateReferenceSystem;
055: import org.opengis.referencing.operation.TransformException;
056:
057: import com.vividsolutions.jts.geom.Coordinate;
058: import com.vividsolutions.jts.geom.Envelope;
059: import com.vividsolutions.jts.geom.TopologyException;
060:
061: /**
062: * The default victim renderer. Based on the Lite-Renderer from Geotools.
063: *
064: * @author Jesse Eichar
065: * @version $Revision: 1.9 $
066: */
067: public class BasicFeatureRenderer extends RendererImpl {
068:
069: private GTRenderer renderer = null;
070:
071: protected MapContext map = null;
072:
073: protected MapLayer[] layers = null;
074:
075: protected BasicRenderListener listener = new BasicRenderListener();
076:
077: public BasicFeatureRenderer() {
078:
079: ClassLoader current = getClass().getClassLoader();
080: try {
081: Thread.currentThread().setContextClassLoader(
082: StreamingRenderer.class.getClassLoader());
083: Logger logger = Logger.getLogger("org.geotools.rendering");//$NON-NLS-1$
084: if (RendererPlugin.isDebugging(Trace.FINEST)) {
085: logger.setLevel(Level.FINE);
086: ConsoleHandler ch = new ConsoleHandler();
087: ch.setLevel(Level.FINE);
088: logger.addHandler(ch);
089: } else {
090: logger.setLevel(Level.SEVERE);
091: }
092: } finally {
093: Thread.currentThread().setContextClassLoader(current);
094: }
095: }
096:
097: /**
098: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#render(java.awt.Graphics2D,
099: * org.eclipse.core.runtime.IProgressMonitor)
100: */
101: public void render(Graphics2D destination, IProgressMonitor monitor)
102: throws RenderException {
103: render(destination,
104: getContext().getViewportModel().getBounds(), monitor);
105: }
106:
107: private final static int NOT_INITIALIZED = -2;
108:
109: int count = NOT_INITIALIZED;
110:
111: protected void setQueries() {
112: try {
113: layers[0].setQuery(getContext().getFeatureQuery());
114: } catch (Exception e) {
115: // do nothing.
116: }
117: }
118:
119: /**
120: * does some additional initialization in preparation for drawing. It only
121: * needs to be done once so there is a quick shortcircuit check in the
122: * beginning Obtains the features source, creates the MapLayer and Map
123: * context objects required for Lite renderer and creates the lite renderer.
124: *
125: * @throws IOException
126: */
127: private void prepareDraw(IProgressMonitor monitor)
128: throws IOException {
129:
130: // check for style information on the blackboard
131: StyleBlackboard styleBlackboard = (StyleBlackboard) getContext()
132: .getLayer().getStyleBlackboard();
133: FeatureSource featureSource = getContext().getLayer()
134: .getResource(FeatureSource.class,
135: new SubProgressMonitor(monitor, 0));
136:
137: Style style = getStyle(styleBlackboard, featureSource);
138:
139: layers = new MapLayer[1];
140: layers[0] = new DefaultMapLayer(featureSource, style, "Test"); //$NON-NLS-1$
141: map = new DefaultMapContext(layers);
142:
143: }
144:
145: protected Style getStyle(StyleBlackboard styleBlackboard,
146: FeatureSource featureSource) {
147: // pull style information off the blackboard
148: Style style = (Style) styleBlackboard.lookup(Style.class);
149: IPreferenceStore store = ProjectPlugin.getPlugin()
150: .getPreferenceStore();
151: boolean transparency = store
152: .getBoolean(PreferenceConstants.P_TRANSPARENCY);
153:
154: if (style != null) {
155: DuplicatorStyleVisitor duplicator = new DuplicatorStyleVisitor(
156: StyleFactoryFinder.createStyleFactory(),
157: FilterFactoryFinder.createFilterFactory());
158: style.accept(duplicator);
159: style = (Style) duplicator.getCopy();
160: if (!transparency) {
161: style = removeTransparency(style);
162: }
163: }
164: if (style == null) {
165: style = Styling.createLineStyle(featureSource.getSchema()
166: .getTypeName(), Color.BLUE);
167: }
168: return style;
169: }
170:
171: private Style removeTransparency(Style style) {
172: style.accept(new TransparencyRemovingVisitor());
173: return style;
174: }
175:
176: /**
177: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#dispose()
178: */
179: public void dispose() {
180: if (getRenderer() != null)
181: getRenderer().stopRendering();
182: }
183:
184: @Override
185: public void setState(int newState) {
186: super .setState(newState);
187: }
188:
189: /**
190: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#render(com.vividsolutions.jts.geom.Envelope,
191: * org.eclipse.core.runtime.IProgressMonitor)
192: */
193: public void render(IProgressMonitor monitor) throws RenderException {
194: Graphics2D graphics = null;
195: try {
196: graphics = getContext().getImage().createGraphics();
197: render(graphics, getRenderBounds(), monitor);
198: } finally {
199: if (graphics != null)
200: graphics.dispose();
201: }
202: }
203:
204: @SuppressWarnings("unchecked")
205: private void render(Graphics2D graphics, Envelope bounds,
206: IProgressMonitor monitor) throws RenderException {
207:
208: getContext().setStatus(ILayer.WAIT);
209: getContext().setStatusMessage(
210: Messages.BasicFeatureRenderer_rendering_status);
211: String endMessage = null;
212: int endStatus = ILayer.DONE;
213: try {
214:
215: if (getContext().getLayer().getSchema() == null
216: || getContext().getLayer().getSchema()
217: .getDefaultGeometry() == null) {
218: endStatus = ILayer.WARNING;
219: endMessage = Messages.BasicFeatureRenderer_layer_has_no_geometry;
220: return;
221: }
222:
223: prepareDraw(monitor);
224:
225: if (monitor.isCanceled())
226: return;
227: // setFeatureLoading(monitor);
228:
229: ReferencedEnvelope validBounds = validateBounds(bounds,
230: monitor, getContext());
231:
232: if (validBounds.isNull())
233: return;
234:
235: try {
236: validBounds.transform(getContext().getLayer().getCRS(),
237: true);
238: } catch (TransformException te) {
239: RendererPlugin.log("", te); //$NON-NLS-1$
240: endMessage = Messages.BasicFeatureRenderer_warning1;
241: endStatus = ILayer.WARNING;
242: return;
243: } catch (AssertionError te) {
244: // this clause is enable this fix to work even during developement
245: RendererPlugin.log("", te); //$NON-NLS-1$
246: endMessage = Messages.BasicFeatureRenderer_warning1;
247: endStatus = ILayer.WARNING;
248: return;
249: } catch (FactoryException e) {
250: throw (RenderException) new RenderException()
251: .initCause(e);
252: }
253:
254: listener.init(monitor);
255: setQueries();
256: Point min = getContext().worldToPixel(
257: new Coordinate(validBounds.getMinX(), validBounds
258: .getMinY()));
259: Point max = getContext().worldToPixel(
260: new Coordinate(validBounds.getMaxX(), validBounds
261: .getMaxY()));
262: int width = Math.abs(max.x - min.x);
263: int height = Math.abs(max.y - min.y);
264: //TODO: if width or height = 0, then it's a point...need to
265: // figure out how much ti render (examine style)
266: Rectangle paintArea;
267: if (height == 0 || width == 0) {
268: width = 50;
269: height = 50;
270: min.x -= 25;
271: min.y -= 25;
272: max.x += 25;
273: max.x += 25;
274: graphics.setBackground(new Color(0, 0, 0, 0));
275: graphics.clearRect(min.x, min.y, width, height);
276: paintArea = new Rectangle(Math.min(min.x, max.x), Math
277: .min(min.y, max.y), width, height);
278: validBounds = getContext().worldBounds(paintArea);
279: } else {
280: paintArea = new Rectangle(Math.min(min.x, max.x), Math
281: .min(min.y, max.y), width, height);
282: }
283:
284: map.setAreaOfInterest(validBounds, getContext()
285: .getViewportModel().getCRS());
286:
287: GTRenderer renderer2 = getRenderer();
288:
289: if (getRenderBounds() != null
290: && !getRenderBounds().isNull()) {
291: graphics.setClip(paintArea);
292: }
293: java.util.Map<String, Object> rendererHints = renderer2
294: .getRendererHints();
295: rendererHints.put(StreamingRenderer.FORCE_CRS_KEY,
296: getContext().getLayer().getCRS()); //$NON-NLS-1$
297: rendererHints.put(
298: StreamingRenderer.DECLARED_SCALE_DENOM_KEY,
299: getContext().getViewportModel()
300: .getScaleDenominator()); //$NON-NLS-1$
301: ILabelPainter labelPainter = getContext().getLabelPainter();
302: Point origin = new Point(paintArea.x, paintArea.y);
303: String layerId = getContext().getLayer().getID().toString();
304: if (getContext().getLayer() instanceof SelectionLayer)
305: layerId = layerId + "-Selection";
306: rendererHints.put(StreamingRenderer.LABEL_CACHE_KEY,
307: new LabelCacheDecorator(labelPainter, origin,
308: layerId)); //$NON-NLS-1$
309:
310: renderer2.setRendererHints(rendererHints);
311: IPreferenceStore store = ProjectPlugin.getPlugin()
312: .getPreferenceStore();
313: boolean antiAliasing = store
314: .getBoolean(PreferenceConstants.P_ANTI_ALIASING);
315: RenderingHints hints = new RenderingHints(
316: RenderingHints.KEY_ANTIALIASING,
317: antiAliasing ? RenderingHints.VALUE_ANTIALIAS_ON
318: : RenderingHints.VALUE_ANTIALIAS_OFF);
319:
320: if (monitor.isCanceled())
321: return;
322:
323: renderer2.setJava2DHints(hints);
324: renderer2.paint(graphics, paintArea, validBounds);
325:
326: } catch (Throwable e1) {
327: if (e1 instanceof InterruptedException)
328: return;
329: RenderException e2 = new RenderException(
330: "Exception(s) occured during rendering: " //$NON-NLS-1$
331: + e1.getLocalizedMessage());
332: e2.initCause(e1);
333: throw e2;
334: } finally {
335: /*
336: * vitalus:
337: * Clear MapContext to remove <code>FeatureListener</code>s from
338: * FeatureStore implementation, otherwises listeners hell takes place
339: * (example is a ShapefileDataStore and its FeatureListenerManager).
340: */
341: map.clearLayerList();
342:
343: getContext().setStatus(endStatus);
344: getContext().setStatusMessage(endMessage);
345: if (listener.exception != null) {
346: if (listener.exception instanceof InterruptedException)
347: return;
348: if (!(listener.exception instanceof TopologyException)) {
349: RenderException e2 = new RenderException(
350: Messages.BasicFeatureRenderer_renderingProblem
351: + listener.exception
352: .getLocalizedMessage());
353: e2.initCause(listener.exception);
354: throw e2;
355: }
356: }
357: if (!listener.featureRendered) {
358: int totalFeatures = -1;
359: try {
360: totalFeatures = getContext().getLayer()
361: .getResource(FeatureSource.class, monitor)
362: .getCount(Query.ALL);
363: } catch (Exception e) {
364: RendererPlugin.log("", e); //$NON-NLS-1$
365: }
366: if (totalFeatures != -1 && totalFeatures > 0) {
367: getContext().setStatus(ILayer.WARNING);
368: getContext().setStatusMessage(
369: Messages.BasicFeatureRenderer_noFeatures);
370: }
371: }
372: }
373: }
374:
375: protected GTRenderer getRenderer() {
376: if (renderer == null) {
377: renderer = new StreamingRenderer();
378: HashMap<String, Object> rendererHints = new HashMap<String, Object>();
379: rendererHints.put("optimizedDataLoadingEnabled", true); //$NON-NLS-1$
380: renderer.setRendererHints(rendererHints);
381: renderer
382: .removeRenderListener(StreamingRenderer.DEFAULT_LISTENER);
383: renderer.addRenderListener(listener);
384:
385: }
386: renderer.setContext(map);
387: return renderer;
388: }
389:
390: public static ReferencedEnvelope validateBounds(Envelope bounds,
391: IProgressMonitor monitor, IRenderContext context)
392: throws IOException, FactoryException, RenderException {
393:
394: double minx, maxx, miny, maxy;
395: Envelope vpBounds = context.getViewportModel().getBounds();
396:
397: if (bounds == null) {
398: minx = vpBounds.getMinX();
399: maxx = vpBounds.getMaxX();
400: miny = vpBounds.getMinY();
401: maxy = vpBounds.getMaxY();
402: } else {
403: if (!bounds.intersects(vpBounds))
404: return new ReferencedEnvelope(context.getCRS());
405:
406: minx = Math.max(vpBounds.getMinX(), bounds.getMinX());
407: maxx = Math.min(vpBounds.getMaxX(), bounds.getMaxX());
408: miny = Math.max(vpBounds.getMinY(), bounds.getMinY());
409: maxy = Math.min(vpBounds.getMaxY(), bounds.getMaxY());
410: }
411: CoordinateReferenceSystem viewportCRS = context.getCRS();
412:
413: ReferencedEnvelope layerBounds = context.getLayer().getBounds(
414: new SubProgressMonitor(monitor, 0), viewportCRS);
415:
416: if (!layerBounds
417: .intersects(new Envelope(minx, maxx, miny, maxy)))
418: return new ReferencedEnvelope(context.getCRS());
419:
420: minx = Math.max(layerBounds.getMinX(), minx);
421: maxx = Math.min(layerBounds.getMaxX(), maxx);
422: miny = Math.max(layerBounds.getMinY(), miny);
423: maxy = Math.min(layerBounds.getMaxY(), maxy);
424:
425: ReferencedEnvelope validBounds = new ReferencedEnvelope(minx,
426: maxx, miny, maxy, viewportCRS);
427:
428: return validBounds;
429: }
430:
431: private class BasicRenderListener implements RenderListener {
432:
433: IProgressMonitor monitor;
434:
435: Exception exception;
436:
437: int exceptionCount = 0;
438:
439: boolean featureRendered = false;
440: int count = 0;
441: long lastUpdate;
442:
443: private static final int UPDATE_INTERVAL = 3000;
444:
445: /**
446: * @see org.geotools.renderer.lite.RenderListener#featureRenderer(org.geotools.feature.Feature)
447: */
448: public void featureRenderer(Feature feature) {
449: if (!featureRendered)
450: featureRendered = true;
451:
452: count++;
453: synchronized (monitor) {
454: if (monitor.isCanceled())
455: getRenderer().stopRendering();
456: }
457: long current = System.currentTimeMillis();
458: if (current - lastUpdate > UPDATE_INTERVAL) {
459: lastUpdate = current;
460: setState(RENDERING);
461: }
462: }
463:
464: /**
465: * @see org.geotools.renderer.lite.RenderListener#errorOccurred(java.lang.Exception)
466: */
467: public void errorOccurred(Exception e) {
468: if (e != null) {
469: e.printStackTrace();
470: if ((e.getMessage().toLowerCase().contains("timeout") || //$NON-NLS-1$
471: e.getMessage().toLowerCase().contains(
472: "time-out") || //$NON-NLS-1$
473: e.getMessage().toLowerCase().contains("timed out"))) { //$NON-NLS-1$
474: exception = new Exception(
475: Messages.BasicFeatureRenderer_request_timed_out);
476: if (getRenderer() != null)
477: getRenderer().stopRendering();
478: }
479: }
480: if (e instanceof IOException) {
481: if (getRenderer() != null)
482: getRenderer().stopRendering();
483: exception = e;
484: }
485:
486: if (exceptionCount > 500)
487: if (getRenderer() != null)
488: getRenderer().stopRendering();
489: exception = e;
490: exceptionCount++;
491: }
492:
493: /**
494: * Initialize listener
495: *
496: * @param monitor
497: */
498: public void init(IProgressMonitor monitor) {
499: lastUpdate = System.currentTimeMillis();
500: this .monitor = monitor;
501: exception = null;
502: exceptionCount = 0;
503: featureRendered = false;
504: count = 0;
505: }
506: }
507:
508: public void refreshImage() {
509: try {
510: render(ProgressManager.instance().get());
511: } catch (RenderException e) {
512: getContext().setStatus(ILayer.ERROR);
513: getContext().setStatusMessage(e.getLocalizedMessage());
514: }
515: }
516: }
|