/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013, 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.coverage;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.IOException;
import org.apache.sis.geometry.GeneralDirectPosition;
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.io.CoverageReader;
import org.geotoolkit.coverage.io.GridCoverageWriteParam;
import org.geotoolkit.coverage.io.GridCoverageWriter;
import org.geotoolkit.coverage.memory.MPCoverageStore;
import org.geotoolkit.util.NamesExt;
import org.opengis.util.GenericName;
import static org.junit.Assert.*;
import org.geotoolkit.storage.coverage.GridMosaic;
import org.geotoolkit.storage.coverage.Pyramid;
import org.geotoolkit.storage.coverage.PyramidalCoverageReference;
import org.junit.Test;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.util.FactoryException;
import org.apache.sis.referencing.CommonCRS;
/**
* Test pyramid coverage writer.
*
* @author Johann Sorel (Geomatys)
*/
public class PyramidWriterTest extends org.geotoolkit.test.TestBase {
private static final GenericName NAME = NamesExt.create("test");
private static final CoordinateReferenceSystem CRS84 = CommonCRS.WGS84.normalizedGeographic();
private static final CoordinateReferenceSystem EPSG4326 = CommonCRS.WGS84.geographic();
private static final GeneralDirectPosition UL84;
private static final GeneralDirectPosition UL4326;
static {
UL84 = new GeneralDirectPosition(CRS84);
UL84.setOrdinate(0, -180);
UL84.setOrdinate(1, 90);
UL4326 = new GeneralDirectPosition(EPSG4326);
UL4326.setOrdinate(0, -90);
UL4326.setOrdinate(1, 180);
}
/**
* Test writing over an existing mosaic 1x1.
*/
@Test
public void testSingleGridOverride() throws DataStoreException{
final MPCoverageStore store = new MPCoverageStore();
final PyramidalCoverageReference ref = (PyramidalCoverageReference) store.create(NAME);
final Pyramid pyramid = ref.createPyramid(CRS84);
final GridMosaic mosaic = ref.createMosaic(pyramid.getId(), new Dimension(1, 1), new Dimension(360, 180), UL84, 1);
ref.writeTile(pyramid.getId(), mosaic.getId(), 0, 0, createImage(360, 180, Color.BLACK));
//sanity check
CoverageReader reader = ref.acquireReader();
RenderedImage candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 360, 180, Color.BLACK);
//write over the tile
final GridCoverageWriter writer = ref.acquireWriter();
final GridCoverageWriteParam param = new GridCoverageWriteParam();
final GeneralEnvelope env = new GeneralEnvelope(CRS84);
env.setRange(0, -180, +180);
env.setRange(1, -90, +90);
param.setEnvelope(env);
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setGridToCRS(new AffineTransform(1, 0, 0, -1, -180, 90));
gcb.setCoordinateReferenceSystem(CRS84);
gcb.setPixelAnchor(PixelInCell.CELL_CORNER);
gcb.setRenderedImage(createImage(360, 180, Color.RED));
writer.write(gcb.build(), param);
ref.recycle(writer);
//image should be red
reader = ref.acquireReader();
candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 360, 180, Color.RED);
}
/**
* Test writing over an existing mosaic 4x4.
*/
@Test
public void testQuadGridOverride() throws DataStoreException{
final MPCoverageStore store = new MPCoverageStore();
final PyramidalCoverageReference ref = (PyramidalCoverageReference) store.create(NAME);
final Pyramid pyramid = ref.createPyramid(CRS84);
final GridMosaic mosaic = ref.createMosaic(pyramid.getId(), new Dimension(4, 2), new Dimension(9, 9), UL84, 10);
for(int y=0;y<2;y++){
for(int x=0;x<4;x++){
ref.writeTile(pyramid.getId(), mosaic.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
//sanity check
CoverageReader reader = ref.acquireReader();
RenderedImage candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 36, 18, Color.BLACK);
//write over the tile
final GridCoverageWriter writer = ref.acquireWriter();
final GridCoverageWriteParam param = new GridCoverageWriteParam();
final GeneralEnvelope env = new GeneralEnvelope(CRS84);
env.setRange(0, -180, +180);
env.setRange(1, -90, +90);
param.setEnvelope(env);
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setGridToCRS(new AffineTransform(10, 0, 0, -10, -180, 90));
gcb.setCoordinateReferenceSystem(CRS84);
gcb.setPixelAnchor(PixelInCell.CELL_CORNER);
gcb.setRenderedImage(createImage(36, 18, Color.RED));
writer.write(gcb.build(), param);
ref.recycle(writer);
//image should be red
reader = ref.acquireReader();
candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 36, 18, Color.RED);
}
/**
* Test writing only a piece over an existing mosaic 4x2.
*/
@Test
public void testPartialQuadGridOverride() throws DataStoreException{
final MPCoverageStore store = new MPCoverageStore();
final PyramidalCoverageReference ref = (PyramidalCoverageReference) store.create(NAME);
final Pyramid pyramid = ref.createPyramid(CRS84);
final GridMosaic mosaic = ref.createMosaic(pyramid.getId(), new Dimension(4, 2), new Dimension(9, 9), UL84, 10);
for(int y=0;y<2;y++){
for(int x=0;x<4;x++){
ref.writeTile(pyramid.getId(), mosaic.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
//sanity check
CoverageReader reader = ref.acquireReader();
RenderedImage candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 36, 18, Color.BLACK);
//write over the tile
final GridCoverageWriter writer = ref.acquireWriter();
final GridCoverageWriteParam param = new GridCoverageWriteParam();
final GeneralEnvelope env = new GeneralEnvelope(CRS84);
env.setRange(0, -120, +70);
env.setRange(1, -30, +60);
param.setEnvelope(env);
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setGridToCRS(new AffineTransform(10, 0, 0, -10, -120, 60));
gcb.setCoordinateReferenceSystem(CRS84);
gcb.setPixelAnchor(PixelInCell.CELL_CORNER);
gcb.setRenderedImage(createImage(19, 9, Color.RED));
writer.write(gcb.build(), param);
ref.recycle(writer);
//image should be black/red
reader = ref.acquireReader();
candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
final Raster data = candidate.getData();
final int[] black = new int[]{Color.BLACK.getRed(),Color.BLACK.getGreen(),Color.BLACK.getBlue(),Color.BLACK.getAlpha()};
final int[] red = new int[]{Color.RED.getRed(),Color.RED.getGreen(),Color.RED.getBlue(),Color.RED.getAlpha()};
final int[] buffer = new int[4];
for(int y=0;y<18;y++){
for(int x=0;x<36;x++){
data.getPixel(x, y, buffer);
if(x>=6 && x<25 && y>=3 && y<12){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
}
/**
* Test writing only a piece over two existing mosaic 4x2 and 2x1.
*/
@Test
public void testPartialQuadGridOverride2() throws DataStoreException, IOException{
final MPCoverageStore store = new MPCoverageStore();
final PyramidalCoverageReference ref = (PyramidalCoverageReference) store.create(NAME);
final Pyramid pyramid = ref.createPyramid(CRS84);
final GridMosaic mosaic1 = ref.createMosaic(pyramid.getId(), new Dimension(4, 2), new Dimension(9, 9), UL84, 10);
for(int y=0;y<2;y++){
for(int x=0;x<4;x++){
ref.writeTile(pyramid.getId(), mosaic1.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
final GridMosaic mosaic2 = ref.createMosaic(pyramid.getId(), new Dimension(2, 1), new Dimension(9, 9), UL84, 20);
for(int y=0;y<1;y++){
for(int x=0;x<2;x++){
ref.writeTile(pyramid.getId(), mosaic2.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
//sanity check
CoverageReader reader = ref.acquireReader();
RenderedImage candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 36, 18, Color.BLACK);
//write over the tile
final GridCoverageWriter writer = ref.acquireWriter();
final GridCoverageWriteParam param = new GridCoverageWriteParam();
final GeneralEnvelope env = new GeneralEnvelope(CRS84);
env.setRange(0, -120, +70);
env.setRange(1, -30, +60);
param.setEnvelope(env);
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setGridToCRS(new AffineTransform(10, 0, 0, -10, -120, 60));
gcb.setCoordinateReferenceSystem(CRS84);
gcb.setPixelAnchor(PixelInCell.CELL_CORNER);
gcb.setRenderedImage(createImage(19, 9, Color.RED));
writer.write(gcb.build(), param);
ref.recycle(writer);
//lower image should be black/red---------------------------------------
reader = ref.acquireReader();
candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
Raster data = candidate.getData();
final int[] black = new int[]{Color.BLACK.getRed(),Color.BLACK.getGreen(),Color.BLACK.getBlue(),Color.BLACK.getAlpha()};
final int[] red = new int[]{Color.RED.getRed(),Color.RED.getGreen(),Color.RED.getBlue(),Color.RED.getAlpha()};
final int[] buffer = new int[4];
for(int y=0;y<18;y++){
for(int x=0;x<36;x++){
data.getPixel(x, y, buffer);
if(x>=6 && x<25 && y>=3 && y<12){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
//the higher tiles------------------------------------------------------
final BufferedImage top1 = mosaic2.getTile(0, 0, null).getImageReader().read(0);
final BufferedImage top2 = mosaic2.getTile(1, 0, null).getImageReader().read(0);
data = top1.getData();
for(int y=0;y<9;y++){
for(int x=0;x<9;x++){
data.getPixel(x, y, buffer);
if(x>=3 && y>=2 && y<6){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
data = top2.getData();
for(int y=0;y<9;y++){
for(int x=0;x<9;x++){
data.getPixel(x, y, buffer);
if(x>=0 & x<3 && y>=2 && y<6){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
}
/**
* Test writing only a piece over two existing mosaic 4x2 and 2x1.
* Source coverage in not in same crs (axis flip)
* CRS:84 > EPSG:4326
*/
@Test
public void testPartialQuadGridOverrideFlip() throws DataStoreException, IOException, NoSuchAuthorityCodeException, FactoryException{
final MPCoverageStore store = new MPCoverageStore();
final PyramidalCoverageReference ref = (PyramidalCoverageReference) store.create(NAME);
final Pyramid pyramid = ref.createPyramid(EPSG4326);
final GridMosaic mosaic1 = ref.createMosaic(pyramid.getId(), new Dimension(2, 4), new Dimension(9, 9), UL4326, 10);
for(int y=0;y<4;y++){
for(int x=0;x<2;x++){
ref.writeTile(pyramid.getId(), mosaic1.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
final GridMosaic mosaic2 = ref.createMosaic(pyramid.getId(), new Dimension(1, 2), new Dimension(9, 9), UL4326, 20);
for(int y=0;y<2;y++){
for(int x=0;x<1;x++){
ref.writeTile(pyramid.getId(), mosaic2.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
//sanity check
CoverageReader reader = ref.acquireReader();
RenderedImage candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 18, 36, Color.BLACK);
//write over the tile
final GridCoverageWriter writer = ref.acquireWriter();
final GridCoverageWriteParam param = new GridCoverageWriteParam();
final GeneralEnvelope env = new GeneralEnvelope(CRS84);
env.setRange(0, -120, +70);
env.setRange(1, -30, +60);
param.setEnvelope(env);
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setGridToCRS(new AffineTransform(10, 0, 0, -10, -120, 60));
gcb.setCoordinateReferenceSystem(CRS84);
gcb.setPixelAnchor(PixelInCell.CELL_CORNER);
gcb.setRenderedImage(createImage(19, 9, Color.RED));
writer.write(gcb.build(), param);
ref.recycle(writer);
//lower image should be black/red---------------------------------------
reader = ref.acquireReader();
candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
Raster data = candidate.getData();
final int[] black = new int[]{Color.BLACK.getRed(),Color.BLACK.getGreen(),Color.BLACK.getBlue(),Color.BLACK.getAlpha()};
final int[] red = new int[]{Color.RED.getRed(),Color.RED.getGreen(),Color.RED.getBlue(),Color.RED.getAlpha()};
final int[] buffer = new int[4];
for(int y=0;y<36;y++){
for(int x=0;x<18;x++){
data.getPixel(x, y, buffer);
if(y>=11 && y<30 && x>=6 && x<15){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
//the higher tiles------------------------------------------------------
final BufferedImage top1 = mosaic2.getTile(0, 0, null).getImageReader().read(0);
final BufferedImage top2 = mosaic2.getTile(0, 1, null).getImageReader().read(0);
data = top1.getData();
for(int y=0;y<9;y++){
for(int x=0;x<9;x++){
data.getPixel(x, y, buffer);
if(y>=6 && x>=3 && x<7){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
data = top2.getData();
for(int y=0;y<9;y++){
for(int x=0;x<9;x++){
data.getPixel(x, y, buffer);
if(y>=0 & y<6 && x>=3 && x<7){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
}
/**
* Test writing only a piece over two existing mosaic 4x2 and 2x1.
* Source coverage in not in same crs (axis flip)
* EPSG:4326 > CRS:84
*/
@Test
public void testPartialQuadGridOverrideFlip2() throws DataStoreException, IOException, NoSuchAuthorityCodeException, FactoryException{
final MPCoverageStore store = new MPCoverageStore();
final PyramidalCoverageReference ref = (PyramidalCoverageReference) store.create(NAME);
final Pyramid pyramid = ref.createPyramid(CRS84);
final GridMosaic mosaic1 = ref.createMosaic(pyramid.getId(), new Dimension(4, 2), new Dimension(9, 9), UL84, 10);
for(int y=0;y<2;y++){
for(int x=0;x<4;x++){
ref.writeTile(pyramid.getId(), mosaic1.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
final GridMosaic mosaic2 = ref.createMosaic(pyramid.getId(), new Dimension(2, 1), new Dimension(9, 9), UL84, 20);
for(int y=0;y<1;y++){
for(int x=0;x<2;x++){
ref.writeTile(pyramid.getId(), mosaic2.getId(), x, y, createImage(9, 9, Color.BLACK));
}
}
//sanity check
CoverageReader reader = ref.acquireReader();
RenderedImage candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
testImage(candidate, 36, 18, Color.BLACK);
//write over the tile
final GridCoverageWriter writer = ref.acquireWriter();
final GridCoverageWriteParam param = new GridCoverageWriteParam();
final GeneralEnvelope env = new GeneralEnvelope(EPSG4326);
env.setRange(0, -30, +60);
env.setRange(1, -120, +70);
param.setEnvelope(env);
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setGridToCRS(new AffineTransform(-10, 0, 0, 10, 60,-120));
gcb.setCoordinateReferenceSystem(EPSG4326);
gcb.setPixelAnchor(PixelInCell.CELL_CORNER);
gcb.setRenderedImage(createImage(9, 19, Color.RED));
writer.write(gcb.build(), param);
ref.recycle(writer);
//lower image should be black/red---------------------------------------
reader = ref.acquireReader();
candidate = ((GridCoverage2D)reader.read(0, null)).getRenderedImage();
ref.recycle(reader);
Raster data = candidate.getData();
final int[] black = new int[]{Color.BLACK.getRed(),Color.BLACK.getGreen(),Color.BLACK.getBlue(),Color.BLACK.getAlpha()};
final int[] red = new int[]{Color.RED.getRed(),Color.RED.getGreen(),Color.RED.getBlue(),Color.RED.getAlpha()};
final int[] buffer = new int[4];
for(int y=0;y<18;y++){
for(int x=0;x<36;x++){
data.getPixel(x, y, buffer);
if(x>=6 && x<25 && y>=3 && y<12){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
//the higher tiles------------------------------------------------------
final BufferedImage top1 = mosaic2.getTile(0, 0, null).getImageReader().read(0);
final BufferedImage top2 = mosaic2.getTile(1, 0, null).getImageReader().read(0);
data = top1.getData();
for(int y=0;y<9;y++){
for(int x=0;x<9;x++){
data.getPixel(x, y, buffer);
if(x>=3 && y>=2 && y<6){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
data = top2.getData();
for(int y=0;y<9;y++){
for(int x=0;x<9;x++){
data.getPixel(x, y, buffer);
if(x>=0 & x<3 && y>=2 && y<6){
assertArrayEquals(""+y+" "+x,red, buffer);
}else{
assertArrayEquals(""+y+" "+x,black, buffer);
}
}
}
}
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 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;
}
}