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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26