/[schmitzm]/trunk/src/skrueger/geotools/LegendIconFeatureRenderer.java
ViewVC logotype

Annotation of /trunk/src/skrueger/geotools/LegendIconFeatureRenderer.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 377 - (hide annotations)
Fri Sep 4 11:33:03 2009 UTC (15 years, 5 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/LegendIconFeatureRenderer.java
File size: 19159 byte(s)
* Patched the ImagePyramid and ImageMosaic classes from GT26-m2 again, because their Transparency doesn't work, nor do they support URLs.
* URL support is not yet fixed.
1 alfonx 244 /*******************************************************************************
2     * Copyright (c) 2009 Martin O. J. Schmitz.
3     *
4     * This file is part of the SCHMITZM library - a collection of utility
5 alfonx 256 * classes based on Java 1.6, focusing (not only) on Java Swing
6 alfonx 244 * and the Geotools library.
7     *
8     * The SCHMITZM project is hosted at:
9     * http://wald.intevation.org/projects/schmitzm/
10     *
11     * This program is free software; you can redistribute it and/or
12     * modify it under the terms of the GNU Lesser General Public License
13     * as published by the Free Software Foundation; either version 3
14     * of the License, or (at your option) any later version.
15     *
16     * This program is distributed in the hope that it will be useful,
17     * but WITHOUT ANY WARRANTY; without even the implied warranty of
18     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19     * GNU General Public License for more details.
20     *
21     * You should have received a copy of the GNU Lesser General Public License (license.txt)
22     * along with this program; if not, write to the Free Software
23     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24     * or try this link: http://www.gnu.org/licenses/lgpl.html
25     *
26     * Contributors:
27     * Martin O. J. Schmitz - initial API and implementation
28     * Stefan A. Krüger - additional utility classes
29     ******************************************************************************/
30 alfonx 154 /**
31     Copyright 2008 Stefan Alfons Krüger and parts from some Geotools code
32    
33     atlas-framework - This file is part of the Atlas Framework
34    
35     This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
36     This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
37     You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
38    
39     Diese Bibliothek ist freie Software; Sie dürfen sie unter den Bedingungen der GNU Lesser General Public License, wie von der Free Software Foundation veröffentlicht, weiterverteilen und/oder modifizieren; entweder gemäß Version 2.1 der Lizenz oder (nach Ihrer Option) jeder späteren Version.
40     Diese Bibliothek wird in der Hoffnung weiterverbreitet, daß sie nützlich sein wird, jedoch OHNE IRGENDEINE GARANTIE, auch ohne die implizierte Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Mehr Details finden Sie in der GNU Lesser General Public License.
41     Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit dieser Bibliothek erhalten haben; falls nicht, schreiben Sie an die Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA.
42     **/
43     package skrueger.geotools;
44    
45     import java.awt.Color;
46     import java.awt.Dimension;
47     import java.awt.Graphics2D;
48     import java.awt.RenderingHints;
49     import java.awt.image.BufferedImage;
50    
51     import javax.swing.tree.DefaultTreeCellRenderer;
52    
53     import org.apache.log4j.Logger;
54     import org.geotools.factory.GeoTools;
55     import org.geotools.factory.Hints;
56     import org.geotools.feature.IllegalAttributeException;
57     import org.geotools.geometry.jts.LiteShape2;
58     import org.geotools.renderer.lite.StyledShapePainter;
59     import org.geotools.renderer.style.SLDStyleFactory;
60     import org.geotools.renderer.style.Style2D;
61     import org.geotools.styling.LineSymbolizer;
62     import org.geotools.styling.PointSymbolizer;
63     import org.geotools.styling.PolygonSymbolizer;
64     import org.geotools.styling.RasterSymbolizer;
65     import org.geotools.styling.Rule;
66     import org.geotools.styling.Style;
67     import org.geotools.styling.Symbolizer;
68     import org.geotools.styling.TextSymbolizer;
69     import org.geotools.util.NumberRange;
70 mojays 325 import org.opengis.feature.simple.SimpleFeature;
71     import org.opengis.feature.simple.SimpleFeatureType;
72 alfonx 332 import org.opengis.feature.type.AttributeDescriptor;
73 alfonx 154
74     import schmitzm.geotools.feature.FeatureUtil;
75    
76     import com.vividsolutions.jts.geom.Coordinate;
77     import com.vividsolutions.jts.geom.GeometryFactory;
78     import com.vividsolutions.jts.geom.LineString;
79     import com.vividsolutions.jts.geom.LinearRing;
80     import com.vividsolutions.jts.geom.Polygon;
81    
82     /**
83     * Based on geoserver!
84     * <hr>
85     * <b>Changes by <a href="mailto:[email protected]">Martin Schmitz</a></b>
86     * <br>
87     * <ul>
88     * <li>07.02.2008:<br>
89 alfonx 318 * Determining the default values of a {@link SimpleFeatureType} by
90     * {@link FeatureUtil#getDefaultAttributeValues(SimpleFeatureType)} instead of using
91 alfonx 332 * {@link AttributeDescriptor#createDefaultValue()} directly, because the latter
92 alfonx 154 * returns {@code null} even though the attribut is not nillable.</li>
93     * </ul>
94     *
95     * @author Stefan Alfons Krüger
96     */
97     public class LegendIconFeatureRenderer extends DefaultTreeCellRenderer {
98 alfonx 377 // private static final Dimension SIZE = new Dimension(30,20);
99 alfonx 154
100     Logger LOGGER = Logger.getLogger(LegendIconFeatureRenderer.class);
101    
102     /**
103     * This is a static class
104     */
105     private LegendIconFeatureRenderer() {
106     }
107    
108     final static LegendIconFeatureRenderer instance = new LegendIconFeatureRenderer();
109    
110     public static LegendIconFeatureRenderer getInstance() {
111     // In GT 2.4.5 it we have to create a new one all the time!
112 alfonx 377
113     // TODO TEST, whether we still need to recreate the renderer here....
114 alfonx 154 return new LegendIconFeatureRenderer();
115     // return instance;
116     }
117    
118     /**
119     * used to create sample point shapes with LiteShape (not lines nor
120     * polygons)
121     */
122 alfonx 377 private static final GeometryFactory geomFac = FeatureUtil.GEOMETRY_FACTORY;
123 alfonx 154
124     /** padding percentage factor at both sides of the legend. */
125     private static final float hpaddingFactor = 0.11f; // was 0.15
126    
127     /** top & bottom padding percentage factor for the legend */
128     private static final float vpaddingFactor = 0.08f; // was 0.15
129    
130     /**
131     * Just a holder to avoid creating many polygon shapes from inside
132     * <code>getSampleShape()</code>
133     */
134     private LiteShape2 sampleRect;
135    
136     /**
137     * Just a holder to avoid creating many line shapes from inside
138     * <code>getSampleShape()</code>
139     */
140     private LiteShape2 sampleLine;
141    
142     /**
143     * Just a holder to avoid creating many point shapes from inside
144     * <code>getSampleShape()</code>
145     */
146     private LiteShape2 samplePoint;
147    
148     private Hints hints;
149    
150     /**
151     * Returns a <code>java.awt.Shape</code> appropiate to render a legend
152     * graphic given the symbolizer type and the legend dimensions.
153     *
154     * @param symbolizer
155     * the Symbolizer for whose type a sample shape will be created
156     * @param legendWidth
157     * the requested width, in output units, of the legend graphic
158     * @param legendHeight
159     * the requested height, in output units, of the legend graphic
160     *
161     * @return an appropiate Line2D, Rectangle2D or LiteShape(Point) for the
162     * symbolizer, wether it is a LineSymbolizer, a PolygonSymbolizer,
163     * or a Point ot Text Symbolizer
164     *
165     * @throws IllegalArgumentException
166     * if an unknown symbolizer impl was passed in.
167     */
168     private LiteShape2 getSampleShape(Symbolizer symbolizer, int legendWidth,
169     int legendHeight) {
170     LiteShape2 sampleShape;
171     final float hpad = (legendWidth * hpaddingFactor);
172     final float vpad = (legendHeight * vpaddingFactor);
173    
174     if (symbolizer instanceof LineSymbolizer) {
175     if (this.sampleLine == null) {
176     Coordinate[] coords = {
177     new Coordinate(hpad, legendHeight - vpad),
178     new Coordinate(legendWidth - hpad, vpad) };
179     LineString geom = geomFac.createLineString(coords);
180    
181     try {
182     this.sampleLine = new LiteShape2(geom, null, null, false);
183     } catch (Exception e) {
184     this.sampleLine = null;
185     }
186     }
187    
188     sampleShape = this.sampleLine;
189     } else if ((symbolizer instanceof PolygonSymbolizer)
190     || (symbolizer instanceof RasterSymbolizer)) {
191     if (this.sampleRect == null) {
192     final float w = legendWidth - (2 * hpad);
193     final float h = legendHeight - (2 * vpad);
194    
195     Coordinate[] coords = { new Coordinate(hpad, vpad),
196     new Coordinate(hpad, vpad + h),
197     new Coordinate(hpad + w, vpad + h),
198     new Coordinate(hpad + w, vpad),
199     new Coordinate(hpad, vpad) };
200     LinearRing shell = geomFac.createLinearRing(coords);
201     Polygon geom = geomFac.createPolygon(shell, null);
202    
203     try {
204     this.sampleRect = new LiteShape2(geom, null, null, false);
205     } catch (Exception e) {
206     this.sampleRect = null;
207     }
208     }
209    
210     sampleShape = this.sampleRect;
211     } else if (symbolizer instanceof PointSymbolizer
212     || symbolizer instanceof TextSymbolizer) {
213     if (this.samplePoint == null) {
214     Coordinate coord = new Coordinate(legendWidth / 2,
215     legendHeight / 2);
216    
217     try {
218     this.samplePoint = new LiteShape2(geomFac
219     .createPoint(coord), null, null, false);
220     } catch (Exception e) {
221     this.samplePoint = null;
222     }
223     }
224    
225     sampleShape = this.samplePoint;
226     } else {
227     throw new IllegalArgumentException("Unknown symbolizer: "
228     + symbolizer);
229     }
230    
231     return sampleShape;
232     }
233 alfonx 361 //
234     // /**
235     // * Puts a BufferedImage into this.legendGraphic
236     // */
237     // public void produceLegendGraphic(SimpleFeatureType featureType, Style gt2Style,
238     // Rule[] applicableRules) {
239     //
240     // // final FeatureTypeStyle[] ftStyles = gt2Style.getFeatureTypeStyles();
241     //
242     // // if (request.getRule() != null) {
243     // // applicableRules = new Rule[] { request.getRule() };
244     // // } else {
245     // // applicableRules = getApplicableRules(ftStyles, scaleDenominator);
246     // // }
247     //
248     // final int ruleCount = applicableRules.length;
249     //
250     // /**
251     // * A legend graphic is produced for each applicable rule. They're being
252     // * holded here until the process is done and then painted on a "stack"
253     // * like legend.
254     // */
255     // final List<BufferedImage> legendsStack = new ArrayList<BufferedImage>(
256     // ruleCount);
257     //
258     // for (int i = 0; i < ruleCount; i++) {
259     // BufferedImage image = createImageForRule(applicableRules[i],
260     // featureType, SIZE);
261     // legendsStack.add(image);
262     // }
263     //
264     // // this.legendGraphic =
265     // // scaleImage(mergeLegends(legendsStack,applicableRules), request);
266     // this.legendGraphic = mergeLegends(legendsStack, applicableRules);
267     // }
268     //
269     // /**
270     // * Recieves a list of <code>BufferedImages</code> and produces a new one
271     // * which holds all the images in <code>imageStack</code> one above the
272     // * other.
273     // *
274     // * @param imageStack
275     // * the list of BufferedImages, one for each applicable Rule
276     // * @param rules
277     // * The applicable rules, one for each image in the stack
278     // * @param request
279     // * The request.
280     // *
281     // * @return the stack image with all the images on the argument list.
282     // *
283     // * @throws IllegalArgumentException
284     // * if the list is empty
285     // */
286     // private static BufferedImage mergeLegends(List<BufferedImage> imageStack,
287     // Rule[] rules) {
288     // if (imageStack.size() == 0) {
289     // throw new IllegalArgumentException("No legend graphics passed");
290     // }
291     //
292     // BufferedImage finalLegend = null;
293     //
294     // if (imageStack.size() == 1) {
295     // finalLegend = (BufferedImage) imageStack.get(0);
296     // } else {
297     // final int imgCount = imageStack.size();
298     // final String[] labels = new String[imgCount];
299     //
300     // BufferedImage img = ((BufferedImage) imageStack.get(0));
301     // FontMetrics fontMetrics = img.getGraphics().getFontMetrics();
302     //
303     // final int rowHeight = Math.max(fontMetrics.getHeight(), img
304     // .getHeight());
305     //
306     // // calculate the total dimensions of the image
307     // int totalHeight = rowHeight * imgCount;
308     // int totalWidth = 0;
309     //
310     // for (int i = 0; i < imgCount; i++) {
311     // img = (BufferedImage) imageStack.get(i);
312     //
313     // Rule rule = rules[i];
314     //
315     // // does this rule have a label
316     // labels[i] = rule.getTitle();
317     //
318     // if (labels[i] == null) {
319     // labels[i] = rule.getName();
320     // }
321     //
322     // int w = img.getWidth();
323     //
324     // if (labels[i] != null) {
325     // Graphics g = img.getGraphics();
326     // w += g.getFontMetrics().stringWidth(labels[i]);
327     // }
328     //
329     // totalWidth = Math.max(w, totalWidth);
330     // }
331     //
332     // // create the final image
333     // finalLegend = new BufferedImage(totalWidth, totalHeight,
334     // BufferedImage.TYPE_INT_ARGB);
335     //
336     // Graphics2D finalGraphics = finalLegend.createGraphics();
337     //
338     // finalGraphics.setColor(Color.white);
339     // finalGraphics.fillRect(0, 0, totalWidth, totalHeight);
340     //
341     // int h = 0;
342     //
343     // for (int i = 0; i < imgCount; i++) {
344     // img = (BufferedImage) imageStack.get(i);
345     //
346     // // draw the image
347     // int y = h;
348     //
349     // if (img.getHeight() < rowHeight) {
350     // // move the image to the center of the row
351     // y += (int) ((rowHeight - img.getHeight()) / 2d);
352     // }
353     //
354     // finalGraphics.drawImage(img, 0, y, imgObs);
355     //
356     // // draw the label
357     // if (labels[i] != null) {
358     // finalGraphics.setColor(Color.BLACK);
359     //
360     // y = (h + rowHeight) - fontMetrics.getDescent();
361     //
362     // if (fontMetrics.getHeight() < rowHeight) {
363     // // move the baseline to the center of the row
364     // y -= (int) ((rowHeight - fontMetrics.getHeight()) / 2d);
365     // }
366     //
367     // finalGraphics.drawString(labels[i], img.getWidth(), y);
368     // }
369     //
370     // h += rowHeight;
371     // }
372     // }
373     //
374     // return finalLegend;
375     // }
376     //
377     // /**
378     // * DOCUMENT ME!
379     // *
380     // * @return
381     // *
382     // * @throws IllegalStateException
383     // * DOCUMENT ME!
384     // */
385     // public BufferedImage getLegendGraphic() {
386     // if (this.legendGraphic == null) {
387     // throw new IllegalStateException();
388     // }
389     // return this.legendGraphic;
390     // }
391 alfonx 154
392     // /**
393     // * Paints a little rectangle in the color defined by the
394     // * {@link ColorMapEntry}
395     // *
396     // * unused
397     // *
398     // * @param cme
399     // * {@link ColorMapEntry}
400     // * @param cm
401     // * {@link ColorMap} to determine type (VALUES, INTERVALL )
402     // * @param iconWidth
403     // * Size of the rectangle
404     // * @param iconHeight
405     // * Size of the rectangle
406     // * @return {@link ImageIcon} of that color, maybe with a white border
407     // * @author Stefan Alfons Krüger
408     // */
409     // public static ImageIcon createImageForColorMapEntry(ColorMapEntry cme,
410     // ColorMap cm, Integer iconWidth, Integer iconHeight) {
411     //
412     // BufferedImage imageForRule = new BufferedImage(iconWidth, iconHeight,
413     // BufferedImage.TYPE_INT_ARGB);
414     //
415     // final Color color = StylingUtil.getColorFromColorMapEntry(cme);
416     //
417     // // Paint a block
418     // for (int x = 0; x < iconWidth; x++) {
419     // for (int y = 0; y < iconHeight; y++) {
420     // int rgb = color.getRGB();
421     // int rand = 2;
422     //
423     // // Paint a white border if the type is VALUES
424     // if ((x < rand) || (x > iconWidth - rand) || (y < rand)
425     // || (y > iconHeight - rand)) {
426     // if (cm.getType() == ColorMap.TYPE_VALUES) {
427     // rgb = Color.white.getRGB();
428     // }
429     // }
430     // imageForRule.setRGB(x, y, rgb);
431     // }
432     // }
433     // return new ImageIcon(imageForRule);
434     // }
435    
436     /**
437     * Creates a little BufferedImage that presents the Style/Symbols used by
438 alfonx 318 * the {@link MapLegend} to show a legend for the {@link SimpleFeatureType}
439 alfonx 154 *
440     * @param rule
441     * {@link Rule} that provides the text and the style to present
442     * @param featureType
443 alfonx 318 * Schema that describes the kind of the sample {@link SimpleFeature}
444 alfonx 154 * that will be rendered with the {@link Style}
445     * @param bg
446     * Background {@link Color} or <code>null</code>
447     *
448     * @throws IllegalAttributeException
449     */
450 alfonx 318 public BufferedImage createImageForRule(Rule rule, SimpleFeatureType featureType,
451 alfonx 154 Dimension size, Color bg) {
452    
453     Symbolizer[] symbolizers = rule.getSymbolizers();
454    
455     BufferedImage buffImage = new BufferedImage(size.width, size.height,
456     BufferedImage.TYPE_INT_ARGB);
457     Graphics2D graphics = buffImage.createGraphics();
458    
459     if (bg != null) {
460     // ****************************************************************************
461     // The following lines define the backgroup color for the rendered
462     // images
463     // ****************************************************************************
464     graphics.setBackground(bg);
465     graphics.setColor(bg);
466     graphics.fillRect(0, 0, size.width, size.height);
467     }
468    
469     // Enable anti-aliasing for the legend symbols
470     graphics.setRenderingHints(getHints());
471    
472     // TODO scaleDenominator = 100000000000000000000000000000d; Was ist das
473     // fuer ein Quatsch?
474     final double scaleDenominator = 100000000000000000000000000000d;
475     final NumberRange scaleRange = new NumberRange(scaleDenominator,
476     scaleDenominator);
477    
478     try {
479    
480     for (int sIdx = 0; sIdx < symbolizers.length; sIdx++) {
481     Symbolizer symbolizer = symbolizers[sIdx];
482    
483     if (symbolizer instanceof TextSymbolizer) {
484     LOGGER
485     .warn("createImageForRule for a TextSymbolizer. Sure we want that?");
486     continue;
487     }
488    
489     // if (symbolizer instanceof RasterSymbolizer) {
490     // log.warn("createImageForRule method can't be used for
491     // RasterSymbolizers..");
492     // }
493     // else
494 alfonx 318 final SimpleFeature sampleFeature = FeatureUtil.createSampleFeature(featureType);
495 alfonx 154
496     // The SLDStyleFactory has to be recreated, as it seams to cache
497     // some stuff.
498     SLDStyleFactory sldStyleFactory = new SLDStyleFactory();
499     Style2D style2d = sldStyleFactory.createStyle(sampleFeature,
500     symbolizer, scaleRange);
501    
502     LiteShape2 shape = getSampleShape(symbolizer, size.width,
503     size.height);
504    
505     if (style2d != null) {
506     new StyledShapePainter(null).paint(graphics, shape,
507     style2d, scaleDenominator);
508     }
509     }
510     } catch (Exception e) {
511     LOGGER
512     .error(
513     "Error during createImageForRule, returning empty Image",
514     e);
515     }
516     return buffImage;
517     }
518    
519     /**
520     * Define Java2D and other Hints
521     *
522     * @param hints
523     * @author <a href="mailto:[email protected]">Stefan Alfons
524     * Kr&uuml;ger</a>
525     */
526     public void setHints(Hints hints) {
527     getHints().add(hints);
528     }
529    
530     private Hints getHints() {
531     if (hints == null) {
532     hints = GeoTools.getDefaultHints();
533     hints.put(RenderingHints.KEY_ANTIALIASING,
534     RenderingHints.VALUE_ANTIALIAS_ON);
535     hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
536     RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
537     hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
538     RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
539     hints.put(RenderingHints.KEY_RENDERING,
540     RenderingHints.VALUE_RENDER_QUALITY);
541     hints.put(RenderingHints.KEY_COLOR_RENDERING,
542     RenderingHints.VALUE_COLOR_RENDER_QUALITY);
543     }
544     return hints;
545     }
546    
547     /**
548     * Creates a little BufferedImage that presents the Style/Symbols used by
549 alfonx 318 * the {@link MapLegend} to show a legend for the {@link SimpleFeatureType}
550 alfonx 154 *
551     * @param rule
552     * {@link Rule} that provides the text and the style to present
553     * @param featureType
554 alfonx 318 * Schema that describes the kind of the sample {@link SimpleFeature}
555 alfonx 154 * that will be rendered with the {@link Style}
556     *
557     * @throws IllegalAttributeException
558     */
559     public BufferedImage createImageForRule(final Rule rule,
560 alfonx 318 final SimpleFeatureType featureType, final Dimension size) {
561 alfonx 154 return createImageForRule(rule, featureType, size, null);
562     }
563    
564     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26