/[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 372 - (show annotations)
Thu Sep 3 18:41:50 2009 UTC (15 years, 5 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/LegendIconFeatureRenderer.java
File size: 19420 byte(s)
* Renamed the folder with the GT JARs from 'gt-2.6' to 'gt'
* Extended the build.xml to write the correct version into the .jnlp files
* Many many small adaptations to GT2.6 .. AtlasStyler is working again.
* Changed all the clone?() methods in StylingUtil to use the DuplicatingStyleVisitor

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26