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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26