/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2011, Open Source Geospatial Foundation (OSGeo) * (C) 2008-2011 TOPP - www.openplans.org. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotools.process.feature.gs; import java.util.Iterator; import java.util.NoSuchElementException; import org.geotools.process.factory.DescribeParameter; import org.geotools.process.factory.DescribeProcess; import org.geotools.process.factory.DescribeResult; import org.geotools.process.gs.GSProcess; import org.geotools.process.gs.WrappingIterator; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.feature.AttributeTypeBuilder; import org.geotools.feature.collection.DecoratingSimpleFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.feature.type.GeometryTypeImpl; import org.geotools.util.Converters; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.MultiPolygon; /** * Buffers a feature collection using a certain distance * * @author Gianni Barrotta - Sinergis * @author Andrea Di Nora - Sinergis * @author Pietro Arena - Sinergis * @author Andrea Aime - GeoSolutions * * @source $URL$ */ @DescribeProcess(title = "buffer", description = "Buffers each feature in a collection by a fixed amount or by a value coming from a feature attribute. Works in pure cartesian mode.") public class BufferFeatureCollection implements GSProcess { @DescribeResult(description = "The buffered feature collection") public SimpleFeatureCollection execute( @DescribeParameter(name = "feature collection", description = "Feature collection") SimpleFeatureCollection features, @DescribeParameter(name = "width of the buffer", description = "The width of the buffer") Double distance, @DescribeParameter(name = "name of the layer attribute containing the width of the buffer", description = "Name of the layer attribute",min=0) String attribute) { if (distance == null && (attribute == null || attribute == "")) { throw new IllegalArgumentException("Buffer distance was not specified"); } if(attribute != null && !"".equals(attribute)) { if(features.getSchema().getDescriptor(attribute) == null) { boolean found = false; // case insensitive search for (AttributeDescriptor ad : features.getSchema().getAttributeDescriptors()) { if(ad.getLocalName().equals(attribute)) { attribute = ad.getLocalName(); found = true; break; } } if(!found) { throw new IllegalArgumentException("Attribute not found among the source collection ones: " + attribute); } } } else { attribute = null; } return new BufferedFeatureCollection(features, attribute, distance); } /** * Wrapper that will trigger the buffer computation as features are requested */ static class BufferedFeatureCollection extends DecoratingSimpleFeatureCollection { Double distance; String attribute; SimpleFeatureType schema; public BufferedFeatureCollection(SimpleFeatureCollection delegate, String attribute, Double distance) { super(delegate); this.distance = distance; this.attribute = attribute; // create schema SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); for (AttributeDescriptor descriptor : delegate.getSchema().getAttributeDescriptors()) { if (!(descriptor.getType() instanceof GeometryTypeImpl) || (!delegate.getSchema().getGeometryDescriptor().equals(descriptor))) { tb.add(descriptor); } else { AttributeTypeBuilder builder = new AttributeTypeBuilder(); builder.setBinding(MultiPolygon.class); AttributeDescriptor attributeDescriptor = builder.buildDescriptor(descriptor .getLocalName(), builder.buildType()); tb.add(attributeDescriptor); if(tb.getDefaultGeometry() == null) { tb.setDefaultGeometry(descriptor.getLocalName()); } } } tb.setDescription(delegate.getSchema().getDescription()); tb.setCRS(delegate.getSchema().getCoordinateReferenceSystem()); tb.setName(delegate.getSchema().getName()); this.schema = tb.buildFeatureType(); } @Override public SimpleFeatureIterator features() { return new BufferedFeatureIterator(delegate, this.attribute, this.distance, getSchema()); } @Override public Iterator<SimpleFeature> iterator() { return new WrappingIterator(features()); } @Override public void close(Iterator<SimpleFeature> close) { if (close instanceof WrappingIterator) { ((WrappingIterator) close).close(); } } @Override public SimpleFeatureType getSchema() { return this.schema; } } /** * Buffers each feature as we scroll over the collection */ static class BufferedFeatureIterator implements SimpleFeatureIterator { SimpleFeatureIterator delegate; SimpleFeatureCollection collection; Double distance; String attribute; int count; SimpleFeatureBuilder fb; SimpleFeature next; public BufferedFeatureIterator(SimpleFeatureCollection delegate, String attribute, Double distance, SimpleFeatureType schema) { this.delegate = delegate.features(); this.distance = distance; this.collection = delegate; this.attribute = attribute; fb = new SimpleFeatureBuilder(schema); } public void close() { delegate.close(); } public boolean hasNext() { while (next == null && delegate.hasNext()) { SimpleFeature f = delegate.next(); for (Object value : f.getAttributes()) { if (value instanceof Geometry) { Double fDistance = distance; if(this.attribute != null) { fDistance = Converters.convert(f.getAttribute(this.attribute), Double.class); } if(fDistance != null && fDistance != 0.0) { value = ((Geometry) value).buffer(fDistance); } } fb.add(value); } next = fb.buildFeature("" + count); count++; fb.reset(); } return next != null; } public SimpleFeature next() throws NoSuchElementException { if (!hasNext()) { throw new NoSuchElementException("hasNext() returned false!"); } SimpleFeature result = next; next = null; return result; } } }