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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26