001/* 002 * GeoTools - The Open Source Java GIS Toolkit 003 * http://geotools.org 004 * 005 * (C) 2011, Open Source Geospatial Foundation (OSGeo) 006 * (C) 2001-2007 TOPP - www.openplans.org. 007 * 008 * This library is free software; you can redistribute it and/or 009 * modify it under the terms of the GNU Lesser General Public 010 * License as published by the Free Software Foundation; 011 * version 2.1 of the License. 012 * 013 * This library is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 016 * Lesser General Public License for more details. 017 */ 018package org.kepler.gis.data; 019 020import java.util.NoSuchElementException; 021 022import org.geotools.data.simple.SimpleFeatureCollection; 023import org.geotools.data.simple.SimpleFeatureIterator; 024import org.geotools.feature.AttributeTypeBuilder; 025import org.geotools.feature.collection.DecoratingSimpleFeatureCollection; 026import org.geotools.feature.simple.SimpleFeatureBuilder; 027import org.geotools.feature.simple.SimpleFeatureTypeBuilder; 028import org.geotools.geometry.jts.ReferencedEnvelope; 029import org.geotools.process.ProcessException; 030import org.geotools.process.factory.DescribeParameter; 031import org.geotools.process.factory.DescribeProcess; 032import org.geotools.process.factory.DescribeResult; 033import org.geotools.process.vector.VectorProcess; 034import org.opengis.feature.simple.SimpleFeature; 035import org.opengis.feature.simple.SimpleFeatureType; 036import org.opengis.feature.type.AttributeDescriptor; 037import org.opengis.feature.type.PropertyDescriptor; 038import org.opengis.filter.Filter; 039 040/** 041 * A process providing the union between two feature collections 042 * 043 * @author Gianni Barrotta - Sinergis 044 * @author Andrea Di Nora - Sinergis 045 * @author Pietro Arena - Sinergis 046 */ 047@DescribeProcess(title = "Union Feature Collections", description = "Returns single feature collection containing all features from two input feature collections. The output attribute schema is a combination of the attributes from the inputs. Attributes with same name but different types will be converted to strings.") 048public class UnionFeatureCollection implements VectorProcess { 049 050 static final String SCHEMA_NAME = "Union_Layer"; 051 052 @DescribeResult(name = "result", description = "Output feature collection") 053 public SimpleFeatureCollection execute( 054 @DescribeParameter(name = "first", description = "First input feature collection") SimpleFeatureCollection firstFeatures, 055 @DescribeParameter(name = "second", description = "Second feature collection") SimpleFeatureCollection secondFeatures) 056 throws ClassNotFoundException { 057 try (SimpleFeatureIterator firstIterator = firstFeatures.features(); 058 SimpleFeatureIterator secondIterator = secondFeatures 059 .features();) { 060 if (!(firstIterator.next().getDefaultGeometry().getClass() 061 .equals(secondIterator.next().getDefaultGeometry() 062 .getClass()))) { 063 throw new ProcessException( 064 "Different default geometries, cannot perform union"); 065 } 066 } 067 return new UnitedFeatureCollection(firstFeatures, secondFeatures); 068 } 069 070 static class UnitedFeatureCollection extends DecoratingSimpleFeatureCollection { 071 072 SimpleFeatureCollection features; 073 074 SimpleFeatureType schema; 075 076 public UnitedFeatureCollection(SimpleFeatureCollection delegate, 077 SimpleFeatureCollection features) throws ClassNotFoundException { 078 super(delegate); 079 this.features = features; 080 081 // Create schema containing the attributes from both the feature collections 082 SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); 083 for (AttributeDescriptor descriptor : delegate.getSchema().getAttributeDescriptors()) { 084 if (sameNames(features.getSchema(), descriptor) 085 && !sameTypes(features.getSchema(), descriptor)) { 086 AttributeTypeBuilder builder = new AttributeTypeBuilder(); 087 builder.setName(descriptor.getLocalName()); 088 builder.setNillable(descriptor.isNillable()); 089 builder.setBinding(String.class); 090 builder.setMinOccurs(descriptor.getMinOccurs()); 091 builder.setMaxOccurs(descriptor.getMaxOccurs()); 092 builder.setDefaultValue(descriptor.getDefaultValue()); 093 try (SimpleFeatureIterator delegateIterator = this.delegate 094 .features();) { 095 builder.setCRS(delegateIterator.next().getFeatureType() 096 .getCoordinateReferenceSystem()); 097 } 098 AttributeDescriptor attributeDescriptor = builder.buildDescriptor(descriptor 099 .getName(), builder.buildType()); 100 tb.add(attributeDescriptor); 101 } else { 102 tb.add(descriptor); 103 } 104 } 105 for (AttributeDescriptor descriptor : features.getSchema().getAttributeDescriptors()) { 106 if (!sameNames(delegate.getSchema(), descriptor) 107 && !sameTypes(delegate.getSchema(), descriptor)) { 108 tb.add(descriptor); 109 } 110 } 111 112 tb.setCRS(delegate.getSchema().getCoordinateReferenceSystem()); 113 tb.setNamespaceURI(delegate.getSchema().getName().getNamespaceURI()); 114 tb.setName(delegate.getSchema().getName()); 115 this.schema = tb.buildFeatureType(); 116 } 117 118 @Override 119 public SimpleFeatureIterator features() { 120 return new UnitedFeatureIterator(delegate.features(), delegate, features.features(), 121 features, getSchema()); 122 } 123 124 @Override 125 public ReferencedEnvelope getBounds() { 126 ReferencedEnvelope envelope = ReferencedEnvelope.create(super.getBounds()); 127 envelope.expandToInclude(features.getBounds()); 128 return envelope; 129 } 130 131 @Override 132 public SimpleFeatureType getSchema() { 133 return this.schema; 134 } 135 136 @Override 137 public int size() { 138 return super.size() + features.size(); 139 } 140 141 @Override 142 public SimpleFeatureCollection subCollection(Filter filter) { 143 try { 144 return new UnitedFeatureCollection(super.subCollection(filter), 145 features.subCollection(filter)); 146 } catch (ClassNotFoundException e) { 147 throw new RuntimeException("ClassNotFoundException", e); 148 } 149 } 150 151 private boolean sameNames(SimpleFeatureType schema, AttributeDescriptor f) { 152 for (AttributeDescriptor descriptor : schema.getAttributeDescriptors()) { 153 if (descriptor.getName().equals(f.getName())) { 154 return true; 155 } 156 } 157 return false; 158 } 159 160 private boolean sameTypes(SimpleFeatureType schema, AttributeDescriptor f) { 161 for (AttributeDescriptor descriptor : schema.getAttributeDescriptors()) { 162 if (descriptor.getType().equals(f.getType())) { 163 return true; 164 } 165 } 166 return false; 167 } 168 } 169 170 static class UnitedFeatureIterator implements SimpleFeatureIterator { 171 172 SimpleFeatureIterator firstDelegate; 173 174 SimpleFeatureIterator secondDelegate; 175 176 SimpleFeatureCollection firstCollection; 177 178 SimpleFeatureCollection secondCollection; 179 180 SimpleFeatureBuilder fb; 181 182 SimpleFeature next; 183 184 int iterationIndex = 0; 185 186 public UnitedFeatureIterator(SimpleFeatureIterator firstDelegate, 187 SimpleFeatureCollection firstCollection, SimpleFeatureIterator secondDelegate, 188 SimpleFeatureCollection secondCollection, SimpleFeatureType schema) { 189 this.firstDelegate = firstDelegate; 190 this.secondDelegate = secondDelegate; 191 this.firstCollection = firstCollection; 192 this.secondCollection = secondCollection; 193 fb = new SimpleFeatureBuilder(schema); 194 } 195 196 @Override 197 public void close() { 198 firstDelegate.close(); 199 secondDelegate.close(); 200 } 201 202 @Override 203 public boolean hasNext() { 204 205 while (next == null && firstDelegate.hasNext()) { 206 SimpleFeature f = firstDelegate.next(); 207 for (PropertyDescriptor property : fb.getFeatureType().getDescriptors()) { 208 fb.set(property.getName(), f.getAttribute(property.getName())); 209 210 } 211 next = fb.buildFeature(Integer.toString(iterationIndex)); 212 fb.reset(); 213 iterationIndex++; 214 } 215 while (next == null && secondDelegate.hasNext() && !firstDelegate.hasNext()) { 216 SimpleFeature f = secondDelegate.next(); 217 for (PropertyDescriptor property : fb.getFeatureType().getDescriptors()) { 218 fb.set(property.getName(), f.getAttribute(property.getName())); 219 } 220 next = fb.buildFeature(Integer.toString(iterationIndex)); 221 fb.reset(); 222 iterationIndex++; 223 } 224 return next != null; 225 } 226 227 @Override 228 public SimpleFeature next() throws NoSuchElementException { 229 if (!hasNext()) { 230 throw new NoSuchElementException("hasNext() returned false!"); 231 } 232 233 SimpleFeature result = next; 234 next = null; 235 return result; 236 } 237 238 } 239}