/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2011 - 2012, 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 org.opengis.util.GenericName;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CancellationException;
import org.apache.sis.feature.builder.AttributeRole;
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.apache.sis.internal.feature.AttributeConvention;
import org.geotoolkit.coverage.GridSampleDimension;
import org.geotoolkit.coverage.grid.GeneralGridGeometry;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.grid.GridCoverageBuilder;
import org.geotoolkit.coverage.io.CoverageStoreException;
import org.geotoolkit.coverage.io.GridCoverageReadParam;
import org.geotoolkit.coverage.io.GridCoverageReader;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.image.io.metadata.ReferencingBuilder;
import org.geotoolkit.image.io.metadata.SpatialMetadata;
import org.geotoolkit.image.io.metadata.SpatialMetadataFormat;
import org.geotoolkit.process.ProcessDescriptor;
import org.geotoolkit.process.ProcessException;
import org.geotoolkit.process.ProcessFinder;
import org.geotoolkit.process.Process;
import org.apache.sis.referencing.CRS;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.geotoolkit.internal.image.io.GridDomainAccessor;
import org.geotoolkit.processing.coverage.AbstractProcessTest;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.util.FactoryException;
import org.opengis.metadata.spatial.CellGeometry;
import org.opengis.referencing.datum.PixelInCell;
import org.junit.Test;
import static org.junit.Assert.*;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureType;
import org.opengis.feature.PropertyType;
/**
* Junit test for CoverageToFeature process
* @author Quentin Boileau
* @module
*/
public class CoverageToFeatureTest extends AbstractProcessTest {
private static final int max = 5; /* Define the number of row and columns of the generated coverage */
public CoverageToFeatureTest() {
super(CoverageToFeaturesDescriptor.NAME);
}
/**
* Test coverageToFeature process with a PixelInCell.CELL_CENTER coverage
* @throws NoSuchAuthorityCodeException
* @throws FactoryException
*/
@Test
public void coverageToFeatureTestPixelCenter() throws NoSuchAuthorityCodeException, FactoryException, ProcessException {
Hints.putSystemDefault(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE);
final PixelInCell pixPos = PixelInCell.CELL_CENTER;
final GridCoverageReader reader = buildReader(pixPos);
// Process
final ProcessDescriptor desc = ProcessFinder.getProcessDescriptor("coverage", "coveragetofeatures");
final ParameterValueGroup in = desc.getInputDescriptor().createValue();
in.parameter("reader_in").setValue(reader);
final Process proc = desc.createProcess(in);
//Features out
final Collection<Feature> featureListOut = (Collection<Feature>) proc.call().parameter("feature_out").getValue();
final List<Feature> featureListResult = (List<Feature>) buildFCResultPixelCenter();
assertEquals(featureListResult.get(0).getType(), featureListOut.iterator().next().getType());
assertEquals(featureListOut.size(), featureListResult.size());
final Iterator<Feature> iteratorOut = featureListOut.iterator();
final Iterator<Feature> iteratorResult = featureListResult.iterator();
final ArrayList<Geometry> geomsOut = new ArrayList<>();
int itOut = 0;
while (iteratorOut.hasNext()) {
Feature featureOut = iteratorOut.next();
geomsOut.add((Geometry) featureOut.getPropertyValue("cellgeom"));
geomsOut.add((Geometry) featureOut.getPropertyValue("position"));
}
final ArrayList<Geometry> geomsResult = new ArrayList<>();
int itResult = 0;
while (iteratorResult.hasNext()) {
Feature featureResult = iteratorResult.next();
geomsResult.add((Geometry) featureResult.getPropertyValue("cellgeom"));
geomsResult.add((Geometry) featureResult.getPropertyValue("position"));
}
assertEquals(geomsResult.size(), geomsOut.size());
for (int i = 0; i < geomsResult.size(); i++) {
Geometry gOut = geomsOut.get(i);
Geometry gResult = geomsResult.get(i);
assertArrayEquals(gResult.getCoordinates(), gOut.getCoordinates());
}
}
/**
* Test coverageToFeature process with a PixelInCell.CELL_CORNER coverage
* @throws NoSuchAuthorityCodeException
* @throws FactoryException
*/
@Test
public void coverageToFeatureTestPixelCorner() throws NoSuchAuthorityCodeException, FactoryException, ProcessException {
Hints.putSystemDefault(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE);
final PixelInCell pixPos = PixelInCell.CELL_CORNER;
final GridCoverageReader reader = buildReader(pixPos);
// Process
final ProcessDescriptor desc = ProcessFinder.getProcessDescriptor("coverage", "coveragetofeatures");
final ParameterValueGroup in = desc.getInputDescriptor().createValue();
in.parameter("reader_in").setValue(reader);
final Process proc = desc.createProcess(in);
//Features out
final Collection<Feature> featureListOut = (Collection<Feature>) proc.call().parameter("feature_out").getValue();
final List<Feature> featureListResult = (List<Feature>) buildFCResultPixelCorner();
assertEquals(featureListResult.get(0).getType(), featureListOut.iterator().next().getType());
assertEquals(featureListOut.size(), featureListResult.size());
final Iterator<Feature> iteratorOut = featureListOut.iterator();
final Iterator<Feature> iteratorResult = featureListResult.iterator();
final ArrayList<Geometry> geomsOut = new ArrayList<>();
int itOut = 0;
while (iteratorOut.hasNext()) {
Feature featureOut = iteratorOut.next();
geomsOut.add((Geometry) featureOut.getPropertyValue("cellgeom"));
geomsOut.add((Geometry) featureOut.getPropertyValue("position"));
}
final ArrayList<Geometry> geomsResult = new ArrayList<>();
int itResult = 0;
while (iteratorResult.hasNext()) {
Feature featureResult = iteratorResult.next();
geomsResult.add((Geometry) featureResult.getPropertyValue("cellgeom"));
geomsResult.add((Geometry) featureResult.getPropertyValue("position"));
}
assertEquals(geomsResult.size(), geomsOut.size());
for (int i = 0; i < geomsResult.size(); i++) {
Geometry gOut = geomsOut.get(i);
Geometry gResult = geomsResult.get(i);
assertArrayEquals(gResult.getCoordinates(), gOut.getCoordinates());
}
}
private GridCoverageReader buildReader(PixelInCell pixPos) throws NoSuchAuthorityCodeException, FactoryException {
final BufferedImage image = new BufferedImage(max, max, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < max; y++) {
for (int x = 0; x < max; x++) {
int val = x << 8;
image.setRGB(x, y, val + y);
}
}
final CoordinateReferenceSystem crs2d = CRS.forCode("EPSG:3395");
final AffineTransform2D gridToCRS;
if (pixPos == PixelInCell.CELL_CENTER) {
gridToCRS = new AffineTransform2D(1, 0, 0, 1, 0.5, 0.5);
} else {
gridToCRS = new AffineTransform2D(1, 0, 0, 1, 0.5, 1.5);
}
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setCoordinateReferenceSystem(crs2d);
gcb.setGridToCRS((AffineTransform)gridToCRS);
gcb.setRenderedImage(image);
return new SimpleCoverageReader(gcb.getGridCoverage2D(), pixPos);
}
private FeatureType buildFeatureType() throws NoSuchAuthorityCodeException, FactoryException {
final FeatureTypeBuilder typeBuilder = new FeatureTypeBuilder();
typeBuilder.setName("FeatureCoverage");
typeBuilder.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY);
typeBuilder.addAttribute(Point.class).setName("position").setCRS(CRS.forCode("EPSG:3395")).addRole(AttributeRole.DEFAULT_GEOMETRY);
typeBuilder.addAttribute(Polygon.class).setName("cellgeom").setCRS(CRS.forCode("EPSG:3395"));
typeBuilder.addAttribute(String.class).setName("orientation");
for (int i = 0; i < 3; i++) {
typeBuilder.addAttribute(Double.class).setName("band-" + i);
}
return typeBuilder.build();
}
private List<Feature> buildFCResultPixelCenter() throws NoSuchAuthorityCodeException, FactoryException {
final FeatureType type = buildFeatureType();
final List<Feature> featureList = new ArrayList<>();
final GeometryFactory geometryFactory = new GeometryFactory();
double buffX = 0;
double buffY = 0;
for (int y = 0; y < max; y++) {
for (int x = 0; x < max; x++) {
buffX = x + 0.5;
buffY = y + 0.5;
Point pos = geometryFactory.createPoint(new Coordinate(buffX, buffY));
LinearRing line = geometryFactory.createLinearRing(new Coordinate[]{
new Coordinate(buffX - 0.5, buffY + 0.5),
new Coordinate(buffX + 0.5, buffY + 0.5),
new Coordinate(buffX + 0.5, buffY - 0.5),
new Coordinate(buffX - 0.5, buffY - 0.5),
new Coordinate(buffX - 0.5, buffY + 0.5)
});
Feature myfeature = type.newInstance();
myfeature.setPropertyValue("@identifier", "id-" + x + "-" + y);
myfeature.setPropertyValue("cellgeom",geometryFactory.createPolygon(line, null));
myfeature.setPropertyValue("position",pos);
myfeature.setPropertyValue("orientation",PixelInCell.CELL_CENTER.name());
myfeature.setPropertyValue("band-0",0.0);
myfeature.setPropertyValue("band-1",(double)x);
myfeature.setPropertyValue("band-2",(double)y);
featureList.add((x + (y * max)), myfeature);
}
}
return featureList;
}
private List<Feature> buildFCResultPixelCorner() throws NoSuchAuthorityCodeException, FactoryException {
final FeatureType type = buildFeatureType();
final List<Feature> featureList = new ArrayList<Feature>();
final GeometryFactory geometryFactory = new GeometryFactory();
double buffX = 0;
double buffY = 0;
for (int y = 0; y < max; y++) {
for (int x = 0; x < max; x++) {
buffX = x;
buffY = y + 1.0;
Point pos = geometryFactory.createPoint(new Coordinate(buffX, buffY));
LinearRing line = geometryFactory.createLinearRing(new Coordinate[]{
new Coordinate(buffX, buffY),
new Coordinate(buffX + 1.0, buffY),
new Coordinate(buffX + 1.0, buffY - 1.0),
new Coordinate(buffX, buffY - 1.0),
new Coordinate(buffX, buffY)
});
Feature myfeature = type.newInstance();
myfeature.setPropertyValue("@identifier", "id-" + x + "-" + y);
myfeature.setPropertyValue("cellgeom",geometryFactory.createPolygon(line, null));
myfeature.setPropertyValue("position",pos);
myfeature.setPropertyValue("orientation",PixelInCell.CELL_CENTER.name());
myfeature.setPropertyValue("band-0",0.0);
myfeature.setPropertyValue("band-1",(double)x);
myfeature.setPropertyValue("band-2",(double)y);
featureList.add((x + (y * max)), myfeature);
}
}
return featureList;
}
private static class SimpleCoverageReader extends GridCoverageReader {
private final GridCoverage2D coverage;
private final PixelInCell pixPos;
public SimpleCoverageReader(final GridCoverage2D coverage, PixelInCell pixPos) {
this.coverage = coverage;
this.pixPos = pixPos;
}
@Override
public List<? extends GenericName> getCoverageNames() throws CoverageStoreException, CancellationException {
return Collections.emptyList();
}
@Override
public GeneralGridGeometry getGridGeometry(final int i) throws CoverageStoreException, CancellationException {
return (GeneralGridGeometry) coverage.getGridGeometry();
}
@Override
public List<GridSampleDimension> getSampleDimensions(final int i) throws CoverageStoreException, CancellationException {
return Collections.singletonList(coverage.getSampleDimension(i));
}
@Override
public GridCoverage read(final int i, final GridCoverageReadParam gcrp) throws CoverageStoreException, CancellationException {
return coverage;
}
@Override
public SpatialMetadata getCoverageMetadata(int i) throws CoverageStoreException {
SpatialMetadata meta = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(SpatialMetadataFormat.GEOTK_FORMAT_NAME));
GridDomainAccessor grid = new GridDomainAccessor(meta);
grid.setGridGeometry(coverage.getGridGeometry(), pixPos, CellGeometry.POINT, -1);
ReferencingBuilder ref = new ReferencingBuilder(meta);
ref.setCoordinateReferenceSystem(coverage.getCoordinateReferenceSystem());
return meta;
}
}
}