質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Q&A

解決済

1回答

2508閲覧

CategoryAxisの拡大

oscikonome

総合スコア16

JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

0グッド

0クリップ

投稿2017/02/07 13:08

以下のようなソースにより、チャートをドラッグで範囲指定して拡大、クリックして戻す、を実装しています。

実際はデータが多く、見づらくなるため拡大したいのですが、
これをX軸をCategoryAxisにした場合でも同様に拡大したいのです。
Y軸にだけ拡大を実装することはできるのですが、X軸がそのままなので密になっている部分をピンポイントで拡大して見ることができていません。

ドラッグで直接拡大できずとも、何かしらうまい具合の操作で拡大して密な部分を表示する方法があれば教えていただければと思います。

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class Test6 extends Application {

@Override public void start(Stage stage) { final NumberAxis xAxis = new NumberAxis(); final NumberAxis yAxis = new NumberAxis(); xAxis.setLabel("X軸"); yAxis.setLabel("Y軸"); //creating the chart final LineChart<Double, Double> lineChart = new LineChart(xAxis, yAxis); lineChart.setLegendVisible(false); ObservableList<XYChart.Series<Double, Double>> lineData = getLineData(); lineChart.setData(lineData); StackPane chartContainer = new StackPane(); new ChartZoom(lineChart, chartContainer); Scene scene = new Scene(chartContainer, 600, 400); stage.setScene(scene); stage.show(); }

private ObservableList<XYChart.Series<Double, Double>> getLineData() {
Series<Double, Double> series1 = new Series<>();
Series<Double, Double> series2 = new Series<>();

for (int i = 0; i < 50; i++) { series1.getData().add(new XYChart.Data<Double, Double>(Math.random() * 2 - 1.4, Math.random() - .7)); series2.getData().add(new XYChart.Data<Double, Double>(Math.random() * 2 - .6, Math.random() - .3)); } ObservableList<XYChart.Series<Double, Double>> seriesList = FXCollections.observableArrayList(); seriesList.addAll(series1, series2); return seriesList; } public static void main(String[] args) { launch(args); }

}

class ChartZoom extends Rectangle {

private StackPane chartContainer; private LineChart lineChart; public ChartZoom(LineChart lineChart, StackPane chartContainer) { this.chartContainer = chartContainer; this.lineChart = lineChart; this.chartContainer.getChildren().add(lineChart); this.chartContainer.getChildren().add(this); initZoom(); setManaged(false); setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5)); setUpZooming(); } //初期状態 private void initZoom() { NumberAxis xAxis = (NumberAxis) lineChart.getXAxis(); NumberAxis yAxis = (NumberAxis) lineChart.getYAxis(); xAxis.setAutoRanging(true); xAxis.setForceZeroInRange(false); yAxis.setAutoRanging(true); yAxis.setForceZeroInRange(false); } //押下、ドラッグ、離脱で座標を取得 private void setUpZooming() { final ObjectProperty<Point2D> mouseAnchor = new SimpleObjectProperty<>(); lineChart.setOnMousePressed((MouseEvent event) -> { setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5)); mouseAnchor.set(new Point2D(event.getX(), event.getY())); setWidth(0); setHeight(0); }); lineChart.setOnMouseDragged((MouseEvent event) -> { double x = event.getX(); double y = event.getY(); setX(Math.min(x, mouseAnchor.get().getX())); setY(Math.min(y, mouseAnchor.get().getY())); setWidth(x - mouseAnchor.get().getX()); setHeight(y - mouseAnchor.get().getY()); }); lineChart.setOnMouseReleased((MouseEvent event) -> { if ((getWidth() > 0) && (getHeight() > 0)) { doZoom(); }else { initZoom(); } }); } //拡大範囲を設定 private void doZoom() { Point2D zoomTopLeft = new Point2D(getX(), getY()); Point2D zoomBottomRight = new Point2D(getX() + getWidth(), getY() + getHeight()); Point2D chartInScene = lineChart.localToScene(0, 0); NumberAxis xAxis = (NumberAxis) lineChart.getXAxis(); xAxis.setAutoRanging(false); Point2D xAxisInScene = xAxis.localToScene(0, 0); NumberAxis yAxis = (NumberAxis) lineChart.getYAxis(); yAxis.setAutoRanging(false); double xOffset = zoomTopLeft.getX() - xAxisInScene.getX() + chartInScene.getX(); double yOffset = zoomBottomRight.getY() - xAxisInScene.getY() + chartInScene.getY(); double xAxisScale = xAxis.getScale(); double yAxisScale = yAxis.getScale(); xAxis.setLowerBound(xAxis.getLowerBound() + xOffset / xAxisScale); xAxis.setUpperBound(xAxis.getLowerBound() + getWidth() / xAxisScale); yAxis.setLowerBound(yAxis.getLowerBound() + yOffset / yAxisScale); yAxis.setUpperBound(yAxis.getLowerBound() - getHeight() / yAxisScale); setWidth(0); setHeight(0); }

}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

試してみてはいないですが、ValueAxisのようにlowerBound, upperBoundはないもののsetCategoriesメソッドで表示するカテゴリー一覧を明示的に指定できそうなので・・・

  • ズーム範囲を指定する際

getValueForDisplayで選択範囲の左右の座標位置に最も近いカテゴリーを求めておく

  • ズーム時

表示範囲のカテゴリー範囲のリストを作成しsetCategoriesで設定する。チャートのリストと不一致の場合は自動的にsetAutoRanging(false)になるようです。

  • ズーム解除

setCategoriesへ全データを指定しなおす

