/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2011, Geomatys * * 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.geotoolkit.processing.vector.douglaspeucker; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier; import java.util.Collections; import org.apache.sis.measure.Units; import org.geotoolkit.data.FeatureCollection; import org.geotoolkit.geometry.jts.JTS; import org.geotoolkit.processing.AbstractProcess; import org.geotoolkit.processing.vector.VectorProcessUtils; import org.apache.sis.referencing.CRS; import org.apache.sis.referencing.CommonCRS; import org.opengis.feature.Feature; import org.opengis.feature.PropertyType; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.opengis.util.FactoryException; import org.apache.sis.feature.FeatureExt; import org.apache.sis.internal.feature.AttributeConvention; import static org.geotoolkit.processing.vector.douglaspeucker.DouglasPeuckerDescriptor.*; import static org.geotoolkit.parameter.Parameters.*; import org.opengis.feature.AttributeType; /** * Process to simplify geometry contained into a Features. * If the simplification accuracy is more than geometry envelope width or height * and the simplification behavior boolean is true, the returned geometry will be null. * The used unit for the accuracy is meters. * * @author Quentin Boileau */ public class DouglasPeuckerProcess extends AbstractProcess { /** * Default constructor */ public DouglasPeuckerProcess(final ParameterValueGroup input) { super(INSTANCE,input); } /** * {@inheritDoc } */ @Override protected void execute() { final FeatureCollection inputFeatureList = value(FEATURE_IN, inputParameters); final Double inputAccuracy = value(ACCURACY_IN, inputParameters); final Boolean inputBehavior = value(DEL_SMALL_GEO_IN, inputParameters) != null ? value(DEL_SMALL_GEO_IN, inputParameters) : DEL_SMALL_GEO_IN.getDefaultValue(); final Boolean inputLenient = value(LENIENT_TRANSFORM_IN, inputParameters) != null ? value(LENIENT_TRANSFORM_IN, inputParameters) : LENIENT_TRANSFORM_IN.getDefaultValue(); final FeatureCollection resultFeatureList = new DouglasPeuckerFeatureCollection(inputFeatureList, inputAccuracy, inputBehavior, inputLenient); getOrCreate(FEATURE_OUT, outputParameters).setValue(resultFeatureList); } /** * Simplify feature geometry using the DouglasPeucker Algorithm. * Geometries are transform into better projection and simplified * If the simplified accuracy is bigger than geometry envelope and the inputBehavior * is set to true, the result geometry will be <code>null</code>. * @param oldFeature * @param accuracy * @param behavior - boolean to set the process behavior with small geometries * @param lenient - boolean used to set the lenient parameter during CRS change * @return the simplified Feature * @throws NoSuchAuthorityCodeException * @throws FactoryException * @throws MismatchedDimensionException * @throws TransformException */ static Feature simplifyFeature(final Feature oldFeature, final Double accuracy, final boolean behavior, final boolean lenient) throws FactoryException, MismatchedDimensionException, TransformException { final CoordinateReferenceSystem originalCRS = FeatureExt.getCRS(oldFeature.getType()); final GeographicCRS longLatCRS = CommonCRS.WGS84.normalizedGeographic(); final MathTransform mtToLongLatCRS = CRS.findOperation(originalCRS, longLatCRS, null).getMathTransform(); final Feature resultFeature = oldFeature.getType().newInstance(); FeatureExt.setId(resultFeature, FeatureExt.getId(oldFeature)); for (PropertyType property : oldFeature.getType().getProperties(true)) { if(!(property instanceof AttributeType)) continue; final String name = property.getName().toString(); final Object value = oldFeature.getPropertyValue(name); if (AttributeConvention.isGeometryAttribute(property)) { //convert geometry into WGS84 final Geometry convertedGeometry = JTS.transform((Geometry) value, mtToLongLatCRS); Envelope convertEnvelope = convertedGeometry.getEnvelopeInternal(); //create custom projection for the geometry final MathTransform projection = VectorProcessUtils.changeProjection(convertEnvelope, longLatCRS, Units.METRE); //Apply the custom projection to geometry final Geometry calculatedGeom = JTS.transform(convertedGeometry, projection); convertEnvelope = calculatedGeom.getEnvelopeInternal(); //We compare if the simplification accuracy is more than geometry envelope width or height if (convertEnvelope.getWidth() < accuracy && convertEnvelope.getHeight() < accuracy) { //In this case, if behavior boolean is true, we return null for the feature //else we set the geometry feature to null if (behavior) { return null; } else { resultFeature.setPropertyValue(name, JTS.emptyGeometry(convertedGeometry.getClass(),null,null)); } } else { //simplify geometry Geometry simplifiedGeometry = DouglasPeuckerSimplifier.simplify(calculatedGeom, accuracy); //restor to original CRS simplifiedGeometry = JTS.transform(simplifiedGeometry, projection.inverse()); simplifiedGeometry = JTS.transform(simplifiedGeometry, mtToLongLatCRS.inverse()); resultFeature.setPropertyValue(name, simplifiedGeometry); } } else { resultFeature.setPropertyValue(name, value); } } return resultFeature; } }