/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.snap.rcp.layermanager.layersrc.shapefile; import com.bc.ceres.binding.ConversionException; import com.bc.ceres.binding.Property; import com.bc.ceres.binding.PropertyContainer; import com.bc.ceres.binding.PropertySet; import com.bc.ceres.binding.ValidationException; import com.bc.ceres.binding.dom.DefaultDomConverter; import com.bc.ceres.binding.dom.DomConverter; import com.bc.ceres.binding.dom.DomElement; import com.bc.ceres.binding.dom.XppDomElement; import com.bc.ceres.core.ProgressMonitor; import com.bc.ceres.glayer.Layer; import com.bc.ceres.glayer.LayerContext; import com.bc.ceres.glayer.LayerType; import com.bc.ceres.glayer.annotations.LayerTypeMetadata; import com.thoughtworks.xstream.io.copy.HierarchicalStreamCopier; import com.thoughtworks.xstream.io.xml.XppDomWriter; import com.thoughtworks.xstream.io.xml.XppReader; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import org.esa.snap.core.util.FeatureUtils; import org.geotools.data.FeatureSource; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.FeatureCollection; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.styling.SLDParser; import org.geotools.styling.SLDTransformer; import org.geotools.styling.Style; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import javax.xml.transform.TransformerException; import java.io.IOException; import java.io.StringReader; import java.net.URL; import java.util.ArrayList; import java.util.List; /** * The type of a {@link FeatureLayer}. * <p> * Unstable API. Use at own risk. */ @LayerTypeMetadata(name = "FeatureLayerType", aliasNames = {"FeatureLayerType"}) public class FeatureLayerType extends LayerType { public static final String PROPERTY_NAME_SLD_STYLE = "sldStyle"; public static final String PROPERTY_NAME_FEATURE_COLLECTION = "featureCollection"; public static final String PROPERTY_NAME_FEATURE_COLLECTION_URL = "featureCollectionUrl"; public static final String PROPERTY_NAME_FEATURE_COLLECTION_CRS = "featureCollectionTargetCrs"; public static final String PROPERTY_NAME_FEATURE_COLLECTION_CLIP_GEOMETRY = "featureCollectionClipGeometry"; @Override public boolean isValidFor(LayerContext ctx) { return true; } @Override public Layer createLayer(LayerContext ctx, PropertySet configuration) { CoordinateReferenceSystem targetCrs = null; if (ctx != null) { targetCrs = (CoordinateReferenceSystem) ctx.getCoordinateReferenceSystem(); } FeatureCollection<SimpleFeatureType, SimpleFeature> fc; fc = (FeatureCollection<SimpleFeatureType, SimpleFeature>) configuration.getValue(FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION); if (fc == null) { try { final URL url = (URL) configuration.getValue(FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION_URL); FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = FeatureUtils.getFeatureSource(url); fc = featureSource.getFeatures(); } catch (IOException e) { throw new IllegalArgumentException(e); } } final CoordinateReferenceSystem featureCrs = (CoordinateReferenceSystem) configuration.getValue( FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION_CRS); final Geometry clipGeometry = (Geometry) configuration.getValue( FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION_CLIP_GEOMETRY); fc = FeatureUtils.clipCollection(fc, featureCrs, clipGeometry, DefaultGeographicCRS.WGS84, null, targetCrs, ProgressMonitor.NULL); return new FeatureLayer(this, fc, configuration); } @Override public PropertySet createLayerConfig(LayerContext ctx) { final PropertyContainer configuration = new PropertyContainer(); // Mandatory Parameters configuration.addProperty(Property.create(PROPERTY_NAME_FEATURE_COLLECTION, FeatureCollection.class)); configuration.getDescriptor(PROPERTY_NAME_FEATURE_COLLECTION).setTransient(true); configuration.addProperty(Property.create(PROPERTY_NAME_SLD_STYLE, Style.class)); configuration.getDescriptor(PROPERTY_NAME_SLD_STYLE).setDomConverter(new StyleDomConverter()); configuration.getDescriptor(PROPERTY_NAME_SLD_STYLE).setNotNull(true); // Optional Parameters configuration.addProperty(Property.create(PROPERTY_NAME_FEATURE_COLLECTION_CLIP_GEOMETRY, Geometry.class)); configuration.getDescriptor(PROPERTY_NAME_FEATURE_COLLECTION_CLIP_GEOMETRY).setDomConverter( new GeometryDomConverter()); configuration.addProperty(Property.create(PROPERTY_NAME_FEATURE_COLLECTION_URL, URL.class)); configuration.addProperty(Property.create(PROPERTY_NAME_FEATURE_COLLECTION_CRS, CoordinateReferenceSystem.class)); configuration.getDescriptor(PROPERTY_NAME_FEATURE_COLLECTION_CRS).setDomConverter(new CRSDomConverter()); return configuration; } private static class StyleDomConverter implements DomConverter { @Override public Class<?> getValueType() { return Style.class; } @Override public Object convertDomToValue(DomElement parentElement, Object value) throws ConversionException, ValidationException { final DomElement child = parentElement.getChild(0); SLDParser s = new SLDParser(CommonFactoryFinder.getStyleFactory(null), new StringReader(child.toXml())); final Style[] styles = s.readXML(); return styles[0]; } @Override public void convertValueToDom(Object value, DomElement parentElement) throws ConversionException { Style style = (Style) value; final SLDTransformer transformer = new SLDTransformer(); transformer.setIndentation(2); try { final String s = transformer.transform(style); XppDomWriter domWriter = new XppDomWriter(); new HierarchicalStreamCopier().copy(new XppReader(new StringReader(s)), domWriter); parentElement.addChild(new XppDomElement(domWriter.getConfiguration())); } catch (TransformerException e) { throw new IllegalArgumentException(e); } } } private static class CRSDomConverter implements DomConverter { @Override public Class<?> getValueType() { return null; } @Override public Object convertDomToValue(DomElement parentElement, Object value) throws ConversionException, ValidationException { try { value = CRS.parseWKT(parentElement.getValue()); } catch (FactoryException e) { throw new IllegalArgumentException(e); } return value; } @Override public void convertValueToDom(Object value, DomElement parentElement) throws ConversionException { CoordinateReferenceSystem crs = (CoordinateReferenceSystem) value; parentElement.setValue(crs.toWKT()); } } private static class GeometryDomConverter implements DomConverter { @Override public Class<?> getValueType() { return Geometry.class; } @Override public Object convertDomToValue(DomElement parentElement, Object value) throws ConversionException, ValidationException { com.vividsolutions.jts.geom.GeometryFactory gf = new com.vividsolutions.jts.geom.GeometryFactory(); final DefaultDomConverter domConverter = new DefaultDomConverter(Coordinate.class); final DomElement[] children = parentElement.getChildren("coordinate"); List<Coordinate> coordList = new ArrayList<Coordinate>(); for (DomElement child : children) { final Coordinate coordinate = (Coordinate) domConverter.convertDomToValue(child, null); coordList.add(coordinate); } return gf.createPolygon(gf.createLinearRing(coordList.toArray(new Coordinate[coordList.size()])), null); } @Override public void convertValueToDom(Object value, DomElement parentElement) throws ConversionException { Geometry geom = (Geometry) value; final Coordinate[] coordinates = geom.getCoordinates(); final DefaultDomConverter domConverter = new DefaultDomConverter(Coordinate.class); for (Coordinate coordinate : coordinates) { final DomElement child = parentElement.createChild("coordinate"); domConverter.convertValueToDom(coordinate, child); } } } }