如何使用ValueProvider将常规垂直轴添加到图表

时间:2013-03-29 14:40:36

标签: java gwt gxt

这就是我现在在图表中添加一行的方法。这是我要显示的仲裁函数的抽象类:

public abstract class ArbitraryFunction implements
    ValueProvider<ArbitraryFunctionData, Double> {

  private String field;

  public abstract Double f(Double x);

  /**
   * Constructor
   */
  public ArbitraryFunction(String field) {
    this.field = field;
  }

  @Override
  public Double getValue(ArbitraryFunctionData object) {
    return object.get(field);
  }

  @Override
  public void setValue(ArbitraryFunctionData object, Double value) {
    object.put(field, value);
  }

  @Override
  public String getPath() {
    return field;
  }
}

这是图表的创建方式:

ArbitraryFunction f1 = new ArbitraryFunction("f1") {
      @Override
      public Double f(Double x) {
        return Math.sin(x);
      }
    };

functionMap.put(f1.getPath(), f1);

// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
  ArbitraryFunctionData d = new ArbitraryFunctionData();
  d.setName("" + x);
  for (Map.Entry<String, ArbitraryFunction> entry : functionMap.entrySet()) {
    ArbitraryFunction tmp = entry.getValue();
    d.put(tmp.getPath(), tmp.f(x));
  }
  store.add(d);
}

chart.setStore(store);

verticalAxis.setPosition(Position.LEFT);
verticalAxis.addField(f1);
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);

这项工作已达到预期目的。图表显示了我应该这样做的线条,垂直轴也是正确的。但是我在绘制水平轴方面遇到了问题,因为我不知道需要给horizontalAxis.addField( ??? )什么。我尝试了一些东西,但没有任何效果。

有谁知道我需要如何设置水平轴?

1 个答案:

答案 0 :(得分:1)

你想要横轴值是多少?它是另一个NumericAxis - 每个数据点都有一个x值,它应该被绘制吗?你的循环中的每个d都有一个字符串name和一些值 - 也许你想要一个CategoryAxis<ArbitraryFunctionData, String>来绘制那些name值?


看起来我之前误解了 - 你的Function对象只是在设置中使用,而不是改变你如何绘制数据

我仍然不确定你的目标是什么,但听起来你大多想要绘制一些线条。每个数据点(ArbitraryFunctionData?)似乎都有正在使用的每个函数的Y值和一个标题,但没有X值,因此没有办法将每个点用两个数轴绘制为(X,Y),就像( name,Y)使用CategoryAxis和NumericAxis。这最终会或多或少地像这个样本:http://www.sencha.com/examples/#ExamplePlace:linechart - 沿着底部的字符串,以及沿着侧面的数字。

这里有一个,主要是基于你已经拥有的想法/结构:

public class FunctionPlotter implements EntryPoint {
  public static class ArbitraryFunctionData {
    private double xValue;
    private Map<String, Double> yValues = new HashMap<String, Double>();
    public double get(String key) {
      return yValues.get(key);
    }
    public void put(String key, double yValue) {
      yValues.put(key, yValue);
    }
    public double getXValue() {
      return xValue;
    }
    public void setxValue(double xValue) {
      this.xValue = xValue;
    }
  }
  public interface AFDProperties extends PropertyAccess<ArbitraryFunctionData> {
    //xvalue is unique, key off of that
    @Path("xValue")
    ModelKeyProvider<ArbitraryFunctionData> key();

    //automatic ValueProvider generation for the get/setXValue methods
    ValueProvider<ArbitraryFunctionData, Double> xValue();
  }

  /** 
   * This is really doing two different jobs at once - wasn't quite was I was trying to suggest in 
   * that other question. See the second version of this for clarification...
   */
  public static abstract class ArbitraryFunction implements ValueProvider<ArbitraryFunctionData, Double> {
    private final String field;

    public ArbitraryFunction(String field) {
      this.field = field;
    }

    public abstract Double f(Double x);

    @Override
    public Double getValue(ArbitraryFunctionData object) {
      return object.get(field);
    }

    @Override
    public void setValue(ArbitraryFunctionData object, Double value) {
      object.put(field, value);
    }

    @Override
    public String getPath() {
      return field;
    }
  }

  @Override
  public void onModuleLoad() {
    Viewport vp = new Viewport();

    Set<ArbitraryFunction> functions = new HashSet<ArbitraryFunction>();
    ArbitraryFunction f1 = new ArbitraryFunction("f1") {
      @Override
      public Double f(Double x) {
        return Math.sin(x);
      }
    };
    functions.add(f1);

    AFDProperties props = GWT.create(AFDProperties.class);
    ListStore<ArbitraryFunctionData> store = new ListStore<ArbitraryFunctionData>(props.key());
    // collects the data of the functions and adds them to the store
    for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
      // Create one data object, and set the X value, since that is the same for all Y values
      ArbitraryFunctionData d = new ArbitraryFunctionData();
      d.setxValue(x);

      // For each function, set the corresponding Y value
      for (ArbitraryFunction func : functions) {
        d.put(func.getPath(), func.f(x));
      }
      store.add(d);
    }

