/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2005-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-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.
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*/
package org.geotoolkit.coverage.processing;
import java.util.Locale;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
import org.opengis.coverage.Coverage;
import org.opengis.coverage.SampleDimensionType;
import org.opengis.coverage.processing.Operation;
import org.opengis.coverage.processing.OperationNotFoundException;
import org.opengis.coverage.processing.GridCoverageProcessor;
import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.util.InternationalString;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.factory.Factory;
import org.geotoolkit.coverage.AbstractCoverage;
import org.geotoolkit.coverage.CoverageFactoryFinder;
import org.geotoolkit.coverage.grid.Interpolator2D;
import org.geotoolkit.resources.Loggings;
import org.geotoolkit.resources.Vocabulary;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.Localized;
import org.geotoolkit.image.internal.ImageUtilities;
/**
* Provides operations for different ways of accessing the grid coverage values as well as
* image processing functionality. The list of available processing operations is implementation
* dependent. The class has a discovery mechanism to determine the available processing operations.
* <p>
* These processing operations will transform values within a single sample dimension, and
* leave the values in other sample dimensions unaffected. The modified sample dimension may
* also change its type (e.g. from {@link SampleDimensionType#UNSIGNED_4BITS UNSIGNED_4BITS} to
* {@link SampleDimensionType#UNSIGNED_1BIT UNSIGNED_1BIT}). The actual underlying grid data
* remains unchanged.
* <p>
* The class has been designed to allow the adaptations to be done in a "pipe-lined" manner.
* The class operates on {@link Coverage} to create new a {@link Coverage}. The class does not
* need to make a copy of the source grid data. Instead, it can return a grid coverage object
* which applies the adaptations on the original grid coverage whenever a block of data is
* requested. In this way, a pipeline of several grid coverages can be constructed cheaply.
* <p>
* This class can perform any of the following:
* <ul>
* <li>Change the number of bands being accessed.</li>
* <li>Change the value sequencing in which the grid values are retrieved.</li>
* <li>Allow re-sampling of the grid coverage for a different geometry.
* Creating a new {@link Coverage} with different grid geometry allows for reprojecting
* the grid coverage to another projection and another georeferencing type, resampling to
* another cell resolution and subsetting the grid coverage.</li>
* <li>Modify the way the grid values are accessed (filtered, classified...).</li>
* <li>Change the interpolation method used when evaluating points which fall between grid cells.</li>
* <li>Filtering.</li>
* <li>Image enhancements.</li>
* <li><i>etc.</i></li>
* </ul>
*
* @author Martin Desruisseaux (IRD)
* @version 3.00
*
* @since 2.2
* @module
*/
public abstract class AbstractCoverageProcessor extends Factory implements GridCoverageProcessor, Localized {
/**
* The logger for coverage processing operations.
*/
public static final Logger LOGGER = Logging.getLogger("org.geotoolkit.coverage.processing");
/**
* The logging level for reporting coverage operations.
* This level is equals or slightly lower than {@link Level#INFO}.
*/
public static final Level OPERATION = new LogLevel("OPERATION", 780);
/**
* The grid coverage logging level type.
*/
private static final class LogLevel extends Level {
private static final long serialVersionUID = -2944283575307061508L;
protected LogLevel(final String name, final int level) {
super(name, level);
}
}
/**
* Constructs a coverage processor.
*/
protected AbstractCoverageProcessor() {
super();
}
/**
* Returns a default processor instance. This method is a shortcut for
* {@link CoverageFactoryFinder#getCoverageProcessor(Hints)} with hints
* asking for an instance of {@link AbstractCoverageProcessor}.
*
* @return The default processor instance.
*/
public static AbstractCoverageProcessor getInstance() {
return (AbstractCoverageProcessor) CoverageFactoryFinder.getCoverageProcessor(
new Hints(Hints.GRID_COVERAGE_PROCESSOR, AbstractCoverageProcessor.class));
}
/**
* Retrieves grid processing operations information. Each operation information contains
* the name of the operation as well as a list of its parameters.
*
* @return The available processing operations
*/
@Override
public abstract Collection<Operation> getOperations();
/**
* Returns the operation for the specified name.
*
* @param name Name of the operation.
* @return The operation for the given name.
* @throws OperationNotFoundException if there is no operation for the specified name.
*/
public abstract Operation getOperation(String name) throws OperationNotFoundException;
/**
* Applies an operation.
*
* @param parameters Parameters required for the operation.
* @return The result as a coverage.
* @throws OperationNotFoundException if there is no operation for the parameter group name.
* @throws CoverageProcessingException if the operation can not be executed.
*/
public abstract Coverage doOperation(final ParameterValueGroup parameters)
throws OperationNotFoundException, CoverageProcessingException;
/**
* The locale for logging message or reporting errors. The default implementations
* returns the {@linkplain Locale#getDefault() default locale}. Subclasses can override
* this method if a different locale is wanted.
*
* @return The locale for logging message.
*/
@Override
public Locale getLocale() {
return Locale.getDefault();
}
/**
* Logs a message for an operation. The message will be logged only if the source grid
* coverage is different from the result (i.e. if the operation did some work).
*
* @param source The source grid coverage.
* @param result The resulting grid coverage.
* @param operationName the operation name.
* @param fromCache {@code true} if the result has been fetch from the cache.
*/
final void log(final Coverage source,
final Coverage result,
final String operationName,
final boolean fromCache)
{
if (source != result) {
String interp = "Nearest";
if (result instanceof Interpolator2D) {
interp = ImageUtilities.getInterpolationName(
((Interpolator2D) result).getInterpolation());
}
final Locale locale = getLocale();
final LogRecord record = Loggings.getResources(locale).getLogRecord(
OPERATION, Loggings.Keys.AppliedOperation_4,
getName((source != null) ? source : result, locale),
operationName, interp, Integer.valueOf(fromCache ? 1 : 0));
record.setSourceClassName(getClass().getCanonicalName());
record.setSourceMethodName("doOperation");
record.setLoggerName(LOGGER.getName());
LOGGER.log(record);
}
}
/**
* Returns the primary source coverage from the specified parameters, or {@code null} if none.
*/
static Coverage getPrimarySource(final ParameterValueGroup parameters) {
try {
return (Coverage) parameters.parameter("Source").getValue();
} catch (ParameterNotFoundException exception) {
/*
* "Source" parameter may not exists. Conservatively
* assumes that the operation will do some useful work.
*/
return null;
}
}
/**
* Returns the operation name for the specified parameters.
*/
static String getOperationName(final ParameterValueGroup parameters) {
return parameters.getDescriptor().getName().getCode().trim();
}
/**
* Returns the coverage name in the specified locale.
*/
private static String getName(final Coverage coverage, final Locale locale) {
if (coverage instanceof AbstractCoverage) {
final InternationalString name = ((AbstractCoverage) coverage).getName();
if (name != null) {
return name.toString(locale);
}
}
return Vocabulary.getResources(locale).getString(Vocabulary.Keys.Untitled);
}
}