2012-10-16 4 views
4

jFreeChart 1.0.14를 사용하고 있습니다. 가로가 DateAxis 인 그래프가 있습니다. 그리고 진드기 레이블을 가운데에 두어 다음 두 번 진드기 안에있게하려고합니다.JFreeCharts 축 : 눈금 레이블 맞춤

는 JFreeChart는 일반적인 경우에이 방법을 눈금 레이블을 정렬 :

----+------------+------------+---- 
    Mon   Tue   Wed 

alt text

하지만 눈금 레이블이 방법으로 정렬하고 싶습니다 :

----+------------+------------+------------+---- 
     Mon   Tue   Wed 

alt text

을 이미지의 눈금 레이블이 정렬됩니다. 원본 이미지를 편집하여 그래픽 편집기에서 손으로. jFreeChart 라이브러리는 출력되지 않습니다.

DateAxis의 API를 통해 어떤 방법으로이를 수행 할 수 있습니까? 어떤 도움을 주셔서 감사합니다 ... :)

어떤 도움이나 아이디어를 주셔서 감사합니다. Honza (sporak)

+1

는 거의 동일한 소리 때문에 그 http://www.jfree.org/phpBB2/viewtopic.php?f=3&t=115716에 질문을 한 것이 있습니까? 이미 JFreeChart 프로젝트 리더의 대답이 있습니다. 아무도 당신에게 더 나은 조언을 줄 수는 없다고 생각합니다 :) – halex

+1

@halex 맞습니다. 그 질문은 똑같습니다. OP에게 : David Gilbert는 JFreeChart의 배후에있는 사람입니다. 그래서 포럼에서 그에게 물어볼 것을 권합니다. 만약 그가 당신을 도울 수 없다면, 아무도 할 수 없습니다. ;) – brimborium

+0

