/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2015, 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;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import javax.media.jai.BorderExtender;
import javax.media.jai.BorderExtenderConstant;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.ROI;
import javax.media.jai.operator.MosaicDescriptor;
import javax.media.jai.operator.MosaicType;
import org.geotools.image.ImageWorker;
import it.geosolutions.jaiext.utilities.ImageLayout2;
/**
* This class is responsible for implementing the strategies for the mosaicking which can be a flat merge of band-stacking merge.
*
* @author Simone Giannecchini, GeoSolutions TODO check more conditions to use {@link MosaicDescriptor}
*/
public enum MergeBehavior {
STACK {
@Override
public RenderedImage process(RenderedImage[] sources, double[] backgroundValues,
double[][] inputThreshold, PlanarImage[] sourceAlpha, ROI[] sourceROI,
MosaicType mosaicType, RenderingHints hints) {
// checks
if (sources.length == 1) {
return FLAT.process(sources, backgroundValues, inputThreshold, sourceAlpha,
sourceROI, mosaicType, hints);
}
// Step 1 check if we need to create a background mosaic
Rectangle union = new Rectangle(PlanarImage.wrapRenderedImage(sources[0]).getBounds());
boolean performMosaic = false;
for (int i = 1; i < sources.length; i++) {
// current extent
Rectangle currentExtent = PlanarImage.wrapRenderedImage(sources[0]).getBounds();
if (!currentExtent.equals(union)) {
performMosaic = true;
// union
union = union.union(currentExtent);
}
}
// Step 2 (Optional) use Border operator to bring the images to the same extent
// needs to use mosaic ? DO we have ROIs?
boolean border = true;
if (sourceROI != null) {
for (ROI roi : sourceROI) {
if (roi != null) {
border = false;
break;
}
}
}
if (performMosaic) {
// BORDER extender
final BorderExtender borderExtenderConstant = new BorderExtenderConstant(
backgroundValues);
// loop on sources
for (int i = 0; i < sources.length; i++) {
if (border) {
// do we need to extend?
if (!PlanarImage.wrapRenderedImage(sources[i]).getBounds().equals(union)) {
// current extent
Rectangle currentExtent = PlanarImage.wrapRenderedImage(sources[0])
.getBounds();
// add BORDER to the current source
ImageWorker worker = new ImageWorker(sources[i])
.setRenderingHints(hints);
worker.border(union.x - currentExtent.x,
union.x + union.width - currentExtent.x - currentExtent.width,
union.y - currentExtent.y,
union.y + union.height - currentExtent.y - currentExtent.height,
borderExtenderConstant);
sources[i] = worker.getRenderedImage();
}
} else {
// use msoaic in case we have source ROIs
final ImageLayout layout = (ImageLayout) (hints != null
? hints.get(JAI.KEY_IMAGE_LAYOUT) : new ImageLayout2());
layout.setWidth(union.width).setHeight(union.height).setMinX(union.x)
.setMinY(union.y);
final RenderingHints localHints;
if (hints != null) {
localHints = (RenderingHints) hints.clone();
localHints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));
} else {
localHints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
}
return new ImageWorker(localHints).setBackground(backgroundValues)
.mosaic(sources, MosaicDescriptor.MOSAIC_TYPE_OVERLAY, sourceAlpha,
sourceROI, inputThreshold, null)
.getRenderedImage();
}
}
}
// Step 3, band merge the images
// loop on sources
final ImageWorker worker = new ImageWorker(sources[0]);
worker.setRenderingHints(hints);
for (int i = 1; i < sources.length; i++) {
worker.addBand(sources[i], false);
}
// return final image
return worker.getRenderedImage();
}
},
FLAT {
@Override
public RenderedImage process(RenderedImage[] sources, double[] backgroundValues,
double[][] inputThreshold, PlanarImage[] sourceAlpha, ROI[] sourceROI,
MosaicType mosaicType, RenderingHints localHints) {
return new ImageWorker(localHints).setBackground(backgroundValues)
.mosaic(sources, mosaicType, sourceAlpha, sourceROI, inputThreshold, null)
.getRenderedImage();
}
};
/**
* Process input {@link RenderedImage} to produce the output of this mosaic.
*
* @return a {@link RenderedImage}.
*/
public abstract RenderedImage process(RenderedImage[] sources, double[] backgroundValues,
double[][] inputThreshold, PlanarImage[] sourceAlpha, javax.media.jai.ROI[] sourceROI,
MosaicType mosaicType, RenderingHints localHints);
/**
* Retrieves the default {@link MergeBehavior}.
*
* @return the default {@link MergeBehavior}.
*/
public static MergeBehavior getDefault() {
return FLAT;
}
/**
* Retrieves the possible values as Strings.
*
* @return an arrays of {@link String} that contains the representation of each value.
*/
public static String[] valuesAsStrings() {
final MergeBehavior[] values = MergeBehavior.values();
final String[] valuesS = new String[values.length];
for (int i = 0; i < values.length; i++) {
valuesS[i] = values[i].toString();
}
return null;
}
}