/[schmitzm]/branches/2.3.x/src/skrueger/geotools/StyledLayerUtil.java
ViewVC logotype

Annotation of /branches/2.3.x/src/skrueger/geotools/StyledLayerUtil.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1228 - (hide annotations)
Wed Nov 3 20:44:16 2010 UTC (14 years, 3 months ago) by alfonx
Original Path: trunk/src/skrueger/geotools/StyledLayerUtil.java
File size: 48380 byte(s)
c
1 alfonx 244 /*******************************************************************************
2     * Copyright (c) 2009 Martin O. J. Schmitz.
3     *
4     * This file is part of the SCHMITZM library - a collection of utility
5 alfonx 256 * classes based on Java 1.6, focusing (not only) on Java Swing
6 alfonx 244 * 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 alfonx 862 * Stefan A. Tzeggai - additional utility classes
29 alfonx 244 ******************************************************************************/
30     package skrueger.geotools;
31    
32 alfonx 397 import java.awt.Color;
33     import java.awt.Dimension;
34 alfonx 400 import java.awt.Graphics2D;
35     import java.awt.Rectangle;
36     import java.awt.geom.AffineTransform;
37 alfonx 397 import java.awt.image.BufferedImage;
38 alfonx 403 import java.awt.image.ColorModel;
39 alfonx 405 import java.awt.image.ComponentColorModel;
40     import java.awt.image.DataBuffer;
41 alfonx 1215 import java.awt.image.IndexColorModel;
42 alfonx 244 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 alfonx 607 import java.util.ArrayList;
48 alfonx 244 import java.util.List;
49     import java.util.Map;
50 alfonx 769 import java.util.Set;
51 alfonx 244
52 alfonx 403 import javax.swing.BorderFactory;
53 alfonx 397 import javax.swing.ImageIcon;
54 alfonx 516 import javax.swing.JComponent;
55 alfonx 397 import javax.swing.JLabel;
56    
57 alfonx 516 import net.miginfocom.swing.MigLayout;
58    
59 alfonx 244 import org.apache.log4j.Logger;
60 alfonx 403 import org.geotools.coverage.grid.GeneralGridEnvelope;
61 alfonx 244 import org.geotools.coverage.grid.GridCoverage2D;
62 alfonx 403 import org.geotools.coverage.grid.GridGeometry2D;
63 alfonx 244 import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
64 alfonx 405 import org.geotools.coverage.grid.io.AbstractGridFormat;
65 alfonx 244 import org.geotools.feature.FeatureCollection;
66 alfonx 464 import org.geotools.feature.NameImpl;
67 alfonx 403 import org.geotools.geometry.jts.ReferencedEnvelope;
68 alfonx 244 import org.geotools.map.DefaultMapLayer;
69     import org.geotools.map.MapLayer;
70 alfonx 405 import org.geotools.parameter.Parameter;
71 alfonx 400 import org.geotools.renderer.lite.gridcoverage2d.GridCoverageRenderer;
72 alfonx 244 import org.geotools.styling.ColorMap;
73     import org.geotools.styling.ColorMapEntry;
74 alfonx 397 import org.geotools.styling.FeatureTypeStyle;
75 alfonx 244 import org.geotools.styling.RasterSymbolizer;
76 alfonx 397 import org.geotools.styling.Rule;
77 alfonx 244 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 alfonx 420 import org.opengis.feature.simple.SimpleFeature;
83 alfonx 397 import org.opengis.feature.simple.SimpleFeatureType;
84 alfonx 607 import org.opengis.feature.type.AttributeDescriptor;
85     import org.opengis.feature.type.GeometryDescriptor;
86 alfonx 464 import org.opengis.feature.type.Name;
87 alfonx 403 import org.opengis.parameter.GeneralParameterValue;
88 alfonx 244
89 alfonx 400 import schmitzm.geotools.JTSUtil;
90 alfonx 420 import schmitzm.geotools.feature.FeatureUtil;
91 alfonx 244 import schmitzm.geotools.styling.StylingUtil;
92     import schmitzm.io.IOUtil;
93     import schmitzm.lang.LangUtil;
94 alfonx 1225 import schmitzm.swing.ExceptionDialog;
95 alfonx 516 import schmitzm.swing.JPanel;
96 alfonx 244 import schmitzm.swing.SwingUtil;
97 alfonx 788 import skrueger.AttributeMetadataImpl;
98 alfonx 772 import skrueger.AttributeMetadataInterface;
99 alfonx 244 import skrueger.RasterLegendData;
100     import skrueger.i8n.Translation;
101    
102 alfonx 1228 import com.vividsolutions.jts.geom.Geometry;
103    
104 alfonx 244 /**
105     * This class provides static helper methods for dealing with
106     * {@link StyledLayerInterface} stuff.
107 alfonx 400 *
108     * @author <a href="mailto:[email protected]">Martin Schmitz</a>
109     * (University of Bonn/Germany)
110 alfonx 244 * @version 1.0
111     */
112     public class StyledLayerUtil {
113 alfonx 400 private static final Logger LOGGER = Logger.getLogger(StyledLayerUtil.class
114     .getName());
115     private static final SAXBuilder SAX_BUILDER = new SAXBuilder();
116     private static final XMLOutputter XML_OUTPUTTER = new XMLOutputter();
117 alfonx 244
118 alfonx 400 /** URL for Atlas XML schema */
119     public static final String AMLURI = "http://www.wikisquare.de/AtlasML";
120     /** Name of the XML Element for the attribute meta data map */
121     public static final String ELEM_NAME_AMD = "attributeMetaData";
122     /** Name of the XML Element for the raster legend data */
123     public static final String ELEM_NAME_RLD = "rasterLegendData";
124     /** Name of the XML Element for an attribute meta data map entry */
125     public static final String ELEM_NAME_ATTRIBUTE = "dataAttribute";
126     /** Name of the XML Element for an raster legend data entry */
127     public static final String ELEM_NAME_RASTERLEGEND = "rasterLegendItem";
128     /** Name of the XML Element for a translation */
129     public static final String ELEM_NAME_TRANSLATION = "translation";
130 alfonx 244
131 alfonx 400 /**
132     * Creates a Geotools {@link MapLayer} from an object. If the object is a
133     * {@link StyledLayerInterface} then its sytle is used. In case of direct
134     * Geotools objects ({@link GridCoverage2D},
135     * {@link AbstractGridCoverage2DReader}, {@link FeatureCollection}) a
136     * default style is generated.
137     *
138     * @param object
139     * an Object
140     * @exception Exception
141     * if {@code null} is given as object or an error occurs
142     * during layer creation
143     */
144 alfonx 516 public static MapLayer createMapLayer(final Object object) throws Exception {
145 alfonx 400 return createMapLayer(object, null);
146     }
147 alfonx 244
148 alfonx 400 /**
149     * Creates a Geotools {@link MapLayer} from an object. If the object is a
150     * {@link StyledLayerInterface} then its sytle is used. In case of direct
151     * Geotools objects ({@link GridCoverage2D},
152     * {@link AbstractGridCoverage2DReader}, {@link FeatureCollection}) a
153     * default style is generated.
154     *
155     * @param object
156     * an Object
157     * @param forcedStyle
158     * (SLD-)Style to force for the object
159     * @exception Exception
160     * if {@code null} is given as object or an error occurs
161     * during layer creation
162     */
163 alfonx 516 public static MapLayer createMapLayer(Object object, final Style forcedStyle)
164 alfonx 400 throws Exception {
165     MapLayer layer = null;
166     Style style = null;
167     if (object instanceof StyledLayerInterface) {
168     style = ((StyledLayerInterface<?>) object).getStyle();
169     object = ((StyledLayerInterface<?>) object).getGeoObject();
170     }
171     if (forcedStyle != null)
172     style = forcedStyle;
173     if (style == null)
174     style = StylingUtil.createDefaultStyle(object);
175 alfonx 244
176 alfonx 400 if (object instanceof GridCoverage2D)
177     layer = new DefaultMapLayer((GridCoverage2D) object, style);
178     if (object instanceof AbstractGridCoverage2DReader)
179     layer = new DefaultMapLayer((AbstractGridCoverage2DReader) object,
180     style);
181     if (object instanceof FeatureCollection)
182     layer = new DefaultMapLayer((FeatureCollection) object, style);
183 alfonx 244
184 alfonx 400 if (layer == null)
185     throw new Exception("Can not create MapLayer from "
186     + (object == null ? "null" : object.getClass()));
187 alfonx 244
188 alfonx 400 return layer;
189     }
190 alfonx 244
191 alfonx 400 /**
192     * Creates an default instance of {@link StyledLayerInterface} for a
193     * Geotools object ({@link GridCoverage2D}, {@link FeatureCollection}) with
194     * a default style.
195     *
196     * @param object
197     * an Object
198     * @param title
199     * title for the object
200     * @exception UnsupportedOperationException
201     * if {@code null} is given as object or an error occurs
202     * during creation
203     */
204 alfonx 516 public static StyledLayerInterface<?> createStyledLayer(
205     final Object object, final String title) {
206 alfonx 400 return createStyledLayer(object, title, null);
207     }
208 alfonx 244
209 alfonx 400 /**
210     * Creates an default instance of {@link StyledLayerInterface} for a
211     * Geotools object ({@link GridCoverage2D}, {@link FeatureCollection}) with
212     * a given style.
213     *
214     * @param object
215     * an Object
216     * @param title
217     * title for the object
218     * @param style
219     * style and meta data for the object
220     * @exception UnsupportedOperationException
221     * if {@code null} is given as object or an error occurs
222     * during creation
223     */
224 alfonx 516 public static StyledLayerInterface<?> createStyledLayer(
225     final Object object, final String title,
226     final StyledLayerStyle style) {
227 alfonx 400 StyledLayerInterface<?> styledLayer = null;
228 alfonx 244
229 alfonx 516 final String id = (title != null) ? title : "defaultID";
230 alfonx 244
231 alfonx 400 if (object instanceof GridCoverage2D)
232     styledLayer = new StyledGridCoverage((GridCoverage2D) object, id,
233     title, style);
234     else if (object instanceof AbstractGridCoverage2DReader)
235     styledLayer = new StyledGridCoverageReader(
236     (AbstractGridCoverage2DReader) object, id, title, style);
237     else if (object instanceof FeatureCollection)
238     styledLayer = new StyledFeatureCollection(
239     (FeatureCollection) object, id, title, style);
240 alfonx 244
241 alfonx 400 if (styledLayer == null)
242     throw new UnsupportedOperationException(
243     "Can not create StyledLayerInterface object from "
244     + (object == null ? "null" : object.getClass()));
245 alfonx 244
246 alfonx 400 return styledLayer;
247     }
248 alfonx 244
249 alfonx 400 /**
250     * Return only the visible or invisible entries of an AttributeMetaData-Map.
251     *
252     * @param amdMap
253     * AttributeMetaData-Map
254     * @param visible
255     * indicated whether the visible or invisible entries are
256     * returned
257 alfonx 607 *
258     * TODO replace with
259     * {@link AttributeMetadataMap#sortedValuesVisibleOnly()}
260 alfonx 400 */
261 alfonx 1215 public static AttributeMetadataMap<? extends AttributeMetadataInterface> getVisibleAttributeMetaData(
262 alfonx 772 final AttributeMetadataMap<? extends AttributeMetadataInterface> amdMap,
263 alfonx 769 final boolean visible) {
264 alfonx 516
265 alfonx 1215 final AttributeMetadataMap<AttributeMetadataInterface> filteredMap = (AttributeMetadataMap<AttributeMetadataInterface>) amdMap
266     .clone();
267     if (filteredMap.size() > 0) {
268 alfonx 769 filteredMap.clear(); // Just in case the close copies the contents
269     }
270 alfonx 1215
271 alfonx 772 for (final AttributeMetadataInterface amd : amdMap.values())
272 alfonx 464 if (amd.isVisible() == visible)
273     filteredMap.put(amd.getName(), amd);
274 alfonx 244
275 alfonx 400 return filteredMap;
276     }
277 alfonx 244
278 alfonx 400 /**
279 alfonx 769 * Parses a {@link AttributeMetadataImpl} object from an JDOM-
280     * {@link Element}. This method works like {@link
281 alfonx 400 * AMLImport#parseDataAttribute(org.w3c.dom.Node}, but for JDOM.
282     *
283 alfonx 534 * TODO 20.11.2009, SK: There are some new attribute weight, functiona,
284     * functionX and nodata in AttributeMetaData that should be parsed/exported
285     * too. but this method is only used by ISDSS, which is not supporting that
286     * stuff anyways.
287     *
288 alfonx 400 * @param element
289     * {@link Element} to parse
290     */
291 alfonx 769 public static AttributeMetadataImpl parseAttributeMetaData(
292     final Element element) {
293 mojays 1039 final String namespace = element.getAttributeValue("namespace");
294     final String localname = element.getAttributeValue("localname");
295 alfonx 770 final NameImpl aName = new NameImpl(namespace, localname);
296 alfonx 400 final Boolean visible = Boolean.valueOf(element
297     .getAttributeValue("visible"));
298     final String unit = element.getAttributeValue("unit");
299 alfonx 244
300 alfonx 400 Translation name = new Translation();
301     Translation desc = new Translation();
302     for (final Element childElement : (List<Element>) element.getChildren()) {
303     if (childElement.getName() == null)
304     continue;
305 alfonx 244
306 alfonx 400 if (childElement.getName().equals("name"))
307     name = parseTranslation(childElement);
308     else if (childElement.getName().equals("desc"))
309     desc = parseTranslation(childElement);
310     }
311 alfonx 769 return new AttributeMetadataImpl(aName, visible, name, desc, unit);
312 alfonx 400 }
313 alfonx 244
314 alfonx 400 /**
315 alfonx 769 * Parses a {@link AttributeMetadataImpl} map from an JDOM-{@link Element}
316     * with {@code <attribute>}-childs.
317 alfonx 400 *
318     * @param element
319     * {@link Element} to parse
320 alfonx 677 *
321 alfonx 769 * TODO Since GP 1.3 the {@link AttributeMetadataImpl} class has
322     * more attributes which are not used by Xulu/ISDSS. GP
323 alfonx 658 * exports/imports the AMD via AMLExporter and AMLImporter
324 alfonx 677 * classes. (SK, 3.2.2010) *
325 alfonx 400 */
326 alfonx 420 public static AttributeMetadataMap parseAttributeMetaDataMap(
327 alfonx 400 final Element element) {
328 alfonx 769 final AttributeMetadataMap metaData = new AttributeMetadataImplMap();
329 alfonx 516 final List<Element> attributesElements = element
330 alfonx 400 .getChildren(ELEM_NAME_ATTRIBUTE);
331 alfonx 516 for (final Element attibuteElement : attributesElements) {
332 alfonx 769 final AttributeMetadataImpl attrMetaData = parseAttributeMetaData(attibuteElement);
333 alfonx 464 metaData.put(attrMetaData.getName(), attrMetaData);
334 alfonx 400 }
335     return metaData;
336     }
337 alfonx 244
338 alfonx 400 /**
339 alfonx 769 * Loads a {@link AttributeMetadataImpl} object from an URL.
340 alfonx 400 *
341     * @param documentUrl
342     * {@link URL} to parse
343     * @see #parseAttributeMetaData(Element)
344     */
345 alfonx 420 public static AttributeMetadataMap loadAttributeMetaDataMap(
346 alfonx 400 final URL documentUrl) throws Exception {
347 alfonx 516 final Document document = SAX_BUILDER.build(documentUrl);
348 alfonx 400 return parseAttributeMetaDataMap(document.getRootElement());
349     }
350 alfonx 244
351 alfonx 400 /**
352 alfonx 769 * Creates an JDOM {@link Element} for the given
353     * {@link AttributeMetadataImpl} object.
354 alfonx 400 *
355     * @param amd
356     * meta data for one attribute
357 alfonx 658 *
358 alfonx 769 * TODO Since GP 1.3 the {@link AttributeMetadataImpl} class has
359     * more attributes which are not used by Xulu/ISDSS. GP
360 alfonx 658 * exports/imports the AMD via AMLExporter and AMLImporter
361     * classes. (SK, 3.2.2010)
362 alfonx 400 */
363     public static Element createAttributeMetaDataElement(
364 alfonx 772 final AttributeMetadataInterface amd) {
365 alfonx 400 final Element element = new Element(ELEM_NAME_ATTRIBUTE, AMLURI);
366 alfonx 1215 element.setAttribute("namespace",
367     String.valueOf(amd.getName().getNamespaceURI()));
368 alfonx 464 element.setAttribute("localname", String.valueOf(amd.getLocalName()));
369 alfonx 400 element.setAttribute("visible", String.valueOf(amd.isVisible()));
370     element.setAttribute("unit", amd.getUnit());
371     // Creating a aml:name tag...
372     element.addContent(createTranslationElement("name", amd.getTitle()));
373     // Creating a aml:desc tag...
374     element.addContent(createTranslationElement("desc", amd.getDesc()));
375     return element;
376     }
377 alfonx 244
378 alfonx 400 /**
379 alfonx 769 * Creates an JDOM {@link Element} for the given
380     * {@link AttributeMetadataImpl} map.
381 alfonx 400 *
382     * @param amdMap
383     * map of attribute meta data
384     */
385     public static Element createAttributeMetaDataMapElement(
386 alfonx 772 final AttributeMetadataMap<? extends AttributeMetadataInterface> amdMap) {
387 alfonx 400 final Element element = new Element(ELEM_NAME_AMD, AMLURI);
388 alfonx 772 for (final AttributeMetadataInterface amd : amdMap.values())
389 alfonx 400 element.addContent(createAttributeMetaDataElement(amd));
390     return element;
391     }
392 alfonx 244
393 alfonx 400 /**
394 alfonx 769 * Saves a {@link AttributeMetadataImpl AttributeMetaData-Map} to an URL.
395 alfonx 400 *
396     * @param amdMap
397 alfonx 769 * map of {@link AttributeMetadataImpl}
398 alfonx 400 * @param documentUrl
399     * {@link URL} to store the XML
400     */
401     public static void saveAttributeMetaDataMap(
402 alfonx 420 final AttributeMetadataMap amdMap, final URL documentUrl)
403 alfonx 400 throws Exception {
404     // Create XML-Document
405     final FileWriter out = new FileWriter(new File(documentUrl.toURI()));
406     XML_OUTPUTTER.output(createAttributeMetaDataMapElement(amdMap), out);
407     out.flush();
408     out.close();
409     }
410 alfonx 244
411 alfonx 400 /**
412     * Parses a {@link RasterLegendData} object from an JDOM-{@link Element}.
413     * This method works like {@link
414     * AMLImport#parseRasterLegendData(org.w3c.dom.Node}, but for JDOM.
415     *
416     * @param element
417     * {@link Element} to parse
418     */
419 alfonx 516 public static RasterLegendData parseRasterLegendData(final Element element) {
420 alfonx 244
421 alfonx 400 final boolean paintGaps = Boolean.valueOf(element
422     .getAttributeValue("paintGaps"));
423 alfonx 244
424 alfonx 516 final RasterLegendData rld = new RasterLegendData(paintGaps);
425 alfonx 244
426 alfonx 516 for (final Element childElement : (List<Element>) element.getChildren()) {
427 alfonx 400 final String name = childElement.getName();
428     // Cancel if it's an attribute
429     if (childElement.getChildren().size() == 0)
430     continue;
431 alfonx 244
432 alfonx 400 if (name.equals(ELEM_NAME_RASTERLEGEND)) {
433     final String valueAttr = childElement
434     .getAttributeValue("value");
435     if (valueAttr == null)
436     throw new UnsupportedOperationException(
437     "Attribute 'value' missing for definition of <"
438     + ELEM_NAME_RASTERLEGEND + ">");
439     final double value = Double.valueOf(valueAttr);
440 alfonx 244
441 alfonx 400 // first and only item should be the label
442     final Element labelElement = childElement.getChild("label");
443     // id label element is missing, the translation is searched
444     // directly
445     // as childs of the rasterLegendItem element
446 alfonx 516 final Translation label = parseTranslation(labelElement != null ? labelElement
447 alfonx 400 : childElement);
448     rld.put(value, label);
449     }
450     }
451 alfonx 244
452 alfonx 400 return rld;
453     }
454 alfonx 244
455 alfonx 400 /**
456     * Loads a {@link RasterLegendData} object from an URL.
457     *
458     * @param documentUrl
459     * {@link URL} to parse
460     * @see #parseAttributeMetaData(Element)
461     */
462     public static RasterLegendData loadRasterLegendData(final URL documentUrl)
463     throws Exception {
464 alfonx 516 final Document document = SAX_BUILDER.build(documentUrl);
465 alfonx 400 return parseRasterLegendData(document.getRootElement());
466     }
467 alfonx 244
468 alfonx 400 /**
469     * Creates an JDOM {@link Element} for the given {@link RasterLegendData}
470     * map.
471     *
472     * @param rld
473     * raster legend data
474     */
475     public static Element createRasterLegendDataElement(
476     final RasterLegendData rld) {
477     final Element element = new Element(ELEM_NAME_RLD, AMLURI);
478     element.setAttribute("paintGaps", rld.isPaintGaps().toString());
479 alfonx 516 for (final Double key : rld.getSortedKeys()) {
480     final Element item = new Element(ELEM_NAME_RASTERLEGEND, AMLURI);
481 alfonx 400 item.setAttribute("value", key.toString());
482     item.addContent(createTranslationElement("label", rld.get(key)));
483     element.addContent(item);
484     }
485     return element;
486     }
487 alfonx 244
488 alfonx 400 /**
489     * Creates {@link RasterLegendData} from a {@link ColorMap}.
490     *
491     * @param colorMap
492     * a color map
493     * @param paintGaps
494     * indicated whether gaps are painted between the legend items
495     * @param digits
496     * number of digits the grid value classes (and legend) are
497     * rounded to (null means no round; >= 0 means digits after
498     * comma; < 0 means digits before comma)
499     */
500 alfonx 516 public static RasterLegendData generateRasterLegendData(
501     final ColorMap colorMap, final boolean paintGaps,
502     final Integer digits) {
503     final DecimalFormat decFormat = digits != null ? new DecimalFormat(
504     SwingUtil.getNumberFormatPattern(digits)) : null;
505     final RasterLegendData rld = new RasterLegendData(paintGaps);
506     for (final ColorMapEntry cme : colorMap.getColorMapEntries()) {
507     final double value = StylingUtil.getQuantityFromColorMapEntry(cme);
508 alfonx 400 String label = cme.getLabel();
509     // if no label is set (e.g. quantitative style),
510     // use the value as label
511     if (label == null || label.equals(""))
512     if (digits == null)
513     label = String.valueOf(value);
514     else
515     label = decFormat.format(LangUtil.round(value, digits));
516     rld.put(value, new Translation(" " + label));
517     }
518     return rld;
519     }
520 alfonx 244
521 alfonx 400 /**
522     * Creates {@link RasterLegendData} from the {@link ColorMap} of a style.
523     *
524     * @param style
525     * a raster style (must contain a {@link RasterSymbolizer})
526     * @param paintGaps
527     * indicated whether gaps are painted between the legend items
528     * @param digits
529     * number of digits the grid value classes (and legend) are
530     * rounded to (null means no round; >= 0 means digits after
531     * comma; < 0 means digits before comma)
532     */
533 alfonx 516 public static RasterLegendData generateRasterLegendData(final Style style,
534     final boolean paintGaps, final Integer digits) {
535     final ColorMap colorMap = StylingUtil.getColorMapFromStyle(style);
536 alfonx 400 if (colorMap == null)
537     throw new IllegalArgumentException(
538     "Color map can not be determined from style!");
539     return generateRasterLegendData(colorMap, paintGaps, digits);
540     }
541 alfonx 244
542 alfonx 400 /**
543     * Saves a {@link RasterLegendData} to an URL.
544     *
545     * @param rld
546     * raster legend data
547     * @param documentUrl
548     * {@link URL} to store the XML
549     */
550     public static void saveRasterLegendData(final RasterLegendData rld,
551     final URL documentUrl) throws Exception {
552     // Create XML-Document
553     final FileWriter out = new FileWriter(new File(documentUrl.toURI()));
554     XML_OUTPUTTER.output(createRasterLegendDataElement(rld), out);
555     out.flush();
556     out.close();
557     }
558 alfonx 244
559 alfonx 400 /**
560     * Parses a {@link Translation} object from an JDOM-{@link Element}. This
561     * method works like {@link AMLImport#parseTranslation(org.w3c.dom.Node},
562     * but for JDOM.
563     *
564     * @param element
565     * {@link Element} to parse
566     */
567     public final static Translation parseTranslation(final Element element) {
568 alfonx 516 final Translation trans = new Translation();
569 alfonx 244
570 alfonx 400 if (element == null)
571     return trans;
572 alfonx 244
573 alfonx 400 for (final Element translationElement : (List<Element>) element
574     .getChildren()) {
575     final String name = translationElement.getName();
576     if (name == null)
577     continue;
578 alfonx 244
579 alfonx 400 // lang attribute
580     String lang = translationElement.getAttributeValue("lang");
581     // set the default, if no language code is set
582     if (lang == null)
583     lang = Translation.DEFAULT_KEY;
584 alfonx 244
585 alfonx 400 final String translationText = translationElement.getValue();
586     if (translationText == null)
587     trans.put(lang, "");
588     else
589     trans.put(lang, translationText);
590     }
591 alfonx 244
592 alfonx 400 // if no <translation> is given, the value of the node should
593     // be used as a default translation
594     if (trans.size() == 0)
595     trans.put(Translation.DEFAULT_KEY, element.getValue());
596     // trans = new Translation(
597     // ((List<Element>)element.getChildren()).get(0).getValue() );
598 alfonx 244
599 alfonx 400 return trans;
600     }
601 alfonx 244
602 alfonx 400 /**
603     * Creates an JDOM {@link Element} for the given {@link Translation}.
604     *
605     * @param tagname
606     * Name of the Element
607     * @param translation
608     * Translation to store in the Element
609     */
610 alfonx 516 public final static Element createTranslationElement(final String tagname,
611     final Translation translation) {
612     final Element element = new Element(tagname, AMLURI);
613 alfonx 400 if (translation == null)
614     throw new UnsupportedOperationException(
615     "Translation element can not be created from null!");
616 alfonx 244
617 alfonx 400 // If only a default translation is set, the <translation
618     // lang="..">..</tranlation>
619     // part is not used
620     if (translation.keySet().size() == 1
621     && translation.get(Translation.DEFAULT_KEY) != null) {
622     element.addContent(translation.get(Translation.DEFAULT_KEY));
623     return element;
624     }
625 alfonx 244
626 alfonx 400 // add a <translation lang="..">..</tranlation> part to the element for
627     // all languages
628 alfonx 516 for (final String lang : translation.keySet()) {
629     final Element translationElement = new Element(
630     ELEM_NAME_TRANSLATION, AMLURI);
631 alfonx 400 translationElement.setAttribute("lang", lang);
632     String translationString = translation.get(lang);
633     if (translationString == null)
634     translationString = "";
635     translationElement.addContent(translationString);
636     element.addContent(translationElement);
637     }
638 alfonx 244
639 alfonx 400 return element;
640     }
641 alfonx 244
642 alfonx 400 /**
643     * Sets a style to {@link StyledLayerInterface}.
644     *
645     * @param styledObject
646     * a styled object
647     * @param style
648     * a Style
649     */
650 alfonx 516 public static void setStyledLayerStyle(
651     final StyledLayerInterface styledObject,
652     final StyledLayerStyle<?> style) {
653 alfonx 400 // set SLD style
654     styledObject.setStyle(style.getGeoObjectStyle());
655     // set meta data
656     if (styledObject instanceof StyledGridCoverageInterface
657     && (style.getMetaData() instanceof RasterLegendData || style
658     .getMetaData() == null)) {
659 alfonx 516 final RasterLegendData sourceRld = (RasterLegendData) style
660     .getMetaData();
661     final RasterLegendData destRld = ((StyledGridCoverageInterface) styledObject)
662 alfonx 400 .getLegendMetaData();
663     if (destRld != null && sourceRld != null) {
664     destRld.setPaintGaps(sourceRld.isPaintGaps());
665     destRld.clear();
666     destRld.putAll(sourceRld);
667     }
668     return;
669     }
670     if (styledObject instanceof StyledFeatureCollectionInterface
671     && (style.getMetaData() instanceof Map || style.getMetaData() == null)) {
672 alfonx 516 final AttributeMetadataMap sourceAmd = (AttributeMetadataMap) style
673 alfonx 400 .getMetaData();
674 alfonx 516 final AttributeMetadataMap destAmd = ((StyledFeatureCollectionInterface) styledObject)
675 alfonx 400 .getAttributeMetaDataMap();
676     if (destAmd != null && sourceAmd != null) {
677     destAmd.clear();
678     destAmd.putAll(sourceAmd);
679     }
680     return;
681     }
682 alfonx 244
683 alfonx 400 throw new UnsupportedOperationException(
684     "Style is not compatible to object: "
685     + (style.getMetaData() == null ? null : style
686     .getMetaData().getClass().getSimpleName())
687     + " <-> "
688     + (styledObject == null ? null : styledObject
689     .getClass().getSimpleName()));
690     }
691 alfonx 244
692 alfonx 400 /**
693     * Returns the style a {@link StyledLayerInterface} as a
694     * {@link StyledLayerStyle}.
695     *
696     * @param styledObject
697     * a styled object
698     * @return {@code StyledLayerStyle<RasterLegendData>} for
699 alfonx 1215 * {@link StyledGridCoverageInterface} or
700     * {@code StyledLayerStyle<Map<Integer,AttributeMetaData>>} for
701 alfonx 400 * {@link StyledFeatureCollectionInterface}
702     */
703     public static StyledLayerStyle<?> getStyledLayerStyle(
704 alfonx 516 final StyledLayerInterface styledObject) {
705 alfonx 400 if (styledObject instanceof StyledGridCoverageInterface)
706     return getStyledLayerStyle((StyledGridCoverageInterface) styledObject);
707     if (styledObject instanceof StyledFeatureCollectionInterface)
708     return getStyledLayerStyle((StyledFeatureCollectionInterface) styledObject);
709     throw new UnsupportedOperationException(
710     "Unknown type of StyledLayerInterface: "
711     + (styledObject == null ? null : styledObject
712     .getClass().getSimpleName()));
713     }
714 alfonx 244
715 alfonx 400 /**
716     * Returns the style and raster meta data of a
717     * {@link StyledGridCoverageInterface} as a {@link StyledLayerStyle}.
718     *
719     * @param styledGC
720     * a styled grid coverage
721     */
722     public static StyledLayerStyle<RasterLegendData> getStyledLayerStyle(
723 alfonx 516 final StyledGridCoverageInterface styledGC) {
724 alfonx 400 return new StyledLayerStyle<RasterLegendData>(styledGC.getStyle(),
725     styledGC.getLegendMetaData());
726     }
727 alfonx 244
728 alfonx 400 /**
729     * Returns the style and attribute meta data of a
730     * {@link StyledFeatureCollectionInterface} as a {@link StyledLayerStyle}.
731     *
732     * @param styledFC
733     * a styled feature collection
734     */
735 alfonx 464 public static StyledLayerStyle<AttributeMetadataMap> getStyledLayerStyle(
736 alfonx 516 final StyledFeatureCollectionInterface styledFC) {
737     return new StyledLayerStyle<AttributeMetadataMap>(styledFC.getStyle(),
738     styledFC.getAttributeMetaDataMap());
739 alfonx 400 }
740 alfonx 244
741 alfonx 400 /**
742     * Loads a {@linkplain Style SLD-Style} and {@linkplain RasterLegendData
743     * Raster-LegendData} for a given geo-object (raster) source. The SLD file
744     * must be present. A 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 alfonx 516 final URL geoObjectURL, final String sldExt, final String rldExt) {
756 alfonx 400 RasterLegendData metaData = null;
757     Style sldStyle = null;
758     try {
759 alfonx 516 final Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(
760 alfonx 400 geoObjectURL, sldExt));
761     // SLD must be present
762     if (styles == null || styles.length == 0)
763     return null;
764     sldStyle = styles[0];
765 alfonx 516 } catch (final Exception err) {
766 alfonx 400 // SLD must be present
767     LangUtil.logDebugError(LOGGER, err);
768     return null;
769     }
770 alfonx 244
771 alfonx 400 try {
772     metaData = StyledLayerUtil.loadRasterLegendData(IOUtil
773     .changeUrlExt(geoObjectURL, rldExt));
774 alfonx 516 } catch (final FileNotFoundException err) {
775 alfonx 400 // ignore missing raster legend data
776 alfonx 516 } catch (final Exception err) {
777 alfonx 400 // any other error during legend data creation leads to error
778     LangUtil.logDebugError(LOGGER, err);
779     return null;
780     }
781     return new StyledLayerStyle<RasterLegendData>(sldStyle, metaData);
782     }
783 alfonx 244
784 alfonx 400 /**
785     * Loads a {@linkplain Style SLD-Style} from a {@code .sld} file and
786     * {@linkplain RasterLegendData Raster-LegendData} from a {@code .rld} file
787     * for a given geo-object (raster) source. The SLD file must be present. A
788     * missing raster legend-data file is tolerated.
789     *
790     * @param geoObjectURL
791     * URL of the (already read) raster object
792     * @param sldExt
793     * file extention for the SLD file
794     * @param rldExt
795     * file extention for the raster legend-data file
796     * @return {@code null} in case of any error
797     */
798     public static StyledLayerStyle<RasterLegendData> loadStyledRasterStyle(
799 alfonx 516 final URL geoObjectURL) {
800 alfonx 400 return loadStyledRasterStyle(geoObjectURL, "sld", "rld");
801     }
802 alfonx 244
803 alfonx 400 /**
804 alfonx 769 * Loads a {@linkplain Style SLD-Style} and a
805     * {@linkplain AttributeMetadataImpl AttributeMetaData-Map} for a given
806     * geo-object (feature) source. The SLD file must be present. A missing
807     * attribute meta-data file is tolerated.
808 alfonx 400 *
809     * @param geoObjectURL
810     * URL of the (already read) feature object
811     * @param sldExt
812     * file extention for the SLD file
813     * @param rldExt
814     * file extention for the raster legend-data file
815     * @return {@code null} in case of any error
816     */
817 alfonx 464 public static StyledLayerStyle<AttributeMetadataMap> loadStyledFeatureStyle(
818 alfonx 516 final URL geoObjectURL, final String sldExt, final String rldExt) {
819 alfonx 464 AttributeMetadataMap metaData = null;
820 alfonx 400 Style sldStyle = null;
821     try {
822 alfonx 516 final Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(
823 alfonx 400 geoObjectURL, sldExt));
824     // SLD must be present
825     if (styles == null || styles.length == 0)
826     return null;
827     sldStyle = styles[0];
828 alfonx 516 } catch (final Exception err) {
829 alfonx 400 // SLD must be present
830     LangUtil.logDebugError(LOGGER, err);
831     return null;
832     }
833 alfonx 244
834 alfonx 400 try {
835     metaData = StyledLayerUtil.loadAttributeMetaDataMap(IOUtil
836     .changeUrlExt(geoObjectURL, rldExt));
837 alfonx 516 } catch (final FileNotFoundException err) {
838 alfonx 400 // ignore missing attribute meta data
839 alfonx 516 } catch (final Exception err) {
840 alfonx 400 // any other error during meta data creation leads to error
841     LangUtil.logDebugError(LOGGER, err);
842     return null;
843     }
844 alfonx 244
845 alfonx 516 return new StyledLayerStyle<AttributeMetadataMap>(sldStyle, metaData);
846 alfonx 400 }
847 alfonx 244
848 alfonx 400 /**
849     * Loads a {@linkplain Style SLD-Style} from a {@code .sld} file and
850 alfonx 1215 * {@linkplain AttributeMetadataImpl AttributeMetaData-Map} from a
851     * {@code .amd} file for a given geo-object (feature) source. The SLD file
852     * must be present. A missing attribute meta-data file is tolerated.
853 alfonx 400 *
854     * @param geoObjectURL
855     * URL of the (already read) feature object
856     * @param sldExt
857     * file extention for the SLD file
858     * @param rldExt
859     * file extention for the raster legend-data file
860     * @return {@code null} in case of any error
861     */
862 alfonx 464 public static StyledLayerStyle<AttributeMetadataMap> loadStyledFeatureStyle(
863 alfonx 516 final URL geoObjectURL) {
864 alfonx 400 return loadStyledFeatureStyle(geoObjectURL, "sld", "amd");
865     }
866 alfonx 244
867 alfonx 400 /**
868     * Stores a {@linkplain Style SLD-Style} and {@linkplain RasterLegendData
869     * Raster-LegendData} for a given geo-object (raster) source.
870     *
871     * @param style
872     * style to save
873     * @param geoObjectURL
874     * URL of the raster object
875     * @param sldExt
876     * file extention for the SLD file
877     * @param mdExt
878     * file extention for the meta-data file
879     */
880 alfonx 516 public static <T> void saveStyledLayerStyle(
881     final StyledLayerStyle<T> style, final URL geoObjectURL,
882     final String sldExt, final String mdExt) throws Exception {
883 alfonx 400 // Store the SLD
884 alfonx 516 final Style sldStyle = style.getGeoObjectStyle();
885 alfonx 400 if (sldStyle != null) {
886 alfonx 1212 StylingUtil.saveStyleToSld(sldStyle, IOUtil.changeFileExt(new File(
887 alfonx 400 geoObjectURL.toURI()), sldExt));
888     }
889 alfonx 244
890 alfonx 400 // Store the meta data
891 alfonx 516 final T metaData = style.getMetaData();
892 alfonx 400 if (metaData != null) {
893     if (metaData instanceof RasterLegendData) {
894 alfonx 1215 saveRasterLegendData((RasterLegendData) metaData,
895     IOUtil.changeUrlExt(geoObjectURL, mdExt));
896 alfonx 400 // } else if ( metaData instanceof
897     // Map<Integer,AttributeMetaData> ) { // LEIDER NICHT
898     // KOMPILIERBAR!!
899     } else if (metaData instanceof Map) {
900 alfonx 516 saveAttributeMetaDataMap((AttributeMetadataMap) metaData,
901     IOUtil.changeUrlExt(geoObjectURL, mdExt));
902 alfonx 400 } else
903     throw new UnsupportedOperationException(
904     "Export for meta data not yet supported: "
905     + metaData.getClass().getSimpleName());
906     }
907     }
908 alfonx 244
909 alfonx 400 /**
910     * Stores the {@linkplain Style SLD-Style} to a {@code .sld} file and the
911 alfonx 769 * meta data ({@link RasterLegendData} or {@link AttributeMetadataImpl}) to
912     * a {@code .rld} or {@code .amd} file. for a given geo-object source.
913 alfonx 400 *
914     * @param style
915     * style to save
916     * @param geoObjectURL
917     * URL of the (already read) raster object
918     */
919 alfonx 516 public static void saveStyledLayerStyle(final StyledLayerStyle<?> style,
920     final URL geoObjectURL) throws Exception {
921 alfonx 400 if (style.getMetaData() instanceof RasterLegendData)
922     saveStyledLayerStyle(style, geoObjectURL, "sld", "rld");
923     else
924     saveStyledLayerStyle(style, geoObjectURL, "sld", "amd");
925     }
926 alfonx 244
927 alfonx 397 /**
928 alfonx 694 * *If appended to the name of a rule, this rule shall not be shown in the
929     * legend
930     */
931     public final static String HIDE_IN_LAYER_LEGEND_HINT = "HIDE_IN_LEGEND";
932    
933     /**
934 alfonx 516 * Creates a {@link JPanel} that shows a legend for a list of
935 alfonx 397 * {@link FeatureTypeStyle}s and a targeted featureType
936     *
937 alfonx 824 * @param style
938     * The Style to presented in this legend
939 alfonx 397 * @param featureType
940     * If this a legend for Point, Polygon or Line?
941     *
942 alfonx 1215 * @author <a href="mailto:[email protected]">Stefan Alfons Tzeggai</a>
943 alfonx 397 */
944 alfonx 824 public static JPanel createLegendSwingPanel(Style style,
945 alfonx 516 final SimpleFeatureType featureType, final int iconWidth,
946     final int iconHeight) {
947 alfonx 397
948 alfonx 1225 if (featureType == null) {
949     ExceptionDialog.show(new IllegalStateException(
950     "featureType is null!"));
951     return new JPanel();
952     }
953    
954 alfonx 516 final List<FeatureTypeStyle> list = style.featureTypeStyles();
955 alfonx 397
956 alfonx 517 final JPanel panel = new JPanel(new MigLayout("wrap 2", "[]:3:[]"));
957 alfonx 397
958 alfonx 516 if (style == null) {
959     // No Style => no legend
960     return panel;
961     }
962 alfonx 397
963 alfonx 516 for (final FeatureTypeStyle ftStyle : list) {
964    
965 alfonx 397 // One child-node for every rule
966 alfonx 516 final List<Rule> rules = ftStyle.rules();
967     for (final Rule rule : rules) {
968 alfonx 397
969 alfonx 694 // Check if this RULE shall actually appear in the legend
970 alfonx 769 if (rule.getName() != null
971     && rule.getName().endsWith(HIDE_IN_LAYER_LEGEND_HINT))
972 alfonx 694 continue;
973    
974 alfonx 397 /**
975     * Let's not create a hbox for Rules that only contain
976     * TextSymbolizers
977     */
978     if (StylingUtil.getTextSymbolizers(rule.getSymbolizers())
979     .size() == rule.getSymbolizers().length)
980     continue;
981    
982     final BufferedImage imageForRule = LegendIconFeatureRenderer
983     .getInstance().createImageForRule(rule, featureType,
984 alfonx 516 new Dimension(iconWidth, iconHeight));
985 alfonx 397
986 alfonx 516 final ImageIcon legendIcon = new ImageIcon(imageForRule);
987 alfonx 397
988     final JLabel iconLabel = new JLabel(legendIcon);
989 alfonx 517 panel.add(iconLabel, "sgx1");
990     // hbox.setAlignmentX(0f);
991     // hbox.add(iconLabel);
992     // hbox.add(Box.createHorizontalStrut(3));
993 alfonx 397
994 alfonx 516 final Translation labelT = new Translation();
995 alfonx 409 labelT.fromOneLine(rule.getDescription().getTitle());
996 alfonx 397 final JLabel classTitleLabel = new JLabel(labelT.toString());
997 alfonx 517
998     panel.add(classTitleLabel, "sgx2");
999 alfonx 400 classTitleLabel.setLabelFor(iconLabel);
1000 alfonx 397 }
1001     }
1002    
1003 alfonx 516 return panel;
1004 alfonx 397 }
1005    
1006     /**
1007 alfonx 516 * Creates a {@link JComponent} that contains a legend for a given
1008 alfonx 824 * {@link StyledRasterInterface} and a given {@link Style}.
1009 alfonx 400 *
1010 alfonx 516 * @param style
1011     * if <code>null</code>, the default {@link Style} is extracetd
1012     * from the {@link StyledRasterInterface}
1013 alfonx 397 */
1014 alfonx 824 public static JPanel createLegendSwingPanel(
1015 alfonx 516 final StyledRasterInterface<?> styledRaster, Style style,
1016     final int iconWidth, final int iconHeight) {
1017 alfonx 403
1018 alfonx 516 // If no style is given, we use the default style for this layer
1019     if (style == null)
1020     style = styledRaster.getStyle();
1021    
1022 alfonx 403 /**
1023     * Determine whether a Style is responsible for the coloring
1024     */
1025     ColorModel colorModel = null;
1026 alfonx 405 if (!isStyleable(styledRaster)
1027 alfonx 516 || (isStyleable(styledRaster) && style == null)) {
1028 alfonx 405 colorModel = getColorModel(styledRaster);
1029 alfonx 403 }
1030    
1031 alfonx 516 final RasterLegendData rasterLegendData = styledRaster
1032     .getLegendMetaData();
1033     final List<Double> legendRasterValues = rasterLegendData
1034     .getSortedKeys();
1035     final Map<Double, GridCoverage2D> sampleRasters = rasterLegendData
1036 alfonx 400 .createSampleRasters();
1037    
1038 alfonx 607 final JPanel panel = new JPanel(new MigLayout("wrap 2, gapy 0"));
1039 alfonx 400
1040 alfonx 516 for (final Double rValue : legendRasterValues) {
1041 alfonx 400
1042 alfonx 516 // final Dimension ICON_SIZE = new Dimension(iconWidth,
1043     // new JLabel().getFontMetrics(new JLabel().getFont())
1044     // .getHeight() > 5 ? new JLabel().getFontMetrics(
1045     // new JLabel().getFont()).getHeight() : iconHeight);
1046 alfonx 400
1047     // ****************************************************************************
1048     // Create the actual icon
1049     // ****************************************************************************
1050 alfonx 516 final BufferedImage buffImage = new BufferedImage(iconWidth,
1051     iconHeight, BufferedImage.TYPE_INT_ARGB);
1052 alfonx 517
1053 alfonx 516 final Graphics2D graphics = buffImage.createGraphics();
1054 alfonx 400
1055 alfonx 405 if (colorModel != null) {
1056 alfonx 516 // The colors come from the ColorModel!
1057 alfonx 517
1058 alfonx 403 try {
1059 alfonx 405 Object inData = null;
1060     switch (colorModel.getTransferType()) {
1061     case DataBuffer.TYPE_BYTE:
1062     inData = new byte[] { rValue.byteValue() };
1063     break;
1064     case DataBuffer.TYPE_USHORT:
1065     inData = new short[] { rValue.shortValue() };
1066     break;
1067     case DataBuffer.TYPE_INT:
1068     inData = new int[] { rValue.intValue() };
1069     break;
1070     case DataBuffer.TYPE_SHORT:
1071     inData = new short[] { rValue.shortValue() };
1072     break;
1073     case DataBuffer.TYPE_FLOAT:
1074     inData = new float[] { rValue.floatValue() };
1075     break;
1076     case DataBuffer.TYPE_DOUBLE:
1077     inData = new double[] { rValue.doubleValue() };
1078     break;
1079     default:
1080     inData = rValue.intValue();
1081     }
1082     final Color color = new Color(colorModel.getRGB(inData));
1083 alfonx 403 graphics.setBackground(color);
1084     graphics.setColor(color);
1085 alfonx 516 graphics.fillRect(0, 0, iconWidth, iconHeight);
1086     } catch (final Exception e) {
1087     LOGGER.info(
1088 alfonx 420 "Dann nehmen wir halt den GridCoverageRenderer", e);
1089 alfonx 405 colorModel = null;
1090     }
1091 alfonx 516 } else {
1092     // The colors come from the Style
1093 alfonx 400
1094 alfonx 405 /**
1095     * The coverage contains only one value of value rValue
1096     */
1097 alfonx 516 final GridCoverage2D sampleCov = sampleRasters.get(rValue);
1098 alfonx 405 GridCoverageRenderer renderer;
1099     try {
1100 alfonx 1215 renderer = new GridCoverageRenderer(
1101     sampleCov.getCoordinateReferenceSystem(),
1102     JTSUtil.createEnvelope(sampleCov.getEnvelope()),
1103 alfonx 405 new Rectangle(iconWidth, iconHeight),
1104     (AffineTransform) null);
1105 alfonx 516 } catch (final Exception e1) {
1106 alfonx 405 throw new RuntimeException(
1107 alfonx 516 "Creating a GridCoverageRenderer failed:", e1);
1108 alfonx 405 }
1109 alfonx 403
1110 alfonx 405 /**
1111     * Iterate over all FeatureTypeStyles.
1112     */
1113 alfonx 516 final List<RasterSymbolizer> rSymbols = StylingUtil
1114 alfonx 405 .getRasterSymbolizers(style);
1115    
1116 alfonx 516 for (final RasterSymbolizer symbolizer : rSymbols) {
1117 alfonx 405 try {
1118     renderer.paint(graphics, sampleCov, symbolizer);
1119 alfonx 516 } catch (final Exception ee) {
1120 alfonx 405 LOGGER.error("Unable to paint " + symbolizer
1121     + " into the legend image", ee);
1122 alfonx 403 }
1123 alfonx 400 }
1124 alfonx 405 }
1125 alfonx 400
1126 alfonx 516 final JLabel iconLabel = new JLabel(new ImageIcon(buffImage));
1127 alfonx 517 panel.add(iconLabel, "sgx1");
1128 alfonx 400
1129 alfonx 516 final Translation labelT = rasterLegendData.get(rValue);
1130 alfonx 400 final JLabel classTitleLabel = new JLabel(labelT.toString());
1131 alfonx 517 panel.add(classTitleLabel, "sgx2"
1132 alfonx 607 + (rasterLegendData.isPaintGaps() ? ", gapy 0:0:0 5:5:5"
1133     : ""));
1134 alfonx 400 classTitleLabel.setLabelFor(iconLabel);
1135    
1136 alfonx 607 if (rasterLegendData.isPaintGaps()) {
1137 alfonx 403 iconLabel
1138     .setBorder(BorderFactory.createLineBorder(Color.black));
1139     }
1140    
1141 alfonx 400 }
1142    
1143 alfonx 516 return panel;
1144 alfonx 397 }
1145    
1146 alfonx 405 /**
1147     * Extracts the {@link ColorModel} of any {@link StyledRasterInterface}. May
1148     * return <code>null</code> if the geoobject can not be accessed.
1149     */
1150 alfonx 420 @SuppressWarnings("unchecked")
1151 alfonx 516 public static ColorModel getColorModel(
1152     final StyledRasterInterface<?> styledGrid) {
1153 alfonx 405 ColorModel colorModel = null;
1154     try {
1155 alfonx 516 final Object geoObject = styledGrid.getGeoObject();
1156 alfonx 405 if (geoObject instanceof GridCoverage2D) {
1157 alfonx 516 final GridCoverage2D cov = (GridCoverage2D) geoObject;
1158 alfonx 405 colorModel = cov.getRenderedImage().getColorModel();
1159 alfonx 1203 } else if (styledGrid instanceof StyledGridCoverageReaderInterface) {
1160 alfonx 405
1161 alfonx 516 final Parameter readGG = new Parameter(
1162 alfonx 405 AbstractGridFormat.READ_GRIDGEOMETRY2D);
1163    
1164 alfonx 1212 final ReferencedEnvelope mapExtend = new ReferencedEnvelope(
1165 alfonx 405 styledGrid.getEnvelope(), styledGrid.getCrs());
1166    
1167     readGG.setValue(new GridGeometry2D(new GeneralGridEnvelope(
1168 alfonx 420 new Rectangle(0, 0, 1, 1)), mapExtend));
1169 alfonx 405
1170 alfonx 1215 AbstractGridCoverage2DReader aReader;
1171     if (geoObject instanceof FeatureCollection) {
1172     final FeatureCollection<SimpleFeatureType, SimpleFeature> rFc = (FeatureCollection<SimpleFeatureType, SimpleFeature>) geoObject;
1173 alfonx 420
1174 alfonx 1215 aReader = (AbstractGridCoverage2DReader) FeatureUtil
1175     .getWrappedGeoObject(rFc);
1176    
1177     } else if (geoObject instanceof AbstractGridCoverage2DReader) {
1178     aReader = (AbstractGridCoverage2DReader) geoObject;
1179    
1180     } else
1181     throw new RuntimeException("need a reader...");
1182     //
1183    
1184 alfonx 516 final GridCoverage2D cov = (GridCoverage2D) aReader
1185 alfonx 405 .read(new GeneralParameterValue[] { readGG });
1186     colorModel = cov.getRenderedImage().getColorModel();
1187     }
1188 alfonx 516 } catch (final Exception e) {
1189 alfonx 420 LOGGER.error("Error reading the colormodel from " + styledGrid, e);
1190 alfonx 405 return null;
1191     }
1192     return colorModel;
1193     }
1194    
1195     /**
1196     * @return <code>true</code> if a {@link RasterSymbolizer} can be applied
1197     * and will have an effect. Some rasters (e.g. GeoTIFF) can come
1198     * with their own {@link ColorModel} and will ignore any
1199     * {@link RasterSymbolizer} = SLD.
1200     */
1201 alfonx 516 public static boolean isStyleable(
1202     final StyledRasterInterface<?> styledRaster) {
1203     final ColorModel colorModel = getColorModel(styledRaster);
1204 alfonx 405
1205 alfonx 607 // LOGGER.info("The colormodel of " + styledRaster.getTitle() + " is "
1206     // + colorModel != null ? colorModel.getClass().getSimpleName() :
1207     // "NULL");
1208    
1209 alfonx 405 if (colorModel == null)
1210     return true;
1211     if (colorModel instanceof ComponentColorModel)
1212     return true;
1213 alfonx 1215 if (colorModel instanceof IndexColorModel)
1214     return true;
1215    
1216 alfonx 405 return false;
1217     }
1218 alfonx 517
1219     /**
1220     * Set the given Style as the Style of the {@link MapLayer}, unless the
1221     * styles are the same (not comparing selection stuff). If the
1222     * {@link MapLayer}s {@link Style} is changed, the selection FTS is kept.<br/>
1223     * Remember {@link MapLayer#setStyle(Style)} triggers an event leading to a
1224     * repaint, so only use it when needed.
1225     *
1226 alfonx 607 * @return <code>true</code> if the {@link MapLayer}'s {@link Style} has
1227     * been changed.
1228 alfonx 517 */
1229 alfonx 607 public static boolean updateMapLayerStyleIfChangedAndKeepSelection(
1230     MapLayer mapLayer, Style style2) {
1231 alfonx 517
1232     Style mapLayerStyleCleaned = StylingUtil
1233 alfonx 607 .removeSelectionFeatureTypeStyle(mapLayer.getStyle());
1234    
1235     Style newStyleCleaned = StylingUtil
1236     .removeSelectionFeatureTypeStyle(style2);
1237    
1238     if (StylingUtil.isStyleDifferent(mapLayerStyleCleaned, newStyleCleaned)) {
1239    
1240 alfonx 517 // They are different when compared without SELECTION FTS!
1241 alfonx 607
1242 alfonx 517 // Now let's copy any SELECTION FTS to the now style
1243 alfonx 607 FeatureTypeStyle selectionFeatureTypeStyle = StylingUtil
1244     .getSelectionFeatureTypeStyle(mapLayer.getStyle());
1245 alfonx 517 if (selectionFeatureTypeStyle != null) {
1246 alfonx 607 newStyleCleaned.featureTypeStyles().add(
1247     selectionFeatureTypeStyle);
1248     // newStyleCleaned is not so clean anymore... We just alled a
1249     // selcetion FTS
1250     }
1251    
1252 alfonx 517 mapLayer.setStyle(newStyleCleaned);
1253 alfonx 607
1254 alfonx 517 return true;
1255 alfonx 607
1256 alfonx 526 } else {
1257     return false;
1258     }
1259 alfonx 517 }
1260    
1261 alfonx 607 /**
1262     * After loading an atlas, the AttribteMetaData contains whatever is written
1263     * in the XML. But the DBF may have changed! This method checks an
1264     * {@link AttributeMetadataMap} against a schema and also corrects
1265     * upperCase/lowerCase problems. It will also remove any geometry column
1266     * attribute metadata.
1267     */
1268     /**
1269     * After loading an atlas, the AttribteMetaData contains whatever is written
1270     * in the XML. But the DBF may have changed!
1271     */
1272 alfonx 677 public static void checkAttribMetaData(
1273 alfonx 769 AttributeMetadataMap<AttributeMetadataImpl> attributeMetaDataMap,
1274     SimpleFeatureType schema) {
1275 alfonx 607
1276 alfonx 769 if (schema == null)
1277     throw new IllegalArgumentException("schmema may not be null!");
1278    
1279 alfonx 607 ArrayList<Name> willRemove = new ArrayList<Name>();
1280    
1281     // 1. Check.. all attributes in the atm should be in the schema as well.
1282     // maybe correct some upperCase/loweCase stuff
1283    
1284 alfonx 772 for (AttributeMetadataInterface atm : attributeMetaDataMap.values()) {
1285 alfonx 607
1286     AttributeDescriptor foundDescr = schema
1287     .getDescriptor(atm.getName());
1288     if (foundDescr == null) {
1289 alfonx 1215 NameImpl bestMatch = FeatureUtil.findBestMatchingAttribute(
1290     schema, atm.getLocalName());
1291 alfonx 607 if (bestMatch == null)
1292     willRemove.add(atm.getName());
1293     else
1294     atm.setName(bestMatch);
1295     } else if (foundDescr instanceof GeometryDescriptor) {
1296     // We don't want GeometryColumns in here
1297     willRemove.add(atm.getName());
1298     }
1299     }
1300    
1301     // Remove the ones that were not findable in the schema
1302     for (Name removeName : willRemove) {
1303 alfonx 677 if (attributeMetaDataMap.remove(removeName) == null) {
1304 alfonx 607 LOGGER.warn("removing the AMData didn't work");
1305     }
1306     }
1307    
1308     // 2. check... all attributes from the schema must have an ATM
1309     for (AttributeDescriptor ad : schema.getAttributeDescriptors()) {
1310     if (ad instanceof GeometryDescriptor)
1311     continue;
1312     if (!attributeMetaDataMap.containsKey(ad.getName())) {
1313 alfonx 1215 attributeMetaDataMap.put(new NameImpl(ad.getName()
1314     .getNamespaceURI(), ad.getName().getLocalPart()),
1315 alfonx 769 new AttributeMetadataImpl(ad, schema
1316     .getAttributeDescriptors().indexOf(ad),
1317     attributeMetaDataMap.getLanguages()));
1318 alfonx 607 }
1319     }
1320     }
1321    
1322 alfonx 677 /**
1323     * Checks every attribute name in the {@link AttributeMetadataMap} for its
1324     * binding type. It the type is textual, add the mrpty string as a NODATA
1325     * value.
1326     *
1327     * @param attributeMetaDataMap
1328     * @param schema
1329     */
1330     public static void addEmptyStringToAllTextualAttributes(
1331 alfonx 772 AttributeMetadataMap<? extends AttributeMetadataInterface> attributeMetaDataMap,
1332 alfonx 769 SimpleFeatureType schema) {
1333 alfonx 677
1334     for (Name name : attributeMetaDataMap.keySet()) {
1335     if (String.class.isAssignableFrom(schema.getDescriptor(name)
1336     .getType().getBinding())) {
1337     attributeMetaDataMap.get(name).getNodataValues().add("");
1338     }
1339     }
1340     }
1341 alfonx 769
1342     /**
1343     * @return a nicely formatted String containing all NODATA values of any
1344 alfonx 1215 * {@link AttributeMetadataInterface} object. Strings are quoted so
1345     * that any empty {@link String} can be seen.
1346 alfonx 769 */
1347     public static String formatNoDataValues(Set<Object> nodataValuesList) {
1348     String nicelyFormatted = "";
1349     if (nodataValuesList != null) {
1350     if (nodataValuesList.size() == 0)
1351     nicelyFormatted = "";
1352     else {
1353     for (Object ndo : nodataValuesList) {
1354     if (ndo instanceof String)
1355     nicelyFormatted += "\"" + ndo + "\"";
1356     else
1357     nicelyFormatted += ndo.toString();
1358    
1359     nicelyFormatted += ",";
1360     }
1361     // Remove the extra comma
1362 alfonx 1215 nicelyFormatted = nicelyFormatted.substring(0,
1363     nicelyFormatted.length() - 1);
1364 alfonx 769 }
1365     }
1366     return nicelyFormatted;
1367     }
1368 alfonx 1228
1369     /**
1370     * Creates a new {@link AttributeMetadataMap} with instances of
1371     * {@link AttributeMetadataInterface} for every non-geometry attribute.
1372     * Default NODATA values (like "" for String) are set.
1373     */
1374     public static AttributeMetadataMap<AttributeMetadataImpl> createDefaultAttributeMetadataMap(
1375     SimpleFeatureType schema) {
1376     AttributeMetadataImplMap attMap = new AttributeMetadataImplMap();
1377    
1378     for (int i = 0; i < schema.getAttributeCount(); i++) {
1379     AttributeDescriptor attDesc = schema.getDescriptor(i);
1380    
1381     if (Geometry.class.isAssignableFrom(attDesc.getType().getBinding())) {
1382     // Ignore the Geometry column
1383     continue;
1384     }
1385    
1386     // TODO AttributeMetadataAS would be nicer, which would not work
1387     // with Translations ;-)
1388     AttributeMetadataImpl attMetaData = new AttributeMetadataImpl(
1389     new NameImpl(attDesc.getName().getNamespaceURI(), attDesc
1390     .getName().getLocalPart()), attMap.getLanguages());
1391    
1392     if (String.class.isAssignableFrom(attDesc.getType().getBinding())) {
1393     // For Strings we add the "" as NODATA values
1394     attMetaData.addNodataValue("");
1395     }
1396    
1397     attMap.put(attDesc.getName(), attMetaData);
1398     }
1399     return attMap;
1400     }
1401 alfonx 244 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26