/* * 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.operations; import java.io.IOException; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map.Entry; import java.util.logging.Logger; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.process.spatialstatistics.core.FeatureTypes; import org.geotools.process.spatialstatistics.core.StatisticsField; import org.geotools.process.spatialstatistics.core.StatisticsVisitor; import org.geotools.process.spatialstatistics.core.StatisticsVisitorResult; import org.geotools.process.spatialstatistics.core.StringHelper; import org.geotools.process.spatialstatistics.core.SummaryFieldBuilder; import org.geotools.process.spatialstatistics.storage.IFeatureInserter; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.filter.expression.Expression; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.operation.union.CascadedPolygonUnion; /** * Dissolves features based on specified attributes and aggregation functions. * * @author Minpa Lee, MangoSystem * * @source $URL$ */ public class DissolveOperation extends GeneralOperation { protected static final Logger LOGGER = Logging.getLogger(DissolveOperation.class); static final String NULL = "NULLVALUE"; private Boolean useMultiPart = Boolean.TRUE; private Hashtable<Object, Hashtable<String, StatisticsVisitor>> attMap; private Hashtable<Object, List<Geometry>> geoMap; public boolean isUseMultiPart() { return useMultiPart; } public void setUseMultiPart(boolean useMultiPart) { this.useMultiPart = useMultiPart; } public SimpleFeatureCollection execute(SimpleFeatureCollection features, String dissolveField, String summaryFields) throws IOException { return execute(features, dissolveField, summaryFields, null); } public SimpleFeatureCollection execute(SimpleFeatureCollection features, String dissolveField, String summaryFields, String targetFields) throws IOException { if (StringHelper.isNullOrEmpty(dissolveField)) { throw new NullPointerException("dissolveField parameter required!"); } SimpleFeatureType schema = features.getSchema(); dissolveField = FeatureTypes.validateProperty(schema, dissolveField); // create TransactionalFeatureCollection Class<?> binding = schema.getGeometryDescriptor().getType().getBinding(); CoordinateReferenceSystem crs = schema.getCoordinateReferenceSystem(); SummaryFieldBuilder sfBuilder = SummaryFieldBuilder.getInstance(); List<StatisticsField> statisticsList; if (targetFields == null || targetFields.isEmpty()) { statisticsList = sfBuilder.buildFields(schema, summaryFields); } else { statisticsList = sfBuilder.buildFields(schema, summaryFields, targetFields); } if (statisticsList == null) { throw new NullPointerException("cannot build statistics fields!"); } String geomName = schema.getGeometryDescriptor().getLocalName(); SimpleFeatureType featureType = FeatureTypes.getDefaultType(schema.getTypeName(), geomName, binding, crs); featureType = FeatureTypes.add(featureType, schema.getDescriptor(dissolveField)); featureType = addAttributes(featureType, statisticsList); // build unique fields final List<String> uvFields = new ArrayList<String>(); for (StatisticsField curField : statisticsList) { if (!uvFields.contains(curField.getSrcField())) { uvFields.add(curField.getSrcField()); } } // calculate statistics calculateStatistics(features, dissolveField, schema, uvFields); // post process IFeatureInserter writer = getFeatureWriter(featureType); try { for (Entry<Object, List<Geometry>> entry : geoMap.entrySet()) { CascadedPolygonUnion unionOp = new CascadedPolygonUnion(entry.getValue()); Geometry unionGeometry = unionOp.union(); if (useMultiPart) { // multi part feature SimpleFeature newFeature = writer.buildFeature(); newFeature.setDefaultGeometry(unionGeometry); newFeature.setAttribute(dissolveField, entry.getKey()); Hashtable<String, StatisticsVisitor> map = attMap.get(entry.getKey()); if (map != null) { for (StatisticsField field : statisticsList) { StatisticsVisitorResult ret = map.get(field.getSrcField()).getResult(); if (ret == null) { continue; } Object val = ret.getValue(field.getStatType()); newFeature.setAttribute(field.getTargetField(), val); } } writer.write(newFeature); } else { // single part feature for (int idx = 0; idx < unionGeometry.getNumGeometries(); idx++) { SimpleFeature newFeature = writer.buildFeature(); newFeature.setDefaultGeometry(unionGeometry.getGeometryN(idx)); newFeature.setAttribute(dissolveField, entry.getKey()); Hashtable<String, StatisticsVisitor> map = attMap.get(entry.getKey()); if (map != null) { for (StatisticsField field : statisticsList) { StatisticsVisitorResult ret = map.get(field.getSrcField()) .getResult(); if (ret == null) { continue; } Object val = ret.getValue(field.getStatType()); newFeature.setAttribute(field.getTargetField(), val); } } writer.write(newFeature); } } } } finally { writer.close(); } return writer.getFeatureCollection(); } private void calculateStatistics(SimpleFeatureCollection features, String dissolveField, SimpleFeatureType schema, List<String> uvFields) { attMap = new Hashtable<Object, Hashtable<String, StatisticsVisitor>>(); geoMap = new Hashtable<Object, List<Geometry>>(); SimpleFeatureIterator featureIter = features.features(); try { Expression disExpresion = ff.property(dissolveField); while (featureIter.hasNext()) { SimpleFeature feature = featureIter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Object disValue = disExpresion.evaluate(feature); if (disValue == null) { disValue = NULL; } // geometries if (geoMap.get(disValue) == null) { geoMap.put(disValue, new ArrayList<Geometry>()); } geoMap.get(disValue).add(geometry); // attributes if (attMap.get(disValue) == null) { attMap.put(disValue, new Hashtable<String, StatisticsVisitor>()); for (String field : uvFields) { attMap.get(disValue).put(field, new StatisticsVisitor(schema, field)); } } for (String field : uvFields) { attMap.get(disValue).get(field).visit(feature); } } } finally { featureIter.close(); } } private SimpleFeatureType addAttributes(SimpleFeatureType featureType, List<StatisticsField> attributeList) { if (attributeList == null) { return featureType; } for (StatisticsField st : attributeList) { featureType = FeatureTypes.add(featureType, st.getTargetField(), st.fieldType, st.getFieldLength()); } return featureType; } }