/[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 1228 - (show annotations)
Wed Nov 3 20:44:16 2010 UTC (14 years, 3 months ago) by alfonx
File size: 48380 byte(s)
c
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.awt.image.IndexColorModel;
42 import java.io.File;
43 import java.io.FileNotFoundException;
44 import java.io.FileWriter;
45 import java.net.URL;
46 import java.text.DecimalFormat;
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Set;
51
52 import javax.swing.BorderFactory;
53 import javax.swing.ImageIcon;
54 import javax.swing.JComponent;
55 import javax.swing.JLabel;
56
57 import net.miginfocom.swing.MigLayout;
58
59 import org.apache.log4j.Logger;
60 import org.geotools.coverage.grid.GeneralGridEnvelope;
61 import org.geotools.coverage.grid.GridCoverage2D;
62 import org.geotools.coverage.grid.GridGeometry2D;
63 import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
64 import org.geotools.coverage.grid.io.AbstractGridFormat;
65 import org.geotools.feature.FeatureCollection;
66 import org.geotools.feature.NameImpl;
67 import org.geotools.geometry.jts.ReferencedEnvelope;
68 import org.geotools.map.DefaultMapLayer;
69 import org.geotools.map.MapLayer;
70 import org.geotools.parameter.Parameter;
71 import org.geotools.renderer.lite.gridcoverage2d.GridCoverageRenderer;
72 import org.geotools.styling.ColorMap;
73 import org.geotools.styling.ColorMapEntry;
74 import org.geotools.styling.FeatureTypeStyle;
75 import org.geotools.styling.RasterSymbolizer;
76 import org.geotools.styling.Rule;
77 import org.geotools.styling.Style;
78 import org.jdom.Document;
79 import org.jdom.Element;
80 import org.jdom.input.SAXBuilder;
81 import org.jdom.output.XMLOutputter;
82 import org.opengis.feature.simple.SimpleFeature;
83 import org.opengis.feature.simple.SimpleFeatureType;
84 import org.opengis.feature.type.AttributeDescriptor;
85 import org.opengis.feature.type.GeometryDescriptor;
86 import org.opengis.feature.type.Name;
87 import org.opengis.parameter.GeneralParameterValue;
88
89 import schmitzm.geotools.JTSUtil;
90 import schmitzm.geotools.feature.FeatureUtil;
91 import schmitzm.geotools.styling.StylingUtil;
92 import schmitzm.io.IOUtil;
93 import schmitzm.lang.LangUtil;
94 import schmitzm.swing.ExceptionDialog;
95 import schmitzm.swing.JPanel;
96 import schmitzm.swing.SwingUtil;
97 import skrueger.AttributeMetadataImpl;
98 import skrueger.AttributeMetadataInterface;
99 import skrueger.RasterLegendData;
100 import skrueger.i8n.Translation;
101
102 import com.vividsolutions.jts.geom.Geometry;
103
104 /**
105 * This class provides static helper methods for dealing with
106 * {@link StyledLayerInterface} stuff.
107 *
108 * @author <a href="mailto:[email protected]">Martin Schmitz</a>
109 * (University of Bonn/Germany)
110 * @version 1.0
111 */
112 public class StyledLayerUtil {
113 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
118 /** 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
131 /**
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 public static MapLayer createMapLayer(final Object object) throws Exception {
145 return createMapLayer(object, null);
146 }
147
148 /**
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 public static MapLayer createMapLayer(Object object, final Style forcedStyle)
164 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
176 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
184 if (layer == null)
185 throw new Exception("Can not create MapLayer from "
186 + (object == null ? "null" : object.getClass()));
187
188 return layer;
189 }
190
191 /**
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 public static StyledLayerInterface<?> createStyledLayer(
205 final Object object, final String title) {
206 return createStyledLayer(object, title, null);
207 }
208
209 /**
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 public static StyledLayerInterface<?> createStyledLayer(
225 final Object object, final String title,
226 final StyledLayerStyle style) {
227 StyledLayerInterface<?> styledLayer = null;
228
229 final String id = (title != null) ? title : "defaultID";
230
231 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
241 if (styledLayer == null)
242 throw new UnsupportedOperationException(
243 "Can not create StyledLayerInterface object from "
244 + (object == null ? "null" : object.getClass()));
245
246 return styledLayer;
247 }
248
249 /**
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 *
258 * TODO replace with
259 * {@link AttributeMetadataMap#sortedValuesVisibleOnly()}
260 */
261 public static AttributeMetadataMap<? extends AttributeMetadataInterface> getVisibleAttributeMetaData(
262 final AttributeMetadataMap<? extends AttributeMetadataInterface> amdMap,
263 final boolean visible) {
264
265 final AttributeMetadataMap<AttributeMetadataInterface> filteredMap = (AttributeMetadataMap<AttributeMetadataInterface>) amdMap
266 .clone();
267 if (filteredMap.size() > 0) {
268 filteredMap.clear(); // Just in case the close copies the contents
269 }
270
271 for (final AttributeMetadataInterface amd : amdMap.values())
272 if (amd.isVisible() == visible)
273 filteredMap.put(amd.getName(), amd);
274
275 return filteredMap;
276 }
277
278 /**
279 * Parses a {@link AttributeMetadataImpl} object from an JDOM-
280 * {@link Element}. This method works like {@link
281 * AMLImport#parseDataAttribute(org.w3c.dom.Node}, but for JDOM.
282 *
283 * 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 * @param element
289 * {@link Element} to parse
290 */
291 public static AttributeMetadataImpl parseAttributeMetaData(
292 final Element element) {
293 final String namespace = element.getAttributeValue("namespace");
294 final String localname = element.getAttributeValue("localname");
295 final NameImpl aName = new NameImpl(namespace, localname);
296 final Boolean visible = Boolean.valueOf(element
297 .getAttributeValue("visible"));
298 final String unit = element.getAttributeValue("unit");
299
300 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
306 if (childElement.getName().equals("name"))
307 name = parseTranslation(childElement);
308 else if (childElement.getName().equals("desc"))
309 desc = parseTranslation(childElement);
310 }
311 return new AttributeMetadataImpl(aName, visible, name, desc, unit);
312 }
313
314 /**
315 * Parses a {@link AttributeMetadataImpl} map from an JDOM-{@link Element}
316 * with {@code <attribute>}-childs.
317 *
318 * @param element
319 * {@link Element} to parse
320 *
321 * TODO Since GP 1.3 the {@link AttributeMetadataImpl} class has
322 * more attributes which are not used by Xulu/ISDSS. GP
323 * exports/imports the AMD via AMLExporter and AMLImporter
324 * classes. (SK, 3.2.2010) *
325 */
326 public static AttributeMetadataMap parseAttributeMetaDataMap(
327 final Element element) {
328 final AttributeMetadataMap metaData = new AttributeMetadataImplMap();
329 final List<Element> attributesElements = element
330 .getChildren(ELEM_NAME_ATTRIBUTE);
331 for (final Element attibuteElement : attributesElements) {
332 final AttributeMetadataImpl attrMetaData = parseAttributeMetaData(attibuteElement);
333 metaData.put(attrMetaData.getName(), attrMetaData);
334 }
335 return metaData;
336 }
337
338 /**
339 * Loads a {@link AttributeMetadataImpl} object from an URL.
340 *
341 * @param documentUrl
342 * {@link URL} to parse
343 * @see #parseAttributeMetaData(Element)
344 */
345 public static AttributeMetadataMap loadAttributeMetaDataMap(
346 final URL documentUrl) throws Exception {
347 final Document document = SAX_BUILDER.build(documentUrl);
348 return parseAttributeMetaDataMap(document.getRootElement());
349 }
350
351 /**
352 * Creates an JDOM {@link Element} for the given
353 * {@link AttributeMetadataImpl} object.
354 *
355 * @param amd
356 * meta data for one attribute
357 *
358 * TODO Since GP 1.3 the {@link AttributeMetadataImpl} class has
359 * more attributes which are not used by Xulu/ISDSS. GP
360 * exports/imports the AMD via AMLExporter and AMLImporter
361 * classes. (SK, 3.2.2010)
362 */
363 public static Element createAttributeMetaDataElement(
364 final AttributeMetadataInterface amd) {
365 final Element element = new Element(ELEM_NAME_ATTRIBUTE, AMLURI);
366 element.setAttribute("namespace",
367 String.valueOf(amd.getName().getNamespaceURI()));
368 element.setAttribute("localname", String.valueOf(amd.getLocalName()));
369 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
378 /**
379 * Creates an JDOM {@link Element} for the given
380 * {@link AttributeMetadataImpl} map.
381 *
382 * @param amdMap
383 * map of attribute meta data
384 */
385 public static Element createAttributeMetaDataMapElement(
386 final AttributeMetadataMap<? extends AttributeMetadataInterface> amdMap) {
387 final Element element = new Element(ELEM_NAME_AMD, AMLURI);
388 for (final AttributeMetadataInterface amd : amdMap.values())
389 element.addContent(createAttributeMetaDataElement(amd));
390 return element;
391 }
392
393 /**
394 * Saves a {@link AttributeMetadataImpl AttributeMetaData-Map} to an URL.
395 *
396 * @param amdMap
397 * map of {@link AttributeMetadataImpl}
398 * @param documentUrl
399 * {@link URL} to store the XML
400 */
401 public static void saveAttributeMetaDataMap(
402 final AttributeMetadataMap amdMap, final URL documentUrl)
403 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
411 /**
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 public static RasterLegendData parseRasterLegendData(final Element element) {
420
421 final boolean paintGaps = Boolean.valueOf(element
422 .getAttributeValue("paintGaps"));
423
424 final RasterLegendData rld = new RasterLegendData(paintGaps);
425
426 for (final Element childElement : (List<Element>) element.getChildren()) {
427 final String name = childElement.getName();
428 // Cancel if it's an attribute
429 if (childElement.getChildren().size() == 0)
430 continue;
431
432 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
441 // 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 final Translation label = parseTranslation(labelElement != null ? labelElement
447 : childElement);
448 rld.put(value, label);
449 }
450 }
451
452 return rld;
453 }
454
455 /**
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 final Document document = SAX_BUILDER.build(documentUrl);
465 return parseRasterLegendData(document.getRootElement());
466 }
467
468 /**
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 for (final Double key : rld.getSortedKeys()) {
480 final Element item = new Element(ELEM_NAME_RASTERLEGEND, AMLURI);
481 item.setAttribute("value", key.toString());
482 item.addContent(createTranslationElement("label", rld.get(key)));
483 element.addContent(item);
484 }
485 return element;
486 }
487
488 /**
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 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 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
521 /**
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 public static RasterLegendData generateRasterLegendData(final Style style,
534 final boolean paintGaps, final Integer digits) {
535 final ColorMap colorMap = StylingUtil.getColorMapFromStyle(style);
536 if (colorMap == null)
537 throw new IllegalArgumentException(
538 "Color map can not be determined from style!");
539 return generateRasterLegendData(colorMap, paintGaps, digits);
540 }
541
542 /**
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
559 /**
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 final Translation trans = new Translation();
569
570 if (element == null)
571 return trans;
572
573 for (final Element translationElement : (List<Element>) element
574 .getChildren()) {
575 final String name = translationElement.getName();
576 if (name == null)
577 continue;
578
579 // 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
585 final String translationText = translationElement.getValue();
586 if (translationText == null)
587 trans.put(lang, "");
588 else
589 trans.put(lang, translationText);
590 }
591
592 // 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
599 return trans;
600 }
601
602 /**
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 public final static Element createTranslationElement(final String tagname,
611 final Translation translation) {
612 final Element element = new Element(tagname, AMLURI);
613 if (translation == null)
614 throw new UnsupportedOperationException(
615 "Translation element can not be created from null!");
616
617 // 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
626 // add a <translation lang="..">..</tranlation> part to the element for
627 // all languages
628 for (final String lang : translation.keySet()) {
629 final Element translationElement = new Element(
630 ELEM_NAME_TRANSLATION, AMLURI);
631 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
639 return element;
640 }
641
642 /**
643 * Sets a style to {@link StyledLayerInterface}.
644 *
645 * @param styledObject
646 * a styled object
647 * @param style
648 * a Style
649 */
650 public static void setStyledLayerStyle(
651 final StyledLayerInterface styledObject,
652 final StyledLayerStyle<?> style) {
653 // 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 final RasterLegendData sourceRld = (RasterLegendData) style
660 .getMetaData();
661 final RasterLegendData destRld = ((StyledGridCoverageInterface) styledObject)
662 .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 final AttributeMetadataMap sourceAmd = (AttributeMetadataMap) style
673 .getMetaData();
674 final AttributeMetadataMap destAmd = ((StyledFeatureCollectionInterface) styledObject)
675 .getAttributeMetaDataMap();
676 if (destAmd != null && sourceAmd != null) {
677 destAmd.clear();
678 destAmd.putAll(sourceAmd);
679 }
680 return;
681 }
682
683 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
692 /**
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 * {@link StyledGridCoverageInterface} or
700 * {@code StyledLayerStyle<Map<Integer,AttributeMetaData>>} for
701 * {@link StyledFeatureCollectionInterface}
702 */
703 public static StyledLayerStyle<?> getStyledLayerStyle(
704 final StyledLayerInterface styledObject) {
705 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
715 /**
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 final StyledGridCoverageInterface styledGC) {
724 return new StyledLayerStyle<RasterLegendData>(styledGC.getStyle(),
725 styledGC.getLegendMetaData());
726 }
727
728 /**
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 public static StyledLayerStyle<AttributeMetadataMap> getStyledLayerStyle(
736 final StyledFeatureCollectionInterface styledFC) {
737 return new StyledLayerStyle<AttributeMetadataMap>(styledFC.getStyle(),
738 styledFC.getAttributeMetaDataMap());
739 }
740
741 /**
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 final URL geoObjectURL, final String sldExt, final String rldExt) {
756 RasterLegendData metaData = null;
757 Style sldStyle = null;
758 try {
759 final Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(
760 geoObjectURL, sldExt));
761 // SLD must be present
762 if (styles == null || styles.length == 0)
763 return null;
764 sldStyle = styles[0];
765 } catch (final Exception err) {
766 // SLD must be present
767 LangUtil.logDebugError(LOGGER, err);
768 return null;
769 }
770
771 try {
772 metaData = StyledLayerUtil.loadRasterLegendData(IOUtil
773 .changeUrlExt(geoObjectURL, rldExt));
774 } catch (final FileNotFoundException err) {
775 // ignore missing raster legend data
776 } catch (final Exception err) {
777 // 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
784 /**
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 final URL geoObjectURL) {
800 return loadStyledRasterStyle(geoObjectURL, "sld", "rld");
801 }
802
803 /**
804 * 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 *
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 public static StyledLayerStyle<AttributeMetadataMap> loadStyledFeatureStyle(
818 final URL geoObjectURL, final String sldExt, final String rldExt) {
819 AttributeMetadataMap metaData = null;
820 Style sldStyle = null;
821 try {
822 final Style[] styles = StylingUtil.loadSLD(IOUtil.changeUrlExt(
823 geoObjectURL, sldExt));
824 // SLD must be present
825 if (styles == null || styles.length == 0)
826 return null;
827 sldStyle = styles[0];
828 } catch (final Exception err) {
829 // SLD must be present
830 LangUtil.logDebugError(LOGGER, err);
831 return null;
832 }
833
834 try {
835 metaData = StyledLayerUtil.loadAttributeMetaDataMap(IOUtil
836 .changeUrlExt(geoObjectURL, rldExt));
837 } catch (final FileNotFoundException err) {
838 // ignore missing attribute meta data
839 } catch (final Exception err) {
840 // any other error during meta data creation leads to error
841 LangUtil.logDebugError(LOGGER, err);
842 return null;
843 }
844
845 return new StyledLayerStyle<AttributeMetadataMap>(sldStyle, metaData);
846 }
847
848 /**
849 * Loads a {@linkplain Style SLD-Style} from a {@code .sld} file and
850 * {@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 *
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 public static StyledLayerStyle<AttributeMetadataMap> loadStyledFeatureStyle(
863 final URL geoObjectURL) {
864 return loadStyledFeatureStyle(geoObjectURL, "sld", "amd");
865 }
866
867 /**
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 public static <T> void saveStyledLayerStyle(
881 final StyledLayerStyle<T> style, final URL geoObjectURL,
882 final String sldExt, final String mdExt) throws Exception {
883 // Store the SLD
884 final Style sldStyle = style.getGeoObjectStyle();
885 if (sldStyle != null) {
886 StylingUtil.saveStyleToSld(sldStyle, IOUtil.changeFileExt(new File(
887 geoObjectURL.toURI()), sldExt));
888 }
889
890 // Store the meta data
891 final T metaData = style.getMetaData();
892 if (metaData != null) {
893 if (metaData instanceof RasterLegendData) {
894 saveRasterLegendData((RasterLegendData) metaData,
895 IOUtil.changeUrlExt(geoObjectURL, mdExt));
896 // } else if ( metaData instanceof
897 // Map<Integer,AttributeMetaData> ) { // LEIDER NICHT
898 // KOMPILIERBAR!!
899 } else if (metaData instanceof Map) {
900 saveAttributeMetaDataMap((AttributeMetadataMap) metaData,
901 IOUtil.changeUrlExt(geoObjectURL, mdExt));
902 } else
903 throw new UnsupportedOperationException(
904 "Export for meta data not yet supported: "
905 + metaData.getClass().getSimpleName());
906 }
907 }
908
909 /**
910 * Stores the {@linkplain Style SLD-Style} to a {@code .sld} file and the
911 * meta data ({@link RasterLegendData} or {@link AttributeMetadataImpl}) to
912 * a {@code .rld} or {@code .amd} file. for a given geo-object source.
913 *
914 * @param style
915 * style to save
916 * @param geoObjectURL
917 * URL of the (already read) raster object
918 */
919 public static void saveStyledLayerStyle(final StyledLayerStyle<?> style,
920 final URL geoObjectURL) throws Exception {
921 if (style.getMetaData() instanceof RasterLegendData)
922 saveStyledLayerStyle(style, geoObjectURL, "sld", "rld");
923 else
924 saveStyledLayerStyle(style, geoObjectURL, "sld", "amd");
925 }
926
927 /**
928 * *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 * Creates a {@link JPanel} that shows a legend for a list of
935 * {@link FeatureTypeStyle}s and a targeted featureType
936 *
937 * @param style
938 * The Style to presented in this legend
939 * @param featureType
940 * If this a legend for Point, Polygon or Line?
941 *
942 * @author <a href="mailto:[email protected]">Stefan Alfons Tzeggai</a>
943 */
944 public static JPanel createLegendSwingPanel(Style style,
945 final SimpleFeatureType featureType, final int iconWidth,
946 final int iconHeight) {
947
948 if (featureType == null) {
949 ExceptionDialog.show(new IllegalStateException(
950 "featureType is null!"));
951 return new JPanel();
952 }
953
954 final List<FeatureTypeStyle> list = style.featureTypeStyles();
955
956 final JPanel panel = new JPanel(new MigLayout("wrap 2", "[]:3:[]"));
957
958 if (style == null) {
959 // No Style => no legend
960 return panel;
961 }
962
963 for (final FeatureTypeStyle ftStyle : list) {
964
965 // One child-node for every rule
966 final List<Rule> rules = ftStyle.rules();
967 for (final Rule rule : rules) {
968
969 // Check if this RULE shall actually appear in the legend
970 if (rule.getName() != null
971 && rule.getName().endsWith(HIDE_IN_LAYER_LEGEND_HINT))
972 continue;
973
974 /**
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 new Dimension(iconWidth, iconHeight));
985
986 final ImageIcon legendIcon = new ImageIcon(imageForRule);
987
988 final JLabel iconLabel = new JLabel(legendIcon);
989 panel.add(iconLabel, "sgx1");
990 // hbox.setAlignmentX(0f);
991 // hbox.add(iconLabel);
992 // hbox.add(Box.createHorizontalStrut(3));
993
994 final Translation labelT = new Translation();
995 labelT.fromOneLine(rule.getDescription().getTitle());
996 final JLabel classTitleLabel = new JLabel(labelT.toString());
997
998 panel.add(classTitleLabel, "sgx2");
999 classTitleLabel.setLabelFor(iconLabel);
1000 }
1001 }
1002
1003 return panel;
1004 }
1005
1006 /**
1007 * Creates a {@link JComponent} that contains a legend for a given
1008 * {@link StyledRasterInterface} and a given {@link Style}.
1009 *
1010 * @param style
1011 * if <code>null</code>, the default {@link Style} is extracetd
1012 * from the {@link StyledRasterInterface}
1013 */
1014 public static JPanel createLegendSwingPanel(
1015 final StyledRasterInterface<?> styledRaster, Style style,
1016 final int iconWidth, final int iconHeight) {
1017
1018 // If no style is given, we use the default style for this layer
1019 if (style == null)
1020 style = styledRaster.getStyle();
1021
1022 /**
1023 * Determine whether a Style is responsible for the coloring
1024 */
1025 ColorModel colorModel = null;
1026 if (!isStyleable(styledRaster)
1027 || (isStyleable(styledRaster) && style == null)) {
1028 colorModel = getColorModel(styledRaster);
1029 }
1030
1031 final RasterLegendData rasterLegendData = styledRaster
1032 .getLegendMetaData();
1033 final List<Double> legendRasterValues = rasterLegendData
1034 .getSortedKeys();
1035 final Map<Double, GridCoverage2D> sampleRasters = rasterLegendData
1036 .createSampleRasters();
1037
1038 final JPanel panel = new JPanel(new MigLayout("wrap 2, gapy 0"));
1039
1040 for (final Double rValue : legendRasterValues) {
1041
1042 // 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
1047 // ****************************************************************************
1048 // Create the actual icon
1049 // ****************************************************************************
1050 final BufferedImage buffImage = new BufferedImage(iconWidth,
1051 iconHeight, BufferedImage.TYPE_INT_ARGB);
1052
1053 final Graphics2D graphics = buffImage.createGraphics();
1054
1055 if (colorModel != null) {
1056 // The colors come from the ColorModel!
1057
1058 try {
1059 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 graphics.setBackground(color);
1084 graphics.setColor(color);
1085 graphics.fillRect(0, 0, iconWidth, iconHeight);
1086 } catch (final Exception e) {
1087 LOGGER.info(
1088 "Dann nehmen wir halt den GridCoverageRenderer", e);
1089 colorModel = null;
1090 }
1091 } else {
1092 // The colors come from the Style
1093
1094 /**
1095 * The coverage contains only one value of value rValue
1096 */
1097 final GridCoverage2D sampleCov = sampleRasters.get(rValue);
1098 GridCoverageRenderer renderer;
1099 try {
1100 renderer = new GridCoverageRenderer(
1101 sampleCov.getCoordinateReferenceSystem(),
1102 JTSUtil.createEnvelope(sampleCov.getEnvelope()),
1103 new Rectangle(iconWidth, iconHeight),
1104 (AffineTransform) null);
1105 } catch (final Exception e1) {
1106 throw new RuntimeException(
1107 "Creating a GridCoverageRenderer failed:", e1);
1108 }
1109
1110 /**
1111 * Iterate over all FeatureTypeStyles.
1112 */
1113 final List<RasterSymbolizer> rSymbols = StylingUtil
1114 .getRasterSymbolizers(style);
1115
1116 for (final RasterSymbolizer symbolizer : rSymbols) {
1117 try {
1118 renderer.paint(graphics, sampleCov, symbolizer);
1119 } catch (final Exception ee) {
1120 LOGGER.error("Unable to paint " + symbolizer
1121 + " into the legend image", ee);
1122 }
1123 }
1124 }
1125
1126 final JLabel iconLabel = new JLabel(new ImageIcon(buffImage));
1127 panel.add(iconLabel, "sgx1");
1128
1129 final Translation labelT = rasterLegendData.get(rValue);
1130 final JLabel classTitleLabel = new JLabel(labelT.toString());
1131 panel.add(classTitleLabel, "sgx2"
1132 + (rasterLegendData.isPaintGaps() ? ", gapy 0:0:0 5:5:5"
1133 : ""));
1134 classTitleLabel.setLabelFor(iconLabel);
1135
1136 if (rasterLegendData.isPaintGaps()) {
1137 iconLabel
1138 .setBorder(BorderFactory.createLineBorder(Color.black));
1139 }
1140
1141 }
1142
1143 return panel;
1144 }
1145
1146 /**
1147 * Extracts the {@link ColorModel} of any {@link StyledRasterInterface}. May
1148 * return <code>null</code> if the geoobject can not be accessed.
1149 */
1150 @SuppressWarnings("unchecked")
1151 public static ColorModel getColorModel(
1152 final StyledRasterInterface<?> styledGrid) {
1153 ColorModel colorModel = null;
1154 try {
1155 final Object geoObject = styledGrid.getGeoObject();
1156 if (geoObject instanceof GridCoverage2D) {
1157 final GridCoverage2D cov = (GridCoverage2D) geoObject;
1158 colorModel = cov.getRenderedImage().getColorModel();
1159 } else if (styledGrid instanceof StyledGridCoverageReaderInterface) {
1160
1161 final Parameter readGG = new Parameter(
1162 AbstractGridFormat.READ_GRIDGEOMETRY2D);
1163
1164 final ReferencedEnvelope mapExtend = new ReferencedEnvelope(
1165 styledGrid.getEnvelope(), styledGrid.getCrs());
1166
1167 readGG.setValue(new GridGeometry2D(new GeneralGridEnvelope(
1168 new Rectangle(0, 0, 1, 1)), mapExtend));
1169
1170 AbstractGridCoverage2DReader aReader;
1171 if (geoObject instanceof FeatureCollection) {
1172 final FeatureCollection<SimpleFeatureType, SimpleFeature> rFc = (FeatureCollection<SimpleFeatureType, SimpleFeature>) geoObject;
1173
1174 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 final GridCoverage2D cov = (GridCoverage2D) aReader
1185 .read(new GeneralParameterValue[] { readGG });
1186 colorModel = cov.getRenderedImage().getColorModel();
1187 }
1188 } catch (final Exception e) {
1189 LOGGER.error("Error reading the colormodel from " + styledGrid, e);
1190 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 public static boolean isStyleable(
1202 final StyledRasterInterface<?> styledRaster) {
1203 final ColorModel colorModel = getColorModel(styledRaster);
1204
1205 // LOGGER.info("The colormodel of " + styledRaster.getTitle() + " is "
1206 // + colorModel != null ? colorModel.getClass().getSimpleName() :
1207 // "NULL");
1208
1209 if (colorModel == null)
1210 return true;
1211 if (colorModel instanceof ComponentColorModel)
1212 return true;
1213 if (colorModel instanceof IndexColorModel)
1214 return true;
1215
1216 return false;
1217 }
1218
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 * @return <code>true</code> if the {@link MapLayer}'s {@link Style} has
1227 * been changed.
1228 */
1229 public static boolean updateMapLayerStyleIfChangedAndKeepSelection(
1230 MapLayer mapLayer, Style style2) {
1231
1232 Style mapLayerStyleCleaned = StylingUtil
1233 .removeSelectionFeatureTypeStyle(mapLayer.getStyle());
1234
1235 Style newStyleCleaned = StylingUtil
1236 .removeSelectionFeatureTypeStyle(style2);
1237
1238 if (StylingUtil.isStyleDifferent(mapLayerStyleCleaned, newStyleCleaned)) {
1239
1240 // They are different when compared without SELECTION FTS!
1241
1242 // Now let's copy any SELECTION FTS to the now style
1243 FeatureTypeStyle selectionFeatureTypeStyle = StylingUtil
1244 .getSelectionFeatureTypeStyle(mapLayer.getStyle());
1245 if (selectionFeatureTypeStyle != null) {
1246 newStyleCleaned.featureTypeStyles().add(
1247 selectionFeatureTypeStyle);
1248 // newStyleCleaned is not so clean anymore... We just alled a
1249 // selcetion FTS
1250 }
1251
1252 mapLayer.setStyle(newStyleCleaned);
1253
1254 return true;
1255
1256 } else {
1257 return false;
1258 }
1259 }
1260
1261 /**
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 public static void checkAttribMetaData(
1273 AttributeMetadataMap<AttributeMetadataImpl> attributeMetaDataMap,
1274 SimpleFeatureType schema) {
1275
1276 if (schema == null)
1277 throw new IllegalArgumentException("schmema may not be null!");
1278
1279 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 for (AttributeMetadataInterface atm : attributeMetaDataMap.values()) {
1285
1286 AttributeDescriptor foundDescr = schema
1287 .getDescriptor(atm.getName());
1288 if (foundDescr == null) {
1289 NameImpl bestMatch = FeatureUtil.findBestMatchingAttribute(
1290 schema, atm.getLocalName());
1291 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 if (attributeMetaDataMap.remove(removeName) == null) {
1304 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 attributeMetaDataMap.put(new NameImpl(ad.getName()
1314 .getNamespaceURI(), ad.getName().getLocalPart()),
1315 new AttributeMetadataImpl(ad, schema
1316 .getAttributeDescriptors().indexOf(ad),
1317 attributeMetaDataMap.getLanguages()));
1318 }
1319 }
1320 }
1321
1322 /**
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 AttributeMetadataMap<? extends AttributeMetadataInterface> attributeMetaDataMap,
1332 SimpleFeatureType schema) {
1333
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
1342 /**
1343 * @return a nicely formatted String containing all NODATA values of any
1344 * {@link AttributeMetadataInterface} object. Strings are quoted so
1345 * that any empty {@link String} can be seen.
1346 */
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 nicelyFormatted = nicelyFormatted.substring(0,
1363 nicelyFormatted.length() - 1);
1364 }
1365 }
1366 return nicelyFormatted;
1367 }
1368
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 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26