/[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 452 - (hide annotations)
Fri Oct 9 19:16:27 2009 UTC (15 years, 4 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/LegendIconFeatureRenderer.java
File size: 19607 byte(s)
* AS-Feature: The labeling tab now has a preview pane and can set the priority column. Both still has to be improved.
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 452 // TODO TEST, whether we still need to recreate the renderer here....
115     // TODO TEST, whether we still need to recreate the renderer here....
116     // TODO TEST, whether we still need to recreate the renderer here....
117     // TODO TEST, whether we still need to recreate the renderer here....
118     // TODO TEST, whether we still need to recreate the renderer here....
119     // TODO TEST, whether we still need to recreate the renderer here....
120     // TODO TEST, whether we still need to recreate the renderer here....
121 alfonx 154 return new LegendIconFeatureRenderer();
122     // return instance;
123     }
124    
125     /**
126     * used to create sample point shapes with LiteShape (not lines nor
127     * polygons)
128     */
129 alfonx 377 private static final GeometryFactory geomFac = FeatureUtil.GEOMETRY_FACTORY;
130 alfonx 154
131     /** padding percentage factor at both sides of the legend. */
132     private static final float hpaddingFactor = 0.11f; // was 0.15
133    
134     /** top & bottom padding percentage factor for the legend */
135     private static final float vpaddingFactor = 0.08f; // was 0.15
136    
137     /**
138     * Just a holder to avoid creating many polygon shapes from inside
139     * <code>getSampleShape()</code>
140     */
141     private LiteShape2 sampleRect;
142    
143     /**
144     * Just a holder to avoid creating many line shapes from inside
145     * <code>getSampleShape()</code>
146     */
147     private LiteShape2 sampleLine;
148    
149     /**
150     * Just a holder to avoid creating many point shapes from inside
151     * <code>getSampleShape()</code>
152     */
153     private LiteShape2 samplePoint;
154    
155     private Hints hints;
156    
157     /**
158     * Returns a <code>java.awt.Shape</code> appropiate to render a legend
159     * graphic given the symbolizer type and the legend dimensions.
160     *
161     * @param symbolizer
162     * the Symbolizer for whose type a sample shape will be created
163     * @param legendWidth
164     * the requested width, in output units, of the legend graphic
165     * @param legendHeight
166     * the requested height, in output units, of the legend graphic
167     *
168     * @return an appropiate Line2D, Rectangle2D or LiteShape(Point) for the
169     * symbolizer, wether it is a LineSymbolizer, a PolygonSymbolizer,
170     * or a Point ot Text Symbolizer
171     *
172     * @throws IllegalArgumentException
173     * if an unknown symbolizer impl was passed in.
174     */
175     private LiteShape2 getSampleShape(Symbolizer symbolizer, int legendWidth,
176     int legendHeight) {
177     LiteShape2 sampleShape;
178     final float hpad = (legendWidth * hpaddingFactor);
179     final float vpad = (legendHeight * vpaddingFactor);
180    
181     if (symbolizer instanceof LineSymbolizer) {
182     if (this.sampleLine == null) {
183     Coordinate[] coords = {
184     new Coordinate(hpad, legendHeight - vpad),
185     new Coordinate(legendWidth - hpad, vpad) };
186     LineString geom = geomFac.createLineString(coords);
187    
188     try {
189     this.sampleLine = new LiteShape2(geom, null, null, false);
190     } catch (Exception e) {
191     this.sampleLine = null;
192     }
193     }
194    
195     sampleShape = this.sampleLine;
196     } else if ((symbolizer instanceof PolygonSymbolizer)
197     || (symbolizer instanceof RasterSymbolizer)) {
198     if (this.sampleRect == null) {
199     final float w = legendWidth - (2 * hpad);
200     final float h = legendHeight - (2 * vpad);
201    
202     Coordinate[] coords = { new Coordinate(hpad, vpad),
203     new Coordinate(hpad, vpad + h),
204     new Coordinate(hpad + w, vpad + h),
205     new Coordinate(hpad + w, vpad),
206     new Coordinate(hpad, vpad) };
207     LinearRing shell = geomFac.createLinearRing(coords);
208     Polygon geom = geomFac.createPolygon(shell, null);
209    
210     try {
211     this.sampleRect = new LiteShape2(geom, null, null, false);
212     } catch (Exception e) {
213     this.sampleRect = null;
214     }
215     }
216    
217     sampleShape = this.sampleRect;
218     } else if (symbolizer instanceof PointSymbolizer
219     || symbolizer instanceof TextSymbolizer) {
220     if (this.samplePoint == null) {
221     Coordinate coord = new Coordinate(legendWidth / 2,
222     legendHeight / 2);
223    
224     try {
225     this.samplePoint = new LiteShape2(geomFac
226     .createPoint(coord), null, null, false);
227     } catch (Exception e) {
228     this.samplePoint = null;
229     }
230     }
231    
232     sampleShape = this.samplePoint;
233     } else {
234     throw new IllegalArgumentException("Unknown symbolizer: "
235     + symbolizer);
236     }
237    
238     return sampleShape;
239     }
240 alfonx 361 //
241     // /**
242     // * Puts a BufferedImage into this.legendGraphic
243     // */
244     // public void produceLegendGraphic(SimpleFeatureType featureType, Style gt2Style,
245     // Rule[] applicableRules) {
246     //
247     // // final FeatureTypeStyle[] ftStyles = gt2Style.getFeatureTypeStyles();
248     //
249     // // if (request.getRule() != null) {
250     // // applicableRules = new Rule[] { request.getRule() };
251     // // } else {
252     // // applicableRules = getApplicableRules(ftStyles, scaleDenominator);
253     // // }
254     //
255     // final int ruleCount = applicableRules.length;
256     //
257     // /**
258     // * A legend graphic is produced for each applicable rule. They're being
259     // * holded here until the process is done and then painted on a "stack"
260     // * like legend.
261     // */
262     // final List<BufferedImage> legendsStack = new ArrayList<BufferedImage>(
263     // ruleCount);
264     //
265     // for (int i = 0; i < ruleCount; i++) {
266     // BufferedImage image = createImageForRule(applicableRules[i],
267     // featureType, SIZE);
268     // legendsStack.add(image);
269     // }
270     //
271     // // this.legendGraphic =
272     // // scaleImage(mergeLegends(legendsStack,applicableRules), request);
273     // this.legendGraphic = mergeLegends(legendsStack, applicableRules);
274     // }
275     //
276     // /**
277     // * Recieves a list of <code>BufferedImages</code> and produces a new one
278     // * which holds all the images in <code>imageStack</code> one above the
279     // * other.
280     // *
281     // * @param imageStack
282     // * the list of BufferedImages, one for each applicable Rule
283     // * @param rules
284     // * The applicable rules, one for each image in the stack
285     // * @param request
286     // * The request.
287     // *
288     // * @return the stack image with all the images on the argument list.
289     // *
290     // * @throws IllegalArgumentException
291     // * if the list is empty
292     // */
293     // private static BufferedImage mergeLegends(List<BufferedImage> imageStack,
294     // Rule[] rules) {
295     // if (imageStack.size() == 0) {
296     // throw new IllegalArgumentException("No legend graphics passed");
297     // }
298     //
299     // BufferedImage finalLegend = null;
300     //
301     // if (imageStack.size() == 1) {
302     // finalLegend = (BufferedImage) imageStack.get(0);
303     // } else {
304     // final int imgCount = imageStack.size();
305     // final String[] labels = new String[imgCount];
306     //
307     // BufferedImage img = ((BufferedImage) imageStack.get(0));
308     // FontMetrics fontMetrics = img.getGraphics().getFontMetrics();
309     //
310     // final int rowHeight = Math.max(fontMetrics.getHeight(), img
311     // .getHeight());
312     //
313     // // calculate the total dimensions of the image
314     // int totalHeight = rowHeight * imgCount;
315     // int totalWidth = 0;
316     //
317     // for (int i = 0; i < imgCount; i++) {
318     // img = (BufferedImage) imageStack.get(i);
319     //
320     // Rule rule = rules[i];
321     //
322     // // does this rule have a label
323     // labels[i] = rule.getTitle();
324     //
325     // if (labels[i] == null) {
326     // labels[i] = rule.getName();
327     // }
328     //
329     // int w = img.getWidth();
330     //
331     // if (labels[i] != null) {
332     // Graphics g = img.getGraphics();
333     // w += g.getFontMetrics().stringWidth(labels[i]);
334     // }
335     //
336     // totalWidth = Math.max(w, totalWidth);
337     // }
338     //
339     // // create the final image
340     // finalLegend = new BufferedImage(totalWidth, totalHeight,
341     // BufferedImage.TYPE_INT_ARGB);
342     //
343     // Graphics2D finalGraphics = finalLegend.createGraphics();
344     //
345     // finalGraphics.setColor(Color.white);
346     // finalGraphics.fillRect(0, 0, totalWidth, totalHeight);
347     //
348     // int h = 0;
349     //
350     // for (int i = 0; i < imgCount; i++) {
351     // img = (BufferedImage) imageStack.get(i);
352     //
353     // // draw the image
354     // int y = h;
355     //
356     // if (img.getHeight() < rowHeight) {
357     // // move the image to the center of the row
358     // y += (int) ((rowHeight - img.getHeight()) / 2d);
359     // }
360     //
361     // finalGraphics.drawImage(img, 0, y, imgObs);
362     //
363     // // draw the label
364     // if (labels[i] != null) {
365     // finalGraphics.setColor(Color.BLACK);
366     //
367     // y = (h + rowHeight) - fontMetrics.getDescent();
368     //
369     // if (fontMetrics.getHeight() < rowHeight) {
370     // // move the baseline to the center of the row
371     // y -= (int) ((rowHeight - fontMetrics.getHeight()) / 2d);
372     // }
373     //
374     // finalGraphics.drawString(labels[i], img.getWidth(), y);
375     // }
376     //
377     // h += rowHeight;
378     // }
379     // }
380     //
381     // return finalLegend;
382     // }
383     //
384     // /**
385     // * DOCUMENT ME!
386     // *
387     // * @return
388     // *
389     // * @throws IllegalStateException
390     // * DOCUMENT ME!
391     // */
392     // public BufferedImage getLegendGraphic() {
393     // if (this.legendGraphic == null) {
394     // throw new IllegalStateException();
395     // }
396     // return this.legendGraphic;
397     // }
398 alfonx 154
399     // /**
400     // * Paints a little rectangle in the color defined by the
401     // * {@link ColorMapEntry}
402     // *
403     // * unused
404     // *
405     // * @param cme
406     // * {@link ColorMapEntry}
407     // * @param cm
408     // * {@link ColorMap} to determine type (VALUES, INTERVALL )
409     // * @param iconWidth
410     // * Size of the rectangle
411     // * @param iconHeight
412     // * Size of the rectangle
413     // * @return {@link ImageIcon} of that color, maybe with a white border
414     // * @author Stefan Alfons Krüger
415     // */
416     // public static ImageIcon createImageForColorMapEntry(ColorMapEntry cme,
417     // ColorMap cm, Integer iconWidth, Integer iconHeight) {
418     //
419     // BufferedImage imageForRule = new BufferedImage(iconWidth, iconHeight,
420     // BufferedImage.TYPE_INT_ARGB);
421     //
422     // final Color color = StylingUtil.getColorFromColorMapEntry(cme);
423     //
424     // // Paint a block
425     // for (int x = 0; x < iconWidth; x++) {
426     // for (int y = 0; y < iconHeight; y++) {
427     // int rgb = color.getRGB();
428     // int rand = 2;
429     //
430     // // Paint a white border if the type is VALUES
431     // if ((x < rand) || (x > iconWidth - rand) || (y < rand)
432     // || (y > iconHeight - rand)) {
433     // if (cm.getType() == ColorMap.TYPE_VALUES) {
434     // rgb = Color.white.getRGB();
435     // }
436     // }
437     // imageForRule.setRGB(x, y, rgb);
438     // }
439     // }
440     // return new ImageIcon(imageForRule);
441     // }
442    
443     /**
444     * Creates a little BufferedImage that presents the Style/Symbols used by
445 alfonx 318 * the {@link MapLegend} to show a legend for the {@link SimpleFeatureType}
446 alfonx 154 *
447     * @param rule
448     * {@link Rule} that provides the text and the style to present
449     * @param featureType
450 alfonx 318 * Schema that describes the kind of the sample {@link SimpleFeature}
451 alfonx 154 * that will be rendered with the {@link Style}
452     * @param bg
453     * Background {@link Color} or <code>null</code>
454     *
455     * @throws IllegalAttributeException
456     */
457 alfonx 318 public BufferedImage createImageForRule(Rule rule, SimpleFeatureType featureType,
458 alfonx 154 Dimension size, Color bg) {
459    
460     Symbolizer[] symbolizers = rule.getSymbolizers();
461    
462     BufferedImage buffImage = new BufferedImage(size.width, size.height,
463     BufferedImage.TYPE_INT_ARGB);
464     Graphics2D graphics = buffImage.createGraphics();
465    
466     if (bg != null) {
467     // ****************************************************************************
468     // The following lines define the backgroup color for the rendered
469     // images
470     // ****************************************************************************
471     graphics.setBackground(bg);
472     graphics.setColor(bg);
473     graphics.fillRect(0, 0, size.width, size.height);
474     }
475    
476     // Enable anti-aliasing for the legend symbols
477     graphics.setRenderingHints(getHints());
478    
479     // TODO scaleDenominator = 100000000000000000000000000000d; Was ist das
480     // fuer ein Quatsch?
481 alfonx 452 final NumberRange<Integer> scaleRange = NumberRange.create(Integer.MAX_VALUE,Integer.MAX_VALUE);
482 alfonx 154
483     try {
484    
485     for (int sIdx = 0; sIdx < symbolizers.length; sIdx++) {
486     Symbolizer symbolizer = symbolizers[sIdx];
487    
488     if (symbolizer instanceof TextSymbolizer) {
489     LOGGER
490     .warn("createImageForRule for a TextSymbolizer. Sure we want that?");
491     continue;
492     }
493    
494     // if (symbolizer instanceof RasterSymbolizer) {
495     // log.warn("createImageForRule method can't be used for
496     // RasterSymbolizers..");
497     // }
498     // else
499 alfonx 318 final SimpleFeature sampleFeature = FeatureUtil.createSampleFeature(featureType);
500 alfonx 154
501     // The SLDStyleFactory has to be recreated, as it seams to cache
502     // some stuff.
503     SLDStyleFactory sldStyleFactory = new SLDStyleFactory();
504     Style2D style2d = sldStyleFactory.createStyle(sampleFeature,
505     symbolizer, scaleRange);
506    
507     LiteShape2 shape = getSampleShape(symbolizer, size.width,
508     size.height);
509    
510     if (style2d != null) {
511     new StyledShapePainter(null).paint(graphics, shape,
512 alfonx 452 style2d, Integer.MAX_VALUE );
513 alfonx 154 }
514     }
515     } catch (Exception e) {
516     LOGGER
517     .error(
518     "Error during createImageForRule, returning empty Image",
519     e);
520     }
521     return buffImage;
522     }
523    
524     /**
525     * Define Java2D and other Hints
526     *
527     * @param hints
528     * @author <a href="mailto:[email protected]">Stefan Alfons
529     * Kr&uuml;ger</a>
530     */
531     public void setHints(Hints hints) {
532     getHints().add(hints);
533     }
534    
535     private Hints getHints() {
536     if (hints == null) {
537     hints = GeoTools.getDefaultHints();
538     hints.put(RenderingHints.KEY_ANTIALIASING,
539     RenderingHints.VALUE_ANTIALIAS_ON);
540     hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
541     RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
542     hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
543     RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
544     hints.put(RenderingHints.KEY_RENDERING,
545     RenderingHints.VALUE_RENDER_QUALITY);
546     hints.put(RenderingHints.KEY_COLOR_RENDERING,
547     RenderingHints.VALUE_COLOR_RENDER_QUALITY);
548     }
549     return hints;
550     }
551    
552     /**
553     * Creates a little BufferedImage that presents the Style/Symbols used by
554 alfonx 318 * the {@link MapLegend} to show a legend for the {@link SimpleFeatureType}
555 alfonx 154 *
556     * @param rule
557     * {@link Rule} that provides the text and the style to present
558     * @param featureType
559 alfonx 318 * Schema that describes the kind of the sample {@link SimpleFeature}
560 alfonx 154 * that will be rendered with the {@link Style}
561     *
562     * @throws IllegalAttributeException
563     */
564     public BufferedImage createImageForRule(final Rule rule,
565 alfonx 318 final SimpleFeatureType featureType, final Dimension size) {
566 alfonx 154 return createImageForRule(rule, featureType, size, null);
567     }
568    
569     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26