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


001:        /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org.  All rights reserved.
002:         * This code is licensed under the GPL 2.0 license, availible at the root
003:         * application directory.
004:         */
005:        package org.vfny.geoserver.wms.responses;
006:
007:        import java.awt.Canvas;
008:        import java.awt.Color;
009:        import java.awt.Font;
010:        import java.awt.Graphics2D;
011:        import java.awt.RenderingHints;
012:        import java.awt.geom.Rectangle2D;
013:        import java.awt.image.BufferedImage;
014:        import java.awt.image.ImageObserver;
015:        import java.awt.image.IndexColorModel;
016:        import java.awt.image.RenderedImage;
017:        import java.io.File;
018:        import java.io.IOException;
019:        import java.net.MalformedURLException;
020:        import java.net.URL;
021:        import java.util.ArrayList;
022:        import java.util.HashMap;
023:        import java.util.List;
024:        import java.util.Map;
025:        import java.util.StringTokenizer;
026:        import java.util.logging.Level;
027:        import java.util.logging.Logger;
028:
029:        import javax.imageio.ImageIO;
030:
031:        import org.geotools.feature.AttributeType;
032:        import org.geotools.feature.Feature;
033:        import org.geotools.feature.FeatureType;
034:        import org.geotools.feature.IllegalAttributeException;
035:        import org.geotools.geometry.jts.LiteShape2;
036:        import org.geotools.renderer.lite.StyledShapePainter;
037:        import org.geotools.renderer.style.SLDStyleFactory;
038:        import org.geotools.renderer.style.Style2D;
039:        import org.geotools.styling.FeatureTypeStyle;
040:        import org.geotools.styling.LineSymbolizer;
041:        import org.geotools.styling.PointSymbolizer;
042:        import org.geotools.styling.PolygonSymbolizer;
043:        import org.geotools.styling.RasterSymbolizer;
044:        import org.geotools.styling.Rule;
045:        import org.geotools.styling.Style;
046:        import org.geotools.styling.Symbolizer;
047:        import org.geotools.styling.TextSymbolizer;
048:        import org.geotools.util.NumberRange;
049:        import org.vfny.geoserver.global.GeoserverDataDirectory;
050:        import org.vfny.geoserver.wms.GetLegendGraphicProducer;
051:        import org.vfny.geoserver.wms.WmsException;
052:        import org.vfny.geoserver.wms.requests.GetLegendGraphicRequest;
053:
054:        import com.vividsolutions.jts.geom.Coordinate;
055:        import com.vividsolutions.jts.geom.GeometryFactory;
056:        import com.vividsolutions.jts.geom.LineString;
057:        import com.vividsolutions.jts.geom.LinearRing;
058:        import com.vividsolutions.jts.geom.Polygon;
059:
060:        /**
061:         * Template {@linkPlain
062:         * org.vfny.geoserver.responses.wms.GetLegendGraphicProducer} based on
063:         * GeoTools' {@link
064:         * http://svn.geotools.org/geotools/trunk/gt/module/main/src/org/geotools/renderer/lite/StyledShapePainter.java
065:         * StyledShapePainter} that produces a BufferedImage with the appropiate
066:         * legend graphic for a given GetLegendGraphic WMS request.
067:         *
068:         * <p>
069:         * It should be enough for a subclass to implement {@linkPlain
070:         * org.vfny.geoserver.responses.wms.GetLegendGraphicProducer#writeTo(OutputStream)}
071:         * and <code>getContentType()</code> in order to encode the BufferedImage
072:         * produced by this class to the appropiate output format.
073:         * </p>
074:         *
075:         * <p>
076:         * This class takes literally the fact that the arguments <code>WIDTH</code>
077:         * and <code>HEIGHT</code> are just <i>hints</i> about the desired dimensions
078:         * of the produced graphic, and the need to produce a legend graphic
079:         * representative enough of the SLD style for which it is being generated.
080:         * Thus, if no <code>RULE</code> parameter was passed and the style has more
081:         * than one applicable Rule for the actual scale factor, there will be
082:         * generated a legend graphic of the specified width, but with as many stacked
083:         * graphics as applicable rules were found, providing by this way a
084:         * representative enough legend.
085:         * </p>
086:         *
087:         * @author Gabriel Roldan, Axios Engineering
088:         * @version $Id: DefaultRasterLegendProducer.java 7945 2007-12-05 18:09:38Z aaime $
089:         */
090:        public abstract class DefaultRasterLegendProducer implements 
091:                GetLegendGraphicProducer {
092:            /** shared package's logger */
093:            private static final Logger LOGGER = org.geotools.util.logging.Logging
094:                    .getLogger(DefaultRasterLegendProducer.class.getPackage()
095:                            .getName());
096:
097:            /** Factory that will resolve symbolizers into rendered styles */
098:            private static final SLDStyleFactory styleFactory = new SLDStyleFactory();
099:
100:            /** Tolerance used to compare doubles for equality */
101:            private static final double TOLERANCE = 1e-6;
102:
103:            /**
104:             * Singleton shape painter to serve all legend requests. We can use a
105:             * single shape painter instance as long as it remains thread safe.
106:             */
107:            private static final StyledShapePainter shapePainter = new StyledShapePainter(
108:                    null);
109:
110:            /**
111:             * used to create sample point shapes with LiteShape (not lines nor
112:             * polygons)
113:             */
114:            private static final GeometryFactory geomFac = new GeometryFactory();
115:
116:            /**
117:             * Default Legend graphics background color
118:             */
119:            public static final Color BG_COLOR = Color.WHITE;
120:            /**
121:             * Default label color
122:             */
123:            public static final Color FONT_COLOR = Color.BLACK;
124:            /**
125:             * Image observer to help in creating the stack like legend graphic from
126:             * the images created for each rule
127:             */
128:            private static final ImageObserver imgObs = new Canvas();
129:
130:            /** padding percentaje factor at both sides of the legend. */
131:            private static final float hpaddingFactor = 0.15f;
132:
133:            /** top & bottom padding percentaje factor for the legend */
134:            private static final float vpaddingFactor = 0.15f;
135:
136:            /** The image produced at <code>produceLegendGraphic</code> */
137:            private BufferedImage legendGraphic;
138:
139:            /**
140:             * set to <code>true</code> when <code>abort()</code> gets called,
141:             * indicates that the rendering of the legend graphic should stop
142:             * gracefully as soon as possible
143:             */
144:            private boolean renderingStopRequested;
145:
146:            /**
147:             * Just a holder to avoid creating many polygon shapes from inside
148:             * <code>getSampleShape()</code>
149:             */
150:            private LiteShape2 sampleRect;
151:
152:            /**
153:             * Just a holder to avoid creating many line shapes from inside
154:             * <code>getSampleShape()</code>
155:             */
156:            private LiteShape2 sampleLine;
157:
158:            /**
159:             * Just a holder to avoid creating many point shapes from inside
160:             * <code>getSampleShape()</code>
161:             */
162:            private LiteShape2 samplePoint;
163:
164:            /**
165:             * Default constructor. Subclasses may provide its own with a String
166:             * parameter to establish its desired output format, if they support more
167:             * than one (e.g. a JAI based one)
168:             */
169:            public DefaultRasterLegendProducer() {
170:                super ();
171:            }
172:
173:            /**
174:             * Takes a GetLegendGraphicRequest and produces a BufferedImage that then
175:             * can be used by a subclass to encode it to the appropiate output format.
176:             *
177:             * @param request the "parsed" request, where "parsed" means that it's
178:             *        values are already validated so this method must not take care
179:             *        of verifying the requested layer exists and the like.
180:             *
181:             * @throws WmsException if there are problems creating a "sample" feature
182:             *         instance for the FeatureType <code>request</code> returns as
183:             *         the required layer (which should not occur).
184:             */
185:            public void produceLegendGraphic(GetLegendGraphicRequest request)
186:                    throws WmsException {
187:                final Feature sampleFeature = createSampleFeature(request
188:                        .getLayer());
189:
190:                final Style gt2Style = request.getStyle();
191:                final FeatureTypeStyle[] ftStyles = gt2Style
192:                        .getFeatureTypeStyles();
193:
194:                final double scaleDenominator = request.getScale();
195:
196:                final Rule[] applicableRules;
197:
198:                if (request.getRule() != null) {
199:                    applicableRules = new Rule[] { request.getRule() };
200:                } else {
201:                    applicableRules = getApplicableRules(ftStyles,
202:                            scaleDenominator);
203:                }
204:
205:                final NumberRange scaleRange = new NumberRange(
206:                        scaleDenominator, scaleDenominator);
207:
208:                final int ruleCount = applicableRules.length;
209:
210:                /**
211:                 * A legend graphic is produced for each applicable rule. They're being
212:                 * held here until the process is done and then painted on a "stack"
213:                 * like legend.
214:                 */
215:                final List /*<BufferedImage>*/legendsStack = new ArrayList(
216:                        ruleCount);
217:
218:                final int w = request.getWidth();
219:                final int h = request.getHeight();
220:
221:                final Color bgColor = getBackgroundColor(request);
222:                for (int i = 0; i < ruleCount; i++) {
223:                    Symbolizer[] symbolizers = applicableRules[i]
224:                            .getSymbolizers();
225:
226:                    //BufferedImage image = prepareImage(w, h, request.isTransparent());
227:                    final boolean transparent = request.isTransparent();
228:                    final RenderedImage image = ImageUtils.createImage(w, h,
229:                            (IndexColorModel) null, transparent);
230:                    final Map hintsMap = new HashMap();
231:                    Graphics2D graphics = ImageUtils.prepareTransparency(
232:                            transparent, bgColor, image, hintsMap);
233:                    graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
234:                            RenderingHints.VALUE_ANTIALIAS_ON);
235:
236:                    for (int sIdx = 0; sIdx < symbolizers.length; sIdx++) {
237:                        Symbolizer symbolizer = symbolizers[sIdx];
238:
239:                        if (symbolizer instanceof  RasterSymbolizer) {
240:                            BufferedImage imgShape;
241:
242:                            try {
243:                                File styles = GeoserverDataDirectory
244:                                        .findCreateConfigDir("styles");
245:                                File rasterLegend = new File(styles,
246:                                        "rasterLegend.png");
247:                                if (rasterLegend.exists())
248:                                    imgShape = ImageIO.read(rasterLegend);
249:                                else
250:                                    imgShape = ImageIO
251:                                            .read(DefaultRasterLegendProducer.class
252:                                                    .getResource("rasterLegend.png"));
253:                            } catch (Exception e) {
254:                                LOGGER.log(Level.SEVERE, e
255:                                        .getLocalizedMessage(), e);
256:                                throw new WmsException(e);
257:                            }
258:
259:                            graphics.drawImage(imgShape, 0, 0, w, h, null);
260:                        } else {
261:                            Style2D style2d = styleFactory.createStyle(
262:                                    sampleFeature, symbolizer, scaleRange);
263:                            LiteShape2 shape = getSampleShape(symbolizer, w, h);
264:
265:                            if (style2d != null) {
266:                                shapePainter.paint(graphics, shape, style2d,
267:                                        scaleDenominator);
268:                            }
269:                        }
270:                    }
271:
272:                    legendsStack.add(image);
273:                }
274:
275:                //JD: changd legend behaviour, see GEOS-812
276:                //this.legendGraphic = scaleImage(mergeLegends(legendsStack), request);
277:                this .legendGraphic = mergeLegends(legendsStack,
278:                        applicableRules, request);
279:            }
280:
281:            /**
282:             * Recieves a list of <code>BufferedImages</code> and produces a new one
283:             * which holds all  the images in <code>imageStack</code> one above the
284:             * other.
285:             *
286:             * @param imageStack the list of BufferedImages, one for each applicable
287:             *        Rule
288:             * @param rules The applicable rules, one for each image in the stack
289:             * @param request The request.
290:             *
291:             * @return the stack image with all the images on the argument list.
292:             *
293:             * @throws IllegalArgumentException if the list is empty
294:             */
295:            private static BufferedImage mergeLegends(List imageStack,
296:                    Rule[] rules, GetLegendGraphicRequest req) {
297:
298:                Font labelFont = getLabelFont(req);
299:                boolean useAA = false;
300:                if (req.getLegendOptions().get("fontAntiAliasing") instanceof  String) {
301:                    String aaVal = (String) req.getLegendOptions().get(
302:                            "fontAntiAliasing");
303:                    if (aaVal.equalsIgnoreCase("on")
304:                            || aaVal.equalsIgnoreCase("true")
305:                            || aaVal.equalsIgnoreCase("yes")
306:                            || aaVal.equalsIgnoreCase("1")) {
307:                        useAA = true;
308:                    }
309:                }
310:
311:                boolean forceLabelsOn = false;
312:                boolean forceLabelsOff = false;
313:                if (req.getLegendOptions().get("forceLabels") instanceof  String) {
314:                    String forceLabelsOpt = (String) req.getLegendOptions()
315:                            .get("forceLabels");
316:                    if (forceLabelsOpt.equalsIgnoreCase("on")) {
317:                        forceLabelsOn = true;
318:                    } else if (forceLabelsOpt.equalsIgnoreCase("off")) {
319:                        forceLabelsOff = true;
320:                    }
321:                }
322:
323:                if (imageStack.size() == 0) {
324:                    throw new IllegalArgumentException(
325:                            "No legend graphics passed");
326:                }
327:
328:                final BufferedImage finalLegend;
329:
330:                if (imageStack.size() == 1 && !forceLabelsOn) {
331:                    finalLegend = (BufferedImage) imageStack.get(0);
332:                } else {
333:                    final int imgCount = imageStack.size();
334:                    final String[] labels = new String[imgCount];
335:
336:                    BufferedImage img = ((BufferedImage) imageStack.get(0));
337:
338:                    int totalHeight = 0;
339:                    int totalWidth = 0;
340:                    int[] rowHeights = new int[imgCount];
341:
342:                    for (int i = 0; i < imgCount; i++) {
343:                        img = (BufferedImage) imageStack.get(i);
344:
345:                        if (forceLabelsOff) {
346:                            totalWidth = (int) Math.ceil(Math.max(img
347:                                    .getWidth(), totalWidth));
348:                            rowHeights[i] = img.getHeight();
349:                            totalHeight += img.getHeight();
350:                        } else {
351:
352:                            Rule rule = rules[i];
353:
354:                            //What's the label on this rule?  We prefer to use
355:                            //the 'title' if it's available, but fall-back to 'name'
356:                            labels[i] = rule.getTitle();
357:                            if (labels[i] == null)
358:                                labels[i] = rule.getName();
359:                            if (labels[i] == null)
360:                                labels[i] = "";
361:
362:                            Graphics2D g = img.createGraphics();
363:                            g.setFont(labelFont);
364:
365:                            if (useAA) {
366:                                g.setRenderingHint(
367:                                        RenderingHints.KEY_TEXT_ANTIALIASING,
368:                                        RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
369:                            } else {
370:                                g
371:                                        .setRenderingHint(
372:                                                RenderingHints.KEY_TEXT_ANTIALIASING,
373:                                                RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
374:                            }
375:
376:                            if (labels[i] != null && labels[i].length() > 0) {
377:                                final BufferedImage renderedLabel = renderLabel(
378:                                        labels[i], g, req);
379:                                final Rectangle2D bounds = new Rectangle2D.Double(
380:                                        0, 0, renderedLabel.getWidth(),
381:                                        renderedLabel.getHeight());
382:
383:                                totalWidth = (int) Math.ceil(Math.max(img
384:                                        .getWidth()
385:                                        + bounds.getWidth(), totalWidth));
386:                                rowHeights[i] = (int) Math.ceil(Math.max(img
387:                                        .getHeight(), bounds.getHeight()));
388:                            } else {
389:                                totalWidth = (int) Math.ceil(Math.max(img
390:                                        .getWidth(), totalWidth));
391:                                rowHeights[i] = (int) Math
392:                                        .ceil(img.getHeight());
393:                            }
394:                            totalHeight += rowHeights[i];
395:                        }
396:                    }
397:
398:                    //buffer the width a bit
399:                    totalWidth += 2;
400:
401:                    final boolean transparent = req.isTransparent();
402:                    final Color backgroundColor = getBackgroundColor(req);
403:                    final Map hintsMap = new HashMap();
404:                    //create the final image
405:                    finalLegend = ImageUtils.createImage(totalWidth,
406:                            totalHeight, (IndexColorModel) null, transparent);
407:                    Graphics2D finalGraphics = ImageUtils
408:                            .prepareTransparency(transparent, backgroundColor,
409:                                    finalLegend, hintsMap);
410:
411:                    int topOfRow = 0;
412:
413:                    for (int i = 0; i < imgCount; i++) {
414:                        img = (BufferedImage) imageStack.get(i);
415:
416:                        //draw the image
417:                        int y = topOfRow;
418:
419:                        if (img.getHeight() < rowHeights[i]) {
420:                            //move the image to the center of the row
421:                            y += (int) ((rowHeights[i] - img.getHeight()) / 2d);
422:                        }
423:
424:                        finalGraphics.drawImage(img, 0, y, imgObs);
425:                        if (forceLabelsOff) {
426:                            topOfRow += rowHeights[i];
427:                            continue;
428:                        }
429:
430:                        finalGraphics.setFont(labelFont);
431:
432:                        if (useAA) {
433:                            finalGraphics.setRenderingHint(
434:                                    RenderingHints.KEY_TEXT_ANTIALIASING,
435:                                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
436:                        } else {
437:                            finalGraphics.setRenderingHint(
438:                                    RenderingHints.KEY_TEXT_ANTIALIASING,
439:                                    RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
440:                        }
441:
442:                        //draw the label
443:                        if (labels[i] != null && labels[i].length() > 0) {
444:                            //first create the actual overall label image.
445:                            final BufferedImage renderedLabel = renderLabel(
446:                                    labels[i], finalGraphics, req);
447:
448:                            y = topOfRow;
449:
450:                            if (renderedLabel.getHeight() < rowHeights[i]) {
451:                                y += (int) ((rowHeights[i] - renderedLabel
452:                                        .getHeight()) / 2d);
453:                            }
454:
455:                            finalGraphics.drawImage(renderedLabel, img
456:                                    .getWidth(), y, imgObs);
457:                        }
458:
459:                        topOfRow += rowHeights[i];
460:                    }
461:                }
462:
463:                return finalLegend;
464:            }
465:
466:            private static Font getLabelFont(GetLegendGraphicRequest req) {
467:
468:                String legendFontName = "Sans-Serif";
469:                String legendFontFamily = "plain";
470:                int legendFontSize = 12;
471:
472:                Map legendOptions = req.getLegendOptions();
473:                if (legendOptions.get("fontName") != null) {
474:                    legendFontName = (String) legendOptions.get("fontName");
475:                }
476:                if (legendOptions.get("fontStyle") != null) {
477:                    legendFontFamily = (String) legendOptions.get("fontStyle");
478:                }
479:                if (legendOptions.get("fontSize") != null) {
480:                    try {
481:                        legendFontSize = Integer
482:                                .parseInt((String) legendOptions
483:                                        .get("fontSize"));
484:                    } catch (Exception e) {
485:                        LOGGER
486:                                .warning("Error trying to interpret legendOption 'fontSize': "
487:                                        + legendOptions.get("fontSize"));
488:                    }
489:                }
490:
491:                Font legendFont;
492:                if (legendFontFamily.equalsIgnoreCase("italic")) {
493:                    legendFont = new Font(legendFontName, Font.ITALIC,
494:                            legendFontSize);
495:                } else if (legendFontFamily.equalsIgnoreCase("bold")) {
496:                    legendFont = new Font(legendFontName, Font.BOLD,
497:                            legendFontSize);
498:                } else {
499:                    legendFont = new Font(legendFontName, Font.PLAIN,
500:                            legendFontSize);
501:                }
502:
503:                return legendFont;
504:            }
505:
506:            private static Color getLabelFontColor(GetLegendGraphicRequest req) {
507:                Map legendOptions = req.getLegendOptions();
508:                String color = (String) legendOptions.get("fontColor");
509:                if (color == null) {
510:                    //return the default
511:                    return FONT_COLOR;
512:                }
513:
514:                try {
515:                    return color(color);
516:                } catch (NumberFormatException e) {
517:                    LOGGER.warning("Could not decode label color: " + color
518:                            + ", default to " + FONT_COLOR.toString());
519:                    return FONT_COLOR;
520:                }
521:            }
522:
523:            /**
524:             * Returns the image background color for the given
525:             * {@link GetLegendGraphicRequest}.
526:             * 
527:             * @param req
528:             * @return the Color for the hexadecimal value passed as the
529:             *         <code>BGCOLOR</code>
530:             *         {@link GetLegendGraphicRequest#getLegendOptions() legend option},
531:             *         or the default background color if no bgcolor were passed.
532:             */
533:            private static Color getBackgroundColor(GetLegendGraphicRequest req) {
534:                Map legendOptions = req.getLegendOptions();
535:                String color = (String) legendOptions.get("bgColor");
536:                if (color == null) {
537:                    //return the default
538:                    return BG_COLOR;
539:                }
540:
541:                try {
542:                    return color(color);
543:                } catch (NumberFormatException e) {
544:                    LOGGER.warning("Could not decode background color: "
545:                            + color + ", default to " + BG_COLOR.toString());
546:                    return BG_COLOR;
547:                }
548:
549:            }
550:
551:            private static Color color(String hex) throws NumberFormatException {
552:                if (!hex.startsWith("#")) {
553:                    hex = "#" + hex;
554:                }
555:                return Color.decode(hex);
556:            }
557:
558:            /**
559:             * Return a {@link BufferedImage} representing this label.
560:             * The characters '\n' '\r' and '\f' are interpreted as linebreaks,
561:             * as is the characater combination "\n" (as opposed to the actual '\n' character).
562:             * This allows people to force line breaks in their labels by
563:             * including the character "\" followed by "n" in their
564:             * label.
565:             *
566:             * @param label - the label to render
567:             * @param g - the Graphics2D that will be used to render this label
568:             * @return a {@link BufferedImage} of the properly rendered label.
569:             */
570:            public static BufferedImage renderLabel(String label, Graphics2D g,
571:                    GetLegendGraphicRequest req) {
572:                // We'll accept '/n' as a text string
573:                //to indicate a line break, as well as a traditional 'real' line-break in the XML.
574:                BufferedImage renderedLabel;
575:                Color labelColor = getLabelFontColor(req);
576:                if ((label.indexOf("\n") != -1) || (label.indexOf("\\n") != -1)) {
577:                    //this is a label WITH line-breaks...we need to figure out it's height *and*
578:                    //width, and then adjust the legend size accordingly
579:                    Rectangle2D bounds = new Rectangle2D.Double(0, 0, 0, 0);
580:                    ArrayList lineHeight = new ArrayList();
581:                    // four backslashes... "\\" -> '\', so "\\\\n" -> '\' + '\' + 'n'
582:                    final String realLabel = label.replaceAll("\\\\n", "\n");
583:                    StringTokenizer st = new StringTokenizer(realLabel,
584:                            "\n\r\f");
585:
586:                    while (st.hasMoreElements()) {
587:                        final String token = st.nextToken();
588:                        Rectangle2D this LineBounds = g.getFontMetrics()
589:                                .getStringBounds(token, g);
590:
591:                        //if this is directly added as thisLineBounds.getHeight(), then there are rounding errors
592:                        //because we can only DRAW fonts at discrete integer coords.
593:                        final int this LineHeight = (int) Math
594:                                .ceil(this LineBounds.getHeight());
595:                        bounds.add(0, this LineHeight + bounds.getHeight());
596:                        bounds.add(this LineBounds.getWidth(), 0);
597:                        lineHeight.add(new Integer((int) Math
598:                                .ceil(this LineBounds.getHeight())));
599:                    }
600:
601:                    //make the actual label image
602:                    renderedLabel = new BufferedImage((int) Math.ceil(bounds
603:                            .getWidth()), (int) Math.ceil(bounds.getHeight()),
604:                            BufferedImage.TYPE_INT_ARGB);
605:
606:                    st = new StringTokenizer(realLabel, "\n\r\f");
607:
608:                    Graphics2D rlg = renderedLabel.createGraphics();
609:                    rlg.setColor(labelColor);
610:                    rlg.setFont(g.getFont());
611:                    rlg
612:                            .setRenderingHint(
613:                                    RenderingHints.KEY_TEXT_ANTIALIASING,
614:                                    g
615:                                            .getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING));
616:
617:                    int y = 0 - g.getFontMetrics().getDescent();
618:                    int c = 0;
619:
620:                    while (st.hasMoreElements()) {
621:                        y += ((Integer) lineHeight.get(c++)).intValue();
622:                        rlg.drawString(st.nextToken(), 0, y);
623:                    }
624:                } else {
625:                    //this is a traditional 'regular-old' label.  Just figure the
626:                    //size and act accordingly.
627:                    int height = (int) Math.ceil(g.getFontMetrics()
628:                            .getStringBounds(label, g).getHeight());
629:                    int width = (int) Math.ceil(g.getFontMetrics()
630:                            .getStringBounds(label, g).getWidth());
631:                    renderedLabel = new BufferedImage(width, height,
632:                            BufferedImage.TYPE_INT_ARGB);
633:
634:                    Graphics2D rlg = renderedLabel.createGraphics();
635:                    rlg.setColor(labelColor);
636:                    rlg.setFont(g.getFont());
637:                    rlg
638:                            .setRenderingHint(
639:                                    RenderingHints.KEY_TEXT_ANTIALIASING,
640:                                    g
641:                                            .getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING));
642:                    rlg.drawString(label, 0, height
643:                            - rlg.getFontMetrics().getDescent());
644:                }
645:
646:                return renderedLabel;
647:            }
648:
649:            /**
650:             * Returns a <code>java.awt.Shape</code> appropiate to render a legend
651:             * graphic given the symbolizer type and the legend dimensions.
652:             *
653:             * @param symbolizer the Symbolizer for whose type a sample shape will be
654:             *        created
655:             * @param legendWidth the requested width, in output units, of the legend
656:             *        graphic
657:             * @param legendHeight the requested height, in output units, of the legend
658:             *        graphic
659:             *
660:             * @return an appropiate Line2D, Rectangle2D or LiteShape(Point) for the
661:             *         symbolizer, wether it is a LineSymbolizer, a PolygonSymbolizer,
662:             *         or a Point ot Text Symbolizer
663:             *
664:             * @throws IllegalArgumentException if an unknown symbolizer impl was
665:             *         passed in.
666:             */
667:            private LiteShape2 getSampleShape(Symbolizer symbolizer,
668:                    int legendWidth, int legendHeight) {
669:                LiteShape2 sampleShape;
670:                final float hpad = (legendWidth * hpaddingFactor);
671:                final float vpad = (legendHeight * vpaddingFactor);
672:
673:                if (symbolizer instanceof  LineSymbolizer) {
674:                    if (this .sampleLine == null) {
675:                        Coordinate[] coords = {
676:                                new Coordinate(hpad, legendHeight - vpad),
677:                                new Coordinate(legendWidth - hpad, vpad) };
678:                        LineString geom = geomFac.createLineString(coords);
679:
680:                        try {
681:                            this .sampleLine = new LiteShape2(geom, null, null,
682:                                    false);
683:                        } catch (Exception e) {
684:                            this .sampleLine = null;
685:                        }
686:                    }
687:
688:                    sampleShape = this .sampleLine;
689:                } else if ((symbolizer instanceof  PolygonSymbolizer)
690:                        || (symbolizer instanceof  RasterSymbolizer)) {
691:                    if (this .sampleRect == null) {
692:                        final float w = legendWidth - (2 * hpad);
693:                        final float h = legendHeight - (2 * vpad);
694:
695:                        Coordinate[] coords = { new Coordinate(hpad, vpad),
696:                                new Coordinate(hpad, vpad + h),
697:                                new Coordinate(hpad + w, vpad + h),
698:                                new Coordinate(hpad + w, vpad),
699:                                new Coordinate(hpad, vpad) };
700:                        LinearRing shell = geomFac.createLinearRing(coords);
701:                        Polygon geom = geomFac.createPolygon(shell, null);
702:
703:                        try {
704:                            this .sampleRect = new LiteShape2(geom, null, null,
705:                                    false);
706:                        } catch (Exception e) {
707:                            this .sampleRect = null;
708:                        }
709:                    }
710:
711:                    sampleShape = this .sampleRect;
712:                } else if (symbolizer instanceof  PointSymbolizer
713:                        || symbolizer instanceof  TextSymbolizer) {
714:                    if (this .samplePoint == null) {
715:                        Coordinate coord = new Coordinate(legendWidth / 2,
716:                                legendHeight / 2);
717:
718:                        try {
719:                            this .samplePoint = new LiteShape2(geomFac
720:                                    .createPoint(coord), null, null, false);
721:                        } catch (Exception e) {
722:                            this .samplePoint = null;
723:                        }
724:                    }
725:
726:                    sampleShape = this .samplePoint;
727:                } else {
728:                    throw new IllegalArgumentException("Unknown symbolizer: "
729:                            + symbolizer);
730:                }
731:
732:                return sampleShape;
733:            }
734:
735:            /**
736:             * Creates a sample Feature instance in the hope that it can be used in the
737:             * rendering of the legend graphic.
738:             *
739:             * @param schema the schema for which to create a sample Feature instance
740:             *
741:             * @return
742:             *
743:             * @throws WmsException
744:             */
745:            private Feature createSampleFeature(FeatureType schema)
746:                    throws WmsException {
747:                Feature sampleFeature;
748:
749:                try {
750:                    AttributeType[] atts = schema.getAttributeTypes();
751:                    Object[] attributes = new Object[atts.length];
752:
753:                    for (int i = 0; i < atts.length; i++)
754:                        attributes[i] = atts[i].createDefaultValue();
755:
756:                    sampleFeature = schema.create(attributes);
757:                } catch (IllegalAttributeException e) {
758:                    e.printStackTrace();
759:                    throw new WmsException(e);
760:                }
761:
762:                return sampleFeature;
763:            }
764:
765:            /**
766:             * Finds the applicable Rules for the given scale denominator.
767:             *
768:             * @param ftStyles
769:             * @param scaleDenominator
770:             *
771:             * @return
772:             */
773:            private Rule[] getApplicableRules(FeatureTypeStyle[] ftStyles,
774:                    double scaleDenominator) {
775:                /**
776:                 * Holds both the rules that apply and the ElseRule's if any, in the
777:                 * order they appear
778:                 */
779:                final List ruleList = new ArrayList();
780:
781:                // get applicable rules at the current scale
782:                for (int i = 0; i < ftStyles.length; i++) {
783:                    FeatureTypeStyle fts = ftStyles[i];
784:                    Rule[] rules = fts.getRules();
785:
786:                    for (int j = 0; j < rules.length; j++) {
787:                        Rule r = rules[j];
788:
789:                        if (isWithInScale(r, scaleDenominator)) {
790:                            ruleList.add(r);
791:
792:                            /*
793:                             * I'm commented this out since I guess it has no sense
794:                             * for producing the legend, since wether or not the rule
795:                             * has an else filter, the legend is drawn only if the
796:                             * scale denominator lies inside the rule's scale range.
797:                                      if (r.hasElseFilter()) {
798:                                          ruleList.add(r);
799:                                      }
800:                             */
801:                        }
802:                    }
803:                }
804:
805:                return (Rule[]) ruleList.toArray(new Rule[ruleList.size()]);
806:            }
807:
808:            /**
809:             * Checks if a rule can be triggered at the current scale level
810:             *
811:             * @param r The rule
812:             * @param scaleDenominator the scale denominator to check if it is between
813:             *        the rule's scale range. -1 means that it allways is.
814:             *
815:             * @return true if the scale is compatible with the rule settings
816:             */
817:            private boolean isWithInScale(Rule r, double scaleDenominator) {
818:                return (scaleDenominator == -1)
819:                        || (((r.getMinScaleDenominator() - TOLERANCE) <= scaleDenominator) && ((r
820:                                .getMaxScaleDenominator() + TOLERANCE) > scaleDenominator));
821:            }
822:
823:            /**
824:             * DOCUMENT ME!
825:             *
826:             * @return
827:             *
828:             * @throws IllegalStateException DOCUMENT ME!
829:             */
830:            public BufferedImage getLegendGraphic() {
831:                if (this .legendGraphic == null) {
832:                    throw new IllegalStateException();
833:                }
834:
835:                return this .legendGraphic;
836:            }
837:
838:            /**
839:             * Asks the rendering to stop processing.
840:             */
841:            public void abort() {
842:                this .renderingStopRequested = true;
843:            }
844:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.