/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2014, Open Source Geospatial Foundation (OSGeo) * * 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.spatialstatistics.transformation; import java.util.Hashtable; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.logging.Logger; import org.geotools.data.Join; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.feature.collection.SubFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.process.spatialstatistics.core.FeatureTypes; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.filter.Filter; /** * Join Attribute SimpleFeatureCollection Implementation * * @author Minpa Lee, MangoSystem * * @source $URL$ */ public class JoinAttributeFeatureCollection extends GXTSimpleFeatureCollection { protected static final Logger LOGGER = Logging.getLogger(JoinAttributeFeatureCollection.class); // TODO : GeoTools v.8.x use Join Query? private SimpleFeatureType schema; private String primaryKey; private SimpleFeatureCollection joinFeatures; private String foreignKey; private Join.Type joinType; private Hashtable<String, String> joinFields; public JoinAttributeFeatureCollection(SimpleFeatureCollection inputFeatures, String primaryKey, SimpleFeatureCollection joinFeatures, String foreignKey, Join.Type joinType) { super(inputFeatures); this.joinFields = new Hashtable<String, String>(); this.joinType = joinType; this.primaryKey = FeatureTypes.validateProperty(inputFeatures.getSchema(), primaryKey); this.foreignKey = FeatureTypes.validateProperty(joinFeatures.getSchema(), foreignKey); this.joinFeatures = joinFeatures; this.schema = buildTargetSchema(inputFeatures.getSchema(), joinFeatures.getSchema()); } private SimpleFeatureType buildTargetSchema(SimpleFeatureType originSchema, SimpleFeatureType destSchema) { SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.setNamespaceURI(originSchema.getName().getNamespaceURI()); builder.setName(originSchema.getTypeName()); // 0. default geometry descriptor if (originSchema.getGeometryDescriptor() != null) { GeometryDescriptor descriptor = originSchema.getGeometryDescriptor(); builder.setCRS(descriptor.getCoordinateReferenceSystem()); builder.add(descriptor); } else { if (destSchema.getGeometryDescriptor() != null) { GeometryDescriptor descriptor = destSchema.getGeometryDescriptor(); builder.setCRS(descriptor.getCoordinateReferenceSystem()); builder.add(descriptor); } } // 1. first schema for (AttributeDescriptor ad : originSchema.getAttributeDescriptors()) { if (ad instanceof GeometryDescriptor) { continue; // skip geometry } else { builder.add(ad); } } // 2. second schema for (AttributeDescriptor ad : destSchema.getAttributeDescriptors()) { if (ad instanceof GeometryDescriptor) { continue; // skip geometry } else { String name = ad.getLocalName(); if (originSchema.indexOf(name) == -1) { builder.add(ad); joinFields.put(ad.getLocalName(), name); } else { // get unique field name for (int index = 1; index < Integer.MAX_VALUE; index++) { name = ad.getLocalName() + "_" + index; if (originSchema.indexOf(name) == -1) { break; } } // build AttributeDescriptor Class<?> binding = ad.getType().getBinding(); if (CharSequence.class.isAssignableFrom(binding)) { int length = FeatureTypes.getAttributeLength(ad); if (length == 0) { length = 254; // string default length } builder.length(length); } builder.minOccurs(ad.getMinOccurs()); builder.maxOccurs(ad.getMaxOccurs()); builder.nillable(ad.isNillable()); builder.add(name, binding); joinFields.put(ad.getLocalName(), name); } } } return builder.buildFeatureType(); } @Override public SimpleFeatureType getSchema() { return schema; } @Override public SimpleFeatureCollection subCollection(Filter filter) { if (filter == Filter.INCLUDE) { return this; } return new SubFeatureCollection(this, filter); } @Override public SimpleFeatureIterator features() { return new AttributeJoinFeatureIterator(delegate.features(), getSchema(), primaryKey, joinFeatures, foreignKey, joinType, joinFields); } static class AttributeJoinFeatureIterator implements SimpleFeatureIterator { private SimpleFeatureIterator originIter; private String primaryKey; private SimpleFeatureCollection joinFeatures; private String foreignKey; private Join.Type joinType; private Hashtable<String, String> joinFields; private SimpleFeatureBuilder builder; private SimpleFeature nextFeature = null; private String typeName; private int counter = 1; public AttributeJoinFeatureIterator(SimpleFeatureIterator originIter, SimpleFeatureType schema, String primaryKey, SimpleFeatureCollection joinFeatures, String foreignKey, Join.Type joinType, Hashtable<String, String> joinFields) { this.originIter = originIter; this.primaryKey = primaryKey; this.joinFeatures = joinFeatures; this.foreignKey = foreignKey; this.joinType = joinType; this.joinFields = joinFields; this.builder = new SimpleFeatureBuilder(schema); this.typeName = schema.getTypeName(); this.counter = 1; } public void close() { originIter.close(); } public boolean hasNext() { nextFeature = null; boolean hasNext = originIter.hasNext(); if (!hasNext) { return false; } SimpleFeature origin = originIter.next(); for (AttributeDescriptor ad : origin.getFeatureType().getAttributeDescriptors()) { Object value = origin.getAttribute(ad.getLocalName()); builder.set(ad.getLocalName(), value); } boolean hasJoin = false; Object keyValue = origin.getAttribute(primaryKey); Filter filter = ff.equal(ff.property(foreignKey), ff.literal(keyValue), true); SimpleFeatureIterator destIter = joinFeatures.subCollection(filter).features(); try { while (destIter.hasNext()) { SimpleFeature dest = destIter.next(); for (Entry<String, String> entry : joinFields.entrySet()) { Object value = dest.getAttribute(entry.getKey()); builder.set(entry.getValue(), value); } hasJoin = true; nextFeature = builder.buildFeature(buildID(typeName, counter++)); builder.reset(); break; // one to one } } finally { destIter.close(); } if (joinType == Join.Type.INNER && !hasJoin) { return hasNext(); } else if (joinType == Join.Type.OUTER && !hasJoin) { nextFeature = builder.buildFeature(buildID(typeName, counter++)); builder.reset(); } return true; } public SimpleFeature next() throws NoSuchElementException { if (!hasNext()) { throw new NoSuchElementException("hasNext() returned false!"); } return nextFeature; } } }