/*
* 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.gridcoverage;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.RenderedOp;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.image.jai.Registry;
import org.geotools.process.ProcessException;
import org.geotools.process.spatialstatistics.core.FeatureTypes;
import org.geotools.process.spatialstatistics.enumeration.RasterPixelType;
import org.geotools.process.spatialstatistics.operations.GeneralOperation;
import org.geotools.process.spatialstatistics.storage.IFeatureInserter;
import org.geotools.util.logging.Logging;
import org.jaitools.media.jai.vectorize.VectorizeDescriptor;
import org.jaitools.media.jai.vectorize.VectorizeRIF;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.util.AffineTransformation;
import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier;
/**
* Converts a raster dataset to polygon features.
*
* @author Minpa Lee, MangoSystem
*
* @reference org.geotools.process.raster.PolygonExtractionProcess
*
* @source $URL$
*/
public class RasterToPolygonOperation extends GeneralOperation {
protected static final Logger LOGGER = Logging.getLogger(RasterToPolygonOperation.class);
static {
Registry.registerRIF(JAI.getDefaultInstance(), new VectorizeDescriptor(),
new VectorizeRIF(), Registry.JAI_TOOLS_PRODUCT);
}
public SimpleFeatureCollection execute(GridCoverage2D inputGc, Integer bandIndex,
boolean weeding, String valueField) throws ProcessException, IOException {
return execute(inputGc, 0, weeding, valueField, true, null);
}
public SimpleFeatureCollection execute(GridCoverage2D inputGc, Integer bandIndex,
boolean weeding, String valueField, boolean insideEdges, List<Number> noDataValues)
throws ProcessException, IOException {
valueField = valueField == null || valueField.isEmpty() ? "value" : valueField;
// build nodata list
List<Number> outsideValues = new ArrayList<Number>();
if (noDataValues != null) {
outsideValues.addAll(noDataValues);
} else {
outsideValues.add(RasterHelper.getNoDataValue(inputGc));
}
outsideValues.add(RasterHelper.getSuggestedNoDataValue(inputGc));
double tolerance = 0;
if (weeding) {
// ArcGIS: The Douglas-Puecker algorithm for line generalization is used
// with a tolerance of sqrt(0.5) * cell size.
tolerance = Math.sqrt(0.5) * RasterHelper.getCellSize(inputGc);
}
// prepare feature type
CoordinateReferenceSystem crs = inputGc.getCoordinateReferenceSystem();
SimpleFeatureType featureType = FeatureTypes.getDefaultType("RasterToVector",
Polygon.class, crs);
RasterPixelType pixelType = RasterHelper.getTransferType(inputGc);
switch (pixelType) {
case FLOAT:
case DOUBLE:
featureType = FeatureTypes.add(featureType, valueField, Double.class, 38);
break;
default:
featureType = FeatureTypes.add(featureType, valueField, Integer.class, 38);
break;
}
featureType = FeatureTypes.add(featureType, "area", Double.class, 38);
// prepare transactional feature store
IFeatureInserter featureWriter = getFeatureWriter(featureType);
try {
GridGeometry2D gg2D = inputGc.getGridGeometry();
AffineTransform mt = (AffineTransform) gg2D.getGridToCRS2D(PixelOrientation.UPPER_LEFT);
AffineTransformation affineTrans = new AffineTransformation(mt.getScaleX(),
mt.getShearX(), mt.getTranslateX(), mt.getShearY(), mt.getScaleY(),
mt.getTranslateY());
// perform jai operation
Object o = JAI.getDefaultInstance().getOperationRegistry()
.getDescriptor(OperationDescriptor.class, "Vectorize");
System.out.println(o);
final ParameterBlockJAI pb = new ParameterBlockJAI("Vectorize");
pb.setSource("source0", inputGc.getRenderedImage());
pb.setParameter("band", Integer.valueOf(bandIndex));
pb.setParameter("outsideValues", outsideValues);
pb.setParameter("insideEdges", Boolean.valueOf(insideEdges));
final RenderedOp dest = JAI.create("Vectorize", pb);
@SuppressWarnings("unchecked")
final Collection<Polygon> property = (Collection<Polygon>) dest
.getProperty(VectorizeDescriptor.VECTOR_PROPERTY_NAME);
for (Polygon polygon : property) {
if (polygon == null || polygon.isEmpty()) {
continue;
}
Double value = (Double) polygon.getUserData();
polygon.setUserData(null);
// filter coordinates in place
polygon.apply(affineTrans);
if (weeding) {
polygon = (Polygon) DouglasPeuckerSimplifier.simplify(polygon, tolerance);
if (polygon == null || polygon.isEmpty()) {
continue;
}
}
// create feature and set geometry
SimpleFeature newFeature = featureWriter.buildFeature();
newFeature.setDefaultGeometry(polygon);
newFeature.setAttribute("area", polygon.getArea());
switch (pixelType) {
case FLOAT:
case DOUBLE:
newFeature.setAttribute(valueField, value.doubleValue());
break;
default:
newFeature.setAttribute(valueField, value.intValue());
break;
}
featureWriter.write(newFeature);
}
} catch (IllegalArgumentException iae) {
featureWriter.rollback(iae);
} catch (IOException e) {
featureWriter.rollback(e);
} catch (Exception e) {
featureWriter.rollback(e);
} catch (Throwable t) {
t.printStackTrace();
featureWriter.rollback();
} finally {
featureWriter.close(null);
}
return featureWriter.getFeatureCollection();
}
}