という方法が取れないでしょうか?


追記:最初「CategoryAxisを派生してValueAxisのlowerBound, upperBoundのようなプロパティーを実装してみては?」と回答しようとして、あまりに迂遠だと思い上の回答をしたのですが、期待と違う結果になるのですね・・・すみません。

ValueAxisを用いてValueに「CategoryAxisとして見せたい文字列ラベルと順序を表すComparableを両方持つ」ようなものを使い、ノードビジュアルをカスタマイズしてラベルのみを表示するように工夫したらどうだろうと思いつきました。試してないのでこれも単なるアイデアですがチャートを多用するならそうした実験もありかも知れません。

投稿2017/02/10 09:42

編集2017/02/14 06:53
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

oscikonome

2017/02/14 03:32

以下のように変更して試してみました。 getValueForDisplayの挙動がまだよくわかっていませんが、 軸の拡大には成功しました。 しかし、データ点が拡大されず、ラインが消えてしまいました。 CategoryAxisをNumberAxisにして、整数値とカテゴリを紐づけたTableViewなどを添えておくのがいい落としどころでしょうか。 CategoryAxisである以上当初の通りに実装することは難しそうなので、 本回答でいったん解決とします。ありがとうございます。 public class Test extends Application { @Override public void start(Stage stage) { final CategoryAxis xAxis = new CategoryAxis(); final NumberAxis yAxis = new NumberAxis(); xAxis.setLabel("X軸"); yAxis.setLabel("Y軸"); //creating the chart final LineChart<String, Double> lineChart = new LineChart(xAxis, yAxis); lineChart.setLegendVisible(false); ObservableList<XYChart.Series<String, Double>> lineData = getLineData(); lineChart.setData(lineData); StackPane chartContainer = new StackPane(); new ChartZoom(lineChart, chartContainer); Scene scene = new Scene(chartContainer, 600, 400); stage.setScene(scene); stage.show(); } private ObservableList<XYChart.Series<String, Double>> getLineData() { Series<String, Double> series1 = new Series<>(); Series<String, Double> series2 = new Series<>(); for (int i = 0; i < 50; i++) { series1.getData().add(new XYChart.Data<String, Double>(String.valueOf(i), Math.random() - .7)); series2.getData().add(new XYChart.Data<String, Double>(String.valueOf(i), Math.random() - .3)); } ObservableList<XYChart.Series<String, Double>> seriesList = FXCollections.observableArrayList(); seriesList.addAll(series1, series2); return seriesList; } public static void main(String[] args) { launch(args); } } class ChartZoom extends Rectangle { private StackPane chartContainer; private LineChart lineChart; public ChartZoom(LineChart lineChart, StackPane chartContainer) { this.chartContainer = chartContainer; this.lineChart = lineChart; this.chartContainer.getChildren().add(lineChart); this.chartContainer.getChildren().add(this); initZoom(); setManaged(false); setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5)); setUpZooming(); } //初期状態 private void initZoom() { CategoryAxis xAxis = (CategoryAxis) lineChart.getXAxis(); NumberAxis yAxis = (NumberAxis) lineChart.getYAxis(); xAxis.setAutoRanging(true); //xAxis.setForceZeroInRange(false); yAxis.setAutoRanging(true); yAxis.setForceZeroInRange(false); } //押下、ドラッグ、離脱で座標を取得 private void setUpZooming() { final ObjectProperty<Point2D> mouseAnchor = new SimpleObjectProperty<>(); lineChart.setOnMousePressed((MouseEvent event) -> { setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5)); mouseAnchor.set(new Point2D(event.getX(), event.getY())); setWidth(0); setHeight(0); }); lineChart.setOnMouseDragged((MouseEvent event) -> { double x = event.getX(); double y = event.getY(); setX(Math.min(x, mouseAnchor.get().getX())); setY(Math.min(y, mouseAnchor.get().getY())); setWidth(x - mouseAnchor.get().getX()); setHeight(y - mouseAnchor.get().getY()); }); lineChart.setOnMouseReleased((MouseEvent event) -> { if ((getWidth() > 0) && (getHeight() > 0)) { doZoom(); }else { initZoom(); } }); } //拡大範囲を設定 private void doZoom() { Point2D zoomTopLeft = new Point2D(getX(), getY()); Point2D zoomBottomRight = new Point2D(getX() + getWidth(), getY() + getHeight()); Point2D chartInScene = lineChart.localToScene(0, 0); CategoryAxis xAxis = (CategoryAxis) lineChart.getXAxis(); xAxis.setAutoRanging(false); Point2D xAxisInScene = xAxis.localToScene(0, 0); String x = xAxis.getValueForDisplay(getX()); ObservableList<String> value = FXCollections.observableArrayList(); value.add(x); NumberAxis yAxis = (NumberAxis) lineChart.getYAxis(); yAxis.setAutoRanging(false); double xOffset = zoomTopLeft.getX() - xAxisInScene.getX() + chartInScene.getX(); double yOffset = zoomBottomRight.getY() - xAxisInScene.getY() + chartInScene.getY(); //double xAxisScale = xAxis.getScale(); double yAxisScale = yAxis.getScale(); // xAxis.setLowerBound(xAxis.getLowerBound() + xOffset / xAxisScale); // xAxis.setUpperBound(xAxis.getLowerBound() + getWidth() / xAxisScale); xAxis.setCategories(value); yAxis.setLowerBound(yAxis.getLowerBound() + yOffset / yAxisScale); yAxis.setUpperBound(yAxis.getLowerBound() - getHeight() / yAxisScale); setWidth(0); setHeight(0); } }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問