package org.geotoolkit.coverage;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.grid.GridCoverageBuilder;
import org.geotoolkit.coverage.memory.MPCoverageStore;
import org.geotoolkit.coverage.memory.MemoryCoverageStore;
import org.geotoolkit.util.NamesExt;
import org.opengis.util.GenericName;
import org.geotoolkit.image.interpolation.InterpolationCase;
import org.geotoolkit.storage.coverage.*;
import org.junit.Test;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
//import java.io.File;//-- debug
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//import javax.imageio.ImageIO;//-- debug
import org.opengis.referencing.datum.PixelInCell;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.util.Utilities;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Test pyramid generation with PyramidCoverageBuilder.
* TODO add more tests
*
* @author Quentin Boileau (Geomatys)
*/
public class PyramidCoverageBuilderTest extends org.geotoolkit.test.TestBase {
private static final CoordinateReferenceSystem EPSG4326 = CommonCRS.WGS84.geographic();
@Test
public void testPyramid () throws DataStoreException, TransformException, IOException, FactoryException {
GeneralEnvelope env1 = new GeneralEnvelope(EPSG4326);
env1.setRange(0, 0, +20);
env1.setRange(1, 0, +20);
AffineTransform af = new AffineTransform(0.1, 0, 0, -0.1, 0, 20);
CoverageReference ref1 = createCoverage("cov1", env1, af, createImage(200, 200, Color.RED));
final MPCoverageStore mpCovStore = new MPCoverageStore();
final PyramidCoverageBuilder pcb = new PyramidCoverageBuilder(new Dimension(100, 100), InterpolationCase.NEIGHBOR, 2);
final double[] fillValue = new double[4];
final double[] scales = new double[]{0.1};
final Map<Envelope, double[]> map = new HashMap<>();
map.put(env1, scales);
final GenericName name = NamesExt.create("memory_store_test");
pcb.create(ref1, mpCovStore, name, map, fillValue, null, null);
//test reference
CoverageReference outRef = mpCovStore.getCoverageReference(name);
assertNotNull(outRef);
assertTrue(outRef instanceof AbstractPyramidalCoverageReference);
AbstractPyramidalCoverageReference outRefPy = (AbstractPyramidalCoverageReference) outRef;
//test pyramids
PyramidSet pyramidSet = outRefPy.getPyramidSet();
assertNotNull(pyramidSet);
Collection<Pyramid> pyramids = pyramidSet.getPyramids();
assertEquals(1, pyramids.size());
//test pyramid
Pyramid pyramid = pyramids.iterator().next();
assertTrue(Utilities.equalsIgnoreMetadata(EPSG4326, pyramid.getCoordinateReferenceSystem()));
assertArrayEquals(scales, pyramid.getScales(), 0.0000001);
//test mosaics
List<GridMosaic> mosaics = pyramid.getMosaics();
assertEquals(1, mosaics.size());
GridMosaic gridMosaic = mosaics.get(0);
assertEquals(new Dimension(2,2), gridMosaic.getGridSize());
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 2; x++) {
TileReference tile = gridMosaic.getTile(x, y, null);
RenderedImage img = (RenderedImage) tile.getInput();
testImage(img, 100, 100, Color.RED);
}
}
}
/**
* Test overlaps coverages.
* Try to write two overlapping coverage in the same pyramid using PyramidCoverageBuilder with
* reuseTile parameter at true.
* The result tiles should contain both data from the first coverage (Red) and th second coverage (Blue).
*/
@Test
public void testOverlaps () throws DataStoreException, TransformException, IOException, FactoryException {
int tileSize = 100;
GeneralEnvelope env1 = new GeneralEnvelope(EPSG4326);
env1.setRange(0, 0, +20);
env1.setRange(1, 0, +20);
AffineTransform af1 = new AffineTransform(0.1, 0, 0, -0.1, 0, 20);
CoverageReference ref1 = createCoverage("cov1", env1, af1, createImage(200, 200, Color.RED));
GeneralEnvelope env2 = new GeneralEnvelope(EPSG4326);
env2.setRange(0, +10, +30);
env2.setRange(1, +10, +30);
AffineTransform af2 = new AffineTransform(0.1, 0, 0, -0.1, +10, +30);
CoverageReference ref2 = createCoverage("cov2", env2, af2, createImage(200, 200, Color.BLUE));
/*
+-------+
| |
+----+ Blue |
| | |
| +---+---+
| Red |
+--------+
*/
final MPCoverageStore mpCovStore = new MPCoverageStore();
final PyramidCoverageBuilder pcb = new PyramidCoverageBuilder(new Dimension(tileSize, tileSize), InterpolationCase.NEIGHBOR, 2, true);
final GeneralEnvelope env = new GeneralEnvelope(EPSG4326);
env.setRange(0, 0, +30);
env.setRange(1, 0, +30);
final double[] fillValue = new double[4];
final double[] scales = new double[]{0.15};
final Map<Envelope, double[]> map = new HashMap<>();
map.put(env, scales);
final GenericName name = NamesExt.create("memory_store_test");
//pyramid 1st coverage
pcb.create(ref1, mpCovStore, name, map, fillValue, null, null);
//append 2nd coverage
pcb.create(ref2, mpCovStore, name, map, fillValue, null, null);
//test reference
CoverageReference outRef = mpCovStore.getCoverageReference(name);
assertNotNull(outRef);
assertTrue(outRef instanceof AbstractPyramidalCoverageReference);
AbstractPyramidalCoverageReference outRefPy = (AbstractPyramidalCoverageReference) outRef;
//test pyramids
PyramidSet pyramidSet = outRefPy.getPyramidSet();
assertNotNull(pyramidSet);
Collection<Pyramid> pyramids = pyramidSet.getPyramids();
assertEquals(1, pyramids.size());
//test pyramid
Pyramid pyramid = pyramids.iterator().next();
assertTrue(Utilities.equalsIgnoreMetadata(EPSG4326, pyramid.getCoordinateReferenceSystem()));
assertArrayEquals(scales, pyramid.getScales(), 0.0000001);
//test mosaics
List<GridMosaic> mosaics = pyramid.getMosaics();
assertEquals(1, mosaics.size());
GridMosaic gridMosaic = mosaics.get(0);
System.out.println(gridMosaic.getEnvelope());
assertEquals(new Dimension(2,2), gridMosaic.getGridSize());
//test tile 0x0
/*
+---+
| |
+-----+ B |
| R | |
+-----+---+
*/
TileReference tile = gridMosaic.getTile(0, 0, null);
RenderedImage tileImg = (RenderedImage) tile.getInput();
BufferedImage expectedImage = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = expectedImage.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 67, tileSize, tileSize);
g.setColor(Color.BLUE);
g.fillRect(67, 0, tileSize, tileSize);
// ImageIO.write(tileImg, "png", new File("/myPath/0_0.png"));//-- debug
testImage(tileImg, expectedImage, 0);
//test tile 0x1
/*
+-----+---+
| | B |
| R +---|
| |
+---------+
*/
tile = gridMosaic.getTile(0, 1, null);
tileImg = (RenderedImage) tile.getInput();
expectedImage = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_INT_ARGB);
g = expectedImage.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, tileSize, tileSize);
g.setColor(Color.BLUE);
g.fillRect(67, 0, tileSize, 33);
// ImageIO.write(tileImg, "png", new File("/myPath/0_1.png"));//-- debug
testImage(tileImg, expectedImage, 1);
//test tile 1x0
/*
+---------+
| |
| B |
| |
+---------+
*/
tile = gridMosaic.getTile(1, 0, null);
tileImg = (RenderedImage) tile.getInput();
// ImageIO.write(tileImg, "png", new File("/myPath/1_0.png"));//-- debug
testImage(tileImg, tileSize, tileSize, Color.BLUE);
//test tile 1x1
/*
+---------+
| B |
+---+-----|
| R | |
+---------+
*/
tile = gridMosaic.getTile(1, 1, null);
tileImg = (RenderedImage) tile.getInput();
expectedImage = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_INT_ARGB);
g = expectedImage.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, 33, tileSize);
g.setColor(Color.BLUE);
g.fillRect(0, 0, tileSize, 33);
// ImageIO.write(tileImg, "png", new File("/myPath/1_1.png"));//-- debug
testImage(tileImg, expectedImage, 3);
}
@Test
public void testAppendImageWithDifferentSamples () throws DataStoreException, TransformException, IOException, FactoryException {
int tileSize = 100;
GeneralEnvelope env1 = new GeneralEnvelope(EPSG4326);
env1.setRange(0, 0, +20);
env1.setRange(1, 0, +20);
AffineTransform af1 = new AffineTransform(0.1, 0, 0, -0.1, 0, 20);
CoverageReference ref1 = createCoverage("cov1", env1, af1, createImage(200, 200, Color.RED));
GeneralEnvelope env2 = new GeneralEnvelope(EPSG4326);
env2.setRange(0, +10, +30);
env2.setRange(1, +10, +30);
AffineTransform af2 = new AffineTransform(0.1, 0, 0, -0.1, +10, +30);
CoverageReference ref2 = createCoverage("cov2", env2, af2, createImage1Band(200, 200));
final MPCoverageStore mpCovStore = new MPCoverageStore();
final PyramidCoverageBuilder pcb = new PyramidCoverageBuilder(new Dimension(tileSize, tileSize), InterpolationCase.NEIGHBOR, 2, true);
final GeneralEnvelope env = new GeneralEnvelope(EPSG4326);
env.setRange(0, 0, +30);
env.setRange(1, 0, +30);
final double[] fillValue = new double[4];
final double[] scales = new double[]{0.15};
final Map<Envelope, double[]> map = new HashMap<>();
map.put(env, scales);
final GenericName name = NamesExt.create("memory_store_test");
//pyramid 1st coverage
pcb.create(ref1, mpCovStore, name, map, fillValue, null, null);
//append 2nd coverage should fail
try {
pcb.create(ref2, mpCovStore, name, map, fillValue, null, null);
fail("Append of coverage with different sample dimension should have failed.");
} catch (DataStoreException e) {
//test success
}
}
private CoverageReference createCoverage(String name, GeneralEnvelope env, AffineTransform gridToCRS, RenderedImage image) throws DataStoreException {
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setName(name);
gcb.setEnvelope(env);
gcb.setPixelAnchor(PixelInCell.CELL_CORNER);
gcb.setGridToCRS(gridToCRS);
gcb.setRenderedImage(image);
final GridCoverage2D coverage = gcb.getGridCoverage2D();
final MemoryCoverageStore store = new MemoryCoverageStore(coverage);
return store.getCoverageReference(store.getNames().iterator().next());
}
private static void testImage(RenderedImage img, int width, int height, Color fill){
assertNotNull(img);
assertEquals(img.getWidth(), width);
assertEquals(img.getHeight(), height);
final int[] color = new int[]{fill.getRed(),fill.getGreen(),fill.getBlue(),fill.getAlpha()};
final int[] buffer = new int[4];
final Raster data = img.getData();
for(int y=0;y<height;y++){
for(int x=0;x<width;x++){
data.getPixel(x, y, buffer);
assertArrayEquals(color, buffer);
}
}
}
private static void testImage(RenderedImage candidateImg, RenderedImage expectedImage, int nb) {
assertNotNull(candidateImg);
assertEquals(expectedImage.getWidth(), candidateImg.getWidth());
assertEquals(expectedImage.getHeight(), candidateImg.getHeight());
final int[] expectedBuf = new int[4];
final int[] candidateBuf = new int[4];
final Raster expectedData = expectedImage.getData();
final Raster candidateData = candidateImg.getData();
for(int y = 0; y < expectedImage.getHeight(); y++){
for(int x = 0; x < expectedImage.getWidth(); x++){
expectedData.getPixel(x, y, expectedBuf);
candidateData.getPixel(x, y, candidateBuf);
assertArrayEquals(expectedBuf, candidateBuf);
}
}
}
private static BufferedImage createImage1Band(int width, int height){
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
final Graphics2D g = image.createGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
return image;
}
private static BufferedImage createImage(int width, int height, Color color){
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g = image.createGraphics();
g.setColor(color);
g.fillRect(0, 0, width, height);
return image;
}
}