/* * 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.sar.gpf; import com.bc.ceres.core.ProgressMonitor; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.MetadataAttribute; import org.esa.snap.core.datamodel.MetadataElement; 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.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.OperatorUtils; import org.esa.snap.engine_utilities.gpf.ReaderUtils; import java.awt.Rectangle; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; /** * De-Burst a WSS product */ @OperatorMetadata(alias = "DeburstWSS", category = "Radar/ENVISAT ASAR", authors = "Jun Lu, Luis Veci", version = "1.0", copyright = "Copyright (C) 2014 by Array Systems Computing Inc.", description = "Debursts an ASAR WSS product") public final class DeburstWSSOp extends Operator { @SourceProduct(alias = "source") private Product sourceProduct; @TargetProduct private Product targetProduct; @Parameter(valueSet = {SS1, SS2, SS3, SS4, SS5}, defaultValue = SS1, label = "Sub Swath:") private String subSwath = SS1; @Parameter(defaultValue = "false", label = "Produce Intensities Only") private boolean produceIntensitiesOnly = false; @Parameter(defaultValue = "false", label = "Mean Average Intensities") private boolean average = false; private final Vector<Integer> startLine = new Vector<>(5); private static final double zeroThreshold = 1000; private static final double zeroThresholdSmall = 500; private LineTime[] lineTimes = null; private boolean lineTimesSorted = false; private int margin = 50; // edge of target band where not to write private double nodatavalue = 0; private double lineTimeInterval = 0; private final Map<Band, ComplexBand> bandMap = new HashMap<>(5); private final static String SS1 = "SS1"; private final static String SS2 = "SS2"; private final static String SS3 = "SS3"; private final static String SS4 = "SS4"; private final static String SS5 = "SS5"; private int subSwathNum; private int targetWidth; private int targetHeight; /** * Default constructor. The graph processing framework * requires that an operator has a default constructor. */ public DeburstWSSOp() { } /** * 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 { // check product type if (!sourceProduct.getProductType().equals("ASA_WSS_1P")) { throw new OperatorException("Source product is not an ASA_WSS_1P"); } final int subSwathBandNum = getRealBandNumFromSubSwath(subSwath); subSwathNum = getSubSwathNumber(subSwath); getSourceMetadata(); targetProduct = new Product(sourceProduct.getName() + "_" + subSwath, sourceProduct.getProductType(), targetWidth, targetHeight); targetProduct.setPreferredTileSize(targetWidth, 50); final Band[] sourceBands = sourceProduct.getBands(); if (produceIntensitiesOnly) { final Band tgtBand = targetProduct.addBand("Intensity_" + subSwath, ProductData.TYPE_FLOAT32); tgtBand.setUnit(Unit.INTENSITY); tgtBand.setNoDataValueUsed(true); tgtBand.setNoDataValue(nodatavalue); bandMap.put(tgtBand, new ComplexBand(sourceBands[subSwathBandNum], sourceBands[subSwathBandNum + 1])); } else { final Band trgI = targetProduct.addBand("i_" + subSwath, sourceBands[subSwathBandNum].getDataType()); trgI.setUnit(Unit.REAL); trgI.setNoDataValueUsed(true); trgI.setNoDataValue(nodatavalue); final Band trgQ = targetProduct.addBand("q_" + subSwath, sourceBands[subSwathBandNum + 1].getDataType()); trgQ.setUnit(Unit.IMAGINARY); trgQ.setNoDataValueUsed(true); trgQ.setNoDataValue(nodatavalue); bandMap.put(trgI, new ComplexBand(sourceBands[subSwathBandNum], sourceBands[subSwathBandNum + 1])); ReaderUtils.createVirtualIntensityBand(targetProduct, trgI, trgQ, '_' + subSwath); } copyMetaData(sourceProduct.getMetadataRoot(), targetProduct.getMetadataRoot()); ProductUtils.copyFlagCodings(sourceProduct, targetProduct); targetProduct.setStartTime(sourceProduct.getStartTime()); targetProduct.setEndTime(sourceProduct.getEndTime()); targetProduct.setDescription(sourceProduct.getDescription()); createTiePointGrids(); // update the metadata with the affect of the processing updateTargetProductMetadata(); } catch (Throwable e) { OperatorUtils.catchOperatorException(getId(), e); } } /** * Compute mean pixel spacing (in m). */ private void getSourceMetadata() { final MetadataElement origRoot = AbstractMetadata.getOriginalProductMetadata(sourceProduct); final MetadataElement mppRootElem = origRoot.getElement("MAIN_PROCESSING_PARAMS_ADS"); final MetadataElement mpp = mppRootElem.getElementAt(subSwathNum); targetHeight = mpp.getAttributeInt("num_output_lines") / 3; targetWidth = mpp.getAttributeInt("num_samples_per_line"); if (!isBandReady(sourceProduct, subSwathNum)) { throw new OperatorException("Time codes for " + subSwath + " not ready yet. Please try again."); } } private static boolean isBandReady(final Product srcProduct, final int subSwathNum) { final MetadataElement imgRecElem = srcProduct.getMetadataRoot().getElement("Image Record"); if (imgRecElem == null) return false; final MetadataElement bandElem = imgRecElem.getElement(srcProduct.getBandAt(subSwathNum * 2).getName()); if (bandElem == null) return false; final MetadataAttribute attrib = bandElem.getAttribute("t"); return attrib != null; } /** * Update metadata in the target product. */ private void updateTargetProductMetadata() { final MetadataElement absTgt = AbstractMetadata.getAbstractedMetadata(targetProduct); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_output_lines, targetHeight); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.num_samples_per_line, targetWidth); AbstractMetadata.setAttribute(absTgt, AbstractMetadata.SWATH, subSwath); if (produceIntensitiesOnly) AbstractMetadata.setAttribute(absTgt, AbstractMetadata.SAMPLE_TYPE, "DETECTED"); final MetadataElement srcOrigRoot = AbstractMetadata.getOriginalProductMetadata(sourceProduct); final MetadataElement srcMPPRootElem = srcOrigRoot.getElement("MAIN_PROCESSING_PARAMS_ADS"); final MetadataElement srcMPP = srcMPPRootElem.getElementAt(subSwathNum); final ProductData.UTC startTime = srcMPP.getAttributeUTC("first_zero_doppler_time", AbstractMetadata.NO_METADATA_UTC); final ProductData.UTC endTime = srcMPP.getAttributeUTC("last_zero_doppler_time", AbstractMetadata.NO_METADATA_UTC); absTgt.setAttributeUTC(AbstractMetadata.first_line_time, startTime); absTgt.setAttributeUTC(AbstractMetadata.last_line_time, endTime); lineTimeInterval = srcMPP.getAttributeDouble(AbstractMetadata.line_time_interval); absTgt.setAttributeDouble(AbstractMetadata.line_time_interval, lineTimeInterval); final MetadataElement tgtOrigRoot = AbstractMetadata.getOriginalProductMetadata(targetProduct); final MetadataElement tgtMppRootElem = tgtOrigRoot.getElement("MAIN_PROCESSING_PARAMS_ADS"); tgtOrigRoot.removeElement(tgtMppRootElem); final MetadataElement tgtmds1RootElem = tgtOrigRoot.getElement("MDS1_SQ_ADS"); tgtOrigRoot.removeElement(tgtmds1RootElem); final MetadataElement srcMDS1RootElem = srcOrigRoot.getElement("MAIN_PROCESSING_PARAMS_ADS"); final MetadataElement srcMDS1 = srcMDS1RootElem.getElementAt(subSwathNum); final MetadataElement tgtMPP = srcMDS1.createDeepClone(); tgtMPP.setName("MAIN_PROCESSING_PARAMS_ADS"); tgtOrigRoot.addElement(tgtMPP); final MetadataElement tgtMDS1 = srcMPP.createDeepClone(); tgtMDS1.setName("MDS1_SQ_ADS"); tgtOrigRoot.addElement(tgtMDS1); targetProduct.setStartTime(startTime); targetProduct.setEndTime(endTime); updateOrbitStateVectors(absTgt, subSwathNum); } private static void updateOrbitStateVectors(final MetadataElement absTgt, final int subSwathNum) { final MetadataElement orbVectorsElem = absTgt.getElement(AbstractMetadata.orbit_state_vectors); if (subSwathNum == 0) { final int[] nums = {1, 2, 3, 4, 5}; removeAndRenameOrbitStateVectors(orbVectorsElem, nums); } else if (subSwathNum == 1) { final int[] nums = {6, 7, 8, 9, 10}; removeAndRenameOrbitStateVectors(orbVectorsElem, nums); } else if (subSwathNum == 2) { final int[] nums = {11, 12, 13, 14, 15}; removeAndRenameOrbitStateVectors(orbVectorsElem, nums); } else if (subSwathNum == 3) { final int[] nums = {16, 17, 18, 19, 20}; removeAndRenameOrbitStateVectors(orbVectorsElem, nums); } else if (subSwathNum == 4) { final int[] nums = {21, 22, 23, 24, 25}; removeAndRenameOrbitStateVectors(orbVectorsElem, nums); } } private static void removeAndRenameOrbitStateVectors(final MetadataElement orbVectorsElem, final int[] nums) { int i = 1; for (int n = 1; n <= 25; ++n) { final MetadataElement orbElem = orbVectorsElem.getElement(AbstractMetadata.orbit_vector + n); if (!contains(nums, n)) { orbVectorsElem.removeElement(orbElem); } else { orbElem.setName(AbstractMetadata.orbit_vector + i); ++i; } } } private static boolean contains(final int[] nums, final int n) { for (int i : nums) { if (i == n) return true; } return false; } private void createTiePointGrids() { final MetadataElement origRoot = AbstractMetadata.getOriginalProductMetadata(sourceProduct); final MetadataElement geolocRootElem = origRoot.getElement("GEOLOCATION_GRID_ADS"); final MetadataElement[] geolocElems = geolocRootElem.getElements(); final MetadataElement mainProcRootElem = origRoot.getElement("MAIN_PROCESSING_PARAMS_ADS"); final MetadataElement[] mainProcElems = mainProcRootElem.getElements(); Double lineTimeInterval = 0.0; for (MetadataElement mainProcElem : mainProcElems) { final String swathStr = mainProcElem.getAttributeString("swath_num"); if (swathStr.equalsIgnoreCase(subSwath)) { lineTimeInterval = mainProcElem.getAttribute("line_time_interval").getData().getElemDouble(); break; } } int subSamplingX = 1; final List<Double> time = new ArrayList<>(13); final List<Float> lats = new ArrayList<>(143); final List<Float> lons = new ArrayList<>(143); final List<Float> slant = new ArrayList<>(143); final List<Float> incidence = new ArrayList<>(143); for (MetadataElement geolocElem : geolocElems) { final String swathStr = geolocElem.getAttributeString("swath_number"); if (swathStr.equalsIgnoreCase(subSwath)) { subSamplingX = geolocElem.getAttribute("ASAR_Geo_Grid_ADSR.sd/first_line_tie_points.samp_numbers") .getData().getElemIntAt(1) - 1; final MetadataAttribute attrib = geolocElem.getAttribute("first_zero_doppler_time"); if (attrib != null) { final String timeStr = attrib.getData().getElemString(); double timeMJD = 0.0; try { timeMJD = ProductData.UTC.parse(timeStr).getMJD(); } catch (ParseException e) { throw new IllegalArgumentException("Unable to parse metadata attribute " + timeStr); } time.add(timeMJD * 24.0 * 3600.0); } addTiePoints(geolocElem, "ASAR_Geo_Grid_ADSR.sd/first_line_tie_points.lats", lats); addTiePoints(geolocElem, "ASAR_Geo_Grid_ADSR.sd/first_line_tie_points.longs", lons); addTiePoints(geolocElem, "ASAR_Geo_Grid_ADSR.sd/first_line_tie_points.slant_range_times", slant); addTiePoints(geolocElem, "ASAR_Geo_Grid_ADSR.sd/first_line_tie_points.angles", incidence); } } final int subSamplingY = (int) ((time.get(1) - time.get(0)) / lineTimeInterval + 0.5); final int length = lats.size(); final float[] latList = new float[length]; final float[] lonList = new float[length]; final float[] slantList = new float[length]; final float[] incList = new float[length]; for (int i = 0; i < length; ++i) { latList[i] = (float) (lats.get(i) / Constants.oneMillion); lonList[i] = (float) (lons.get(i) / Constants.oneMillion); slantList[i] = slant.get(i); incList[i] = incidence.get(i); } final int gridWidth = 11; final int gridHeight = length / 11; final TiePointGrid latGrid = new TiePointGrid(OperatorUtils.TPG_LATITUDE, gridWidth, gridHeight, 0, 0, subSamplingX, subSamplingY, latList); latGrid.setUnit(Unit.DEGREES); final TiePointGrid lonGrid = new TiePointGrid(OperatorUtils.TPG_LONGITUDE, gridWidth, gridHeight, 0, 0, subSamplingX, subSamplingY, lonList, TiePointGrid.DISCONT_AT_180); lonGrid.setUnit(Unit.DEGREES); final TiePointGrid slantGrid = new TiePointGrid(OperatorUtils.TPG_SLANT_RANGE_TIME, gridWidth, gridHeight, 0, 0, subSamplingX, subSamplingY, slantList); slantGrid.setUnit(Unit.NANOSECONDS); final TiePointGrid incGrid = new TiePointGrid(OperatorUtils.TPG_INCIDENT_ANGLE, gridWidth, gridHeight, 0, 0, subSamplingX, subSamplingY, incList); incGrid.setUnit(Unit.DEGREES); targetProduct.addTiePointGrid(latGrid); targetProduct.addTiePointGrid(lonGrid); targetProduct.addTiePointGrid(slantGrid); targetProduct.addTiePointGrid(incGrid); targetProduct.setSceneGeoCoding(new TiePointGeoCoding(latGrid, lonGrid)); } private static void addTiePoints(final MetadataElement elem, final String tag, final List<Float> array) { final MetadataAttribute attrib = elem.getAttribute(tag); if (attrib != null) { if (attrib.getDataType() == ProductData.TYPE_FLOAT32) { final float[] fList = (float[]) attrib.getData().getElems(); for (float f : fList) { array.add(f); } } else { final int[] iList = (int[]) attrib.getData().getElems(); for (int i : iList) { array.add((float) i); } } } } private static int getSubSwathNumber(final String subSwath) { switch (subSwath) { case SS1: return 0; case SS2: return 1; case SS3: return 2; case SS4: return 3; } return 4; } private static int getRealBandNumFromSubSwath(final String subSwath) { switch (subSwath) { case SS1: return 0; case SS2: return 2; case SS3: return 4; case SS4: return 6; } return 8; } private static void copyMetaData(final MetadataElement source, final MetadataElement target) { for (final MetadataElement element : source.getElements()) { if (!element.getName().equals("Image Record")) target.addElement(element.createDeepClone()); } for (final MetadataAttribute attribute : source.getAttributes()) { target.addAttribute(attribute.createDeepClone()); } } /** * 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 { Tile targetTileI = null, targetTileQ = null, targetTileIntensity = null; //final Rectangle targetRectangle = targetTile.getRectangle(); //System.out.println("targetRect " + targetRectangle.x + " " + targetRectangle.y + " w " // + targetRectangle.width + " h " + targetRectangle.height); try { ComplexBand cBand; if (produceIntensitiesOnly) { final Band tgtBand = targetProduct.getBandAt(0); targetTileIntensity = targetTiles.get(tgtBand); cBand = bandMap.get(tgtBand); } else { final Band tgtBandI = targetProduct.getBandAt(0); final Band tgtBandQ = targetProduct.getBandAt(1); targetTileI = targetTiles.get(tgtBandI); targetTileQ = targetTiles.get(tgtBandQ); cBand = bandMap.get(tgtBandI); } if (!lineTimesSorted) { sortLineTimes(sourceProduct, cBand.i); } final int maxY = targetRectangle.y + targetRectangle.height; final int maxX = targetRectangle.x + targetRectangle.width; //int targetLine = targetRectangle.y; //double startTime = targetLine * lineTimeInterval; //final double threshold = 0.000000139; final double threshold = 0.000000135; final double start = targetProduct.getStartTime().getMJD(); final double end = targetProduct.getEndTime().getMJD(); final double interval = (end - start) / targetHeight; final Vector<Integer> burstLines = new Vector<>(4); for (int y = targetRectangle.y; y < maxY; ++y) { double startTime = start + (y * interval); burstLines.clear(); double min1 = Float.MAX_VALUE; double min2 = Float.MAX_VALUE; double min3 = Float.MAX_VALUE; int i1 = 0, i2 = 0, i3 = 0; for (int i = 0; i < lineTimes.length; ++i) { if (lineTimes[i].visited || lineTimes[i].time < 1) continue; double t = lineTimes[i].time; final double diff = Math.abs(t - startTime); if (diff < min1) { if (min1 < min2) { if (min2 < min3) { min3 = min2; i3 = i2; } min2 = min1; i2 = i1; } min1 = diff; i1 = i; } else if (diff < min2) { if (min2 < min3) { min3 = min2; i3 = i2; } min2 = diff; i2 = i; } else if (diff < min3) { min3 = diff; i3 = i; } } burstLines.add(lineTimes[i1].line); setVisited(i1); burstLines.add(lineTimes[i2].line); setVisited(i2); burstLines.add(lineTimes[i3].line); setVisited(i3); if (!burstLines.isEmpty()) { final boolean ok = deburstTile(burstLines, y, targetRectangle.x, maxX, cBand.i, cBand.q, targetTileI, targetTileQ, targetTileIntensity); if (!ok) System.out.println("not ok " + y); } } /* int i = 0; while (i < lineTimes.length) { if (lineTimes[i].visited || lineTimes[i].time < startTime) { ++i; continue; } burstLines.clear(); burstLines.add(lineTimes[i].line); setVisited(i); int j = i + 1; while (j < lineTimes.length && burstLines.size() < 3) { if (lineTimes[j].visited) { ++j; continue; } final double diff = Math.abs(lineTimes[j].time - lineTimes[i].time); if (diff < threshold) { burstLines.add(lineTimes[j].line); setVisited(j); } ++j; } ++i; //System.out.println(targetLine+" found "+ burstLines.size() + " burstlines"); if (!burstLines.isEmpty()) { final boolean ok = deburstTile(burstLines, targetLine, targetRectangle.x, maxX, cBand.i, cBand.q, targetTileI, targetTileQ, targetTileIntensity); if(ok) ++targetLine; } startTime = targetLine * lineTimeInterval; if(targetLine >= targetRectangle.y + targetRectangle.height) break; } */ } catch (Throwable e) { OperatorUtils.catchOperatorException(getId(), e); } } private synchronized void setVisited(final int i) { lineTimes[i].visited = true; } private synchronized void sortLineTimes(final Product srcProduct, final Band srcBand) { if (lineTimesSorted) return; final MetadataElement imgRecElem = srcProduct.getMetadataRoot().getElement("Image Record"); final MetadataElement bandElem = imgRecElem.getElement(srcBand.getName()); final MetadataAttribute attrib = bandElem.getAttribute("t"); final double[] timeData = (double[]) attrib.getData().getElems(); lineTimes = new LineTime[timeData.length]; for (int y = 0; y < timeData.length; ++y) { lineTimes[y] = new LineTime(y, timeData[y]); } Arrays.sort(lineTimes, new LineTimeComparator()); lineTimesSorted = true; } private boolean deburstTile(final Vector<Integer> burstLines, final int targetLine, final int startX, final int endX, final Band srcBandI, final Band srcBandQ, final Tile targetTileI, final Tile targetTileQ, final Tile targetTileIntensity) { final Integer[] burstLineList = new Integer[burstLines.size()]; burstLines.toArray(burstLineList); final int rowLength = endX - startX; final double[] peakLine = new double[rowLength]; final double[] peakLineI = new double[rowLength]; final double[] peakLineQ = new double[rowLength]; final double[] sumLine = new double[rowLength]; final int[] avgTotals = new int[rowLength]; Arrays.fill(peakLine, -Float.MAX_VALUE); Arrays.fill(sumLine, 0.0); Arrays.fill(avgTotals, 0); double Ival, Qval, intensity; final Vector<short[]> srcDataListI = new Vector<>(3); final Vector<short[]> srcDataListQ = new Vector<>(3); final int widthMargin = targetWidth - margin; try { // get all burst lines getBurstLines(burstLineList, srcBandI, srcBandQ, startX, endX, srcDataListI, srcDataListQ); if (srcDataListI.isEmpty()) return false; final int dataListSize = srcDataListI.size(); // for all x peakpick or average from the bursts for (int x = startX, i = 0; x < endX; ++x, ++i) { for (int j = 0; j < dataListSize; ++j) { final short[] srcDataI = srcDataListI.get(j); final short[] srcDataQ = srcDataListQ.get(j); Ival = srcDataI[i]; Qval = srcDataQ[i]; intensity = (Ival * Ival) + (Qval * Qval); if (intensity > peakLine[i]) { peakLine[i] = intensity; peakLineI[i] = Ival; peakLineQ[i] = Qval; } if (average) { if (!isInvalid(Ival, Qval, zeroThresholdSmall)) { sumLine[i] += intensity; avgTotals[i] += 1; } } } if (average && avgTotals[i] > 1) sumLine[i] /= avgTotals[i]; } if (produceIntensitiesOnly) { final ProductData data = targetTileIntensity.getDataBuffer(); if (average) { for (int x = startX, i = 0; x < endX; ++x, ++i) { if (x < margin || x > widthMargin) { data.setElemDoubleAt(targetTileIntensity.getDataBufferIndex(x, targetLine), nodatavalue); } else { data.setElemDoubleAt(targetTileIntensity.getDataBufferIndex(x, targetLine), sumLine[i]); } } } else { for (int x = startX, i = 0; x < endX; ++x, ++i) { if (peakLine[i] == -Float.MAX_VALUE) { peakLine[i] = 0; //System.out.println("uninitPeak " + i + " at " + targetLine); } if (x < margin || x > widthMargin) { data.setElemDoubleAt(targetTileIntensity.getDataBufferIndex(x, targetLine), nodatavalue); } else { data.setElemDoubleAt(targetTileIntensity.getDataBufferIndex(x, targetLine), peakLine[i]); } } } } else { final ProductData dataI = targetTileI.getDataBuffer(); final ProductData dataQ = targetTileQ.getDataBuffer(); for (int x = startX, i = 0; x < endX; ++x, ++i) { if (peakLine[i] == -Float.MAX_VALUE) { peakLineI[i] = 0; peakLineQ[i] = 0; System.out.println("uninitPeak " + i + " at " + targetLine); } final int index = targetTileI.getDataBufferIndex(x, targetLine); if (x < margin || x > widthMargin) { dataI.setElemDoubleAt(index, nodatavalue); dataQ.setElemDoubleAt(index, nodatavalue); } else { dataI.setElemDoubleAt(index, peakLineI[i]); dataQ.setElemDoubleAt(index, peakLineQ[i]); } } } return true; } catch (Exception e) { System.out.println("deburstTile " + e.toString()); } return false; } private void getBurstLines(final Integer[] burstLineList, final Band srcBandI, final Band srcBandQ, final int startX, final int endX, final Vector<short[]> srcDataListI, final Vector<short[]> srcDataListQ) { Tile sourceRasterI, sourceRasterQ; final int srcBandHeight = srcBandI.getRasterHeight() - 1; final int srcBandWidth = srcBandI.getRasterWidth() - 1; for (Integer y : burstLineList) { if (y > srcBandHeight) continue; final Rectangle sourceRectangle = new Rectangle(startX, y, endX, 1); sourceRasterI = getSourceTile(srcBandI, sourceRectangle); final short[] srcDataI = (short[]) sourceRasterI.getRawSamples().getElems(); sourceRasterQ = getSourceTile(srcBandQ, sourceRectangle); final short[] srcDataQ = (short[]) sourceRasterQ.getRawSamples().getElems(); int invalidCount = 0; int total = 0; final int max = Math.min(srcBandWidth, srcDataI.length); for (int i = 500; i < max; i += 50) { if (isInvalid(srcDataI[i], srcDataQ[i], zeroThreshold)) ++invalidCount; ++total; } if (invalidCount / (float) total > 0.4) { //System.out.println("skipping " + y); continue; } srcDataListI.add(srcDataI); srcDataListQ.add(srcDataQ); } } private static void addToAverage(int i, double Ival, double Qval, final double[] sumLine, final int[] avgTotals) { if (!isInvalid(Ival, Qval, zeroThresholdSmall)) { sumLine[i] += (Ival * Ival) + (Qval * Qval); avgTotals[i] += 1; } } private static boolean isInvalid(final double i, final double q, final double threshold) { return i > -threshold && i < threshold && q > -threshold && q < threshold; } private final static class LineTime { boolean visited = false; final int line; final double time; LineTime(final int y, final double utc) { line = y; time = utc; } } private final static class LineTimeComparator implements Comparator<LineTime> { public int compare(LineTime a, LineTime b) { if (a.time < b.time) return -1; else if (a.time > b.time) return 1; return 0; } } private final static class ComplexBand { final Band i; final Band q; public ComplexBand(final Band iBand, final Band qBand) { i = iBand; q = qBand; } } /** * 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(DeburstWSSOp.class); } } }