如何将ScatterChart与水平和垂直线混合

时间:2018-01-15 21:30:06

标签: javafx kotlin tornadofx

我试图在ScatterChart上创建垂直和水平线以及点。我找不到混合它们的方法。

enter image description here

这是我在图表上生成点的代码。

vbox {
    add(ScatterChart(NumberAxis(), NumberAxis()).apply {
        val seriesMap: HashMap<String, XYChart.Series<Number, Number>> = HashMap()

        pointsList
                .map { it.decisionClass }
                .distinct()
                .forEach {
                    seriesMap.put(it, XYChart.Series())
                }

        for (point in pointsList) {
            seriesMap.get(point.decisionClass)?.data(point.axisesValues[0], point.axisesValues[1])
        }

        seriesMap
                .toSortedMap()
                .forEach { key, value ->
                    value.name = key
                    data.add(value)
                }
        (xAxis as NumberAxis).setForceZeroInRange(false)
        (yAxis as NumberAxis).setForceZeroInRange(false)
    })
}

1 个答案:

答案 0 :(得分:2)

我不知道Kotlin,所以这个答案是用Java写的。我想你可以把它翻译成Kotlin(如果是的话,可以随意发布另一个答案)。

要向图表添加其他节点,您需要做三件事:

  1. 调用getPlotChildren()并将新节点添加到图表的“plot children”
  2. 在布置图表时,覆盖layoutPlotChildren()方法以更新节点的位置
  3. 使用getDisplayPosition(...)中定义的Axis,从轴上的值获取图表绘图区域坐标系中的位置。
  4. 以下SSCCE创建的散点图有点类似于您在屏幕截图中发布的散点图,并添加了指定系列的线(即左侧的线延伸图表的高度并通过最小x值该系列;顶部的线条延伸图表的宽度,并通过系列的最大y值等)。我添加了单选按钮,这样你就可以选择哪些系列是“有界”的。

    import java.util.Random;
    
    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.scene.Scene;
    import javafx.scene.chart.NumberAxis;
    import javafx.scene.chart.ScatterChart;
    import javafx.scene.chart.XYChart.Data;
    import javafx.scene.chart.XYChart.Series;
    import javafx.scene.control.RadioButton;
    import javafx.scene.control.ToggleGroup;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.VBox;
    import javafx.scene.shape.Line;
    import javafx.stage.Stage;
    
    public class ScatterChartWithLines extends Application {
    
        private final class ScatterChartWithBoundary extends ScatterChart<Number, Number> {
    
            private Series<Number, Number> boundedSeries ;
    
            private final NumberAxis yAxis;
            private final NumberAxis xAxis;
            private final Line leftLine = new Line();
            private final Line rightLine = new Line();
            private final Line topLine = new Line();
            private final Line bottomLine = new Line();
            {
                getPlotChildren().addAll(leftLine, rightLine, topLine, bottomLine);
            }
    
            private ScatterChartWithBoundary(NumberAxis xAxis, NumberAxis yAxis) {
                super(xAxis, yAxis);
                this.yAxis = yAxis;
                this.xAxis = xAxis;
            }
    
            @Override
            protected void layoutPlotChildren() {
                super.layoutPlotChildren();
                getPlotChildren().removeAll(leftLine, rightLine, topLine, bottomLine);
                if (boundedSeries != null) {
                    getPlotChildren().addAll(leftLine, rightLine, topLine, bottomLine);
                    double minX = Double.MAX_VALUE ;
                    double minY = Double.MAX_VALUE ;
                    double maxX = Double.MIN_VALUE ;
                    double maxY = Double.MIN_VALUE ;
                    for (Data<Number, Number> d : boundedSeries.getData()) {
                        if (d.getXValue().doubleValue() < minX) minX = d.getXValue().doubleValue() ;
                        if (d.getXValue().doubleValue() > maxX) maxX = d.getXValue().doubleValue() ;
                        if (d.getYValue().doubleValue() < minY) minY = d.getYValue().doubleValue() ;
                        if (d.getYValue().doubleValue() > maxY) maxY = d.getYValue().doubleValue() ;
                    }
                    positionLineInAxisCoordinates(leftLine, minX, yAxis.getLowerBound(), minX, yAxis.getUpperBound());
                    positionLineInAxisCoordinates(rightLine, maxX, yAxis.getLowerBound(), maxX, yAxis.getUpperBound());
                    positionLineInAxisCoordinates(bottomLine, xAxis.getLowerBound(), minY, xAxis.getUpperBound(), minY);
                    positionLineInAxisCoordinates(topLine, xAxis.getLowerBound(), maxY, xAxis.getUpperBound(), maxY);
                }
            }
    
            private void positionLineInAxisCoordinates(Line line, double startX, double startY, double endX, double endY) {
                double x0 = xAxis.getDisplayPosition(startX);
                double x1 = xAxis.getDisplayPosition(endX);
                double y0 = yAxis.getDisplayPosition(startY);
                double y1 = yAxis.getDisplayPosition(endY);
    
    
                line.setStartX(x0);
                line.setStartY(y0);
                line.setEndX(x1);
                line.setEndY(y1);
            }
    
            public void setBoundedSeries(Series<Number, Number> boundedSeries) {
                if (! getData().contains(boundedSeries)) {
                    throw new IllegalArgumentException("Specified series is not displayed in this chart");
                }
                this.boundedSeries = boundedSeries ;
                requestChartLayout();
            }
        }
    
        private final Random rng = new Random();
    
        @Override
        public void start(Stage primaryStage) {
            Series<Number, Number> series1 = new Series<>("Series 1", FXCollections.observableArrayList());
            Series<Number, Number> series2 = new Series<>("Series 2", FXCollections.observableArrayList());
            Series<Number, Number> series3 = new Series<>("Series 3", FXCollections.observableArrayList());
    
            for (int i = 0 ; i < 40 ; i++) {
                series1.getData().add(new Data<>(rng.nextDouble()*2 + 4, rng.nextDouble()*3 + 2));
                series2.getData().add(new Data<>(rng.nextDouble()*2.5 + 4.75, rng.nextDouble()*1.5 + 2));
                series3.getData().add(new Data<>(rng.nextDouble()*3 + 5, rng.nextDouble()*1.5 + 2.75));         
            }
    
            NumberAxis xAxis = new NumberAxis();
            NumberAxis yAxis = new NumberAxis();
            xAxis.setForceZeroInRange(false);
            yAxis.setForceZeroInRange(false);
    
            ScatterChartWithBoundary chart = new ScatterChartWithBoundary(xAxis, yAxis);
    
            chart.getData().addAll(series1, series2, series3);
    
            VBox buttons = new VBox(2);
            ToggleGroup toggleGroup = new ToggleGroup();
            for (Series<Number, Number> series : chart.getData()) { 
                RadioButton rb = new RadioButton(series.getName());
                rb.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
                    if (isNowSelected) {
                        chart.setBoundedSeries(series);
                    }
                });
                rb.setToggleGroup(toggleGroup);
                buttons.getChildren().add(rb);
            }
    
    
            BorderPane root = new BorderPane(chart);
            root.setTop(buttons);
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    这是一个典型的结果:

    enter image description here