/*
* 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.coverage.coveragetofeatures;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import java.awt.geom.Point2D;
import org.apache.sis.feature.builder.AttributeRole;
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.geotoolkit.coverage.grid.GeneralGridGeometry;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.io.CoverageStoreException;
import org.geotoolkit.coverage.io.GridCoverageReader;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.processing.AbstractProcess;
import org.geotoolkit.process.ProcessException;
import org.geotoolkit.utility.parameter.ParametersExt;
import org.apache.sis.internal.feature.AttributeConvention;
import org.opengis.coverage.SampleDimensionType;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureType;
import org.opengis.metadata.spatial.Georectified;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import static org.geotoolkit.parameter.Parameters.*;
/**
* Process CoverageToFeature create a collection of Feature based on a coverage layer.
* Each features refers to a coverage's cell. The resulting feature type is :
* <ul>
* <li>name : FeatureCoverage</li>
* <li>position : cell position defines with a Point</li>
* <li>cellgeom : cell geometry defines with a polygon </li>
* <li>orientation : the PixelInCell position (CELL_CENTER or CELL_CORNER)</li>
* <li>band-(0-n) : each bands contained in the cell</li>
* </ul>
* @author Quentin Boileau
* @module
*/
public class CoverageToFeaturesProcess extends AbstractProcess {
/**
* Default constructor
*/
public CoverageToFeaturesProcess(final ParameterValueGroup input) {
super(CoverageToFeaturesDescriptor.INSTANCE,input);
}
/**
*
* @param reader source coverage reader
*/
public CoverageToFeaturesProcess(GridCoverageReader reader){
super(CoverageToFeaturesDescriptor.INSTANCE, asParameters(reader));
}
private static ParameterValueGroup asParameters(GridCoverageReader reader){
final ParameterValueGroup params = CoverageToFeaturesDescriptor.INPUT_DESC.createValue();
ParametersExt.getOrCreateValue(params, CoverageToFeaturesDescriptor.READER_IN.getName().getCode()).setValue(reader);
return params;
}
/**
* Execute process now.
*
* @return features
* @throws ProcessException
*/
public FeatureCollection executeNow() throws ProcessException {
execute();
return (FeatureCollection) outputParameters.parameter(CoverageToFeaturesDescriptor.FEATURE_OUT.getName().getCode()).getValue();
}
/**
* {@inheritDoc }
*/
@Override
protected void execute() throws ProcessException{
try {
final GridCoverageReader reader = value(CoverageToFeaturesDescriptor.READER_IN, inputParameters);
final GridCoverage2D coverage = (GridCoverage2D) reader.read(0, null);
final GeneralGridGeometry gridGeom = reader.getGridGeometry(0);
final CoverageToFeatureCollection resultFeatureList =
new CoverageToFeatureCollection(reader, gridGeom.getExtent(), coverage, gridGeom);
getOrCreate(CoverageToFeaturesDescriptor.FEATURE_OUT, outputParameters).setValue(resultFeatureList);
} catch (CoverageStoreException ex) {
throw new ProcessException(ex.getMessage(), this, ex);
}
}
/**
* Create the new FeatureType from the coverage and the reader.
*
* @param coverage
* @param reader
* @return the FeatureType of Features
* @throws CoverageStoreException
*/
static FeatureType createFeatureType(final GridCoverage2D coverage, final GridCoverageReader reader) throws CoverageStoreException {
final int nbBand = coverage.getNumSampleDimensions();
final FeatureTypeBuilder typeBuilder = new FeatureTypeBuilder();
typeBuilder.setName("FeatureCoverage");
typeBuilder.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY);
typeBuilder.addAttribute(Point.class).setName("position").setCRS(reader.getGridGeometry(0).getCoordinateReferenceSystem()).addRole(AttributeRole.DEFAULT_GEOMETRY);
typeBuilder.addAttribute(Polygon.class).setName("cellgeom").setCRS(reader.getGridGeometry(0).getCoordinateReferenceSystem());
typeBuilder.addAttribute(String.class).setName("orientation");
int type = 1;
if (coverage.getSampleDimension(0).getSampleDimensionType() == SampleDimensionType.REAL_32BITS) {
type = 1; //Float
} else if (coverage.getSampleDimension(0).getSampleDimensionType() == SampleDimensionType.REAL_64BITS) {
type = 2; //Double
} else {
type = 3; //Int
}
for (int i = 0; i < nbBand; i++) {
typeBuilder.addAttribute(Double.class).setName("band-" + i);
}
return typeBuilder.build();
}
/**
* Create a Feature with a cell coordinate (x,y).
*
* @param type the FeatureType
* @param x
* @param y
* @param coverage
* @param reader
* @param gridGeom
* @return the cell Feature
* @throws CoverageStoreException
* @throws TransformException
*/
static Feature convertToFeature(FeatureType type, int x, int y, GridCoverage2D coverage, GridCoverageReader reader,
GeneralGridGeometry gridGeom) throws CoverageStoreException, TransformException {
final GeometryFactory geomFac = new GeometryFactory();
//get the number of band contained in a cell
final int nbBand = coverage.getNumSampleDimensions();
final Georectified rep = reader.getCoverageMetadata(0).getInstanceForType(Georectified.class);
//Define the pixel position in cells
PixelInCell posPix = PixelInCell.CELL_CENTER;
final PixelOrientation pixOr = rep.getPointInPixel();
if (pixOr == PixelOrientation.CENTER) {
posPix = PixelInCell.CELL_CENTER;
} else if (pixOr == PixelOrientation.UPPER_LEFT) {
posPix = PixelInCell.CELL_CORNER;
}
//get the MathTransform frome grid to the CRS
final MathTransform transfo = gridGeom.getGridToCRS(posPix);
double[] pt1 = new double[]{x, y};
double[] pt2 = new double[2];
//transform x,y cell coordinate with the gridToCRS MathTransform
transfo.transform(pt1, 0, pt2, 0, 1);
//make a Point2D with transformed coordinates in order to get cell bands
Point2D point2d = new Point2D.Double();
point2d.setLocation(pt2[0], pt2[1]);
double[] infoBand = new double[nbBand];
coverage.evaluate(point2d, infoBand);
double gapX = gridGeom.getResolution()[0];
double gapY = gridGeom.getResolution()[1];
//compute the cell geometry from transform coordinates
Coordinate[] coord;
if (posPix == PixelInCell.CELL_CENTER) {
coord = new Coordinate[]{
new Coordinate(pt2[0] - gapX / 2, pt2[1] + gapY / 2),
new Coordinate(pt2[0] + gapX / 2, pt2[1] + gapY / 2),
new Coordinate(pt2[0] + gapX / 2, pt2[1] - gapY / 2),
new Coordinate(pt2[0] - gapX / 2, pt2[1] - gapY / 2),
new Coordinate(pt2[0] - gapX / 2, pt2[1] + gapY / 2)
};
} else {
coord = new Coordinate[]{
new Coordinate(pt2[0], pt2[1]),
new Coordinate(pt2[0] + gapX, pt2[1]),
new Coordinate(pt2[0] + gapX, pt2[1] - gapY),
new Coordinate(pt2[0], pt2[1] - gapY),
new Coordinate(pt2[0], pt2[1])
};
}
//create the Feature
Feature myfeature = type.newInstance();
myfeature.setPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString(), "id-" + x + "-" + y);
myfeature.setPropertyValue("cellgeom", geomFac.createPolygon(geomFac.createLinearRing(coord), null));
myfeature.setPropertyValue("position", geomFac.createPoint(new Coordinate(pt2[0], pt2[1])));
myfeature.setPropertyValue("orientation", posPix.name());
for (int att = 0; att < nbBand; att++) {
myfeature.setPropertyValue("band-" + att, infoBand[att]);
}
return myfeature;
}
}