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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26