'PeriodAxis'가 작동하면 [answer your own question] (http://meta.stackexchange.com/q/17463/163188)을 할 수 있습니다. – trashgod

답변

5

아무도 도와주지 않으므로 DateAxis를 무시하고 틱 레이블 정렬을 사용하여 Axis를 얻으려고했습니다. 나는 그것을 'AlignedDateAxis'이라고 명명했다. DateAxis을 확장하고 눈금 레이블 렌더링에 사용되는 메서드 만 재정의합니다. 틱 레이블은 두 가지 방법으로 렌더링 할 수 있습니다. 표준 방식으로 틱 아래에 레이블을 배치하고 중간에 레이블을 가운데에 배치하는 방식입니다.

JFreeCharts 라이브러리에 대해 잘 알지 못하기 때문에 일부 클래스와 메서드를 이해하는 것이 쉽지 않았습니다. 나는 그것이 모든 일반적인 경우에 잘 작동되기를 바랍니다. 내 수업에는 javadoc 및 내 모국어로 된 의견 만 포함되어 있으므로 여기에 작성하지 마십시오.

package x.y.z; 

import java.awt.Graphics2D; 
import java.awt.Shape; 
import java.awt.geom.Line2D; 
import java.awt.geom.Rectangle2D; 
import java.util.Date; 
import java.util.List; 

import org.jfree.chart.axis.AxisState; 
import org.jfree.chart.axis.DateAxis; 
import org.jfree.chart.axis.DateTick; 
import org.jfree.chart.axis.DateTickUnit; 
import org.jfree.chart.axis.TickType; 
import org.jfree.chart.axis.ValueTick; 
import org.jfree.text.TextUtilities; 
import org.jfree.ui.RectangleEdge; 

/** 
* Extension of DateAxis for jFreeChart graphs that can render tick labels in two ways: 
* 1) labels under ticks (common way, same as DateAxis) 
* 2) labels in the middle of interval (new feature) 
* @author Honza Spurny, Czech Republic 
* @version 1.0 
*/ 
public class AlignedDateAxis extends DateAxis { 

    /** 
    * Tick labels alignment setting. 
    */ 
    private TickLabelPosition tickLabelPosition = TickLabelPosition.DEFAULT_VALUE; 

    /** 
    * Value for interface Serializable. 
    */ 
    static private final long serialVersionUID = 1; 

    // *********************** 
    // ***  Enums  *** 
    // *********************** 

    /** 
    * Tick label alignment modes. 
    */ 
    public enum TickLabelPosition { 
     /** 
     * Tick label is rendered under/next by own tick. 
     * (common rendering same as in DateAxis) 
     */ 
     INTERVAL_START, 
     /** 
     * Tick label is placed in the middle of interval 
     * (between two subsequent ticks) 
     */ 
     INTERVAL_MIDDLE; 

     static public final TickLabelPosition DEFAULT_VALUE = INTERVAL_START; 
    } 

    // ****************************** 
    // ***  Constructors  *** 
    // ****************************** 

    /** 
    * Default constructor. 
    */ 
    public AlignedDateAxis() { 
     super(); 
    } 

    /** 
    * Constructor. 
    * @param tickLabelPos tick label alignment mode setting for this axis. 
    */ 
    public AlignedDateAxis(TickLabelPosition tickLabelPos) { 
     this(); 
     this.setTickLabelPosition(tickLabelPos); 
    } 

    // ********************************* 
    // ***  GET/SET methods  *** 
    // ********************************* 

    public TickLabelPosition getTickLabelPosition() { 
     return this.tickLabelPosition; 
    } 

    public void setTickLabelPosition(TickLabelPosition value) { 
     this.tickLabelPosition = value; 
    } 

    // ******************************************************* 
    // ***  Overrided methods for label rendering  *** 
    // ******************************************************* 

    /** 
    * Auxiliary method to calculate tick label position of given tick. 
    * @param tick tick we need calculate label position for (DateTick is expected) 
    * @param cursor the cursor 
    * @param dataArea area to draw the ticks and labels in 
    * @param edge edge of dataArea 
    * @return Returns coordinates [x,y] where label should be placed. 
    */ 
    @Override 
    protected float[] calculateAnchorPoint(ValueTick tick, double cursor, Rectangle2D dataArea, RectangleEdge edge) { 
     float[] resultAnchor = super.calculateAnchorPoint(tick, cursor, dataArea, edge); 

     // Time of tick 
     Date tickDate = (tick instanceof DateTick) ? 
         ((DateTick) tick).getDate() : 
         new Date((long)tick.getValue()); 

     // Tick label shift. 
     // (for INTERVAL_START it is 0, for INTERVAL_MIDDLE it is calculated) 
     double labelShift; 

     switch (this.getTickLabelPosition()) { 
      case INTERVAL_MIDDLE: 
       // Getting next tick value... 
       DateTickUnit unit = this.getTickUnit(); 
       Date nextTickDate = unit.addToDate(tickDate, this.getTimeZone()); 
       double nextTickVal = this.valueToJava2D(nextTickDate.getTime(), dataArea, edge); 

       // Shifting label in between ticks... 
       labelShift = (nextTickVal - resultAnchor[0])/2; 
       break; 

      case INTERVAL_START: 
      default: 
       labelShift = 0; 
       break; 
     } 

     // Edge defines which coordinate is shifted. 
     if (RectangleEdge.isTopOrBottom(edge)) { 
      resultAnchor[0] += labelShift; 
     } else if (RectangleEdge.isLeftOrRight(edge)) { 
      resultAnchor[1] += labelShift; 
     } 

     return resultAnchor; 
    } 

    /** 
    * Renders this axis with ticks and labels. 
    * @param g2 graphics to draw the axis in. 
    * @param cursor the cursor 
    * @param plotArea area to draw the chart in 
    * @param dataArea area to draw this axis in 
    * @param edge edge of dataArea 
    * @return Returns state of axis. 
    */ 
    @Override 
    protected AxisState drawTickMarksAndLabels(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge) { 
     AxisState state = new AxisState(cursor); 
     if (this.isAxisLineVisible()) this.drawAxisLine(g2, cursor, dataArea, edge); 

     List<DateTick> ticks = this.refreshTicks(g2, state, dataArea, edge); 
     state.setTicks(ticks); 
     g2.setFont(this.getTickLabelFont()); 

     for (DateTick tick: ticks) {   
      if (this.isTickLabelsVisible()) { 
       g2.setPaint(this.getTickLabelPaint()); 
       float anchorPoint[] = this.calculateAnchorPoint(tick, cursor, dataArea, edge); 

       // TextUtilities.drawRotatedString(tick.getText(), g2, anchorPoint[0], anchorPoint[1], tick.getTextAnchor(), tick.getAngle(), tick.getRotationAnchor()); 
       // Commented code above is original code from DateAxis. 

       // ---[Override]--- 
       // Position of tick label is shifted so it can point outside the dataArea. 
       // We have to check whether the tick label on this position is drawable into the given area.        
       Shape labelBounds = TextUtilities.calculateRotatedStringBounds(tick.getText(), g2, anchorPoint[0], anchorPoint[1], tick.getTextAnchor(), tick.getAngle(), tick.getRotationAnchor()); 
       double labelEdge = (RectangleEdge.isTopOrBottom(edge)) ? (labelBounds.getBounds2D().getMaxX()) : (labelBounds.getBounds2D().getMaxY()); 
       double dataAreaBound = (RectangleEdge.isTopOrBottom(edge)) ? (dataArea.getMaxX()) : (dataArea.getMaxY()); 

       // Magic constant 5: tick label can be rendered although it exceeds area edge for max. 5px 
       // (it still looks good - visualy tested :-) 
       if ((labelEdge - 5) <= dataAreaBound) { 
        TextUtilities.drawRotatedString(tick.getText(), g2, anchorPoint[0], anchorPoint[1], tick.getTextAnchor(), tick.getAngle(), tick.getRotationAnchor()); 
       } 
       // ---[/Override]--- 
      } 

      if ((this.isTickMarksVisible() && tick.getTickType().equals(TickType.MAJOR)) || (this.isMinorTickMarksVisible() && tick.getTickType().equals(TickType.MINOR))) { 
       double ol = tick.getTickType().equals(TickType.MINOR) ? this.getMinorTickMarkOutsideLength() : this.getTickMarkOutsideLength(); 
       double il = tick.getTickType().equals(TickType.MINOR) ? this.getMinorTickMarkInsideLength() : this.getTickMarkInsideLength(); 
       float tickVal = (float)this.valueToJava2D(tick.getValue(), dataArea, edge); 
       Line2D mark = null; 
       g2.setStroke(this.getTickMarkStroke()); 
       g2.setPaint(this.getTickMarkPaint()); 
       if(edge == RectangleEdge.LEFT) { 
        mark = new Line2D.Double(cursor - ol, tickVal, cursor + il, tickVal); 
       } else if(edge == RectangleEdge.RIGHT) { 
        mark = new Line2D.Double(cursor + ol, tickVal, cursor - il, tickVal); 
       } else if(edge == RectangleEdge.TOP) { 
        mark = new Line2D.Double(tickVal, cursor - ol, tickVal, cursor + il); 
       } else if(edge == RectangleEdge.BOTTOM) { 
        mark = new Line2D.Double(tickVal, cursor + ol, tickVal, cursor - il); 
       } 
       g2.draw(mark); 
      } 
     } 

     if (this. isTickLabelsVisible()) { 
      double used = 0.0;    
       if (edge == RectangleEdge.LEFT) { 
        used += this.findMaximumTickLabelWidth(ticks, g2, plotArea, this.isVerticalTickLabels()); 
        state.cursorLeft(used); 
       } else if (edge == RectangleEdge.RIGHT) { 
        used = this.findMaximumTickLabelWidth(ticks, g2, plotArea, this.isVerticalTickLabels()); 
        state.cursorRight(used); 
       } else if (edge == RectangleEdge.TOP) { 
        used = this.findMaximumTickLabelHeight(ticks, g2, plotArea, this.isVerticalTickLabels()); 
        state.cursorUp(used); 
       } else if (edge == RectangleEdge.BOTTOM) { 
        used = this.findMaximumTickLabelHeight(ticks, g2, plotArea, this.isVerticalTickLabels()); 
        state.cursorDown(used); 
       } 
     } 

     return state; 
    } 
} 

이 축 급 (AlignedDateAxis이) 나를 위해 잘 작동하고 내 프로젝트에 사용하고 있습니다.

Honza (sporak는)

+0

Nice. 매력처럼 작동합니다 :-) – simon04