Java tutorial
/* * Copyright (C) 2014 by Array Systems Computing Inc. http://www.array.ca * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program 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 General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.nest.gpf; import com.bc.ceres.core.ProgressMonitor; import org.apache.commons.math3.util.FastMath; import org.esa.beam.framework.dataio.ProductReader; import org.esa.beam.framework.dataio.ProductSubsetBuilder; import org.esa.beam.framework.dataio.ProductSubsetDef; import org.esa.beam.framework.datamodel.*; import org.esa.beam.framework.gpf.Operator; import org.esa.beam.framework.gpf.OperatorException; import org.esa.beam.framework.gpf.OperatorSpi; import org.esa.beam.framework.gpf.Tile; import org.esa.beam.framework.gpf.annotations.OperatorMetadata; import org.esa.beam.framework.gpf.annotations.Parameter; import org.esa.beam.framework.gpf.annotations.SourceProduct; import org.esa.beam.framework.gpf.annotations.TargetProduct; import org.esa.beam.util.ProductUtils; import org.esa.snap.datamodel.AbstractMetadata; import org.esa.snap.datamodel.Unit; import org.esa.snap.gpf.OperatorUtils; import org.esa.snap.util.ResourceUtils; import java.awt.*; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.StringTokenizer; /** * This operator down samples a real or complex image using sub-sampling method or kernel filtering method. * <p/> * With sub-sampling method, the image is down sampled with user specified sub-sampling rates in both range * and azimuth directions. For complex image, the i and q bands in the image are down sampled separately, * and the down sampled image is still a complex image. * <p/> * With kernel filtering method, the image is down sampled with a kernel moving across the image with a * step-size determined by the size of the required output image. The kernel can be selected from pre- * defined shapes or defined by the user. For complex image, intensity image is computed from the i and * q bands before kernel filtering is applied. The down sampled image is always real image. The user can * determine the output image size by specifying the output image size, or the pixel spacings, or the * down sampling ratios. * <p/> * The parameters used by the operator are as the follows: * <p/> * Source Band: All bands (real or virtual) of the source product. * Under-Sampling Method: Sub-Sampling method or Kernel Filtering method * <p/> * For Sub-Sampling method, the following parameters are used: * <p/> * Sub-Sampling in X: User provided sub-sampling rate in range. * Sub-Sampling in Y: User provided sub-sampling rate in azimuth. * <p/> * For Kernel Filtering method, the following parameters are used: * <p/> * Filter Type: The kernel filter type. * Filter Size: The kernel filter size. * Kernel File: The user defined kernel. * Output Image Rows: The row size of the down sampled image. * Output Image Columns: The column size of the down sampled image. * Width Ratio: The ratio of the down sampled image width and the source image width. * Height Ratio: The ratio of the down sampled image height and the source image height. * Range Spacing: The range pixel spacing of the down sampled image. * Azimuth Spacing: The azimuth pixel spacing of the down sampled image. */ @OperatorMetadata(alias = "Undersample", category = "Utilities/Resampling", authors = "Jun Lu, Luis Veci", copyright = "Copyright (C) 2014 by Array Systems Computing Inc.", description = "Undersample the datset") public class UndersamplingOp extends Operator { @SourceProduct(alias = "source") private Product sourceProduct; @TargetProduct private Product targetProduct; @Parameter(description = "The list of source bands.", alias = "sourceBands", itemAlias = "band", rasterDataNodeType = Band.class, label = "Source Bands") private String[] sourceBandNames; @Parameter(valueSet = { SUB_SAMPLING, KERNEL_FILTERING }, defaultValue = KERNEL_FILTERING, label = "Under-Sampling Method") private String method = KERNEL_FILTERING; // @Parameter(valueSet = {SUMMARY, EDGE_DETECT, EDGE_ENHANCEMENT, LOW_PASS, HIGH_PASS, HORIZONTAL, VERTICAL, USER_DEFINED}, // defaultValue = LOW_PASS, label="Filter Type") private String filterType = LOW_PASS; @Parameter(valueSet = { FILTER_SIZE_3x3, FILTER_SIZE_5x5, FILTER_SIZE_7x7 }, defaultValue = FILTER_SIZE_3x3, label = "Filter Size") private String filterSize = FILTER_SIZE_3x3; // @Parameter(description = "The kernel file", label="Kernel File") private File kernelFile = null; @Parameter(defaultValue = "2", label = "Sub-Sampling in X") private int subSamplingX = 2; @Parameter(defaultValue = "2", label = "Sub-Sampling in Y") private int subSamplingY = 2; @Parameter(valueSet = { IMAGE_SIZE, RATIO, PIXEL_SPACING }, defaultValue = IMAGE_SIZE, label = "Output Image By:") private String outputImageBy = RATIO; @Parameter(description = "The row dimension of the output image", defaultValue = "1000", label = "Output Image Rows") private int targetImageHeight = 1000; @Parameter(description = "The col dimension of the output image", defaultValue = "1000", label = "Output Image Columns") private int targetImageWidth = 1000; @Parameter(description = "The width ratio of the output/input images", defaultValue = "0.5", label = "Width Ratio") private float widthRatio = 0.5f; @Parameter(description = "The height ratio of the output/input images", defaultValue = "0.5", label = "Height Ratio") private float heightRatio = 0.5f; @Parameter(description = "The range pixel spacing", defaultValue = "12.5", label = "Range Spacing") private float rangeSpacing = 12.5f; @Parameter(description = "The azimuth pixel spacing", defaultValue = "12.5", label = "Azimuth Spacing") private float azimuthSpacing = 12.5f; private ProductReader subsetReader = null; private MetadataElement absRoot = null; private int filterWidth; private int filterHeight; private int sourceImageWidth; private int sourceImageHeight; private double stepRange; // step size in range direction for moving window filtering private double stepAzimuth; // step size in azimuth direction for moving window filtering private float srcRangeSpacing; // range pixel spacing of source image private float srcAzimuthSpacing; // azimuth pixel spacing of source image private float[][] kernel; // kernel for filtering private final HashMap<String, String[]> targetBandNameToSourceBandName = new HashMap<String, String[]>(); public static final String SUB_SAMPLING = "Sub-Sampling"; public static final String KERNEL_FILTERING = "LowPass Filtering"; public static final String SUMMARY = "Summary"; public static final String EDGE_DETECT = "Edge Detect"; public static final String EDGE_ENHANCEMENT = "Edge Enhancement"; public static final String LOW_PASS = "Low Pass"; public static final String HIGH_PASS = "High Pass"; public static final String HORIZONTAL = "Horizontal"; public static final String VERTICAL = "Vertical"; private static final String USER_DEFINED = "User Defined"; public static final String IMAGE_SIZE = "Image Size"; public static final String RATIO = "Ratio"; public static final String PIXEL_SPACING = "Pixel Spacing"; public static final String FILTER_SIZE_3x3 = "3x3"; public static final String FILTER_SIZE_5x5 = "5x5"; public static final String FILTER_SIZE_7x7 = "7x7"; @Override public void initialize() throws OperatorException { GeoCoding sourceGeoCoding = sourceProduct.getGeoCoding(); if (sourceGeoCoding instanceof CrsGeoCoding) { throw new OperatorException("Undersampling is not intended for map projected products"); } sourceImageWidth = sourceProduct.getSceneRasterWidth(); sourceImageHeight = sourceProduct.getSceneRasterHeight(); absRoot = AbstractMetadata.getAbstractedMetadata(sourceProduct); if (method.equals(SUB_SAMPLING)) { initializeForSubSampling(); } else if (method.equals(KERNEL_FILTERING)) { initializeForKernelFiltering(); } else { throw new OperatorException("Unknown undersampling method: " + method); } } /** * Initialization for sub-sampling. * * @throws OperatorException The exceptions. */ private void initializeForSubSampling() throws OperatorException { try { if (sourceBandNames == null || sourceBandNames.length == 0) { final Band[] bands = sourceProduct.getBands(); final List<String> bandNameList = new ArrayList<String>(sourceProduct.getNumBands()); for (Band band : bands) { bandNameList.add(band.getName()); } sourceBandNames = bandNameList.toArray(new String[bandNameList.size()]); } for (int i = 0; i < sourceBandNames.length; i++) { final String unit1 = sourceProduct.getBand(sourceBandNames[i]).getUnit(); if (unit1 != null && unit1.contains(Unit.REAL)) { if (i + 1 < sourceBandNames.length && sourceProduct.getBand(sourceBandNames[i + 1]).getUnit().contains(Unit.IMAGINARY)) { i++; } else { throw new OperatorException("Real and imaginary bands should be selected in pairs"); } } } subsetReader = new ProductSubsetBuilder(); final ProductSubsetDef subsetDef = new ProductSubsetDef(); subsetDef.addNodeNames(sourceProduct.getTiePointGridNames()); subsetDef.addNodeNames(sourceBandNames); subsetDef.setSubSampling(subSamplingX, subSamplingY); subsetDef.setIgnoreMetadata(false); subsetDef.setTreatVirtualBandsAsRealBands(true); targetProduct = subsetReader.readProductNodes(sourceProduct, subsetDef); ProductUtils.copyMetadata(sourceProduct, targetProduct); ProductUtils.copyFlagCodings(sourceProduct, targetProduct); ProductUtils.copyGeoCoding(sourceProduct, targetProduct); targetProduct.setStartTime(sourceProduct.getStartTime()); targetProduct.setEndTime(sourceProduct.getEndTime()); updateTargetProductMetadata(subSamplingX, subSamplingY); } catch (Throwable e) { OperatorUtils.catchOperatorException(getId(), e); } } private void updateTargetProductMetadata(int subSamplingX, int subSamplingY) { getSrcImagePixelSpacings(); targetImageWidth = (sourceImageWidth - 1) / subSamplingX + 1; targetImageHeight = (sourceImageHeight - 1) / subSamplingY + 1; rangeSpacing = srcRangeSpacing * sourceImageWidth / targetImageWidth; azimuthSpacing = srcAzimuthSpacing * sourceImageHeight / targetImageHeight; final MetadataElement absTgt = AbstractMetadata.getAbstractedMetadata(targetProduct); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.azimuth_spacing, azimuthSpacing); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.range_spacing, rangeSpacing); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_samples_per_line, targetImageWidth); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_output_lines, targetImageHeight); final float oldLineTimeInterval = (float) absTgt.getAttributeDouble(AbstractMetadata.line_time_interval); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.line_time_interval, oldLineTimeInterval * (float) subSamplingY); } /** * Initialization for kernel filtering. * * @throws OperatorException The exceptions. */ private void initializeForKernelFiltering() throws OperatorException { try { getFilterDimension(); getSrcImagePixelSpacings(); computeTargetImageSizeAndPixelSpacings(); computeRangeAzimuthStepSizes(); getKernelFile(); createTargetProduct(); } catch (Exception e) { throw new OperatorException(e.getMessage()); } } private void getFilterDimension() { if (filterSize.equals(FILTER_SIZE_3x3)) { filterWidth = 3; filterHeight = 3; } else if (filterSize.equals(FILTER_SIZE_5x5)) { filterWidth = 5; filterHeight = 5; } else if (filterSize.equals(FILTER_SIZE_7x7)) { filterWidth = 7; filterHeight = 7; } else { throw new OperatorException("Unknown filter size: " + filterSize); } } /** * Get the range and azimuth spacings (in meter). * * @throws Exception when metadata is missing */ void getSrcImagePixelSpacings() { srcRangeSpacing = (float) absRoot.getAttributeDouble(AbstractMetadata.range_spacing); //System.out.println("Range spacing is " + srcRangeSpacing); srcAzimuthSpacing = (float) absRoot.getAttributeDouble(AbstractMetadata.azimuth_spacing); //System.out.println("Azimuth spacing is " + srcAzimuthSpacing); } /** * Compute target image size and range/azimuth spacings. * * @throws OperatorException The exceptions. */ private void computeTargetImageSizeAndPixelSpacings() throws OperatorException { if (outputImageBy.equals(IMAGE_SIZE)) { if (targetImageHeight <= 0 || targetImageHeight >= sourceImageHeight || targetImageWidth <= 0 || targetImageWidth >= sourceImageWidth) { throw new OperatorException( "Output image size must be positive and smaller than the source image size"); } rangeSpacing = srcRangeSpacing * sourceImageWidth / targetImageWidth; azimuthSpacing = srcAzimuthSpacing * sourceImageHeight / targetImageHeight; } else if (outputImageBy.equals(RATIO)) { if (widthRatio <= 0 || widthRatio > 1 || heightRatio <= 0 || heightRatio > 1) { throw new OperatorException("The width or height ratio must be within range (0, 1)"); } targetImageHeight = (int) (heightRatio * sourceImageHeight + 0.5f); targetImageWidth = (int) (widthRatio * sourceImageWidth + 0.5f); rangeSpacing = srcRangeSpacing / widthRatio; azimuthSpacing = srcAzimuthSpacing / heightRatio; } else if (outputImageBy.equals(PIXEL_SPACING)) { if (rangeSpacing <= srcRangeSpacing || azimuthSpacing <= srcAzimuthSpacing) { throw new OperatorException("The azimuth or range spacing must be greater than the source spacing"); } targetImageHeight = (int) (srcRangeSpacing / rangeSpacing * sourceImageHeight + 0.5); targetImageWidth = (int) (srcAzimuthSpacing / azimuthSpacing * sourceImageWidth + 0.5); } else { throw new OperatorException( "Please specify output image size, or row and column ratios, or pixel spacings"); } } /** * Compute range and azimuth step size for kernel filtering. */ private void computeRangeAzimuthStepSizes() { stepAzimuth = (double) (sourceImageHeight - filterHeight) / (double) (targetImageHeight - 1); stepRange = (double) (sourceImageWidth - filterWidth) / (double) (targetImageWidth - 1); } /** * Read pre-defined or user defined kernel file. */ private void getKernelFile() { String fileName = ""; boolean isPreDefinedKernel; if (filterType.equals(USER_DEFINED)) { // user defined kernel file isPreDefinedKernel = false; } else { // pre-defined kernel file with user specified filter diemnsion isPreDefinedKernel = true; if (filterType.equals(SUMMARY)) { fileName = "sum_" + filterHeight + "_" + filterWidth + ".ker"; } else if (filterType.equals(EDGE_DETECT)) { fileName = "edd_" + filterHeight + "_" + filterWidth + ".ker"; } else if (filterType.equals(EDGE_ENHANCEMENT)) { fileName = "ede_" + filterHeight + "_" + filterWidth + ".ker"; } else if (filterType.equals(LOW_PASS)) { fileName = "lop_" + filterHeight + "_" + filterWidth + ".ker"; } else if (filterType.equals(HIGH_PASS)) { fileName = "hip_" + filterHeight + "_" + filterWidth + ".ker"; } else if (filterType.equals(HORIZONTAL)) { fileName = "hor_" + filterHeight + "_" + filterWidth + ".ker"; } else if (filterType.equals(VERTICAL)) { fileName = "ver_" + filterHeight + "_" + filterWidth + ".ker"; } else { throw new OperatorException("Incorrect filter type: " + filterType); } kernelFile = getResFile(fileName); } kernel = readFile(kernelFile.getAbsolutePath()); if (isPreDefinedKernel) { if (filterHeight != kernel.length || filterWidth != kernel[0].length) { throw new OperatorException("Kernel size does not match given filter size"); } } else { // user defined kernel filterHeight = kernel.length; filterWidth = kernel[0].length; } } private static File getResFile(String fileName) { final String homeUrl = ResourceUtils.findHomeFolder().getAbsolutePath(); final String path = homeUrl + File.separator + "resource" + File.separator + "kernels" + File.separator + fileName; return new File(path); } /** * Read data from kernel file and save them in a 2D array. * * @param fileName The kernel file name * @return array The 2D array holding kernel data */ public static float[][] readFile(String fileName) { // get reader FileInputStream stream; try { stream = new FileInputStream(fileName); } catch (FileNotFoundException e) { throw new OperatorException("File not found: " + fileName); } final BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); // read data from file and save them in 2-D array String line = ""; StringTokenizer st; float[][] array; int rowIdx = 0; try { // get the 1st line if ((line = reader.readLine()) == null) { throw new OperatorException("Empty file: " + fileName); } st = new StringTokenizer(line); if (st.countTokens() != 2) { throw new OperatorException("Incorrect file format: " + fileName); } final int numRows = Integer.parseInt(st.nextToken()); final int numCols = Integer.parseInt(st.nextToken()); array = new float[numRows][numCols]; // get the rest numRows lines while ((line = reader.readLine()) != null) { st = new StringTokenizer(line); if (st.countTokens() != numCols) { throw new OperatorException("Incorrect file format: " + fileName); } for (int j = 0; j < numCols; j++) { array[rowIdx][j] = Float.parseFloat(st.nextToken()); } rowIdx++; } if (numRows != rowIdx) { throw new OperatorException("Incorrect number of lines in file: " + fileName); } reader.close(); stream.close(); } catch (IOException e) { throw new OperatorException(e); } return array; } /** * Create target product. */ private void createTargetProduct() { targetProduct = new Product(sourceProduct.getName(), sourceProduct.getProductType(), targetImageWidth, targetImageHeight); OperatorUtils.addSelectedBands(sourceProduct, sourceBandNames, targetProduct, targetBandNameToSourceBandName, false, true); ProductUtils.copyMetadata(sourceProduct, targetProduct); ProductUtils.copyFlagCodings(sourceProduct, targetProduct); targetProduct.setStartTime(sourceProduct.getStartTime()); targetProduct.setEndTime(sourceProduct.getEndTime()); addGeoCoding(); updateTargetProductMetadata(); } private void addGeoCoding() { final TiePointGrid lat = OperatorUtils.getLatitude(sourceProduct); final TiePointGrid lon = OperatorUtils.getLongitude(sourceProduct); final TiePointGrid incidenceAngle = OperatorUtils.getIncidenceAngle(sourceProduct); final TiePointGrid slantRgTime = OperatorUtils.getSlantRangeTime(sourceProduct); if (lat == null || lon == null || incidenceAngle == null || slantRgTime == null) { // for unit test ProductUtils.copyTiePointGrids(sourceProduct, targetProduct); ProductUtils.copyGeoCoding(sourceProduct, targetProduct); return; } final int gridWidth = 11; final int gridHeight = 11; final float subSamplingX = targetImageWidth / (gridWidth - 1.0f); final float subSamplingY = targetImageHeight / (gridHeight - 1.0f); final PixelPos[] newTiePointPos = new PixelPos[gridWidth * gridHeight]; int k = 0; for (int j = 0; j < gridHeight; j++) { final float ty = Math.min(j * subSamplingY, targetImageHeight - 1); final float y = (int) (ty * stepAzimuth + 0.5) + (int) (filterHeight * 0.5); for (int i = 0; i < gridWidth; i++) { final float tx = Math.min(i * subSamplingX, targetImageWidth - 1); final float x = (int) (tx * stepRange + 0.5) + (int) (filterWidth * 0.5); newTiePointPos[k] = new PixelPos(); newTiePointPos[k].x = x; newTiePointPos[k].y = y; k++; } } OperatorUtils.createNewTiePointGridsAndGeoCoding(sourceProduct, targetProduct, gridWidth, gridHeight, subSamplingX, subSamplingY, newTiePointPos); } /** * Update metadata in the target product. */ private void updateTargetProductMetadata() { final MetadataElement absTgt = AbstractMetadata.getAbstractedMetadata(targetProduct); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.azimuth_spacing, azimuthSpacing); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.range_spacing, rangeSpacing); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_samples_per_line, targetImageWidth); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_output_lines, targetImageHeight); final float oldLineTimeInterval = (float) absTgt.getAttributeDouble(AbstractMetadata.line_time_interval); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.line_time_interval, oldLineTimeInterval * (float) stepAzimuth); final String oldFirstLineTime = absTgt.getAttributeString(AbstractMetadata.first_line_time); final int idx = oldFirstLineTime.lastIndexOf(':') + 1; final String oldSecondsStr = oldFirstLineTime.substring(idx); final double oldSeconds = Double.parseDouble(oldSecondsStr); final double newSeconds = oldSeconds + oldLineTimeInterval * (filterHeight - 1) / 2.0; final String newFirstLineTime = String.valueOf(oldFirstLineTime.subSequence(0, idx)) + newSeconds + "000000"; AbstractMetadata.setAttribute(absTgt, AbstractMetadata.first_line_time, AbstractMetadata.parseUTC(newFirstLineTime.substring(0, 27))); // AbstractMetadata.parseUTC(newFirstLineTime.substring(0,27), "dd-MMM-yyyy HH:mm:ss")); } @Override public void computeTile(Band targetBand, Tile targetTile, ProgressMonitor pm) throws OperatorException { try { if (method.equals(SUB_SAMPLING)) { computeTileUsingSubSampling(targetBand, targetTile, pm); } else if (method.equals(KERNEL_FILTERING)) { computeTileUsingKernelFiltering(targetBand, targetTile); } else { throw new OperatorException("Unknown undersampling method: " + method); } } catch (Throwable e) { OperatorUtils.catchOperatorException(getId(), e); } finally { pm.done(); } } private void computeTileUsingSubSampling(Band targetBand, Tile targetTile, ProgressMonitor pm) { final ProductData destBuffer = targetTile.getRawSamples(); final Rectangle rectangle = targetTile.getRectangle(); try { subsetReader.readBandRasterData(targetBand, rectangle.x, rectangle.y, rectangle.width, rectangle.height, destBuffer, pm); targetTile.setRawSamples(destBuffer); } catch (IOException e) { throw new OperatorException(e); } } private void computeTileUsingKernelFiltering(Band targetBand, Tile targetTile) { final Rectangle targetTileRectangle = targetTile.getRectangle(); final int tx0 = targetTileRectangle.x; final int ty0 = targetTileRectangle.y; final int tw = targetTileRectangle.width; final int th = targetTileRectangle.height; //System.out.println("tx0 = " + tx0 + ", ty0 = " + ty0 + ", tw = " + tw + ", th = " + th); final int x0 = (int) (tx0 * stepRange + 0.5f); final int y0 = (int) (ty0 * stepAzimuth + 0.5f); final int w = (int) ((tx0 + tw - 1) * stepRange + 0.5f) + filterWidth - (int) (tx0 * stepRange + 0.5f); final int h = (int) ((ty0 + th - 1) * stepAzimuth + 0.5f) + filterHeight - (int) (ty0 * stepAzimuth + 0.5f); final Rectangle sourceTileRectangle = new Rectangle(x0, y0, w, h); //System.out.println("x0 = " + x0 + ", y0 = " + y0 + ", w = " + w + ", h = " + h); Tile sourceRaster1 = null; Tile sourceRaster2 = null; Band sourceBand1; final String[] srcBandNames = targetBandNameToSourceBandName.get(targetBand.getName()); if (srcBandNames.length == 1) { sourceBand1 = sourceProduct.getBand(srcBandNames[0]); sourceRaster1 = getSourceTile(sourceBand1, sourceTileRectangle); if (sourceRaster1 == null) { throw new OperatorException("Cannot get source tile"); } } else { sourceBand1 = sourceProduct.getBand(srcBandNames[0]); final Band sourceBand2 = sourceProduct.getBand(srcBandNames[1]); sourceRaster1 = getSourceTile(sourceBand1, sourceTileRectangle); sourceRaster2 = getSourceTile(sourceBand2, sourceTileRectangle); if (sourceRaster1 == null || sourceRaster2 == null) { throw new OperatorException("Cannot get source tile"); } } final Unit.UnitType bandUnitType = Unit.getUnitType(sourceBand1); final ProductData trgData = targetTile.getDataBuffer(); double filteredValue; final int maxy = ty0 + th; final int maxx = tx0 + tw; for (int ty = ty0; ty < maxy; ty++) { for (int tx = tx0; tx < maxx; tx++) { filteredValue = getFilteredValue(tx, ty, sourceRaster1, sourceRaster2, bandUnitType); trgData.setElemDoubleAt(targetTile.getDataBufferIndex(tx, ty), filteredValue); } } } private double getFilteredValue(int tx, int ty, Tile sourceRaster1, Tile sourceRaster2, Unit.UnitType bandUnitType) { final int x0 = (int) (tx * stepRange + 0.5); final int y0 = (int) (ty * stepAzimuth + 0.5); final int maxY = y0 + filterHeight; final int maxX = x0 + filterWidth; final ProductData srcData1 = sourceRaster1.getDataBuffer(); ProductData srcData2 = null; if (sourceRaster2 != null) srcData2 = sourceRaster2.getDataBuffer(); float numPixels = filterWidth * filterHeight; double filteredValue = 0.0; for (int y = y0; y < maxY; y++) { for (int x = x0; x < maxX; x++) { final int index = sourceRaster1.getDataBufferIndex(x, y); final float weight = kernel[maxY - 1 - y][maxX - 1 - x] / numPixels; if (bandUnitType == Unit.UnitType.INTENSITY_DB || bandUnitType == Unit.UnitType.AMPLITUDE_DB) { final double dn = srcData1.getElemDoubleAt(index); filteredValue += FastMath.pow(10, dn / 10.0) * weight; // dB to linear } else if (sourceRaster2 == null) { filteredValue += srcData1.getElemDoubleAt(index) * weight; } else { // COMPLEX final double i = srcData1.getElemDoubleAt(index); final double q = srcData2.getElemDoubleAt(index); filteredValue += (i * i + q * q) * weight; } } } if (bandUnitType == Unit.UnitType.INTENSITY_DB || bandUnitType == Unit.UnitType.AMPLITUDE_DB) { filteredValue = 10.0 * Math.log10(filteredValue); // linear to dB } return filteredValue; } /** * Set undersampling method. The function is for unit test only. * * @param samplingMethod The undersampling method. */ public void setUndersamplingMethod(String samplingMethod) { method = samplingMethod; } /** * Set sub-sampling rate for both x and y. The function is for unit test only. * * @param subSamplingRateX The sub-sampling rate for x. * @param subSamplingRateY The sub-sampling rate for y. */ public void setSubSamplingRate(int subSamplingRateX, int subSamplingRateY) { subSamplingX = subSamplingRateX; subSamplingY = subSamplingRateY; } /** * Set filter type. The function is for unit test only. * * @param type The filter type. */ public void setFilterType(String type) { filterType = type; } /** * Set filter size. The function is for unit test only. * * @param size The filter size. */ public void setFilterSize(String size) { filterSize = size; } /** * Set the output image dimension. The function is for unit test only. * * @param numRows The number of rows. * @param numCols The number of columns. */ public void setOutputImageSize(int numRows, int numCols) { targetImageHeight = numRows; targetImageWidth = numCols; } /** * Set the output image method. The function is for unit test only. * * @param method The output image method. */ public void setOutputImageBy(String method) { outputImageBy = method; } /** * The SPI is used to register this operator in the graph processing framework * via the SPI configuration file * {@code META-INF/services/org.esa.beam.framework.gpf.OperatorSpi}. * This class may also serve as a factory for new operator instances. * * @see org.esa.beam.framework.gpf.OperatorSpi#createOperator() * @see org.esa.beam.framework.gpf.OperatorSpi#createOperator(java.util.Map, java.util.Map) */ public static class Spi extends OperatorSpi { public Spi() { super(UndersamplingOp.class); } } }