use of javax.media.jai.Warp in project geotoolkit by Geomatys.
the class WarpPolynomial method createMathTransform.
/**
* Creates a warp transform from the specified group of parameter values.
*
* @param values The group of parameter values.
* @return The created math transform.
* @throws ParameterNotFoundException if a required parameter was not found.
*/
@Override
public MathTransform createMathTransform(MathTransformFactory factory, final ParameterValueGroup values) throws ParameterNotFoundException {
Parameters params = Parameters.castOrWrap(values);
final int degree = params.getValue(DEGREE);
final float[] xCoeffs = params.getValue(X_COEFFS);
final float[] yCoeffs = params.getValue(Y_COEFFS);
final float preScaleX = scale(PRE_SCALE_X, params);
final float preScaleY = scale(PRE_SCALE_Y, params);
final float postScaleX = scale(POST_SCALE_X, params);
final float postScaleY = scale(POST_SCALE_Y, params);
final Warp warp;
switch(degree) {
case 1:
warp = new WarpAffine(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY);
break;
case 2:
warp = new WarpQuadratic(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY);
break;
case 3:
warp = new WarpCubic(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY);
break;
default:
warp = new WarpGeneralPolynomial(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY);
break;
}
return WarpTransform2D.create(warp);
}
use of javax.media.jai.Warp in project geotoolkit by Geomatys.
the class WarpFactory method create.
/**
* Creates an image warp applicable to the given domain of validity. This method will typically
* create more efficient warps than the {@linkplain #create(CharSequence, MathTransform2D)
* unbounded method}.
*
* @param name The image or {@linkplain GridCoverage coverage} name, or {@code null}.
* @param transform The transform to returns as an image warp.
* @param domain The domain of validity in source coordinates.
* @return The warp for the given transform.
* @throws TransformException If at least one point in the given domain can not be transformed.
*/
public Warp create(final CharSequence name, final MathTransform2D transform, final Rectangle domain) throws TransformException {
if (transform instanceof WarpTransform2D) {
return ((WarpTransform2D) transform).getWarp();
}
if (transform instanceof AffineTransform) {
return create((AffineTransform) transform);
}
final WarpKey key = new WarpKey(transform, domain);
Warp warp = cache.peek(key);
if (warp == null) {
WarpCache.Handler<Warp> handler = cache.lock(key);
try {
warp = handler.peek();
if (warp == null) {
warp = create(name, transform, key);
}
} finally {
handler.putAndUnlock(warp);
}
}
return warp;
}
use of javax.media.jai.Warp in project geotoolkit by Geomatys.
the class ImageCoverageWriter method write.
/**
* Writes a single coverage, which may be an element of a sequence. This method needs to be
* informed when it is writing the first or the last coverage of a sequence. If there is only
* one coverage to write, than both {@code isFirst} and {@code isLast} must be {@code true}.
* <p>
* In current implementation, the stream metadata are generated from the first image only
* (when {@code isFirst == true}) and the log message (if any) shows the grid geometry of
* the last coverage only (when {@code isLast == true}). It should not be an issue in the
* common case where all coverage in the sequence have similar grid geometry or metadata.
*
* @param coverages The coverages to write.
* @param param Optional parameters used to control the writing process, or {@code null}.
* @param isFirst {@code true} if writing the first coverage of a sequence.
* @param isLast {@code true} if writing the last coverage of a sequence.
* @param startTime Nano time when the writing process started, or {@link Long#MIN_VALUE}
* if the operation duration is not logged.
* @throws IllegalStateException If the output destination has not been set.
* @throws CoverageStoreException If the iterable contains an unsupported number of coverages,
* or if an error occurs while writing the information to the output destination.
* @throws CancellationException If {@link #abort()} has been invoked in an other thread during
* the execution of this method.
*
* @since 3.20
*/
private void write(final GridCoverage coverage, final GridCoverageWriteParam param, final boolean isFirst, final boolean isLast, final long startTime) throws DataStoreException, CancellationException {
/*
* Prepares an initially empty ImageWriteParam, to be filled later with the values
* provided in the GridCoverageWriteParam. In order to get the ImageWriteParam, we
* need the ImageWriter, which need the RenderedImage, which need the GridGeometry.
*/
GridGeometry gridGeometry = coverage.getGridGeometry().reduce(0, 1);
RenderedImage image = coverage.render(null);
while (image instanceof RenderedImageAdapter) {
image = ((RenderedImageAdapter) image).getWrappedImage();
}
if (isFirst) {
final String imageFormat = (param != null) ? param.getFormatName() : null;
setImageOutput(image, imageFormat);
}
/*
* The ImageWriter is created by the call to setImageOutput.
* We can verify its validity only at this point.
*/
// Protect from changes.
final ImageWriter imageWriter = this.imageWriter;
if (imageWriter == null) {
throw new IllegalStateException(formatErrorMessage(Errors.Keys.NoImageOutput));
}
if (!isLast && !imageWriter.canWriteSequence()) {
throw new CoverageStoreException(Errors.format(Errors.Keys.UnsupportedMultiOccurrence_1, GridCoverage.class));
}
// TODO: DEPRECATED: to be removed in Apache SIS.
final boolean isNetcdfHack = imageWriter.getClass().getName().equals("org.geotoolkit.image.io.plugin.NetcdfImageWriter");
final boolean isTiffHack = imageWriter.getClass().getName().equals("org.geotoolkit.image.io.plugin.TiffImageWriter");
/*
* Convert the geodetic coordinates to pixel coordinates.
*/
final ImageWriteParam imageParam;
try {
imageParam = createImageWriteParam(image);
} catch (IOException e) {
throw new CoverageStoreException(formatErrorMessage(e), e);
}
MathTransform2D destToExtractedGrid = null;
PlanarImage toDispose = null;
if (param != null) {
/*
* Now convert the GridCoverageWriteParam values to ImageWriteParam value.
* First of all, convert the ISO 119123 InterpolationMethod code to the JAI
* code.
*/
final int interp;
final InterpolationMethod interpolation = param.getInterpolation();
if (interpolation.equals(InterpolationMethod.NEAREST_NEIGHBOUR)) {
interp = Interpolation.INTERP_NEAREST;
} else if (interpolation.equals(InterpolationMethod.BILINEAR)) {
interp = Interpolation.INTERP_BILINEAR;
} else if (interpolation.equals(InterpolationMethod.BICUBIC)) {
interp = Interpolation.INTERP_BICUBIC;
} else {
throw new CoverageStoreException(Errors.getResources(locale).getString(Errors.Keys.IllegalArgument_2, "interpolation", interpolation.name()));
}
destToExtractedGrid = geodeticToPixelCoordinates(gridGeometry, param, imageParam, isNetcdfHack);
imageParam.setSourceBands(param.getSourceBands());
final Rectangle sourceRegion = imageParam.getSourceRegion();
final Rectangle requestRegion = requestedBounds;
if (interp != Interpolation.INTERP_NEAREST || !isIdentity(destToExtractedGrid) || isGreater(requestRegion.width, imageParam.getSourceXSubsampling(), sourceRegion.width) || isGreater(requestRegion.height, imageParam.getSourceYSubsampling(), sourceRegion.height)) {
/*
* We need to resample the image if:
*
* - The transform from the source grid to the target grid is not affine;
* - The above transform is affine but more complex than scale and translations;
* - The translation or scale factors of the above transform are not integers;
* - The requested envelope is greater than the coverage envelope;
*/
final InternationalString name = null;
final ImageLayout layout = new ImageLayout(requestRegion.x, requestRegion.y, requestRegion.width, requestRegion.height);
/*
* Some codecs (e.g. JPEG) require that the whole image is available
* as a single raster.
*/
layout.setTileWidth(requestRegion.width);
layout.setTileHeight(requestRegion.height);
final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
// Will be used for logging purpose.
destToExtractedGrid = (MathTransform2D) destGridToSource;
final Warp warp;
try {
warp = WarpFactory.DEFAULT.create(name, destToExtractedGrid, sourceRegion);
} catch (TransformException e) {
throw new CoverageStoreException(formatErrorMessage(e), e);
}
if (DEBUG) {
/*
* To be enabled only when debugging.
* Simplified output example from the writeSubsampledRegion() test:
*
* Grid to source: ┌ ┐
* │ 2 0 9 │
* │ 0 3 9 │
* │ 0 0 1 │
* └ ┘
* Source region: Rectangle[x=9, y=9, width=9, height=15]
* Warp origin: [9.5, 10.0]
*
* If we had no scale factor, the Warp origin would be the same than the
* translations. If we have scale factors be were mapping pixel corners,
* then the warp origin would also be the same.
*
* But the JAI Warp operation maps pixel center. It does so by adding 0.5
* to pixel coordinates before applying the Warp, and removing 0.5 to the
* result (see WarpTransform2D.getWarp(...) javadoc). This is actually the
* desired behavior, as we can see with the picture below which represents
* only the first pixel of the destination image. The cell are the source
* pixels, the transform is the above matrix, and the coordinates are
* relative to the source grid:
*
* (9,9)
* ┌─────┬─────┐
* │ │ │
* ├─────┼─────┤
* │ (10,10.5) │ after the -0.5 final offset, become (9.5, 10).
* ├─────┼─────┤
* │ │ │
* └─────┴─────┘
* (11,12)
*/
Object tr = destToExtractedGrid;
if (tr instanceof LinearTransform) {
tr = ((LinearTransform) tr).getMatrix();
}
final TableAppender table = new TableAppender(" ");
table.setMultiLinesCells(true);
table.appendHorizontalSeparator();
table.append("Warping coverage:");
table.nextColumn();
table.append(String.valueOf(name));
table.nextLine();
table.append("Grid to source:");
table.nextColumn();
table.append(String.valueOf(tr));
table.nextLine();
table.append("Source region:");
table.nextColumn();
table.append(String.valueOf(sourceRegion));
table.nextLine();
table.append("Warp origin:");
table.nextColumn();
table.append(Arrays.toString(warp.warpPoint(0, 0, null)));
table.nextLine();
table.appendHorizontalSeparator();
System.out.println(table);
}
double[] backgroundValues = param.getBackgroundValues();
if (backgroundValues == null) {
backgroundValues = CoverageUtilities.getBackgroundValues(coverage);
}
image = toDispose = WarpDescriptor.create(image, warp, Interpolation.getInstance(interp), backgroundValues, hints);
imageParam.setSourceRegion(null);
imageParam.setSourceSubsampling(1, 1, 0, 0);
}
/*
* Set other parameters inferred from the GridCoverageWriteParam.
*/
if (imageParam.canWriteCompressed()) {
final Float compression = param.getCompressionQuality();
if (compression != null) {
imageParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageParam.setCompressionQuality(compression);
}
}
}
if (imageParam.canWriteTiles()) {
imageParam.setTilingMode(ImageWriteParam.MODE_EXPLICIT);
// -- one destination tile equals source image tile representation
imageParam.setTiling(image.getTileWidth() / imageParam.getSourceXSubsampling(), image.getTileHeight() / imageParam.getSourceYSubsampling(), 0, 0);
}
/*
* Creates metadata with the information calculated so far. The code above this
* point should have created an image having a grid geometry matching the user
* request, so we will write that user request in the metadata.
*/
final ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(image);
final IIOMetadata streamMetadata = isFirst ? imageWriter.getDefaultStreamMetadata(imageParam) : null;
final IIOMetadata imageMetadata = imageWriter.getDefaultImageMetadata(imageType, imageParam);
if (imageMetadata != null && ArraysExt.contains(imageMetadata.getMetadataFormatNames(), GEOTK_FORMAT_NAME)) {
CoordinateReferenceSystem crs = null;
Envelope env = null;
double[] res = null;
if (param != null) {
crs = param.getCoordinateReferenceSystem();
env = param.getEnvelope();
res = param.getResolution();
}
if (crs == null && gridGeometry.isDefined(GridGeometry.CRS)) {
crs = gridGeometry.getCoordinateReferenceSystem();
}
if (env == null && gridGeometry.isDefined(GridGeometry.ENVELOPE)) {
env = gridGeometry.getEnvelope();
}
if (crs != null) {
final ReferencingBuilder builder = new ReferencingBuilder(imageMetadata);
builder.setCoordinateReferenceSystem(crs);
}
if (env != null) {
final GridDomainAccessor accessor = new GridDomainAccessor(imageMetadata);
final Dimension size = getImageSize(image, imageParam);
final double ymax = env.getMaximum(Y_DIMENSION);
final double[] origin = env.getLowerCorner().getCoordinate();
final int dim = origin.length;
origin[Y_DIMENSION] = ymax;
if (res != null) {
accessor.setOrigin(origin);
final double[] p = new double[dim];
final double[] median = new double[dim];
for (int i = 0; i < dim; i++) {
Arrays.fill(p, 0);
if (i == X_DIMENSION) {
p[i] = +res[X_DIMENSION];
} else if (i == Y_DIMENSION) {
p[i] = -res[Y_DIMENSION];
} else {
p[i] = 1;
}
accessor.addOffsetVector(p);
median[i] = env.getMedian(i);
}
accessor.setSpatialRepresentation(median, null, PixelOrientation.UPPER_LEFT);
final int[] maxGrid = new int[dim];
Arrays.fill(maxGrid, 1);
maxGrid[X_DIMENSION] = size.width - 1;
maxGrid[Y_DIMENSION] = size.height - 1;
accessor.setLimits(new int[dim], maxGrid);
} else {
final double[] envBounds = env.getUpperCorner().getCoordinate();
envBounds[Y_DIMENSION] = env.getMinimum(Y_DIMENSION);
final int[] high = new int[dim];
Arrays.fill(high, 1);
high[X_DIMENSION] = size.width - 1;
high[Y_DIMENSION] = size.height - 1;
accessor.setRectifiedGridDomain(origin, envBounds, null, high, null, false);
accessor.setSpatialRepresentation(origin, envBounds, null, PixelOrientation.UPPER_LEFT);
}
}
final List<SampleDimension> dims = coverage.getSampleDimensions();
final DimensionAccessor accessor = new DimensionAccessor(imageMetadata);
for (int i = 0, n = dims.size(); i < n; i++) {
final SampleDimension band = dims.get(i);
accessor.selectChild(accessor.appendChild());
if (band != null) {
accessor.setDimension(band, locale);
}
}
}
/*
* Now process to the coverage writing. If the coverage is the only image (i.e. is both
* the first and the last image), then we will write everything in a single operation by
* a call to ImageWriter.write(...). Otherwise we will need to use the
* prepareWriteSequence() - writeSequence(...) - endWriteSequence() cycle.
*/
checkAbortState();
try {
if (streamMetadata != null) {
completeImageMetadata(streamMetadata, null);
}
completeImageMetadata(imageMetadata, coverage);
final IIOImage iio = new IIOImage(image, null, imageMetadata);
if (isFirst & isLast) {
imageWriter.write(streamMetadata, iio, imageParam);
} else {
if (isFirst) {
imageWriter.prepareWriteSequence(streamMetadata);
}
imageWriter.writeToSequence(iio, imageParam);
if (isLast) {
imageWriter.endWriteSequence();
}
}
} catch (IOException e) {
throw new CoverageStoreException(formatErrorMessage(e), e);
}
checkAbortState();
/*
* Finally, logs the operation after the last image if logging are enabled.
* The log level will depend on how long it took to write every images in
* the sequence.
*/
if (isLast && startTime != Long.MIN_VALUE) {
final long time = System.nanoTime() - startTime;
final Level level = getLogLevel(time);
if (LOGGER.isLoggable(level)) {
final Dimension size = getImageSize(image, imageParam);
CoordinateReferenceSystem crs = null;
if (param != null) {
crs = param.getCoordinateReferenceSystem();
}
ImageCoverageStore.logOperation(level, locale, ImageCoverageWriter.class, true, output, 0, coverage, size, crs, destToExtractedGrid, time);
}
}
if (toDispose != null) {
toDispose.dispose();
}
}
use of javax.media.jai.Warp in project geowave by locationtech.
the class WarpRIF method create.
/**
* Creates a new instance of warp operator according to the warp object and interpolation method.
*
* @param paramBlock The warp and interpolation objects.
*/
@Override
public RenderedImage create(final ParameterBlock paramBlock, final RenderingHints renderHints) {
final Interpolation interp = (Interpolation) paramBlock.getObjectParameter(1);
if ((interp instanceof InterpolationNearest) || (interp instanceof javax.media.jai.InterpolationNearest)) {
// Get ImageLayout from renderHints if any.
final ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints);
RenderedImage source = paramBlock.getRenderedSource(0);
final Warp warp = (Warp) paramBlock.getObjectParameter(0);
final double[] backgroundValues = (double[]) paramBlock.getObjectParameter(2);
ROI roi = null;
final Object roi_ = paramBlock.getObjectParameter(3);
if (roi_ instanceof ROI) {
roi = (ROI) roi_;
final PlanarImage temp = PlanarImage.wrapRenderedImage(source);
temp.setProperty("ROI", roi);
source = temp;
}
Range noData = (Range) paramBlock.getObjectParameter(4);
noData = RangeFactory.convert(noData, source.getSampleModel().getDataType());
return new WarpNearestOpImage(source, renderHints, layout, warp, interp, roi, noData, backgroundValues);
}
return super.create(paramBlock, renderHints);
}
use of javax.media.jai.Warp in project geotoolkit by Geomatys.
the class DimapAccessor method readGridToCRS2D.
/**
* Read the Grid to CRS transform.
* Those informations are provided by the Geoposition tag.
*
* @param doc
* @return AffineTransform
* @throws org.opengis.util.FactoryException
* @throws org.opengis.referencing.operation.TransformException
*/
public static AffineTransform readGridToCRS2D(final Element doc) throws FactoryException, TransformException {
final Element ele = firstElement(doc, TAG_GEOPOSITION);
final Element insert = firstElement(ele, TAG_GEOPOSITION_INSERT);
final Element points = firstElement(ele, TAG_GEOPOSITION_POINTS);
final Element affine = firstElement(ele, TAG_GEOPOSITION_AFFINE);
if (insert != null) {
// X = ULXMAP + XDIM * i
// Y = ULYMAP - YDIM * j
final double ulx = textValueSafe(insert, TAG_ULXMAP, Double.class);
final double uly = textValueSafe(insert, TAG_ULYMAP, Double.class);
final double xdim = textValueSafe(insert, TAG_XDIM, Double.class);
final double ydim = textValueSafe(insert, TAG_YDIM, Double.class);
return new AffineTransform(xdim, 0, 0, -ydim, ulx, uly);
} else if (affine != null) {
// X (CRS) = X0 + X1 * X(Data) + X2 * Y(Data)
// Y (CRS) = Y0 + Y1 * X(Data) + Y2 * Y(Data)
final double x0 = textValueSafe(affine, TAG_AFFINE_X0, Double.class);
final double x1 = textValueSafe(affine, TAG_AFFINE_X1, Double.class);
final double x2 = textValueSafe(affine, TAG_AFFINE_X2, Double.class);
final double y0 = textValueSafe(affine, TAG_AFFINE_Y0, Double.class);
final double y1 = textValueSafe(affine, TAG_AFFINE_Y1, Double.class);
final double y2 = textValueSafe(affine, TAG_AFFINE_Y2, Double.class);
return new AffineTransform(x0, y0, x1, y1, x2, y2);
} else if (points != null) {
// transformation in not accurate if the method has been defined.
// read the points and calculate an average transform from them.
final NodeList tiePoints = ele.getElementsByTagName(TAG_TIE_POINT);
final List<Point2D> sources = new ArrayList<>();
final List<Point2D> dests = new ArrayList<>();
for (int i = 0, n = tiePoints.getLength(); i < n; i++) {
final Element vertex = (Element) tiePoints.item(i);
final double coordX = textValueSafe(vertex, TAG_TIE_POINT_CRS_X, Double.class);
final double coordY = textValueSafe(vertex, TAG_TIE_POINT_CRS_Y, Double.class);
final int dataY = textValueSafe(vertex, TAG_TIE_POINT_DATA_X, Double.class).intValue();
final int dataX = textValueSafe(vertex, TAG_TIE_POINT_DATA_Y, Double.class).intValue();
dests.add(new Point2D.Double(coordX, coordY));
sources.add(new Point2D.Double(dataX, dataY));
}
final WarpTransform2D warptrs = new WarpTransform2D(sources.toArray(new Point2D[sources.size()]), dests.toArray(new Point2D[dests.size()]), 1);
final Warp warp = warptrs.getWarp();
if (warp instanceof WarpAffine) {
final WarpAffine wa = (WarpAffine) warp;
return wa.getTransform();
} else {
throw new TransformException("Wrap transform is not affine.");
}
} else {
throw new TransformException("Geopositioning type unknowned.");
}
}
Aggregations