    Chart<ArbitraryFunctionData> chart = new Chart<ArbitraryFunctionData>();
    chart.setStore(store);

    //Y-axis
    NumericAxis<ArbitraryFunctionData> verticalAxis = new NumericAxis<ArbitraryFunctionData>();
    verticalAxis.setPosition(Position.LEFT);
    verticalAxis.addField(f1);//needs to know this field to properly set the range of values
    //f2, f3, etc
    verticalAxis.setSteps(2);
    verticalAxis.setMinorTickSteps(5);
    chart.addAxis(verticalAxis);

    // X-Axis, this time reading from the xValue, not the series of ValueProviders
    NumericAxis<ArbitraryFunctionData> horizAxis = new NumericAxis<ArbitraryFunctionData>();
    horizAxis.setPosition(Position.BOTTOM);
    horizAxis.addField(props.xValue());//same value for all
    horizAxis.setSteps(2);
    horizAxis.setMinorTickSteps(5);
    chart.addAxis(horizAxis);

    for (ArbitraryFunction func : functions) {
      LineSeries<ArbitraryFunctionData> line = new LineSeries<ArbitraryFunctionData>();
      // configure x axis
      line.setXAxisPosition(Position.BOTTOM);//where is it
      line.setXField(props.xValue());//what value do i use
      // configure y axis
      line.setYAxisPosition(Position.LEFT);//where is it
      line.setYField(func);//what value do i use

      //probably want to customized per func
      line.setStroke(RGB.GRAY);
      line.setStrokeWidth(2);

      chart.addSeries(line);
    }

    vp.setWidget(chart);
    RootPanel.get().add(vp);
  }
}

这里有两个,这次有更简单的数据,实际上使函数成为自己的ValueProvider,并保持数据简单 - 只需一个双!请注意,ValueProvider 函数,我们从不调用getValue,我们让轴/系列为我们做!在这里添加了第二个功能,以证明它确实有效。

public class FunctionPlotter implements EntryPoint {

  /**
   * Where did everything go? We're just making a ValueProvider now that can handle 
   * each number as a value, and working out the details from there
   *
   * For fun, added per-function coloring too
   */
  public abstract static class Function implements ValueProvider<Double, Double> {
    private final String name;
    private final Color color;
    public Function(String name, Color color) {
      this.name = name;
      this.color = color;
    }
    @Override
    public abstract Double getValue(Double object);

    @Override
    public String getPath() {
      return name;
    }
    @Override
    public void setValue(Double object, Double value) {
      //no-op
    }
    public Color getColor() {
      return color;
    }
  }

  @Override
  public void onModuleLoad() {
    Viewport vp = new Viewport();

    Set<Function> functions = new HashSet<Function>();
    Function f1 = new Function("f1", RGB.RED) {
      @Override
      public Double getValue(Double x) {
        return Math.sin(x);
      }
    };
    functions.add(f1);
    Function f2 = new Function("f2", RGB.BLACK) {
      @Override
      public Double getValue(Double x) {
        return Math.cos(x);
      }
    };
    functions.add(f2);

    //Turns out Stores can hold any objects - should probably factor out this key provider for reuse...
    ListStore<Double> store = new ListStore<Double>(new ModelKeyProvider<Double>() {
      @Override
      public String getKey(Double item) {
        return item.toString();
      }
    });
    // collects the data of the functions and adds them to the store
    for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
      store.add(x);
    }

    Chart<Double> chart = new Chart<Double>();
    chart.setStore(store);

    //Y-axis
    NumericAxis<Double> verticalAxis = new NumericAxis<Double>();
    verticalAxis.setPosition(Position.LEFT);
    for (Function func : functions) {
      verticalAxis.addField(func);//needs to know this field to properly set the range of values
    }
    verticalAxis.setSteps(2);
    verticalAxis.setMinorTickSteps(5);
    chart.addAxis(verticalAxis);

    // X-Axis, this time reading from the xValue, not the series of ValueProviders
    NumericAxis<Double> horizAxis = new NumericAxis<Double>();
    horizAxis.setPosition(Position.BOTTOM);
    horizAxis.addField(new IdentityValueProvider<Double>());//magic value provider that returns the same string
    horizAxis.setSteps(2);
    horizAxis.setMinorTickSteps(5);
    chart.addAxis(horizAxis);

    for (Function func : functions) {
      LineSeries<Double> line = new LineSeries<Double>();
      // configure x axis
      line.setXAxisPosition(Position.BOTTOM);//where is it
      line.setXField(new IdentityValueProvider<Double>());//what value do i use
      // configure y axis
      line.setYAxisPosition(Position.LEFT);//where is it
      line.setYField(func);//what value do i use

      //probably want to customized per func
      line.setStroke(func.getColor());
      line.setStrokeWidth(2);

      chart.addSeries(line);
    }

    vp.setWidget(chart);
    RootPanel.get().add(vp);
  }
}