Created
October 31, 2013 16:29
-
-
Save james-d/7252698 to your computer and use it in GitHub Desktop.
Example of a LineChart that can be zoomed via mouse.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Collections; | |
import java.util.Random; | |
import javafx.application.Application; | |
import javafx.beans.binding.BooleanBinding; | |
import javafx.beans.property.ObjectProperty; | |
import javafx.beans.property.SimpleObjectProperty; | |
import javafx.collections.FXCollections; | |
import javafx.collections.ObservableList; | |
import javafx.event.ActionEvent; | |
import javafx.event.EventHandler; | |
import javafx.geometry.Insets; | |
import javafx.geometry.Point2D; | |
import javafx.geometry.Pos; | |
import javafx.scene.Node; | |
import javafx.scene.Scene; | |
import javafx.scene.chart.Axis; | |
import javafx.scene.chart.LineChart; | |
import javafx.scene.chart.NumberAxis; | |
import javafx.scene.chart.XYChart.Data; | |
import javafx.scene.chart.XYChart.Series; | |
import javafx.scene.control.Button; | |
import javafx.scene.input.MouseEvent; | |
import javafx.scene.layout.BorderPane; | |
import javafx.scene.layout.HBox; | |
import javafx.scene.layout.StackPane; | |
import javafx.scene.paint.Color; | |
import javafx.scene.shape.Rectangle; | |
import javafx.stage.Stage; | |
public class ZoomableLineChart extends Application { | |
private static final int NUM_DATA_POINTS = 1000 ; | |
@Override | |
public void start(Stage primaryStage) { | |
final LineChart<Number, Number> chart = createChart(); | |
final StackPane chartContainer = new StackPane(); | |
chartContainer.getChildren().add(chart); | |
final Rectangle zoomRect = new Rectangle(); | |
zoomRect.setManaged(false); | |
zoomRect.setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5)); | |
chartContainer.getChildren().add(zoomRect); | |
setUpZooming(zoomRect, chart); | |
final HBox controls = new HBox(10); | |
controls.setPadding(new Insets(10)); | |
controls.setAlignment(Pos.CENTER); | |
final Button zoomButton = new Button("Zoom"); | |
final Button resetButton = new Button("Reset"); | |
zoomButton.setOnAction(new EventHandler<ActionEvent>() { | |
@Override | |
public void handle(ActionEvent event) { | |
doZoom(zoomRect, chart); | |
} | |
}); | |
resetButton.setOnAction(new EventHandler<ActionEvent>() { | |
@Override | |
public void handle(ActionEvent event) { | |
final NumberAxis xAxis = (NumberAxis)chart.getXAxis(); | |
xAxis.setLowerBound(0); | |
xAxis.setUpperBound(1000); | |
final NumberAxis yAxis = (NumberAxis)chart.getYAxis(); | |
yAxis.setLowerBound(0); | |
yAxis.setUpperBound(1000); | |
zoomRect.setWidth(0); | |
zoomRect.setHeight(0); | |
} | |
}); | |
final BooleanBinding disableControls = | |
zoomRect.widthProperty().lessThan(5) | |
.or(zoomRect.heightProperty().lessThan(5)); | |
zoomButton.disableProperty().bind(disableControls); | |
controls.getChildren().addAll(zoomButton, resetButton); | |
final BorderPane root = new BorderPane(); | |
root.setCenter(chartContainer); | |
root.setBottom(controls); | |
final Scene scene = new Scene(root, 600, 400); | |
primaryStage.setScene(scene); | |
primaryStage.show(); | |
} | |
private LineChart<Number, Number> createChart() { | |
final NumberAxis xAxis = createAxis(); | |
final NumberAxis yAxis = createAxis(); | |
final LineChart<Number, Number> chart = new LineChart<>(xAxis, yAxis); | |
chart.setAnimated(false); | |
chart.setCreateSymbols(false); | |
chart.setData(generateChartData()); | |
return chart ; | |
} | |
private NumberAxis createAxis() { | |
final NumberAxis xAxis = new NumberAxis(); | |
xAxis.setAutoRanging(false); | |
xAxis.setLowerBound(0); | |
xAxis.setUpperBound(1000); | |
return xAxis; | |
} | |
private ObservableList<Series<Number, Number>> generateChartData() { | |
final Series<Number, Number> series = new Series<>(); | |
series.setName("Data"); | |
final Random rng = new Random(); | |
for (int i=0; i<NUM_DATA_POINTS; i++) { | |
Data<Number, Number> dataPoint = new Data<Number, Number>(i, rng.nextInt(1000)); | |
series.getData().add(dataPoint); | |
} | |
return FXCollections.observableArrayList(Collections.singleton(series)); | |
} | |
private void setUpZooming(final Rectangle rect, final Node zoomingNode) { | |
final ObjectProperty<Point2D> mouseAnchor = new SimpleObjectProperty<>(); | |
zoomingNode.setOnMousePressed(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
mouseAnchor.set(new Point2D(event.getX(), event.getY())); | |
rect.setWidth(0); | |
rect.setHeight(0); | |
} | |
}); | |
zoomingNode.setOnMouseDragged(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
double x = event.getX(); | |
double y = event.getY(); | |
rect.setX(Math.min(x, mouseAnchor.get().getX())); | |
rect.setY(Math.min(y, mouseAnchor.get().getY())); | |
rect.setWidth(Math.abs(x - mouseAnchor.get().getX())); | |
rect.setHeight(Math.abs(y - mouseAnchor.get().getY())); | |
} | |
}); | |
} | |
private void doZoom(Rectangle zoomRect, LineChart<Number, Number> chart) { | |
Point2D zoomTopLeft = new Point2D(zoomRect.getX(), zoomRect.getY()); | |
Point2D zoomBottomRight = new Point2D(zoomRect.getX() + zoomRect.getWidth(), zoomRect.getY() + zoomRect.getHeight()); | |
final NumberAxis yAxis = (NumberAxis) chart.getYAxis(); | |
Point2D yAxisInScene = yAxis.localToScene(0, 0); | |
final NumberAxis xAxis = (NumberAxis) chart.getXAxis(); | |
Point2D xAxisInScene = xAxis.localToScene(0, 0); | |
double xOffset = zoomTopLeft.getX() - yAxisInScene.getX() ; | |
double yOffset = zoomBottomRight.getY() - xAxisInScene.getY(); | |
double xAxisScale = xAxis.getScale(); | |
double yAxisScale = yAxis.getScale(); | |
xAxis.setLowerBound(xAxis.getLowerBound() + xOffset / xAxisScale); | |
xAxis.setUpperBound(xAxis.getLowerBound() + zoomRect.getWidth() / xAxisScale); | |
yAxis.setLowerBound(yAxis.getLowerBound() + yOffset / yAxisScale); | |
yAxis.setUpperBound(yAxis.getLowerBound() - zoomRect.getHeight() / yAxisScale); | |
System.out.println(yAxis.getLowerBound() + " " + yAxis.getUpperBound()); | |
zoomRect.setWidth(0); | |
zoomRect.setHeight(0); | |
} | |
public static void main(String[] args) { | |
launch(args); | |
} | |
} |
There's a small bug in this, where the zoom is actually a bit to the right of the zoom band that you see visually. This is caused by the yAxis point (0,0) being at the top-left of the yAxis, and not taking into account the thickness of the axis. When the code gets the xOffset, it should take into acount the right side of the axis, not the left.
Line 150 should be as follows:
double xOffset = zoomTopLeft.getX() - (yAxisInScene.getX() + yAxis.getWidth());
Line 150 should be as follows:
double xOffset = zoomTopLeft.getX() - (yAxisInScene.getX() - yAxis.getWidth());
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@polisettyManoj
Hey i just saw your post.
Step 1. Create class called Data -> Example Company (AAPL)
Step 2. There are just a few changes you need to make
`private ComboBox companyComboBox;
private String symbol;'
From the class company, you are going to make two fields that are called symbol and company -> make getMethods
Make a constructor for the Company class
- Company
- Parameters: String symbol, String name
- super
-this.symbol = symbol
Make a method called getCompanies()
public static List<Company> getCompanies() { List<Company> company =new ArrayList<>(); list.add(new Company("AAPL", "AAPL"); return company }
Part 3: Head back to MainApp
create a field called listCompany
within the init Method
-
ComboBox<Company> companyComboBox = new ComboBox<>(); companyComboBox.setItems(listCompany);
- companyComboBox.getSelectionModel().selectedItemProperty().addListener((
ChangeListener<? super Company>)
(ObservableValue<? extends Company> observable, Company oldValue, Company newValue) -> {
if (newValue.getSymbol != null) {
symbol = newValue.getSymbol();
// call to method start()
start();
} else {
// do something else
}
}
next add companyComboBox to Hbox
`private static void start() {
}`
and finally to add the real-time data
- Data is provided by "https://github.com/WojciechZankowski/iextrading4j"
- private static class AddToQueue implements Runnable {
}
The rest should be pretty easy to do yourself