Source Code Cross Referenced for KMLWriter.java in  » GIS » GeoServer » org » vfny » geoserver » wms » responses » map » kml » 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 » GeoServer » org.vfny.geoserver.wms.responses.map.kml 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org.  All rights reserved.
0002:         * This code is licensed under the GPL 2.0 license, availible at the root
0003:         * application directory.
0004:         */
0005:        package org.vfny.geoserver.wms.responses.map.kml;
0006:
0007:        import java.awt.Color;
0008:        import java.awt.Paint;
0009:        import java.io.IOException;
0010:        import java.io.OutputStream;
0011:        import java.io.OutputStreamWriter;
0012:        import java.io.StringWriter;
0013:        import java.nio.charset.Charset;
0014:        import java.text.DecimalFormat;
0015:        import java.text.DecimalFormatSymbols;
0016:        import java.text.NumberFormat;
0017:        import java.util.ArrayList;
0018:        import java.util.Iterator;
0019:        import java.util.List;
0020:        import java.util.Locale;
0021:        import java.util.NoSuchElementException;
0022:        import java.util.logging.Logger;
0023:
0024:        import javax.media.jai.util.Range;
0025:        import javax.servlet.http.HttpServletRequest;
0026:        import javax.xml.transform.TransformerException;
0027:
0028:        import org.geoserver.template.FeatureWrapper;
0029:        import org.geoserver.template.GeoServerTemplateLoader;
0030:        import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
0031:        import org.geotools.data.DataSourceException;
0032:        import org.geotools.feature.Feature;
0033:        import org.geotools.feature.FeatureCollection;
0034:        import org.geotools.feature.FeatureIterator;
0035:        import org.geotools.feature.FeatureType;
0036:        import org.geotools.feature.GeometryAttributeType;
0037:        import org.geotools.feature.IllegalAttributeException;
0038:        import org.geotools.geometry.jts.JTS;
0039:        import org.geotools.gml.producer.GeometryTransformer;
0040:        import org.geotools.map.MapLayer;
0041:        import org.geotools.referencing.CRS;
0042:        import org.geotools.renderer.style.LineStyle2D;
0043:        import org.geotools.renderer.style.MarkStyle2D;
0044:        import org.geotools.renderer.style.PolygonStyle2D;
0045:        import org.geotools.renderer.style.SLDStyleFactory;
0046:        import org.geotools.renderer.style.Style2D;
0047:        import org.geotools.renderer.style.TextStyle2D;
0048:        import org.geotools.styling.FeatureTypeStyle;
0049:        import org.geotools.styling.LineSymbolizer;
0050:        import org.geotools.styling.Mark;
0051:        import org.geotools.styling.PointSymbolizer;
0052:        import org.geotools.styling.PolygonSymbolizer;
0053:        import org.geotools.styling.RasterSymbolizer;
0054:        import org.geotools.styling.Rule;
0055:        import org.geotools.styling.Style;
0056:        import org.geotools.styling.Symbolizer;
0057:        import org.geotools.styling.TextSymbolizer;
0058:        import org.geotools.util.NumberRange;
0059:        import org.opengis.filter.Filter;
0060:        import org.opengis.filter.expression.Expression;
0061:        import org.opengis.geometry.MismatchedDimensionException;
0062:        import org.opengis.referencing.FactoryException;
0063:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0064:        import org.opengis.referencing.operation.MathTransform;
0065:        import org.opengis.referencing.operation.TransformException;
0066:        import org.vfny.geoserver.global.GeoServer;
0067:        import org.vfny.geoserver.wms.WMSMapContext;
0068:
0069:        import com.vividsolutions.jts.geom.Coordinate;
0070:        import com.vividsolutions.jts.geom.Geometry;
0071:        import com.vividsolutions.jts.geom.GeometryCollection;
0072:        import com.vividsolutions.jts.geom.MultiLineString;
0073:        import com.vividsolutions.jts.geom.MultiPoint;
0074:        import com.vividsolutions.jts.geom.MultiPolygon;
0075:
0076:        import freemarker.template.Configuration;
0077:        import freemarker.template.Template;
0078:        import freemarker.template.TemplateException;
0079:
0080:        /**
0081:         * Writer for KML/KMZ (Keyhole Markup Language) files. Normaly controled by an
0082:         * EncodeKML instance, this class handles the styling information and ensures
0083:         * that the geometries produced match the pseudo GML expected by GE.
0084:         * 
0085:         * @REVISIT: Once this is fully working, revisit as an extention to
0086:         *           TransformerBase
0087:         * @author James Macgill
0088:         * @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $
0089:         * @author $Author: Simone Giannecchini (simboss1@gmail.com) $
0090:         * @author Brent Owens
0091:         * 
0092:         * @deprecated use {@link KMLTransformer}.
0093:         */
0094:        public class KMLWriter extends OutputStreamWriter {
0095:            private static final Logger LOGGER = org.geotools.util.logging.Logging
0096:                    .getLogger(KMLWriter.class.getPackage().getName());
0097:
0098:            /**
0099:             * a number formatter set up to write KML legible numbers
0100:             */
0101:            private static DecimalFormat formatter;
0102:
0103:            /**
0104:             * The template configuration
0105:             */
0106:            private static Configuration templateConfig;
0107:
0108:            /**
0109:             * Resolves the FeatureTypeStyle info per feature into a Style2D object.
0110:             */
0111:            private SLDStyleFactory styleFactory = new SLDStyleFactory();
0112:
0113:            // TODO: calcuate a real value based on image size to bbox ratio, as image
0114:            // size has no meanining for KML yet this is a fudge.
0115:            private double scaleDenominator = 1;
0116:
0117:            /** Tolerance used to compare doubles for equality */
0118:            private static final double TOLERANCE = 1e-6;
0119:
0120:            /**
0121:             * The CRS of the data we are querying. It is a bit of a hack because
0122:             * sometimes when we grab the CRS from the feature itself, we get null. This
0123:             * variable is paired with setSourceCrs() so EncodeKML can can use the
0124:             * feature type's schema to set the CRS.
0125:             */
0126:            private CoordinateReferenceSystem sourceCrs;
0127:
0128:            /**
0129:             * Handles the outputing of geometries as GML
0130:             */
0131:            private GeometryTransformer transformer;
0132:
0133:            static {
0134:                Locale locale = new Locale("en", "US");
0135:
0136:                DecimalFormatSymbols decimalSymbols = new DecimalFormatSymbols(
0137:                        locale);
0138:                decimalSymbols.setDecimalSeparator('.');
0139:                formatter = new DecimalFormat();
0140:                formatter.setDecimalFormatSymbols(decimalSymbols);
0141:
0142:                // do not group
0143:                formatter.setGroupingSize(0);
0144:
0145:                // do not show decimal separator if it is not needed
0146:                formatter.setDecimalSeparatorAlwaysShown(false);
0147:                formatter.setDecimalFormatSymbols(null);
0148:
0149:                // set default number of fraction digits
0150:                formatter.setMaximumFractionDigits(5);
0151:
0152:                // minimun fraction digits to 0 so they get not rendered if not needed
0153:                formatter.setMinimumFractionDigits(0);
0154:
0155:                // initialize the template engine, this is static to maintain a cache
0156:                // over instantiations of kml writer
0157:                templateConfig = new Configuration();
0158:                templateConfig.setObjectWrapper(new FeatureWrapper());
0159:            }
0160:
0161:            /** Holds the map layer set, styling info and area of interest bounds */
0162:            private WMSMapContext mapContext;
0163:
0164:            /**
0165:             * Creates a new KMLWriter object.
0166:             * 
0167:             * @param out
0168:             *            OutputStream to write the KML into
0169:             * @param config
0170:             *            WMSMapContext describing the map to be generated.
0171:             */
0172:            public KMLWriter(OutputStream out, WMSMapContext mapContext) {
0173:                super (out, Charset.forName("UTF-8"));
0174:                this .mapContext = mapContext;
0175:
0176:                transformer = new GeometryTransformer();
0177:                // transformer.setUseDummyZ(true);
0178:                transformer.setOmitXMLDeclaration(true);
0179:                transformer.setNamespaceDeclarationEnabled(true);
0180:
0181:                GeoServer config = mapContext.getRequest().getGeoServer();
0182:                transformer.setNumDecimals(config.getNumDecimals());
0183:            }
0184:
0185:            /**
0186:             * Sets the maximum number of digits allowed in the fraction portion of a
0187:             * number.
0188:             * 
0189:             * @param numDigits
0190:             * @see NumberFormat#setMaximumFractionDigits
0191:             */
0192:            public void setMaximunFractionDigits(int numDigits) {
0193:                formatter.setMaximumFractionDigits(numDigits);
0194:            }
0195:
0196:            /**
0197:             * Gets the maximum number of digits allowed in the fraction portion of a
0198:             * number.
0199:             * 
0200:             * @return int numDigits
0201:             * @see NumberFormat#getMaximumFractionDigits
0202:             */
0203:            public int getMaximunFractionDigits() {
0204:                return formatter.getMaximumFractionDigits();
0205:            }
0206:
0207:            /**
0208:             * Sets the minimum number of digits allowed in the fraction portion of a
0209:             * number.
0210:             * 
0211:             * @param numDigits
0212:             * @see NumberFormat#setMinimumFractionDigits
0213:             */
0214:            public void setMinimunFractionDigits(int numDigits) {
0215:                formatter.setMinimumFractionDigits(numDigits);
0216:            }
0217:
0218:            /*
0219:             * Sets the minimum number of digits allowed in the fraction portion of a
0220:             * number.
0221:             * 
0222:             * @param numDigits
0223:             * 
0224:             * @see NumberFormat#getMinimumFractionDigits
0225:             */
0226:            public int getMinimunFractionDigits() {
0227:                return formatter.getMinimumFractionDigits();
0228:            }
0229:
0230:            public void setRequestedScale(double scale) {
0231:                scaleDenominator = scale;
0232:            }
0233:
0234:            public void setSourceCrs(CoordinateReferenceSystem crs) {
0235:                sourceCrs = crs;
0236:            }
0237:
0238:            /**
0239:             * Formated version of standard write double
0240:             * 
0241:             * @param d
0242:             *            The double to format and write out.
0243:             * 
0244:             * @throws IOException
0245:             */
0246:            public void write(double d) throws IOException {
0247:                write(formatter.format(d));
0248:            }
0249:
0250:            /**
0251:             * Convinience method to add a newline char to the output
0252:             * 
0253:             * @throws IOException
0254:             */
0255:            public void newline() throws IOException {
0256:                super .write('\n');
0257:            }
0258:
0259:            public void writeFeaturesAsRaster(final FeatureCollection features,
0260:                    final MapLayer layer, final int order) throws IOException,
0261:                    AbortedException {
0262:                Style style = layer.getStyle();
0263:
0264:                try {
0265:                    FeatureType featureType = features.getSchema();
0266:
0267:                    setUpWriterHandler(featureType);
0268:
0269:                    FeatureTypeStyle[] fts = style.getFeatureTypeStyles();
0270:                    processStylersRaster(features, fts, layer, order);
0271:                    LOGGER.fine("encoded "
0272:                            + featureType.getTypeName().toString());
0273:                } catch (NoSuchElementException ex) {
0274:                    throw new DataSourceException(ex.getMessage(), ex);
0275:                } catch (IllegalAttributeException ex) {
0276:                    throw new DataSourceException(ex.getMessage(), ex);
0277:                }
0278:            }
0279:
0280:            public void writeFeaturesAsVectors(
0281:                    final FeatureCollection features, final MapLayer layer)
0282:                    throws IOException, AbortedException {
0283:                Style style = layer.getStyle();
0284:
0285:                try {
0286:                    FeatureType featureType = features.getSchema();
0287:
0288:                    setUpWriterHandler(featureType);
0289:
0290:                    FeatureTypeStyle[] fts = style.getFeatureTypeStyles();
0291:                    processStylersVector(features, fts, layer);
0292:                    LOGGER.fine("encoded "
0293:                            + featureType.getTypeName().toString());
0294:                } catch (NoSuchElementException ex) {
0295:                    throw new DataSourceException(ex.getMessage(), ex);
0296:                } catch (IllegalAttributeException ex) {
0297:                    throw new DataSourceException(ex.getMessage(), ex);
0298:                }
0299:            }
0300:
0301:            public void writeCoverages(final FeatureCollection features,
0302:                    final MapLayer layer) throws IOException, AbortedException {
0303:                Style style = layer.getStyle();
0304:
0305:                try {
0306:                    FeatureType featureType = features.getSchema();
0307:
0308:                    setUpWriterHandler(featureType);
0309:
0310:                    FeatureTypeStyle[] fts = style.getFeatureTypeStyles();
0311:                    processStylersCoverage(features, fts, layer);
0312:                    LOGGER.fine("encoded "
0313:                            + featureType.getTypeName().toString());
0314:                } catch (NoSuchElementException ex) {
0315:                    throw new DataSourceException(ex.getMessage(), ex);
0316:                } catch (IllegalAttributeException ex) {
0317:                    throw new DataSourceException(ex.getMessage(), ex);
0318:                }
0319:            }
0320:
0321:            /**
0322:             * Write all the features in a collection which pass the rules in the
0323:             * provided Style object.
0324:             * 
0325:             * @TODO: support Name and Description information
0326:             */
0327:
0328:            /*
0329:             * public void writeFeatures(final FeatureCollection features, final
0330:             * MapLayer layer, final int order, final boolean kmz, final boolean
0331:             * vectorResult) throws IOException, AbortedException { Style style =
0332:             * layer.getStyle();
0333:             * 
0334:             * try { FeatureType featureType = features.getSchema();
0335:             * 
0336:             * setUpWriterHandler(featureType); FeatureTypeStyle[] fts =
0337:             * style.getFeatureTypeStyles(); if (!kmz) processStylers(features, fts,
0338:             * layer, order); else processStylersKMZ(features, fts, layer, order,
0339:             * vectorResult);
0340:             * 
0341:             * 
0342:             * LOGGER.fine(new StringBuffer("encoded
0343:             * ").append(featureType.getTypeName()).toString()); } catch
0344:             * (NoSuchElementException ex) { throw new
0345:             * DataSourceException(ex.getMessage(), ex); } catch
0346:             * (IllegalAttributeException ex) { throw new
0347:             * DataSourceException(ex.getMessage(), ex); } }
0348:             */
0349:
0350:            /**
0351:             * Start a new KML folder. From the spec 2.0: A top-level, optional tag used
0352:             * to structure hierarchical arrangement of other folders, placemarks,
0353:             * ground overlays, and screen overlays. Use this tag to structure and
0354:             * organize your information in the Google Earth client.
0355:             * 
0356:             * In this context we should be using a Folder per map layer.
0357:             * 
0358:             * @param name
0359:             *            A String to label this folder with, if null the name tag will
0360:             *            be ommited
0361:             * @param description
0362:             *            Supplies descriptive information. This description appears in
0363:             *            the Places window when the user clicks on the folder or ground
0364:             *            overlay, and in a pop-up window when the user clicks on either
0365:             *            the Placemark name in the Places window, or the placemark
0366:             *            icon. The description element supports plain text as well as
0367:             *            HTML formatting. A valid URL string for the World Wide Web is
0368:             *            automatically converted to a hyperlink to that URL (e.g.
0369:             *            http://www.google.com). if null the description tag will be
0370:             *            ommited
0371:             */
0372:            public void startFolder(String name, String description)
0373:                    throws IOException {
0374:                write("<Folder>");
0375:
0376:                if (name != null) {
0377:                    write("<name>" + name + "</name>");
0378:                }
0379:
0380:                if (description != null) {
0381:                    write("<description>" + description + "</description>");
0382:                }
0383:            }
0384:
0385:            public void startDocument(String name, String description)
0386:                    throws IOException {
0387:                write("<Document>");
0388:
0389:                if (name != null) {
0390:                    write("<name>" + name + "</name>");
0391:                }
0392:
0393:                if (description != null) {
0394:                    write("<description>" + description + "</description>");
0395:                }
0396:            }
0397:
0398:            public void endFolder() throws IOException {
0399:                write("</Folder>");
0400:            }
0401:
0402:            public void endDocument() throws IOException {
0403:                write("</Document>");
0404:            }
0405:
0406:            /**
0407:             * Gather any information needed to write the KML document.
0408:             * 
0409:             * @TODO: support writing of 'Schema' tags based on featureType
0410:             */
0411:            private void setUpWriterHandler(FeatureType featureType)
0412:                    throws IOException {
0413:                String typeName = featureType.getTypeName();
0414:
0415:                /*
0416:                 * REVISIT: To use attributes properly we need to be using the 'schema'
0417:                 * part of KML to contain custom data..
0418:                 */
0419:                List atts = new ArrayList(0); // config.getAttributes(typeName);
0420:            }
0421:
0422:            /**
0423:             * Write out the geometry. Contains workaround for the fact that KML2.0 does
0424:             * not support multipart geometries in the same way that GML does.
0425:             * 
0426:             * @param geom
0427:             *            The Geometry to be encoded, multi part geometries will be
0428:             *            written as a sequence.
0429:             * @param trans
0430:             *            A GeometryTransformer to produce the gml output, its output is
0431:             *            post processed to remove gml namespace prefixes.
0432:             */
0433:            protected void writeGeometry(Geometry geom,
0434:                    GeometryTransformer trans) throws IOException,
0435:                    TransformerException {
0436:                if (isMultiPart(geom)) {
0437:                    for (int i = 0; i < geom.getNumGeometries(); i++) {
0438:                        writeGeometry(geom.getGeometryN(i), trans);
0439:                    }
0440:                } else {
0441:                    // remove gml prefixing as KML does not accept them
0442:                    StringWriter tempWriter = new StringWriter();
0443:                    // trans.setNumDecimals(config.getNumDecimals());
0444:                    trans.transform(geom, tempWriter);
0445:
0446:                    String tempBuffer = tempWriter.toString();
0447:                    // @REVISIT: should check which prefix is being used, this will only
0448:                    // work for the default (99.9%) of cases.
0449:                    write(tempBuffer.replaceAll("gml:", ""));
0450:                }
0451:            }
0452:
0453:            protected void writeLookAt(Geometry geom, GeometryTransformer trans)
0454:                    throws IOException, TransformerException {
0455:                final Coordinate[] coordinates = getCentroid(geom)
0456:                        .getCoordinates();
0457:                write("<LookAt>");
0458:                write("<longitude>" + coordinates[0].x + "</longitude>");
0459:                write("<latitude>" + coordinates[0].y + "</latitude>");
0460:                write("<range>700</range>");
0461:                write("<tilt>10.0</tilt>");
0462:                write("<heading>10.0</heading>");
0463:                write("</LookAt>");
0464:            }
0465:
0466:            protected void writePlaceMarkPoint(Geometry geom,
0467:                    GeometryTransformer trans) throws IOException,
0468:                    TransformerException {
0469:                final Coordinate[] coordinates = getCentroid(geom)
0470:                        .getCoordinates();
0471:                write("<Point><coordinates>" + coordinates[0].x + ","
0472:                        + coordinates[0].y + "," + coordinates[0].z
0473:                        + "</coordinates></Point>");
0474:            }
0475:
0476:            /**
0477:             * Test to see if the geometry is a Multi geometry
0478:             * 
0479:             * @return true if geom instance of MultiPolygon, MultiPoint or
0480:             *         MultiLineString
0481:             */
0482:            protected boolean isMultiPart(Geometry geom) {
0483:                Class geomClass = geom.getClass();
0484:
0485:                return (geomClass.equals(MultiPolygon.class)
0486:                        || geomClass.equals(MultiPoint.class) || geomClass
0487:                        .equals(MultiLineString.class));
0488:            }
0489:
0490:            /**
0491:             * Applies each feature type styler in turn to all of the features.
0492:             * 
0493:             * @param features
0494:             *            A FeatureCollection contatining the features to be rendered
0495:             * @param featureStylers
0496:             *            An array of feature stylers to be applied
0497:             * @throws IOException
0498:             * @throws IllegalAttributeException
0499:             * @TODO: multiple features types result in muliple data passes, could be
0500:             *        split into separate tempory files then joined.
0501:             */
0502:            private void processStylersVector(final FeatureCollection features,
0503:                    final FeatureTypeStyle[] featureStylers,
0504:                    final MapLayer layer) throws IOException,
0505:                    IllegalAttributeException {
0506:                final int ftsLength = featureStylers.length;
0507:
0508:                for (int i = 0; i < ftsLength; i++) {
0509:                    FeatureTypeStyle fts = featureStylers[i];
0510:                    final String typeName = features.getSchema().getTypeName();
0511:
0512:                    if ((typeName != null)
0513:                            && (features.getSchema().isDescendedFrom(null,
0514:                                    fts.getFeatureTypeName()) || typeName
0515:                                    .equalsIgnoreCase(fts.getFeatureTypeName()))) {
0516:                        // get applicable rules at the current scale
0517:                        Rule[] rules = fts.getRules();
0518:                        List ruleList = new ArrayList();
0519:                        List elseRuleList = new ArrayList();
0520:                        populateRuleLists(rules, ruleList, elseRuleList, false);
0521:
0522:                        if ((ruleList.size() == 0)
0523:                                && (elseRuleList.size() == 0)) {
0524:                            return; // bail out early if no rules made it (because of
0525:                            // scale denominators)
0526:                        }
0527:
0528:                        // REVISIT: once scaleDemominator can actualy be determined
0529:                        // re-evaluate sensible ranges for GE
0530:                        NumberRange scaleRange = new NumberRange(
0531:                                scaleDenominator, scaleDenominator);
0532:                        FeatureIterator reader = features.features();
0533:
0534:                        while (true) {
0535:                            try {
0536:                                if (!reader.hasNext()) {
0537:                                    break;
0538:                                }
0539:
0540:                                boolean doElse = true;
0541:
0542:                                Feature feature = reader.next();
0543:                                StringBuffer featureLabel = new StringBuffer(""); // this
0544:                                // gets
0545:                                // filled
0546:                                // in
0547:                                // if
0548:                                // there
0549:                                // is a
0550:                                // textsymbolizer
0551:                                String id = feature.getID();
0552:                                id = id.replaceAll("&", "");
0553:                                id = id.replaceAll(">", "");
0554:                                id = id.replaceAll("<", "");
0555:                                id = id.replaceAll("%", "");
0556:                                startDocument(id, layer.getTitle());
0557:
0558:                                // start writing out the styles
0559:                                write("<Style id=\"GeoServerStyle"
0560:                                        + feature.getID() + "\">");
0561:
0562:                                // applicable rules
0563:                                for (Iterator it = ruleList.iterator(); it
0564:                                        .hasNext();) {
0565:                                    Rule r = (Rule) it.next();
0566:                                    LOGGER.finer(new StringBuffer(
0567:                                            "applying rule: ").append(
0568:                                            r.toString()).toString());
0569:
0570:                                    Filter filter = r.getFilter();
0571:
0572:                                    // if there is no filter or the filter says to do
0573:                                    // the feature anyways, render it
0574:                                    if ((filter == null)
0575:                                            || filter.evaluate(feature)) {
0576:                                        doElse = false;
0577:                                        LOGGER
0578:                                                .finer("processing Symobolizer ...");
0579:
0580:                                        Symbolizer[] symbolizers = r
0581:                                                .getSymbolizers();
0582:                                        processVectorSymbolizers(feature,
0583:                                                symbolizers, scaleRange,
0584:                                                featureLabel);
0585:                                    }
0586:                                }
0587:
0588:                                if (doElse) {
0589:                                    // rules with an else filter
0590:                                    LOGGER.finer("rules with an else filter");
0591:
0592:                                    for (Iterator it = elseRuleList.iterator(); it
0593:                                            .hasNext();) {
0594:                                        Rule r = (Rule) it.next();
0595:                                        Symbolizer[] symbolizers = r
0596:                                                .getSymbolizers();
0597:                                        LOGGER
0598:                                                .finer("processing Symobolizer ...");
0599:                                        processVectorSymbolizers(feature,
0600:                                                symbolizers, scaleRange,
0601:                                                featureLabel);
0602:                                    }
0603:                                }
0604:
0605:                                write("</Style>"); // close off styles
0606:
0607:                                // we have written out the style, so now lets write out
0608:                                // the geometry
0609:                                String fTitle = featureLabel.toString();
0610:
0611:                                if (fTitle.equals("")) {
0612:                                    fTitle = feature.getID();
0613:                                }
0614:
0615:                                write("<Placemark>");
0616:                                write("<name><![CDATA[" + featureLabel
0617:                                        + "]]></name>"); // CDATA
0618:                                // needed
0619:                                // for
0620:                                // ampersands
0621:
0622:                                final FeatureType schema = features.getSchema();
0623:
0624:                                // if there are supposed to be detailed descriptions,
0625:                                // write them out
0626:                                write("<description><![CDATA[");
0627:                                writeDescription(feature, schema);
0628:                                write("]]></description>");
0629:
0630:                                writeLookAt(findGeometry(feature), transformer);
0631:                                write("<styleUrl>#GeoServerStyle"
0632:                                        + feature.getID() + "</styleUrl>");
0633:                                write("<MultiGeometry>");
0634:                                writePlaceMarkPoint(findGeometry(feature),
0635:                                        transformer);
0636:                                writeGeometry(findGeometry(feature),
0637:                                        transformer);
0638:                                write("</MultiGeometry>");
0639:                                write("</Placemark>");
0640:                                newline();
0641:
0642:                                endDocument(); // </Document>
0643:                            } catch (Exception e) {
0644:                                // that feature failed but others may still work
0645:                                // REVISIT: don't like eating exceptions, even with a
0646:                                // log.
0647:                                // e.printStackTrace();
0648:                                LOGGER.warning(new StringBuffer(
0649:                                        "KML transform for feature failed ")
0650:                                        .append(e.getMessage()).toString());
0651:                            }
0652:                        }
0653:
0654:                        // FeatureIterators may be backed by a stream so this tidies
0655:                        // things up.
0656:                        features.close(reader);
0657:                    }
0658:                }
0659:            }
0660:
0661:            /**
0662:             * Applies each feature type styler in turn to all of the features.
0663:             * 
0664:             * @param features
0665:             *            A FeatureCollection contatining the features to be rendered
0666:             * @param featureStylers
0667:             *            An array of feature stylers to be applied
0668:             * @throws IOException
0669:             * @throws IllegalAttributeException
0670:             * @TODO: multiple features types result in muliple data passes, could be
0671:             *        split into separate tempory files then joined.
0672:             */
0673:            private void processStylersCoverage(
0674:                    final FeatureCollection features,
0675:                    final FeatureTypeStyle[] featureStylers,
0676:                    final MapLayer layer) throws IOException,
0677:                    IllegalAttributeException {
0678:                final int ftStylesLength = featureStylers.length;
0679:
0680:                for (int i = 0; i < ftStylesLength; i++) { // for each style
0681:
0682:                    FeatureTypeStyle fts = featureStylers[i];
0683:                    String typeName = features.getSchema().getTypeName();
0684:
0685:                    if ((typeName != null)
0686:                            && (features.getSchema().isDescendedFrom(null,
0687:                                    fts.getFeatureTypeName()) || typeName
0688:                                    .equalsIgnoreCase(fts.getFeatureTypeName()))) {
0689:                        // get applicable rules at the current scale
0690:                        Rule[] rules = fts.getRules();
0691:                        List ruleList = new ArrayList();
0692:                        List elseRuleList = new ArrayList();
0693:                        populateRuleLists(rules, ruleList, elseRuleList, false);
0694:
0695:                        if ((ruleList.size() == 0)
0696:                                && (elseRuleList.size() == 0)) {
0697:                            return;
0698:                        }
0699:
0700:                        FeatureIterator reader = features.features();
0701:
0702:                        // we aren't going to iterate through the features because we
0703:                        // just need to prepare
0704:                        // the kml document for one feature; it is a raster result.
0705:                        try {
0706:                            if (!reader.hasNext()) {
0707:                                continue; // no features, so move on
0708:                            }
0709:
0710:                            boolean doElse = true;
0711:                            Feature feature = reader.next();
0712:
0713:                            // applicable rules
0714:                            for (Iterator it = ruleList.iterator(); it
0715:                                    .hasNext();) {
0716:                                Rule r = (Rule) it.next();
0717:                                LOGGER
0718:                                        .finer(new StringBuffer(
0719:                                                "applying rule: ").append(
0720:                                                r.toString()).toString());
0721:
0722:                                Filter filter = r.getFilter();
0723:
0724:                                if ((filter == null)
0725:                                        || filter.evaluate(feature)) {
0726:                                    doElse = false;
0727:                                    LOGGER
0728:                                            .finer("processing raster-result Symobolizer ...");
0729:
0730:                                    Symbolizer[] symbolizers = r
0731:                                            .getSymbolizers();
0732:
0733:                                    processRasterSymbolizersForCoverage(
0734:                                            feature, symbolizers, layer);
0735:                                }
0736:                            }
0737:
0738:                            if (doElse) {
0739:                                // rules with an else filter
0740:                                LOGGER.finer("rules with an else filter");
0741:
0742:                                for (Iterator it = elseRuleList.iterator(); it
0743:                                        .hasNext();) {
0744:                                    Rule r = (Rule) it.next();
0745:                                    Symbolizer[] symbolizers = r
0746:                                            .getSymbolizers();
0747:                                    LOGGER
0748:                                            .finer("processing raster-result Symobolizer ...");
0749:
0750:                                    processRasterSymbolizersForCoverage(
0751:                                            feature, symbolizers, layer);
0752:                                }
0753:                            }
0754:                        } catch (Exception e) {
0755:                            // that feature failed but others may still work
0756:                            // REVISIT: don't like eating exceptions, even with a log.
0757:                            LOGGER.warning(new StringBuffer(
0758:                                    "KML transform for feature failed ")
0759:                                    .append(e.getMessage()).toString());
0760:                        }
0761:
0762:                        // FeatureIterators may be backed by a stream so this tidies
0763:                        // things up.
0764:                        features.close(reader);
0765:                    } // end if
0766:                } // end for loop
0767:            }
0768:
0769:            /**
0770:             * 
0771:             * @param features
0772:             * @param featureStylers
0773:             * @param layer
0774:             * @param order
0775:             * @throws IOException
0776:             * @throws IllegalAttributeException
0777:             */
0778:            private void processStylersRaster(final FeatureCollection features,
0779:                    final FeatureTypeStyle[] featureStylers,
0780:                    final MapLayer layer, final int order) throws IOException,
0781:                    IllegalAttributeException {
0782:                startFolder("layer_" + order, layer.getTitle());
0783:
0784:                int layerCounter = order;
0785:
0786:                final int ftStylesLength = featureStylers.length;
0787:
0788:                for (int i = 0; i < ftStylesLength; i++) { // for each style
0789:
0790:                    FeatureTypeStyle fts = featureStylers[i];
0791:                    String typeName = features.getSchema().getTypeName();
0792:
0793:                    if ((typeName != null)
0794:                            && (features.getSchema().isDescendedFrom(null,
0795:                                    fts.getFeatureTypeName()) || typeName
0796:                                    .equalsIgnoreCase(fts.getFeatureTypeName()))) {
0797:                        // get applicable rules at the current scale
0798:                        Rule[] rules = fts.getRules();
0799:                        List ruleList = new ArrayList();
0800:                        List elseRuleList = new ArrayList();
0801:                        populateRuleLists(rules, ruleList, elseRuleList, true);
0802:
0803:                        if ((ruleList.size() == 0)
0804:                                && (elseRuleList.size() == 0)) {
0805:                            return;
0806:                        }
0807:
0808:                        FeatureIterator reader = features.features();
0809:
0810:                        // we aren't going to iterate through the features because we
0811:                        // just need to prepare
0812:                        // the kml document for one feature; it is a raster result.
0813:                        try {
0814:                            if (!reader.hasNext()) {
0815:                                continue; // no features, so move on
0816:                            }
0817:
0818:                            boolean doElse = true;
0819:                            Feature feature = reader.next();
0820:
0821:                            // applicable rules
0822:                            for (Iterator it = ruleList.iterator(); it
0823:                                    .hasNext();) {
0824:                                Rule r = (Rule) it.next();
0825:                                LOGGER
0826:                                        .finer(new StringBuffer(
0827:                                                "applying rule: ").append(
0828:                                                r.toString()).toString());
0829:
0830:                                Filter filter = r.getFilter();
0831:
0832:                                if ((filter == null)
0833:                                        || filter.evaluate(feature)) {
0834:                                    doElse = false;
0835:                                    LOGGER
0836:                                            .finer("processing raster-result Symobolizer ...");
0837:
0838:                                    Symbolizer[] symbolizers = r
0839:                                            .getSymbolizers();
0840:
0841:                                    processRasterSymbolizers(feature,
0842:                                            symbolizers, order);
0843:                                    layerCounter++;
0844:                                }
0845:                            }
0846:
0847:                            if (doElse) {
0848:                                // rules with an else filter
0849:                                LOGGER.finer("rules with an else filter");
0850:
0851:                                for (Iterator it = elseRuleList.iterator(); it
0852:                                        .hasNext();) {
0853:                                    Rule r = (Rule) it.next();
0854:                                    Symbolizer[] symbolizers = r
0855:                                            .getSymbolizers();
0856:                                    LOGGER
0857:                                            .finer("processing raster-result Symobolizer ...");
0858:
0859:                                    processRasterSymbolizers(feature,
0860:                                            symbolizers, order);
0861:                                    layerCounter++;
0862:                                }
0863:                            }
0864:                        } catch (Exception e) {
0865:                            // that feature failed but others may still work
0866:                            // REVISIT: don't like eating exceptions, even with a log.
0867:                            LOGGER.warning(new StringBuffer(
0868:                                    "KML transform for feature failed ")
0869:                                    .append(e.getMessage()).toString());
0870:                        }
0871:
0872:                        // FeatureIterators may be backed by a stream so this tidies
0873:                        // things up.
0874:                        features.close(reader);
0875:                    } // end if
0876:                } // end for loop
0877:
0878:                endFolder(); // close the folder </Folder>
0879:            }
0880:
0881:            /**
0882:             * Sorts the rules into "If" rules and "Else" rules. The rules are sorted
0883:             * into their respective lists.
0884:             * 
0885:             * @param rules
0886:             * @param ruleList
0887:             * @param elseRuleList
0888:             * @param ignoreScale
0889:             *            ignore the scale denominator
0890:             */
0891:            private void populateRuleLists(Rule[] rules, List ruleList,
0892:                    List elseRuleList, boolean ignoreScale) {
0893:                final int rulesLength = rules.length;
0894:
0895:                for (int j = 0; j < rulesLength; j++) {
0896:                    Rule r = rules[j];
0897:
0898:                    if (ignoreScale) {
0899:                        if (r.hasElseFilter()) {
0900:                            elseRuleList.add(r);
0901:                        } else {
0902:                            ruleList.add(r);
0903:                        }
0904:                    } else {
0905:                        if (isWithinScale(r)) {
0906:                            if (r.hasElseFilter()) {
0907:                                elseRuleList.add(r);
0908:                            } else {
0909:                                ruleList.add(r);
0910:                            }
0911:                        }
0912:                    }
0913:                }
0914:            }
0915:
0916:            private void writeDescription(Feature feature,
0917:                    final FeatureType schema) throws IOException {
0918:                if (mapContext.getRequest().getKMattr()) {
0919:                    // descriptions are "templatable" by users, so see if there is a
0920:                    // template available for use
0921:                    GeoServerTemplateLoader templateLoader = new GeoServerTemplateLoader(
0922:                            getClass());
0923:                    templateLoader.setFeatureType(schema);
0924:
0925:                    Template template = null;
0926:
0927:                    // Configuration is not thread safe
0928:                    synchronized (templateConfig) {
0929:                        templateConfig.setTemplateLoader(templateLoader);
0930:                        template = templateConfig
0931:                                .getTemplate("kmlDescription.ftl");
0932:                    }
0933:
0934:                    try {
0935:                        template.setEncoding("UTF-8");
0936:                        template.process(feature, this );
0937:                    } catch (TemplateException e) {
0938:                        String msg = "Error occured processing template.";
0939:                        throw (IOException) new IOException(msg).initCause(e);
0940:                    }
0941:                }
0942:            }
0943:
0944:            private void processVectorSymbolizers(final Feature feature,
0945:                    final Symbolizer[] symbolizers, Range scaleRange,
0946:                    StringBuffer featureLabel) throws IOException,
0947:                    TransformerException {
0948:                final int length = symbolizers.length;
0949:
0950:                // for each Symbolizer (text, polygon, line etc...)
0951:                for (int m = 0; m < length; m++) {
0952:                    LOGGER.finer(new StringBuffer("applying symbolizer ")
0953:                            .append(symbolizers[m]).toString());
0954:
0955:                    if (symbolizers[m] instanceof  TextSymbolizer) {
0956:                        TextSymbolizer ts = (TextSymbolizer) symbolizers[m];
0957:                        Expression ex = ts.getLabel();
0958:                        featureLabel.append((String) ex.evaluate(feature,
0959:                                String.class)); // attach
0960:                        // the
0961:                        // lable
0962:                        // title
0963:
0964:                        Style2D style = styleFactory.createStyle(feature,
0965:                                symbolizers[m], scaleRange);
0966:                        writeStyle(style, feature.getID(), symbolizers[m]);
0967:                    } else { // all other symbolizers
0968:
0969:                        Style2D style = styleFactory.createStyle(feature,
0970:                                symbolizers[m], scaleRange);
0971:                        writeStyle(style, feature.getID(), symbolizers[m]);
0972:                    }
0973:                } // end for loop
0974:            }
0975:
0976:            /**
0977:             * Writes out the KML for a ground overlay. The image is processed later on.
0978:             * 
0979:             * This will style the KML for raster output. There are no descriptions with
0980:             * vector output, as that would make the result really large (assuming that
0981:             * they chose raster output because a lot of features were requested).
0982:             * 
0983:             * 
0984:             * @param feature
0985:             * @param symbolizers
0986:             * @param order
0987:             * @throws IOException
0988:             * @throws TransformerException
0989:             */
0990:            private void processRasterSymbolizers(final Feature feature,
0991:                    final Symbolizer[] symbolizers, final int order)
0992:                    throws IOException, TransformerException {
0993:                if (symbolizers.length < 1) {
0994:                    return; // no symbolizers so return
0995:                }
0996:
0997:                LOGGER.finer("applying one symbolizer: "
0998:                        + symbolizers[0].toString());
0999:
1000:                com.vividsolutions.jts.geom.Envelope envelope = this .mapContext
1001:                        .getRequest().getBbox();
1002:                write(new StringBuffer("<GroundOverlay>").append("<name>")
1003:                        .append(feature.getID()).append("</name>").append(
1004:                                "<drawOrder>").append(order).append(
1005:                                "</drawOrder>").append("<Icon>").toString());
1006:
1007:                final double[] BBOX = new double[] { envelope.getMinX(),
1008:                        envelope.getMinY(), envelope.getMaxX(),
1009:                        envelope.getMaxY() };
1010:                write(new StringBuffer("<href>layer_").append(order).append(
1011:                        ".png</href>").append(
1012:                        "<viewRefreshMode>never</viewRefreshMode>").append(
1013:                        "<viewBoundScale>0.75</viewBoundScale>").append(
1014:                        "</Icon>").append("<LatLonBox>").append("<north>")
1015:                        .append(BBOX[3]).append("</north>").append("<south>")
1016:                        .append(BBOX[1]).append("</south>").append("<east>")
1017:                        .append(BBOX[2]).append("</east>").append("<west>")
1018:                        .append(BBOX[0]).append("</west>").append(
1019:                                "</LatLonBox>").append("</GroundOverlay>")
1020:                        .toString());
1021:            }
1022:
1023:            /**
1024:             * Writes out the KML for a ground overlay. The image is processed later on.
1025:             * 
1026:             * @param feature
1027:             * @param symbolizers
1028:             * @param order
1029:             * @throws IOException
1030:             * @throws TransformerException
1031:             */
1032:            private void processRasterSymbolizersForCoverage(
1033:                    final Feature feature, final Symbolizer[] symbolizers,
1034:                    final MapLayer layer) throws IOException,
1035:                    TransformerException {
1036:                if (symbolizers.length < 1) {
1037:                    return; // no symbolizers so return
1038:                }
1039:
1040:                LOGGER.finer("applying one symbolizer: "
1041:                        + symbolizers[0].toString());
1042:
1043:                final AbstractGridCoverage2DReader gcReader = (AbstractGridCoverage2DReader) feature
1044:                        .getAttribute("grid");
1045:
1046:                // TODO add read parameters feature.getAttribute("params")
1047:                final HttpServletRequest request = this .mapContext.getRequest()
1048:                        .getHttpServletRequest();
1049:                final String baseURL = org.vfny.geoserver.util.Requests
1050:                        .getBaseUrl(request, null);
1051:
1052:                com.vividsolutions.jts.geom.Envelope envelope = this .mapContext
1053:                        .getRequest().getBbox();
1054:                write(new StringBuffer("<GroundOverlay>").append("<name>")
1055:                        .append(feature.getID()).append("</name>").append(
1056:                                "<Icon>").toString());
1057:
1058:                final double[] BBOX = new double[] { envelope.getMinX(),
1059:                        envelope.getMinY(), envelope.getMaxX(),
1060:                        envelope.getMaxY() };
1061:
1062:                final StringBuffer getMapRequest = new StringBuffer(baseURL)
1063:                        .append("wms?bbox=")
1064:                        .append(BBOX[0])
1065:                        .append(",")
1066:                        .append(BBOX[1])
1067:                        .append(",")
1068:                        .append(BBOX[2])
1069:                        .append(",")
1070:                        .append(BBOX[3])
1071:                        .append("&amp;styles=")
1072:                        .append(layer.getStyle().getName())
1073:                        .append(
1074:                                "&amp;Format=image/png&amp;request=GetMap&amp;layers=")
1075:                        .append(layer.getTitle())
1076:                        .append(
1077:                                "&amp;width="
1078:                                        + this .mapContext.getMapWidth()
1079:                                        + "&amp;height="
1080:                                        + this .mapContext.getMapHeight()
1081:                                        + "&amp;srs=EPSG:4326&amp;transparent=true&amp;");
1082:
1083:                write(new StringBuffer("<href>").append(getMapRequest).append(
1084:                        "</href>").append(
1085:                        "<viewRefreshMode>never</viewRefreshMode>").append(
1086:                        "<viewBoundScale>0.75</viewBoundScale>").append(
1087:                        "</Icon>").append("<LatLonBox>").append("<north>")
1088:                        .append(BBOX[3]).append("</north>").append("<south>")
1089:                        .append(BBOX[1]).append("</south>").append("<east>")
1090:                        .append(BBOX[2]).append("</east>").append("<west>")
1091:                        .append(BBOX[0]).append("</west>").append(
1092:                                "</LatLonBox>").append("</GroundOverlay>")
1093:                        .toString());
1094:            }
1095:
1096:            /**
1097:             * Applies each of a set of symbolizers in turn to a given feature.
1098:             * <p>
1099:             * This is an internal method and should only be called by processStylers.
1100:             * </p>
1101:             * 
1102:             * The KML color tag: The order of expression is alpha, blue, green, red
1103:             * (ABGR). The range of values for any one color is 0 to 255 (00 to ff). For
1104:             * opacity, 00 is fully transparent and ff is fully opaque.
1105:             * 
1106:             * @param feature
1107:             *            The feature to be rendered
1108:             * @param symbolizers
1109:             *            An array of symbolizers which actually perform the rendering.
1110:             * @param scaleRange
1111:             *            The scale range we are working on... provided in order to make
1112:             *            the style factory happy
1113:             */
1114:            private boolean processSymbolizers(
1115:                    final FeatureCollection features, final Feature feature,
1116:                    final Symbolizer[] symbolizers, Range scaleRange,
1117:                    final MapLayer layer, final int order,
1118:                    final int layerCounter, StringBuffer title,
1119:                    boolean vectorResult) throws IOException,
1120:                    TransformerException {
1121:                boolean res = false;
1122:
1123:                // String title=null;
1124:                final int length = symbolizers.length;
1125:
1126:                // for each Symbolizer (text, polygon, line etc...)
1127:                for (int m = 0; m < length; m++) {
1128:                    LOGGER.finer(new StringBuffer("applying symbolizer ")
1129:                            .append(symbolizers[m]).toString());
1130:
1131:                    if (symbolizers[m] instanceof  RasterSymbolizer) {
1132:                        // LOGGER.info("Removed by bao for testing");
1133:                        /*
1134:                         * final GridCoverage gc = (GridCoverage)
1135:                         * feature.getAttribute("grid"); final HttpServletRequest
1136:                         * request =
1137:                         * this.mapContext.getRequest().getHttpServletRequest(); final
1138:                         * String baseURL =
1139:                         * org.vfny.geoserver.util.Requests.getBaseUrl(request);
1140:                         * com.vividsolutions.jts.geom.Envelope envelope =
1141:                         * this.mapContext.getRequest().getBbox();
1142:                         */
1143:
1144:                        /**
1145:                         * EXAMPLE OUTPUT: <GroundOverlay> <name>Google Earth - New
1146:                         * Image Overlay</name> <Icon>
1147:                         * <href>http://localhost:8081/geoserver/wms?bbox=-130,24,-66,50&amp;styles=raster&amp;Format=image/tiff&amp;request=GetMap&amp;layers=nurc:Img_Sample&amp;width=550&amp;height=250&amp;srs=EPSG:4326&amp;</href>
1148:                         * <viewRefreshMode>never</viewRefreshMode>
1149:                         * <viewBoundScale>0.75</viewBoundScale> </Icon> <LatLonBox>
1150:                         * <north>50.0</north> <south>24.0</south> <east>-66.0</east>
1151:                         * <west>-130.0</west> </LatLonBox> </GroundOverlay>
1152:                         */
1153:
1154:                        /*
1155:                         * write(new StringBuffer("<GroundOverlay>"). append("<name>").append(((GridCoverage2D)gc).getName()).append("</name>").
1156:                         * append("<drawOrder>").append(order).append("</drawOrder>").
1157:                         * append("<Icon>").toString()); final double[] BBOX = new
1158:                         * double[] { envelope.getMinX(), envelope.getMinY(),
1159:                         * envelope.getMaxX(), envelope.getMaxY() }; if (layerCounter<0) {
1160:                         * final StringBuffer getMapRequest = new
1161:                         * StringBuffer(baseURL).append("wms?bbox=").append(BBOX[0]).append(",").
1162:                         * append(BBOX[1]).append(",").append(BBOX[2]).append(",").append(BBOX[3]).append("&amp;styles=").
1163:                         * append(layer.getStyle().getName()).append("&amp;Format=image/png&amp;request=GetMap&amp;layers=").
1164:                         * append(layer.getTitle()).append("&amp;width="+this.mapContext.getMapWidth()+"&amp;height="+this.mapContext.getMapHeight()+"&amp;srs=EPSG:4326&amp;");
1165:                         * write("<href>"+getMapRequest.toString()+"</href>"); } else {
1166:                         * write("<href>layer_"+order+".png</href>"); } write(new
1167:                         * StringBuffer("<viewRefreshMode>never</viewRefreshMode>").
1168:                         * append("<viewBoundScale>0.75</viewBoundScale>"). append("</Icon>").
1169:                         * append("<LatLonBox>"). append("<north>").append(BBOX[3]).append("</north>").
1170:                         * append("<south>").append(BBOX[1]).append("</south>").
1171:                         * append("<east>").append(BBOX[2]).append("</east>").
1172:                         * append("<west>").append(BBOX[0]).append("</west>").
1173:                         * append("</LatLonBox>"). append("</GroundOverlay>").toString());
1174:                         * //Geometry g = findGeometry(feature, symbolizers[m]);
1175:                         * //writeRasterStyle(getMapRequest.toString(),
1176:                         * feature.getID());
1177:                         */
1178:                        res = true;
1179:                    } else if (vectorResult) {
1180:                        // TODO: come back and sort out crs transformation
1181:                        // CoordinateReferenceSystem crs = findGeometryCS(feature,
1182:                        // symbolizers[m]);
1183:                        if (symbolizers[m] instanceof  TextSymbolizer) {
1184:                            TextSymbolizer ts = (TextSymbolizer) symbolizers[m];
1185:                            Expression ex = ts.getLabel();
1186:                            String value = (String) ex.evaluate(feature,
1187:                                    String.class);
1188:                            title.append(value);
1189:
1190:                            Style2D style = styleFactory.createStyle(feature,
1191:                                    symbolizers[m], scaleRange);
1192:                            writeStyle(style, feature.getID(), symbolizers[m]);
1193:                        } else {
1194:                            Style2D style = styleFactory.createStyle(feature,
1195:                                    symbolizers[m], scaleRange);
1196:                            writeStyle(style, feature.getID(), symbolizers[m]);
1197:                        }
1198:                    } else if (!vectorResult) {
1199:                        com.vividsolutions.jts.geom.Envelope envelope = this .mapContext
1200:                                .getRequest().getBbox();
1201:                        write(new StringBuffer("<GroundOverlay>").append(
1202:                                "<name>").append(feature.getID()).append(
1203:                                "</name>").append("<drawOrder>").append(order)
1204:                                .append("</drawOrder>").append("<Icon>")
1205:                                .toString());
1206:
1207:                        final double[] BBOX = new double[] {
1208:                                envelope.getMinX(), envelope.getMinY(),
1209:                                envelope.getMaxX(), envelope.getMaxY() };
1210:                        write(new StringBuffer("<href>layer_")
1211:                                .append(order)
1212:                                .append(".png</href>")
1213:                                .append(
1214:                                        "<viewRefreshMode>never</viewRefreshMode>")
1215:                                .append("<viewBoundScale>0.75</viewBoundScale>")
1216:                                .append("</Icon>").append("<LatLonBox>")
1217:                                .append("<north>").append(BBOX[3]).append(
1218:                                        "</north>").append("<south>").append(
1219:                                        BBOX[1]).append("</south>").append(
1220:                                        "<east>").append(BBOX[2]).append(
1221:                                        "</east>").append("<west>").append(
1222:                                        BBOX[0]).append("</west>").append(
1223:                                        "</LatLonBox>").append(
1224:                                        "</GroundOverlay>").toString());
1225:                    } else {
1226:                        LOGGER
1227:                                .info("KMZ processSymbolizerz unknown case. Please report error.");
1228:                    }
1229:                }
1230:
1231:                return res;
1232:            }
1233:
1234:            /**
1235:             * Adds the <style> tag to the KML document.
1236:             * 
1237:             * @param style
1238:             * @param id
1239:             * @throws IOException
1240:             */
1241:            private void writeStyle(final Style2D style, final String id,
1242:                    Symbolizer sym) throws IOException {
1243:                if (style instanceof  PolygonStyle2D
1244:                        && sym instanceof  PolygonSymbolizer) {
1245:                    if ((((PolygonStyle2D) style).getFill() == null)
1246:                            && (((PolygonStyle2D) style).getStroke() == null)) {
1247:                        LOGGER
1248:                                .info("Empty PolygonSymbolizer, using default fill and stroke.");
1249:                    }
1250:
1251:                    final StringBuffer styleString = new StringBuffer();
1252:
1253:                    PolygonSymbolizer polySym = (PolygonSymbolizer) sym;
1254:
1255:                    // ** LABEL **
1256:                    styleString.append("<IconStyle>");
1257:
1258:                    if (!mapContext.getRequest().getKMattr()) { // if they don't want
1259:                        // attributes
1260:                        styleString.append("<color>#00ffffff</color>"); // fully
1261:                        // transparent
1262:                    }
1263:
1264:                    styleString
1265:                            .append("<Icon><href>root://icons/palette-3.png</href><x>224</x><w>32</w><h>32</h></Icon>");
1266:                    styleString.append("</IconStyle>");
1267:
1268:                    // ** FILL **
1269:                    styleString.append("<PolyStyle><color>");
1270:
1271:                    if (polySym.getFill() != null) // if they specified a fill
1272:                    {
1273:                        int opacity = 255; // default to full opacity
1274:
1275:                        if (polySym.getFill().getOpacity() != null) {
1276:                            float op = getOpacity(polySym.getFill()
1277:                                    .getOpacity());
1278:                            opacity = (new Float(255 * op)).intValue();
1279:                        }
1280:
1281:                        Paint p = ((PolygonStyle2D) style).getFill();
1282:
1283:                        if (p instanceof  Color) {
1284:                            styleString.append("#").append(intToHex(opacity))
1285:                                    .append(colorToHex((Color) p)); // transparancy needs to
1286:                            // come from the opacity
1287:                            // value.
1288:                        } else {
1289:                            styleString.append("#ffaaaaaa"); // should not occure in
1290:                            // normal parsing
1291:                        }
1292:                    } else { // no fill specified, make transparent
1293:                        styleString.append("#00aaaaaa");
1294:                    }
1295:
1296:                    // if there is an outline, specify that we have one, then style it
1297:                    styleString.append("</color>");
1298:
1299:                    if (polySym.getStroke() != null) {
1300:                        styleString.append("<outline>1</outline>");
1301:                    } else {
1302:                        styleString.append("<outline>0</outline>");
1303:                    }
1304:
1305:                    styleString.append("</PolyStyle>");
1306:
1307:                    // ** OUTLINE **
1308:                    if (polySym.getStroke() != null) // if there is an outline
1309:                    {
1310:                        styleString.append("<LineStyle><color>");
1311:
1312:                        int opacity = 255; // default to full opacity
1313:
1314:                        if (polySym.getStroke().getOpacity() != null) {
1315:                            float op = getOpacity(polySym.getStroke()
1316:                                    .getOpacity());
1317:                            opacity = (new Float(255 * op)).intValue();
1318:                        }
1319:
1320:                        Paint p = ((PolygonStyle2D) style).getContour();
1321:
1322:                        if (p instanceof  Color) {
1323:                            styleString.append("#").append(intToHex(opacity))
1324:                                    .append(colorToHex((Color) p)); // transparancy needs to
1325:                            // come from the opacity
1326:                            // value.
1327:                        } else {
1328:                            styleString.append("#ffaaaaaa"); // should not occure in
1329:                            // normal parsing
1330:                        }
1331:
1332:                        styleString.append("</color>");
1333:
1334:                        // stroke width
1335:                        if (polySym.getStroke().getWidth() != null) {
1336:                            int width = getWidth(polySym.getStroke().getWidth());
1337:                            styleString.append("<width>").append(width).append(
1338:                                    "</width>");
1339:                        }
1340:
1341:                        styleString.append("</LineStyle>");
1342:                    }
1343:
1344:                    write(styleString.toString());
1345:                } else if (style instanceof  LineStyle2D
1346:                        && sym instanceof  LineSymbolizer) {
1347:                    if (((LineStyle2D) style).getStroke() == null) {
1348:                        LOGGER
1349:                                .info("Empty LineSymbolizer, using default stroke.");
1350:                    }
1351:
1352:                    LineSymbolizer lineSym = (LineSymbolizer) sym;
1353:
1354:                    // ** LABEL **
1355:                    final StringBuffer styleString = new StringBuffer();
1356:                    styleString.append("<IconStyle>");
1357:
1358:                    if (!mapContext.getRequest().getKMattr()) { // if they don't want
1359:                        // attributes
1360:                        styleString.append("<color>#00ffffff</color>"); // fully
1361:                        // transparent
1362:                    }
1363:
1364:                    styleString.append("</IconStyle>");
1365:
1366:                    // ** LINE **
1367:                    styleString.append("<LineStyle><color>");
1368:
1369:                    if (lineSym.getStroke() != null) {
1370:                        int opacity = 255;
1371:
1372:                        if (lineSym.getStroke().getOpacity() != null) {
1373:                            float op = getOpacity(lineSym.getStroke()
1374:                                    .getOpacity());
1375:                            opacity = (new Float(255 * op)).intValue();
1376:                        }
1377:
1378:                        Paint p = ((LineStyle2D) style).getContour();
1379:
1380:                        if (p instanceof  Color) {
1381:                            styleString.append("#").append(intToHex(opacity))
1382:                                    .append(colorToHex((Color) p)); // transparancy needs to
1383:                            // come from the opacity
1384:                            // value.
1385:                        } else {
1386:                            styleString.append("#ffaaaaaa"); // should not occure in
1387:                            // normal parsing
1388:                        }
1389:
1390:                        styleString.append("</color>");
1391:
1392:                        // stroke width
1393:                        if (lineSym.getStroke().getWidth() != null) {
1394:                            int width = getWidth(lineSym.getStroke().getWidth());
1395:                            styleString.append("<width>").append(width).append(
1396:                                    "</width>");
1397:                        }
1398:                    } else // no style defined, so use default
1399:                    {
1400:                        styleString.append("#ffaaaaaa");
1401:                        styleString.append("</color><width>1</width>");
1402:                    }
1403:
1404:                    styleString.append("</LineStyle>");
1405:
1406:                    write(styleString.toString());
1407:                } else if (style instanceof  TextStyle2D
1408:                        && sym instanceof  TextSymbolizer) {
1409:                    final StringBuffer styleString = new StringBuffer();
1410:                    TextSymbolizer textSym = (TextSymbolizer) sym;
1411:
1412:                    styleString.append("<LabelStyle><color>");
1413:
1414:                    if (textSym.getFill() != null) {
1415:                        int opacity = 255;
1416:
1417:                        if (textSym.getFill().getOpacity() != null) {
1418:                            float op = getOpacity(textSym.getFill()
1419:                                    .getOpacity());
1420:                            opacity = (new Float(255 * op)).intValue();
1421:                        }
1422:
1423:                        Paint p = ((TextStyle2D) style).getFill();
1424:
1425:                        if (p instanceof  Color) {
1426:                            styleString.append("#").append(intToHex(opacity))
1427:                                    .append(colorToHex((Color) p)); // transparancy needs to
1428:                            // come from the opacity
1429:                            // value.
1430:                        } else {
1431:                            styleString.append("#ffaaaaaa"); // should not occure in
1432:                            // normal parsing
1433:                        }
1434:
1435:                        styleString.append("</color></LabelStyle>");
1436:                    } else {
1437:                        styleString.append("#ffaaaaaa");
1438:                        styleString.append("</color></LabelStyle>");
1439:                    }
1440:
1441:                    write(styleString.toString());
1442:                } else if (style instanceof  MarkStyle2D
1443:                        && sym instanceof  PointSymbolizer) {
1444:                    // we can sorta style points. Just with color however.
1445:                    final StringBuffer styleString = new StringBuffer();
1446:                    PointSymbolizer pointSym = (PointSymbolizer) sym;
1447:
1448:                    styleString.append("<IconStyle><color>");
1449:
1450:                    if ((pointSym.getGraphic() != null)
1451:                            && (pointSym.getGraphic().getMarks() != null)) {
1452:                        Mark[] marks = pointSym.getGraphic().getMarks();
1453:
1454:                        if ((marks.length > 0) && (marks[0] != null)) {
1455:                            Mark mark = marks[0];
1456:
1457:                            int opacity = 255;
1458:
1459:                            if (mark.getFill().getOpacity() != null) {
1460:                                float op = getOpacity(mark.getFill()
1461:                                        .getOpacity());
1462:                                opacity = (new Float(255 * op)).intValue();
1463:                            }
1464:
1465:                            Paint p = ((MarkStyle2D) style).getFill();
1466:
1467:                            if (p instanceof  Color) {
1468:                                styleString.append("#").append(
1469:                                        intToHex(opacity)).append(
1470:                                        colorToHex((Color) p)); // transparancy
1471:                                // needs to come
1472:                                // from the
1473:                                // opacity
1474:                                // value.
1475:                            } else {
1476:                                styleString.append("#ffaaaaaa"); // should not occure
1477:                                // in normal parsing
1478:                            }
1479:                        } else {
1480:                            styleString.append("#ffaaaaaa");
1481:                        }
1482:                    } else {
1483:                        styleString.append("#ffaaaaaa");
1484:                    }
1485:
1486:                    styleString.append("</color>");
1487:                    styleString.append("<colorMode>normal</colorMode>");
1488:                    styleString
1489:                            .append("<Icon><href>root://icons/palette-4.png</href>");
1490:                    styleString
1491:                            .append("<x>32</x><y>128</y><w>32</w><h>32</h></Icon>");
1492:
1493:                    styleString.append("</IconStyle>");
1494:
1495:                    write(styleString.toString());
1496:                }
1497:            }
1498:
1499:            /**
1500:             * @param href
1501:             * @param id
1502:             * @throws IOException
1503:             */
1504:            private void writeRasterStyle(final String href, final String id)
1505:                    throws IOException {
1506:                final StringBuffer styleString = new StringBuffer();
1507:                styleString.append("<Style id=\"GeoServerStyle").append(id)
1508:                        .append("\">");
1509:                styleString
1510:                        .append("<IconStyle><Icon><href>")
1511:                        .append(href)
1512:                        .append(
1513:                                "</href><viewRefreshMode>never</viewRefreshMode>")
1514:                        .append("<viewBoundScale>0.75</viewBoundScale><w>")
1515:                        .append(this .mapContext.getMapWidth())
1516:                        .append("</w><h>").append(
1517:                                this .mapContext.getMapHeight()).append(
1518:                                "</h></Icon></IconStyle>");
1519:                styleString
1520:                        .append("<PolyStyle><fill>0</fill><outline>0</outline></PolyStyle>");
1521:                styleString.append("</Style>");
1522:
1523:                write(styleString.toString());
1524:            }
1525:
1526:            private boolean isWithinScale(Rule r) {
1527:                double min = r.getMinScaleDenominator();
1528:                double max = r.getMaxScaleDenominator();
1529:
1530:                if (((min - TOLERANCE) <= scaleDenominator)
1531:                        && ((max + TOLERANCE) >= scaleDenominator)) {
1532:                    return true;
1533:                } else {
1534:                    return false;
1535:                }
1536:            }
1537:
1538:            /**
1539:             * Finds the geometric attribute requested by the symbolizer
1540:             * 
1541:             * @param f
1542:             *            The feature
1543:             * @param s
1544:             *            The symbolizer
1545:             * @return The geometry requested in the symbolizer, or the default geometry
1546:             *         if none is specified
1547:             */
1548:            private com.vividsolutions.jts.geom.Geometry findGeometry(
1549:                    Feature f, Symbolizer s) {
1550:                String geomName = getGeometryPropertyName(s);
1551:
1552:                // get the geometry
1553:                Geometry geom;
1554:
1555:                if (geomName == null) {
1556:                    geom = f.getDefaultGeometry();
1557:                } else {
1558:                    geom = (com.vividsolutions.jts.geom.Geometry) f
1559:                            .getAttribute(geomName);
1560:                }
1561:
1562:                // if the symbolizer is a point symbolizer generate a suitable location
1563:                // to place the
1564:                // point in order to avoid recomputing that location at each rendering
1565:                // step
1566:                if (s instanceof  PointSymbolizer) {
1567:                    geom = getCentroid(geom); // djb: major simpificatioN
1568:                }
1569:
1570:                return geom;
1571:            }
1572:
1573:            /**
1574:             * Returns the default geometry in the feature.
1575:             * 
1576:             * @param f
1577:             *            feature to find the geometry in
1578:             * @return
1579:             */
1580:            private com.vividsolutions.jts.geom.Geometry findGeometry(Feature f) {
1581:                // get the geometry
1582:                Geometry geom = f.getDefaultGeometry();
1583:
1584:                // CoordinateReferenceSystem sourceCRS =
1585:                // f.getFeatureType().getDefaultGeometry().getCoordinateSystem();
1586:                if (!CRS.equalsIgnoreMetadata(sourceCrs, this .mapContext
1587:                        .getCoordinateReferenceSystem())) {
1588:                    try {
1589:                        MathTransform transform = CRS.findMathTransform(
1590:                                sourceCrs, this .mapContext
1591:                                        .getCoordinateReferenceSystem(), true);
1592:                        geom = JTS.transform(geom, transform);
1593:                    } catch (MismatchedDimensionException e) {
1594:                        LOGGER.severe(e.getLocalizedMessage());
1595:                    } catch (TransformException e) {
1596:                        LOGGER.severe(e.getLocalizedMessage());
1597:                    } catch (FactoryException e) {
1598:                        LOGGER.severe(e.getLocalizedMessage());
1599:                    }
1600:                }
1601:
1602:                return geom;
1603:            }
1604:
1605:            /**
1606:             * Finds the centroid of the input geometry if input = point, line, polygon
1607:             * --> return a point that represents the centroid of that geom if input =
1608:             * geometry collection --> return a multipoint that represents the centoid
1609:             * of each sub-geom
1610:             * 
1611:             * @param g
1612:             * @return
1613:             */
1614:            public Geometry getCentroid(Geometry g) {
1615:                if (g instanceof  GeometryCollection) {
1616:                    GeometryCollection gc = (GeometryCollection) g;
1617:                    Coordinate[] pts = new Coordinate[gc.getNumGeometries()];
1618:
1619:                    for (int t = 0; t < gc.getNumGeometries(); t++) {
1620:                        pts[t] = gc.getGeometryN(t).getCentroid()
1621:                                .getCoordinate();
1622:                    }
1623:
1624:                    return g.getFactory().createMultiPoint(pts);
1625:                } else {
1626:                    return g.getCentroid();
1627:                }
1628:            }
1629:
1630:            /**
1631:             * Finds the geometric attribute coordinate reference system
1632:             * 
1633:             * @param f
1634:             *            The feature
1635:             * @param s
1636:             *            The symbolizer
1637:             * @return The geometry requested in the symbolizer, or the default geometry
1638:             *         if none is specified
1639:             */
1640:            private org.opengis.referencing.crs.CoordinateReferenceSystem findGeometryCS(
1641:                    Feature f, Symbolizer s) {
1642:                String geomName = getGeometryPropertyName(s);
1643:
1644:                if (geomName != null) {
1645:                    return ((GeometryAttributeType) f.getFeatureType()
1646:                            .getAttributeType(geomName)).getCoordinateSystem();
1647:                } else {
1648:                    return ((GeometryAttributeType) f.getFeatureType()
1649:                            .getDefaultGeometry()).getCoordinateSystem();
1650:                }
1651:            }
1652:
1653:            /**
1654:             * Utility method to find which geometry property is referenced by a given
1655:             * symbolizer.
1656:             * 
1657:             * @param s
1658:             *            The symbolizer
1659:             * @TODO: this is c&p from lite renderer code as the method was private
1660:             *        consider moving to a public unility class.
1661:             */
1662:            private String getGeometryPropertyName(Symbolizer s) {
1663:                String geomName = null;
1664:
1665:                // TODO: fix the styles, the getGeometryPropertyName should probably be
1666:                // moved into an
1667:                // interface...
1668:                if (s instanceof  PolygonSymbolizer) {
1669:                    geomName = ((PolygonSymbolizer) s)
1670:                            .getGeometryPropertyName();
1671:                } else if (s instanceof  PointSymbolizer) {
1672:                    geomName = ((PointSymbolizer) s).getGeometryPropertyName();
1673:                } else if (s instanceof  LineSymbolizer) {
1674:                    geomName = ((LineSymbolizer) s).getGeometryPropertyName();
1675:                } else if (s instanceof  TextSymbolizer) {
1676:                    geomName = ((TextSymbolizer) s).getGeometryPropertyName();
1677:                }
1678:
1679:                return geomName;
1680:            }
1681:
1682:            /**
1683:             * Utility method to convert an int into hex, padded to two characters.
1684:             * handy for generating colour strings.
1685:             * 
1686:             * @param i
1687:             *            Int to convert
1688:             * @return String a two character hex representation of i NOTE: this is a
1689:             *         utility method and should be put somewhere more useful.
1690:             */
1691:            protected static String intToHex(int i) {
1692:                String prelim = Integer.toHexString(i);
1693:
1694:                if (prelim.length() < 2) {
1695:                    prelim = "0" + prelim;
1696:                }
1697:
1698:                return prelim;
1699:            }
1700:
1701:            /**
1702:             * Utility method to convert a Color into a KML color ref
1703:             * 
1704:             * @param c
1705:             *            The color to convert
1706:             * @return A string in BBGGRR format - note alpha must be prefixed seperatly
1707:             *         before use.
1708:             */
1709:            private String colorToHex(Color c) {
1710:                return intToHex(c.getBlue()) + intToHex(c.getGreen())
1711:                        + intToHex(c.getRed());
1712:            }
1713:
1714:            /**
1715:             * Borrowed from StreamingRenderer
1716:             * 
1717:             * @param sym
1718:             * @return
1719:             */
1720:            private float getOpacity(final Symbolizer sym) {
1721:                float alpha = 1.0f;
1722:                Expression exp = null;
1723:
1724:                if (sym instanceof  PolygonSymbolizer) {
1725:                    exp = ((PolygonSymbolizer) sym).getFill().getOpacity();
1726:                } else if (sym instanceof  LineSymbolizer) {
1727:                    exp = ((LineSymbolizer) sym).getStroke().getOpacity();
1728:                } else if (sym instanceof  PointSymbolizer) {
1729:                    exp = ((PointSymbolizer) sym).getGraphic().getOpacity();
1730:                } else if (sym instanceof  TextSymbolizer) {
1731:                    exp = ((TextSymbolizer) sym).getFill().getOpacity();
1732:                } else {
1733:                    LOGGER.info("Symbolizer not matched; was of class: " + sym);
1734:                }
1735:
1736:                if (exp == null) {
1737:                    LOGGER
1738:                            .info("Could not determine proper symbolizer opacity.");
1739:
1740:                    return alpha;
1741:                }
1742:
1743:                Float number = (Float) exp.evaluate(null, Float.class);
1744:
1745:                if (number == null) {
1746:                    return alpha;
1747:                }
1748:
1749:                return number.floatValue();
1750:            }
1751:
1752:            private float getOpacity(final Expression exp) {
1753:                float alpha = 1.0f;
1754:
1755:                Float number = (Float) exp.evaluate(null, Float.class);
1756:
1757:                if (number == null) {
1758:                    return alpha;
1759:                }
1760:
1761:                return number.floatValue();
1762:            }
1763:
1764:            private int getWidth(final Expression exp) {
1765:                int defaultWidth = 1;
1766:
1767:                Integer number = (Integer) exp.evaluate(null, Integer.class);
1768:
1769:                if (number == null) {
1770:                    return defaultWidth;
1771:                }
1772:
1773:                return number.intValue();
1774:            }
1775:        }
w_w__w__.__j___a___v__a___2_s__.c_o___m__ | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.