package jas.hist; import jas.plot.Overlay; import jas.util.xml.HasXMLRepresentation; import java.util.Observable; /** * JASHist2DHistogramData is used for data which can be viewed as a 2D plot * but NOT as a ScatterPlot. The subclass JASHistScatterPlotData is used * for data which can also be displayed as a ScatterPlot. */ class JASHist2DHistogramData extends JASHistData { JASHist2DHistogramData(DataManager dm,Rebinnable2DHistogramData ds) { super(dm); dataSource = ds; initTransientData(); JASHistStyle s = null; if (ds instanceof HasStyle) s = ((HasStyle) ds).getStyle(); if (s == null) s = createStyle(); setStyle(s); String property = System.getProperty("hurry", "false"); hurry = property != null && property.equalsIgnoreCase("true"); } JASHistStyle createStyle() { return new JASHist2DHistogramStyle(); } private void initTransientData() { zLimitsValid = false; isBinned = false; } public void setStyle(JASHistStyle style) { if (!(style instanceof JASHist2DHistogramStyle)) throw new IllegalArgumentException("Style is not subclass of JASHist2DHistogramStyle"); if (this.style != null) this.style.deleteObserver(this); this.style = (JASHist2DHistogramStyle) style; this.style.addObserver(this); } public String getTitle() { return dataSource.getTitle(); } Overlay createOverlay() { return new TwoDOverlay(this); } void writeAsXML(XMLPrintWriter pw, boolean snapshot) { String theXAxisType = pw.convertAxisTypeToString(dataSource.getXAxisType()); String theYAxisType = pw.convertAxisTypeToString(dataSource.getYAxisType()); pw.setAttribute("type","histogram2d"); pw.openTag("data2d"); if (snapshot) { double[][][] result = dataSource.rebin(xBins,xLow,xHigh,yBins,yLow,yHigh,true,hurry, style.getShowOverflow()); if (result == null) result = new double[1][xBins][yBins]; double[][] data = result[0]; pw.setAttribute("title",getTitle()); pw.setAttribute("xSize",data.length); pw.setAttribute("ySize",data[0].length); pw.openTag("bins2d"); double[][] positiveError = new double[0][0]; double[][] negativeError = new double[0][0]; boolean havePlusError = (result.length > 1); boolean haveMinusError = (result.length > 2); if (havePlusError) { positiveError = result[1]; if (haveMinusError) { negativeError = result[2]; } } for (int i=0; i < data.length; i++) { for (int j=0; j < data[i].length; j++) { pw.print(data[i][j]); if (havePlusError && positiveError.length > i && positiveError[i].length > j) { pw.print("," + positiveError[i][j]); if (haveMinusError && negativeError.length > i && negativeError[i].length > j) { pw.println("," + negativeError[i][j]); } else { pw.println(); } } else { pw.println(); } } } pw.closeTag(); //output the x axis attributes pw.printBinnedDataAxisAttributes( "x", "" + getXMin(), "" + getXMax(), "" + dataSource.getXBins(), theXAxisType); //output the y axis attributes pw.printBinnedDataAxisAttributes( "y", "" + getYMin(), "" + getYMax(), "" + dataSource.getYBins(), theYAxisType); if (dataSource instanceof HasStatistics) { Statistics stats = ((HasStatistics) dataSource).getStatistics(); if (stats != null) { pw.openTag("statistics"); String[] names = stats.getStatisticNames(); for (int i=0; i<names.length; i++) { String name = names[i]; pw.setAttribute("name",name); String valueString = null; if (stats instanceof ExtendedStatistics) { Object value = ((ExtendedStatistics) stats).getExtendedStatistic(name); if (value != null) valueString = value.toString(); } if (valueString == null) valueString = String.valueOf(stats.getStatistic(name)); pw.setAttribute("value",valueString); pw.printTag("statistic"); } pw.closeTag(); } } } else // !snapshot { if (dataSource instanceof HasXMLRepresentation) { ((HasXMLRepresentation) dataSource).writeAsXML(pw); } else { if (dataSource instanceof HasDataSource) pw.setAttribute("name",dataSource.getClass().getName()); else pw.setAttribute("name","???"); pw.setAttribute("param","???"); pw.printTag("class"); } } /* //!PA - implement writing 2d string axis labels if and when we actually implement 2d string axes // (and watch out for min and max undef bug on string labels) if (theAxisType.equals("string")) { jas.hist.JASHistXMLUtils.writeTabs(pw, (indentLevel + 1)); pw.println("<axisLabels>"); String[] labels = getAxisLabels(); for (int i=0; i < labels.length; i++) { jas.hist.JASHistXMLUtils.writeTabs(pw, (indentLevel + 2)); pw.println("<axisLabel value=\"" + labels[i] + "\">"); } jas.hist.JASHistXMLUtils.writeTabs(pw, (indentLevel + 1)); pw.println("</axisLabels>"); } */ String histStyleName = JASHist2DHistogramStyle.getHistStyleName(style.getHistStyle()); pw.setAttribute("histStyle",histStyleName); if (histStyleName.equals("STYLE_COLORMAP")) { pw.setAttribute("colorMapScheme", JASHist2DHistogramStyle.getColorMapSchemeName(style.getColorMapScheme())); } pw.setAttribute("shapeColor", jas.util.ColorConverter.colorToString(style.getShapeColor())); pw.setAttribute("overflowBinColor", jas.util.ColorConverter.colorToString(style.getOverflowBinColor())); pw.setAttribute("startDataColor", jas.util.ColorConverter.colorToString(style.getStartDataColor())); pw.setAttribute("endDataColor", jas.util.ColorConverter.colorToString(style.getEndDataColor())); pw.setAttribute("showOverflow",style.getShowOverflow()); pw.setAttribute("showPlot",style.getShowPlot()); if (style.getLogZ()) pw.setAttribute("logZ",true); pw.printTag("style2d"); pw.closeTag(); } boolean isRebinnable() { return dataSource.isRebinnable(); } double getXMin() { double result = dataSource.getXMin(); if (style.getShowOverflow()) { result -= (dataSource.getXMax()-result)/dataSource.getXBins(); } return result; } double getXMax() { double result = dataSource.getXMax(); if (style.getShowOverflow()) { result += (result-dataSource.getXMin())/dataSource.getXBins(); } return result; } double getYMin() { double result = dataSource.getYMin(); if (style.getShowOverflow()) { result -= (dataSource.getYMax()-result)/dataSource.getYBins(); } return result; } double getYMax() { double result = dataSource.getYMax(); if (style.getShowOverflow()) { result += (result-dataSource.getYMin())/dataSource.getYBins(); } return result; } int getXBins() { return dataSource.getXBins(); } int getYBins() { return dataSource.getYBins(); } void setXRange(int xBins,double xLow, double xHigh) { if (isRebinnable()) { if (xBins != this.xBins || xLow != this.xLow || xHigh != this.xHigh) { this.xBins = xBins; isBinned = false; zLimitsValid = false; } } else { this.xBins = dataSource.getXBins(); } this.xLow = xLow; this.xHigh = xHigh; } void setYRange(int yBins,double yLow, double yHigh) { if (isRebinnable()) { if (yBins != this.yBins || yLow != this.yLow || yHigh != this.yHigh) { this.yBins = yBins; isBinned = false; zLimitsValid = false; } } else { this.yBins = dataSource.getYBins(); } this.yLow = yLow; this.yHigh = yHigh; } private void doBin() { // no support for String axes yet isBinned = true; // Set before call to rebin to avoid race condition double xl, xh, yl, yh; if (isRebinnable()) { xl = xLow; xh = xHigh; yl = yLow; yh = yHigh; } else { xl = ((Rebinnable2DHistogramData) dataSource).getXMin(); xh = ((Rebinnable2DHistogramData) dataSource).getXMax(); yl = ((Rebinnable2DHistogramData) dataSource).getYMin(); yh = ((Rebinnable2DHistogramData) dataSource).getYMax(); } double[][][] result = dataSource.rebin(xBins,xl,xh,yBins,yl,yh,true,hurry,style.getShowOverflow()); if (result == null) result = new double[1][xBins][yBins]; // apply normalization if (normalization != null) { double factor = 1./normalization.getNormalizationFactor(); for (int k=0; k<result.length; k++) { double[][] yy = result[k]; for (int j=0; j<yy.length; j++) { double[] y = yy[j]; for (int i=0; i<y.length; i++) y[i] *= factor; } } } data = result[0]; if (data.length != xBins) System.err.println("Warning xbins="+xBins+" data.length="+data.length); // todo: no handling for error bars yet if (overlay instanceof TwoDOverlay) { ((TwoDOverlay) overlay).setData(data,xl,xh,yl,yh, xBins, yBins); } } protected void calcZLimits() { if (!isBinned) doBin(); zLimitsValid = true; double zLogMin; if (xBins == 0 || yBins == 0) { zMin = 0; zLogMin = Double.POSITIVE_INFINITY; zMax = 1; } else { zLogMin = Double.POSITIVE_INFINITY; zMin= Double.POSITIVE_INFINITY; zMax = Double.NEGATIVE_INFINITY; for(int j=0; j<data[0].length; j++) { for(int i=0; i<data.length; i++) { double d = data[i][j]; zMin = Math.min(zMin, d); if (d>0) zLogMin = Math.min(zLogMin,d); zMax = Math.max(zMax, d); } } } if (overlay instanceof TwoDOverlay) { ((TwoDOverlay) overlay).setZMinMax(zMin,zMax,zLogMin); } } public void update(Observable o, Object arg) { // Dragons: Likely to be called by different thread if (o == dataSource) { // Currently there are several types of update notifications // - DataUpdate // - RangeUpdate // - FinalUpdate // - TitleUpdate // - Reset (Ususlly for when partition changes) HistogramUpdate hu = (HistogramUpdate) arg; // If we are not binned, we must pass the update onto any fits. // If we are binned then hopefully our observer will ask us to rebin // ourselves, and the fit will be informed then. isBinned = false; zLimitsValid = false; //if (hu.isReset()) //parent.resetNumberOfBins(this); //if (hu.isRangeUpdate() || hu.isReset()) //parent.invalidateXAxis(); //long delay = hu.isFinalUpdate() || hu.isReset() ? 0 : 1000; //parent.scheduleDataUpdate(delay); parent.update(hu, this); } else if (o == style) { // Binning may change due to overflow bins being changed. // TODO: Could be more selective about when we need to rebin/redraw isBinned = false; parent.styleUpdate(this); } else if (o == normalization) { normalizationChanged(false); } } void normalizationChanged(boolean now) { // treat the same as dataChanged // Could be more efficient by just renormalizing the cached copy of the data isBinned = false; zLimitsValid = false; parent.update(null,this); } public boolean hasChanged() { return !isBinned; } void validate() { if (!isBinned) doBin(); System.out.println("validate called"); } void axisChanged() { parent.axisChanged(this); } public DataSource getDataSource() { return dataSource; } public JASHistStyle getStyle() { return style; } void destroy() { if (dataSource instanceof Observable) ((Observable) dataSource).deleteObserver(this); style.deleteObserver(this); super.deleteNormalizationObserver(); } protected Rebinnable2DHistogramData dataSource; protected JASHist2DHistogramStyle style; // directly accessed by Overlay protected boolean hurry; protected boolean dataValid; boolean isBinned = false; private boolean zLimitsValid = false; private double[][] data; private int xBins; private double xLow; private double xHigh; private int yBins; private double yLow; private double yHigh; private double zMin; private double zMax; //static final long serialVersionUID = -3529869583896718619L; }