Source Code Cross Referenced for ShapefileRenderer.java in  » GIS » GeoTools-2.4.1 » org » geotools » renderer » shape » 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.shape 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    Geotools2 - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2002, 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;
0009:         *    version 2.1 of the License.
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:         */
0017:        package org.geotools.renderer.shape;
0018:
0019:        import java.awt.Graphics2D;
0020:        import java.awt.Rectangle;
0021:        import java.awt.RenderingHints;
0022:        import java.awt.Shape;
0023:        import java.awt.font.GlyphVector;
0024:        import java.awt.geom.AffineTransform;
0025:        import java.awt.geom.NoninvertibleTransformException;
0026:        import java.io.File;
0027:        import java.io.IOException;
0028:        import java.net.URL;
0029:        import java.util.ArrayList;
0030:        import java.util.Collections;
0031:        import java.util.HashMap;
0032:        import java.util.HashSet;
0033:        import java.util.Iterator;
0034:        import java.util.List;
0035:        import java.util.Map;
0036:        import java.util.Set;
0037:        import java.util.logging.Level;
0038:        import java.util.logging.Logger;
0039:
0040:        import javax.media.jai.util.Range;
0041:
0042:        import org.geotools.data.DataStore;
0043:        import org.geotools.data.DefaultQuery;
0044:        import org.geotools.data.Diff;
0045:        import org.geotools.data.FIDReader;
0046:        import org.geotools.data.FeatureStore;
0047:        import org.geotools.data.Query;
0048:        import org.geotools.data.Transaction;
0049:        import org.geotools.data.TransactionStateDiff;
0050:        import org.geotools.data.shapefile.ShapefileDataStore;
0051:        import org.geotools.data.shapefile.ShapefileRendererUtil;
0052:        import org.geotools.data.shapefile.dbf.DbaseFileHeader;
0053:        import org.geotools.data.shapefile.dbf.DbaseFileReader;
0054:        import org.geotools.data.shapefile.dbf.IndexedDbaseFileReader;
0055:        import org.geotools.data.shapefile.shp.ShapeType;
0056:        import org.geotools.data.shapefile.shp.ShapefileReader;
0057:        import org.geotools.data.shapefile.shp.ShapefileReader.Record;
0058:        import org.geotools.feature.AttributeType;
0059:        import org.geotools.feature.Feature;
0060:        import org.geotools.feature.FeatureType;
0061:        import org.geotools.feature.FeatureTypeBuilder;
0062:        import org.geotools.feature.GeometryAttributeType;
0063:        import org.geotools.feature.SchemaException;
0064:        import org.geotools.filter.FilterAttributeExtractor;
0065:        import org.geotools.filter.Filters;
0066:        import org.geotools.geometry.jts.Decimator;
0067:        import org.geotools.geometry.jts.JTS;
0068:        import org.geotools.geometry.jts.LiteCoordinateSequenceFactory;
0069:        import org.geotools.geometry.jts.LiteShape2;
0070:        import org.geotools.geometry.jts.ReferencedEnvelope;
0071:        import org.geotools.index.quadtree.StoreException;
0072:        import org.geotools.map.DefaultMapContext;
0073:        import org.geotools.map.MapContext;
0074:        import org.geotools.map.MapLayer;
0075:        import org.geotools.referencing.CRS;
0076:        import org.geotools.referencing.ReferencingFactoryFinder;
0077:        import org.geotools.referencing.crs.DefaultGeographicCRS;
0078:        import org.geotools.referencing.operation.matrix.GeneralMatrix;
0079:        import org.geotools.renderer.GTRenderer;
0080:        import org.geotools.renderer.RenderListener;
0081:        import org.geotools.renderer.lite.LabelCache;
0082:        import org.geotools.renderer.lite.LabelCacheDefault;
0083:        import org.geotools.renderer.lite.ListenerList;
0084:        import org.geotools.renderer.lite.RendererUtilities;
0085:        import org.geotools.renderer.lite.StreamingRenderer;
0086:        import org.geotools.renderer.style.SLDStyleFactory;
0087:        import org.geotools.renderer.style.Style2D;
0088:        import org.geotools.styling.FeatureTypeStyle;
0089:        import org.geotools.styling.LineSymbolizer;
0090:        import org.geotools.styling.PointSymbolizer;
0091:        import org.geotools.styling.PolygonSymbolizer;
0092:        import org.geotools.styling.Rule;
0093:        import org.geotools.styling.Style;
0094:        import org.geotools.styling.StyleAttributeExtractor;
0095:        import org.geotools.styling.Symbolizer;
0096:        import org.geotools.styling.TextSymbolizer;
0097:        import org.geotools.styling.visitor.DuplicatingStyleVisitor;
0098:        import org.geotools.util.NumberRange;
0099:        import org.opengis.filter.Filter;
0100:        import org.opengis.referencing.FactoryException;
0101:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0102:        import org.opengis.referencing.operation.CoordinateOperation;
0103:        import org.opengis.referencing.operation.MathTransform;
0104:        import org.opengis.referencing.operation.Operation;
0105:        import org.opengis.referencing.operation.TransformException;
0106:
0107:        import com.vividsolutions.jts.geom.Coordinate;
0108:        import com.vividsolutions.jts.geom.Envelope;
0109:        import com.vividsolutions.jts.geom.Geometry;
0110:        import com.vividsolutions.jts.geom.GeometryFactory;
0111:        import com.vividsolutions.jts.geom.LineString;
0112:        import com.vividsolutions.jts.geom.LinearRing;
0113:        import com.vividsolutions.jts.geom.MultiLineString;
0114:        import com.vividsolutions.jts.geom.MultiPoint;
0115:        import com.vividsolutions.jts.geom.MultiPolygon;
0116:        import com.vividsolutions.jts.geom.Point;
0117:        import com.vividsolutions.jts.geom.Polygon;
0118:
0119:        /**
0120:         * A LiteRenderer Implementations that is optimized for shapefiles.
0121:         * 
0122:         * @author jeichar
0123:         * @since 2.1.x
0124:         * @source $URL:
0125:         *         http://svn.geotools.org/geotools/branches/2.2.x/ext/shaperenderer/src/org/geotools/renderer/shape/ShapefileRenderer.java $
0126:         */
0127:        public class ShapefileRenderer implements  GTRenderer {
0128:            public static final Logger LOGGER = org.geotools.util.logging.Logging
0129:                    .getLogger("org.geotools.renderer.shape");
0130:
0131:            /** Tolerance used to compare doubles for equality */
0132:            private static final double TOLERANCE = 1e-6;
0133:            private static final GeometryFactory geomFactory = new GeometryFactory(
0134:                    new LiteCoordinateSequenceFactory());
0135:            private static final Coordinate[] COORDS;
0136:            private static final MultiPolygon MULTI_POLYGON_GEOM;
0137:            private static final Polygon POLYGON_GEOM;
0138:            private static final LinearRing LINE_GEOM;
0139:            private static final MultiLineString MULTI_LINE_GEOM;
0140:            private static final Point POINT_GEOM;
0141:            private static final MultiPoint MULTI_POINT_GEOM;
0142:
0143:            /**
0144:             * Computes the scale as the ratio between map distances and real world distances,
0145:             * assuming 90dpi and taking into consideration projection deformations and actual
0146:             * earth shape. <br>
0147:             * Use this method only when in need of accurate computation. Will break if the
0148:             * data extent is outside of the currenct projection definition area. 
0149:             */
0150:            public static final String SCALE_ACCURATE = "ACCURATE";
0151:
0152:            /**
0153:             * Very simple and lenient scale computation method that conforms to the OGC SLD 
0154:             * specification 1.0, page 26. <br>This method is quite approximative, but should
0155:             * never break and ensure constant scale even on lat/lon unprojected maps (because
0156:             * in that case scale is computed as if the area was along the equator no matter
0157:             * what the real position is).
0158:             */
0159:            public static final String SCALE_OGC = "OGC";
0160:
0161:            private String scaleComputationMethodDEFAULT = SCALE_ACCURATE;
0162:            static {
0163:                COORDS = new Coordinate[5];
0164:                COORDS[0] = new Coordinate(0.0, 0.0);
0165:                COORDS[1] = new Coordinate(5.0, 0.0);
0166:                COORDS[2] = new Coordinate(5.0, 5.0);
0167:                COORDS[3] = new Coordinate(0.0, 5.0);
0168:                COORDS[4] = new Coordinate(0.0, 0.0);
0169:                LINE_GEOM = geomFactory.createLinearRing(COORDS);
0170:                MULTI_LINE_GEOM = geomFactory
0171:                        .createMultiLineString(new LineString[] { LINE_GEOM });
0172:                POLYGON_GEOM = geomFactory.createPolygon(LINE_GEOM,
0173:                        new LinearRing[0]);
0174:                MULTI_POLYGON_GEOM = geomFactory
0175:                        .createMultiPolygon(new Polygon[] { POLYGON_GEOM });
0176:                POINT_GEOM = geomFactory.createPoint(COORDS[2]);
0177:                MULTI_POINT_GEOM = geomFactory.createMultiPoint(COORDS);
0178:            }
0179:
0180:            /**
0181:             * This listener is added to the list of listeners automatically. It should be removed if the
0182:             * default logging is not needed.
0183:             */
0184:            public static final DefaultRenderListener DEFAULT_LISTENER = new DefaultRenderListener();
0185:
0186:            private static final IndexInfo STREAMING_RENDERER_INFO = new IndexInfo(
0187:                    (byte) 0, null, null);
0188:            static int NUM_SAMPLES = 200;
0189:            private RenderingHints hints;
0190:
0191:            /** Factory that will resolve symbolizers into rendered styles */
0192:            private SLDStyleFactory styleFactory = new SLDStyleFactory();
0193:            private boolean renderingStopRequested;
0194:            private boolean concatTransforms;
0195:            private MapContext context;
0196:            LabelCache labelCache = new LabelCacheDefault();
0197:            private ListenerList renderListeners = new ListenerList();
0198:            boolean caching = false;
0199:            private double scaleDenominator;
0200:            DbaseFileHeader dbfheader;
0201:            private Object defaultGeom;
0202:            IndexInfo[] layerIndexInfo;
0203:
0204:            /**
0205:             * Maps between the AttributeType index of the new generated FeatureType and the real
0206:             * attributeType
0207:             */
0208:            int[] attributeIndexing;
0209:
0210:            /** The painter class we use to depict shapes onto the screen */
0211:            private StyledShapePainter painter = new StyledShapePainter(
0212:                    labelCache);
0213:            private Map decimators = new HashMap();
0214:
0215:            /**
0216:             * Text will be rendered using the usual calls gc.drawString/drawGlyphVector.
0217:             * This is a little faster, and more consistent with how the platform renders
0218:             * the text in other applications. The downside is that on most platform the label
0219:             * and its eventual halo are not properly centered.
0220:             */
0221:            public static final String TEXT_RENDERING_STRING = "STRING";
0222:
0223:            /**
0224:             * Text will be rendered using the associated {@link GlyphVector} outline, that is, a {@link Shape}.
0225:             * This ensures perfect centering between the text and the halo, but introduces more text aliasing.
0226:             */
0227:            public static final String TEXT_RENDERING_OUTLINE = "OUTLINE";
0228:
0229:            /**
0230:             * The text rendering method, either TEXT_RENDERING_OUTLINE or TEXT_RENDERING_STRING
0231:             */
0232:            public static final String TEXT_RENDERING_KEY = "textRenderingMethod";
0233:            private String textRenderingModeDEFAULT = TEXT_RENDERING_STRING;
0234:
0235:            public static final String LABEL_CACHE_KEY = "labelCache";
0236:            public static final String FORCE_CRS_KEY = "forceCRS";
0237:            public static final String DPI_KEY = "dpi";
0238:            public static final String DECLARED_SCALE_DENOM_KEY = "declaredScaleDenominator";
0239:            public static final String MEMORY_PRE_LOADING_KEY = "memoryPreloadingEnabled";
0240:            public static final String OPTIMIZED_DATA_LOADING_KEY = "optimizedDataLoadingEnabled";
0241:            public static final String SCALE_COMPUTATION_METHOD_KEY = "scaleComputationMethod";
0242:
0243:            /**
0244:             * "optimizedDataLoadingEnabled" - Boolean  yes/no (see default optimizedDataLoadingEnabledDEFAULT)
0245:             * "memoryPreloadingEnabled"     - Boolean  yes/no (see default memoryPreloadingEnabledDEFAULT)
0246:             * "declaredScaleDenominator"    - Double   the value of the scale denominator to use by the renderer.  
0247:             *                                          by default the value is calculated based on the screen size 
0248:             *                                          and the displayed area of the map.
0249:             *  "dpi"                        - Integer  number of dots per inch of the display 90 DPI is the default (as declared by OGC)      
0250:             *  "forceCRS"                   - CoordinateReferenceSystem declares to the renderer that all layers are of the CRS declared in this hint                               
0251:             *  "labelCache"                 - Declares the label cache that will be used by the renderer.                               
0252:             */
0253:            private Map rendererHints = null;
0254:
0255:            public ShapefileRenderer(MapContext context) {
0256:                setContext(context);
0257:            }
0258:
0259:            public ShapefileRenderer() {
0260:            }
0261:
0262:            public void paint(Graphics2D graphics, Rectangle paintArea,
0263:                    ReferencedEnvelope mapArea) {
0264:                if (mapArea == null || paintArea == null) {
0265:                    LOGGER.info("renderer passed null arguments");
0266:                    return;
0267:                } // Other arguments get checked later
0268:                paint(graphics, paintArea, mapArea, RendererUtilities
0269:                        .worldToScreenTransform(mapArea, paintArea));
0270:            }
0271:
0272:            private DbaseFileHeader getDBFHeader(ShapefileDataStore ds) {
0273:                DbaseFileReader reader = null;
0274:
0275:                try {
0276:                    reader = ShapefileRendererUtil.getDBFReader(ds);
0277:
0278:                    return reader.getHeader();
0279:                } catch (IOException e) {
0280:                    e.printStackTrace();
0281:                } finally {
0282:                    if (reader != null) {
0283:                        try {
0284:                            reader.close();
0285:                        } catch (IOException e) {
0286:                            // TODO Auto-generated catch block
0287:                            e.printStackTrace();
0288:                        }
0289:                    }
0290:                }
0291:
0292:                return null;
0293:            }
0294:
0295:            private void processStylers(Graphics2D graphics,
0296:                    ShapefileDataStore datastore, Query query, Envelope bbox,
0297:                    Rectangle screenSize, MathTransform mt, Style style,
0298:                    IndexInfo info, Transaction transaction, String layerId)
0299:                    throws IOException {
0300:                if (LOGGER.isLoggable(Level.FINE)) {
0301:                    LOGGER.fine("processing "
0302:                            + style.getFeatureTypeStyles().length + " stylers");
0303:                }
0304:
0305:                FeatureTypeStyle[] featureStylers = style
0306:                        .getFeatureTypeStyles();
0307:                FeatureType type;
0308:
0309:                try {
0310:                    type = createFeatureType(query, style, datastore);
0311:                } catch (Exception e) {
0312:                    fireErrorEvent(e);
0313:
0314:                    return;
0315:                }
0316:
0317:                for (int i = 0; i < featureStylers.length; i++) {
0318:                    if (LOGGER.isLoggable(Level.FINE)) {
0319:                        LOGGER.fine("processing style " + i);
0320:                    }
0321:
0322:                    FeatureTypeStyle fts = featureStylers[i];
0323:                    String typeName = datastore.getSchema().getTypeName();
0324:
0325:                    if ((typeName != null)
0326:                            && (datastore.getSchema().isDescendedFrom(null,
0327:                                    fts.getFeatureTypeName()) || typeName
0328:                                    .equalsIgnoreCase(fts.getFeatureTypeName()))) {
0329:                        // get applicable rules at the current scale
0330:                        Rule[] rules = fts.getRules();
0331:                        List ruleList = new ArrayList();
0332:                        List elseRuleList = new ArrayList();
0333:
0334:                        // TODO process filter for geometry expressions and restrict bbox further based on 
0335:                        // the result
0336:
0337:                        for (int j = 0; j < rules.length; j++) {
0338:                            if (LOGGER.isLoggable(Level.FINE)) {
0339:                                LOGGER.fine("processing rule " + j);
0340:                            }
0341:
0342:                            Rule r = rules[j];
0343:
0344:                            // make copy so I don't accidentally modify style
0345:                            DuplicatingStyleVisitor duplicator = new DuplicatingStyleVisitor();
0346:                            r.accept(duplicator);
0347:                            r = (Rule) duplicator.getCopy();
0348:                            if (r.getFilter() != null) {
0349:                                // now reproject the geometries in filter because geoms are retrieved projected to screen space
0350:                                FilterTransformer transformer = new FilterTransformer(
0351:                                        mt);
0352:                                r.setFilter((Filter) r.getFilter().accept(
0353:                                        transformer, null));
0354:                            }
0355:                            if (isWithInScale(r)) {
0356:                                if (r.hasElseFilter()) {
0357:                                    elseRuleList.add(r);
0358:                                } else {
0359:                                    ruleList.add(r);
0360:                                }
0361:                            }
0362:                        }
0363:
0364:                        // process the features according to the rules
0365:                        // TODO: find a better way to declare the scale ranges so that
0366:                        // we
0367:                        // get style caching also between multiple rendering runs
0368:                        NumberRange scaleRange = new NumberRange(
0369:                                scaleDenominator, scaleDenominator);
0370:
0371:                        Set modifiedFIDs = processTransaction(graphics, bbox,
0372:                                mt, datastore, transaction, typeName, query,
0373:                                ruleList, elseRuleList, scaleRange, layerId);
0374:
0375:                        // don't try to read the shapefile if there is nothing to draw
0376:                        if (ruleList.size() > 0 || elseRuleList.size() > 0)
0377:                            processShapefile(graphics, datastore, bbox,
0378:                                    screenSize, mt, info, type, query,
0379:                                    ruleList, elseRuleList, modifiedFIDs,
0380:                                    scaleRange, layerId);
0381:                    }
0382:                }
0383:            }
0384:
0385:            private Set processTransaction(Graphics2D graphics, Envelope bbox,
0386:                    MathTransform transform, DataStore ds,
0387:                    Transaction transaction, String typename, Query query,
0388:                    List ruleList, List elseRuleList, NumberRange scaleRange,
0389:                    String layerId) {
0390:                if (transaction == Transaction.AUTO_COMMIT) {
0391:                    return Collections.EMPTY_SET;
0392:                }
0393:
0394:                TransactionStateDiff state = (TransactionStateDiff) transaction
0395:                        .getState(ds);
0396:
0397:                if (state == null) {
0398:                    return Collections.EMPTY_SET;
0399:                }
0400:
0401:                Set fids = new HashSet();
0402:                Map modified = null;
0403:                Map added = null;
0404:                Diff diff = null;
0405:
0406:                try {
0407:                    diff = state.diff(typename);
0408:                    modified = diff.modified2;
0409:                    added = diff.added;
0410:                    fids = new HashSet();
0411:                } catch (IOException e) {
0412:                    fids = Collections.EMPTY_SET;
0413:                    return fids;
0414:                }
0415:
0416:                if (!diff.isEmpty()) {
0417:                    Feature feature;
0418:
0419:                    for (Iterator modifiedIter = modified.keySet().iterator(), addedIter = added
0420:                            .values().iterator(); modifiedIter.hasNext()
0421:                            || addedIter.hasNext();) {
0422:                        if (renderingStopRequested) {
0423:                            break;
0424:                        }
0425:
0426:                        boolean doElse = true;
0427:                        if (modifiedIter.hasNext()) {
0428:                            String fid = (String) modifiedIter.next();
0429:                            feature = (Feature) modified.get(fid);
0430:                            fids.add(fid);
0431:                        } else {
0432:                            feature = (Feature) addedIter.next();
0433:                        }
0434:
0435:                        if (!query.getFilter().evaluate(feature))
0436:                            continue;
0437:
0438:                        if (feature != TransactionStateDiff.NULL) {
0439:                            // applicable rules
0440:                            for (Iterator it = ruleList.iterator(); it
0441:                                    .hasNext();) {
0442:                                Rule r = (Rule) it.next();
0443:
0444:                                if (LOGGER.isLoggable(Level.FINER)) {
0445:                                    LOGGER.finer("applying rule: "
0446:                                            + r.toString());
0447:                                }
0448:
0449:                                if (LOGGER.isLoggable(Level.FINER)) {
0450:                                    LOGGER.finer("this rule applies ...");
0451:                                }
0452:
0453:                                Filter filter = r.getFilter();
0454:
0455:                                if ((filter == null)
0456:                                        || filter.evaluate(feature)) {
0457:                                    doElse = false;
0458:
0459:                                    if (LOGGER.isLoggable(Level.FINER)) {
0460:                                        LOGGER
0461:                                                .finer("processing Symobolizer ...");
0462:                                    }
0463:
0464:                                    Symbolizer[] symbolizers = r
0465:                                            .getSymbolizers();
0466:
0467:                                    try {
0468:                                        processSymbolizers(graphics, feature,
0469:                                                symbolizers, scaleRange,
0470:                                                transform, layerId);
0471:                                    } catch (Exception e) {
0472:                                        fireErrorEvent(e);
0473:
0474:                                        continue;
0475:                                    }
0476:
0477:                                    if (LOGGER.isLoggable(Level.FINER)) {
0478:                                        LOGGER.finer("... done!");
0479:                                    }
0480:                                }
0481:                            }
0482:
0483:                            if (doElse) {
0484:                                // rules with an else filter
0485:                                if (LOGGER.isLoggable(Level.FINER)) {
0486:                                    LOGGER.finer("rules with an else filter");
0487:                                }
0488:
0489:                                for (Iterator it = elseRuleList.iterator(); it
0490:                                        .hasNext();) {
0491:                                    Rule r = (Rule) it.next();
0492:                                    Symbolizer[] symbolizers = r
0493:                                            .getSymbolizers();
0494:
0495:                                    if (LOGGER.isLoggable(Level.FINER)) {
0496:                                        LOGGER
0497:                                                .finer("processing Symobolizer ...");
0498:                                    }
0499:
0500:                                    try {
0501:                                        processSymbolizers(graphics, feature,
0502:                                                symbolizers, scaleRange,
0503:                                                transform, layerId);
0504:                                    } catch (Exception e) {
0505:                                        fireErrorEvent(e);
0506:
0507:                                        continue;
0508:                                    }
0509:
0510:                                    if (LOGGER.isLoggable(Level.FINER)) {
0511:                                        LOGGER.finer("... done!");
0512:                                    }
0513:                                }
0514:                            }
0515:
0516:                            if (LOGGER.isLoggable(Level.FINER)) {
0517:                                LOGGER.finer("feature rendered event ...");
0518:                            }
0519:                        }
0520:                    }
0521:                }
0522:
0523:                return fids;
0524:            }
0525:
0526:            private void processShapefile(Graphics2D graphics,
0527:                    ShapefileDataStore datastore, Envelope bbox,
0528:                    Rectangle screenSize, MathTransform mt, IndexInfo info,
0529:                    FeatureType type, Query query, List ruleList,
0530:                    List elseRuleList, Set modifiedFIDs,
0531:                    NumberRange scaleRange, String layerId) throws IOException {
0532:                IndexedDbaseFileReader dbfreader = null;
0533:
0534:                // don't waste time processing the dbf file if the only attribute loades is the geometry
0535:                if (type.getAttributeCount() > 1) {
0536:                    try {
0537:                        dbfreader = ShapefileRendererUtil
0538:                                .getDBFReader(datastore);
0539:                    } catch (Exception e) {
0540:                        fireErrorEvent(e);
0541:                    }
0542:                }
0543:
0544:                OpacityFinder opacityFinder = new OpacityFinder(
0545:                        getAcceptableSymbolizers(type.getDefaultGeometry()));
0546:
0547:                for (Iterator iter = ruleList.iterator(); iter.hasNext();) {
0548:                    Rule rule = (Rule) iter.next();
0549:                    rule.accept(opacityFinder);
0550:                }
0551:
0552:                IndexInfo.Reader shpreader = null;
0553:                boolean useJTS = true;
0554:
0555:                try {
0556:                    shpreader = new IndexInfo.Reader(info,
0557:                            ShapefileRendererUtil.getShpReader(datastore, bbox,
0558:                                    screenSize, mt, opacityFinder.hasOpacity,
0559:                                    useJTS), bbox);
0560:                } catch (Exception e) {
0561:                    fireErrorEvent(e);
0562:                    return;
0563:                }
0564:
0565:                FIDReader fidReader = null;
0566:                try {
0567:                    fidReader = ShapefileRendererUtil.getFidReader(datastore,
0568:                            shpreader);
0569:                } catch (Exception e) {
0570:                    fireErrorEvent(e);
0571:                    return;
0572:                }
0573:
0574:                try {
0575:                    while (true) {
0576:                        try {
0577:                            if (renderingStopRequested) {
0578:                                break;
0579:                            }
0580:
0581:                            if (!shpreader.hasNext()) {
0582:                                break;
0583:                            }
0584:
0585:                            boolean doElse = true;
0586:
0587:                            if (LOGGER.isLoggable(Level.FINER)) {
0588:                                LOGGER.fine("trying to read geometry ...");
0589:                            }
0590:
0591:                            String nextFid = fidReader.next();
0592:                            if (modifiedFIDs.contains(nextFid)) {
0593:                                shpreader.next();
0594:                                if (dbfreader != null
0595:                                        && !dbfreader.IsRandomAccessEnabled())
0596:                                    dbfreader.skip();
0597:                                continue;
0598:                            }
0599:                            if (dbfreader != null
0600:                                    && dbfreader.IsRandomAccessEnabled())
0601:                                dbfreader.goTo(shpreader.getRecordNumber());
0602:                            ShapefileReader.Record record = shpreader.next();
0603:
0604:                            Object geom = record.shape();
0605:
0606:                            if (geom == null) {
0607:                                LOGGER.finest("skipping geometry");
0608:                                if (dbfreader != null
0609:                                        && !dbfreader.IsRandomAccessEnabled())
0610:                                    dbfreader.skip();
0611:                                continue;
0612:                            }
0613:
0614:                            Feature feature = createFeature(type, record,
0615:                                    dbfreader, nextFid);
0616:                            if (!query.getFilter().evaluate(feature))
0617:                                continue;
0618:
0619:                            if (renderingStopRequested) {
0620:                                break;
0621:                            }
0622:
0623:                            if (LOGGER.isLoggable(Level.FINEST)) {
0624:                                LOGGER.finest("... done: " + geom.toString());
0625:                            }
0626:
0627:                            if (LOGGER.isLoggable(Level.FINER)) {
0628:                                LOGGER.fine("... done: " + type.getTypeName());
0629:                            }
0630:
0631:                            // applicable rules
0632:                            for (Iterator it = ruleList.iterator(); it
0633:                                    .hasNext();) {
0634:                                Rule r = (Rule) it.next();
0635:
0636:                                if (LOGGER.isLoggable(Level.FINER)) {
0637:                                    LOGGER.finer("applying rule: "
0638:                                            + r.toString());
0639:                                }
0640:
0641:                                if (LOGGER.isLoggable(Level.FINER)) {
0642:                                    LOGGER.finer("this rule applies ...");
0643:                                }
0644:
0645:                                Filter filter = r.getFilter();
0646:
0647:                                if ((filter == null)
0648:                                        || filter.evaluate(feature)) {
0649:                                    doElse = false;
0650:
0651:                                    if (LOGGER.isLoggable(Level.FINER)) {
0652:                                        LOGGER
0653:                                                .finer("processing Symobolizer ...");
0654:                                    }
0655:
0656:                                    Symbolizer[] symbolizers = r
0657:                                            .getSymbolizers();
0658:
0659:                                    processSymbolizers(graphics, feature, geom,
0660:                                            symbolizers, scaleRange, useJTS,
0661:                                            layerId);
0662:
0663:                                    if (LOGGER.isLoggable(Level.FINER)) {
0664:                                        LOGGER.finer("... done!");
0665:                                    }
0666:                                }
0667:                            }
0668:
0669:                            if (doElse) {
0670:                                // rules with an else filter
0671:                                if (LOGGER.isLoggable(Level.FINER)) {
0672:                                    LOGGER.finer("rules with an else filter");
0673:                                }
0674:
0675:                                for (Iterator it = elseRuleList.iterator(); it
0676:                                        .hasNext();) {
0677:                                    Rule r = (Rule) it.next();
0678:                                    Symbolizer[] symbolizers = r
0679:                                            .getSymbolizers();
0680:
0681:                                    if (LOGGER.isLoggable(Level.FINER)) {
0682:                                        LOGGER
0683:                                                .finer("processing Symobolizer ...");
0684:                                    }
0685:
0686:                                    processSymbolizers(graphics, feature, geom,
0687:                                            symbolizers, scaleRange, useJTS,
0688:                                            layerId);
0689:
0690:                                    if (LOGGER.isLoggable(Level.FINER)) {
0691:                                        LOGGER.finer("... done!");
0692:                                    }
0693:                                }
0694:                            }
0695:
0696:                            if (LOGGER.isLoggable(Level.FINER)) {
0697:                                LOGGER.finer("feature rendered event ...");
0698:                            }
0699:                        } catch (Exception e) {
0700:                            fireErrorEvent(e);
0701:                        }
0702:                    }
0703:                } finally {
0704:                    try {
0705:                        if (dbfreader != null) {
0706:                            dbfreader.close();
0707:                        }
0708:                    } finally {
0709:                        try {
0710:                            if (shpreader != null) {
0711:                                shpreader.close();
0712:                            }
0713:                        } finally {
0714:                            if (fidReader == null)
0715:                                fidReader.close();
0716:                        }
0717:                    }
0718:                }
0719:            }
0720:
0721:            private Class[] getAcceptableSymbolizers(
0722:                    GeometryAttributeType defaultGeometry) {
0723:                if (Polygon.class.isAssignableFrom(defaultGeometry.getType())
0724:                        || MultiPolygon.class.isAssignableFrom(defaultGeometry
0725:                                .getType())) {
0726:                    return new Class[] { PointSymbolizer.class,
0727:                            LineSymbolizer.class, PolygonSymbolizer.class };
0728:                }
0729:
0730:                return new Class[] { PointSymbolizer.class,
0731:                        LineSymbolizer.class };
0732:            }
0733:
0734:            Feature createFeature(FeatureType type, Record record,
0735:                    DbaseFileReader dbfreader, String id) throws Exception {
0736:                if (type.getAttributeCount() == 1) {
0737:                    return type.create(new Object[] { getGeom(record.shape(),
0738:                            type.getDefaultGeometry()) }, id);
0739:                } else {
0740:                    DbaseFileHeader header = dbfreader.getHeader();
0741:
0742:                    Object[] all = dbfreader.readEntry();
0743:                    Object[] values = new Object[type.getAttributeCount()];
0744:
0745:                    for (int i = 0; i < (values.length - 1); i++) {
0746:                        values[i] = all[attributeIndexing[i]];
0747:
0748:                        if (header.getFieldName(attributeIndexing[i]).equals(
0749:                                type.getAttributeType(i))) {
0750:                            System.out.println("ok");
0751:                        }
0752:                    }
0753:
0754:                    values[values.length - 1] = getGeom(record.shape(), type
0755:                            .getDefaultGeometry());
0756:
0757:                    return type.create(values, id);
0758:                }
0759:            }
0760:
0761:            /**
0762:             * Return provided geom; or use a default value if null.
0763:             * 
0764:             * @param geom Provided Geometry as read from record.shape()
0765:             * @param defaultGeometry GeometryAttributeType used to determine default value
0766:             * @return provided geom or default value if null
0767:             */
0768:            private Object getGeom(Object geom,
0769:                    GeometryAttributeType defaultGeometry) {
0770:                if (geom instanceof  Geometry) {
0771:                    return geom;
0772:                }
0773:                return getGeom(defaultGeometry);
0774:            }
0775:
0776:            /**
0777:             * This class keeps a couple of default geometries on hand to use
0778:             * when making a feature with default values.
0779:             * 
0780:             * @param defaultGeometry
0781:             * @return placeholder to use as a default while waiting for a real geometry.
0782:             */
0783:            private Object getGeom(GeometryAttributeType defaultGeometry) {
0784:                if (MultiPolygon.class.isAssignableFrom(defaultGeometry
0785:                        .getType())) {
0786:                    return MULTI_POLYGON_GEOM;
0787:                } else if (MultiLineString.class
0788:                        .isAssignableFrom(defaultGeometry.getType())) {
0789:                    return MULTI_LINE_GEOM;
0790:                } else if (Point.class.isAssignableFrom(defaultGeometry
0791:                        .getType())) {
0792:                    return POINT_GEOM;
0793:                } else if (MultiPoint.class.isAssignableFrom(defaultGeometry
0794:                        .getType())) {
0795:                    return MULTI_POINT_GEOM;
0796:                }
0797:                return null; // we don't have a good default value - null will need to do
0798:            }
0799:
0800:            /**
0801:             * DOCUMENT ME!
0802:             * 
0803:             * @param query
0804:             * @param style
0805:             * @param schema DOCUMENT ME!
0806:             * @return
0807:             * @throws FactoryConfigurationError
0808:             * @throws SchemaException
0809:             */
0810:            FeatureType createFeatureType(Query query, Style style,
0811:                    ShapefileDataStore ds) throws SchemaException, IOException {
0812:                FeatureType schema = ds.getSchema();
0813:                String[] attributes = findStyleAttributes(
0814:                        (query == null) ? Query.ALL : query, style, schema);
0815:                AttributeType[] types = new AttributeType[attributes.length];
0816:                attributeIndexing = new int[attributes.length];
0817:
0818:                if (attributes.length == 1
0819:                        && attributes[0].equals(schema.getDefaultGeometry()
0820:                                .getLocalName())) {
0821:                    types[0] = schema.getAttributeType(attributes[0]);
0822:                } else {
0823:                    dbfheader = getDBFHeader(ds);
0824:                    for (int i = 0; i < types.length; i++) {
0825:                        types[i] = schema.getAttributeType(attributes[i]);
0826:
0827:                        for (int j = 0; j < dbfheader.getNumFields(); j++) {
0828:                            if (dbfheader.getFieldName(j).equals(attributes[i])) {
0829:                                attributeIndexing[i] = j;
0830:
0831:                                break;
0832:                            }
0833:                        }
0834:                    }
0835:                }
0836:
0837:                FeatureType type = FeatureTypeBuilder.newFeatureType(types,
0838:                        schema.getTypeName(), schema.getNamespace(), false,
0839:                        null, schema.getDefaultGeometry());
0840:
0841:                return type;
0842:            }
0843:
0844:            /**
0845:             * Inspects the <code>MapLayer</code>'s style and retrieves it's needed attribute names,
0846:             * returning at least the default geometry attribute name.
0847:             * 
0848:             * @param query DOCUMENT ME!
0849:             * @param style the <code>Style</code> to determine the needed attributes from
0850:             * @param schema the featuresource schema
0851:             * @return the minimun set of attribute names needed to render <code>layer</code>
0852:             */
0853:            private String[] findStyleAttributes(final Query query,
0854:                    Style style, FeatureType schema) {
0855:                StyleAttributeExtractor sae = new StyleAttributeExtractor() {
0856:                    public void visit(Rule rule) {
0857:
0858:                        DuplicatingStyleVisitor dupeStyleVisitor = new DuplicatingStyleVisitor();
0859:                        dupeStyleVisitor.visit(rule);
0860:                        Rule clone = (Rule) dupeStyleVisitor.getCopy();
0861:
0862:                        super .visit(clone);
0863:                    }
0864:                };
0865:
0866:                sae.visit(style);
0867:
0868:                FilterAttributeExtractor qae = new FilterAttributeExtractor();
0869:                query.getFilter().accept(qae, null);
0870:                Set ftsAttributes = new HashSet(sae.getAttributeNameSet());
0871:                ftsAttributes.addAll(qae.getAttributeNameSet());
0872:                // the code following assumes we won't extract the default geometry, and that's
0873:                // most of the time true, but fails if the filter or the style uses it.
0874:                ftsAttributes
0875:                        .remove(schema.getDefaultGeometry().getLocalName());
0876:                return (String[]) ftsAttributes.toArray(new String[0]);
0877:            }
0878:
0879:            /**
0880:             * DOCUMENT ME!
0881:             * 
0882:             * @param graphics
0883:             * @param feature DOCUMENT ME!
0884:             * @param geom
0885:             * @param symbolizers
0886:             * @param scaleRange
0887:             * @param layerId 
0888:             */
0889:            private void processSymbolizers(Graphics2D graphics,
0890:                    Feature feature, Object geom, Symbolizer[] symbolizers,
0891:                    NumberRange scaleRange, boolean isJTS, String layerId) {
0892:                for (int m = 0; m < symbolizers.length; m++) {
0893:                    if (LOGGER.isLoggable(Level.FINER)) {
0894:                        LOGGER.finer("applying symbolizer " + symbolizers[m]);
0895:                    }
0896:
0897:                    if (renderingStopRequested) {
0898:                        break;
0899:                    }
0900:
0901:                    if (symbolizers[m] instanceof  TextSymbolizer) {
0902:                        try {
0903:                            labelCache.put(layerId,
0904:                                    (TextSymbolizer) symbolizers[m], feature,
0905:                                    new LiteShape2(
0906:                                            feature.getDefaultGeometry(), null,
0907:                                            null, false, false), scaleRange);
0908:                        } catch (Exception e) {
0909:                            fireErrorEvent(e);
0910:                        }
0911:                    } else {
0912:                        try {
0913:                            Style2D style = styleFactory.createStyle(feature,
0914:                                    symbolizers[m], scaleRange);
0915:                            if (isJTS) {
0916:                                painter.paint(graphics, new LiteShape2(
0917:                                        (Geometry) geom, null, null, false,
0918:                                        false), style, scaleDenominator);
0919:                            } else {
0920:                                painter.paint(graphics,
0921:                                        getShape((SimpleGeometry) geom), style,
0922:                                        scaleDenominator);
0923:                            }
0924:                        } catch (Exception e) {
0925:                            fireErrorEvent(e);
0926:                        }
0927:                    }
0928:
0929:                }
0930:                fireFeatureRenderedEvent(feature);
0931:            }
0932:
0933:            /**
0934:             * Applies each of a set of symbolizers in turn to a given feature.
0935:             * <p>
0936:             * This is an internal method and should only be called by processStylers.
0937:             * </p>
0938:             * 
0939:             * @param graphics
0940:             * @param feature The feature to be rendered
0941:             * @param symbolizers An array of symbolizers which actually perform the rendering.
0942:             * @param scaleRange The scale range we are working on... provided in order to make the style
0943:             *        factory happy
0944:             * @param transform DOCUMENT ME!
0945:             * @param layerId 
0946:             * @throws TransformException
0947:             * @throws FactoryException
0948:             */
0949:            private void processSymbolizers(final Graphics2D graphics,
0950:                    final Feature feature, final Symbolizer[] symbolizers,
0951:                    Range scaleRange, MathTransform transform, String layerId)
0952:                    throws TransformException, FactoryException {
0953:                LiteShape2 shape;
0954:
0955:                for (int m = 0; m < symbolizers.length; m++) {
0956:                    if (LOGGER.isLoggable(Level.FINER)) {
0957:                        LOGGER.finer("applying symbolizer " + symbolizers[m]);
0958:                    }
0959:
0960:                    Geometry g = feature.getDefaultGeometry();
0961:                    shape = new LiteShape2(g, transform,
0962:                            getDecimator(transform), false);
0963:
0964:                    if (symbolizers[m] instanceof  TextSymbolizer) {
0965:                        labelCache.put(layerId,
0966:                                (TextSymbolizer) symbolizers[m], feature,
0967:                                shape, scaleRange);
0968:                    } else {
0969:                        Style2D style = styleFactory.createStyle(feature,
0970:                                symbolizers[m], scaleRange);
0971:                        painter.paint(graphics, shape, style, scaleDenominator);
0972:                    }
0973:                }
0974:
0975:                fireFeatureRenderedEvent(feature);
0976:            }
0977:
0978:            /**
0979:             * DOCUMENT ME!
0980:             * 
0981:             * @param mathTransform DOCUMENT ME!
0982:             * @return
0983:             * @throws org.opengis.referencing.operation.NoninvertibleTransformException
0984:             */
0985:            private Decimator getDecimator(MathTransform mathTransform)
0986:                    throws org.opengis.referencing.operation.NoninvertibleTransformException {
0987:                Decimator decimator = null;
0988:
0989:                if (mathTransform != null)
0990:                    decimator = (Decimator) decimators.get(mathTransform);
0991:
0992:                if (decimator == null) {
0993:                    decimator = new Decimator(mathTransform.inverse());
0994:
0995:                    decimators.put(mathTransform, decimator);
0996:                }
0997:
0998:                return decimator;
0999:            }
1000:
1001:            //
1002:            //    /**
1003:            //     * Creates a JTS shape that is an approximation of the SImpleGeometry. This is ONLY use for
1004:            //     * labelling and is only created if a text symbolizer is part of the current style.
1005:            //     * 
1006:            //     * @param geom the geometry to wrap
1007:            //     * @return
1008:            //     * @throws TransformException
1009:            //     * @throws FactoryException
1010:            //     * @throws RuntimeException DOCUMENT ME!
1011:            //     */
1012:            //    LiteShape2 getLiteShape2( SimpleGeometry geom ) throws TransformException, FactoryException {
1013:            //        Geometry jtsGeom;
1014:            //        if ((geom.type == ShapeType.POLYGON) || (geom.type == ShapeType.POLYGONM)
1015:            //                || (geom.type == ShapeType.POLYGONZ)) {
1016:            //            double[] points = getPointSample(geom, true);
1017:            //            CoordinateSequence seq = new LiteCoordinateSequence(points);
1018:            //            Polygon poly;
1019:            //
1020:            //            try {
1021:            //                poly = geomFactory.createPolygon(geomFactory.createLinearRing(seq),
1022:            //                        new LinearRing[]{});
1023:            //            } catch (Exception e) {
1024:            //                throw new RuntimeException(e);
1025:            //            }
1026:            //
1027:            //            jtsGeom = geomFactory.createMultiPolygon(new Polygon[]{poly});
1028:            //        } else if ((geom.type == ShapeType.ARC) || (geom.type == ShapeType.ARCM)
1029:            //                || (geom.type == ShapeType.ARCZ)) {
1030:            //            double[] points = getPointSample(geom, false);
1031:            //            CoordinateSequence seq = new LiteCoordinateSequence(points);
1032:            //            jtsGeom = geomFactory.createMultiLineString(new LineString[]{geomFactory
1033:            //                    .createLineString(seq)});
1034:            //        } else if ((geom.type == ShapeType.MULTIPOINT) || (geom.type == ShapeType.MULTIPOINTM)
1035:            //                || (geom.type == ShapeType.MULTIPOINTZ)) {
1036:            //            double[] points = getPointSample(geom, false);
1037:            //            CoordinateSequence seq = new LiteCoordinateSequence(points);
1038:            //            jtsGeom = geomFactory.createMultiPoint(seq);
1039:            //        } else {
1040:            //            jtsGeom = geomFactory.createPoint(new Coordinate(geom.coords[0][0], geom.coords[0][1]));
1041:            //        }
1042:            //
1043:            //        LiteShape2 shape = new LiteShape2(jtsGeom, null, null, false);
1044:            //
1045:            //        return shape;
1046:            //    }
1047:
1048:            //    /**
1049:            //     * takes a random sampling from the geometry. Only uses the larges part of the geometry.
1050:            //     * 
1051:            //     * @param geom
1052:            //     * @param isPolygon DOCUMENT ME!
1053:            //     * @return
1054:            //     */
1055:            //    private double[] getPointSample( SimpleGeometry geom, boolean isPolygon ) {
1056:            //        int largestPart = 0;
1057:            //
1058:            //        for( int i = 0; i < geom.coords.length; i++ ) {
1059:            //            if (geom.coords[i].length > geom.coords[largestPart].length) {
1060:            //                largestPart = i;
1061:            //            }
1062:            //        }
1063:            //
1064:            //        return geom.coords[largestPart];
1065:            //    }
1066:
1067:            /**
1068:             * DOCUMENT ME!
1069:             * 
1070:             * @param geom
1071:             * @return
1072:             */
1073:            private Shape getShape(SimpleGeometry geom) {
1074:                if ((geom.type == ShapeType.ARC)
1075:                        || (geom.type == ShapeType.ARCM)
1076:                        || (geom.type == ShapeType.ARCZ)) {
1077:                    return new MultiLineShape(geom);
1078:                }
1079:
1080:                if ((geom.type == ShapeType.POLYGON)
1081:                        || (geom.type == ShapeType.POLYGONM)
1082:                        || (geom.type == ShapeType.POLYGONZ)) {
1083:                    return new PolygonShape(geom);
1084:                }
1085:
1086:                if ((geom.type == ShapeType.POINT)
1087:                        || (geom.type == ShapeType.POINTM)
1088:                        || (geom.type == ShapeType.POINTZ)
1089:                        || (geom.type == ShapeType.MULTIPOINT)
1090:                        || (geom.type == ShapeType.MULTIPOINTM)
1091:                        || (geom.type == ShapeType.MULTIPOINTZ)) {
1092:                    return new MultiPointShape(geom);
1093:                }
1094:
1095:                return null;
1096:            }
1097:
1098:            /**
1099:             * Checks if a rule can be triggered at the current scale level
1100:             * 
1101:             * @param r The rule
1102:             * @return true if the scale is compatible with the rule settings
1103:             */
1104:            private boolean isWithInScale(Rule r) {
1105:                return ((r.getMinScaleDenominator() - TOLERANCE) <= scaleDenominator)
1106:                        && ((r.getMaxScaleDenominator() + TOLERANCE) > scaleDenominator);
1107:            }
1108:
1109:            /**
1110:             * adds a listener that responds to error events of feature rendered events.
1111:             * 
1112:             * @param listener the listener to add.
1113:             * @see RenderListener
1114:             */
1115:            public void addRenderListener(RenderListener listener) {
1116:                renderListeners.add(listener);
1117:            }
1118:
1119:            /**
1120:             * Removes a render listener.
1121:             * 
1122:             * @param listener the listener to remove.
1123:             * @see RenderListener
1124:             */
1125:            public void removeRenderListener(RenderListener listener) {
1126:                renderListeners.remove(listener);
1127:            }
1128:
1129:            private void fireFeatureRenderedEvent(Feature feature) {
1130:                Object[] objects = renderListeners.getListeners();
1131:
1132:                for (int i = 0; i < objects.length; i++) {
1133:                    RenderListener listener = (RenderListener) objects[i];
1134:                    listener.featureRenderer(feature);
1135:                }
1136:            }
1137:
1138:            private void fireErrorEvent(Exception e) {
1139:                Object[] objects = renderListeners.getListeners();
1140:
1141:                for (int i = 0; i < objects.length; i++) {
1142:                    RenderListener listener = (RenderListener) objects[i];
1143:                    listener.errorOccurred(e);
1144:                }
1145:            }
1146:
1147:            /**
1148:             * Setter for property scaleDenominator.
1149:             * 
1150:             * @param scaleDenominator New value of property scaleDenominator.
1151:             */
1152:            protected void setScaleDenominator(double scaleDenominator) {
1153:                this .scaleDenominator = scaleDenominator;
1154:            }
1155:
1156:            /**
1157:             * If you call this method from another thread than the one that called <code>paint</code> or
1158:             * <code>render</code> the rendering will be forcefully stopped before termination
1159:             */
1160:            public void stopRendering() {
1161:                renderingStopRequested = true;
1162:                labelCache.stop();
1163:            }
1164:
1165:            /**
1166:             * DOCUMENT ME!
1167:             * 
1168:             * @return Returns the caching.
1169:             */
1170:            public boolean isCaching() {
1171:                return caching;
1172:            }
1173:
1174:            /**
1175:             * DOCUMENT ME!
1176:             * 
1177:             * @param caching The caching to set.
1178:             */
1179:            public void setCaching(boolean caching) {
1180:                this .caching = caching;
1181:            }
1182:
1183:            public MapContext getContext() {
1184:                return context;
1185:            }
1186:
1187:            public boolean isConcatTransforms() {
1188:                return concatTransforms;
1189:            }
1190:
1191:            public void setConcatTransforms(boolean concatTransforms) {
1192:                this .concatTransforms = concatTransforms;
1193:            }
1194:
1195:            public IndexInfo useIndex(ShapefileDataStore ds)
1196:                    throws IOException, StoreException {
1197:                IndexInfo info;
1198:                String filename = null;
1199:                URL url = ShapefileRendererUtil.getshpURL(ds);
1200:
1201:                if (url == null) {
1202:                    throw new NullPointerException(
1203:                            "Null URL for ShapefileDataSource");
1204:                }
1205:
1206:                try {
1207:                    filename = java.net.URLDecoder.decode(url.toString(),
1208:                            "US-ASCII");
1209:                } catch (java.io.UnsupportedEncodingException use) {
1210:                    throw new java.net.MalformedURLException(
1211:                            "Unable to decode " + url + " cause "
1212:                                    + use.getMessage());
1213:                }
1214:
1215:                filename = filename.substring(0, filename.length() - 4);
1216:
1217:                String grxext = ".grx";
1218:                String qixext = ".qix";
1219:
1220:                if (ds.isLocal()) {
1221:                    File grxTree = new File(new URL(filename + grxext)
1222:                            .getPath());
1223:                    File qixTree = new File(new URL(filename + qixext)
1224:                            .getPath());
1225:                    URL shx = new URL(filename + ".shx");
1226:
1227:                    if (!new File(shx.getPath()).exists()) {
1228:                        info = new IndexInfo(IndexInfo.TREE_NONE, null, null);
1229:                    } else if (qixTree.exists()) {
1230:                        info = new IndexInfo(IndexInfo.QUAD_TREE, new URL(
1231:                                filename + qixext), shx);
1232:                        LOGGER.fine("Using quad tree");
1233:                    } else if (grxTree.exists()) {
1234:                        info = new IndexInfo(IndexInfo.R_TREE, new URL(filename
1235:                                + grxext), shx);
1236:                        LOGGER.fine("Using r-tree");
1237:                    } else {
1238:                        info = new IndexInfo(IndexInfo.TREE_NONE, null, null);
1239:                        LOGGER.fine("No indexing");
1240:                    }
1241:                } else {
1242:                    info = new IndexInfo(IndexInfo.TREE_NONE, null, null);
1243:                }
1244:
1245:                return info;
1246:            }
1247:
1248:            /**
1249:             * By default ignores all feature renderered events and logs all exceptions as severe.
1250:             */
1251:            private static class DefaultRenderListener implements 
1252:                    RenderListener {
1253:                /**
1254:                 * @see org.geotools.renderer.lite.RenderListener#featureRenderer(org.geotools.feature.Feature)
1255:                 */
1256:                public void featureRenderer(Feature feature) {
1257:                    // do nothing.
1258:                }
1259:
1260:                /**
1261:                 * @see org.geotools.renderer.lite.RenderListener#errorOccurred(java.lang.Exception)
1262:                 */
1263:                public void errorOccurred(Exception e) {
1264:                    LOGGER.log(Level.SEVERE, e.getMessage(), e);
1265:                }
1266:            }
1267:
1268:            public void setJava2DHints(RenderingHints hints) {
1269:                this .hints = hints;
1270:            }
1271:
1272:            public RenderingHints getJava2DHints() {
1273:                return hints;
1274:            }
1275:
1276:            public void setRendererHints(Map hints) {
1277:                if (hints != null && hints.containsKey(LABEL_CACHE_KEY)) {
1278:                    LabelCache cache = (LabelCache) hints.get(LABEL_CACHE_KEY);
1279:                    if (cache == null)
1280:                        throw new NullPointerException(
1281:                                "Label_Cache_Hint has a null value for the labelcache");
1282:
1283:                    this .labelCache = cache;
1284:                    this .painter = new StyledShapePainter(cache);
1285:                }
1286:                rendererHints = hints;
1287:            }
1288:
1289:            public Map getRendererHints() {
1290:                return rendererHints;
1291:            }
1292:
1293:            public void setContext(MapContext context) {
1294:                if (context == null) {
1295:                    context = new DefaultMapContext(DefaultGeographicCRS.WGS84);
1296:                }
1297:
1298:                this .context = context;
1299:
1300:                MapLayer[] layers = context.getLayers();
1301:                layerIndexInfo = new IndexInfo[layers.length];
1302:
1303:                for (int i = 0; i < layers.length; i++) {
1304:                    DataStore ds = layers[i].getFeatureSource().getDataStore();
1305:                    if (ds instanceof  ShapefileDataStore) {
1306:
1307:                        ShapefileDataStore sds = (ShapefileDataStore) ds;
1308:
1309:                        try {
1310:                            layerIndexInfo[i] = useIndex(sds);
1311:                        } catch (Exception e) {
1312:                            layerIndexInfo[i] = new IndexInfo(
1313:                                    IndexInfo.TREE_NONE, null, null);
1314:                            LOGGER.fine("Exception while trying to use index"
1315:                                    + e.getLocalizedMessage());
1316:                        }
1317:                    } else {
1318:                        layerIndexInfo[i] = STREAMING_RENDERER_INFO;
1319:                    }
1320:                }
1321:            }
1322:
1323:            public void paint(Graphics2D graphics, Rectangle paintArea,
1324:                    AffineTransform worldToScreen) {
1325:                if (worldToScreen == null || paintArea == null) {
1326:                    LOGGER.info("renderer passed null arguments");
1327:                    return;
1328:                } // Other arguments get checked later
1329:                // First, create the bbox in real world coordinates
1330:                ReferencedEnvelope mapArea;
1331:                try {
1332:                    mapArea = RendererUtilities.createMapEnvelope(paintArea,
1333:                            worldToScreen, getContext()
1334:                                    .getCoordinateReferenceSystem());
1335:                    paint(graphics, paintArea, mapArea, worldToScreen);
1336:                } catch (NoninvertibleTransformException e) {
1337:                    fireErrorEvent(new Exception(
1338:                            "Can't create pixel to world transform", e));
1339:                }
1340:            }
1341:
1342:            public void paint(Graphics2D graphics, Rectangle paintArea,
1343:                    ReferencedEnvelope envelope, AffineTransform transform) {
1344:
1345:                if (hints != null) {
1346:                    graphics.setRenderingHints(hints);
1347:                }
1348:
1349:                if ((graphics == null) || (paintArea == null)) {
1350:                    LOGGER.info("renderer passed null arguments");
1351:
1352:                    return;
1353:                }
1354:
1355:                // reset the abort flag
1356:                renderingStopRequested = false;
1357:
1358:                if (LOGGER.isLoggable(Level.FINE)) {
1359:                    LOGGER.fine("Affine Transform is " + transform);
1360:                }
1361:
1362:                /*
1363:                 * If we are rendering to a component which has already set up some form of transformation
1364:                 * then we can concatenate our transformation to it. An example of this is the ZoomPane
1365:                 * component of the swinggui module.
1366:                 */
1367:                if (concatTransforms) {
1368:                    AffineTransform atg = graphics.getTransform();
1369:
1370:                    // graphics.setTransform(new AffineTransform());
1371:                    atg.concatenate(transform);
1372:                    transform = atg;
1373:                }
1374:
1375:                try {
1376:                    setScaleDenominator(computeScale(envelope, context
1377:                            .getCoordinateReferenceSystem(), paintArea,
1378:                            this .rendererHints));
1379:                } catch (Exception e) // probably either (1) no CRS (2) error xforming
1380:                {
1381:                    LOGGER
1382:                            .throwing(
1383:                                    "RendererUtilities",
1384:                                    "calculateScale(envelope, coordinateReferenceSystem, imageWidth, imageHeight, hints)",
1385:                                    e);
1386:                    setScaleDenominator(1 / transform.getScaleX()); // DJB old method - the best we can do
1387:                }
1388:
1389:                MapLayer[] layers = context.getLayers();
1390:
1391:                // get detstination CRS
1392:                CoordinateReferenceSystem destinationCrs = context
1393:                        .getCoordinateReferenceSystem();
1394:                labelCache.start();
1395:                labelCache.clear();
1396:                if (labelCache instanceof  LabelCacheDefault) {
1397:                    boolean outlineEnabled = TEXT_RENDERING_OUTLINE
1398:                            .equals(getTextRenderingMethod());
1399:                    ((LabelCacheDefault) labelCache)
1400:                            .setOutlineRenderingEnabled(outlineEnabled);
1401:                }
1402:                for (int i = 0; i < layers.length; i++) {
1403:                    MapLayer currLayer = layers[i];
1404:
1405:                    if (!currLayer.isVisible()) {
1406:                        // Only render layer when layer is visible
1407:                        continue;
1408:                    }
1409:
1410:                    if (renderingStopRequested) {
1411:                        return;
1412:                    }
1413:
1414:                    if (layerIndexInfo[i] == STREAMING_RENDERER_INFO) {
1415:                        renderWithStreamingRenderer(currLayer, graphics,
1416:                                paintArea, envelope, transform);
1417:                        continue;
1418:                    }
1419:                    labelCache.startLayer("" + i);
1420:
1421:                    ReferencedEnvelope bbox = envelope;
1422:
1423:                    try {
1424:                        GeometryAttributeType geom = currLayer
1425:                                .getFeatureSource().getSchema()
1426:                                .getDefaultGeometry();
1427:
1428:                        CoordinateReferenceSystem dataCRS;
1429:                        if (getForceCRSHint() == null)
1430:                            dataCRS = geom.getCoordinateSystem();
1431:                        else
1432:                            dataCRS = getForceCRSHint();
1433:
1434:                        MathTransform mt;
1435:                        CoordinateOperation op;
1436:
1437:                        try {
1438:                            op = CRS.getCoordinateOperationFactory(true)
1439:                                    .createOperation(dataCRS, destinationCrs);
1440:                            mt = op.getMathTransform();
1441:                            bbox = bbox.transform(dataCRS, true, 10);
1442:                        } catch (Exception e) {
1443:                            op = null;
1444:                            mt = null;
1445:                        }
1446:
1447:                        MathTransform at = ReferencingFactoryFinder
1448:                                .getMathTransformFactory(null)
1449:                                .createAffineTransform(
1450:                                        new GeneralMatrix(transform));
1451:
1452:                        if (mt == null) {
1453:                            mt = at;
1454:                        } else {
1455:                            mt = ReferencingFactoryFinder
1456:                                    .getMathTransformFactory(null)
1457:                                    .createConcatenatedTransform(mt, at);
1458:                        }
1459:
1460:                        // dbfheader must be set so that the attributes required for theming can be read in.
1461:                        ShapefileDataStore ds = (ShapefileDataStore) currLayer
1462:                                .getFeatureSource().getDataStore();
1463:
1464:                        // graphics.setTransform(transform);
1465:                        // extract the feature type stylers from the style object
1466:                        // and process them
1467:
1468:                        Transaction transaction = Transaction.AUTO_COMMIT;
1469:
1470:                        if (currLayer.getFeatureSource() instanceof  FeatureStore) {
1471:                            transaction = ((FeatureStore) currLayer
1472:                                    .getFeatureSource()).getTransaction();
1473:                        }
1474:
1475:                        DefaultQuery query = new DefaultQuery(currLayer
1476:                                .getQuery());
1477:                        if (query.getFilter() != null) {
1478:                            // now reproject the geometries in filter because geoms are retrieved projected to screen space
1479:                            FilterTransformer transformer = new FilterTransformer(
1480:                                    dataCRS, destinationCrs, mt);
1481:                            query.setFilter((Filter) query.getFilter().accept(
1482:                                    transformer, null));
1483:                        }
1484:
1485:                        // by processing the filter we can further restrict the maximum bounds that are
1486:                        // required.  For example if a filter 
1487:                        //BoundsExtractor extractor=new BoundsExtractor(bbox);
1488:                        //if( query.getFilter()!=null )
1489:                        //    query.getFilter().accept(extractor);
1490:                        //
1491:                        //processStylers(graphics, ds, query, extractor.getIntersection(), paintArea,
1492:                        //        mt, currLayer.getStyle(), layerIndexInfo[i], transaction);
1493:                        processStylers(graphics, ds, query, bbox, paintArea,
1494:                                mt, currLayer.getStyle(), layerIndexInfo[i],
1495:                                transaction, "" + i);
1496:                    } catch (Exception exception) {
1497:                        fireErrorEvent(new Exception(
1498:                                "Exception rendering layer " + currLayer,
1499:                                exception));
1500:                    }
1501:
1502:                    labelCache.endLayer("" + i, graphics, paintArea);
1503:                }
1504:
1505:                labelCache.end(graphics, paintArea);
1506:                LOGGER.fine("Style cache hit ratio: "
1507:                        + styleFactory.getHitRatio() + " , hits "
1508:                        + styleFactory.getHits() + ", requests "
1509:                        + styleFactory.getRequests());
1510:            }
1511:
1512:            /**
1513:             * Returns the text rendering method
1514:             */
1515:            private String getTextRenderingMethod() {
1516:                if (rendererHints == null)
1517:                    return textRenderingModeDEFAULT;
1518:                String result = (String) rendererHints.get(TEXT_RENDERING_KEY);
1519:                if (result == null)
1520:                    return textRenderingModeDEFAULT;
1521:                return result;
1522:            }
1523:
1524:            /**
1525:             * <p>
1526:             * Returns scale computation algorithm to be used. 
1527:             * </p>
1528:             */
1529:            private String getScaleComputationMethod() {
1530:                if (rendererHints == null)
1531:                    return scaleComputationMethodDEFAULT;
1532:                String result = (String) rendererHints
1533:                        .get(SCALE_COMPUTATION_METHOD_KEY);
1534:                if (result == null)
1535:                    return scaleComputationMethodDEFAULT;
1536:                return result;
1537:            }
1538:
1539:            private double computeScale(ReferencedEnvelope envelope,
1540:                    CoordinateReferenceSystem crs, Rectangle paintArea,
1541:                    Map hints) {
1542:                if (getScaleComputationMethod().equals(SCALE_ACCURATE)) {
1543:                    try {
1544:                        return RendererUtilities.calculateScale(envelope,
1545:                                paintArea.width, paintArea.height, hints);
1546:                    } catch (Exception e) // probably either (1) no CRS (2) error xforming
1547:                    {
1548:                        LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
1549:                    }
1550:                }
1551:                return RendererUtilities.calculateOGCScale(envelope,
1552:                        paintArea.width, hints);
1553:            }
1554:
1555:            private void renderWithStreamingRenderer(MapLayer layer,
1556:                    Graphics2D graphics, Rectangle paintArea,
1557:                    ReferencedEnvelope envelope, AffineTransform transform) {
1558:                MapContext context = null;
1559:                try {
1560:                    context = new DefaultMapContext(new MapLayer[] { layer },
1561:                            envelope.getCoordinateReferenceSystem());
1562:                    StreamingRenderer renderer = new StreamingRenderer();
1563:                    renderer.setContext(context);
1564:                    renderer.setJava2DHints(getJava2DHints());
1565:                    Map rendererHints2 = new HashMap(
1566:                            getRendererHints() != null ? getRendererHints()
1567:                                    : Collections.EMPTY_MAP);
1568:                    rendererHints2.put(LABEL_CACHE_KEY,
1569:                            new IntegratingLabelCache(labelCache));
1570:                    renderer.setRendererHints(rendererHints2);
1571:                    renderer.paint(graphics, paintArea, envelope, transform);
1572:                } finally {
1573:                    if (context != null)
1574:                        context.clearLayerList();
1575:                }
1576:            }
1577:
1578:            /**
1579:             * If the forceCRS hint is set then return the value.
1580:             * @return the value of the forceCRS hint or null
1581:             */
1582:            private CoordinateReferenceSystem getForceCRSHint() {
1583:                if (rendererHints == null)
1584:                    return null;
1585:                Object crs = this .rendererHints.get("forceCRS");
1586:                if (crs instanceof  CoordinateReferenceSystem)
1587:                    return (CoordinateReferenceSystem) crs;
1588:
1589:                return null;
1590:            }
1591:
1592:            /**
1593:             * @deprecated
1594:             */
1595:            public void paint(Graphics2D graphics, Rectangle paintArea,
1596:                    Envelope mapArea) {
1597:                paint(graphics, paintArea, new ReferencedEnvelope(mapArea,
1598:                        context.getCoordinateReferenceSystem()));
1599:            }
1600:
1601:            /**
1602:             * @deprecated
1603:             */
1604:            public void paint(Graphics2D graphics, Rectangle paintArea,
1605:                    Envelope mapArea, AffineTransform worldToScreen) {
1606:                paint(graphics, paintArea, new ReferencedEnvelope(mapArea,
1607:                        context.getCoordinateReferenceSystem()), worldToScreen);
1608:            }
1609:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.