/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2011, Open Source Geospatial Foundation (OSGeo)
* (C) 2001-2007 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.ProcessException;
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.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.PropertyDescriptor;
/**
* A process providing the union between two feature collections
*
* @author Gianni Barrotta - Sinergis
* @author Andrea Di Nora - Sinergis
* @author Pietro Arena - Sinergis
*/
@DescribeProcess(title = "union", description = "Returns a SQL like union between two feature collections " +
"(will contain attributes from both collections, if two attributes are not the same type " +
"they will be turned into strings)")
public class UnionFeatureCollection implements GSProcess {
static final String SCHEMA_NAME = "Union_Layer";
@DescribeResult(description = "feature collection containg the union between the two feature collections")
public SimpleFeatureCollection execute(
@DescribeParameter(name = "first feature collection", description = "First feature collection") SimpleFeatureCollection firstFeatures,
@DescribeParameter(name = "second feature collection", description = "Second feature collection") SimpleFeatureCollection secondFeatures)
throws ClassNotFoundException {
if (!(firstFeatures.features().next().getDefaultGeometry().getClass().equals(secondFeatures
.features().next().getDefaultGeometry().getClass()))) {
throw new ProcessException("Different default geometries, cannot perform union");
} else {
return new UnitedFeatureCollection(firstFeatures, secondFeatures);
}
}
static class UnitedFeatureCollection extends DecoratingSimpleFeatureCollection {
SimpleFeatureCollection features;
SimpleFeatureType schema;
public UnitedFeatureCollection(SimpleFeatureCollection delegate,
SimpleFeatureCollection features) throws ClassNotFoundException {
super(delegate);
this.features = features;
// Create schema containing the attributes from both the feature collections
SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
for (AttributeDescriptor descriptor : delegate.getSchema().getAttributeDescriptors()) {
if (sameNames(features.getSchema(), descriptor)
&& !sameTypes(features.getSchema(), descriptor)) {
AttributeTypeBuilder builder = new AttributeTypeBuilder();
builder.setName(descriptor.getLocalName());
builder.setNillable(descriptor.isNillable());
builder.setBinding(String.class);
builder.setMinOccurs(descriptor.getMinOccurs());
builder.setMaxOccurs(descriptor.getMaxOccurs());
builder.setDefaultValue(descriptor.getDefaultValue());
builder.setCRS(this.delegate.features().next().getFeatureType()
.getCoordinateReferenceSystem());
AttributeDescriptor attributeDescriptor = builder.buildDescriptor(descriptor
.getName(), builder.buildType());
tb.add(attributeDescriptor);
} else {
tb.add(descriptor);
}
}
for (AttributeDescriptor descriptor : features.getSchema().getAttributeDescriptors()) {
if (!sameNames(delegate.getSchema(), descriptor)
&& !sameTypes(delegate.getSchema(), descriptor)) {
tb.add(descriptor);
}
}
tb.setCRS(delegate.getSchema().getCoordinateReferenceSystem());
tb.setNamespaceURI(delegate.getSchema().getName().getNamespaceURI());
tb.setName(delegate.getSchema().getName());
this.schema = tb.buildFeatureType();
}
@Override
public SimpleFeatureIterator features() {
return new UnitedFeatureIterator(delegate.features(), delegate, features.features(),
features, 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;
}
private boolean sameNames(SimpleFeatureType schema, AttributeDescriptor f) {
for (AttributeDescriptor descriptor : schema.getAttributeDescriptors()) {
if (descriptor.getName().equals(f.getName())) {
return true;
}
}
return false;
}
private boolean sameTypes(SimpleFeatureType schema, AttributeDescriptor f) {
for (AttributeDescriptor descriptor : schema.getAttributeDescriptors()) {
if (descriptor.getType().equals(f.getType())) {
return true;
}
}
return false;
}
}
static class UnitedFeatureIterator implements SimpleFeatureIterator {
SimpleFeatureIterator firstDelegate;
SimpleFeatureIterator secondDelegate;
SimpleFeatureCollection firstCollection;
SimpleFeatureCollection secondCollection;
SimpleFeatureBuilder fb;
SimpleFeature next;
int iterationIndex = 0;
public UnitedFeatureIterator(SimpleFeatureIterator firstDelegate,
SimpleFeatureCollection firstCollection, SimpleFeatureIterator secondDelegate,
SimpleFeatureCollection secondCollection, SimpleFeatureType schema) {
this.firstDelegate = firstDelegate;
this.secondDelegate = secondDelegate;
this.firstCollection = firstCollection;
this.secondCollection = secondCollection;
fb = new SimpleFeatureBuilder(schema);
}
public void close() {
firstDelegate.close();
secondDelegate.close();
}
public boolean hasNext() {
while (next == null && firstDelegate.hasNext()) {
SimpleFeature f = firstDelegate.next();
for (PropertyDescriptor property : fb.getFeatureType().getDescriptors()) {
fb.set(property.getName(), f.getAttribute(property.getName()));
}
next = fb.buildFeature(Integer.toString(iterationIndex));
fb.reset();
iterationIndex++;
}
while (next == null && secondDelegate.hasNext() && !firstDelegate.hasNext()) {
SimpleFeature f = secondDelegate.next();
for (PropertyDescriptor property : fb.getFeatureType().getDescriptors()) {
fb.set(property.getName(), f.getAttribute(property.getName()));
}
next = fb.buildFeature(Integer.toString(iterationIndex));
fb.reset();
iterationIndex++;
}
return next != null;
}
public SimpleFeature next() throws NoSuchElementException {
if (!hasNext()) {
throw new NoSuchElementException("hasNext() returned false!");
}
SimpleFeature result = next;
next = null;
return result;
}
}
}