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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 403 - (show annotations)
Mon Sep 14 13:29:42 2009 UTC (15 years, 5 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/StyledLayerUtil.java
File size: 39998 byte(s)
* Moving towards a better raster legend!
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 package skrueger.geotools;
31
32 import java.awt.Color;
33 import java.awt.Dimension;
34 import java.awt.Graphics;
35 import java.awt.Graphics2D;
36 import java.awt.Rectangle;
37 import java.awt.geom.AffineTransform;
38 import java.awt.image.BufferedImage;
39 import java.awt.image.ColorModel;
40 import java.io.File;
41 import java.io.FileNotFoundException;
42 import java.io.FileWriter;
43 import java.io.IOException;
44 import java.net.URL;
45 import java.text.DecimalFormat;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.SortedMap;
50 import java.util.TreeMap;
51
52 import javax.swing.BorderFactory;
53 import javax.swing.Box;
54 import javax.swing.BoxLayout;
55 import javax.swing.ImageIcon;
56 import javax.swing.JLabel;
57
58 import org.apache.log4j.Logger;
59 import org.geotools.coverage.grid.GeneralGridEnvelope;
60 import org.geotools.coverage.grid.GridCoverage2D;
61 import org.geotools.coverage.grid.GridGeometry2D;
62 import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
63 import org.geotools.feature.FeatureCollection;
64 import org.geotools.gce.imagepyramid.ImagePyramidReader;
65 import org.geotools.geometry.jts.ReferencedEnvelope;
66 import org.geotools.map.DefaultMapLayer;
67 import org.geotools.map.MapLayer;
68 import org.geotools.renderer.lite.gridcoverage2d.GridCoverageRenderer;
69 import org.geotools.styling.ColorMap;
70 import org.geotools.styling.ColorMapEntry;
71 import org.geotools.styling.FeatureTypeStyle;
72 import org.geotools.styling.RasterSymbolizer;
73 import org.geotools.styling.Rule;
74 import org.geotools.styling.Style;
75 import org.jdom.Document;
76 import org.jdom.Element;
77 import org.jdom.input.SAXBuilder;
78 import org.jdom.output.XMLOutputter;
79 import org.opengis.feature.simple.SimpleFeatureType;
80 import org.opengis.parameter.GeneralParameterValue;
81
82 import schmitzm.geotools.JTSUtil;
83 import schmitzm.geotools.styling.StylingUtil;
84 import schmitzm.io.IOUtil;
85 import schmitzm.lang.LangUtil;
86 import schmitzm.swing.SwingUtil;
87 import skrueger.AttributeMetaData;
88 import skrueger.RasterLegendData;
89 import skrueger.i8n.Translation;
90
91 /**
92 * This class provides static helper methods for dealing with
93 * {@link StyledLayerInterface} stuff.
94 *
95 * @author <a href="mailto:[email protected]">Martin Schmitz</a>
96 * (University of Bonn/Germany)
97 * @version 1.0
98 */
99 public class StyledLayerUtil {
100 private static final Logger LOGGER = Logger.getLogger(StyledLayerUtil.class
101 .getName());
102 private static final SAXBuilder SAX_BUILDER = new SAXBuilder();
103 private static final XMLOutputter XML_OUTPUTTER = new XMLOutputter();
104
105 /** URL for Atlas XML schema */
106 public static final String AMLURI = "http://www.wikisquare.de/AtlasML";
107 /** Name of the XML Element for the attribute meta data map */
108 public static final String ELEM_NAME_AMD = "attributeMetaData";
109 /** Name of the XML Element for the raster legend data */
110 public static final String ELEM_NAME_RLD = "rasterLegendData";
111 /** Name of the XML Element for an attribute meta data map entry */
112 public static final String ELEM_NAME_ATTRIBUTE = "dataAttribute";
113 /** Name of the XML Element for an raster legend data entry */
114 public static final String ELEM_NAME_RASTERLEGEND = "rasterLegendItem";
115 /** Name of the XML Element for a translation */
116 public static final String ELEM_NAME_TRANSLATION = "translation";
117
118 /**
119 * Creates a Geotools {@link MapLayer} from an object. If the object is a
120 * {@link StyledLayerInterface} then its sytle is used. In case of direct
121 * Geotools objects ({@link GridCoverage2D},
122 * {@link AbstractGridCoverage2DReader}, {@link FeatureCollection}) a
123 * default style is generated.
124 *
125 * @param object
126 * an Object
127 * @exception Exception
128 * if {@code null} is given as object or an error occurs
129 * during layer creation
130 */
131 public static MapLayer createMapLayer(Object object) throws Exception {
132 return createMapLayer(object, null);
133 }
134
135 /**
136 * Creates a Geotools {@link MapLayer} from an object. If the object is a
137 * {@link StyledLayerInterface} then its sytle is used. In case of direct
138 * Geotools objects ({@link GridCoverage2D},
139 * {@link AbstractGridCoverage2DReader}, {@link FeatureCollection}) a
140 * default style is generated.
141 *
142 * @param object
143 * an Object
144 * @param forcedStyle
145 * (SLD-)Style to force for the object
146 * @exception Exception
147 * if {@code null} is given as object or an error occurs
148 * during layer creation
149 */
150 public static MapLayer createMapLayer(Object object, Style forcedStyle)
151 throws Exception {
152 MapLayer layer = null;
153 Style style = null;
154 if (object instanceof StyledLayerInterface) {
155 style = ((StyledLayerInterface<?>) object).getStyle();
156 object = ((StyledLayerInterface<?>) object).getGeoObject();
157 }
158 if (forcedStyle != null)
159 style = forcedStyle;
160 if (style == null)
161 style = StylingUtil.createDefaultStyle(object);
162
163 if (object instanceof GridCoverage2D)
164 layer = new DefaultMapLayer((GridCoverage2D) object, style);
165 if (object instanceof AbstractGridCoverage2DReader)
166 layer = new DefaultMapLayer((AbstractGridCoverage2DReader) object,
167 style);
168 if (object instanceof FeatureCollection)
169 layer = new DefaultMapLayer((FeatureCollection) object, style);
170
171 if (layer == null)
172 throw new Exception("Can not create MapLayer from "
173 + (object == null ? "null" : object.getClass()));
174
175 return layer;
176 }
177
178 /**
179 * Creates an default instance of {@link StyledLayerInterface} for a
180 * Geotools object ({@link GridCoverage2D}, {@link FeatureCollection}) with
181 * a default style.
182 *
183 * @param object
184 * an Object
185 * @param title
186 * title for the object
187 * @exception UnsupportedOperationException
188 * if {@code null} is given as object or an error occurs
189 * during creation
190 */
191 public static StyledLayerInterface<?> createStyledLayer(Object object,
192 String title) {
193 return createStyledLayer(object, title, null);
194 }
195
196 /**
197 * Creates an default instance of {@link StyledLayerInterface} for a
198 * Geotools object ({@link GridCoverage2D}, {@link FeatureCollection}) with
199 * a given style.
200 *
201 * @param object
202 * an Object
203 * @param title
204 * title for the object
205 * @param style
206 * style and meta data for the object
207 * @exception UnsupportedOperationException
208 * if {@code null} is given as object or an error occurs
209 * during creation
210 */
211 public static StyledLayerInterface<?> createStyledLayer(Object object,
212 String title, StyledLayerStyle style) {
213 StyledLayerInterface<?> styledLayer = null;
214
215 String id = (title != null) ? title : "defaultID";
216
217 if (object instanceof GridCoverage2D)
218 styledLayer = new StyledGridCoverage((GridCoverage2D) object, id,
219 title, style);
220 else if (object instanceof AbstractGridCoverage2DReader)
221 styledLayer = new StyledGridCoverageReader(
222 (AbstractGridCoverage2DReader) object, id, title, style);
223 else if (object instanceof FeatureCollection)
224 styledLayer = new StyledFeatureCollection(
225 (FeatureCollection) object, id, title, style);
226
227 if (styledLayer == null)
228 throw new UnsupportedOperationException(
229 "Can not create StyledLayerInterface object from "
230 + (object == null ? "null" : object.getClass()));
231
232 return styledLayer;
233 }
234
235 /**
236 * Return only the visible or invisible entries of an AttributeMetaData-Map.
237 *
238 * @param amdMap
239 * AttributeMetaData-Map
240 * @param visible
241 * indicated whether the visible or invisible entries are
242 * returned
243 */
244 public static SortedMap<Integer, AttributeMetaData> getVisibleAttributeMetaData(
245 Map<Integer, AttributeMetaData> amdMap, boolean visible) {
246 SortedMap<Integer, AttributeMetaData> filteredMap = new TreeMap<Integer, AttributeMetaData>();
247 for (AttributeMetaData amd : amdMap.values())
248 if (amd.isVisible())
249 filteredMap.put(amd.getColIdx(), amd);
250
251 return filteredMap;
252 }
253
254 /**
255 * Parses a {@link AttributeMetaData} object from an JDOM-{@link Element}.
256 * This method works like {@link
257 * AMLImport#parseDataAttribute(org.w3c.dom.Node}, but for JDOM.
258 *
259 * @param element
260 * {@link Element} to parse
261 */
262 public static AttributeMetaData parseAttributeMetaData(final Element element) {
263 final Integer col = Integer.valueOf(element.getAttributeValue("col"));
264 final Boolean visible = Boolean.valueOf(element
265 .getAttributeValue("visible"));
266 final String unit = element.getAttributeValue("unit");
267
268 Translation name = new Translation();
269 Translation desc = new Translation();
270 for (final Element childElement : (List<Element>) element.getChildren()) {
271 if (childElement.getName() == null)
272 continue;
273
274 if (childElement.getName().equals("name"))
275 name = parseTranslation(childElement);
276 else if (childElement.getName().equals("desc"))
277 desc = parseTranslation(childElement);
278 }
279 return new AttributeMetaData(col, visible, name, desc, unit);
280 }
281
282 /**
283 * Parses a {@link AttributeMetaData} map from an JDOM-{@link Element} with
284 * {@code <attribute>}-childs.
285 *
286 * @param element
287 * {@link Element} to parse
288 */
289 public static Map<Integer, AttributeMetaData> parseAttributeMetaDataMap(
290 final Element element) {
291 HashMap<Integer, AttributeMetaData> metaData = new HashMap<Integer, AttributeMetaData>();
292 List<Element> attributesElements = element
293 .getChildren(ELEM_NAME_ATTRIBUTE);
294 for (Element attibuteElement : attributesElements) {
295 AttributeMetaData attrMetaData = parseAttributeMetaData(attibuteElement);
296 metaData.put(attrMetaData.getColIdx(), attrMetaData);
297 }
298 return metaData;
299 }
300
301 /**
302 * Loads a {@link AttributeMetaData} object from an URL.
303 *
304 * @param documentUrl
305 * {@link URL} to parse
306 * @see #parseAttributeMetaData(Element)
307 */
308 public static Map<Integer, AttributeMetaData> loadAttributeMetaDataMap(
309 final URL documentUrl) throws Exception {
310 Document document = SAX_BUILDER.build(documentUrl);
311 return parseAttributeMetaDataMap(document.getRootElement());
312 }
313
314 /**
315 * Creates an JDOM {@link Element} for the given {@link AttributeMetaData}
316 * object.
317 *
318 * @param amd
319 * meta data for one attribute
320 */
321 public static Element createAttributeMetaDataElement(
322 final AttributeMetaData amd) {
323 final Element element = new Element(ELEM_NAME_ATTRIBUTE, AMLURI);
324 element.setAttribute("col", String.valueOf(amd.getColIdx()));
325 element.setAttribute("visible", String.valueOf(amd.isVisible()));
326 element.setAttribute("unit", amd.getUnit());
327 // Creating a aml:name tag...
328 element.addContent(createTranslationElement("name", amd.getTitle()));
329 // Creating a aml:desc tag...
330 element.addContent(createTranslationElement("desc", amd.getDesc()));
331 return element;
332 }
333
334 /**
335 * Creates an JDOM {@link Element} for the given {@link AttributeMetaData}
336 * map.
337 *
338 * @param amdMap
339 * map of attribute meta data
340 */
341 public static Element createAttributeMetaDataMapElement(
342 final Map<Integer, AttributeMetaData> amdMap) {
343 final Element element = new Element(ELEM_NAME_AMD, AMLURI);
344 for (AttributeMetaData amd : amdMap.values())
345 element.addContent(createAttributeMetaDataElement(amd));
346 return element;
347 }
348
349 /**
350 * Saves a {@link AttributeMetaData AttributeMetaData-Map} to an URL.
351 *
352 * @param amdMap
353 * map of {@link AttributeMetaData}
354 * @param documentUrl
355 * {@link URL} to store the XML
356 */
357 public static void saveAttributeMetaDataMap(
358 final Map<Integer, AttributeMetaData> amdMap, final URL documentUrl)
359 throws Exception {
360 // Create XML-Document
361 final FileWriter out = new FileWriter(new File(documentUrl.toURI()));
362 XML_OUTPUTTER.output(createAttributeMetaDataMapElement(amdMap), out);
363 out.flush();
364 out.close();
365 }
366
367 /**
368 * Parses a {@link RasterLegendData} object from an JDOM-{@link Element}.
369 * This method works like {@link
370 * AMLImport#parseRasterLegendData(org.w3c.dom.Node}, but for JDOM.
371 *
372 * @param element
373 * {@link Element} to parse
374 */
375 public static RasterLegendData parseRasterLegendData(Element element) {
376
377 final boolean paintGaps = Boolean.valueOf(element
378 .getAttributeValue("paintGaps"));
379
380 RasterLegendData rld = new RasterLegendData(paintGaps);
381
382 for (Element childElement : (List<Element>) element.getChildren()) {
383 final String name = childElement.getName();
384 // Cancel if it's an attribute
385 if (childElement.getChildren().size() == 0)
386 continue;
387
388 if (name.equals(ELEM_NAME_RASTERLEGEND)) {
389 final String valueAttr = childElement
390 .getAttributeValue("value");
391 if (valueAttr == null)
392 throw new UnsupportedOperationException(
393 "Attribute 'value' missing for definition of <"
394 + ELEM_NAME_RASTERLEGEND + ">");
395 final double value = Double.valueOf(valueAttr);
396
397 // first and only item should be the label
398 final Element labelElement = childElement.getChild("label");
399 // id label element is missing, the translation is searched
400 // directly
401 // as childs of the rasterLegendItem element
402 Translation label = parseTranslation(labelElement != null ? labelElement
403 : childElement);
404 rld.put(value, label);
405 }
406 }
407
408 return rld;
409 }
410
411 /**
412 * Loads a {@link RasterLegendData} object from an URL.
413 *
414 * @param documentUrl
415 * {@link URL} to parse
416 * @see #parseAttributeMetaData(Element)
417 */
418 public static RasterLegendData loadRasterLegendData(final URL documentUrl)
419 throws Exception {
420 Document document = SAX_BUILDER.build(documentUrl);
421 return parseRasterLegendData(document.getRootElement());
422 }
423
424 /**
425 * Creates an JDOM {@link Element} for the given {@link RasterLegendData}
426 * map.
427 *
428 * @param rld
429 * raster legend data
430 */
431 public static Element createRasterLegendDataElement(
432 final RasterLegendData rld) {
433 final Element element = new Element(ELEM_NAME_RLD, AMLURI);
434 element.setAttribute("paintGaps", rld.isPaintGaps().toString());
435 for (Double key : rld.getSortedKeys()) {
436 Element item = new Element(ELEM_NAME_RASTERLEGEND, AMLURI);
437 item.setAttribute("value", key.toString());
438 item.addContent(createTranslationElement("label", rld.get(key)));
439 element.addContent(item);
440 }
441 return element;
442 }
443
444 /**
445 * Creates {@link RasterLegendData} from a {@link ColorMap}.
446 *
447 * @param colorMap
448 * a color map
449 * @param paintGaps
450 * indicated whether gaps are painted between the legend items
451 * @param digits
452 * number of digits the grid value classes (and legend) are
453 * rounded to (null means no round; >= 0 means digits after
454 * comma; < 0 means digits before comma)
455 */
456 public static RasterLegendData generateRasterLegendData(ColorMap colorMap,
457 boolean paintGaps, Integer digits) {
458 DecimalFormat decFormat = digits != null ? new DecimalFormat(SwingUtil
459 .getNumberFormatPattern(digits)) : null;
460 RasterLegendData rld = new RasterLegendData(paintGaps);
461 for (ColorMapEntry cme : colorMap.getColorMapEntries()) {
462 double value = StylingUtil.getQuantityFromColorMapEntry(cme);
463 String label = cme.getLabel();
464 // if no label is set (e.g. quantitative style),
465 // use the value as label
466 if (label == null || label.equals(""))
467 if (digits == null)
468 label = String.valueOf(value);
469 else
470 label = decFormat.format(LangUtil.round(value, digits));
471 rld.put(value, new Translation(" " + label));
472 }
473 return rld;
474 }
475
476 /**
477 * Creates {@link RasterLegendData} from the {@link ColorMap} of a style.
478 *
479 * @param style
480 * a raster style (must contain a {@link RasterSymbolizer})
481 * @param paintGaps
482 * indicated whether gaps are painted between the legend items
483 * @param digits
484 * number of digits the grid value classes (and legend) are
485 * rounded to (null means no round; >= 0 means digits after
486 * comma; < 0 means digits before comma)
487 */
488 public static RasterLegendData generateRasterLegendData(Style style,
489 boolean paintGaps, Integer digits) {
490 ColorMap colorMap = StylingUtil.getColorMapFromStyle(style);
491 if (colorMap == null)
492 throw new IllegalArgumentException(
493 "Color map can not be determined from style!");
494 return generateRasterLegendData(colorMap, paintGaps, digits);
495 }
496
497 /**
498 * Saves a {@link RasterLegendData} to an URL.
499 *
500 * @param rld
501 * raster legend data
502 * @param documentUrl
503 * {@link URL} to store the XML
504 */
505 public static void saveRasterLegendData(final RasterLegendData rld,
506 final URL documentUrl) throws Exception {
507 // Create XML-Document
508 final FileWriter out = new FileWriter(new File(documentUrl.toURI()));
509 XML_OUTPUTTER.output(createRasterLegendDataElement(rld), out);
510 out.flush();
511 out.close();
512 }
513
514 /**
515 * Parses a {@link Translation} object from an JDOM-{@link Element}. This
516 * method works like {@link AMLImport#parseTranslation(org.w3c.dom.Node},
517 * but for JDOM.
518 *
519 * @param element
520 * {@link Element} to parse
521 */
522 public final static Translation parseTranslation(final Element element) {
523 Translation trans = new Translation();
524
525 if (element == null)
526 return trans;
527
528 for (final Element translationElement : (List<Element>) element
529 .getChildren()) {
530 final String name = translationElement.getName();
531 if (name == null)
532 continue;
533
534 // lang attribute
535 String lang = translationElement.getAttributeValue("lang");
536 // set the default, if no language code is set
537 if (lang == null)
538 lang = Translation.DEFAULT_KEY;
539
540 final String translationText = translationElement.getValue();
541 if (translationText == null)
542 trans.put(lang, "");
543 else
544 trans.put(lang, translationText);
545 }
546
547 // if no <translation> is given, the value of the node should
548 // be used as a default translation
549 if (trans.size() == 0)
550 trans.put(Translation.DEFAULT_KEY, element.getValue());
551 // trans = new Translation(
552 // ((List<Element>)element.getChildren()).get(0).getValue() );
553
554 return trans;
555 }
556
557 /**
558 * Creates an JDOM {@link Element} for the given {@link Translation}.
559 *
560 * @param tagname
561 * Name of the Element
562 * @param translation
563 * Translation to store in the Element
564 */
565 public final static Element createTranslationElement(String tagname,
566 Translation translation) {
567 Element element = new Element(tagname, AMLURI);
568 if (translation == null)
569 throw new UnsupportedOperationException(
570 "Translation element can not be created from null!");
571
572 // If only a default translation is set, the <translation
573 // lang="..">..</tranlation>
574 // part is not used
575 if (translation.keySet().size() == 1
576 && translation.get(Translation.DEFAULT_KEY) != null) {
577 element.addContent(translation.get(Translation.DEFAULT_KEY));
578 return element;
579 }
580
581 // add a <translation lang="..">..</tranlation> part to the element for
582 // all languages
583 for (String lang : translation.keySet()) {
584 Element translationElement = new Element(ELEM_NAME_TRANSLATION,
585 AMLURI);
586 translationElement.setAttribute("lang", lang);
587 String translationString = translation.get(lang);
588 if (translationString == null)
589 translationString = "";
590 translationElement.addContent(translationString);
591 element.addContent(translationElement);
592 }
593
594 return element;
595 }
596
597 /**
598 * Sets a style to {@link StyledLayerInterface}.
599 *
600 * @param styledObject
601 * a styled object
602 * @param style
603 * a Style
604 */
605 public static void setStyledLayerStyle(StyledLayerInterface styledObject,
606 StyledLayerStyle<?> style) {
607 // set SLD style
608 styledObject.setStyle(style.getGeoObjectStyle());
609 // set meta data
610 if (styledObject instanceof StyledGridCoverageInterface
611 && (style.getMetaData() instanceof RasterLegendData || style
612 .getMetaData() == null)) {
613 RasterLegendData sourceRld = (RasterLegendData) style.getMetaData();
614 RasterLegendData destRld = ((StyledGridCoverageInterface) styledObject)
615 .getLegendMetaData();
616 if (destRld != null && sourceRld != null) {
617 destRld.setPaintGaps(sourceRld.isPaintGaps());
618 destRld.clear();
619 destRld.putAll(sourceRld);
620 }
621 return;
622 }
623 if (styledObject instanceof StyledFeatureCollectionInterface
624 && (style.getMetaData() instanceof Map || style.getMetaData() == null)) {
625 Map<Integer, AttributeMetaData> sourceAmd = (Map<Integer, AttributeMetaData>) style
626 .getMetaData();
627 Map<Integer, AttributeMetaData> destAmd = ((StyledFeatureCollectionInterface) styledObject)
628 .getAttributeMetaDataMap();
629 if (destAmd != null && sourceAmd != null) {
630 destAmd.clear();
631 destAmd.putAll(sourceAmd);
632 }
633 return;
634 }
635
636 throw new UnsupportedOperationException(
637 "Style is not compatible to object: "
638 + (style.getMetaData() == null ? null : style
639 .getMetaData().getClass().getSimpleName())
640 + " <-> "
641 + (styledObject == null ? null : styledObject
642 .getClass().getSimpleName()));
643 }
644
645 /**
646 * Returns the style a {@link StyledLayerInterface} as a
647 * {@link StyledLayerStyle}.
648 *
649 * @param styledObject
650 * a styled object
651 * @return {@code StyledLayerStyle<RasterLegendData>} for
652 * {@link StyledGridCoverageInterface} or {@code
653 * StyledLayerStyle<Map<Integer,AttributeMetaData>>} for
654 * {@link StyledFeatureCollectionInterface}
655 */
656 public static StyledLayerStyle<?> getStyledLayerStyle(
657 StyledLayerInterface styledObject) {
658 if (styledObject instanceof StyledGridCoverageInterface)
659 return getStyledLayerStyle((StyledGridCoverageInterface) styledObject);
660 if (styledObject instanceof StyledFeatureCollectionInterface)
661 return getStyledLayerStyle((StyledFeatureCollectionInterface) styledObject);
662 throw new UnsupportedOperationException(
663 "Unknown type of StyledLayerInterface: "
664 + (styledObject == null ? null : styledObject
665 .getClass().getSimpleName()));
666 }
667
668 /**
669 * Returns the style and raster meta data of a
670 * {@link StyledGridCoverageInterface} as a {@link StyledLayerStyle}.
671 *
672 * @param styledGC
673 * a styled grid coverage
674 */
675 public static StyledLayerStyle<RasterLegendData> getStyledLayerStyle(
676 StyledGridCoverageInterface styledGC) {
677 return new StyledLayerStyle<RasterLegendData>(styledGC.getStyle(),
678 styledGC.getLegendMetaData());
679 }
680
681 /**
682 * Returns the style and attribute meta data of a
683 * {@link StyledFeatureCollectionInterface} as a {@link StyledLayerStyle}.
684 *
685 * @param styledFC
686 * a styled feature collection
687 */
688 public static StyledLayerStyle<Map<Integer, AttributeMetaData>> getStyledLayerStyle(
689 StyledFeatureCollectionInterface styledFC) {
690 return new StyledLayerStyle<Map<Integer, AttributeMetaData>>(styledFC
691 .getStyle(), styledFC.getAttributeMetaDataMap());
692 }
693
694 /**
695 * Loads a {@linkplain Style SLD-Style} and {@linkplain RasterLegendData
696 * Raster-LegendData} for a given geo-object (raster) source. The SLD file
697 * must be present. A missing raster legend-data file is tolerated.
698 *
699 * @param geoObjectURL
700 * URL of the (already read) raster object
701 * @param sldExt
702 * file extention for the SLD file
703 * @param rldExt
704 * file extention for the raster legend-data file
705 * @return {@code null} in case of any error
706 */
707 public static StyledLayerStyle<RasterLegendData> loadStyledRasterStyle(
708 URL geoObjectURL, String sldExt, String rldExt) {
709 RasterLegendData metaData = null;
710 Style sldStyle = null;
711 try {
712 Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(
713 geoObjectURL, sldExt));
714 // SLD must be present
715 if (styles == null || styles.length == 0)
716 return null;
717 sldStyle = styles[0];
718 } catch (Exception err) {
719 // SLD must be present
720 LangUtil.logDebugError(LOGGER, err);
721 return null;
722 }
723
724 try {
725 metaData = StyledLayerUtil.loadRasterLegendData(IOUtil
726 .changeUrlExt(geoObjectURL, rldExt));
727 } catch (FileNotFoundException err) {
728 // ignore missing raster legend data
729 } catch (Exception err) {
730 // any other error during legend data creation leads to error
731 LangUtil.logDebugError(LOGGER, err);
732 return null;
733 }
734 return new StyledLayerStyle<RasterLegendData>(sldStyle, metaData);
735 }
736
737 /**
738 * Loads a {@linkplain Style SLD-Style} from a {@code .sld} file and
739 * {@linkplain RasterLegendData Raster-LegendData} from a {@code .rld} file
740 * for a given geo-object (raster) source. The SLD file must be present. A
741 * missing raster legend-data file is tolerated.
742 *
743 * @param geoObjectURL
744 * URL of the (already read) raster object
745 * @param sldExt
746 * file extention for the SLD file
747 * @param rldExt
748 * file extention for the raster legend-data file
749 * @return {@code null} in case of any error
750 */
751 public static StyledLayerStyle<RasterLegendData> loadStyledRasterStyle(
752 URL geoObjectURL) {
753 return loadStyledRasterStyle(geoObjectURL, "sld", "rld");
754 }
755
756 /**
757 * Loads a {@linkplain Style SLD-Style} and a {@linkplain AttributeMetaData
758 * AttributeMetaData-Map} for a given geo-object (feature) source. The SLD
759 * file must be present. A missing attribute meta-data file is tolerated.
760 *
761 * @param geoObjectURL
762 * URL of the (already read) feature object
763 * @param sldExt
764 * file extention for the SLD file
765 * @param rldExt
766 * file extention for the raster legend-data file
767 * @return {@code null} in case of any error
768 */
769 public static StyledLayerStyle<Map<Integer, AttributeMetaData>> loadStyledFeatureStyle(
770 URL geoObjectURL, String sldExt, String rldExt) {
771 Map<Integer, AttributeMetaData> metaData = null;
772 Style sldStyle = null;
773 try {
774 Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(
775 geoObjectURL, sldExt));
776 // SLD must be present
777 if (styles == null || styles.length == 0)
778 return null;
779 sldStyle = styles[0];
780 } catch (Exception err) {
781 // SLD must be present
782 LangUtil.logDebugError(LOGGER, err);
783 return null;
784 }
785
786 try {
787 metaData = StyledLayerUtil.loadAttributeMetaDataMap(IOUtil
788 .changeUrlExt(geoObjectURL, rldExt));
789 } catch (FileNotFoundException err) {
790 // ignore missing attribute meta data
791 } catch (Exception err) {
792 // any other error during meta data creation leads to error
793 LangUtil.logDebugError(LOGGER, err);
794 return null;
795 }
796
797 return new StyledLayerStyle<Map<Integer, AttributeMetaData>>(sldStyle,
798 metaData);
799 }
800
801 /**
802 * Loads a {@linkplain Style SLD-Style} from a {@code .sld} file and
803 * {@linkplain AttributeMetaData AttributeMetaData-Map} from a {@code .amd}
804 * file for a given geo-object (feature) source. The SLD file must be
805 * present. A missing attribute meta-data file is tolerated.
806 *
807 * @param geoObjectURL
808 * URL of the (already read) feature object
809 * @param sldExt
810 * file extention for the SLD file
811 * @param rldExt
812 * file extention for the raster legend-data file
813 * @return {@code null} in case of any error
814 */
815 public static StyledLayerStyle<Map<Integer, AttributeMetaData>> loadStyledFeatureStyle(
816 URL geoObjectURL) {
817 return loadStyledFeatureStyle(geoObjectURL, "sld", "amd");
818 }
819
820 /**
821 * Stores a {@linkplain Style SLD-Style} and {@linkplain RasterLegendData
822 * Raster-LegendData} for a given geo-object (raster) source.
823 *
824 * @param style
825 * style to save
826 * @param geoObjectURL
827 * URL of the raster object
828 * @param sldExt
829 * file extention for the SLD file
830 * @param mdExt
831 * file extention for the meta-data file
832 */
833 public static <T> void saveStyledLayerStyle(StyledLayerStyle<T> style,
834 URL geoObjectURL, String sldExt, String mdExt) throws Exception {
835 // Store the SLD
836 Style sldStyle = style.getGeoObjectStyle();
837 if (sldStyle != null) {
838 StylingUtil.saveStyleToSLD(sldStyle, IOUtil.changeFileExt(new File(
839 geoObjectURL.toURI()), sldExt));
840 }
841
842 // Store the meta data
843 T metaData = style.getMetaData();
844 if (metaData != null) {
845 if (metaData instanceof RasterLegendData) {
846 saveRasterLegendData((RasterLegendData) metaData, IOUtil
847 .changeUrlExt(geoObjectURL, mdExt));
848 // } else if ( metaData instanceof
849 // Map<Integer,AttributeMetaData> ) { // LEIDER NICHT
850 // KOMPILIERBAR!!
851 } else if (metaData instanceof Map) {
852 saveAttributeMetaDataMap(
853 (Map<Integer, AttributeMetaData>) metaData, IOUtil
854 .changeUrlExt(geoObjectURL, mdExt));
855 } else
856 throw new UnsupportedOperationException(
857 "Export for meta data not yet supported: "
858 + metaData.getClass().getSimpleName());
859 }
860 }
861
862 /**
863 * Stores the {@linkplain Style SLD-Style} to a {@code .sld} file and the
864 * meta data ({@link RasterLegendData} or {@link AttributeMetaData}) to a
865 * {@code .rld} or {@code .amd} file. for a given geo-object source.
866 *
867 * @param style
868 * style to save
869 * @param geoObjectURL
870 * URL of the (already read) raster object
871 */
872 public static void saveStyledLayerStyle(StyledLayerStyle<?> style,
873 URL geoObjectURL) throws Exception {
874 if (style.getMetaData() instanceof RasterLegendData)
875 saveStyledLayerStyle(style, geoObjectURL, "sld", "rld");
876 else
877 saveStyledLayerStyle(style, geoObjectURL, "sld", "amd");
878 }
879
880 /**
881 * Creates a {@link Box} that shows a legend for a list of
882 * {@link FeatureTypeStyle}s and a targeted featureType
883 *
884 * @param featureType
885 * If this a legend for Point, Polygon or Line?
886 * @param list
887 * The Styles to presented in this legend
888 *
889 * @author <a href="mailto:[email protected]">Stefan Alfons
890 * Kr&uuml;ger</a>
891 */
892 public static Box createLegendPanel(List<FeatureTypeStyle> list,
893 SimpleFeatureType featureType, int iconWidth, int iconHeight) {
894
895 Box box = new Box(BoxLayout.Y_AXIS) {
896
897 /**
898 * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein Screenshot
899 * gemacht wird) wird. Dann werden wird der Hintergrund auf WEISS
900 * gesetzt.
901 *
902 * @author <a href="mailto:[email protected]">Stefan Alfons
903 * Kr&uuml;ger</a>
904 */
905 @Override
906 public void print(Graphics g) {
907 final Color orig = getBackground();
908 setBackground(Color.WHITE);
909 // wrap in try/finally so that we always restore the state
910 try {
911 super.print(g);
912 } finally {
913 setBackground(orig);
914 }
915 }
916 };
917
918 for (FeatureTypeStyle ftStyle : list) {
919
920 // One child-node for every rule
921 List<Rule> rules = ftStyle.rules();
922 for (Rule rule : rules) {
923
924 /**
925 * Let's not create a hbox for Rules that only contain
926 * TextSymbolizers
927 */
928 if (StylingUtil.getTextSymbolizers(rule.getSymbolizers())
929 .size() == rule.getSymbolizers().length)
930 continue;
931
932 Box hbox = new Box(BoxLayout.X_AXIS) {
933
934 /**
935 * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein
936 * Screenshot gemacht wird) wird. Dann werden wird der
937 * Hintergrund auf WEISS gesetzt.
938 */
939 @Override
940 public void print(Graphics g) {
941 final Color orig = getBackground();
942 setBackground(Color.WHITE);
943 // wrap in try/finally so that we always restore the
944 // state
945 try {
946 super.print(g);
947 } finally {
948 setBackground(orig);
949 }
950 }
951 };
952
953 /**
954 * The size of the legend Symbol is dependent on the size of the
955 * font.
956 */
957 final int fontHeight = new JLabel().getFontMetrics(
958 new JLabel().getFont()).getHeight();
959
960 final Dimension ICON_SIZE = new Dimension(iconWidth,
961 fontHeight > 5 ? fontHeight : iconHeight);
962
963 // ****************************************************************************
964 // Create the actual icon
965 // ****************************************************************************
966 final BufferedImage imageForRule = LegendIconFeatureRenderer
967 .getInstance().createImageForRule(rule, featureType,
968 ICON_SIZE);
969
970 // LOGGER.debug("Creating a new Legend Image for RUle name =
971 // "+rule.getName());
972
973 ImageIcon legendIcon = new ImageIcon(imageForRule);
974
975 final JLabel iconLabel = new JLabel(legendIcon);
976 hbox.setAlignmentX(0f);
977 hbox.add(iconLabel);
978 hbox.add(Box.createHorizontalStrut(3));
979
980 // ****************************************************************************
981 // The labeltext is read from the SLD. Its either the title
982 // directly, or interpreted as OneLine Code
983 // ****************************************************************************
984 // final String rawText =
985 // rule.getDescription().getTitle().toString();
986 final String rawText = rule.getDescription().getTitle()
987 .toString();
988
989 Translation labelT = new Translation();
990 labelT.fromOneLine(rawText);
991
992 final JLabel classTitleLabel = new JLabel(labelT.toString());
993 hbox.add(classTitleLabel);
994 classTitleLabel.setLabelFor(iconLabel);
995
996 box.add(hbox);
997
998 }
999 }
1000
1001 return box;
1002 }
1003
1004 /**
1005 * Creates a
1006 *
1007 * @param styledGrid
1008 * @param iconHeight
1009 * @param iconWidth
1010 * @return
1011 */
1012 public static Box createLegendPanel(StyledRasterInterface<?> styledGrid,
1013 int iconWidth, int iconHeight) {
1014
1015 /**
1016 * Determine whether a Style is responsible for the coloring
1017 */
1018 Object geoObject = styledGrid.getGeoObject();
1019 ColorModel colorModel = null;
1020 if (geoObject instanceof GridCoverage2D) {
1021 GridCoverage2D cov = (GridCoverage2D) geoObject;
1022 colorModel = cov.getRenderedImage().getColorModel();
1023 } else if (geoObject instanceof ImagePyramidReader) {
1024
1025 org.geotools.parameter.Parameter readGG = new org.geotools.parameter.Parameter(
1026 org.geotools.coverage.grid.io.AbstractGridFormat.READ_GRIDGEOMETRY2D);
1027
1028 ReferencedEnvelope mapExtend = new org.geotools.geometry.jts.ReferencedEnvelope(
1029 styledGrid.getEnvelope(), styledGrid.getCrs());
1030
1031 readGG.setValue(new GridGeometry2D(new GeneralGridEnvelope(
1032 new Rectangle(0, 0, 10, 10)), mapExtend));
1033
1034 final AbstractGridCoverage2DReader aReader = (AbstractGridCoverage2DReader) geoObject;
1035 try {
1036 GridCoverage2D cov = (GridCoverage2D) aReader
1037 .read(new GeneralParameterValue[] { readGG });
1038 colorModel = cov.getRenderedImage().getColorModel();
1039 } catch (IllegalArgumentException e) {
1040 e.printStackTrace();
1041 } catch (IOException e) {
1042 e.printStackTrace();
1043 }
1044 }
1045
1046 RasterLegendData rasterLegendData = styledGrid.getLegendMetaData();
1047 List<Double> legendRasterValues = rasterLegendData.getSortedKeys();
1048 Map<Double, GridCoverage2D> sampleRasters = rasterLegendData
1049 .createSampleRasters();
1050
1051 Box box = new Box(BoxLayout.Y_AXIS) {
1052
1053 /**
1054 * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein Screenshot
1055 * gemacht wird) wird. Dann werden wird der Hintergrund auf WEISS
1056 * gesetzt.
1057 */
1058 @Override
1059 public void print(Graphics g) {
1060 final Color orig = getBackground();
1061 setBackground(Color.WHITE);
1062 // wrap in try/finally so that we always restore the state
1063 try {
1064 super.print(g);
1065 } finally {
1066 setBackground(orig);
1067 }
1068 }
1069 };
1070
1071 for (Double rValue : legendRasterValues) {
1072
1073 /**
1074 *
1075 */
1076 Box hbox = new Box(BoxLayout.X_AXIS) {
1077
1078 /**
1079 * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein
1080 * Screenshot gemacht wird) wird. Dann werden wird der
1081 * Hintergrund auf WEISS gesetzt.
1082 */
1083 @Override
1084 public void print(Graphics g) {
1085 final Color orig = getBackground();
1086 setBackground(Color.WHITE);
1087 // wrap in try/finally so that we always restore the
1088 // state
1089 try {
1090 super.print(g);
1091 } finally {
1092 setBackground(orig);
1093 }
1094 }
1095 };
1096
1097 final Dimension ICON_SIZE = new Dimension(iconWidth,
1098 new JLabel().getFontMetrics(new JLabel().getFont())
1099 .getHeight() > 5 ? new JLabel().getFontMetrics(
1100 new JLabel().getFont()).getHeight() : iconHeight);
1101
1102 // ****************************************************************************
1103 // Create the actual icon
1104 // ****************************************************************************
1105 BufferedImage buffImage = new BufferedImage(ICON_SIZE.width,
1106 ICON_SIZE.height, BufferedImage.TYPE_INT_ARGB);
1107 Graphics2D graphics = buffImage.createGraphics();
1108
1109 if (colorModel != null)
1110 try {
1111 final int rgb = colorModel.getRGB(rValue.intValue());
1112 final Color color = new Color(rgb);
1113 graphics.setBackground(color);
1114 graphics.setColor(color);
1115 graphics.fillRect(0, 0, ICON_SIZE.width, ICON_SIZE.height);
1116 } catch (Exception e) {
1117 LOGGER.debug("Dann nehmen wir den GridCoverageRenderer", e);
1118
1119 /**
1120 * The coverage contains only one value of value rValue
1121 */
1122 GridCoverage2D sampleCov = sampleRasters.get(rValue);
1123 GridCoverageRenderer renderer;
1124 try {
1125 renderer = new GridCoverageRenderer(sampleCov
1126 .getCoordinateReferenceSystem(), JTSUtil
1127 .createEnvelope(sampleCov.getEnvelope()),
1128 new Rectangle(iconWidth, iconHeight),
1129 (AffineTransform) null);
1130 } catch (Exception e1) {
1131 throw new RuntimeException(
1132 "Creating the GridCoverageRenderer:", e1);
1133 }
1134
1135 /**
1136 * Iterate over all FeatureTypeStyles.
1137 */
1138 // for (FeatureTypeStyle ftStyle : styledGrid.getStyle()
1139 // .featureTypeStyles()) {
1140 // One child-node for every rule
1141 // List<Rule> rules = ftStyle.rules();
1142 // for (Rule rule : rules) {
1143 final Style style = styledGrid.getStyle();
1144 List<RasterSymbolizer> rSymbols = StylingUtil
1145 .getRasterSymbolizers(style);
1146
1147 for (RasterSymbolizer symbolizer : rSymbols) {
1148 // LOGGER.debug("Creating a new Legend Image for RUle
1149 // name =
1150 // "+rule.getName());
1151 try {
1152 renderer.paint(graphics, sampleCov, symbolizer);
1153 } catch (Exception ee) {
1154 LOGGER.error("Unable to paint " + symbolizer
1155 + " into the legend image", ee);
1156 }
1157 // }
1158 // }
1159 }
1160 }
1161
1162 ImageIcon legendIcon = new ImageIcon(buffImage);
1163
1164 final JLabel iconLabel = new JLabel(legendIcon);
1165 hbox.setAlignmentX(0f);
1166 hbox.add(iconLabel);
1167 hbox.add(Box.createHorizontalStrut(3));
1168
1169 Translation labelT = rasterLegendData.get(rValue);
1170 final JLabel classTitleLabel = new JLabel(labelT.toString());
1171 hbox.add(classTitleLabel);
1172 classTitleLabel.setLabelFor(iconLabel);
1173
1174 box.add(hbox);
1175
1176 if (rasterLegendData.getPaintGaps()) {
1177 iconLabel
1178 .setBorder(BorderFactory.createLineBorder(Color.black));
1179 box.add(Box.createVerticalStrut(3));
1180 }
1181
1182 }
1183
1184 return box;
1185 }
1186
1187 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26