/[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 397 - (show annotations)
Mon Sep 14 11:40:17 2009 UTC (15 years, 5 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/StyledLayerUtil.java
File size: 34416 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.image.BufferedImage;
36 import java.io.File;
37 import java.io.FileNotFoundException;
38 import java.io.FileWriter;
39 import java.net.URL;
40 import java.text.DecimalFormat;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.SortedMap;
45 import java.util.TreeMap;
46
47 import javax.swing.Box;
48 import javax.swing.BoxLayout;
49 import javax.swing.ImageIcon;
50 import javax.swing.JLabel;
51
52 import org.apache.log4j.Logger;
53 import org.geotools.coverage.grid.GridCoverage2D;
54 import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
55 import org.geotools.feature.FeatureCollection;
56 import org.geotools.map.DefaultMapLayer;
57 import org.geotools.map.MapLayer;
58 import org.geotools.styling.ColorMap;
59 import org.geotools.styling.ColorMapEntry;
60 import org.geotools.styling.FeatureTypeStyle;
61 import org.geotools.styling.RasterSymbolizer;
62 import org.geotools.styling.Rule;
63 import org.geotools.styling.Style;
64 import org.jdom.Document;
65 import org.jdom.Element;
66 import org.jdom.input.SAXBuilder;
67 import org.jdom.output.XMLOutputter;
68 import org.opengis.feature.simple.SimpleFeatureType;
69
70 import schmitzm.geotools.styling.StylingUtil;
71 import schmitzm.io.IOUtil;
72 import schmitzm.lang.LangUtil;
73 import schmitzm.swing.SwingUtil;
74 import skrueger.AttributeMetaData;
75 import skrueger.RasterLegendData;
76 import skrueger.i8n.Translation;
77
78 /**
79 * This class provides static helper methods for dealing with
80 * {@link StyledLayerInterface} stuff.
81 * @author <a href="mailto:[email protected]">Martin Schmitz</a> (University of Bonn/Germany)
82 * @version 1.0
83 */
84 public class StyledLayerUtil {
85 private static final Logger LOGGER = Logger.getLogger(StyledLayerUtil.class.getName());
86 private static final SAXBuilder SAX_BUILDER = new SAXBuilder();
87 private static final XMLOutputter XML_OUTPUTTER = new XMLOutputter();
88
89 /** URL for Atlas XML schema */
90 public static final String AMLURI = "http://www.wikisquare.de/AtlasML";
91 /** Name of the XML Element for the attribute meta data map */
92 public static final String ELEM_NAME_AMD = "attributeMetaData";
93 /** Name of the XML Element for the raster legend data */
94 public static final String ELEM_NAME_RLD = "rasterLegendData";
95 /** Name of the XML Element for an attribute meta data map entry */
96 public static final String ELEM_NAME_ATTRIBUTE = "dataAttribute";
97 /** Name of the XML Element for an raster legend data entry */
98 public static final String ELEM_NAME_RASTERLEGEND = "rasterLegendItem";
99 /** Name of the XML Element for a translation */
100 public static final String ELEM_NAME_TRANSLATION = "translation";
101
102 /**
103 * Creates a Geotools {@link MapLayer} from an object. If the object is a
104 * {@link StyledLayerInterface} then its sytle is used. In case of direct
105 * Geotools objects ({@link GridCoverage2D}, {@link AbstractGridCoverage2DReader},
106 * {@link FeatureCollection}) a default style is generated.
107 * @param object an Object
108 * @exception Exception if {@code null} is given as object or an error occurs during layer creation
109 */
110 public static MapLayer createMapLayer(Object object) throws Exception {
111 return createMapLayer(object,null);
112 }
113
114 /**
115 * Creates a Geotools {@link MapLayer} from an object. If the object is a
116 * {@link StyledLayerInterface} then its sytle is used. In case of direct
117 * Geotools objects ({@link GridCoverage2D}, {@link AbstractGridCoverage2DReader},
118 * {@link FeatureCollection}) a default style is generated.
119 * @param object an Object
120 * @param forcedStyle (SLD-)Style to force for the object
121 * @exception Exception if {@code null} is given as object or an error occurs during layer creation
122 */
123 public static MapLayer createMapLayer(Object object, Style forcedStyle) throws Exception {
124 MapLayer layer = null;
125 Style style = null;
126 if ( object instanceof StyledLayerInterface ) {
127 style = ((StyledLayerInterface<?>)object).getStyle();
128 object = ((StyledLayerInterface<?>)object).getGeoObject();
129 }
130 if ( forcedStyle != null )
131 style = forcedStyle;
132 if ( style == null )
133 style = StylingUtil.createDefaultStyle(object);
134
135 if (object instanceof GridCoverage2D)
136 layer = new DefaultMapLayer( (GridCoverage2D) object, style);
137 if (object instanceof AbstractGridCoverage2DReader)
138 layer = new DefaultMapLayer( (AbstractGridCoverage2DReader) object, style);
139 if (object instanceof FeatureCollection)
140 layer = new DefaultMapLayer( (FeatureCollection) object, style);
141
142 if ( layer == null )
143 throw new Exception("Can not create MapLayer from "+(object == null ? "null" : object.getClass()));
144
145 return layer;
146 }
147
148 /**
149 * Creates an default instance of {@link StyledLayerInterface} for a Geotools
150 * object ({@link GridCoverage2D}, {@link FeatureCollection}) with a default
151 * style.
152 * @param object an Object
153 * @param title title for the object
154 * @exception UnsupportedOperationException if {@code null} is given as object or an error occurs during creation
155 */
156 public static StyledLayerInterface<?> createStyledLayer(Object object, String title) {
157 return createStyledLayer(object, title, null);
158 }
159
160 /**
161 * Creates an default instance of {@link StyledLayerInterface} for a Geotools
162 * object ({@link GridCoverage2D}, {@link FeatureCollection}) with a given
163 * style.
164 * @param object an Object
165 * @param title title for the object
166 * @param style style and meta data for the object
167 * @exception UnsupportedOperationException if {@code null} is given as object or an error occurs during creation
168 */
169 public static StyledLayerInterface<?> createStyledLayer(Object object, String title, StyledLayerStyle style) {
170 StyledLayerInterface<?> styledLayer = null;
171
172 String id = (title != null) ? title : "defaultID";
173
174 if ( object instanceof GridCoverage2D )
175 styledLayer = new StyledGridCoverage(
176 (GridCoverage2D)object,
177 id,
178 title,
179 style
180 );
181 else if ( object instanceof AbstractGridCoverage2DReader )
182 styledLayer = new StyledGridCoverageReader(
183 (AbstractGridCoverage2DReader)object,
184 id,
185 title,
186 style
187 );
188 else if ( object instanceof FeatureCollection )
189 styledLayer = new StyledFeatureCollection(
190 (FeatureCollection)object,
191 id,
192 title,
193 style
194 );
195
196 if ( styledLayer == null )
197 throw new UnsupportedOperationException("Can not create StyledLayerInterface object from "+(object == null ? "null" : object.getClass()));
198
199 return styledLayer;
200 }
201
202 /**
203 * Return only the visible or invisible entries of an AttributeMetaData-Map.
204 * @param amdMap AttributeMetaData-Map
205 * @param visible indicated whether the visible or invisible entries are
206 * returned
207 */
208 public static SortedMap<Integer,AttributeMetaData> getVisibleAttributeMetaData(Map<Integer,AttributeMetaData> amdMap, boolean visible) {
209 SortedMap<Integer,AttributeMetaData> filteredMap = new TreeMap<Integer,AttributeMetaData>();
210 for (AttributeMetaData amd : amdMap.values())
211 if ( amd.isVisible() )
212 filteredMap.put(amd.getColIdx(), amd);
213
214 return filteredMap;
215 }
216
217
218 /**
219 * Parses a {@link AttributeMetaData} object from an JDOM-{@link Element}.
220 * This method works like {@link AMLImport#parseDataAttribute(org.w3c.dom.Node},
221 * but for JDOM.
222 * @param element {@link Element} to parse
223 */
224 public static AttributeMetaData parseAttributeMetaData(final Element element) {
225 final Integer col = Integer.valueOf(element.getAttributeValue("col"));
226 final Boolean visible = Boolean.valueOf(element.getAttributeValue("visible"));
227 final String unit = element.getAttributeValue("unit");
228
229 Translation name = new Translation();
230 Translation desc = new Translation();
231 for (final Element childElement : (List<Element>)element.getChildren()) {
232 if (childElement.getName() == null)
233 continue;
234
235 if (childElement.getName().equals("name"))
236 name = parseTranslation(childElement);
237 else if (childElement.getName().equals("desc"))
238 desc = parseTranslation(childElement);
239 }
240 return new AttributeMetaData(col, visible, name, desc, unit);
241 }
242
243 /**
244 * Parses a {@link AttributeMetaData} map from an JDOM-{@link Element}
245 * with {@code <attribute>}-childs.
246 * @param element {@link Element} to parse
247 */
248 public static Map<Integer,AttributeMetaData> parseAttributeMetaDataMap(final Element element) {
249 HashMap<Integer,AttributeMetaData> metaData = new HashMap<Integer,AttributeMetaData>();
250 List<Element> attributesElements = element.getChildren( ELEM_NAME_ATTRIBUTE );
251 for (Element attibuteElement : attributesElements)
252 {
253 AttributeMetaData attrMetaData = parseAttributeMetaData( attibuteElement );
254 metaData.put( attrMetaData.getColIdx(), attrMetaData );
255 }
256 return metaData;
257 }
258
259 /**
260 * Loads a {@link AttributeMetaData} object from an URL.
261 * @param documentUrl {@link URL} to parse
262 * @see #parseAttributeMetaData(Element)
263 */
264 public static Map<Integer,AttributeMetaData> loadAttributeMetaDataMap(final URL documentUrl) throws Exception {
265 Document document = SAX_BUILDER.build(documentUrl);
266 return parseAttributeMetaDataMap( document.getRootElement() );
267 }
268
269 /**
270 * Creates an JDOM {@link Element} for the given {@link AttributeMetaData}
271 * object.
272 * @param amd meta data for one attribute
273 */
274 public static Element createAttributeMetaDataElement(final AttributeMetaData amd) {
275 final Element element = new Element( ELEM_NAME_ATTRIBUTE , AMLURI);
276 element.setAttribute("col", String.valueOf( amd.getColIdx() ) );
277 element.setAttribute("visible", String.valueOf( amd.isVisible() ) );
278 element.setAttribute("unit", amd.getUnit() );
279 // Creating a aml:name tag...
280 element.addContent( createTranslationElement("name", amd.getTitle()) );
281 // Creating a aml:desc tag...
282 element.addContent( createTranslationElement("desc", amd.getDesc()) );
283 return element;
284 }
285
286 /**
287 * Creates an JDOM {@link Element} for the given {@link AttributeMetaData}
288 * map.
289 * @param amdMap map of attribute meta data
290 */
291 public static Element createAttributeMetaDataMapElement(final Map<Integer,AttributeMetaData> amdMap) {
292 final Element element = new Element( ELEM_NAME_AMD , AMLURI);
293 for (AttributeMetaData amd : amdMap.values())
294 element.addContent( createAttributeMetaDataElement( amd ) );
295 return element;
296 }
297
298 /**
299 * Saves a {@link AttributeMetaData AttributeMetaData-Map} to an URL.
300 * @param amdMap map of {@link AttributeMetaData}
301 * @param documentUrl {@link URL} to store the XML
302 */
303 public static void saveAttributeMetaDataMap(final Map<Integer,AttributeMetaData> amdMap, final URL documentUrl) throws Exception {
304 // Create XML-Document
305 final FileWriter out = new FileWriter( new File(documentUrl.toURI()) );
306 XML_OUTPUTTER.output(
307 createAttributeMetaDataMapElement(amdMap),
308 out
309 );
310 out.flush();
311 out.close();
312 }
313
314
315
316 /**
317 * Parses a {@link RasterLegendData} object from an JDOM-{@link Element}.
318 * This method works like {@link AMLImport#parseRasterLegendData(org.w3c.dom.Node},
319 * but for JDOM.
320 * @param element {@link Element} to parse
321 */
322 public static RasterLegendData parseRasterLegendData(Element element) {
323
324 final boolean paintGaps = Boolean.valueOf( element.getAttributeValue("paintGaps") );
325
326 RasterLegendData rld = new RasterLegendData(paintGaps);
327
328 for ( Element childElement : (List<Element>)element.getChildren() ) {
329 final String name = childElement.getName();
330 // Cancel if it's an attribute
331 if ( childElement.getChildren().size() == 0 )
332 continue;
333
334 if (name.equals( ELEM_NAME_RASTERLEGEND )) {
335 final String valueAttr = childElement.getAttributeValue("value");
336 if ( valueAttr == null )
337 throw new UnsupportedOperationException("Attribute 'value' missing for definition of <"+ELEM_NAME_RASTERLEGEND+">");
338 final double value = Double.valueOf(valueAttr);
339
340 // first and only item should be the label
341 final Element labelElement = childElement.getChild("label");
342 // id label element is missing, the translation is searched directly
343 // as childs of the rasterLegendItem element
344 Translation label = parseTranslation( labelElement != null ? labelElement : childElement );
345 rld.put(value, label);
346 }
347 }
348
349 return rld;
350 }
351
352 /**
353 * Loads a {@link RasterLegendData} object from an URL.
354 * @param documentUrl {@link URL} to parse
355 * @see #parseAttributeMetaData(Element)
356 */
357 public static RasterLegendData loadRasterLegendData(final URL documentUrl) throws Exception {
358 Document document = SAX_BUILDER.build(documentUrl);
359 return parseRasterLegendData( document.getRootElement() );
360 }
361
362 /**
363 * Creates an JDOM {@link Element} for the given {@link RasterLegendData}
364 * map.
365 * @param rld raster legend data
366 */
367 public static Element createRasterLegendDataElement(final RasterLegendData rld) {
368 final Element element = new Element( ELEM_NAME_RLD , AMLURI);
369 element.setAttribute("paintGaps", rld.isPaintGaps().toString());
370 for (Double key : rld.getSortedKeys()) {
371 Element item = new Element( ELEM_NAME_RASTERLEGEND, AMLURI);
372 item.setAttribute("value", key.toString());
373 item.addContent( createTranslationElement("label", rld.get(key)) );
374 element.addContent(item);
375 }
376 return element;
377 }
378
379 /**
380 * Creates {@link RasterLegendData} from a {@link ColorMap}.
381 * @param colorMap a color map
382 * @param paintGaps indicated whether gaps are painted between the legend items
383 * @param digits number of digits the grid value classes (and legend) are
384 * rounded to (null means no round; >= 0 means digits after comma;
385 * < 0 means digits before comma) */
386 public static RasterLegendData generateRasterLegendData(ColorMap colorMap, boolean paintGaps, Integer digits) {
387 DecimalFormat decFormat = digits != null ? new DecimalFormat( SwingUtil.getNumberFormatPattern(digits) ) : null;
388 RasterLegendData rld = new RasterLegendData(paintGaps);
389 for (ColorMapEntry cme : colorMap.getColorMapEntries())
390 {
391 double value = StylingUtil.getQuantityFromColorMapEntry(cme);
392 String label = cme.getLabel();
393 // if no label is set (e.g. quantitative style),
394 // use the value as label
395 if ( label == null || label.equals("") )
396 if ( digits == null )
397 label = String.valueOf(value);
398 else
399 label = decFormat.format( LangUtil.round(value, digits) );
400 rld.put( value, new Translation(" "+label) );
401 }
402 return rld;
403 }
404
405 /**
406 * Creates {@link RasterLegendData} from the {@link ColorMap} of a style.
407 * @param style a raster style (must contain a {@link RasterSymbolizer})
408 * @param paintGaps indicated whether gaps are painted between the legend items
409 * @param digits number of digits the grid value classes (and legend) are
410 * rounded to (null means no round; >= 0 means digits after comma;
411 * < 0 means digits before comma) */
412 public static RasterLegendData generateRasterLegendData(Style style, boolean paintGaps, Integer digits) {
413 ColorMap colorMap = StylingUtil.getColorMapFromStyle(style);
414 if ( colorMap == null)
415 throw new IllegalArgumentException("Color map can not be determined from style!");
416 return generateRasterLegendData(colorMap, paintGaps, digits);
417 }
418
419 /**
420 * Saves a {@link RasterLegendData} to an URL.
421 * @param rld raster legend data
422 * @param documentUrl {@link URL} to store the XML
423 */
424 public static void saveRasterLegendData(final RasterLegendData rld, final URL documentUrl) throws Exception {
425 // Create XML-Document
426 final FileWriter out = new FileWriter( new File(documentUrl.toURI()) );
427 XML_OUTPUTTER.output(
428 createRasterLegendDataElement(rld),
429 out
430 );
431 out.flush();
432 out.close();
433 }
434
435 /**
436 * Parses a {@link Translation} object from an JDOM-{@link Element}.
437 * This method works like {@link AMLImport#parseTranslation(org.w3c.dom.Node},
438 * but for JDOM.
439 * @param element {@link Element} to parse
440 */
441 public final static Translation parseTranslation(final Element element) {
442 Translation trans = new Translation();
443
444 if (element == null)
445 return trans;
446
447 for (final Element translationElement : (List<Element>)element.getChildren()) {
448 final String name = translationElement.getName();
449 if (name == null)
450 continue;
451
452 // lang attribute
453 String lang = translationElement.getAttributeValue("lang");
454 // set the default, if no language code is set
455 if ( lang == null )
456 lang = Translation.DEFAULT_KEY;
457
458 final String translationText = translationElement.getValue();
459 if (translationText == null)
460 trans.put(lang, "");
461 else
462 trans.put(lang, translationText);
463 }
464
465 // if no <translation> is given, the value of the node should
466 // be used as a default translation
467 if (trans.size() == 0)
468 trans.put( Translation.DEFAULT_KEY, element.getValue() );
469 // trans = new Translation( ((List<Element>)element.getChildren()).get(0).getValue() );
470
471 return trans;
472 }
473
474 /**
475 * Creates an JDOM {@link Element} for the given {@link Translation}.
476 * @param tagname Name of the Element
477 * @param translation Translation to store in the Element
478 */
479 public final static Element createTranslationElement(String tagname, Translation translation) {
480 Element element = new Element(tagname, AMLURI);
481 if ( translation == null )
482 throw new UnsupportedOperationException("Translation element can not be created from null!");
483
484 // If only a default translation is set, the <translation lang="..">..</tranlation>
485 // part is not used
486 if (translation.keySet().size() == 1 && translation.get(Translation.DEFAULT_KEY) != null) {
487 element.addContent( translation.get(Translation.DEFAULT_KEY) );
488 return element;
489 }
490
491 // add a <translation lang="..">..</tranlation> part to the element for
492 // all languages
493 for (String lang : translation.keySet()) {
494 Element translationElement = new Element( ELEM_NAME_TRANSLATION , AMLURI);
495 translationElement.setAttribute("lang", lang);
496 String translationString = translation.get(lang);
497 if (translationString == null)
498 translationString = "";
499 translationElement.addContent( translationString );
500 element.addContent(translationElement);
501 }
502
503 return element;
504 }
505
506
507 /**
508 * Sets a style to {@link StyledLayerInterface}.
509 * @param styledObject a styled object
510 * @param style a Style
511 */
512 public static void setStyledLayerStyle(StyledLayerInterface styledObject, StyledLayerStyle<?> style) {
513 // set SLD style
514 styledObject.setStyle( style.getGeoObjectStyle() );
515 // set meta data
516 if ( styledObject instanceof StyledGridCoverageInterface &&
517 (style.getMetaData() instanceof RasterLegendData || style.getMetaData() == null) ) {
518 RasterLegendData sourceRld = (RasterLegendData)style.getMetaData();
519 RasterLegendData destRld = ((StyledGridCoverageInterface)styledObject).getLegendMetaData();
520 if ( destRld != null && sourceRld != null ) {
521 destRld.setPaintGaps(sourceRld.isPaintGaps());
522 destRld.clear();
523 destRld.putAll( sourceRld );
524 }
525 return;
526 }
527 if ( styledObject instanceof StyledFeatureCollectionInterface &&
528 (style.getMetaData() instanceof Map || style.getMetaData() == null) ) {
529 Map<Integer, AttributeMetaData> sourceAmd = (Map<Integer, AttributeMetaData>)style.getMetaData();
530 Map<Integer, AttributeMetaData> destAmd = ((StyledFeatureCollectionInterface)styledObject).getAttributeMetaDataMap();
531 if ( destAmd != null && sourceAmd != null ) {
532 destAmd.clear();
533 destAmd.putAll( sourceAmd );
534 }
535 return;
536 }
537
538 throw new UnsupportedOperationException("Style is not compatible to object: " +
539 (style.getMetaData() == null ? null : style.getMetaData().getClass().getSimpleName()) +
540 " <-> " +
541 (styledObject == null ? null : styledObject.getClass().getSimpleName()));
542 }
543
544 /**
545 * Returns the style a {@link StyledLayerInterface} as a {@link StyledLayerStyle}.
546 * @param styledObject a styled object
547 * @return {@code StyledLayerStyle<RasterLegendData>} for {@link StyledGridCoverageInterface}
548 * or {@code StyledLayerStyle<Map<Integer,AttributeMetaData>>} for
549 * {@link StyledFeatureCollectionInterface}
550 */
551 public static StyledLayerStyle<?> getStyledLayerStyle(StyledLayerInterface styledObject) {
552 if ( styledObject instanceof StyledGridCoverageInterface )
553 return getStyledLayerStyle( (StyledGridCoverageInterface)styledObject );
554 if ( styledObject instanceof StyledFeatureCollectionInterface )
555 return getStyledLayerStyle( (StyledFeatureCollectionInterface)styledObject );
556 throw new UnsupportedOperationException("Unknown type of StyledLayerInterface: "+(styledObject == null ? null : styledObject.getClass().getSimpleName()));
557 }
558
559 /**
560 * Returns the style and raster meta data of a {@link StyledGridCoverageInterface}
561 * as a {@link StyledLayerStyle}.
562 * @param styledGC a styled grid coverage
563 */
564 public static StyledLayerStyle<RasterLegendData> getStyledLayerStyle(StyledGridCoverageInterface styledGC) {
565 return new StyledLayerStyle<RasterLegendData>(
566 styledGC.getStyle(),
567 styledGC.getLegendMetaData()
568 );
569 }
570
571 /**
572 * Returns the style and attribute meta data of a {@link StyledFeatureCollectionInterface}
573 * as a {@link StyledLayerStyle}.
574 * @param styledFC a styled feature collection
575 */
576 public static StyledLayerStyle<Map<Integer,AttributeMetaData>> getStyledLayerStyle(StyledFeatureCollectionInterface styledFC) {
577 return new StyledLayerStyle<Map<Integer,AttributeMetaData>>(
578 styledFC.getStyle(),
579 styledFC.getAttributeMetaDataMap()
580 );
581 }
582
583 /**
584 * Loads a {@linkplain Style SLD-Style} and {@linkplain RasterLegendData Raster-LegendData}
585 * for a given geo-object (raster) source. The SLD file must be present. A missing
586 * raster legend-data file is tolerated.
587 * @param geoObjectURL URL of the (already read) raster object
588 * @param sldExt file extention for the SLD file
589 * @param rldExt file extention for the raster legend-data file
590 * @return {@code null} in case of any error
591 */
592 public static StyledLayerStyle<RasterLegendData> loadStyledRasterStyle(URL geoObjectURL, String sldExt, String rldExt) {
593 RasterLegendData metaData = null;
594 Style sldStyle = null;
595 try {
596 Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(geoObjectURL, sldExt));
597 // SLD must be present
598 if ( styles == null || styles.length == 0 )
599 return null;
600 sldStyle = styles[0];
601 }
602 catch (Exception err) {
603 // SLD must be present
604 LangUtil.logDebugError(LOGGER,err);
605 return null;
606 }
607
608 try {
609 metaData = StyledLayerUtil.loadRasterLegendData( IOUtil.changeUrlExt(geoObjectURL,rldExt) );
610 } catch (FileNotFoundException err) {
611 // ignore missing raster legend data
612 } catch (Exception err) {
613 // any other error during legend data creation leads to error
614 LangUtil.logDebugError(LOGGER,err);
615 return null;
616 }
617 return new StyledLayerStyle<RasterLegendData>(sldStyle, metaData);
618 }
619
620 /**
621 * Loads a {@linkplain Style SLD-Style} from a {@code .sld} file and
622 * {@linkplain RasterLegendData Raster-LegendData} from a {@code .rld} file
623 * for a given geo-object (raster) source. The SLD file must be present. A missing
624 * raster legend-data file is tolerated.
625 * @param geoObjectURL URL of the (already read) raster object
626 * @param sldExt file extention for the SLD file
627 * @param rldExt file extention for the raster legend-data file
628 * @return {@code null} in case of any error
629 */
630 public static StyledLayerStyle<RasterLegendData> loadStyledRasterStyle(URL geoObjectURL) {
631 return loadStyledRasterStyle(geoObjectURL, "sld", "rld");
632 }
633
634 /**
635 * Loads a {@linkplain Style SLD-Style} and a {@linkplain AttributeMetaData AttributeMetaData-Map}
636 * for a given geo-object (feature) source. The SLD file must be present. A missing
637 * attribute meta-data file is tolerated.
638 * @param geoObjectURL URL of the (already read) feature object
639 * @param sldExt file extention for the SLD file
640 * @param rldExt file extention for the raster legend-data file
641 * @return {@code null} in case of any error
642 */
643 public static StyledLayerStyle<Map<Integer,AttributeMetaData>> loadStyledFeatureStyle(URL geoObjectURL, String sldExt, String rldExt) {
644 Map<Integer,AttributeMetaData> metaData = null;
645 Style sldStyle = null;
646 try {
647 Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(geoObjectURL, sldExt));
648 // SLD must be present
649 if ( styles == null || styles.length == 0 )
650 return null;
651 sldStyle = styles[0];
652 } catch (Exception err) {
653 // SLD must be present
654 LangUtil.logDebugError(LOGGER,err);
655 return null;
656 }
657
658 try {
659 metaData = StyledLayerUtil.loadAttributeMetaDataMap( IOUtil.changeUrlExt(geoObjectURL,rldExt) );
660 } catch (FileNotFoundException err) {
661 // ignore missing attribute meta data
662 } catch (Exception err) {
663 // any other error during meta data creation leads to error
664 LangUtil.logDebugError(LOGGER,err);
665 return null;
666 }
667
668 return new StyledLayerStyle<Map<Integer,AttributeMetaData>>(sldStyle, metaData);
669 }
670
671 /**
672 * Loads a {@linkplain Style SLD-Style} from a {@code .sld} file and
673 * {@linkplain AttributeMetaData AttributeMetaData-Map} from a {@code .amd} file
674 * for a given geo-object (feature) source. The SLD file must be present. A missing
675 * attribute meta-data file is tolerated.
676 * @param geoObjectURL URL of the (already read) feature object
677 * @param sldExt file extention for the SLD file
678 * @param rldExt file extention for the raster legend-data file
679 * @return {@code null} in case of any error
680 */
681 public static StyledLayerStyle<Map<Integer,AttributeMetaData>> loadStyledFeatureStyle(URL geoObjectURL) {
682 return loadStyledFeatureStyle(geoObjectURL, "sld", "amd");
683 }
684
685 /**
686 * Stores a {@linkplain Style SLD-Style} and {@linkplain RasterLegendData Raster-LegendData}
687 * for a given geo-object (raster) source.
688 * @param style style to save
689 * @param geoObjectURL URL of the raster object
690 * @param sldExt file extention for the SLD file
691 * @param mdExt file extention for the meta-data file
692 */
693 public static <T> void saveStyledLayerStyle(StyledLayerStyle<T> style, URL geoObjectURL, String sldExt, String mdExt) throws Exception {
694 // Store the SLD
695 Style sldStyle = style.getGeoObjectStyle();
696 if ( sldStyle != null ) {
697 StylingUtil.saveStyleToSLD(
698 sldStyle,
699 IOUtil.changeFileExt(
700 new File(geoObjectURL.toURI()),
701 sldExt
702 )
703 );
704 }
705
706 // Store the meta data
707 T metaData = style.getMetaData();
708 if ( metaData != null ) {
709 if ( metaData instanceof RasterLegendData ) {
710 saveRasterLegendData(
711 (RasterLegendData)metaData,
712 IOUtil.changeUrlExt(geoObjectURL,mdExt)
713 );
714 // } else if ( metaData instanceof Map<Integer,AttributeMetaData> ) { // LEIDER NICHT KOMPILIERBAR!!
715 } else if ( metaData instanceof Map ) {
716 saveAttributeMetaDataMap(
717 (Map<Integer,AttributeMetaData>)metaData,
718 IOUtil.changeUrlExt(geoObjectURL,mdExt)
719 );
720 } else
721 throw new UnsupportedOperationException("Export for meta data not yet supported: "+metaData.getClass().getSimpleName());
722 }
723 }
724
725 /**
726 * Stores the {@linkplain Style SLD-Style} to a {@code .sld} file and
727 * the meta data ({@link RasterLegendData} or {@link AttributeMetaData})
728 * to a {@code .rld} or {@code .amd} file.
729 * for a given geo-object source.
730 * @param style style to save
731 * @param geoObjectURL URL of the (already read) raster object
732 */
733 public static void saveStyledLayerStyle(StyledLayerStyle<?> style, URL geoObjectURL) throws Exception {
734 if ( style.getMetaData() instanceof RasterLegendData )
735 saveStyledLayerStyle(style,geoObjectURL, "sld", "rld");
736 else
737 saveStyledLayerStyle(style,geoObjectURL, "sld", "amd");
738 }
739
740
741
742 /**
743 * Creates a {@link Box} that shows a legend for a list of
744 * {@link FeatureTypeStyle}s and a targeted featureType
745 *
746 * @param featureType
747 * If this a legend for Point, Polygon or Line?
748 * @param list
749 * The Styles to presented in this legend
750 *
751 * @author <a href="mailto:[email protected]">Stefan Alfons
752 * Kr&uuml;ger</a>
753 */
754 public static Box createLegendPanel(List<FeatureTypeStyle> list,
755 SimpleFeatureType featureType, int iconWidth, int iconHeight) {
756
757 Box box = new Box(BoxLayout.Y_AXIS) {
758
759 /**
760 * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein Screenshot
761 * gemacht wird) wird. Dann werden wird der Hintergrund auf WEISS
762 * gesetzt.
763 *
764 * @author <a href="mailto:[email protected]">Stefan Alfons
765 * Kr&uuml;ger</a>
766 */
767 @Override
768 public void print(Graphics g) {
769 final Color orig = getBackground();
770 setBackground(Color.WHITE);
771 // wrap in try/finally so that we always restore the state
772 try {
773 super.print(g);
774 } finally {
775 setBackground(orig);
776 }
777 }
778 };
779
780 for (FeatureTypeStyle ftStyle : list) {
781
782 // One child-node for every rule
783 List<Rule> rules = ftStyle.rules();
784 for (Rule rule : rules) {
785
786 /**
787 * Let's not create a hbox for Rules that only contain
788 * TextSymbolizers
789 */
790 if (StylingUtil.getTextSymbolizers(rule.getSymbolizers())
791 .size() == rule.getSymbolizers().length)
792 continue;
793
794 Box hbox = new Box(BoxLayout.X_AXIS) {
795
796 /**
797 * Nuetzlich wenn die Componente gedruckt (z.B. wenn ein
798 * Screenshot gemacht wird) wird. Dann werden wird der
799 * Hintergrund auf WEISS gesetzt.
800 *
801 * @author <a href="mailto:[email protected]">Stefan
802 * Alfons Kr&uuml;ger</a>
803 */
804 @Override
805 public void print(Graphics g) {
806 final Color orig = getBackground();
807 setBackground(Color.WHITE);
808 // wrap in try/finally so that we always restore the
809 // state
810 try {
811 super.print(g);
812 } finally {
813 setBackground(orig);
814 }
815 }
816 };
817
818 /**
819 * The size of the legend Symbol is dependent on the size of the
820 * font.
821 */
822 final int fontHeight = new JLabel().getFontMetrics(
823 new JLabel().getFont()).getHeight();
824
825 final Dimension ICON_SIZE = new Dimension(iconWidth,
826 fontHeight > 5 ? fontHeight : iconHeight);
827
828 // ****************************************************************************
829 // Create the actual icon
830 // ****************************************************************************
831 final BufferedImage imageForRule = LegendIconFeatureRenderer
832 .getInstance().createImageForRule(rule, featureType,
833 ICON_SIZE);
834
835 // LOGGER.debug("Creating a new Legend Image for RUle name =
836 // "+rule.getName());
837
838 ImageIcon legendIcon = new ImageIcon(imageForRule);
839
840 final JLabel iconLabel = new JLabel(legendIcon);
841 hbox.setAlignmentX(0f);
842 hbox.add(iconLabel);
843 hbox.add(Box.createHorizontalStrut(3));
844
845 // ****************************************************************************
846 // The labeltext is read from the SLD. Its either the title
847 // directly, or interpreted as OneLine Code
848 // ****************************************************************************
849 // final String rawText =
850 // rule.getDescription().getTitle().toString();
851 final String rawText = rule.getDescription().getTitle().toString();
852
853 Translation labelT = new Translation();
854 labelT.fromOneLine(rawText);
855
856 final JLabel classTitleLabel = new JLabel(labelT.toString());
857 hbox.add(classTitleLabel);
858 classTitleLabel.setLabelFor(iconLabel);
859
860 box.add(hbox);
861
862 }
863 }
864
865 return box;
866 }
867
868
869 /**
870 * Creates a
871 * @param styledGrid
872 * @param iconHeight
873 * @param iconWidth
874 * @return
875 */
876 public static Box createLegendPanel(StyledRasterInterface<?> styledGrid, int iconWidth, int iconHeight) {
877 throw new RuntimeException("Not yet...");
878 }
879
880
881 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26