/* * 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.s1tbx.sentinel1.gpf; import com.bc.ceres.core.ProgressMonitor; import org.esa.s1tbx.insar.gpf.support.Sentinel1Utils; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.GeoCoding; import org.esa.snap.core.datamodel.GeoPos; import org.esa.snap.core.datamodel.MetadataAttribute; import org.esa.snap.core.datamodel.MetadataElement; import org.esa.snap.core.datamodel.PixelPos; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductData; import org.esa.snap.core.datamodel.TiePointGeoCoding; import org.esa.snap.core.datamodel.TiePointGrid; import org.esa.snap.core.datamodel.VirtualBand; import org.esa.snap.core.gpf.Operator; import org.esa.snap.core.gpf.OperatorException; import org.esa.snap.core.gpf.OperatorSpi; import org.esa.snap.core.gpf.Tile; import org.esa.snap.core.gpf.annotations.OperatorMetadata; import org.esa.snap.core.gpf.annotations.Parameter; import org.esa.snap.core.gpf.annotations.SourceProduct; import org.esa.snap.core.gpf.annotations.TargetProduct; import org.esa.snap.core.util.ProductUtils; import org.esa.snap.engine_utilities.datamodel.AbstractMetadata; import org.esa.snap.engine_utilities.datamodel.Unit; import org.esa.snap.engine_utilities.eo.Constants; import org.esa.snap.engine_utilities.gpf.InputProductValidator; import org.esa.snap.engine_utilities.gpf.OperatorUtils; import org.esa.snap.engine_utilities.gpf.ReaderUtils; import org.esa.snap.engine_utilities.gpf.TileIndex; import org.esa.snap.engine_utilities.util.Maths; import java.awt.Rectangle; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; /** * De-Burst a Sentinel-1 TOPSAR product */ @OperatorMetadata(alias = "TOPSAR-Deburst", category = "Radar/Sentinel-1 TOPS", authors = "Jun Lu, Luis Veci", version = "1.0", copyright = "Copyright (C) 2014 by Array Systems Computing Inc.", description = "Debursts a Sentinel-1 TOPSAR product") public final class TOPSARDeburstOp extends Operator { @SourceProduct(alias = "source") private Product sourceProduct; @TargetProduct private Product targetProduct; @Parameter(description = "The list of polarisations", label = "Polarisations") private String[] selectedPolarisations; private MetadataElement absRoot = null; private String acquisitionMode = null; private String productType = null; private int numOfSubSwath = 0; private int subSwathIndex = 0; private int targetWidth = 0; private int targetHeight = 0; private double targetFirstLineTime = 0; private double targetLastLineTime = 0; private double targetLineTimeInterval = 0; private double targetSlantRangeTimeToFirstPixel = 0; private double targetSlantRangeTimeToLastPixel = 0; private double targetDeltaSlantRangeTime = 0; private SubSwathEffectStartEndPixels[] subSwathEffectStartEndPixels = null; private Sentinel1Utils su = null; private Sentinel1Utils.SubSwathInfo[] subSwath = null; private static int numOfBoundaryPoints = 6; private static final String PRODUCT_SUFFIX = "_Deb"; /** * Default constructor. The graph processing framework * requires that an operator has a default constructor. */ public TOPSARDeburstOp() { } /** * Initializes this operator and sets the one and only target product. * <p>The target product can be either defined by a field of type {@link Product} annotated with the * {@link TargetProduct TargetProduct} annotation or * by calling {@link #setTargetProduct} method.</p> * <p>The framework calls this method after it has created this operator. * Any client code that must be performed before computation of tile data * should be placed here.</p> * * @throws OperatorException If an error occurs during operator initialisation. * @see #getTargetProduct() */ @Override public void initialize() throws OperatorException { try { final InputProductValidator validator = new InputProductValidator(sourceProduct); validator.checkIfSARProduct(); validator.checkIfSentinel1Product(); validator.checkProductType(new String[]{"SLC"}); validator.checkAcquisitionMode(new String[]{"IW","EW"}); absRoot = AbstractMetadata.getAbstractedMetadata(sourceProduct); getProductType(); getAcquisitionMode(); su = new Sentinel1Utils(sourceProduct); subSwath = su.getSubSwath(); numOfSubSwath = su.getNumOfSubSwath(); //checkIfSplitProduct(); if (selectedPolarisations == null || selectedPolarisations.length == 0) { selectedPolarisations = su.getPolarizations(); } computeTargetStartEndTime(); computeTargetSlantRangeTimeToFirstAndLastPixels(); computeTargetWidthAndHeight(); createTargetProduct(); computeSubSwathEffectStartEndPixels(); updateTargetProductMetadata(); } catch (Throwable e) { OperatorUtils.catchOperatorException(getId(), e); } } /** * Get product type from abstracted metadata. */ private void getProductType() { productType = absRoot.getAttributeString(AbstractMetadata.PRODUCT_TYPE); } /** * Get acquisition mode from abstracted metadata. */ private void getAcquisitionMode() throws OperatorException { acquisitionMode = absRoot.getAttributeString(AbstractMetadata.ACQUISITION_MODE); } /** * Compute azimuth time for the first and last line in the target product. */ private void computeTargetStartEndTime() { targetFirstLineTime = subSwath[0].firstLineTime; targetLastLineTime = subSwath[0].lastLineTime; for (int i = 1; i < numOfSubSwath; i++) { if (targetFirstLineTime > subSwath[i].firstLineTime) { targetFirstLineTime = subSwath[i].firstLineTime; } if (targetLastLineTime < subSwath[i].lastLineTime) { targetLastLineTime = subSwath[i].lastLineTime; } } targetLineTimeInterval = subSwath[0].azimuthTimeInterval; } /** * Compute slant range time to the first and last pixels in the target product. */ private void computeTargetSlantRangeTimeToFirstAndLastPixels() { targetSlantRangeTimeToFirstPixel = subSwath[0].slrTimeToFirstPixel; targetSlantRangeTimeToLastPixel = subSwath[numOfSubSwath - 1].slrTimeToLastPixel; targetDeltaSlantRangeTime = subSwath[0].rangePixelSpacing / Constants.lightSpeed; } /** * Compute target product dimension. */ private void computeTargetWidthAndHeight() { targetHeight = (int)((targetLastLineTime - targetFirstLineTime) / targetLineTimeInterval); targetWidth = (int)((targetSlantRangeTimeToLastPixel - targetSlantRangeTimeToFirstPixel) / targetDeltaSlantRangeTime); } private void computeSubSwathEffectStartEndPixels() { subSwathEffectStartEndPixels = new SubSwathEffectStartEndPixels[numOfSubSwath]; for (int i = 0; i < numOfSubSwath; i++) { subSwathEffectStartEndPixels[i] = new SubSwathEffectStartEndPixels(); if (i == 0) { subSwathEffectStartEndPixels[i].xMin = 0; } else { final double midTime = (subSwath[i - 1].slrTimeToLastValidPixel + subSwath[i].slrTimeToFirstValidPixel) / 2.0; subSwathEffectStartEndPixels[i].xMin = (int)Math.round((midTime - subSwath[i].slrTimeToFirstPixel) / targetDeltaSlantRangeTime); } if (i < numOfSubSwath - 1) { final double midTime = (subSwath[i].slrTimeToLastValidPixel + subSwath[i + 1].slrTimeToFirstValidPixel) / 2.0; subSwathEffectStartEndPixels[i].xMax = (int)Math.round((midTime - subSwath[i].slrTimeToFirstPixel) / targetDeltaSlantRangeTime); } else { subSwathEffectStartEndPixels[i].xMax = (int)Math.round((subSwath[i].slrTimeToLastPixel - subSwath[i].slrTimeToFirstPixel) / targetDeltaSlantRangeTime); } } } /** * Create target product. */ private void createTargetProduct() { targetProduct = new Product(sourceProduct.getName() + PRODUCT_SUFFIX, sourceProduct.getProductType(), targetWidth, targetHeight); final Band[] sourceBands = sourceProduct.getBands(); // source band name is assumed in format: name_acquisitionModeAndSubSwathIndex_polarization_prefix // target band name is then in format: name_polarization_prefix boolean hasVirtualPhaseBands = false; for (Band srcBand:sourceBands) { if (srcBand instanceof VirtualBand && srcBand.getName().toLowerCase().contains("phase")) { hasVirtualPhaseBands = true; break; } } for (Band srcBand:sourceBands) { final String srcBandName = srcBand.getName(); if (!containSelectedPolarisations(srcBandName)) { continue; } if (srcBand instanceof VirtualBand) { continue; } final String tgtBandName = getTargetBandNameFromSourceBandName(srcBandName); if (!targetProduct.containsBand(tgtBandName)) { final Band trgBand = targetProduct.addBand(tgtBandName, srcBand.getDataType()); trgBand.setUnit(srcBand.getUnit()); trgBand.setNoDataValueUsed(true); trgBand.setNoDataValue(srcBand.getNoDataValue()); int i = targetProduct.getBandIndex(tgtBandName); if (trgBand.getUnit().equals(Unit.IMAGINARY) && i-1 >= 0) { Band iBand = targetProduct.getBandAt(i-1); if (iBand.getUnit().equals(Unit.REAL)) { ReaderUtils.createVirtualIntensityBand( targetProduct, iBand, trgBand, '_' + getPrefix(trgBand.getName())); if (hasVirtualPhaseBands) { ReaderUtils.createVirtualPhaseBand(targetProduct, iBand, trgBand, '_' + getPrefix(trgBand.getName())); } } } } } ProductUtils.copyMetadata(sourceProduct, targetProduct); ProductUtils.copyFlagCodings(sourceProduct, targetProduct); ProductUtils.copyQuicklookBandName(sourceProduct, targetProduct); targetProduct.setStartTime(new ProductData.UTC(targetFirstLineTime/Constants.secondsInDay)); targetProduct.setEndTime(new ProductData.UTC(targetLastLineTime/Constants.secondsInDay)); targetProduct.setDescription(sourceProduct.getDescription()); createTiePointGrids(); if(sourceProduct.getQuicklookBandName() != null) { if(targetProduct.getBand(sourceProduct.getQuicklookBandName()) != null) { targetProduct.setQuicklookBandName(sourceProduct.getQuicklookBandName()); } } } private String getTargetBandNameFromSourceBandName(final String srcBandName) { if (numOfSubSwath == 1) { return srcBandName; } final int firstSeparationIdx = srcBandName.indexOf(acquisitionMode); final int secondSeparationIdx = srcBandName.indexOf('_', firstSeparationIdx + 1); return srcBandName.substring(0, firstSeparationIdx) + srcBandName.substring(secondSeparationIdx + 1); } private String getSourceBandNameFromTargetBandName( final String tgtBandName, final String acquisitionMode, final String swathIndexStr) { if (numOfSubSwath == 1) { return tgtBandName; } final String[] srcBandNames = sourceProduct.getBandNames(); for (String srcBandName:srcBandNames) { if (srcBandName.contains(acquisitionMode + swathIndexStr) && getTargetBandNameFromSourceBandName(srcBandName).equals(tgtBandName)) { return srcBandName; } } return null; } private static String getPrefix(final String tgtBandName) { final int firstSeparationIdx = tgtBandName.indexOf('_'); return tgtBandName.substring(firstSeparationIdx+1); } private boolean containSelectedPolarisations(final String bandName) { String bandNamePol = OperatorUtils.getPolarizationFromBandName(bandName); if(bandNamePol == null) return true; for (String pol : selectedPolarisations) { if (bandName.contains(pol)) { return true; } } return false; } /** * Create target product tie point grid. */ private void createTiePointGrids() { final int gridWidth = 20; final int gridHeight = 5; final int subSamplingX = targetWidth / gridWidth; final int subSamplingY = targetHeight / gridHeight; final int maxList = (gridWidth+1) * (gridHeight+1); final float[] latList = new float[maxList]; final float[] lonList = new float[maxList]; final float[] slrtList = new float[maxList]; final float[] incList = new float[maxList]; int k = 0; for (int i = 0; i <= gridHeight; i++) { final int y = i * subSamplingY; final double azTime = targetFirstLineTime + y * targetLineTimeInterval; for (int j = 0; j <= gridWidth; j++) { final int x = j * subSamplingX; final double slrTime = targetSlantRangeTimeToFirstPixel + x * targetDeltaSlantRangeTime; latList[k] = (float)su.getLatitude(azTime, slrTime); lonList[k] = (float)su.getLongitude(azTime, slrTime); slrtList[k] = (float)(su.getSlantRangeTime(azTime, slrTime) * 2 * Constants.oneBillion); // 2-way ns incList[k] = (float)su.getIncidenceAngle(azTime, slrTime); k++; } } final TiePointGrid latGrid = new TiePointGrid( OperatorUtils.TPG_LATITUDE, gridWidth+1, gridHeight+1, 0, 0, subSamplingX, subSamplingY, latList); final TiePointGrid lonGrid = new TiePointGrid( OperatorUtils.TPG_LONGITUDE, gridWidth+1, gridHeight+1, 0, 0, subSamplingX, subSamplingY, lonList); final TiePointGrid slrtGrid = new TiePointGrid( OperatorUtils.TPG_SLANT_RANGE_TIME, gridWidth+1, gridHeight+1, 0, 0, subSamplingX, subSamplingY, slrtList); final TiePointGrid incGrid = new TiePointGrid( OperatorUtils.TPG_INCIDENT_ANGLE, gridWidth+1, gridHeight+1, 0, 0, subSamplingX, subSamplingY, incList); latGrid.setUnit(Unit.DEGREES); lonGrid.setUnit(Unit.DEGREES); slrtGrid.setUnit(Unit.NANOSECONDS); incGrid.setUnit(Unit.DEGREES); targetProduct.addTiePointGrid(latGrid); targetProduct.addTiePointGrid(lonGrid); targetProduct.addTiePointGrid(slrtGrid); targetProduct.addTiePointGrid(incGrid); final TiePointGeoCoding tpGeoCoding = new TiePointGeoCoding(latGrid, lonGrid); targetProduct.setSceneGeoCoding(tpGeoCoding); } /** * Update target product metadata. */ private void updateTargetProductMetadata() throws IOException { updateAbstractMetadata(); updateOriginalMetadata(); } private void updateAbstractMetadata() { final MetadataElement absTgt = AbstractMetadata.getAbstractedMetadata(targetProduct); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_output_lines, targetHeight); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_samples_per_line, targetWidth); absTgt.setAttributeUTC( AbstractMetadata.first_line_time, new ProductData.UTC(targetFirstLineTime/Constants.secondsInDay)); absTgt.setAttributeUTC( AbstractMetadata.last_line_time, new ProductData.UTC(targetLastLineTime/Constants.secondsInDay)); absTgt.setAttributeDouble(AbstractMetadata.line_time_interval, targetLineTimeInterval); TiePointGrid latGrid = targetProduct.getTiePointGrid(OperatorUtils.TPG_LATITUDE); TiePointGrid lonGrid = targetProduct.getTiePointGrid(OperatorUtils.TPG_LONGITUDE); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.first_near_lat, latGrid.getPixelFloat(0, 0)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.first_near_long, lonGrid.getPixelFloat(0, 0)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.first_far_lat, latGrid.getPixelFloat(targetWidth, 0)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.first_far_long, lonGrid.getPixelFloat(targetWidth, 0)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.last_near_lat, latGrid.getPixelFloat(0, targetHeight)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.last_near_long, lonGrid.getPixelFloat(0, targetHeight)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.last_far_lat, latGrid.getPixelFloat(targetWidth, targetHeight)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.last_far_long, lonGrid.getPixelFloat(targetWidth, targetHeight)); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.slant_range_to_first_pixel, targetSlantRangeTimeToFirstPixel * Constants.lightSpeed); addBurstBoundary(absTgt); for(MetadataElement elem : absTgt.getElements()) { if(elem.getName().startsWith(AbstractMetadata.BAND_PREFIX)) { absTgt.removeElement(elem); } } if (numOfSubSwath == 1) { absTgt.addAttribute(new MetadataAttribute("firstValidPixel", ProductData.TYPE_INT16)); absTgt.setAttributeInt("firstValidPixel", subSwath[0].firstValidPixel); absTgt.addAttribute(new MetadataAttribute("lastValidPixel", ProductData.TYPE_INT16)); absTgt.setAttributeInt("lastValidPixel", subSwath[0].lastValidPixel); absTgt.addAttribute(new MetadataAttribute("slrTimeToFirstValidPixel", ProductData.TYPE_FLOAT64)); absTgt.setAttributeDouble("slrTimeToFirstValidPixel", subSwath[0].slrTimeToFirstValidPixel); absTgt.addAttribute(new MetadataAttribute("slrTimeToLastValidPixel", ProductData.TYPE_FLOAT64)); absTgt.setAttributeDouble("slrTimeToLastValidPixel", subSwath[0].slrTimeToLastValidPixel); absTgt.addAttribute(new MetadataAttribute("firstValidLineTime", ProductData.TYPE_FLOAT64)); absTgt.setAttributeDouble("firstValidLineTime", subSwath[0].firstValidLineTime); absTgt.addAttribute(new MetadataAttribute("lastValidLineTime", ProductData.TYPE_FLOAT64)); absTgt.setAttributeDouble("lastValidLineTime", subSwath[0].lastValidLineTime); } } private void addBurstBoundary(final MetadataElement absTgt) { final GeoCoding targetGeoCoding = targetProduct.getSceneGeoCoding(); final List<String> swathList = new ArrayList<>(5); for(MetadataElement elem : absTgt.getElements()) { if(elem.getName().startsWith(AbstractMetadata.BAND_PREFIX)) { final String swath = elem.getAttributeString("swath"); if (swath != null && !swathList.contains(swath)) { swathList.add(swath); } } } double firstLineTime = 0.0, lastLineTime = 0.0, firstPixelTime = 0.0, lastPixelTime = 0.0; final MetadataElement burstBoundary = new MetadataElement("BurstBoundary"); for (int i = 0; i < swathList.size(); i++) { final String subSwathName = swathList.get(i); final MetadataElement swathElem = new MetadataElement(subSwathName); swathElem.addAttribute(new MetadataAttribute("count", ProductData.TYPE_INT16)); swathElem.setAttributeInt("count", subSwath[i].numOfBursts); for (int b = 0; b < subSwath[i].numOfBursts; b++) { final MetadataElement burstElem = new MetadataElement("Burst"+b); final MetadataElement firstLineElem = new MetadataElement("FirstLineBoundaryPoints"); final MetadataElement lastLineElem = new MetadataElement("LastLineBoundaryPoints"); if (b == 0) { firstLineTime = subSwath[i].burstFirstLineTime[b]; } else { firstLineTime = (subSwath[i].burstLastLineTime[b-1] + subSwath[i].burstFirstLineTime[b]) / 2.0; } if (b == subSwath[i].numOfBursts - 1) { lastLineTime = subSwath[i].burstLastLineTime[b]; } else { lastLineTime = (subSwath[i].burstLastLineTime[b] + subSwath[i].burstFirstLineTime[b+1]) / 2.0; } if (i == 0) { firstPixelTime = subSwath[i].slrTimeToFirstValidPixel; } else { firstPixelTime = (subSwath[i-1].slrTimeToLastValidPixel + subSwath[i].slrTimeToFirstValidPixel) / 2.0; } if (i == swathList.size() - 1) { lastPixelTime = subSwath[i].slrTimeToLastValidPixel; } else { lastPixelTime = (subSwath[i].slrTimeToLastValidPixel + subSwath[i+1].slrTimeToFirstValidPixel) / 2.0; } final double deltaTime = (lastPixelTime - firstPixelTime) / (numOfBoundaryPoints - 1); for (int p = 0; p < numOfBoundaryPoints; p++) { final double slrtToPoint = firstPixelTime + p*deltaTime; final MetadataElement firstLinePointElem = createPointElement(firstLineTime, slrtToPoint, targetGeoCoding); firstLineElem.addElement(firstLinePointElem); final MetadataElement lastLinePointElem = createPointElement(lastLineTime, slrtToPoint, targetGeoCoding); lastLineElem.addElement(lastLinePointElem); } burstElem.addElement(firstLineElem); burstElem.addElement(lastLineElem); swathElem.addElement(burstElem); } burstBoundary.addElement(swathElem); } absTgt.addElement(burstBoundary); } private MetadataElement createPointElement( final double lineTime, final double pixelTime, final GeoCoding targetGeoCoding) { final MetadataElement pointElem = new MetadataElement("BoundaryPoint"); final int x = (int)((pixelTime - targetSlantRangeTimeToFirstPixel) / targetDeltaSlantRangeTime); final int y = (int)((lineTime - targetFirstLineTime) / targetLineTimeInterval); GeoPos geoPos = new GeoPos(); targetGeoCoding.getGeoPos(new PixelPos(x, y), geoPos); pointElem.addAttribute(new MetadataAttribute("lat", ProductData.TYPE_FLOAT32)); pointElem.setAttributeDouble("lat", geoPos.lat); pointElem.addAttribute(new MetadataAttribute("lon", ProductData.TYPE_FLOAT32)); pointElem.setAttributeDouble("lon", geoPos.lon); return pointElem; } private void updateOriginalMetadata() throws IOException { updateSwathTiming(); if (su.getNumOfSubSwath() > 1) { updateCalibrationVector(); //updateNoiseVector(); //todo: not implemented yet } } private void updateSwathTiming() { final MetadataElement origProdRoot = AbstractMetadata.getOriginalProductMetadata(targetProduct); MetadataElement annotation = origProdRoot.getElement("annotation"); if (annotation == null) { throw new OperatorException("Annotation Metadata not found"); } final MetadataElement[] elems = annotation.getElements(); for (MetadataElement elem : elems) { final MetadataElement product = elem.getElement("product"); final MetadataElement swathTiming = product.getElement("swathTiming"); swathTiming.setAttributeString("linesPerBurst", "0"); swathTiming.setAttributeString("samplesPerBurst", "0"); final MetadataElement burstList = swathTiming.getElement("burstList"); burstList.setAttributeString("count", "0"); final MetadataElement[] burstListElem = burstList.getElements(); for (MetadataElement aBurstListElem : burstListElem) { burstList.removeElement(aBurstListElem); } } } private static String getMissionPrefix(final MetadataElement absRoot) { final String mission = absRoot.getAttributeString(AbstractMetadata.MISSION); return "S1"+mission.substring(mission.length()-1, mission.length()); } private void updateCalibrationVector() throws IOException { final String[] selectedPols = Sentinel1Utils.getProductPolarizations(absRoot); final MetadataElement origMeta = AbstractMetadata.getOriginalProductMetadata(sourceProduct); if(origMeta == null) { throw new IOException("Original product metadata not found"); } final MetadataElement srcCalibration = origMeta.getElement("calibration"); if(srcCalibration == null) { throw new IOException("Calibration element not found in Original product metadata"); } final MetadataElement bandCalibration = srcCalibration.getElementAt(0).getElement("calibration"); final String missionPrefix = getMissionPrefix(absRoot).toLowerCase(); final MetadataElement origProdRoot = AbstractMetadata.getOriginalProductMetadata(targetProduct); origProdRoot.removeElement(origProdRoot.getElement("calibration")); final MetadataElement calibration = new MetadataElement("calibration"); for (String pol : selectedPols) { final String elemName = missionPrefix + '-' + acquisitionMode + '-' + productType + '-' + pol; final MetadataElement elem = new MetadataElement(elemName); final MetadataElement calElem = bandCalibration.createDeepClone(); final MetadataElement calibrationVectorListElem = calElem.getElement("calibrationVectorList"); calElem.setAttributeString("polarisation", pol); final MetadataElement[] list = calibrationVectorListElem.getElements(); int vectorIndex = 0; final String mergedPixelStr = getMergedPixels(pol); final StringTokenizer tokenizer = new StringTokenizer(mergedPixelStr, " "); final int count = tokenizer.countTokens(); for (MetadataElement calibrationVectorElem : list) { final MetadataElement pixelElem = calibrationVectorElem.getElement("pixel"); pixelElem.setAttributeString("pixel", mergedPixelStr); pixelElem.setAttributeString("count", Integer.toString(count)); final MetadataElement sigmaNoughtElem = calibrationVectorElem.getElement("sigmaNought"); final String mergedSigmaNoughtStr = getMergedVector("SigmaNought", pol, vectorIndex); sigmaNoughtElem.setAttributeString("sigmaNought", mergedSigmaNoughtStr); sigmaNoughtElem.setAttributeString("count", Integer.toString(count)); final MetadataElement betaNoughtElem = calibrationVectorElem.getElement("betaNought"); final String mergedBetaNoughtStr = getMergedVector("betaNought", pol, vectorIndex); betaNoughtElem.setAttributeString("betaNought", mergedBetaNoughtStr); betaNoughtElem.setAttributeString("count", Integer.toString(count)); final MetadataElement gammaNoughtElem = calibrationVectorElem.getElement("gamma"); final String mergedGammaNoughtStr = getMergedVector("gamma", pol, vectorIndex); gammaNoughtElem.setAttributeString("gamma", mergedGammaNoughtStr); gammaNoughtElem.setAttributeString("count", Integer.toString(count)); final MetadataElement dnElem = calibrationVectorElem.getElement("dn"); final String mergedDNStr = getMergedVector("dn", pol, vectorIndex); dnElem.setAttributeString("dn", mergedDNStr); dnElem.setAttributeString("count", Integer.toString(count)); vectorIndex++; } elem.addElement(calElem); calibration.addElement(elem); } origProdRoot.addElement(calibration); } private String getMergedPixels(final String pol) { final StringBuilder mergedPixelStr = new StringBuilder(""); for (int s = 0; s < numOfSubSwath; s++) { final int[] pixelArray = su.getCalibrationPixel(s+1, pol, 0); for (int p:pixelArray) { if (p >= subSwathEffectStartEndPixels[s].xMin && p < subSwathEffectStartEndPixels[s].xMax) { final double slrt = subSwath[s].slrTimeToFirstPixel + p * targetDeltaSlantRangeTime; final int targetPixelIdx = (int)Math.round((slrt - targetSlantRangeTimeToFirstPixel) / targetDeltaSlantRangeTime); mergedPixelStr.append(targetPixelIdx + " "); } } } return mergedPixelStr.toString(); } private String getMergedVector(final String vectorName, final String pol, final int vectorIndex) { final StringBuilder mergedVectorStr = new StringBuilder(""); for (int s = 0; s < numOfSubSwath; s++) { final int[] pixelArray = su.getCalibrationPixel(s+1, pol, vectorIndex); final float[] vectorArray = su.getCalibrationVector(s+1, pol, vectorIndex, vectorName); for (int i = 0; i < pixelArray.length; i++) { if (pixelArray[i] >= subSwathEffectStartEndPixels[s].xMin && pixelArray[i] < subSwathEffectStartEndPixels[s].xMax) { mergedVectorStr.append(vectorArray[i]).append(' '); } } } return mergedVectorStr.toString(); } /** * Called by the framework in order to compute the stack of tiles for the given target bands. * <p>The default implementation throws a runtime exception with the message "not implemented".</p> * * @param targetTiles The current tiles to be computed for each target band. * @param pm A progress monitor which should be used to determine computation cancelation requests. * @throws OperatorException if an error occurs during computation of the target rasters. */ @Override public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle targetRectangle, ProgressMonitor pm) throws OperatorException { try { final int tx0 = targetRectangle.x; final int ty0 = targetRectangle.y; final int tw = targetRectangle.width; final int th = targetRectangle.height; //System.out.println("tx0 = " + tx0 + ", ty0 = " + ty0 + ", tw = " + tw + ", th = " + th); // determine subswaths covered by the tile final double tileSlrtToFirstPixel = targetSlantRangeTimeToFirstPixel + tx0 * targetDeltaSlantRangeTime; final double tileSlrtToLastPixel = targetSlantRangeTimeToFirstPixel + (tx0 + tw - 1) * targetDeltaSlantRangeTime; final double tileFirstLineTime = targetFirstLineTime + ty0 * targetLineTimeInterval; final double tileLastLineTime = targetFirstLineTime + (ty0 + th - 1) * targetLineTimeInterval; int firstSubSwathIndex = -1; int lastSubSwathIndex = -1; for (int i = 0; i < numOfSubSwath; i++) { if (tileSlrtToFirstPixel >= subSwath[i].slrTimeToFirstValidPixel && tileSlrtToFirstPixel <= subSwath[i].slrTimeToLastValidPixel) { if (tileFirstLineTime >= subSwath[i].burstFirstValidLineTime[0] && tileFirstLineTime < subSwath[i].burstLastLineTime[subSwath[i].numOfBursts - 1] || tileLastLineTime >= subSwath[i].burstFirstValidLineTime[0] && tileLastLineTime < subSwath[i].burstLastLineTime[subSwath[i].numOfBursts - 1]) { firstSubSwathIndex = i + 1; break; } } } if (firstSubSwathIndex == numOfSubSwath) { lastSubSwathIndex = firstSubSwathIndex; } else { for (int i = 0; i < numOfSubSwath; i++) { if (tileSlrtToLastPixel >= subSwath[i].slrTimeToFirstValidPixel && tileSlrtToLastPixel <= subSwath[i].slrTimeToLastValidPixel) { if (tileFirstLineTime >= subSwath[i].burstFirstValidLineTime[0] && tileFirstLineTime < subSwath[i].burstLastLineTime[subSwath[i].numOfBursts - 1] || tileLastLineTime >= subSwath[i].burstFirstValidLineTime[0] && tileLastLineTime < subSwath[i].burstLastLineTime[subSwath[i].numOfBursts - 1]) { lastSubSwathIndex = i + 1; } } } } if (firstSubSwathIndex == -1 && lastSubSwathIndex == -1) { return; } if (firstSubSwathIndex != -1 && lastSubSwathIndex == -1) { lastSubSwathIndex = firstSubSwathIndex; } if (firstSubSwathIndex == -1 && lastSubSwathIndex != -1) { firstSubSwathIndex = lastSubSwathIndex; } final int numOfSourceTiles = lastSubSwathIndex - firstSubSwathIndex + 1; final boolean tileInOneSubSwath = (numOfSourceTiles == 1); final Rectangle[] sourceRectangle = new Rectangle[numOfSourceTiles]; int k = 0; for (int i = firstSubSwathIndex; i <= lastSubSwathIndex; i++) { sourceRectangle[k++] = getSourceRectangle(tx0, ty0, tw, th, i); } final BurstInfo burstInfo = new BurstInfo(); final int txMax = tx0 + tw; final int tyMax = ty0 + th; final Band[] tgtBands = targetProduct.getBands(); for (Band tgtBand:tgtBands) { if (tgtBand instanceof VirtualBand) { continue; } final String tgtBandName = tgtBand.getName(); final int dataType = tgtBand.getDataType(); final Tile tgtTile = targetTiles.get(tgtBand); if (tileInOneSubSwath) { if (dataType == ProductData.TYPE_INT16) { computeTileInOneSwathShort(tx0, ty0, txMax, tyMax, firstSubSwathIndex, sourceRectangle, tgtBandName, tgtTile, burstInfo); } else { computeTileInOneSwathFloat(tx0, ty0, txMax, tyMax, firstSubSwathIndex, sourceRectangle, tgtBandName, tgtTile, burstInfo); } } else { if (dataType == ProductData.TYPE_INT16) { computeMultipleSubSwathsShort(tx0, ty0, txMax, tyMax, firstSubSwathIndex, lastSubSwathIndex, sourceRectangle, tgtBandName, tgtTile, burstInfo); } else { computeMultipleSubSwathsFloat(tx0, ty0, txMax, tyMax, firstSubSwathIndex, lastSubSwathIndex, sourceRectangle, tgtBandName, tgtTile, burstInfo); } } } } catch (Throwable e) { OperatorUtils.catchOperatorException(getId(), e); } finally { pm.done(); } } private void computeTileInOneSwathShort(final int tx0, final int ty0, final int txMax, final int tyMax, final int firstSubSwathIndex, final Rectangle[] sourceRectangle, final String tgtBandName, final Tile tgtTile, final BurstInfo burstInfo) { final int yMin = computeYMin(subSwath[firstSubSwathIndex - 1]); final int yMax = computeYMax(subSwath[firstSubSwathIndex - 1]); final int xMin = computeXMin(subSwath[firstSubSwathIndex - 1]); final int xMax = computeXMax(subSwath[firstSubSwathIndex - 1]); final int firstY = Math.max(ty0, yMin); final int lastY = Math.min(tyMax, yMax + 1); final int firstX = Math.max(tx0, xMin); final int lastX = Math.min(txMax, xMax + 1); if (firstY >= lastY || firstX >= lastX) { return; } final String swathIndexStr = numOfSubSwath == 1 ? su.getSubSwathNames()[0].substring(2) : String.valueOf(firstSubSwathIndex); final String srcBandName = getSourceBandNameFromTargetBandName(tgtBandName, acquisitionMode, swathIndexStr); final Band srcBand = sourceProduct.getBand(srcBandName); final Tile srcRaster = getSourceTile(srcBand, sourceRectangle[0]); final TileIndex srcTileIndex = new TileIndex(srcRaster); final TileIndex tgtIndex = new TileIndex(tgtTile); final short[] srcArray = (short[]) srcRaster.getDataBuffer().getElems(); final short[] tgtArray = (short[]) tgtTile.getDataBuffer().getElems(); for (int y = firstY; y < lastY; y++) { if (!getLineIndicesInSourceProduct(y, subSwath[firstSubSwathIndex - 1], burstInfo)) { continue; } final int tgtOffset = tgtIndex.calculateStride(y); final Sentinel1Utils.SubSwathInfo firstSubSwath = subSwath[firstSubSwathIndex - 1]; int offset; if (burstInfo.sy1 != -1 && burstInfo.targetTime > burstInfo.midTime) { offset = srcTileIndex.calculateStride(burstInfo.sy1); } else { offset = srcTileIndex.calculateStride(burstInfo.sy0); } final int sx = (int) Math.round(((targetSlantRangeTimeToFirstPixel + firstX * targetDeltaSlantRangeTime) - firstSubSwath.slrTimeToFirstPixel) / targetDeltaSlantRangeTime); System.arraycopy(srcArray, sx - offset, tgtArray, firstX - tgtOffset, lastX - firstX); } } private void computeTileInOneSwathFloat(final int tx0, final int ty0, final int txMax, final int tyMax, final int firstSubSwathIndex, final Rectangle[] sourceRectangle, final String tgtBandName, final Tile tgtTile, final BurstInfo burstInfo) { final int yMin = computeYMin(subSwath[firstSubSwathIndex - 1]); final int yMax = computeYMax(subSwath[firstSubSwathIndex - 1]); final int xMin = computeXMin(subSwath[firstSubSwathIndex - 1]); final int xMax = computeXMax(subSwath[firstSubSwathIndex - 1]); final int firstY = Math.max(ty0, yMin); final int lastY = Math.min(tyMax, yMax + 1); final int firstX = Math.max(tx0, xMin); final int lastX = Math.min(txMax, xMax + 1); if (firstY >= lastY || firstX >= lastX) { return; } final String swathIndexStr = numOfSubSwath == 1 ? su.getSubSwathNames()[0].substring(2) : String.valueOf(firstSubSwathIndex); final String srcBandName = getSourceBandNameFromTargetBandName(tgtBandName, acquisitionMode, swathIndexStr); final Band srcBand = sourceProduct.getBand(srcBandName); final Tile srcRaster = getSourceTile(srcBand, sourceRectangle[0]); final TileIndex srcTileIndex = new TileIndex(srcRaster); final TileIndex tgtIndex = new TileIndex(tgtTile); final float[] srcArray = (float[]) srcRaster.getDataBuffer().getElems(); final float[] tgtArray = (float[]) tgtTile.getDataBuffer().getElems(); for (int y = firstY; y < lastY; y++) { if (!getLineIndicesInSourceProduct(y, subSwath[firstSubSwathIndex - 1], burstInfo)) { continue; } final int tgtOffset = tgtIndex.calculateStride(y); final Sentinel1Utils.SubSwathInfo firstSubSwath = subSwath[firstSubSwathIndex - 1]; int offset; if (burstInfo.sy1 != -1 && burstInfo.targetTime > burstInfo.midTime) { offset = srcTileIndex.calculateStride(burstInfo.sy1); } else { offset = srcTileIndex.calculateStride(burstInfo.sy0); } final int sx = (int) Math.round(((targetSlantRangeTimeToFirstPixel + firstX * targetDeltaSlantRangeTime) - firstSubSwath.slrTimeToFirstPixel) / targetDeltaSlantRangeTime); System.arraycopy(srcArray, sx - offset, tgtArray, firstX - tgtOffset, lastX - firstX); } } private void computeMultipleSubSwathsShort(final int tx0, final int ty0, final int txMax, final int tyMax, final int firstSubSwathIndex, final int lastSubSwathIndex, final Rectangle[] sourceRectangle, final String tgtBandName, final Tile tgtTile, final BurstInfo burstInfo) { final int numOfSourceTiles = lastSubSwathIndex - firstSubSwathIndex + 1; final TileIndex tgtIndex = new TileIndex(tgtTile); final Tile[] srcTiles = new Tile[numOfSourceTiles]; final short[][] srcArray = new short[numOfSourceTiles][]; final short[] tgtArray = (short[]) tgtTile.getDataBuffer().getElems(); int k = 0; for (int i = firstSubSwathIndex; i <= lastSubSwathIndex; i++) { final String srcBandName = getSourceBandNameFromTargetBandName(tgtBandName, acquisitionMode, String.valueOf(i)); final Band srcBand = sourceProduct.getBand(srcBandName); final Tile srcRaster = getSourceTile(srcBand, sourceRectangle[k]); srcTiles[k] = srcRaster; srcArray[k] = (short[]) srcRaster.getDataBuffer().getElems(); k++; } int sy; for (int y = ty0; y < tyMax; y++) { final int tgtOffset = tgtIndex.calculateStride(y); for (int x = tx0; x < txMax; x++) { int subswathIndex = getSubSwathIndex(x, y, firstSubSwathIndex, lastSubSwathIndex, burstInfo); if (subswathIndex == -1) { continue; } if (!getLineIndicesInSourceProduct(y, subSwath[subswathIndex - 1], burstInfo)) { continue; } short val = 0; k = subswathIndex - firstSubSwathIndex; int sx = getSampleIndexInSourceProduct(x, subSwath[subswathIndex - 1]); if (burstInfo.sy1 != -1 && burstInfo.targetTime > burstInfo.midTime) { sy = burstInfo.sy1; } else { sy = burstInfo.sy0; } int idx = srcTiles[k].getDataBufferIndex(sx, sy); if (idx >= 0) { val = srcArray[k][idx]; } if(burstInfo.swath1 != -1 && val == 0) { // edge of swaths found therefore use other swath if (subswathIndex == burstInfo.swath0) { subswathIndex = burstInfo.swath1; } else { subswathIndex = burstInfo.swath0; } getLineIndicesInSourceProduct(y, subSwath[subswathIndex - 1], burstInfo); k = subswathIndex - firstSubSwathIndex; sx = getSampleIndexInSourceProduct(x, subSwath[subswathIndex - 1]); if (burstInfo.sy1 != -1 && burstInfo.targetTime > burstInfo.midTime) { sy = burstInfo.sy1; } else { sy = burstInfo.sy0; } idx = srcTiles[k].getDataBufferIndex(sx, sy); if (idx >= 0 && !(srcArray[k][idx] == 0)) { val = srcArray[k][idx]; } } tgtArray[x - tgtOffset] = val; } } } private void computeMultipleSubSwathsFloat(final int tx0, final int ty0, final int txMax, final int tyMax, final int firstSubSwathIndex, final int lastSubSwathIndex, final Rectangle[] sourceRectangle, final String tgtBandName, final Tile tgtTile, final BurstInfo burstInfo) { final int numOfSourceTiles = lastSubSwathIndex - firstSubSwathIndex + 1; final TileIndex tgtIndex = new TileIndex(tgtTile); final Tile[] srcTiles = new Tile[numOfSourceTiles]; final float[][] srcArray = new float[numOfSourceTiles][]; final float[] tgtArray = (float[]) tgtTile.getDataBuffer().getElems(); int k = 0; for (int i = firstSubSwathIndex; i <= lastSubSwathIndex; i++) { final String srcBandName = getSourceBandNameFromTargetBandName(tgtBandName, acquisitionMode, String.valueOf(i)); final Band srcBand = sourceProduct.getBand(srcBandName); final Tile srcRaster = getSourceTile(srcBand, sourceRectangle[k]); srcTiles[k] = srcRaster; srcArray[k] = (float[]) srcRaster.getDataBuffer().getElems(); k++; } int sy; for (int y = ty0; y < tyMax; y++) { final int tgtOffset = tgtIndex.calculateStride(y); for (int x = tx0; x < txMax; x++) { int subswathIndex = getSubSwathIndex(x, y, firstSubSwathIndex, lastSubSwathIndex, burstInfo); if (subswathIndex == -1) { continue; } if (!getLineIndicesInSourceProduct(y, subSwath[subswathIndex - 1], burstInfo)) { continue; } float val = 0; k = subswathIndex - firstSubSwathIndex; int sx = getSampleIndexInSourceProduct(x, subSwath[subswathIndex - 1]); if (burstInfo.sy1 != -1 && burstInfo.targetTime > burstInfo.midTime) { sy = burstInfo.sy1; } else { sy = burstInfo.sy0; } int idx = srcTiles[k].getDataBufferIndex(sx, sy); if (idx >= 0) { val = srcArray[k][idx]; } if(burstInfo.swath1 != -1 && val == 0) { // edge of swaths found therefore use other swath if (subswathIndex == burstInfo.swath0) { subswathIndex = burstInfo.swath1; } else { subswathIndex = burstInfo.swath0; } getLineIndicesInSourceProduct(y, subSwath[subswathIndex - 1], burstInfo); k = subswathIndex - firstSubSwathIndex; sx = getSampleIndexInSourceProduct(x, subSwath[subswathIndex - 1]); if (burstInfo.sy1 != -1 && burstInfo.targetTime > burstInfo.midTime) { sy = burstInfo.sy1; } else { sy = burstInfo.sy0; } idx = srcTiles[k].getDataBufferIndex(sx, sy); if (idx >= 0 && !(srcArray[k][idx] == 0)) { val = srcArray[k][idx]; } } tgtArray[x - tgtOffset] = val; } } } /** * Get source tile rectangle. * * @param tx0 X coordinate for the upper left corner pixel in the target tile. * @param ty0 Y coordinate for the upper left corner pixel in the target tile. * @param tw The target tile width. * @param th The target tile height. * @param subSwathIndex The subswath index. * @return The source tile rectangle. */ private Rectangle getSourceRectangle( final int tx0, final int ty0, final int tw, final int th, final int subSwathIndex) { final Sentinel1Utils.SubSwathInfo sw = subSwath[subSwathIndex - 1]; final int x0 = getSampleIndexInSourceProduct(tx0, sw); final int xMax = getSampleIndexInSourceProduct(tx0 + tw - 1, sw); final BurstInfo burstTimes = new BurstInfo(); getLineIndicesInSourceProduct(ty0, sw, burstTimes); int y0; if (burstTimes.sy0 == -1 && burstTimes.sy1 == -1) { y0 = 0; } else { y0 = burstTimes.sy0; } getLineIndicesInSourceProduct(ty0 + th - 1, sw, burstTimes); int yMax; if (burstTimes.sy0 == -1 && burstTimes.sy1 == -1) { yMax = sw.numOfLines - 1; } else { yMax = Math.max(burstTimes.sy0, burstTimes.sy1); } final int w = xMax - x0 + 1; final int h = yMax - y0 + 1; return new Rectangle(x0, y0, w, h); } private int getSampleIndexInSourceProduct(final int tx, final Sentinel1Utils.SubSwathInfo subSwath) { final int sx = (int)((((targetSlantRangeTimeToFirstPixel + tx * targetDeltaSlantRangeTime) - subSwath.slrTimeToFirstPixel) / targetDeltaSlantRangeTime)+0.5); return sx < 0 ? 0 : sx > subSwath.numOfSamples - 1 ? subSwath.numOfSamples - 1 : sx; } private boolean getLineIndicesInSourceProduct( final int ty, final Sentinel1Utils.SubSwathInfo subSwath, final BurstInfo burstTimes) { final double targetLineTime = targetFirstLineTime + ty * targetLineTimeInterval; burstTimes.targetTime = targetLineTime; burstTimes.sy0 = -1; burstTimes.sy1 = -1; int k = 0; for (int i = 0; i < subSwath.numOfBursts; i++) { if (targetLineTime >= subSwath.burstFirstLineTime[i] && targetLineTime < subSwath.burstLastLineTime[i]) { final int sy = i * subSwath.linesPerBurst + (int)(((targetLineTime - subSwath.burstFirstLineTime[i]) / subSwath.azimuthTimeInterval)+0.5); if (k == 0) { burstTimes.sy0 = sy; burstTimes.burstNum0 = i; } else { burstTimes.sy1 = sy; burstTimes.burstNum1 = i; break; } ++k; } } if (burstTimes.sy0 != -1 && burstTimes.sy1 != -1) { // find time between bursts midTime // use first burst if targetLineTime is before midTime burstTimes.midTime = (subSwath.burstLastLineTime[burstTimes.burstNum0] + subSwath.burstFirstLineTime[burstTimes.burstNum1]) / 2.0; } return burstTimes.sy0 != -1 || burstTimes.sy1 != -1; } private int computeYMin(final Sentinel1Utils.SubSwathInfo subSwath) { return (int) ((subSwath.firstValidLineTime - targetFirstLineTime) / targetLineTimeInterval); } private int computeYMax(final Sentinel1Utils.SubSwathInfo subSwath) { return (int) ((subSwath.lastValidLineTime - targetFirstLineTime) / targetLineTimeInterval); } private int computeXMin(final Sentinel1Utils.SubSwathInfo subSwath) { return (int) ((subSwath.slrTimeToFirstValidPixel - targetSlantRangeTimeToFirstPixel) / targetDeltaSlantRangeTime); } private int computeXMax(final Sentinel1Utils.SubSwathInfo subSwath) { return (int) ((subSwath.slrTimeToLastValidPixel - targetSlantRangeTimeToFirstPixel) / targetDeltaSlantRangeTime); } private int getSubSwathIndex(final int tx, final int ty, final int firstSubSwathIndex, final int lastSubSwathIndex, final BurstInfo burstInfo) { final double targetSampleSlrTime = targetSlantRangeTimeToFirstPixel + tx * targetDeltaSlantRangeTime; final double targetLineTime = targetFirstLineTime + ty * targetLineTimeInterval; burstInfo.swath0 = -1; burstInfo.swath1 = -1; int cnt = 0; Sentinel1Utils.SubSwathInfo info; for (int i = firstSubSwathIndex; i <= lastSubSwathIndex; i++) { int i_1 = i - 1; info = subSwath[i_1]; if (targetLineTime >= info.firstValidLineTime && targetLineTime <= info.lastValidLineTime && targetSampleSlrTime >= info.slrTimeToFirstValidPixel && targetSampleSlrTime <= info.slrTimeToLastValidPixel) { if (cnt == 0) { burstInfo.swath0 = i; } else { burstInfo.swath1 = i; break; } ++cnt; } } if (burstInfo.swath1 != -1) { final double middleTime = (subSwath[burstInfo.swath0 - 1].slrTimeToLastValidPixel + subSwath[burstInfo.swath1 - 1].slrTimeToFirstValidPixel) / 2.0; if (targetSampleSlrTime > middleTime) { return burstInfo.swath1; } } return burstInfo.swath0; } private double getSubSwathNoise(final int tx, final double targetLineTime, final Sentinel1Utils.SubSwathInfo sw, final String pol) { final Sentinel1Utils.NoiseVector[] vectorList = sw.noise.get(pol); final int sx = getSampleIndexInSourceProduct(tx, sw); final int sy = (int) ((targetLineTime - vectorList[0].timeMJD*Constants.secondsInDay) / targetLineTimeInterval); int l0 = -1, l1 = -1; int vectorIdx0 = -1, vectorIdxInc = 0; if (sy < vectorList[0].line) { l0 = vectorList[0].line; l1 = l0; vectorIdx0 = 0; } else if (sy >= vectorList[vectorList.length - 1].line) { l0 = vectorList[vectorList.length - 1].line; l1 = l0; vectorIdx0 = vectorList.length - 1; } else { vectorIdxInc = 1; int max = vectorList.length - 1; for (int i = 0; i < max; i++) { if (sy >= vectorList[i].line && sy < vectorList[i + 1].line) { l0 = vectorList[i].line; l1 = vectorList[i + 1].line; vectorIdx0 = i; break; } } } final int[] pixels = vectorList[vectorIdx0].pixels; int p0 = -1, p1 = -1; int pixelIdx0 = -1, pixelIdxInc = 0; if (sx < pixels[0]) { p0 = pixels[0]; p1 = p0; pixelIdx0 = 0; } else if (sx >= pixels[pixels.length - 1]) { p0 = pixels[pixels.length - 1]; p1 = p0; pixelIdx0 = pixels.length - 1; } else { pixelIdxInc = 1; int max = pixels.length - 1; for (int i = 0; i < max; i++) { if (sx >= pixels[i] && sx < pixels[i + 1]) { p0 = pixels[i]; p1 = pixels[i + 1]; pixelIdx0 = i; break; } } } final float[] noiseLUT0 = vectorList[vectorIdx0].noiseLUT; final float[] noiseLUT1 = vectorList[vectorIdx0 + vectorIdxInc].noiseLUT; double dx; if (p0 == p1) { dx = 0; } else { dx = (sx - p0) / (p1 - p0); } double dy; if (l0 == l1) { dy = 0; } else { dy = (sy - l0) / (l1 - l0); } return Maths.interpolationBiLinear(noiseLUT0[pixelIdx0], noiseLUT0[pixelIdx0 + pixelIdxInc], noiseLUT1[pixelIdx0], noiseLUT1[pixelIdx0 + pixelIdxInc], dx, dy); } private static class BurstInfo { public int sy0 = -1; public int sy1 = -1; public int swath0; public int swath1; public int burstNum0 = 0; public int burstNum1 = 0; public double targetTime; public double midTime; public BurstInfo() { } } private static class SubSwathEffectStartEndPixels { public int xMin; public int xMax; public SubSwathEffectStartEndPixels() { } } /** * The SPI is used to register this operator in the graph processing framework * via the SPI configuration file * {@code META-INF/services/org.esa.snap.core.gpf.OperatorSpi}. * This class may also serve as a factory for new operator instances. * * @see OperatorSpi#createOperator() * @see OperatorSpi#createOperator(java.util.Map, java.util.Map) */ public static class Spi extends OperatorSpi { public Spi() { super(TOPSARDeburstOp.class); } } }