Source Code Cross Referenced for StreamingRenderer.java in  » GIS » GeoTools-2.4.1 » org » geotools » renderer » lite » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.renderer.lite 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2004-2006, Geotools Project Managment Committee (PMC)
0005:         *
0006:         *    This library is free software; you can redistribute it and/or
0007:         *    modify it under the terms of the GNU Lesser General Public
0008:         *    License as published by the Free Software Foundation; either
0009:         *    version 2.1 of the License, or (at your option) any later version.
0010:         *
0011:         *    This library is distributed in the hope that it will be useful,
0012:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014:         *    Lesser General Public License for more details.
0015:         */
0016:        package org.geotools.renderer.lite;
0017:
0018:        import java.awt.Graphics2D;
0019:        import java.awt.Rectangle;
0020:        import java.awt.RenderingHints;
0021:        import java.awt.Shape;
0022:        import java.awt.Transparency;
0023:        import java.awt.font.GlyphVector;
0024:        import java.awt.geom.AffineTransform;
0025:        import java.awt.geom.NoninvertibleTransformException;
0026:        import java.awt.image.BufferedImage;
0027:        import java.io.IOException;
0028:        import java.text.NumberFormat;
0029:        import java.util.ArrayList;
0030:        import java.util.Arrays;
0031:        import java.util.Collection;
0032:        import java.util.HashMap;
0033:        import java.util.IdentityHashMap;
0034:        import java.util.Iterator;
0035:        import java.util.LinkedList;
0036:        import java.util.List;
0037:        import java.util.Map;
0038:        import java.util.logging.Level;
0039:        import java.util.logging.Logger;
0040:
0041:        import javax.imageio.ImageIO;
0042:        import javax.media.jai.Interpolation;
0043:        import javax.media.jai.InterpolationBicubic;
0044:        import javax.media.jai.InterpolationBilinear;
0045:        import javax.media.jai.InterpolationNearest;
0046:        import javax.media.jai.JAI;
0047:        import javax.media.jai.util.Range;
0048:
0049:        import org.geotools.coverage.grid.GeneralGridRange;
0050:        import org.geotools.coverage.grid.GridCoverage2D;
0051:        import org.geotools.coverage.grid.GridGeometry2D;
0052:        import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
0053:        import org.geotools.coverage.grid.io.AbstractGridFormat;
0054:        import org.geotools.data.DataUtilities;
0055:        import org.geotools.data.DefaultQuery;
0056:        import org.geotools.data.FeatureSource;
0057:        import org.geotools.data.Query;
0058:        import org.geotools.data.collection.ResourceCollection;
0059:        import org.geotools.data.crs.ForceCoordinateSystemFeatureResults;
0060:        import org.geotools.data.memory.CollectionSource;
0061:        import org.geotools.factory.CommonFactoryFinder;
0062:        import org.geotools.factory.Hints;
0063:        import org.geotools.feature.AttributeType;
0064:        import org.geotools.feature.Feature;
0065:        import org.geotools.feature.FeatureCollection;
0066:        import org.geotools.feature.FeatureType;
0067:        import org.geotools.feature.GeometryAttributeType;
0068:        import org.geotools.feature.IllegalAttributeException;
0069:        import org.geotools.filter.IllegalFilterException;
0070:        import org.geotools.geometry.GeneralEnvelope;
0071:        import org.geotools.geometry.jts.Decimator;
0072:        import org.geotools.geometry.jts.JTS;
0073:        import org.geotools.geometry.jts.LiteCoordinateSequenceFactory;
0074:        import org.geotools.geometry.jts.LiteShape2;
0075:        import org.geotools.geometry.jts.ReferencedEnvelope;
0076:        import org.geotools.map.MapContext;
0077:        import org.geotools.map.MapLayer;
0078:        import org.geotools.parameter.Parameter;
0079:        import org.geotools.referencing.CRS;
0080:        import org.geotools.referencing.operation.BufferedCoordinateOperationFactory;
0081:        import org.geotools.referencing.operation.matrix.XAffineTransform;
0082:        import org.geotools.referencing.operation.transform.ConcatenatedTransform;
0083:        import org.geotools.referencing.operation.transform.ProjectiveTransform;
0084:        import org.geotools.renderer.GTRenderer;
0085:        import org.geotools.renderer.RenderListener;
0086:        import org.geotools.renderer.lite.gridcoverage2d.GridCoverageRenderer;
0087:        import org.geotools.renderer.style.SLDStyleFactory;
0088:        import org.geotools.renderer.style.Style2D;
0089:        import org.geotools.styling.FeatureTypeStyle;
0090:        import org.geotools.styling.LineSymbolizer;
0091:        import org.geotools.styling.PointSymbolizer;
0092:        import org.geotools.styling.PolygonSymbolizer;
0093:        import org.geotools.styling.RasterSymbolizer;
0094:        import org.geotools.styling.Rule;
0095:        import org.geotools.styling.StyleAttributeExtractor;
0096:        import org.geotools.styling.Symbolizer;
0097:        import org.geotools.styling.TextSymbolizer;
0098:        import org.geotools.util.NumberRange;
0099:        import org.opengis.coverage.grid.GridCoverage;
0100:        import org.opengis.coverage.grid.GridCoverageReader;
0101:        import org.opengis.filter.Filter;
0102:        import org.opengis.filter.FilterFactory;
0103:        import org.opengis.filter.expression.PropertyName;
0104:        import org.opengis.filter.spatial.BBOX;
0105:        import org.opengis.parameter.GeneralParameterValue;
0106:        import org.opengis.referencing.FactoryException;
0107:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0108:        import org.opengis.referencing.operation.CoordinateOperation;
0109:        import org.opengis.referencing.operation.CoordinateOperationFactory;
0110:        import org.opengis.referencing.operation.MathTransform;
0111:        import org.opengis.referencing.operation.MathTransform2D;
0112:        import org.opengis.referencing.operation.OperationNotFoundException;
0113:        import org.opengis.referencing.operation.TransformException;
0114:
0115:        import com.vividsolutions.jts.geom.Coordinate;
0116:        import com.vividsolutions.jts.geom.Envelope;
0117:        import com.vividsolutions.jts.geom.Geometry;
0118:        import com.vividsolutions.jts.geom.GeometryCollection;
0119:
0120:        /**
0121:         * A streaming implementation of the GTRenderer interface.
0122:         * <ul>
0123:         * <li>The code is relatively simple to understand, so it can be used as a
0124:         * simple example of an SLD compliant rendering code</li>
0125:         * <li>Uses as little memory as possible</li>
0126:         * </ul>
0127:         * Use this class if you need a stateless renderer that provides low memory
0128:         * footprint and decent rendering performance on the first call but don't need
0129:         * good optimal performance on subsequent calls on the same data. Notice: for
0130:         * the time being, this class doesn't support GridCoverage stylers, that will be
0131:         * rendered using the non geophisics version of the GridCoverage, if available,
0132:         * with the geophisics one, otherwise.
0133:         * 
0134:         * <p>
0135:         * At the moment the streaming renderer is not thread safe
0136:         * 
0137:         * @author James Macgill
0138:         * @author dblasby
0139:         * @author jessie eichar
0140:         * @author Simone Giannecchini
0141:         * @author Andrea Aime
0142:         * @author Alessio Fabiani
0143:         * 
0144:         * @source $URL:
0145:         *         http://svn.geotools.org/geotools/trunk/gt/module/render/src/org/geotools/renderer/lite/StreamingRenderer.java $
0146:         * @version $Id: StreamingRenderer.java 29443 2008-02-25 10:04:01Z jgarnett $
0147:         */
0148:        public final class StreamingRenderer implements  GTRenderer {
0149:
0150:            private final static int defaultMaxFiltersToSendToDatastore = 5; // default
0151:
0152:            /**
0153:             * Computes the scale as the ratio between map distances and real world distances,
0154:             * assuming 90dpi and taking into consideration projection deformations and actual
0155:             * earth shape. <br>
0156:             * Use this method only when in need of accurate computation. Will break if the
0157:             * data extent is outside of the currenct projection definition area. 
0158:             */
0159:            public static final String SCALE_ACCURATE = "ACCURATE";
0160:
0161:            /**
0162:             * Very simple and lenient scale computation method that conforms to the OGC SLD 
0163:             * specification 1.0, page 26. <br>This method is quite approximative, but should
0164:             * never break and ensure constant scale even on lat/lon unprojected maps (because
0165:             * in that case scale is computed as if the area was along the equator no matter
0166:             * what the real position is).
0167:             */
0168:            public static final String SCALE_OGC = "OGC";
0169:
0170:            /** Tolerance used to compare doubles for equality */
0171:            private static final double TOLERANCE = 1e-6;
0172:
0173:            /** The logger for the rendering module. */
0174:            private static final Logger LOGGER = org.geotools.util.logging.Logging
0175:                    .getLogger("org.geotools.rendering");
0176:
0177:            int error = 0;
0178:
0179:            /** Filter factory for creating bounding box filters */
0180:            private final static FilterFactory filterFactory = CommonFactoryFinder
0181:                    .getFilterFactory(null);
0182:
0183:            private final static PropertyName gridPropertyName = filterFactory
0184:                    .property("grid");
0185:
0186:            private final static PropertyName paramsPropertyName = filterFactory
0187:                    .property("params");
0188:
0189:            private final static PropertyName defaultGeometryPropertyName = filterFactory
0190:                    .property("");
0191:
0192:            /**
0193:             * Context which contains the layers and the bouning box which needs to be
0194:             * rendered.
0195:             */
0196:            private MapContext context;
0197:
0198:            /**
0199:             * Flag which determines if the renderer is interactive or not. An
0200:             * interactive renderer will return rather than waiting for time consuming
0201:             * operations to complete (e.g. Image Loading). A non-interactive renderer
0202:             * (e.g. a SVG or PDF renderer) will block for these operations.
0203:             */
0204:            private boolean interactive = true;
0205:
0206:            /**
0207:             * Flag which controls behaviour for applying affine transformation to the
0208:             * graphics object. If true then the transform will be concatenated to the
0209:             * existing transform. If false it will be replaced.
0210:             */
0211:            private boolean concatTransforms = false;
0212:
0213:            /** Geographic map extent, eventually expanded to consider buffer area around the map */
0214:            private ReferencedEnvelope mapExtent;
0215:
0216:            /** Geographic map extent, as provided by the caller */
0217:            private ReferencedEnvelope originalMapExtent;
0218:
0219:            /** The size of the output area in output units. */
0220:            private Rectangle screenSize;
0221:
0222:            /**
0223:             * This flag is set to false when starting rendering, and will be checked
0224:             * during the rendering loop in order to make it stop forcefully
0225:             */
0226:            private boolean renderingStopRequested = false;
0227:
0228:            /**
0229:             * The ratio required to scale the features to be rendered so that they fit
0230:             * into the output space.
0231:             */
0232:            private double scaleDenominator;
0233:
0234:            /** Maximun displacement for generalization during rendering */
0235:            private double generalizationDistance = 1.0;
0236:
0237:            /** Factory that will resolve symbolizers into rendered styles */
0238:            private SLDStyleFactory styleFactory = new SLDStyleFactory();
0239:
0240:            protected LabelCache labelCache = new LabelCacheDefault();
0241:
0242:            /** The painter class we use to depict shapes onto the screen */
0243:            private StyledShapePainter painter = new StyledShapePainter(
0244:                    labelCache);
0245:
0246:            private IndexedFeatureResults indexedFeatureResults;
0247:
0248:            private ListenerList renderListeners = new ListenerList();
0249:
0250:            private RenderingHints java2dHints;
0251:
0252:            private boolean optimizedDataLoadingEnabledDEFAULT = false;
0253:
0254:            private boolean memoryPreloadingEnabledDEFAULT = false;
0255:
0256:            private int renderingBufferDEFAULT = 0;
0257:
0258:            private String scaleComputationMethodDEFAULT = SCALE_OGC;
0259:
0260:            /**
0261:             * Text will be rendered using the usual calls gc.drawString/drawGlyphVector.
0262:             * This is a little faster, and more consistent with how the platform renders
0263:             * the text in other applications. The downside is that on most platform the label
0264:             * and its eventual halo are not properly centered.
0265:             */
0266:            public static final String TEXT_RENDERING_STRING = "STRING";
0267:
0268:            /**
0269:             * Text will be rendered using the associated {@link GlyphVector} outline, that is, a {@link Shape}.
0270:             * This ensures perfect centering between the text and the halo, but introduces more text aliasing.
0271:             */
0272:            public static final String TEXT_RENDERING_OUTLINE = "OUTLINE";
0273:
0274:            /**
0275:             * The text rendering method, either TEXT_RENDERING_OUTLINE or TEXT_RENDERING_STRING
0276:             */
0277:            public static final String TEXT_RENDERING_KEY = "textRenderingMethod";
0278:            private String textRenderingModeDEFAULT = TEXT_RENDERING_STRING;
0279:
0280:            public static final String LABEL_CACHE_KEY = "labelCache";
0281:            public static final String FORCE_CRS_KEY = "forceCRS";
0282:            public static final String DPI_KEY = "dpi";
0283:            public static final String DECLARED_SCALE_DENOM_KEY = "declaredScaleDenominator";
0284:            public static final String MEMORY_PRE_LOADING_KEY = "memoryPreloadingEnabled";
0285:            public static final String OPTIMIZED_DATA_LOADING_KEY = "optimizedDataLoadingEnabled";
0286:            public static final String SCALE_COMPUTATION_METHOD_KEY = "scaleComputationMethod";
0287:
0288:            /**
0289:             * "optimizedDataLoadingEnabled" - Boolean  yes/no (see default optimizedDataLoadingEnabledDEFAULT)
0290:             * "memoryPreloadingEnabled"     - Boolean  yes/no (see default memoryPreloadingEnabledDEFAULT)
0291:             * "declaredScaleDenominator"    - Double   the value of the scale denominator to use by the renderer.  
0292:             *                                          by default the value is calculated based on the screen size 
0293:             *                                          and the displayed area of the map.
0294:             *  "dpi"                        - Integer  number of dots per inch of the display 90 DPI is the default (as declared by OGC)      
0295:             *  "forceCRS"                   - CoordinateReferenceSystem declares to the renderer that all layers are of the CRS declared in this hint                               
0296:             *  "labelCache"                 - Declares the label cache that will be used by the renderer.                               
0297:             */
0298:            private Map rendererHints = null;
0299:
0300:            private AffineTransform worldToScreenTransform = null;
0301:
0302:            private CoordinateReferenceSystem destinationCrs;
0303:
0304:            private boolean canTransform;
0305:
0306:            /**
0307:             * Creates a new instance of LiteRenderer without a context. Use it only to
0308:             * gain access to utility methods of this class or if you want to render
0309:             * random feature collections instead of using the map context interface
0310:             */
0311:            public StreamingRenderer() {
0312:
0313:            }
0314:
0315:            /**
0316:             * Sets the flag which controls behaviour for applying affine transformation
0317:             * to the graphics object.
0318:             * 
0319:             * @param flag
0320:             *            If true then the transform will be concatenated to the
0321:             *            existing transform. If false it will be replaced.
0322:             */
0323:            public void setConcatTransforms(boolean flag) {
0324:                concatTransforms = flag;
0325:            }
0326:
0327:            /**
0328:             * Flag which controls behaviour for applying affine transformation to the
0329:             * graphics object.
0330:             * 
0331:             * @return a boolean flag. If true then the transform will be concatenated
0332:             *         to the existing transform. If false it will be replaced.
0333:             */
0334:            public boolean getConcatTransforms() {
0335:                return concatTransforms;
0336:            }
0337:
0338:            /**
0339:             * adds a listener that responds to error events of feature rendered events.
0340:             * 
0341:             * @see RenderListener
0342:             * 
0343:             * @param listener
0344:             *            the listener to add.
0345:             */
0346:            public void addRenderListener(RenderListener listener) {
0347:                renderListeners.add(listener);
0348:            }
0349:
0350:            /**
0351:             * Removes a render listener.
0352:             * 
0353:             * @see RenderListener
0354:             * 
0355:             * @param listener
0356:             *            the listener to remove.
0357:             */
0358:            public void removeRenderListener(RenderListener listener) {
0359:                renderListeners.remove(listener);
0360:            }
0361:
0362:            private void fireFeatureRenderedEvent(Object feature) {
0363:                if (!(feature instanceof  Feature)) {
0364:                    return;
0365:                }
0366:                final Object[] objects = renderListeners.getListeners();
0367:                final int length = objects.length;
0368:                RenderListener listener;
0369:                for (int i = 0; i < length; i++) {
0370:                    listener = (RenderListener) objects[i];
0371:                    listener.featureRenderer((Feature) feature);
0372:                }
0373:            }
0374:
0375:            private void fireErrorEvent(Exception e) {
0376:                Object[] objects = renderListeners.getListeners();
0377:                final int length = objects.length;
0378:                RenderListener listener;
0379:                for (int i = 0; i < length; i++) {
0380:                    listener = (RenderListener) objects[i];
0381:                    listener.errorOccurred(e);
0382:                }
0383:            }
0384:
0385:            /**
0386:             * If you call this method from another thread than the one that called
0387:             * <code>paint</code> or <code>render</code> the rendering will be
0388:             * forcefully stopped before termination
0389:             */
0390:            public void stopRendering() {
0391:                renderingStopRequested = true;
0392:                labelCache.stop();
0393:            }
0394:
0395:            /**
0396:             * Renders features based on the map layers and their styles as specified in
0397:             * the map context using <code>setContext</code>. <p/> This version of
0398:             * the method assumes that the size of the output area and the
0399:             * transformation from coordinates to pixels are known. The latter
0400:             * determines the map scale. The viewport (the visible part of the map) will
0401:             * be calculated internally.
0402:             * 
0403:             * @param graphics
0404:             *            The graphics object to draw to.
0405:             * @param paintArea
0406:             *            The size of the output area in output units (eg: pixels).
0407:             * @param worldToScreen
0408:             *            A transform which converts World coordinates to Screen
0409:             *            coordinates.
0410:             * @task Need to check if the Layer CoordinateSystem is different to the
0411:             *       BoundingBox rendering CoordinateSystem and if so, then transform
0412:             *       the coordinates.
0413:             * @deprecated Use paint(Graphics2D graphics, Rectangle paintArea,
0414:             *             ReferencedEnvelope mapArea) or paint(Graphics2D graphics,
0415:             *             Rectangle paintArea, ReferencedEnvelope mapArea,
0416:             *             AffineTransform worldToScreen) instead.
0417:             */
0418:            public void paint(Graphics2D graphics, Rectangle paintArea,
0419:                    AffineTransform worldToScreen) {
0420:                if (worldToScreen == null || paintArea == null) {
0421:                    LOGGER.info("renderer passed null arguments");
0422:                    return;
0423:                } // Other arguments get checked later
0424:                // First, create the bbox in real world coordinates
0425:                Envelope mapArea;
0426:                try {
0427:                    mapArea = RendererUtilities.createMapEnvelope(paintArea,
0428:                            worldToScreen);
0429:                    paint(graphics, paintArea, mapArea, worldToScreen);
0430:                } catch (NoninvertibleTransformException e) {
0431:                    LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
0432:                    fireErrorEvent(new Exception(
0433:                            "Can't create pixel to world transform", e));
0434:                }
0435:            }
0436:
0437:            /**
0438:             * Renders features based on the map layers and their styles as specified in
0439:             * the map context using <code>setContext</code>. <p/> This version of
0440:             * the method assumes that the area of the visible part of the map and the
0441:             * size of the output area are known. The transform between the two is
0442:             * calculated internally.
0443:             * 
0444:             * @param graphics
0445:             *            The graphics object to draw to.
0446:             * @param paintArea
0447:             *            The size of the output area in output units (eg: pixels).
0448:             * @param mapArea
0449:             *            the map's visible area (viewport) in map coordinates.
0450:             * @deprecated Use paint(Graphics2D graphics, Rectangle paintArea,
0451:             *             ReferencedEnvelope mapArea) or paint(Graphics2D graphics,
0452:             *             Rectangle paintArea, ReferencedEnvelope mapArea,
0453:             *             AffineTransform worldToScreen) instead.
0454:             */
0455:            public void paint(Graphics2D graphics, Rectangle paintArea,
0456:                    Envelope mapArea) {
0457:                if (mapArea == null || paintArea == null) {
0458:                    LOGGER.info("renderer passed null arguments");
0459:                    return;
0460:                } // Other arguments get checked later
0461:                paint(graphics, paintArea, mapArea, RendererUtilities
0462:                        .worldToScreenTransform(mapArea, paintArea));
0463:            }
0464:
0465:            /**
0466:             * Renders features based on the map layers and their styles as specified in
0467:             * the map context using <code>setContext</code>. <p/> This version of
0468:             * the method assumes that the area of the visible part of the map and the
0469:             * size of the output area are known. The transform between the two is
0470:             * calculated internally.
0471:             * 
0472:             * @param graphics
0473:             *            The graphics object to draw to.
0474:             * @param paintArea
0475:             *            The size of the output area in output units (eg: pixels).
0476:             * @param mapArea
0477:             *            the map's visible area (viewport) in map coordinates.
0478:             */
0479:            public void paint(Graphics2D graphics, Rectangle paintArea,
0480:                    ReferencedEnvelope mapArea) {
0481:                if (mapArea == null || paintArea == null) {
0482:                    LOGGER.info("renderer passed null arguments");
0483:                    return;
0484:                } // Other arguments get checked later
0485:                paint(graphics, paintArea, mapArea, RendererUtilities
0486:                        .worldToScreenTransform(mapArea, paintArea));
0487:            }
0488:
0489:            /**
0490:             * Renders features based on the map layers and their styles as specified in
0491:             * the map context using <code>setContext</code>. <p/> This version of
0492:             * the method assumes that paint area, enelope and worldToScreen transform
0493:             * are already computed. Use this method to avoid recomputation. <b>Note
0494:             * however that no check is performed that they are really in sync!<b/>
0495:             * 
0496:             * @param graphics
0497:             *            The graphics object to draw to.
0498:             * @param paintArea
0499:             *            The size of the output area in output units (eg: pixels).
0500:             * @param mapArea
0501:             *            the map's visible area (viewport) in map coordinates.
0502:             * @param worldToScreen
0503:             *            A transform which converts World coordinates to Screen
0504:             *            coordinates.
0505:             * @deprecated Use paint(Graphics2D graphics, Rectangle paintArea,
0506:             *             ReferencedEnvelope mapArea) or paint(Graphics2D graphics,
0507:             *             Rectangle paintArea, ReferencedEnvelope mapArea,
0508:             *             AffineTransform worldToScreen) instead.
0509:             */
0510:            public void paint(Graphics2D graphics, Rectangle paintArea,
0511:                    Envelope mapArea, AffineTransform worldToScreen) {
0512:                paint(graphics, paintArea, new ReferencedEnvelope(mapArea,
0513:                        context.getCoordinateReferenceSystem()), worldToScreen);
0514:            }
0515:
0516:            private double computeScale(ReferencedEnvelope envelope,
0517:                    Rectangle paintArea, Map hints) {
0518:                if (getScaleComputationMethod().equals(SCALE_ACCURATE)) {
0519:                    try {
0520:                        return RendererUtilities.calculateScale(envelope,
0521:                                paintArea.width, paintArea.height, hints);
0522:                    } catch (Exception e) // probably either (1) no CRS (2) error xforming
0523:                    {
0524:                        LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
0525:                    }
0526:                }
0527:                return RendererUtilities.calculateOGCScale(envelope,
0528:                        paintArea.width, hints);
0529:            }
0530:
0531:            /**
0532:             * Renders features based on the map layers and their styles as specified in
0533:             * the map context using <code>setContext</code>. <p/> This version of
0534:             * the method assumes that paint area, enelope and worldToScreen transform
0535:             * are already computed. Use this method to avoid recomputation. <b>Note
0536:             * however that no check is performed that they are really in sync!<b/>
0537:             * 
0538:             * @param graphics
0539:             *            The graphics object to draw to.
0540:             * @param paintArea
0541:             *            The size of the output area in output units (eg: pixels).
0542:             * @param mapArea
0543:             *            the map's visible area (viewport) in map coordinates. Its
0544:             *            associate CRS is ALWAYS 2D
0545:             * @param worldToScreen
0546:             *            A transform which converts World coordinates to Screen
0547:             *            coordinates.
0548:             */
0549:            public void paint(Graphics2D graphics, Rectangle paintArea,
0550:                    ReferencedEnvelope mapArea, AffineTransform worldToScreen) {
0551:                // ////////////////////////////////////////////////////////////////////
0552:                // 
0553:                // Check for null arguments, recompute missing ones if possible
0554:                //
0555:                // ////////////////////////////////////////////////////////////////////
0556:                if (graphics == null || paintArea == null) {
0557:                    LOGGER.severe("renderer passed null arguments");
0558:                    throw new NullPointerException(
0559:                            "renderer passed null arguments");
0560:                } else if (mapArea == null && paintArea == null) {
0561:                    LOGGER.severe("renderer passed null arguments");
0562:                    throw new NullPointerException(
0563:                            "renderer passed null arguments");
0564:                } else if (mapArea == null) {
0565:
0566:                    LOGGER.severe("renderer passed null arguments");
0567:                    throw new NullPointerException(
0568:                            "renderer passed null arguments");
0569:                } else if (worldToScreen == null) {
0570:                    worldToScreen = RendererUtilities.worldToScreenTransform(
0571:                            mapArea, paintArea);
0572:                    if (worldToScreen == null)
0573:                        return;
0574:                }
0575:
0576:                // ////////////////////////////////////////////////////////////////////
0577:                // 
0578:                // Setting base information
0579:                //
0580:                // TODO the way this thing is built is a mess if you try to use it in a
0581:                // multithreaded environment. I will fix this at the end.
0582:                //
0583:                // ////////////////////////////////////////////////////////////////////
0584:                destinationCrs = mapArea.getCoordinateReferenceSystem();
0585:                mapExtent = new ReferencedEnvelope(mapArea);
0586:                this .screenSize = paintArea;
0587:                this .worldToScreenTransform = worldToScreen;
0588:                error = 0;
0589:                if (java2dHints != null)
0590:                    graphics.setRenderingHints(java2dHints);
0591:                // reset the abort flag
0592:                renderingStopRequested = false;
0593:
0594:                // ////////////////////////////////////////////////////////////////////
0595:                //
0596:                // Managing transformations , CRSs and scales
0597:                //
0598:                // If we are rendering to a component which has already set up some form
0599:                // of transformation then we can concatenate our transformation to it.
0600:                // An example of this is the ZoomPane component of the swinggui module.
0601:                // ////////////////////////////////////////////////////////////////////
0602:                if (concatTransforms) {
0603:                    AffineTransform atg = graphics.getTransform();
0604:                    atg.concatenate(worldToScreenTransform);
0605:                    worldToScreenTransform = atg;
0606:                    graphics.setTransform(worldToScreenTransform);
0607:                }
0608:
0609:                // compute scale according to the user specified method
0610:                scaleDenominator = computeScale(mapArea, paintArea,
0611:                        rendererHints);
0612:
0613:                //////////////////////////////////////////////////////////////////////
0614:                //
0615:                // Consider expanding the map extent so that a few more geometries
0616:                // will be considered, in order to catch those outside of the rendering
0617:                // bounds whose stroke is so thick that it countributes rendered area
0618:                //
0619:                //////////////////////////////////////////////////////////////////////
0620:                int buffer = getRenderingBuffer();
0621:                originalMapExtent = mapExtent;
0622:                if (buffer > 0) {
0623:                    mapExtent = new ReferencedEnvelope(expandEnvelope(
0624:                            mapExtent, worldToScreen, buffer), mapExtent
0625:                            .getCoordinateReferenceSystem());
0626:                }
0627:
0628:                // ////////////////////////////////////////////////////////////////////
0629:                //
0630:                // Processing all the map layers in the context using the accompaining
0631:                // styles
0632:                //
0633:                // ////////////////////////////////////////////////////////////////////
0634:                final MapLayer[] layers = context.getLayers();
0635:                labelCache.start();
0636:                if (labelCache instanceof  LabelCacheDefault) {
0637:                    boolean outlineEnabled = TEXT_RENDERING_OUTLINE
0638:                            .equals(getTextRenderingMethod());
0639:                    ((LabelCacheDefault) labelCache)
0640:                            .setOutlineRenderingEnabled(outlineEnabled);
0641:                }
0642:                final int layersNumber = layers.length;
0643:                MapLayer currLayer;
0644:                for (int i = 0; i < layersNumber; i++) // DJB: for each layer (ie. one
0645:                {
0646:                    currLayer = layers[i];
0647:
0648:                    if (!currLayer.isVisible()) {
0649:                        // Only render layer when layer is visible
0650:                        continue;
0651:                    }
0652:
0653:                    if (renderingStopRequested) {
0654:                        return;
0655:                    }
0656:                    labelCache.startLayer(i + "");
0657:                    try {
0658:
0659:                        // extract the feature type stylers from the style object
0660:                        // and process them
0661:                        processStylers(graphics, currLayer,
0662:                                worldToScreenTransform, destinationCrs,
0663:                                mapExtent, screenSize, i + "");
0664:                    } catch (Throwable t) {
0665:                        LOGGER.log(Level.SEVERE, t.getLocalizedMessage(), t);
0666:                        fireErrorEvent(new Exception(new StringBuffer(
0667:                                "Exception rendering layer ").append(currLayer)
0668:                                .toString(), t));
0669:                    }
0670:
0671:                    labelCache.endLayer(i + "", graphics, screenSize);
0672:                }
0673:
0674:                labelCache.end(graphics, paintArea);
0675:
0676:                if (LOGGER.isLoggable(Level.FINE))
0677:                    LOGGER.fine(new StringBuffer("Style cache hit ratio: ")
0678:                            .append(styleFactory.getHitRatio()).append(
0679:                                    " , hits ").append(styleFactory.getHits())
0680:                            .append(", requests ").append(
0681:                                    styleFactory.getRequests()).toString());
0682:                if (error > 0) {
0683:                    LOGGER
0684:                            .warning(new StringBuffer(
0685:                                    "Number of Errors during paint(Graphics2D, AffineTransform) = ")
0686:                                    .append(error).toString());
0687:                }
0688:            }
0689:
0690:            /**
0691:             * Extends the provided {@link Envelope} in order to add the number of pixels
0692:             * specified by <code>buffer</code> in every direction.
0693:             * 
0694:             * @param envelope to extend.
0695:             * @param worldToScreen by means  of which doing the extension.
0696:             * @param buffer to use for the extension.
0697:             * @return an extende version of the provided {@link Envelope}.
0698:             */
0699:            private Envelope expandEnvelope(Envelope envelope,
0700:                    AffineTransform worldToScreen, int buffer) {
0701:                assert buffer > 0;
0702:                double bufferX = Math.abs(buffer * 1.0
0703:                        / XAffineTransform.getScaleX0(worldToScreen));
0704:                double bufferY = Math.abs(buffer * 1.0
0705:                        / XAffineTransform.getScaleY0(worldToScreen));
0706:                return new Envelope(envelope.getMinX() - bufferX, envelope
0707:                        .getMaxX()
0708:                        + bufferX, envelope.getMinY() - bufferY, envelope
0709:                        .getMaxY()
0710:                        + bufferY);
0711:            }
0712:
0713:            /**
0714:             * Queries a given layer's <code>Source</code> instance to be rendered. 
0715:             * <p>
0716:             * <em><strong>Note: This is proof-of-concept quality only!</strong> At 
0717:             * the moment the query is not filtered, that means all objects with all 
0718:             * fields are read from the datastore for every call to this method. This 
0719:             * method should work like 
0720:             * {@link #queryLayer(MapLayer, FeatureSource, FeatureType, LiteFeatureTypeStyle[], Envelope, CoordinateReferenceSystem, CoordinateReferenceSystem, Rectangle, GeometryAttributeType)} 
0721:             * and eventually replace it.</em>
0722:             * </p>
0723:             * 
0724:             * @param currLayer The actually processed layer for rendering
0725:             * @param source Source to read data from
0726:             */
0727:            //TODO: Implement filtering for bbox and read in only the need attributes 
0728:            Collection queryLayer(MapLayer currLayer, CollectionSource source) {
0729:
0730:                Collection results = null;
0731:                DefaultQuery query = new DefaultQuery(DefaultQuery.ALL);
0732:                Query definitionQuery;
0733:
0734:                definitionQuery = currLayer.getQuery();
0735:
0736:                if (definitionQuery != Query.ALL) {
0737:                    if (query == Query.ALL) {
0738:                        query = new DefaultQuery(definitionQuery);
0739:                    } else {
0740:                        query = new DefaultQuery(DataUtilities.mixQueries(
0741:                                definitionQuery, query, "liteRenderer"));
0742:                    }
0743:                }
0744:
0745:                results = source.content(query.getFilter());
0746:
0747:                return results;
0748:            }
0749:
0750:            /**
0751:             * Queries a given layer's features to be rendered based on the target
0752:             * rendering bounding box.
0753:             * <p>
0754:             * If <code>optimizedDataLoadingEnabled</code> attribute has been set to
0755:             * <code>true</code>, the following optimization will be performed in
0756:             * order to limit the number of features returned:
0757:             * <ul>
0758:             * <li>Just the features whose geometric attributes lies within
0759:             * <code>envelope</code> will be queried</li>
0760:             * <li>The queried attributes will be limited to just those needed to
0761:             * perform the rendering, based on the requiered geometric and non geometric
0762:             * attributes found in the Layer's style rules</li>
0763:             * <li>If a <code>Query</code> has been set to limit the resulting
0764:             * layer's features, the final filter to obtain them will respect it. This
0765:             * means that the bounding box filter and the Query filter will be combined,
0766:             * also including maxFeatures from Query</li>
0767:             * <li>At least that the layer's definition query explicitly says to
0768:             * retrieve some attribute, no attributes will be requested from it, for
0769:             * performance reassons. So it is desirable to not use a Query for filtering
0770:             * a layer wich includes attributes. Note that including the attributes in
0771:             * the result is not necessary for the query's filter to get properly
0772:             * processed. </li>
0773:             * </ul>
0774:             * </p>
0775:             * <p>
0776:             * <b>NOTE </b>: This is an internal method and should only be called by
0777:             * <code>paint(Graphics2D, Rectangle, AffineTransform)</code>. It is
0778:             * package protected just to allow unit testing it.
0779:             * </p>
0780:             * 
0781:             * @param currLayer
0782:             *            the actually processing layer for renderition
0783:             * @param schema
0784:             * @param source
0785:             * @param envelope
0786:             *            the spatial extent wich is the target area fo the rendering
0787:             *            process
0788:             * @param destinationCrs
0789:             *            DOCUMENT ME!
0790:             * @param sourceCrs
0791:             * @param screenSize
0792:             * @param geometryAttribute
0793:             * @return the set of features resulting from <code>currLayer</code> after
0794:             *         quering its feature source
0795:             * @throws IllegalFilterException
0796:             *             if something goes wrong constructing the bbox filter
0797:             * @throws IOException
0798:             * @throws IllegalAttributeException
0799:             * @see MapLayer#setQuery(org.geotools.data.Query)
0800:             */
0801:            /*
0802:             * Default visibility for testing purposes
0803:             */
0804:
0805:            FeatureCollection queryLayer(MapLayer currLayer,
0806:                    FeatureSource source, FeatureType schema,
0807:                    LiteFeatureTypeStyle[] styles, Envelope mapArea,
0808:                    CoordinateReferenceSystem mapCRS,
0809:                    CoordinateReferenceSystem featCrs, Rectangle screenSize,
0810:                    GeometryAttributeType geometryAttribute,
0811:                    AffineTransform worldToScreenTransform)
0812:                    throws IllegalFilterException, IOException,
0813:                    IllegalAttributeException {
0814:                FeatureCollection results = null;
0815:                DefaultQuery query = new DefaultQuery(DefaultQuery.ALL);
0816:                Query definitionQuery;
0817:                String[] attributes;
0818:                AttributeType[] ats;
0819:                final int length;
0820:                Filter filter = null;
0821:
0822:                // if map extent are not already expanded by a constant buffer, try to compute a layer
0823:                // specific one based on stroke widths
0824:                if (getRenderingBuffer() == 0) {
0825:                    int buffer = findRenderingBuffer(styles);
0826:                    if (buffer > 0) {
0827:                        mapArea = expandEnvelope(mapArea,
0828:                                worldToScreenTransform, buffer);
0829:                        LOGGER.fine("Expanding rendering area by " + buffer
0830:                                + " pixels to consider stroke width");
0831:                    }
0832:                }
0833:                ReferencedEnvelope envelope = new ReferencedEnvelope(mapArea,
0834:                        mapCRS);
0835:                if (isOptimizedDataLoadingEnabled()) {
0836:                    // see what attributes we really need by exploring the styles
0837:                    // for testing purposes we have a null case -->
0838:
0839:                    if (styles == null) {
0840:                        ats = schema.getAttributeTypes();
0841:                        length = ats.length;
0842:                        attributes = new String[length];
0843:                        for (int t = 0; t < length; t++) {
0844:                            attributes[t] = ats[t].getName();
0845:                        }
0846:                    } else {
0847:                        attributes = findStyleAttributes(styles, schema);
0848:                    }
0849:
0850:                    try {
0851:                        // Then create the geometry filters. We have to create one for
0852:                        // each geometric attribute used during the rendering as the
0853:                        // feature may have more than one and the styles could use non
0854:                        // default geometric ones
0855:                        if (mapCRS != null && featCrs != null
0856:                                && !CRS.equalsIgnoreMetadata(featCrs, mapCRS)) {
0857:                            envelope = envelope.transform(featCrs, true, 10);
0858:                        }
0859:
0860:                        if (!isMemoryPreloadingEnabled()) {
0861:                            if (LOGGER.isLoggable(Level.FINE))
0862:                                LOGGER.fine("Querying layer "
0863:                                        + schema.getTypeName() + " with bbox: "
0864:                                        + envelope);
0865:                            filter = createBBoxFilters(schema, attributes,
0866:                                    envelope);
0867:                        } else {
0868:                            filter = Filter.INCLUDE;
0869:                        }
0870:
0871:                        // now build the query using only the attributes and the
0872:                        // bounding box needed
0873:                        query = new DefaultQuery(schema.getTypeName());
0874:                        query.setFilter(filter);
0875:                        query.setPropertyNames(attributes);
0876:                        processRuleForQuery(styles, query);
0877:
0878:                    } catch (Exception e) {
0879:                        fireErrorEvent(new Exception("Error transforming bbox",
0880:                                e));
0881:                        canTransform = false;
0882:                        query = new DefaultQuery(schema.getTypeName());
0883:                        query.setPropertyNames(attributes);
0884:                        Envelope bounds = source.getBounds();
0885:                        if (bounds != null && envelope.intersects(bounds)) {
0886:                            LOGGER
0887:                                    .fine(new StringBuffer(
0888:                                            "Got a tranform exception while trying to de-project the current ")
0889:                                            .append(
0890:                                                    "envelope, bboxs intersect therefore using envelope)")
0891:                                            .toString());
0892:                            filter = null;
0893:                            filter = createBBoxFilters(schema, attributes,
0894:                                    envelope);
0895:                            query.setFilter(filter);
0896:                        } else {
0897:                            LOGGER
0898:                                    .fine(new StringBuffer(
0899:                                            "Got a tranform exception while trying to de-project the current ")
0900:                                            .append(
0901:                                                    "envelope, falling back on full data loading (no bbox query)")
0902:                                            .toString());
0903:                            query.setFilter(Filter.INCLUDE);
0904:                        }
0905:                        processRuleForQuery(styles, query);
0906:
0907:                    }
0908:                }
0909:
0910:                // now, if a definition query has been established for this layer, be
0911:                // sure to respect it by combining it with the bounding box one.
0912:                definitionQuery = currLayer.getQuery();
0913:
0914:                if (definitionQuery != Query.ALL) {
0915:                    if (query == Query.ALL) {
0916:                        query = new DefaultQuery(definitionQuery);
0917:                    } else {
0918:                        query = new DefaultQuery(DataUtilities.mixQueries(
0919:                                definitionQuery, query, "liteRenderer"));
0920:                    }
0921:                }
0922:                query.setCoordinateSystem(featCrs);
0923:                Hints hints = new Hints(Hints.JTS_COORDINATE_SEQUENCE_FACTORY,
0924:                        new LiteCoordinateSequenceFactory(), Hints.FEATURE_2D,
0925:                        Boolean.TRUE);
0926:                query.setHints(hints);
0927:
0928:                if (isMemoryPreloadingEnabled()) {
0929:                    // TODO: attache a feature listener, we must erase the memory cache
0930:                    // if
0931:                    // anything changes in the data store
0932:                    if (indexedFeatureResults == null) {
0933:                        indexedFeatureResults = new IndexedFeatureResults(
0934:                                source.getFeatures(query));
0935:                    }
0936:                    indexedFeatureResults.setQueryBounds(envelope);
0937:                    results = indexedFeatureResults;
0938:                } else { // insert a debug point here to check your query
0939:                    results = source.getFeatures(query);
0940:                }
0941:
0942:                // commenting this out for now, since it's causing connections to be
0943:                // left open, since it's making a transaction that is never committed.
0944:                // I think perhaps not getting FIDs should be set in client software
0945:                // anyways See GEOS-631 and related issues. -ch
0946:                /*
0947:                 * if ((source instanceof FeatureStore) && (doesntHaveFIDFilter(query))) {
0948:                 * try { FeatureStore fs = (FeatureStore) source;
0949:                 * 
0950:                 * if (fs.getTransaction() == Transaction.AUTO_COMMIT) { // play it
0951:                 * safe, only update the transaction info if its an // auto_commit // it
0952:                 * logically possible that someone could be using the // Transaction to
0953:                 * do future (or past) processing. // We dont want to affect a future
0954:                 * Query // thats not possible with an AUTO_COMMIT so its safe.
0955:                 * Transaction t = new DefaultTransaction();
0956:                 * t.putProperty("doNotGetFIDS", Boolean.TRUE); fs.setTransaction(t); } }
0957:                 * catch (Exception e) { if (LOGGER.isLoggable(Level.WARNING))
0958:                 * LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); // we // can
0959:                 * carry on, but report to // user } }
0960:                 */
0961:                return results;
0962:            }
0963:
0964:            /**
0965:             * JE: If there is a single rule "and" its filter together with the query's
0966:             * filter and send it off to datastore. This will allow as more processing
0967:             * to be done on the back end... Very useful if DataStore is a database.
0968:             * Problem is that worst case each filter is ran twice. Next we will modify
0969:             * it to find a "Common" filter between all rules and send that to the
0970:             * datastore.
0971:             * 
0972:             * DJB: trying to be smarter. If there are no "elseRules" and no rules w/o a
0973:             * filter, then it makes sense to send them off to the Datastore We limit
0974:             * the number of Filters sent off to the datastore, just because it could
0975:             * get a bit rediculous. In general, for a database, if you can limit 10% of
0976:             * the rows being returned you're probably doing quite well. The main
0977:             * problem is when your filters really mean you're secretly asking for all
0978:             * the data in which case sending the filters to the Datastore actually
0979:             * costs you. But, databases are *much* faster at processing the Filters
0980:             * than JAVA is and can use statistical analysis to do it.
0981:             * 
0982:             * @param styles
0983:             * @param q
0984:             */
0985:
0986:            private void processRuleForQuery(LiteFeatureTypeStyle[] styles,
0987:                    DefaultQuery q) {
0988:                try {
0989:
0990:                    // first we check to see if there are >
0991:                    // "getMaxFiltersToSendToDatastore" rules
0992:                    // if so, then we dont do anything since no matter what there's too
0993:                    // many to send down.
0994:                    // next we check for any else rules. If we find any --> dont send
0995:                    // anything to Datastore
0996:                    // next we check for rules w/o filters. If we find any --> dont send
0997:                    // anything to Datastore
0998:                    //
0999:                    // otherwise, we're gold and can "or" together all the fiters then
1000:                    // AND it with the original filter.
1001:                    // ie. SELECT * FROM ... WHERE (the_geom && BBOX) AND (filter1 OR
1002:                    // filter2 OR filter3);
1003:
1004:                    final int maxFilters = getMaxFiltersToSendToDatastore();
1005:                    final ArrayList filtersToDS = new ArrayList();
1006:                    final int actualFilters = 0;
1007:                    final int stylesLength = styles.length;
1008:                    int styleElseRulesLength;
1009:                    int styleRulesLength;
1010:                    LiteFeatureTypeStyle style;
1011:                    int u = 0;
1012:                    Rule r;
1013:                    if (stylesLength > maxFilters) // there's at least one per
1014:                        return;
1015:                    for (int t = 0; t < stylesLength; t++) // look at each
1016:                    // featuretypestyle
1017:                    {
1018:                        style = styles[t];
1019:                        styleElseRulesLength = style.elseRules.length;
1020:                        styleRulesLength = style.ruleList.length;
1021:                        if (styleElseRulesLength > 0) // uh-oh has elseRule
1022:                            return;
1023:                        for (u = 0; u < styleRulesLength; u++) // look at each
1024:                        // rule in the
1025:                        // featuretypestyle
1026:                        {
1027:                            r = style.ruleList[u];
1028:                            if (r.getFilter() == null)
1029:                                return; // uh-oh has no filter (want all rows)
1030:                            filtersToDS.add(r.getFilter());
1031:                        }
1032:                    }
1033:                    if (actualFilters > maxFilters)
1034:                        return;
1035:
1036:                    org.opengis.filter.Filter ruleFiltersCombined;
1037:                    Filter newFilter;
1038:                    // We're GOLD -- OR together all the Rule's Filters
1039:                    if (filtersToDS.size() == 1) // special case of 1 filter
1040:                    {
1041:                        ruleFiltersCombined = (Filter) filtersToDS.get(0);
1042:                    } else {
1043:                        // build it up
1044:                        ruleFiltersCombined = (Filter) filtersToDS.get(0);
1045:                        final int size = filtersToDS.size();
1046:                        for (int t = 1; t < size; t++) // NOTE: dont
1047:                        // redo 1st one
1048:                        {
1049:                            newFilter = (Filter) filtersToDS.get(t);
1050:                            ruleFiltersCombined = filterFactory.or(
1051:                                    ruleFiltersCombined, newFilter);
1052:                        }
1053:                    }
1054:                    // combine with the geometry filter (preexisting)
1055:                    ruleFiltersCombined = filterFactory.and(q.getFilter(),
1056:                            ruleFiltersCombined);
1057:
1058:                    // set the actual filter
1059:                    q.setFilter(ruleFiltersCombined);
1060:                } catch (Exception e) {
1061:                    if (LOGGER.isLoggable(Level.WARNING))
1062:                        LOGGER.log(Level.SEVERE,
1063:                                "Could not send rules to datastore due to: "
1064:                                        + e.getLocalizedMessage(), e);
1065:                }
1066:            }
1067:
1068:            /**
1069:             * find out the maximum number of filters we're going to send off to the
1070:             * datastore. See processRuleForQuery() for details.
1071:             * 
1072:             */
1073:            private int getMaxFiltersToSendToDatastore() {
1074:                try {
1075:                    Integer result = (Integer) rendererHints
1076:                            .get("maxFiltersToSendToDatastore");
1077:                    if (result == null)
1078:                        return defaultMaxFiltersToSendToDatastore; // default if not
1079:                    // present in hints
1080:                    return result.intValue();
1081:
1082:                } catch (Exception e) {
1083:                    return defaultMaxFiltersToSendToDatastore;
1084:                }
1085:            }
1086:
1087:            /**
1088:             */
1089:            private boolean isMemoryPreloadingEnabled() {
1090:                if (rendererHints == null)
1091:                    return memoryPreloadingEnabledDEFAULT;
1092:                Object result = null;
1093:                try {
1094:                    result = rendererHints.get("memoryPreloadingEnabled");
1095:                } catch (ClassCastException e) {
1096:
1097:                }
1098:                if (result == null)
1099:                    return memoryPreloadingEnabledDEFAULT;
1100:                return ((Boolean) result).booleanValue();
1101:            }
1102:
1103:            /**
1104:             * Returns an estimate of the rendering buffer needed to properly display this
1105:             * layer taking into consideration the constant stroke sizes in the feature type
1106:             * styles.
1107:             * 
1108:             * @param styles
1109:             *            the feature type styles to be applied to the layer
1110:             * @return an estimate of the buffer that should be used to properly display a layer
1111:             *         rendered with the specified styles 
1112:             */
1113:            private int findRenderingBuffer(LiteFeatureTypeStyle[] styles) {
1114:                final MetaBufferEstimator rbe = new MetaBufferEstimator();
1115:
1116:                for (int t = 0; t < styles.length; t++) {
1117:                    final LiteFeatureTypeStyle lfts = styles[t];
1118:                    Rule[] rules = lfts.elseRules;
1119:                    for (int j = 0; j < rules.length; j++) {
1120:                        rbe.visit(rules[j]);
1121:                    }
1122:                    rules = lfts.ruleList;
1123:                    for (int j = 0; j < rules.length; j++) {
1124:                        rbe.visit(rules[j]);
1125:                    }
1126:                }
1127:
1128:                if (!rbe.isEstimateAccurate())
1129:                    LOGGER
1130:                            .warning("Assuming rendering buffer = "
1131:                                    + rbe.getBuffer()
1132:                                    + ", but estimation is not accurate, you may want to set a buffer manually");
1133:                return rbe.getBuffer();
1134:            }
1135:
1136:            /**
1137:             * Inspects the <code>MapLayer</code>'s style and retrieves it's needed
1138:             * attribute names, returning at least the default geometry attribute name.
1139:             * 
1140:             * @param layer
1141:             *            the <code>MapLayer</code> to determine the needed attributes
1142:             *            from
1143:             * @param schema
1144:             *            the <code>layer</code>'s featuresource schema
1145:             * @return the minimun set of attribute names needed to render
1146:             *         <code>layer</code>
1147:             */
1148:            private String[] findStyleAttributes(LiteFeatureTypeStyle[] styles,
1149:                    FeatureType schema) {
1150:                final StyleAttributeExtractor sae = new StyleAttributeExtractor();
1151:
1152:                LiteFeatureTypeStyle lfts;
1153:                Rule[] rules;
1154:                int rulesLength;
1155:                final int length = styles.length;
1156:                for (int t = 0; t < length; t++) {
1157:                    lfts = styles[t];
1158:                    rules = lfts.elseRules;
1159:                    rulesLength = rules.length;
1160:                    for (int j = 0; j < rulesLength; j++) {
1161:                        sae.visit(rules[j]);
1162:                    }
1163:                    rules = lfts.ruleList;
1164:                    rulesLength = rules.length;
1165:                    for (int j = 0; j < rulesLength; j++) {
1166:                        sae.visit(rules[j]);
1167:                    }
1168:                }
1169:
1170:                String[] ftsAttributes = sae.getAttributeNames();
1171:
1172:                /*
1173:                 * DJB: this is an old comment - erase it soon (see geos-469 and below) -
1174:                 * we only add the default geometry if it was used.
1175:                 * 
1176:                 * GR: if as result of sae.getAttributeNames() ftsAttributes already
1177:                 * contains geometry attribue names, they gets duplicated, wich produces
1178:                 * an error in AbstracDatastore when trying to create a derivate
1179:                 * FeatureType. So I'll add the default geometry only if it is not
1180:                 * already present, but: should all the geometric attributes be added by
1181:                 * default? I will add them, but don't really know what's the expected
1182:                 * behavior
1183:                 */
1184:                List atts = new LinkedList(Arrays.asList(ftsAttributes));
1185:                AttributeType[] attTypes = schema.getAttributeTypes();
1186:                String attName;
1187:
1188:                final int attTypesLength = attTypes.length;
1189:                for (int i = 0; i < attTypesLength; i++) {
1190:                    attName = attTypes[i].getName();
1191:
1192:                    // DJB: This geometry check was commented out. I think it should
1193:                    // actually be back in or
1194:                    // you get ALL the attributes back, which isnt what you want.
1195:                    // ALX: For rasters I need even the "grid" attribute.
1196:
1197:                    // DJB:geos-469, we do not grab all the geometry columns.
1198:                    // for symbolizers, if a geometry is required it is either
1199:                    // explicitly named
1200:                    // ("<Geometry><PropertyName>the_geom</PropertyName></Geometry>")
1201:                    // or the default geometry is assumed (no <Geometry> element).
1202:                    // I've modified the style attribute extractor so it tracks if the
1203:                    // default geometry is used. So, we no longer add EVERY geometry
1204:                    // column to the query!!
1205:
1206:                    if ((attName.equalsIgnoreCase("grid"))
1207:                            && !atts.contains(attName)
1208:                            || (attName.equalsIgnoreCase("params"))
1209:                            && !atts.contains(attName)) {
1210:                        atts.add(attName);
1211:                        if (LOGGER.isLoggable(Level.FINE))
1212:                            LOGGER.fine("added attribute " + attName);
1213:                    }
1214:                }
1215:
1216:                try {
1217:                    // DJB:geos-469 if the default geometry was used in the style, we
1218:                    // need to grab it.
1219:                    if (sae.getDefaultGeometryUsed()
1220:                            && (!atts.contains(schema.getDefaultGeometry()
1221:                                    .getName()))) {
1222:                        atts.add(schema.getDefaultGeometry().getName());
1223:                    }
1224:                } catch (Exception e) {
1225:                    // might not be a geometry column. That will cause problems down the
1226:                    // road (why render a non-geometry layer)
1227:                }
1228:
1229:                ftsAttributes = new String[atts.size()];
1230:                atts.toArray(ftsAttributes);
1231:
1232:                return ftsAttributes;
1233:            }
1234:
1235:            /**
1236:             * Creates the bounding box filters (one for each geometric attribute)
1237:             * needed to query a <code>MapLayer</code>'s feature source to return
1238:             * just the features for the target rendering extent
1239:             * 
1240:             * @param schema
1241:             *            the layer's feature source schema
1242:             * @param attributes
1243:             *            set of needed attributes
1244:             * @param bbox
1245:             *            the expression holding the target rendering bounding box
1246:             * @return an or'ed list of bbox filters, one for each geometric attribute
1247:             *         in <code>attributes</code>. If there are just one geometric
1248:             *         attribute, just returns its corresponding
1249:             *         <code>GeometryFilter</code>.
1250:             * @throws IllegalFilterException
1251:             *             if something goes wrong creating the filter
1252:             */
1253:            private Filter createBBoxFilters(FeatureType schema,
1254:                    String[] attributes, Envelope bbox)
1255:                    throws IllegalFilterException {
1256:                Filter filter = null;
1257:                final int length = attributes.length;
1258:                AttributeType attType;
1259:
1260:                for (int j = 0; j < length; j++) {
1261:                    attType = schema.getAttributeType(attributes[j]);
1262:
1263:                    // DJB: added this for better error messages!
1264:                    if (attType == null) {
1265:                        if (LOGGER.isLoggable(Level.FINE))
1266:                            LOGGER.fine(new StringBuffer("Could not find '")
1267:                                    .append(attributes[j]).append(
1268:                                            "' in the FeatureType (").append(
1269:                                            schema.getTypeName()).append(")")
1270:                                    .toString());
1271:                        throw new IllegalFilterException(new StringBuffer(
1272:                                "Could not find '").append(
1273:                                attributes[j] + "' in the FeatureType (")
1274:                                .append(schema.getTypeName()).append(")")
1275:                                .toString());
1276:                    }
1277:
1278:                    if (attType instanceof  GeometryAttributeType) {
1279:                        BBOX gfilter = filterFactory.bbox(attType.getName(),
1280:                                bbox.getMinX(), bbox.getMinY(), bbox.getMaxX(),
1281:                                bbox.getMaxY(), null);
1282:
1283:                        if (filter == null) {
1284:                            filter = gfilter;
1285:                        } else {
1286:                            filter = filterFactory.or(filter, gfilter);
1287:                        }
1288:                    }
1289:                }
1290:
1291:                return filter;
1292:            }
1293:
1294:            /**
1295:             * Checks if a rule can be triggered at the current scale level
1296:             * 
1297:             * @param r
1298:             *            The rule
1299:             * @return true if the scale is compatible with the rule settings
1300:             */
1301:            private boolean isWithInScale(Rule r) {
1302:                return ((r.getMinScaleDenominator() - TOLERANCE) <= scaleDenominator)
1303:                        && ((r.getMaxScaleDenominator() + TOLERANCE) > scaleDenominator);
1304:            }
1305:
1306:            /**
1307:             * <p>Creates a list of <code>LiteFeatureTypeStyle</code>s with:
1308:             * <ol type="a">
1309:             * <li>out-of-scale rules removed</li>
1310:             * <li>incompatible FeatureTypeStyles removed</li>
1311:             * </ol>
1312:             * </p>
1313:             * 
1314:             * <p><em><strong>Note:</strong> This method has a lot of duplication with 
1315:             * {@link #createLiteFeatureTypeStyles(FeatureTypeStyle[], FeatureType, Graphics2D)}. 
1316:             * </em></p>
1317:             * 
1318:             * @param featureStyles Styles to process
1319:             * @param typeDescription The type description that has to be matched
1320:             * @return ArrayList<LiteFeatureTypeStyle>
1321:             */
1322:            //TODO: Merge the two createLiteFeatureTypeStyles() methods
1323:            private ArrayList createLiteFeatureTypeStyles(
1324:                    FeatureTypeStyle[] featureStyles, Object typeDescription,
1325:                    Graphics2D graphics) throws IOException {
1326:                ArrayList result = new ArrayList();
1327:
1328:                Rule[] rules;
1329:                ArrayList ruleList = new ArrayList();
1330:                ArrayList elseRuleList = new ArrayList();
1331:                Rule r;
1332:                LiteFeatureTypeStyle lfts;
1333:                BufferedImage image;
1334:                int numOfRules;
1335:                int itemNumber = 0;
1336:
1337:                final int length = featureStyles.length;
1338:                for (int i = 0; i < length; i++) {
1339:                    FeatureTypeStyle fts = featureStyles[i];
1340:
1341:                    if (typeDescription == null
1342:                            || typeDescription.toString().indexOf(
1343:                                    fts.getFeatureTypeName()) == -1)
1344:                        continue;
1345:
1346:                    // get applicable rules at the current scale
1347:                    rules = fts.getRules();
1348:                    ruleList = new ArrayList();
1349:                    elseRuleList = new ArrayList();
1350:
1351:                    numOfRules = rules.length;
1352:                    for (int j = 0; j < numOfRules; j++) {
1353:                        // getting rule
1354:                        r = rules[j];
1355:
1356:                        if (isWithInScale(r)) {
1357:                            if (r.hasElseFilter()) {
1358:                                elseRuleList.add(r);
1359:                            } else {
1360:                                ruleList.add(r);
1361:                            }
1362:                        }
1363:                    }
1364:                    if ((ruleList.size() == 0) && (elseRuleList.size() == 0))
1365:                        continue; // DJB: optimization - nothing to render, dont
1366:                    // do anything!!
1367:
1368:                    if (itemNumber == 0) // we can optimize this one!
1369:                    {
1370:                        lfts = new LiteFeatureTypeStyle(graphics, ruleList,
1371:                                elseRuleList);
1372:                    } else {
1373:                        image = graphics.getDeviceConfiguration()
1374:                                .createCompatibleImage(screenSize.width,
1375:                                        screenSize.height,
1376:                                        Transparency.TRANSLUCENT);
1377:                        lfts = new LiteFeatureTypeStyle(image, graphics
1378:                                .getTransform(), ruleList, elseRuleList,
1379:                                java2dHints);
1380:                    }
1381:                    result.add(lfts);
1382:                    itemNumber++;
1383:
1384:                }
1385:
1386:                return result;
1387:            }
1388:
1389:            /**
1390:             * creates a list of LiteFeatureTypeStyles a) out-of-scale rules removed b)
1391:             * incompatible FeatureTypeStyles removed
1392:             * 
1393:             * 
1394:             * @param featureStylers
1395:             * @param features
1396:             * @throws Exception
1397:             * @return ArrayList<LiteFeatureTypeStyle>
1398:             */
1399:            private ArrayList createLiteFeatureTypeStyles(
1400:                    FeatureTypeStyle[] featureStyles, FeatureType ftype,
1401:                    Graphics2D graphics) throws IOException {
1402:                if (LOGGER.isLoggable(Level.FINE))
1403:                    LOGGER.fine("creating rules for scale denominator - "
1404:                            + NumberFormat.getNumberInstance().format(
1405:                                    scaleDenominator));
1406:                ArrayList result = new ArrayList();
1407:
1408:                int itemNumber = 0;
1409:
1410:                Rule[] rules;
1411:                ArrayList ruleList = new ArrayList();
1412:                ArrayList elseRuleList = new ArrayList();
1413:                Rule r;
1414:                LiteFeatureTypeStyle lfts;
1415:                BufferedImage image;
1416:                int numOfRules;
1417:                FeatureTypeStyle fts;
1418:                final String typeName = ftype.getTypeName();
1419:                final int length = featureStyles.length;
1420:                for (int i = 0; i < length; i++)
1421:                // DJB: for each FeatureTypeStyle in the SLD (each on is drawn
1422:                // indpendently)
1423:                {
1424:                    // getting feature styles
1425:                    fts = featureStyles[i];
1426:
1427:                    if ((typeName != null)
1428:                            && (ftype.isDescendedFrom(null, fts
1429:                                    .getFeatureTypeName()) || typeName
1430:                                    .equalsIgnoreCase(fts.getFeatureTypeName()))) {
1431:                        // DJB: this FTS is compatible with this FT.
1432:
1433:                        // get applicable rules at the current scale
1434:                        rules = fts.getRules();
1435:                        ruleList = new ArrayList();
1436:                        elseRuleList = new ArrayList();
1437:
1438:                        numOfRules = rules.length;
1439:                        for (int j = 0; j < numOfRules; j++) {
1440:                            // getting rule
1441:                            r = rules[j];
1442:
1443:                            if (isWithInScale(r)) {
1444:                                if (r.hasElseFilter()) {
1445:                                    elseRuleList.add(r);
1446:                                } else {
1447:                                    ruleList.add(r);
1448:                                }
1449:                            }
1450:                        }
1451:                        if ((ruleList.size() == 0)
1452:                                && (elseRuleList.size() == 0))
1453:                            continue; // DJB: optimization - nothing to render, dont
1454:                        // do anything!!
1455:
1456:                        if (itemNumber == 0) // we can optimize this one!
1457:                        {
1458:                            lfts = new LiteFeatureTypeStyle(graphics, ruleList,
1459:                                    elseRuleList);
1460:                        } else {
1461:                            image = graphics.getDeviceConfiguration()
1462:                                    .createCompatibleImage(screenSize.width,
1463:                                            screenSize.height,
1464:                                            Transparency.TRANSLUCENT);
1465:                            lfts = new LiteFeatureTypeStyle(image, graphics
1466:                                    .getTransform(), ruleList, elseRuleList,
1467:                                    java2dHints);
1468:                        }
1469:                        result.add(lfts);
1470:                        itemNumber++;
1471:
1472:                    }
1473:                }
1474:
1475:                return result;
1476:            }
1477:
1478:            private Collection prepCollection(Collection collection,
1479:                    CoordinateReferenceSystem sourceCrs) throws IOException {
1480:                if (collection instanceof  FeatureCollection) {
1481:                    // will force content into correct CRS if needed
1482:                    return prepFeatureCollection(
1483:                            (FeatureCollection) collection, sourceCrs);
1484:                }
1485:                return collection;
1486:            }
1487:
1488:            /**
1489:             * Prepair a FeatureCollection for display, this method formally ensured that a FeatureReader
1490:             * produced the correct CRS and has now been updated to work with FeatureCollection.
1491:             * <p>
1492:             * What is really going on is the need to set up for reprojection; but *after* decimation has
1493:             * occured.
1494:             * </p>
1495:             *  
1496:             * @param features
1497:             * @param sourceCrs
1498:             * @return FeatureCollection that produces results with the correct CRS
1499:             */
1500:            private FeatureCollection prepFeatureCollection(
1501:                    FeatureCollection features,
1502:                    CoordinateReferenceSystem sourceCrs) {
1503:                // DJB: dont do reprojection here - do it after decimation
1504:                // but we ensure that the reader is producing geometries with
1505:                // the correct CRS
1506:                // NOTE: it, by default, produces ones that are are tagged with
1507:                // the CRS of the datastore, which
1508:                // maybe incorrect.
1509:                // The correct value is in sourceCrs.
1510:
1511:                // this is the reader's CRS
1512:                CoordinateReferenceSystem rCS = features.getSchema()
1513:                        .getDefaultGeometry().getCoordinateSystem();
1514:
1515:                // sourceCrs == source's real SRS
1516:                // if we need to recode the incoming geometries
1517:
1518:                if (rCS != sourceCrs) // not both null or both EXACTLY the
1519:                // same CRS object
1520:                {
1521:                    if (sourceCrs != null) // dont re-tag to null, keep the
1522:                    // DataStore's CRS (this shouldnt
1523:                    // really happen)
1524:                    {
1525:                        // if the datastore is producing null CRS, we recode.
1526:                        // if the datastore's CRS != real CRS, then we recode
1527:                        if ((rCS == null)
1528:                                || !CRS.equalsIgnoreMetadata(rCS, sourceCrs)) {
1529:                            // need to retag the features
1530:                            try {
1531:                                return new ForceCoordinateSystemFeatureResults(
1532:                                        features, sourceCrs);
1533:                            } catch (Exception ee) {
1534:                                LOGGER.log(Level.WARNING, ee
1535:                                        .getLocalizedMessage(), ee);
1536:                            }
1537:                        }
1538:                    }
1539:                }
1540:                return features;
1541:            }
1542:
1543:            /**
1544:             * Applies each feature type styler in turn to all of the features. This
1545:             * perhaps needs some explanation to make it absolutely clear.
1546:             * featureStylers[0] is applied to all features before featureStylers[1] is
1547:             * applied. This can have important consequences as regards the painting
1548:             * order.
1549:             * <p>
1550:             * In most cases, this is the desired effect. For example, all line features
1551:             * may be rendered with a fat line and then a thin line. This produces a
1552:             * 'cased' effect without any strange overlaps.
1553:             * </p>
1554:             * <p>
1555:             * This method is internal and should only be called by render.
1556:             * </p>
1557:             * <p>
1558:             * </p>
1559:             * 
1560:             * @param graphics
1561:             *            DOCUMENT ME!
1562:             * @param features
1563:             *            An array of features to be rendered
1564:             * @param featureStylers
1565:             *            An array of feature stylers to be applied
1566:             * @param at
1567:             *            DOCUMENT ME!
1568:             * @param destinationCrs -
1569:             *            The destination CRS, or null if no reprojection is required
1570:             * @param screenSize
1571:             * @param layerId 
1572:             * @throws IOException
1573:             * @throws IllegalAttributeException
1574:             * @throws IllegalFilterException
1575:             */
1576:            final private void processStylers(final Graphics2D graphics,
1577:                    MapLayer currLayer, AffineTransform at,
1578:                    CoordinateReferenceSystem destinationCrs, Envelope mapArea,
1579:                    Rectangle screenSize, String layerId)
1580:                    throws IllegalFilterException, IOException,
1581:                    IllegalAttributeException {
1582:
1583:                /*
1584:                 * DJB: changed this a wee bit so that it now does the layer query AFTER
1585:                 * it has evaluated the rules for scale inclusion. This makes it so that
1586:                 * geometry columns (and other columns) will not be queried unless they
1587:                 * are actually going to be required. see geos-469
1588:                 */
1589:                // /////////////////////////////////////////////////////////////////////
1590:                //
1591:                // Preparing feature information and styles
1592:                //
1593:                // /////////////////////////////////////////////////////////////////////
1594:                final FeatureTypeStyle[] featureStylers = currLayer.getStyle()
1595:                        .getFeatureTypeStyles();
1596:
1597:                final FeatureSource featureSource = currLayer
1598:                        .getFeatureSource();
1599:
1600:                final Collection result;
1601:                final CoordinateReferenceSystem sourceCrs;
1602:                final NumberRange scaleRange = new NumberRange(
1603:                        scaleDenominator, scaleDenominator);
1604:                final ArrayList lfts;
1605:
1606:                if (featureSource != null) {
1607:                    final FeatureType schema = featureSource.getSchema();
1608:
1609:                    final GeometryAttributeType geometryAttribute = schema
1610:                            .getDefaultGeometry();
1611:                    sourceCrs = geometryAttribute.getCoordinateSystem();
1612:                    if (LOGGER.isLoggable(Level.FINE)) {
1613:                        LOGGER.fine(new StringBuffer("processing ").append(
1614:                                featureStylers.length).append(" stylers for ")
1615:                                .append(
1616:                                        currLayer.getFeatureSource()
1617:                                                .getSchema().getTypeName())
1618:                                .toString());
1619:                    }
1620:                    // transformMap = new HashMap();
1621:                    lfts = createLiteFeatureTypeStyles(featureStylers, schema,
1622:                            graphics);
1623:                    if (lfts.size() == 0)
1624:                        return;
1625:
1626:                    LiteFeatureTypeStyle[] featureTypeStyleArray = (LiteFeatureTypeStyle[]) lfts
1627:                            .toArray(new LiteFeatureTypeStyle[lfts.size()]);
1628:                    // /////////////////////////////////////////////////////////////////////
1629:                    //
1630:                    // DJB: get a featureresults (so you can get a feature reader) for the
1631:                    // data
1632:                    //
1633:                    // /////////////////////////////////////////////////////////////////////
1634:
1635:                    result = queryLayer(currLayer, featureSource, schema,
1636:                            featureTypeStyleArray, mapArea, destinationCrs,
1637:                            sourceCrs, screenSize, geometryAttribute, at);
1638:                } else {
1639:                    CollectionSource source = currLayer.getSource();
1640:                    result = queryLayer(currLayer, currLayer.getSource());
1641:                    sourceCrs = null;
1642:                    lfts = createLiteFeatureTypeStyles(featureStylers, source
1643:                            .describe(), graphics);
1644:                }
1645:
1646:                if (lfts.size() == 0)
1647:                    return; // nothing to do
1648:
1649:                final Collection collection = prepCollection(result, sourceCrs);
1650:                final Iterator iterator = collection.iterator();
1651:                int n_lfts = lfts.size();
1652:                final LiteFeatureTypeStyle[] fts_array = (LiteFeatureTypeStyle[]) lfts
1653:                        .toArray(new LiteFeatureTypeStyle[n_lfts]);
1654:
1655:                try {
1656:                    RenderableFeature rf = new RenderableFeature(
1657:                            fts_array.length > 1);
1658:                    rf.setLayer(currLayer);
1659:                    int t = 0;
1660:                    while (!renderingStopRequested) { // loop exit condition tested inside try catch
1661:                        try {
1662:                            if (!iterator.hasNext()) {
1663:                                break;
1664:                            }
1665:                            rf.setFeature(iterator.next());
1666:                            for (t = 0; t < n_lfts; t++) {
1667:                                process(rf, fts_array[t], scaleRange, at,
1668:                                        destinationCrs, layerId);
1669:                                // draw the content on the image(s)
1670:                            }
1671:                        } catch (Throwable tr) {
1672:                            LOGGER.log(Level.SEVERE, tr.getLocalizedMessage(),
1673:                                    tr);
1674:                            fireErrorEvent(new Exception(
1675:                                    "Error rendering feature", tr));
1676:                        }
1677:                    }
1678:                } finally {
1679:                    if (collection instanceof  ResourceCollection) {
1680:                        ResourceCollection resource = (ResourceCollection) collection;
1681:                        resource.close(iterator);
1682:                    }
1683:                }
1684:                // have to re-form the image now.
1685:                // graphics.setTransform( new AffineTransform() );
1686:                for (int t = 0; t < n_lfts; t++) {
1687:                    if (fts_array[t].myImage != null) // this is the case for the
1688:                    // first one (ie.
1689:                    // fts_array[t].graphics ==
1690:                    // graphics)
1691:                    {
1692:                        graphics.drawImage(fts_array[t].myImage, 0, 0, null);
1693:                        fts_array[t].myImage.flush();
1694:                        fts_array[t].graphics.dispose();
1695:                    }
1696:                }
1697:
1698:            }
1699:
1700:            /**
1701:             * @param rf
1702:             * @param feature 
1703:             * @param style
1704:             * @param layerId 
1705:             */
1706:            final private void process(RenderableFeature rf,
1707:                    LiteFeatureTypeStyle style, Range scaleRange,
1708:                    AffineTransform at,
1709:                    CoordinateReferenceSystem destinationCrs, String layerId)
1710:                    throws TransformException, FactoryException {
1711:                boolean doElse = true;
1712:                Rule[] elseRuleList = style.elseRules;
1713:                Rule[] ruleList = style.ruleList;
1714:                Rule r;
1715:                Filter filter;
1716:                Symbolizer[] symbolizers;
1717:                Graphics2D graphics = style.graphics;
1718:                // applicable rules
1719:                final int length = ruleList.length;
1720:                for (int t = 0; t < length; t++) {
1721:                    r = ruleList[t];
1722:                    filter = r.getFilter();
1723:
1724:                    if ((filter == null) || filter.evaluate(rf.content)) {
1725:                        doElse = false;
1726:                        symbolizers = r.getSymbolizers();
1727:                        processSymbolizers(graphics, rf, symbolizers,
1728:                                scaleRange, at, destinationCrs, layerId);
1729:                    }
1730:                }
1731:
1732:                if (doElse) {
1733:                    final int elseLength = elseRuleList.length;
1734:                    for (int tt = 0; tt < elseLength; tt++) {
1735:                        r = elseRuleList[tt];
1736:                        symbolizers = r.getSymbolizers();
1737:
1738:                        processSymbolizers(graphics, rf, symbolizers,
1739:                                scaleRange, at, destinationCrs, layerId);
1740:
1741:                    }
1742:                }
1743:            }
1744:
1745:            /**
1746:             * Applies each of a set of symbolizers in turn to a given feature.
1747:             * <p>
1748:             * This is an internal method and should only be called by processStylers.
1749:             * </p>
1750:             * @param currLayer 
1751:             * 
1752:             * @param graphics
1753:             * @param drawMe
1754:             *            The feature to be rendered
1755:             * @param symbolizers
1756:             *            An array of symbolizers which actually perform the rendering.
1757:             * @param scaleRange
1758:             *            The scale range we are working on... provided in order to make
1759:             *            the style factory happy
1760:             * @param shape
1761:             * @param destinationCrs
1762:             * @param layerId 
1763:             * @throws TransformException
1764:             * @throws FactoryException
1765:             */
1766:            final private void processSymbolizers(final Graphics2D graphics,
1767:                    final RenderableFeature drawMe,
1768:                    final Symbolizer[] symbolizers, Range scaleRange,
1769:                    AffineTransform at,
1770:                    CoordinateReferenceSystem destinationCrs, String layerId)
1771:                    throws TransformException, FactoryException {
1772:                final int length = symbolizers.length;
1773:                for (int m = 0; m < length; m++) {
1774:
1775:                    // /////////////////////////////////////////////////////////////////
1776:                    //
1777:                    // RASTER
1778:                    //
1779:                    // /////////////////////////////////////////////////////////////////
1780:                    final Symbolizer symbolizer = symbolizers[m];
1781:                    if (symbolizer instanceof  RasterSymbolizer) {
1782:                        renderRaster(graphics, drawMe.content,
1783:                                (RasterSymbolizer) symbolizer, destinationCrs,
1784:                                scaleRange);
1785:
1786:                    } else {
1787:
1788:                        // /////////////////////////////////////////////////////////////////
1789:                        //
1790:                        // FEATURE
1791:                        //
1792:                        // /////////////////////////////////////////////////////////////////
1793:                        LiteShape2 shape = drawMe.getShape(symbolizer, at);
1794:                        if (shape == null)
1795:                            continue;
1796:                        if (symbolizer instanceof  TextSymbolizer
1797:                                && drawMe.content instanceof  Feature) {
1798:                            // TODO: double back and make labelCache work with Objects
1799:                            labelCache
1800:                                    .put(layerId, (TextSymbolizer) symbolizer,
1801:                                            (Feature) drawMe.content, shape,
1802:                                            scaleRange);
1803:                        } else {
1804:                            Style2D style = styleFactory.createStyle(
1805:                                    drawMe.content, symbolizer, scaleRange);
1806:                            painter.paint(graphics, shape, style,
1807:                                    scaleDenominator);
1808:                        }
1809:
1810:                    }
1811:                }
1812:                fireFeatureRenderedEvent(drawMe.content);
1813:            }
1814:
1815:            /**
1816:             * Renders a grid coverage on the device.
1817:             * 
1818:             * @param graphics
1819:             *            DOCUMENT ME!
1820:             * @param drawMe
1821:             *            the feature that contains the GridCoverage. The grid coverage
1822:             *            must be contained in the "grid" attribute
1823:             * @param symbolizer
1824:             *            The raster symbolizer
1825:             * @param scaleRange
1826:             * @param world2Grid 
1827:             * @task make it follow the symbolizer
1828:             */
1829:            private void renderRaster(Graphics2D graphics, Object drawMe,
1830:                    RasterSymbolizer symbolizer,
1831:                    CoordinateReferenceSystem destinationCRS, Range scaleRange) {
1832:                final Object grid = gridPropertyName.evaluate(drawMe);
1833:                if (LOGGER.isLoggable(Level.FINE))
1834:                    LOGGER.fine(new StringBuffer(
1835:                            "rendering Raster for feature ").append(
1836:                            drawMe.toString()).append(" - ").append(grid)
1837:                            .toString());
1838:
1839:                try {
1840:                    // /////////////////////////////////////////////////////////////////
1841:                    //
1842:                    // If the grid object is a reader we ask him to do its best for the
1843:                    // requested resolution, if it is a gridcoverage instead we have to
1844:                    // rely on the gridocerage renderer itself.
1845:                    //
1846:                    // /////////////////////////////////////////////////////////////////
1847:                    //			METABUFFER SUPPORT			
1848:                    //			final GeneralEnvelope metaBufferedEnvelope=handleTileBordersArtifacts(mapExtent,java2dHints,this.worldToScreenTransform);
1849:                    final GridCoverageRenderer gcr = new GridCoverageRenderer(
1850:                            destinationCRS, originalMapExtent, screenSize,
1851:                            java2dHints);
1852:
1853:                    // //
1854:                    // It is a grid coverage
1855:                    // //
1856:                    if (grid instanceof  GridCoverage)
1857:                        //				METABUFFER SUPPORT				
1858:                        //				gcr.paint(graphics, (GridCoverage2D) grid, symbolizer, metaBufferedEnvelope);
1859:                        gcr.paint(graphics, (GridCoverage2D) grid, symbolizer);
1860:                    else if (grid instanceof  GridCoverageReader) {
1861:                        // //
1862:                        // It is an AbstractGridCoverage2DReader, let's use parameters
1863:                        // if we have any supplied by a user.
1864:                        // //
1865:                        // first I created the correct ReadGeometry
1866:                        final Parameter readGG = new Parameter(
1867:                                AbstractGridFormat.READ_GRIDGEOMETRY2D);
1868:                        //	METABUFFER SUPPORT
1869:                        //				readGG.setValue(new GridGeometry2D(new GeneralGridRange(
1870:                        //						screenSize), metaBufferedEnvelope));
1871:                        readGG.setValue(new GridGeometry2D(
1872:                                new GeneralGridRange(screenSize), mapExtent));
1873:                        final GridCoverageReader reader = (GridCoverageReader) grid;
1874:                        // then I try to get read parameters associated with this
1875:                        // coverage if there are any.
1876:                        final Object params = paramsPropertyName
1877:                                .evaluate(drawMe);
1878:                        final GridCoverage2D coverage;
1879:                        if (params != null) {
1880:                            // //
1881:                            //
1882:                            // Getting parameters to control how to read this coverage.
1883:                            // Remember to check to actually have them before forwarding
1884:                            // them to the reader.
1885:                            //
1886:                            // //
1887:                            GeneralParameterValue[] readParams = (GeneralParameterValue[]) params;
1888:                            final int length = readParams.length;
1889:                            if (length > 0) {
1890:                                // we have a valid number of parameters, let's check if
1891:                                // also have a READ_GRIDGEOMETRY2D. In such case we just
1892:                                // override it with the one we just build for this
1893:                                // request.
1894:                                final String name = AbstractGridFormat.READ_GRIDGEOMETRY2D
1895:                                        .getName().toString();
1896:                                int i = 0;
1897:                                for (; i < length; i++)
1898:                                    if (readParams[i].getDescriptor().getName()
1899:                                            .toString().equalsIgnoreCase(name))
1900:                                        break;
1901:                                // did we find anything?
1902:                                if (i < length) {
1903:                                    //we found another READ_GRIDGEOMETRY2D, let's override it.
1904:                                    ((Parameter) readParams[i])
1905:                                            .setValue(readGG);
1906:                                    coverage = (GridCoverage2D) reader
1907:                                            .read(readParams);
1908:                                } else {
1909:                                    // add the correct read geometry to the supplied
1910:                                    // params since we did not find anything
1911:                                    GeneralParameterValue[] readParams2 = new GeneralParameterValue[length + 1];
1912:                                    System.arraycopy(readParams, 0,
1913:                                            readParams2, 0, length);
1914:                                    readParams2[length] = readGG;
1915:                                    coverage = (GridCoverage2D) reader
1916:                                            .read(readParams2);
1917:                                }
1918:                            } else
1919:                                // we have no parameters hence we just use the read grid
1920:                                // geometry to get a coverage
1921:                                coverage = (GridCoverage2D) reader
1922:                                        .read(new GeneralParameterValue[] { readGG });
1923:                        } else {
1924:                            coverage = (GridCoverage2D) reader
1925:                                    .read(new GeneralParameterValue[] { readGG });
1926:                        }
1927:                        //				METABUFFER SUPPORT
1928:                        //				gcr.paint(graphics, coverage, symbolizer,metaBufferedEnvelope);
1929:                        gcr.paint(graphics, coverage, symbolizer);
1930:                    }
1931:                    if (LOGGER.isLoggable(Level.FINE))
1932:                        LOGGER.fine("Raster rendered");
1933:
1934:                } catch (FactoryException e) {
1935:                    LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
1936:                    fireErrorEvent(e);
1937:                } catch (TransformException e) {
1938:                    LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
1939:                    fireErrorEvent(e);
1940:                } catch (NoninvertibleTransformException e) {
1941:                    LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
1942:                    fireErrorEvent(e);
1943:                } catch (IllegalArgumentException e) {
1944:                    LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
1945:                    fireErrorEvent(e);
1946:                } catch (IOException e) {
1947:                    LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
1948:                    fireErrorEvent(e);
1949:                }
1950:
1951:            }
1952:
1953:            /**
1954:             * This method captures a first attempt to handle in a coherent way the problem of having artifacts at the 
1955:             * borders of drown tiles when using Tiling Clients like OpenLayers with higher order interpolation.
1956:             * 
1957:             * @param mapExtent to draw on.
1958:             * @param java2dHints to control the drawing process.
1959:             * @param worldToScreen transformation that maps onto the screen.
1960:             * @return a {@link GeneralEnvelope} that might be expanded in order to avoid
1961:             * 		   artifacts at the borders
1962:             */
1963:            private GeneralEnvelope handleTileBordersArtifacts(
1964:                    ReferencedEnvelope mapExtent, RenderingHints java2dHints,
1965:                    AffineTransform worldToScreen) {
1966:                // /////////////////////////////////////////////////////////////////////
1967:                //
1968:                // Check if interpolation is required.
1969:                //
1970:                // /////////////////////////////////////////////////////////////////////
1971:                if (!java2dHints.containsKey(JAI.KEY_INTERPOLATION)) {
1972:                    if (LOGGER.isLoggable(Level.FINE))
1973:                        LOGGER
1974:                                .fine("Unable to find interpolation for this request.");
1975:                    return new GeneralEnvelope(mapExtent);
1976:                }
1977:                final Interpolation interp = (Interpolation) java2dHints
1978:                        .get(JAI.KEY_INTERPOLATION);
1979:
1980:                // /////////////////////////////////////////////////////////////////////
1981:                //
1982:                // Ok, we have an interpolation, let's decide if we have to extend the
1983:                // requested area accordingly.
1984:                //
1985:                // /////////////////////////////////////////////////////////////////////
1986:                int buffer = 0;
1987:                if (interp instanceof  InterpolationNearest) {
1988:                    if (LOGGER.isLoggable(Level.FINE))
1989:                        LOGGER
1990:                                .fine("Interpolation Nearest no need for extending.");
1991:                    return new GeneralEnvelope(mapExtent);
1992:
1993:                }
1994:                if (interp instanceof  InterpolationBilinear) {
1995:                    if (LOGGER.isLoggable(Level.FINE))
1996:                        LOGGER
1997:                                .fine("Interpolation Bilinear  extending.by 1 pixel at least");
1998:                    buffer = 10;
1999:                } else if (interp instanceof  InterpolationBicubic) {
2000:                    if (LOGGER.isLoggable(Level.FINE))
2001:                        LOGGER
2002:                                .fine("Interpolation Bicubic  extending.by 2 pixel at least");
2003:                    buffer = 30;
2004:                }
2005:                if (buffer <= 0) {
2006:                    return new GeneralEnvelope(mapExtent);
2007:                }
2008:
2009:                // /////////////////////////////////////////////////////////////////////
2010:                //
2011:                // Doing the extension
2012:                //
2013:                // /////////////////////////////////////////////////////////////////////
2014:                final Envelope tempEnv = expandEnvelope(mapExtent,
2015:                        worldToScreen, buffer);
2016:                final GeneralEnvelope newEnv = new GeneralEnvelope(
2017:                        new double[] { tempEnv.getMinX(), tempEnv.getMinY(), },
2018:                        new double[] { tempEnv.getMaxX(), tempEnv.getMaxY() });
2019:                newEnv.setCoordinateReferenceSystem(mapExtent
2020:                        .getCoordinateReferenceSystem());
2021:                return newEnv;
2022:
2023:            }
2024:
2025:            /**
2026:             * Finds the geometric attribute requested by the symbolizer
2027:             * 
2028:             * @param drawMe
2029:             *            The feature
2030:             * @param s
2031:             * 
2032:             * /** Finds the geometric attribute requested by the symbolizer
2033:             * 
2034:             * @param drawMe
2035:             *            The feature
2036:             * @param s
2037:             *            The symbolizer
2038:             * @return The geometry requested in the symbolizer, or the default geometry
2039:             *         if none is specified
2040:             */
2041:            private com.vividsolutions.jts.geom.Geometry findGeometry(
2042:                    Object drawMe, Symbolizer s) {
2043:                PropertyName geomName = getGeometryPropertyName(s);
2044:
2045:                // get the geometry
2046:                Geometry geom;
2047:                if (geomName == null) {
2048:                    if (drawMe instanceof  Feature)
2049:                        geom = ((Feature) drawMe).getDefaultGeometry();
2050:                    else
2051:                        geom = (Geometry) defaultGeometryPropertyName.evaluate(
2052:                                drawMe, Geometry.class);
2053:                } else {
2054:                    geom = (Geometry) geomName.evaluate(drawMe, Geometry.class);
2055:                }
2056:
2057:                // if the symbolizer is a point symbolizer generate a suitable location
2058:                // to place the
2059:                // point in order to avoid recomputing that location at each rendering
2060:                // step
2061:                if (s instanceof  PointSymbolizer) {
2062:                    geom = getCentroid(geom); // djb: major simpificatioN
2063:                }
2064:                return geom;
2065:            }
2066:
2067:            /**
2068:             * Finds the centroid of the input geometry if input = point, line, polygon
2069:             * --> return a point that represents the centroid of that geom if input =
2070:             * geometry collection --> return a multipoint that represents the centoid
2071:             * of each sub-geom
2072:             * 
2073:             * @param g
2074:             */
2075:            private Geometry getCentroid(Geometry g) {
2076:                if (g instanceof  GeometryCollection) {
2077:                    final GeometryCollection gc = (GeometryCollection) g;
2078:                    final Coordinate[] pts = new Coordinate[gc
2079:                            .getNumGeometries()];
2080:                    final int length = gc.getNumGeometries();
2081:                    for (int t = 0; t < length; t++) {
2082:                        pts[t] = gc.getGeometryN(t).getCentroid()
2083:                                .getCoordinate();
2084:                    }
2085:                    return g.getFactory().createMultiPoint(pts);
2086:                } else if (g != null) {
2087:                    return g.getCentroid();
2088:                }
2089:                return null;
2090:            }
2091:
2092:            /**
2093:             * Finds the geometric attribute coordinate reference system.
2094:             * @param drawMe2 
2095:             * 
2096:             * @param f The feature
2097:             * @param s The symbolizer
2098:             * @return The geometry requested in the symbolizer, or the default geometry if none is specified
2099:             */
2100:            private org.opengis.referencing.crs.CoordinateReferenceSystem findGeometryCS(
2101:                    MapLayer currLayer, Object drawMe, Symbolizer s) {
2102:
2103:                if (drawMe instanceof  Feature) {
2104:                    Feature f = (Feature) drawMe;
2105:
2106:                    PropertyName propertyName = getGeometryPropertyName(s);
2107:                    String geomName = propertyName != null ? propertyName
2108:                            .getPropertyName() : null;
2109:                    if (geomName == null || "".equals(geomName)) {
2110:                        FeatureType schema = f.getFeatureType();
2111:                        GeometryAttributeType geom = schema
2112:                                .getDefaultGeometry();
2113:                        return geom.getCoordinateSystem();
2114:                    } else {
2115:                        FeatureType schema = f.getFeatureType();
2116:                        GeometryAttributeType geom = (GeometryAttributeType) schema
2117:                                .getAttributeType(geomName);
2118:                        return geom.getCoordinateSystem();
2119:                    }
2120:                } else if (currLayer.getSource() != null) {
2121:                    return currLayer.getSource().getInfo().getCRS();
2122:                }
2123:
2124:                return null;
2125:            }
2126:
2127:            private PropertyName getGeometryPropertyName(Symbolizer s) {
2128:                String geomName = null;
2129:
2130:                // TODO: fix the styles, the getGeometryPropertyName should probably be
2131:                // moved into an
2132:                // interface...
2133:                if (s instanceof  PolygonSymbolizer) {
2134:                    geomName = ((PolygonSymbolizer) s)
2135:                            .getGeometryPropertyName();
2136:                } else if (s instanceof  PointSymbolizer) {
2137:                    geomName = ((PointSymbolizer) s).getGeometryPropertyName();
2138:                } else if (s instanceof  LineSymbolizer) {
2139:                    geomName = ((LineSymbolizer) s).getGeometryPropertyName();
2140:                } else if (s instanceof  TextSymbolizer) {
2141:                    geomName = ((TextSymbolizer) s).getGeometryPropertyName();
2142:                }
2143:
2144:                if (geomName == null) {
2145:                    return null;
2146:                }
2147:                return filterFactory.property(geomName);
2148:            }
2149:
2150:            /**
2151:             * Getter for property interactive.
2152:             * 
2153:             * @return Value of property interactive.
2154:             */
2155:            public boolean isInteractive() {
2156:                return interactive;
2157:            }
2158:
2159:            /**
2160:             * Sets the interactive status of the renderer. An interactive renderer
2161:             * won't wait for long image loading, preferring an alternative mark instead
2162:             * 
2163:             * @param interactive
2164:             *            new value for the interactive property
2165:             */
2166:            public void setInteractive(boolean interactive) {
2167:                this .interactive = interactive;
2168:            }
2169:
2170:            /**
2171:             * <p>
2172:             * Returns true if the optimized data loading is enabled, false otherwise.
2173:             * </p>
2174:             * <p>
2175:             * When optimized data loading is enabled, lite renderer will try to load
2176:             * only the needed feature attributes (according to styles) and to load only
2177:             * the features that are in (or overlaps with)the bounding box requested for
2178:             * painting
2179:             * </p>
2180:             * 
2181:             */
2182:            private boolean isOptimizedDataLoadingEnabled() {
2183:                if (rendererHints == null)
2184:                    return optimizedDataLoadingEnabledDEFAULT;
2185:                Object result = null;
2186:                try {
2187:                    result = rendererHints.get("optimizedDataLoadingEnabled");
2188:                } catch (ClassCastException e) {
2189:
2190:                }
2191:                if (result == null)
2192:                    return optimizedDataLoadingEnabledDEFAULT;
2193:                return ((Boolean) result).booleanValue();
2194:            }
2195:
2196:            /**
2197:             * <p>
2198:             * Returns the rendering buffer, a measure in pixels used to expand the geometry search area
2199:             * enough to capture the geometries that do stay outside of the current rendering bounds but
2200:             * do affect them because of their large strokes (labels and graphic symbols are handled 
2201:             * differently, see the label chache).
2202:             * </p>
2203:             * 
2204:             */
2205:            private int getRenderingBuffer() {
2206:                if (rendererHints == null)
2207:                    return renderingBufferDEFAULT;
2208:                Number result = (Number) rendererHints.get("renderingBuffer");
2209:                if (result == null)
2210:                    return renderingBufferDEFAULT;
2211:                return result.intValue();
2212:            }
2213:
2214:            /**
2215:             * <p>
2216:             * Returns scale computation algorithm to be used. 
2217:             * </p>
2218:             * 
2219:             */
2220:            private String getScaleComputationMethod() {
2221:                if (rendererHints == null)
2222:                    return scaleComputationMethodDEFAULT;
2223:                String result = (String) rendererHints
2224:                        .get("scaleComputationMethod");
2225:                if (result == null)
2226:                    return scaleComputationMethodDEFAULT;
2227:                return result;
2228:            }
2229:
2230:            /**
2231:             * Returns the text rendering method
2232:             */
2233:            private String getTextRenderingMethod() {
2234:                if (rendererHints == null)
2235:                    return textRenderingModeDEFAULT;
2236:                String result = (String) rendererHints.get(TEXT_RENDERING_KEY);
2237:                if (result == null)
2238:                    return textRenderingModeDEFAULT;
2239:                return result;
2240:            }
2241:
2242:            /**
2243:             * Returns the generalization distance in the screen space.
2244:             * 
2245:             */
2246:            public double getGeneralizationDistance() {
2247:                return generalizationDistance;
2248:            }
2249:
2250:            /**
2251:             * <p>
2252:             * Sets the generalizazion distance in the screen space.
2253:             * </p>
2254:             * <p>
2255:             * Default value is 1, meaning that two subsequent points are collapsed to
2256:             * one if their on screen distance is less than one pixel
2257:             * </p>
2258:             * <p>
2259:             * Set the distance to 0 if you don't want any kind of generalization
2260:             * </p>
2261:             * 
2262:             * @param d
2263:             */
2264:            public void setGeneralizationDistance(double d) {
2265:                generalizationDistance = d;
2266:            }
2267:
2268:            /*
2269:             * (non-Javadoc)
2270:             * 
2271:             * @see org.geotools.renderer.GTRenderer#setJava2DHints(java.awt.RenderingHints)
2272:             */
2273:            public void setJava2DHints(RenderingHints hints) {
2274:                this .java2dHints = hints;
2275:            }
2276:
2277:            /*
2278:             * (non-Javadoc)
2279:             * 
2280:             * @see org.geotools.renderer.GTRenderer#getJava2DHints()
2281:             */
2282:            public RenderingHints getJava2DHints() {
2283:                return java2dHints;
2284:            }
2285:
2286:            public void setRendererHints(Map hints) {
2287:                if (hints != null && hints.containsKey(LABEL_CACHE_KEY)) {
2288:                    LabelCache cache = (LabelCache) hints.get(LABEL_CACHE_KEY);
2289:                    if (cache == null)
2290:                        throw new NullPointerException(
2291:                                "Label_Cache_Hint has a null value for the labelcache");
2292:
2293:                    this .labelCache = cache;
2294:                    this .painter = new StyledShapePainter(cache);
2295:                }
2296:                rendererHints = hints;
2297:            }
2298:
2299:            /*
2300:             * (non-Javadoc)
2301:             * 
2302:             * @see org.geotools.renderer.GTRenderer#getRendererHints()
2303:             */
2304:            public Map getRendererHints() {
2305:                return rendererHints;
2306:            }
2307:
2308:            /*
2309:             * (non-Javadoc)
2310:             * 
2311:             * @see org.geotools.renderer.GTRenderer#setContext(org.geotools.map.MapContext)
2312:             */
2313:            public void setContext(MapContext context) {
2314:                this .context = context;
2315:            }
2316:
2317:            /*
2318:             * (non-Javadoc)
2319:             * 
2320:             * @see org.geotools.renderer.GTRenderer#getContext()
2321:             */
2322:            public MapContext getContext() {
2323:                return context;
2324:            }
2325:
2326:            public boolean isCanTransform() {
2327:                return canTransform;
2328:            }
2329:
2330:            public static MathTransform getMathTransform(
2331:                    CoordinateReferenceSystem sourceCRS,
2332:                    CoordinateReferenceSystem destCRS) {
2333:                try {
2334:                    return CRS.findMathTransform(sourceCRS, destCRS, true);
2335:                } catch (OperationNotFoundException e) {
2336:                    LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
2337:
2338:                } catch (FactoryException e) {
2339:                    LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
2340:                }
2341:                return null;
2342:            }
2343:
2344:            private class RenderableFeature {
2345:                Object content;
2346:                private MapLayer layer;
2347:                private IdentityHashMap symbolizerAssociationHT = new IdentityHashMap(); // associate a value
2348:                private List geometries = new ArrayList();
2349:                private List shapes = new ArrayList();
2350:                private boolean multiLayerRendering;
2351:                private boolean clone;
2352:                private HashMap decimators = new HashMap();
2353:
2354:                public RenderableFeature(boolean multiLayerRendering) {
2355:                    this .multiLayerRendering = multiLayerRendering;
2356:                }
2357:
2358:                public void setLayer(MapLayer layer) {
2359:                    this .layer = layer;
2360:                    this .clone = !layer.getFeatureSource().getSupportedHints()
2361:                            .contains(Hints.FEATURE_DETACHED);
2362:                }
2363:
2364:                public void setFeature(Object feature) {
2365:                    this .content = feature;
2366:                    geometries.clear();
2367:                    shapes.clear();
2368:                }
2369:
2370:                public LiteShape2 getShape(Symbolizer symbolizer,
2371:                        AffineTransform at) throws FactoryException {
2372:                    Geometry g = findGeometry(content, symbolizer); // pulls the geometry
2373:
2374:                    if (g == null)
2375:                        return null;
2376:
2377:                    SymbolizerAssociation sa = (SymbolizerAssociation) symbolizerAssociationHT
2378:                            .get(symbolizer);
2379:                    MathTransform2D transform = null;
2380:                    if (sa == null) {
2381:                        sa = new SymbolizerAssociation();
2382:                        sa.setCRS(findGeometryCS(layer, content, symbolizer));
2383:                        try {
2384:                            if (sa.crs == null
2385:                                    || CRS.equalsIgnoreMetadata(sa.crs,
2386:                                            destinationCrs))
2387:                                transform = null;
2388:                            else
2389:                                transform = (MathTransform2D) StreamingRenderer
2390:                                        .getMathTransform(sa.crs,
2391:                                                destinationCrs);
2392:                            if (transform != null && !transform.isIdentity()) {
2393:                                transform = (MathTransform2D) ConcatenatedTransform
2394:                                        .create(transform, ProjectiveTransform
2395:                                                .create(at));
2396:
2397:                            } else {
2398:                                transform = (MathTransform2D) ProjectiveTransform
2399:                                        .create(at);
2400:                            }
2401:                        } catch (Exception e) {
2402:                            // fall through
2403:                            LOGGER.log(Level.WARNING, e.getLocalizedMessage(),
2404:                                    e);
2405:                        }
2406:                        sa.setXform(transform);
2407:                        symbolizerAssociationHT.put(symbolizer, sa);
2408:                    }
2409:
2410:                    // some shapes may be too close to projection boundaries to
2411:                    // get transformed, try to be lenient
2412:                    try {
2413:                        return getTransformedShape(g, sa.getXform());
2414:                    } catch (TransformException te) {
2415:                        LOGGER.log(Level.FINE, te.getLocalizedMessage(), te);
2416:                        fireErrorEvent(te);
2417:                        return null;
2418:                    } catch (AssertionError ae) {
2419:                        LOGGER.log(Level.FINE, ae.getLocalizedMessage(), ae);
2420:                        fireErrorEvent(new RuntimeException(ae));
2421:                        return null;
2422:                    }
2423:                }
2424:
2425:                private final LiteShape2 getTransformedShape(Geometry g,
2426:                        MathTransform2D transform) throws TransformException,
2427:                        FactoryException {
2428:                    for (int i = 0; i < geometries.size(); i++) {
2429:                        if (geometries.get(i) == g)
2430:                            return (LiteShape2) shapes.get(i);
2431:                    }
2432:                    LiteShape2 shape = new LiteShape2(g, transform,
2433:                            getDecimator(transform), false, clone);
2434:                    geometries.add(g);
2435:                    shapes.add(shape);
2436:                    return shape;
2437:                }
2438:
2439:                /**
2440:                 * @throws org.opengis.referencing.operation.NoninvertibleTransformException
2441:                 */
2442:                private Decimator getDecimator(MathTransform2D mathTransform)
2443:                        throws org.opengis.referencing.operation.NoninvertibleTransformException {
2444:                    Decimator decimator = (Decimator) decimators
2445:                            .get(mathTransform);
2446:                    if (decimator == null) {
2447:                        if (mathTransform != null
2448:                                && !mathTransform.isIdentity())
2449:                            decimator = new Decimator(mathTransform.inverse(),
2450:                                    screenSize);
2451:                        else
2452:                            decimator = new Decimator(null, screenSize);
2453:
2454:                        decimators.put(mathTransform, decimator);
2455:                    }
2456:                    return decimator;
2457:                }
2458:            }
2459:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.