package org.esa.snap.rcp.statistics; import org.jfree.chart.axis.LogarithmicAxis; import org.jfree.chart.axis.NumberTick; import org.jfree.ui.RectangleEdge; import org.jfree.ui.TextAnchor; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.text.NumberFormat; import java.util.List; /** * A logarithmic axis representation with improved scaling and labelling * * @author olafd * Date: 10.04.12 * Time: 21:06 */ public class CustomLogarithmicAxis extends LogarithmicAxis { static final int VERTICAL = 0; static final int HORIZONTAL = 1; /** * Creates a new axis. * * @param label the axis label. */ public CustomLogarithmicAxis(String label) { super(label); } @Override protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { return refreshTicks(edge, HORIZONTAL); } @Override protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { return refreshTicks(edge, VERTICAL); } List refreshTicks(RectangleEdge edge, int mode) { // a much simpler approach compared to LogarithmicAxis... List ticks = new java.util.ArrayList(); double upperBoundVal = 0.0; double lowerBoundVal = 0.0; boolean NEGATIVE = false; if (getRange().getLowerBound() >= 0.0 && getRange().getUpperBound() >= 0.0) { upperBoundVal = getRange().getUpperBound(); lowerBoundVal = getRange().getLowerBound(); if (getRange().getLowerBound() == 0.0) { lowerBoundVal = SMALL_LOG_VALUE; } } else if (getRange().getLowerBound() < 0.0 && getRange().getUpperBound() < 0.0) { upperBoundVal = -1.0 * getRange().getLowerBound(); lowerBoundVal = -1.0 * getRange().getUpperBound(); NEGATIVE = true; } //get log10 version of upper bound and round to integer: final int iEndCount = (int) Math.floor(Math.log10(upperBoundVal)); //get log10 version of lower bound and round to integer: int iBegCount; if (lowerBoundVal == SMALL_LOG_VALUE) { iBegCount = iEndCount - 3; } else { iBegCount = (int) Math.floor(Math.log10(lowerBoundVal)); } String tickLabel; for (int i = iBegCount; i <= iEndCount; i++) { int jEndCount = 9; if (i == iEndCount) { if (iEndCount == 0) { jEndCount = (int) Math.abs(Math.floor(upperBoundVal)); } else { jEndCount = (int) (upperBoundVal / Math.pow(10.0, iEndCount)); } } for (int j = 0; j < jEndCount; j++) { final boolean displayTickLabel = (j == 0) || (iEndCount - iBegCount < 2); double tickVal = Math.pow(10, i) + (Math.pow(10, i) * j); if (NEGATIVE) { tickVal *= -1.0; } if (displayTickLabel) { //create label for tick: tickLabel = NumberFormat.getNumberInstance().format(tickVal); } else { //no label tickLabel = ""; } if (tickValInRange(lowerBoundVal, upperBoundVal, tickVal, NEGATIVE)) { switch (mode) { case VERTICAL: if (NEGATIVE) { addVerticalTicks(edge, ticks, -upperBoundVal, tickLabel, tickVal); } else { addVerticalTicks(edge, ticks, lowerBoundVal, tickLabel, tickVal); } break; case HORIZONTAL: if (NEGATIVE) { addHorizontalTicks(edge, ticks, -upperBoundVal, tickLabel, tickVal); } else { addHorizontalTicks(edge, ticks, lowerBoundVal, tickLabel, tickVal); } break; default: throw new IllegalStateException("Illegal axis orientation - cannot add ticks"); } } } } return ticks; } private boolean tickValInRange(double lowerBoundVal, double upperBoundVal, double tickVal, boolean negative) { if (negative) { return (-upperBoundVal <= tickVal && -lowerBoundVal >= tickVal); } else { return (lowerBoundVal <= tickVal && upperBoundVal >= tickVal); } } private void addHorizontalTicks(RectangleEdge edge, List ticks, double lowerBoundVal, String tickLabel, double tickVal) { if (tickVal >= lowerBoundVal - SMALL_LOG_VALUE) { //tick value not below lowest data value TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; if (edge == RectangleEdge.TOP) { angle = Math.PI / 2.0; } else { angle = -Math.PI / 2.0; } } else { if (edge == RectangleEdge.TOP) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; } else { anchor = TextAnchor.TOP_CENTER; rotationAnchor = TextAnchor.TOP_CENTER; } } ticks.add(new NumberTick(new Double(tickVal), tickLabel, anchor, rotationAnchor, angle)); } } private void addVerticalTicks(RectangleEdge edge, List ticks, double lowerBoundVal, String tickLabel, double tickVal) { if (tickVal >= lowerBoundVal - SMALL_LOG_VALUE) { //tick value not below lowest data value TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; angle = -Math.PI / 2.0; } else { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; angle = Math.PI / 2.0; } } else { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; } else { anchor = TextAnchor.CENTER_LEFT; rotationAnchor = TextAnchor.CENTER_LEFT; } } //create tick object and add to list: ticks.add(new NumberTick(new Double(tickVal), tickLabel, anchor, rotationAnchor, angle)); } } }