/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2008, 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.gce.imagemosaic.jdbc;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.image.*;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.util.logging.Logging;
/**
* This class reads decoded tiles from the queue and performs the mosaicing and
* scaling
*
* @author mcr
*
*
* @source $URL$
*/
public class ImageComposerThread extends AbstractThread {
/** Logger. */
protected final static Logger LOGGER = Logging
.getLogger(ImageComposerThread.class.getPackage().getName());
protected GridCoverageFactory coverageFactory;
private GridCoverage2D gridCoverage2D;
private Color outputTransparentColor,backgroundColor;
private boolean xAxisSwitch;
public ImageComposerThread(Color backgroundColor,Color outputTransparentColor, Rectangle pixelDimension,
GeneralEnvelope requestEnvelope, ImageLevelInfo levelInfo,
LinkedBlockingQueue<TileQueueElement> tileQueue, Config config,
boolean xAxisSwitch, GridCoverageFactory coverageFactory) {
super(pixelDimension, requestEnvelope, levelInfo, tileQueue, config);
this.outputTransparentColor = outputTransparentColor;
this.backgroundColor = backgroundColor;
this.xAxisSwitch = xAxisSwitch;
this.coverageFactory = coverageFactory;
}
private Dimension getStartDimension() {
double width;
double height;
width = pixelDimension.getWidth() / rescaleX;
height = pixelDimension.getHeight() / rescaleY;
return new Dimension((int) Math.round(width), (int) Math.round(height));
}
private BufferedImage getStartImage(BufferedImage copyFrom) {
Dimension dim = getStartDimension();
Hashtable<String,Object> properties = null;
if (copyFrom.getPropertyNames()!=null) {
properties = new Hashtable<String, Object>();
for (String name : copyFrom.getPropertyNames()) {
properties.put(name, copyFrom.getProperty(name));
}
}
SampleModel sm = copyFrom.getSampleModel().createCompatibleSampleModel((int)dim.getWidth(), (int) dim.getHeight());
WritableRaster raster = Raster.createWritableRaster(sm, null);
ColorModel colorModel = copyFrom.getColorModel();
boolean alphaPremultiplied = copyFrom.isAlphaPremultiplied();
DataBuffer dataBuffer = createDataBufferFilledWithNoDataValues(raster, colorModel.getPixelSize());
raster = Raster.createWritableRaster(sm, dataBuffer, null);
BufferedImage image= new BufferedImage(colorModel, raster, alphaPremultiplied, properties);
if (levelInfo.getNoDataValue()==null) {
Graphics2D g2D = (Graphics2D) image.getGraphics();
Color save = g2D.getColor();
g2D.setColor(backgroundColor);
g2D.fillRect(0, 0, image.getWidth(), image.getHeight());
g2D.setColor(save);
}
return image;
}
private BufferedImage getStartImage(int imageType) {
Dimension dim = getStartDimension();
if (imageType == BufferedImage.TYPE_CUSTOM)
imageType = ImageMosaicJDBCReader.DEFAULT_IMAGE_TYPE;
BufferedImage image = new BufferedImage((int) dim.getWidth(), (int) dim
.getHeight(), imageType);
Graphics2D g2D = (Graphics2D) image.getGraphics();
Color save = g2D.getColor();
g2D.setColor(backgroundColor);
g2D.fillRect(0, 0, image.getWidth(), image.getHeight());
g2D.setColor(save);
return image;
}
@Override
public void run() {
BufferedImage image = null;
TileQueueElement queueObject = null;
try {
while ((queueObject = tileQueue.take()).isEndElement() == false) {
if (image == null) {
image = getStartImage(queueObject.getTileImage());
}
int posx = (int) ((queueObject.getEnvelope().getMinimum(0) - requestEnvelope
.getMinimum(0)) / levelInfo.getResX());
int posy = (int) ((requestEnvelope.getMaximum(1) - queueObject
.getEnvelope().getMaximum(1)) / levelInfo.getResY());
image.getRaster().setRect(posx, posy, queueObject.getTileImage().getRaster());
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (image == null) // no tiles ??
image = getStartImage(ImageMosaicJDBCReader.DEFAULT_IMAGE_TYPE);
GeneralEnvelope resultEnvelope = null;
if (xAxisSwitch) {
Rectangle2D tmp = new Rectangle2D.Double(requestEnvelope
.getMinimum(1), requestEnvelope.getMinimum(0),
requestEnvelope.getSpan(1), requestEnvelope.getSpan(0));
resultEnvelope = new GeneralEnvelope(tmp);
resultEnvelope.setCoordinateReferenceSystem(requestEnvelope
.getCoordinateReferenceSystem());
} else {
resultEnvelope = requestEnvelope;
}
image = rescaleImageViaPlanarImage(image);
if (outputTransparentColor == null)
gridCoverage2D= coverageFactory.create(config.getCoverageName(),
image, resultEnvelope);
else {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine("Support for alpha on final mosaic");
RenderedImage result = ImageUtilities.maskColor(outputTransparentColor,image);
gridCoverage2D = coverageFactory.create(config.getCoverageName(),
result, resultEnvelope);
}
}
GridCoverage2D getGridCoverage2D() {
return gridCoverage2D;
}
private DataBuffer createDataBufferFilledWithNoDataValues(WritableRaster raster, int pixelSize) {
int dataType = raster.getDataBuffer().getDataType();
Number noDataValue = levelInfo.getNoDataValue();
int dataBufferSize = raster.getDataBuffer().getSize();
int nrBanks = raster.getDataBuffer().getNumBanks();
DataBuffer dataBuffer;
switch (dataType) {
case DataBuffer.TYPE_INT:
int[][] intDataArray = new int[nrBanks][dataBufferSize];
if (noDataValue!=null) {
for (int i = 0; i < nrBanks;i++)
Arrays.fill(intDataArray[i], noDataValue.intValue());
}
dataBuffer = new DataBufferInt(intDataArray, dataBufferSize);
break;
case DataBuffer.TYPE_FLOAT:
float[][] floatDataArray = new float[nrBanks][dataBufferSize];
if (noDataValue!=null) {
for (int i = 0; i < nrBanks;i++)
Arrays.fill(floatDataArray[i], noDataValue.floatValue());
}
dataBuffer = new DataBufferFloat(floatDataArray, dataBufferSize);
break;
case DataBuffer.TYPE_DOUBLE:
double[][] doubleDataArray = new double[nrBanks][dataBufferSize];
if (noDataValue!=null) {
for (int i = 0; i < nrBanks;i++)
Arrays.fill(doubleDataArray[i], noDataValue.doubleValue());
}
dataBuffer = new DataBufferDouble(doubleDataArray, dataBufferSize);
break;
case DataBuffer.TYPE_SHORT:
short[][] shortDataArray = new short[nrBanks][dataBufferSize];
if (noDataValue!=null) {
for (int i = 0; i < nrBanks;i++)
Arrays.fill(shortDataArray[i], noDataValue.shortValue());
}
dataBuffer = new DataBufferShort(shortDataArray, dataBufferSize);
break;
case DataBuffer.TYPE_BYTE:
byte[][] byteDataArray = new byte[nrBanks][dataBufferSize];
if (noDataValue!=null) {
for (int i = 0; i < nrBanks;i++)
Arrays.fill(byteDataArray[i], noDataValue.byteValue());
}
dataBuffer = new DataBufferByte(byteDataArray, dataBufferSize);
break;
case DataBuffer.TYPE_USHORT:
short[][] ushortDataArray = new short[nrBanks][dataBufferSize];
if (noDataValue!=null) {
for (int i = 0; i < nrBanks;i++)
Arrays.fill(ushortDataArray[i], noDataValue.shortValue());
}
dataBuffer = new DataBufferUShort(ushortDataArray, dataBufferSize);
break;
default:
throw new IllegalStateException("Couldn't create DataBuffer for data type " + dataType
+ " and " + pixelSize + " pixel size");
}
return dataBuffer;
}
}