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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 361 - (show annotations)
Mon Aug 31 18:21:17 2009 UTC (15 years, 6 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/LegendIconFeatureRenderer.java
File size: 19526 byte(s)


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26