Java tutorial
/* * Copyright (C) 2015 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.csa.rstb.polarimetric.rcp.toolviews; import com.bc.ceres.binding.Property; import com.bc.ceres.binding.PropertyContainer; import com.bc.ceres.binding.ValidationException; import com.bc.ceres.core.ProgressMonitor; import com.bc.ceres.core.SubProgressMonitor; import com.bc.ceres.swing.binding.BindingContext; import org.csa.rstb.polarimetric.gpf.HaAlphaDescriptor; import org.esa.s1tbx.dat.graphics.Palette; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.Mask; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductNode; import org.esa.snap.core.datamodel.ProductNodeEvent; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.core.datamodel.Stx; import org.esa.snap.core.datamodel.StxFactory; import org.esa.snap.core.dataop.barithm.BandArithmetic; import org.esa.snap.core.util.Debug; import org.esa.snap.core.util.ProductUtils; import org.esa.snap.core.util.math.MathUtils; import org.esa.snap.rcp.SnapApp; import org.esa.snap.rcp.statistics.AbstractStatisticsTopComponent; import org.esa.snap.rcp.statistics.AxisRangeControl; import org.esa.snap.rcp.statistics.ChartPagePanel; import org.esa.snap.rcp.statistics.MaskSelectionToolSupport; import org.esa.snap.rcp.statistics.PlotAreaSelectionTool; import org.esa.snap.rcp.statistics.RefreshActionEnabler; import org.esa.snap.rcp.statistics.XYImagePlot; import org.esa.snap.rcp.statistics.XYPlotToolTipGenerator; import org.esa.snap.rcp.util.ProgressHandleMonitor; import org.esa.snap.ui.GridBagUtils; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.annotations.XYAnnotation; import org.jfree.chart.annotations.XYLineAnnotation; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.NumberAxis; import org.jfree.ui.RectangleInsets; import org.netbeans.api.progress.ProgressUtils; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.io.IOException; import java.text.MessageFormat; /** * The H-a Alpha plot pane within the statistics window. */ public class HaAlphaPlotPanel extends ChartPagePanel { private static final String NO_DATA_MESSAGE = "This plot requires an H-a Alpha decomposition as input\n" + "The plot will be computed when you hit the 'Refresh View' button.\n" + HELP_TIP_MESSAGE + "\n" + ZOOM_TIP_MESSAGE; private static final String CHART_TITLE = "H-Alpha Plane Plot"; public final static String PROPERTY_NAME_AUTO_MIN_MAX = "autoMinMax"; public final static String PROPERTY_NAME_MIN = "min"; public final static String PROPERTY_NAME_MAX = "max"; public final static String PROPERTY_NAME_USE_ROI_MASK = "useRoiMask"; public final static String PROPERTY_NAME_ROI_MASK = "roiMask"; public final static String PROPERTY_NAME_X_BAND = "xBand"; public final static String PROPERTY_NAME_Y_BAND = "yBand"; private static final int X_VAR = 0; private static final int Y_VAR = 1; private static final int NUM_DECIMALS = 2; private BindingContext bindingContext; private DataSourceConfig dataSourceConfig; private Property xBandProperty; private Property yBandProperty; private static AxisRangeControl[] axisRangeControls = new AxisRangeControl[2]; private IndexColorModel toggledColorModel; private IndexColorModel untoggledColorModel; private ChartPanel densityPlotDisplay; private XYImagePlot plot; private static final Color backgroundColor = new Color(255, 255, 255, 0); private boolean plotColorsInverted; private JCheckBox toggleZoneOverlayCheckBox; private final static Color annotColour = Color.DARK_GRAY; private final static Font annotFont = new Font("Ariel", Font.BOLD, 14); public HaAlphaPlotPanel(AbstractStatisticsTopComponent parentDialog, String helpId) { super(parentDialog, helpId, CHART_TITLE, true); } @Override protected void initComponents() { initParameters(); createUI(); initActionEnablers(); updateComponents(); } private void initActionEnablers() { RefreshActionEnabler roiMaskActionEnabler = new RefreshActionEnabler(refreshButton, PROPERTY_NAME_USE_ROI_MASK, PROPERTY_NAME_ROI_MASK, PROPERTY_NAME_X_BAND, PROPERTY_NAME_Y_BAND); bindingContext.addPropertyChangeListener(roiMaskActionEnabler); RefreshActionEnabler rangeControlActionEnabler = new RefreshActionEnabler(refreshButton, PROPERTY_NAME_MIN, PROPERTY_NAME_AUTO_MIN_MAX, PROPERTY_NAME_MAX); axisRangeControls[X_VAR].getBindingContext().addPropertyChangeListener(rangeControlActionEnabler); axisRangeControls[Y_VAR].getBindingContext().addPropertyChangeListener(rangeControlActionEnabler); } @Override public void nodeDataChanged(ProductNodeEvent event) { super.nodeDataChanged(event); if (!dataSourceConfig.useRoiMask) { return; } final Mask roiMask = dataSourceConfig.roiMask; if (roiMask == null) { return; } final ProductNode sourceNode = event.getSourceNode(); if (!(sourceNode instanceof Mask)) { return; } final String maskName = sourceNode.getName(); if (roiMask.getName().equals(maskName)) { updateComponents(); } } @Override protected void updateComponents() { super.updateComponents(); if (isRasterChanged() || isProductChanged()) { plot.setImage(null); plot.setDataset(null); if (isProductChanged()) { plot.getDomainAxis().setLabel("Entropy"); plot.getRangeAxis().setLabel("Alpha"); } final Product product = getProduct(); if (product != null) { toggleZoneOverlayCheckBox.setEnabled(false); Band entropyBand = product.getBand("Entropy"); Band alphaBand = product.getBand("Alpha"); if (entropyBand != null && alphaBand != null) { try { xBandProperty.setValue(entropyBand); yBandProperty.setValue(alphaBand); } catch (ValidationException ignored) { Debug.trace(ignored); } } } } refreshButton.setEnabled(xBandProperty.getValue() != null && yBandProperty.getValue() != null); } private void initParameters() { axisRangeControls[X_VAR] = new AxisRangeControl("X-Axis"); axisRangeControls[Y_VAR] = new AxisRangeControl("Y-Axis"); initColorModels(); plotColorsInverted = false; dataSourceConfig = new DataSourceConfig(); bindingContext = new BindingContext(PropertyContainer.createObjectBacked(dataSourceConfig)); xBandProperty = bindingContext.getPropertySet().getProperty(PROPERTY_NAME_X_BAND); yBandProperty = bindingContext.getPropertySet().getProperty(PROPERTY_NAME_Y_BAND); } private void initColorModels() { for (int j = 0; j <= 1; j++) { final int palSize = 256; final byte[] r = new byte[palSize]; final byte[] g = new byte[palSize]; final byte[] b = new byte[palSize]; final byte[] a = new byte[palSize]; r[0] = (byte) backgroundColor.getRed(); g[0] = (byte) backgroundColor.getGreen(); b[0] = (byte) backgroundColor.getBlue(); a[0] = (byte) backgroundColor.getAlpha(); final Palette pal = new Palette("Rainbow", new Color[] { Color.black, Color.blue, Color.cyan, Color.green, Color.yellow, Color.orange, Color.red }); for (int i = 1; i < 256; ++i) { float value = i / 255f; if (j == 0) value = (255 - i) / 255f; Color c = pal.lookupColor(value); r[i] = (byte) c.getRed(); g[i] = (byte) c.getGreen(); b[i] = (byte) c.getBlue(); a[i] = (byte) 255; } if (j == 0) { toggledColorModel = new IndexColorModel(8, palSize, r, g, b, a); } else { untoggledColorModel = new IndexColorModel(8, palSize, r, g, b, a); } } } private void createUI() { plot = new XYImagePlot(); plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5)); NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis(); NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); domainAxis.setAutoRangeIncludesZero(false); rangeAxis.setAutoRangeIncludesZero(false); domainAxis.setUpperMargin(0); domainAxis.setLowerMargin(0); rangeAxis.setUpperMargin(0); rangeAxis.setLowerMargin(0); plot.setNoDataMessage(NO_DATA_MESSAGE); plot.getRenderer().setBaseToolTipGenerator(new XYPlotToolTipGenerator()); JFreeChart chart = new JFreeChart(CHART_TITLE, plot); ChartFactory.getChartTheme().apply(chart); chart.removeLegend(); createUI(createChartPanel(chart), createOptionsPanel(), bindingContext); updateUIState(); } private void toggleColor() { BufferedImage image = plot.getImage(); if (image != null) { if (!plotColorsInverted) { image = new BufferedImage(untoggledColorModel, image.getRaster(), image.isAlphaPremultiplied(), null); } else { image = new BufferedImage(toggledColorModel, image.getRaster(), image.isAlphaPremultiplied(), null); } plot.setImage(image); densityPlotDisplay.getChart().setNotify(true); plotColorsInverted = !plotColorsInverted; } } private JPanel createOptionsPanel() { toggleZoneOverlayCheckBox = new JCheckBox("Show Zones"); toggleZoneOverlayCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateChartData(); } }); toggleZoneOverlayCheckBox.setEnabled(false); toggleZoneOverlayCheckBox.setSelected(true); final JPanel optionsPanel = GridBagUtils.createPanel(); final GridBagConstraints gbc = GridBagUtils .createConstraints("anchor=NORTHWEST,fill=HORIZONTAL,insets.top=0,weightx=1,gridx=0"); GridBagUtils.addToPanel(optionsPanel, axisRangeControls[X_VAR].getPanel(), gbc, "gridy=0"); GridBagUtils.addToPanel(optionsPanel, axisRangeControls[Y_VAR].getPanel(), gbc, "gridy=2,insets.left=0,insets.right=0"); GridBagUtils.addToPanel(optionsPanel, new JPanel(), gbc, "gridy=4"); GridBagUtils.addToPanel(optionsPanel, new JSeparator(), gbc, "gridy=5,insets.left=4,insets.right=2"); GridBagUtils.addToPanel(optionsPanel, toggleZoneOverlayCheckBox, gbc, "gridy=6,insets.left=0,insets.right=0"); GridBagUtils.addToPanel(optionsPanel, new JLabel("Zones Descriptions:"), gbc, "gridy=8,insets.left=0,insets.right=0"); JTextArea textArea = new JTextArea("Z1 - Dihedral Reflector\n" + "Z2 - Dipole\n" + "Z3 - Bragg Surface\n" + "Z4 - Double Reflection\n" + "Z5 - Anisotropic Particles\n" + "Z6 - Random Surface\n" + "Z7 - Complex Structures\n" + "Z8 - Random Anisotropic Scatterers\n" + "Z9 - Non-feasible"); GridBagUtils.addToPanel(optionsPanel, textArea, gbc, "gridy=9,insets.left=0,insets.right=0"); return optionsPanel; } private ChartPanel createChartPanel(JFreeChart chart) { densityPlotDisplay = new ChartPanel(chart); MaskSelectionToolSupport maskSelectionToolSupport = new MaskSelectionToolSupport(this, densityPlotDisplay, "scatter_plot_area", "Mask generated from selected scatter plot area", Color.RED, PlotAreaSelectionTool.AreaType.ELLIPSE) { @Override protected String createMaskExpression(PlotAreaSelectionTool.AreaType areaType, Shape shape) { Rectangle2D bounds = shape.getBounds2D(); return createMaskExpression(bounds.getCenterX(), bounds.getCenterY(), 0.5 * bounds.getWidth(), 0.5 * bounds.getHeight()); } protected String createMaskExpression(double x0, double y0, double dx, double dy) { return String.format("sqrt(sqr((%s - %s)/%s) + sqr((%s - %s)/%s)) < 1.0", BandArithmetic.createExternalName(dataSourceConfig.xBand.getName()), x0, dx, BandArithmetic.createExternalName(dataSourceConfig.yBand.getName()), y0, dy); } }; densityPlotDisplay.getPopupMenu().addSeparator(); densityPlotDisplay.getPopupMenu().add(maskSelectionToolSupport.createMaskSelectionModeMenuItem()); densityPlotDisplay.getPopupMenu().add(maskSelectionToolSupport.createDeleteMaskMenuItem()); densityPlotDisplay.getPopupMenu().addSeparator(); densityPlotDisplay.getPopupMenu().add(createCopyDataToClipboardMenuItem()); return densityPlotDisplay; } private RasterDataNode getRaster(int varIndex) { final Product product = getProduct(); if (product == null) { return null; } final String rasterName;// = rasterNameParams[varIndex].getValue().toString(); if (varIndex == X_VAR) { rasterName = dataSourceConfig.xBand.getName(); } else { rasterName = dataSourceConfig.yBand.getName(); } RasterDataNode raster = product.getRasterDataNode(rasterName); if (raster == null) { if (getRaster() != null && rasterName.equalsIgnoreCase(getRaster().getName())) { raster = getRaster(); } } Debug.assertTrue(raster != null); return raster; } private void updateUIState() { super.updateComponents(); } private void checkBandsForRange() throws IllegalArgumentException { if (axisRangeControls[X_VAR].getMin().equals(axisRangeControls[X_VAR].getMax()) && axisRangeControls[Y_VAR].getMin().equals(axisRangeControls[Y_VAR].getMax())) { throw new IllegalArgumentException("Value range of at least one band must be larger than one"); } } @Override protected void updateChartData() { final ChartPagePanel chartPanel = this; final RasterDataNode rasterX = getRaster(X_VAR); final RasterDataNode rasterY = getRaster(Y_VAR); if (rasterX == null || rasterY == null) { return; } ProgressHandleMonitor pm = ProgressHandleMonitor.create("Computing plot"); Runnable operation = () -> { pm.beginTask("Computing plot...", 100); try { checkBandsForRange(); setRange(X_VAR, rasterX, dataSourceConfig.useRoiMask ? dataSourceConfig.roiMask : null, SubProgressMonitor.create(pm, 15)); setRange(Y_VAR, rasterY, dataSourceConfig.useRoiMask ? dataSourceConfig.roiMask : null, SubProgressMonitor.create(pm, 15)); BufferedImage densityPlotImage = ProductUtils.createDensityPlotImage(rasterX, axisRangeControls[X_VAR].getMin().floatValue(), axisRangeControls[X_VAR].getMax().floatValue(), rasterY, axisRangeControls[Y_VAR].getMin().floatValue(), axisRangeControls[Y_VAR].getMax().floatValue(), dataSourceConfig.useRoiMask ? dataSourceConfig.roiMask : null, 512, 512, backgroundColor, null, SubProgressMonitor.create(pm, 70)); densityPlotImage = new BufferedImage(untoggledColorModel, densityPlotImage.getRaster(), densityPlotImage.isAlphaPremultiplied(), null); plotColorsInverted = false; checkBandsForRange(); double minX = axisRangeControls[X_VAR].getMin(); double maxX = axisRangeControls[X_VAR].getMax(); double minY = axisRangeControls[Y_VAR].getMin(); double maxY = axisRangeControls[Y_VAR].getMax(); if (minX > maxX || minY > maxY) { JOptionPane.showMessageDialog(chartPanel, "Failed to compute plot.\n" + "No Pixels considered..", CHART_TITLE, JOptionPane.ERROR_MESSAGE); plot.setDataset(null); return; } if (MathUtils.equalValues(minX, maxX, 1.0e-4)) { minX = Math.floor(minX); maxX = Math.ceil(maxX); } if (MathUtils.equalValues(minY, maxY, 1.0e-4)) { minY = Math.floor(minY); maxY = Math.ceil(maxY); } plot.setImage(densityPlotImage); plot.setImageDataBounds(new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY)); axisRangeControls[X_VAR].adjustComponents(minX, maxX, NUM_DECIMALS); axisRangeControls[Y_VAR].adjustComponents(minY, maxY, NUM_DECIMALS); // plot.getDomainAxis().setLabel(StatisticChartStyling.getAxisLabel(getRaster(X_VAR), "Entropy", false)); // plot.getRangeAxis().setLabel(StatisticChartStyling.getAxisLabel(getRaster(Y_VAR), "Alpha", false)); plot.getDomainAxis().setLabel("Entropy"); plot.getRangeAxis().setLabel("Alpha"); toggleZoneOverlayCheckBox.setEnabled(true); // clear the list java.util.List<XYAnnotation> annotList = plot.getAnnotations(); for (XYAnnotation an : annotList) { plot.removeAnnotation(an); } if (toggleZoneOverlayCheckBox.isSelected()) { drawZoneOverlay(); } } catch (Exception e) { SnapApp.getDefault().handleError("Failed to compute plot", e); } finally { pm.done(); } }; ProgressUtils.runOffEventThreadWithProgressDialog(operation, "Computing plot", pm.getProgressHandle(), true, 50, 1000); } private void drawZoneOverlay() { double minX = axisRangeControls[X_VAR].getMin(); double maxX = axisRangeControls[X_VAR].getMax(); double minY = axisRangeControls[Y_VAR].getMin(); double maxY = axisRangeControls[Y_VAR].getMax(); double H1 = HaAlphaDescriptor.H1; double H2 = HaAlphaDescriptor.H2; double Alpha1 = HaAlphaDescriptor.Alpha1; double Alpha2 = HaAlphaDescriptor.Alpha2; double Alpha3 = HaAlphaDescriptor.Alpha3; double Alpha4 = HaAlphaDescriptor.Alpha4; double Alpha5 = HaAlphaDescriptor.Alpha5; BasicStroke stroke = new BasicStroke(2.0f); XYLineAnnotation line = new XYLineAnnotation(H1, minY, H1, maxY, stroke, annotColour); plot.addAnnotation(line); line = new XYLineAnnotation(H2, minY, H2, maxY, stroke, annotColour); plot.addAnnotation(line); line = new XYLineAnnotation(H1, Alpha1, maxX, Alpha1, stroke, annotColour); plot.addAnnotation(line); line = new XYLineAnnotation(H2, Alpha2, H1, Alpha2, stroke, annotColour); plot.addAnnotation(line); line = new XYLineAnnotation(minX, Alpha3, H2, Alpha3, stroke, annotColour); plot.addAnnotation(line); line = new XYLineAnnotation(minX, Alpha4, H2, Alpha4, stroke, annotColour); plot.addAnnotation(line); line = new XYLineAnnotation(H2, Alpha5, maxX, Alpha5, stroke, annotColour); plot.addAnnotation(line); addText("Z1", minX + (H2 - minX) / 2, Alpha3 + (maxY - Alpha3) / 2); addText("Z2", minX + (H2 - minX) / 2, Alpha4 + (Alpha3 - Alpha4) / 2); addText("Z3", minX + (H2 - minX) / 2, minY + (Alpha4 - minY) / 2); addText("Z4", H2 + (H1 - H2) / 2, Alpha2 + (maxY - Alpha2) / 2); addText("Z5", H2 + (H1 - H2) / 2, Alpha5 + (Alpha2 - Alpha5) / 2); addText("Z6", H2 + (H1 - H2) / 2, minY + (Alpha5 - minY) / 2); addText("Z7", H1 + (maxX - H1) / 2, Alpha1 + (maxY - Alpha1) / 2); addText("Z8", H1 + (maxX - H1) / 2, Alpha5 + (Alpha1 - Alpha5) / 2); addText("Z9", H1 + (maxX - H1) / 2, minY + (Alpha5 - minY) / 2); //Arc2D.Double arc = new Arc2D.Double( // 0, 0, 30, 2 * 30, 3.14, 3.14, Arc2D.OPEN); //plot.addAnnotation(new XYShapeAnnotation(arc, // new BasicStroke(2.0f), Color.blue)); } private void addText(final String text, final double x, final double y) { final XYTextAnnotation annotation = new XYTextAnnotation(text, x, y); annotation.setPaint(annotColour); annotation.setBackgroundPaint(Color.WHITE); annotation.setFont(annotFont); plot.addAnnotation(annotation); } private void setRange(int varIndex, RasterDataNode raster, Mask mask, ProgressMonitor pm) throws IOException { final AxisRangeControl axisRangeControl = axisRangeControls[varIndex]; if (varIndex == X_VAR) { axisRangeControl.adjustComponents(0.0, 1.0, NUM_DECIMALS); return; } if (axisRangeControl.isAutoMinMax()) { Stx stx; if (mask == null) { stx = raster.getStx(false, pm); } else { stx = new StxFactory().withRoiMask(mask).create(raster, pm); } axisRangeControl.adjustComponents(Math.min(0, stx.getMinimum()), Math.max(90, stx.getMaximum()), NUM_DECIMALS); } } @Override protected boolean checkDataToClipboardCopy() { final int warnLimit = 2000; final int excelLimit = 65536; final int numNonEmptyBins = getNumNonEmptyBins(); if (numNonEmptyBins > warnLimit) { String excelNote = ""; if (numNonEmptyBins > excelLimit - 100) { excelNote = "Note that e.g., Microsoft Excel 2002 only supports a total of " + excelLimit + " rows in a sheet.\n"; } final String message = MessageFormat.format("This scatter plot contains {0} non-empty bins.\n" + "For each bin, a text data row containing an x, y and z value will be created.\n" + "{1}\nPress ''Yes'' if you really want to copy this amount of data to the system clipboard.\n", numNonEmptyBins, excelNote); final int status = JOptionPane.showConfirmDialog(this, message, "Copy Data to Clipboard", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (status != JOptionPane.YES_OPTION) { return false; } } return true; } private byte[] getValidData(BufferedImage image) { if (image != null && image.getColorModel() instanceof IndexColorModel && image.getData().getDataBuffer() instanceof DataBufferByte) { return ((DataBufferByte) image.getData().getDataBuffer()).getData(); } return null; } protected int getNumNonEmptyBins() { final byte[] data = getValidData(plot.getImage()); int n = 0; if (data != null) { int b; for (byte aData : data) { b = aData & 0xff; if (b != 0) { n++; } } } return n; } @Override protected String getDataAsText() { final BufferedImage image = plot.getImage(); final Rectangle2D bounds = plot.getImageDataBounds(); final byte[] data = getValidData(image); if (data == null) { return null; } final StringBuilder sb = new StringBuilder(64000); final int w = image.getWidth(); final int h = image.getHeight(); final RasterDataNode rasterX = getRaster(X_VAR); final String nameX = rasterX.getName(); final double sampleMinX = bounds.getMinX(); final double sampleMaxX = bounds.getMaxX(); final RasterDataNode rasterY = getRaster(Y_VAR); final String nameY = rasterY.getName(); final double sampleMinY = bounds.getMinY(); final double sampleMaxY = bounds.getMaxY(); sb.append("Product name:\t").append(rasterX.getProduct().getName()).append("\n"); sb.append("Dataset X name:\t").append(nameX).append("\n"); sb.append("Dataset Y name:\t").append(nameY).append("\n"); sb.append('\n'); sb.append(nameX).append(" minimum:\t").append(sampleMinX).append("\t").append(rasterX.getUnit()) .append("\n"); sb.append(nameX).append(" maximum:\t").append(sampleMaxX).append("\t").append(rasterX.getUnit()) .append("\n"); sb.append(nameX).append(" bin size:\t").append((sampleMaxX - sampleMinX) / w).append("\t") .append(rasterX.getUnit()).append("\n"); sb.append(nameX).append(" #bins:\t").append(w).append("\n"); sb.append('\n'); sb.append(nameY).append(" minimum:\t").append(sampleMinY).append("\t").append(rasterY.getUnit()) .append("\n"); sb.append(nameY).append(" maximum:\t").append(sampleMaxY).append("\t").append(rasterY.getUnit()) .append("\n"); sb.append(nameY).append(" bin size:\t").append((sampleMaxY - sampleMinY) / h).append("\t") .append(rasterY.getUnit()).append("\n"); sb.append(nameY).append(" #bins:\t").append(h).append("\n"); sb.append('\n'); sb.append(nameX); sb.append('\t'); sb.append(nameY); sb.append('\t'); sb.append("Bin counts\t(cropped at 255)"); sb.append('\n'); int x, y, z; double v1, v2; for (int i = 0; i < data.length; i++) { z = data[i] & 0xff; if (z != 0) { x = i % w; y = h - i / w - 1; v1 = sampleMinX + ((x + 0.5) * (sampleMaxX - sampleMinX)) / w; v2 = sampleMinY + ((y + 0.5) * (sampleMaxY - sampleMinY)) / h; sb.append(v1); sb.append('\t'); sb.append(v2); sb.append('\t'); sb.append(z); sb.append('\n'); } } return sb.toString(); } private static class DataSourceConfig { public boolean useRoiMask; public Mask roiMask; private RasterDataNode xBand; private RasterDataNode yBand; private Property xBandProperty; private Property yBandProperty; } }