I have a Tableview<ObservableList<Item>>, which is not updating when the underlying data is updated. Through debugging, I know that the underlying ObservableList<Item>> is being properly updated. I have ensured that all of Item's properties are visible, and in the format myFieldProperty().
Here is my table creation:
pattern= new TableView<>(mainApp.getItemList());
for (ObservableList<Item> row : pattern.getItems()) {
for (int i= pattern.getColumns().size(); i<row.size(); i++){
final int columnIndex = i ;
TableColumn<ObservableList<Item>, Color> column = new TableColumn<>();
column.setCellValueFactory( rowData ->
rowData.getValue()
.get(columnIndex).displayColorProperty()); // the Item for this cell
column.setCellFactory(col -> {
ItemCell cell = new ItemCell();
cell.setOnMouseEntered( e -> {
if (cell.getItem() != null) {
#SuppressWarnings("unchecked")
ObservableList<Item> stitchRow =
(ObservableList<Item>) cell.getTableRow().getItem();
mainApp.getRLController().setItemLabel(itemRow.get(columnIndex).toString());
}
});
cell.setOnMouseExited( e -> {
mainApp.getRLController().setItemLabel(null);
});
cell.setOnMouseClicked((MouseEvent e) -> {
Item newItem = mainApp.getTBController().getSelectedItem();
if (e.getButton() == MouseButton.PRIMARY && newItem != null) {
ObservableList<Item> itemRow =
(ObservableList<Item>) cell.getTableRow().getItem();
itemRow.set(columnIndex, newItem);
mainApp.getRLController().setItemLabel(itemRow.get(columnIndex).toString());
}
});
return cell;
});
column.setMinWidth(7);
column.setPrefWidth(7);
column.setMaxWidth(7);
pattern.getColumns().add(column);
}
}
pattern.setFixedCellSize(7);
pattern.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);`
Code for my Custom Cell Factory:
public class ItemCell extends TableCell<ObservableList<Item>, Color> {
#Override
protected void updateItem(Color color, boolean empty) {
super.updateItem(color, empty);
if (empty || color == null) {
setText(null);
setStyle(null);
} else {
int r = (int) (color.getRed() * 255);
int g = (int) (color.getGreen() * 255);
int b = (int) (color.getBlue() * 255);
this.setStyle("-fx-background-color: rgb(" + r + "," + g + "," + b + ");"
+ "-fx-border-color: black; -fx-table-cell-border-color: black;");
}
}
}
The basic problem is that the object you are changing (the item which is an element of the list representing the row) is not the property that the cell is observing for changes (the displayColorProperty() belonging to the item). You need to arrange to change the value of a property that the cell is observing.
Three possible solutions:
If possible, just change the displayColor (and other data too) of the item displayed by the cell. I.e.
cell.setOnMouseClicked((MouseEvent e) -> {
if (e.getButton() == MouseButton.PRIMARY && newItem != null) {
ObservableList<Item> itemRow =
(ObservableList<Item>) cell.getTableRow().getItem();
Item item = itemRow.get(columnIndex);
item.setDisplayColor(...);
item.set...(...);
// ...
mainApp.getRLController().setItemLabel(item.toString());
}
});
Or, replace the entire row:
cell.setOnMouseClicked((MouseEvent e) -> {
Item newItem = mainApp.getTBController().getSelectedItem();
if (e.getButton() == MouseButton.PRIMARY && newItem != null) {
ObservableList<Item> itemRow =
(ObservableList<Item>) cell.getTableRow().getItem();
ObservableList<Item> newRow = FXCollections.observableArrayList(itemRow);
newRow.set(columnIndex, newItem);
pattern.getItems().set(cell.getTableRow().getIndex(), newRow);
mainApp.getRLController().setItemLabel(newRow.get(columnIndex).toString());
}
});
Otherwise, you could make your table a TableView<ObservableList<ObjectProperty<Item>>>. This gets a little tricky but it's not too bad. This way you can just set the value of the object property to your new item.
Here's a complete example using the third technique:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ColorTableExample extends Application {
#Override
public void start(Stage primaryStage) {
TableView<ObservableList<ObjectProperty<Item>>> table = new TableView<>();
int NUM_ROWS = 20 ;
int NUM_COLS = 15 ;
ObservableList<ObservableList<ObjectProperty<Item>>> data = table.getItems() ;
for (int y = 0 ; y < NUM_ROWS; y++) {
ObservableList<ObjectProperty<Item>> row = FXCollections.observableArrayList();
data.add(row);
double saturation = (1.0 * y) / NUM_ROWS ;
for (int x = 0 ; x < NUM_COLS; x++) {
double hue = x * 360.0 / NUM_COLS ;
Color color = Color.hsb(hue, saturation, 1.0);
row.add(new SimpleObjectProperty<>(new Item(color)));
}
}
for (ObservableList<ObjectProperty<Item>> row : table.getItems()) {
for (int i = table.getColumns().size() ; i < row.size(); i++) {
int columnIndex = i ;
TableColumn<ObservableList<ObjectProperty<Item>>, Item> column = new TableColumn<>(Integer.toString(i+1));
column.setCellValueFactory(rowData -> rowData.getValue().get(columnIndex));
column.setCellFactory(c -> {
TableCell<ObservableList<ObjectProperty<Item>>, Item> cell = new TableCell<ObservableList<ObjectProperty<Item>>, Item>() {
#Override
public void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setStyle("");
} else {
Color color = item.getDisplayColor() ;
int r = (int) (color.getRed() * 255) ;
int g = (int) (color.getGreen() * 255) ;
int b = (int) (color.getBlue() * 255) ;
String style = String.format(
"-fx-background-color: rgb(%d, %d, %d);"
+ "-fx-border-color: black ;"
+ "-fx-table-cell-border-color: black ;"
,r, g, b);
setStyle(style);
}
}
};
cell.setOnMousePressed(evt -> {
if (! cell.isEmpty()) {
ObservableList<ObjectProperty<Item>> rowData = (ObservableList<ObjectProperty<Item>>) cell.getTableRow().getItem();
Color currentColor = cell.getItem().getDisplayColor();
double newHue = ( currentColor.getHue() + 15 ) % 360 ;
Color newColor = Color.hsb(newHue, currentColor.getSaturation(), currentColor.getBrightness());
rowData.get(columnIndex).set(new Item(newColor));
}
});
return cell ;
});
table.getColumns().add(column);
}
}
BorderPane root = new BorderPane(table, null, null, null, null);
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
public static class Item {
private final ObjectProperty<Color> displayColor = new SimpleObjectProperty<>() ;
public Item(Color color) {
this.displayColorProperty().set(color);
}
public final ObjectProperty<Color> displayColorProperty() {
return this.displayColor;
}
public final javafx.scene.paint.Color getDisplayColor() {
return this.displayColorProperty().get();
}
public final void setDisplayColor(final javafx.scene.paint.Color displayColor) {
this.displayColorProperty().set(displayColor);
}
}
public static void main(String[] args) {
launch(args);
}
}
(At some point, it might be easier to refactor everything so that you have an actual class representing each row in the table, instead of using a list.)
There may also be a clever workaround using an extractor for the list, but I couldn't make that work.
Related
I have a barchat with 5 bars, now I'm trying to move each bar from left to right based on high value (more like a bar chart race from flourish # https://app.flourish.studio) not the same but the idea is the same.
To check small numbers against big numbers and big numbers against small numbers I'm using random integers.
For example, If barE is greater then all bars and less then barA, it should move to number 2 and replace bar B. I'm using the "if statement" to try and accomplish this. The problem is, only one transition is happening which is the first one. When random numbers change every 3 seconds the correct transition does not happen. Does anyone know how can I correct this problem?
import java.util.Calendar;
import java.util.TimeZone;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class App extends Application {
private ScheduledExecutorService scheduledExecutorService;
final static String austria = "Austria", brazil = "Brazil", france = "France", england = "England", belgium = "Belgium";
private IntegerProperty secondA, secondB , secondC, secondD, secondE;
private Text secondAText, secondBText , secondCText, secondDText, secondEText;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Realtime Bar Chart Demo");
//defining the axes
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setAnimated(false);
yAxis.setAnimated(false);
//creating the bar chart with two axis
final BarChart<String,Number> bc = new BarChart<>(xAxis,yAxis);
bc.setAnimated(false);
bc.setTitle("Country Summary");
xAxis.setLabel("Country");
yAxis.setLabel("Value");
//defining a series to display data
XYChart.Series<String, Number> seriesA = new XYChart.Series<>();
Data<String, Number> dataA = new XYChart.Data<>(austria,0);
seriesA.getData().add(dataA);
seriesA.setName("Austra");
secondA = new SimpleIntegerProperty(0);
secondAText = new Text("");
secondA.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
dataA.setYValue(newValue);
secondAText.setText(String.valueOf(newValue));
});
XYChart.Series<String, Number> seriesB = new XYChart.Series<>();
Data<String, Number> dataB = new XYChart.Data<>(brazil,0);
seriesB.getData().add(dataB);
seriesB.setName("Brazil");
secondB = new SimpleIntegerProperty(0);
secondB.bind(secondA.add(0));
secondBText = new Text("");
secondB.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
dataB.setYValue(newValue);
secondBText.setText(String.valueOf(newValue));
});
XYChart.Series<String, Number> seriesC = new XYChart.Series<>();
Data<String, Number> dataC = new XYChart.Data<>(france,0);
seriesC.getData().add(dataC);
seriesC.setName("France");
secondC = new SimpleIntegerProperty(0);
secondC.bind(secondA.add(0));
secondCText = new Text("");
secondC.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
dataC.setYValue(newValue);
secondCText.setText(String.valueOf(newValue));
});
XYChart.Series<String, Number> seriesD = new XYChart.Series<>();
Data<String, Number> dataD = new XYChart.Data<>(england,0);
seriesD.getData().add(dataD);
seriesD.setName("England");
secondD = new SimpleIntegerProperty(0);
secondD.bind(secondA.add(0));
secondDText = new Text("");
secondD.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
dataD.setYValue(newValue);
secondDText.setText(String.valueOf(newValue));
});
XYChart.Series<String, Number> seriesE = new XYChart.Series<>();
Data<String, Number> dataE = new XYChart.Data<>(belgium,0);
seriesE.getData().add(dataE);
seriesE.setName("Belgium");
secondE = new SimpleIntegerProperty(0);
secondE.bind(secondA.add(0));
secondEText = new Text("");
secondE.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
dataE.setYValue(newValue);
secondEText.setText(String.valueOf(newValue));
});
// add series to chart
bc.getData().add(seriesA);
bc.getData().add(seriesB);
bc.getData().add(seriesC);
bc.getData().add(seriesD);
bc.getData().add(seriesE);
displayLabelForData(dataA, secondAText);
displayLabelForData(dataB, secondBText);
displayLabelForData(dataC, secondCText);
displayLabelForData(dataD, secondDText);
displayLabelForData(dataE, secondEText);
// setup scene
Scene scene = new Scene(bc, 800, 600);
primaryStage.setScene(scene);
// show the stage
primaryStage.show();
// setup a scheduled executor to periodically put data into the chart
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
// input data onto graph per second scheduledExecutorService.scheduleAtFixedRate(() -> {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
double posA = dataA.getNode().localToScene(dataA.getNode().getBoundsInLocal()).getMinX();
double posB = dataB.getNode().localToScene(dataB.getNode().getBoundsInLocal()).getMinX();
double posC = dataC.getNode().localToScene(dataC.getNode().getBoundsInLocal()).getMinX();
double posD = dataD.getNode().localToScene(dataD.getNode().getBoundsInLocal()).getMinX();
double posE = dataE.getNode().localToScene(dataE.getNode().getBoundsInLocal()).getMinX();
TranslateTransition ttA = new TranslateTransition(Duration.millis(2000), dataA.getNode());
TranslateTransition ttB = new TranslateTransition(Duration.millis(2000), dataB.getNode());
TranslateTransition ttC = new TranslateTransition(Duration.millis(2000), dataC.getNode());
TranslateTransition ttD = new TranslateTransition(Duration.millis(2000), dataD.getNode());
TranslateTransition ttE = new TranslateTransition(Duration.millis(2000), dataE.getNode());
//Genarate random numbers
Integer randomB = ThreadLocalRandom.current().nextInt(60);
Integer randomC = ThreadLocalRandom.current().nextInt(60);
Integer randomD = ThreadLocalRandom.current().nextInt(60);
Integer randomE = ThreadLocalRandom.current().nextInt(60);
int intSecondB = secondB.bind(secondA.add(randomB));
int intSecondC = secondC.bind(secondA.add(randomC));
int intSecondD = secondD.bind(secondA.add(randomD));
int intSecondE = secondE.bind(secondA.add(randomE));
I'm not sure if the problem is with the below if statements or the code itself. To avoid the code for being more long, I only included if statements that compare only seriesB(barB) against other bars.
//using if statement to swich each bar based on value
if (intSecondB >= intSecondA && intSecondB >= intSecondB && intSecondB >= intSecondC && intSecondB >= intSecondD && intSecondB >= intSecondE) {
double diffBA = posB - posA;
ttA.setByX(diffBA);
ttB.setByX(-diffBA);
ttA.setCycleCount(1);
ttA.setAutoReverse(true);
ttA.play();
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
}
if (intSecondB < intSecondA && intSecondB >= intSecondC && intSecondB >= intSecondD && intSecondB >= intSecondE) {
System.out.println("keep seriesB(barB) at its position");
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB >= intSecondD && intSecondB >= intSecondE) {
double diffCB = posC - posB;
ttB.setByX(diffCB);
ttC.setByX(-diffCB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttC.setCycleCount(1);
ttC.setAutoReverse(true);
ttC.play();
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB < intSecondD && intSecondB >= intSecondE) {
double diffDB = posD - posB;
ttB.setByX(diffDB);
ttD.setByX(-diffDB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttD.setCycleCount(1);
ttD.setAutoReverse(true);
ttD.play();
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB < intSecondD && intSecondB < intSecondE) {
double diffEB = posE - posB;
ttB.setByX(diffEB);
ttE.setByX(-diffEB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttE.setCycleCount(1);
ttE.setAutoReverse(true);
ttE.play();
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB < intSecondD && intSecondB < intSecondE) {
double diffFB = posF - posB;
ttB.setByX(diffFB);
ttF.setByX(-diffFB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttF.setCycleCount(1);
ttF.setAutoReverse(true);
ttF.play();
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB < intSecondD && intSecondB < intSecondE) {
double diffGB = posG - posB;
ttB.setByX(diffGB);
ttG.setByX(-diffGB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttG.setCycleCount(1);
ttG.setAutoReverse(true);
ttG.play();
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB < intSecondD && intSecondB < intSecondE) {
double diffHB = posH - posB;
ttB.setByX(diffHB);
ttH.setByX(-diffHB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttH.setCycleCount(1);
ttH.setAutoReverse(true);
ttH.play();
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB < intSecondD && intSecondB < intSecondE) {
double diffIB = posI - posB;
ttB.setByX(diffIB);
ttI.setByX(-diffIB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttI.setCycleCount(1);
ttI.setAutoReverse(true);
ttI.play();
}
if (intSecondB < intSecondA && intSecondB < intSecondC && intSecondB < intSecondD && intSecondB < intSecondE) {
double diffJB = posJ - posB;
ttB.setByX(diffJB);
ttJ.setByX(-diffJB);
ttB.setCycleCount(1);
ttB.setAutoReverse(true);
ttB.play();
ttJ.setCycleCount(1);
ttJ.setAutoReverse(true);
ttJ.play();
}
// Update the chart
Platform.runLater(() -> {
secondA.set( cal.get(Calendar.SECOND));
});
}, 0, 3, TimeUnit.SECONDS);
}
#Override
public void stop() throws Exception {
super.stop();
scheduledExecutorService.shutdownNow();
}
private void displayLabelForData(XYChart.Data<String, Number> data, Text text) {
final Node node = data.getNode();
((Group) node.getParent()).getChildren().add(text);
node.boundsInParentProperty().addListener((ChangeListener<Bounds>) (ov, oldBounds, bounds) -> {
text.setLayoutX(
Math.round( bounds.getMinX() + bounds.getWidth() / 2 - text.prefWidth(-1) / 2));
text.setLayoutY(Math.round( bounds.getMinY() - text.prefHeight(-1) * 0.5));
});
}
public static void main(String[] args) {
launch(args);
}
}
Any help will be appreciated!
Here is one strategy:
Create an ObservableList for the data
Create a SortedList from the underlying list
Register a listener with the SortedList, and when the data change, create an animation:
a. For each bar, find its current position and the position of the bar in the index corresponding to its new order
b. Use those positions to animate the translateX property of the bar
c. Animate the yValue property of the XYChart.Data in the same animation
d. At the end of the animation, reset the chart data to the new sorted data
There are a couple of little "gotchas" here: you need to turn off autoRanging on the CategoryAxis (else it will ignore changes to the order of the bars) and reset the categories using the new ordering when you update the data.
Here is an example. I created a class just to hold the data, without any chart API:
public static class CountryValue {
private final String country ;
private final double value ;
public CountryValue(String country, double value) {
super();
this.country = country;
this.value = value;
}
public String getCountry() {
return country;
}
public double getValue() {
return value;
}
}
and a simple data model to hold a list of these:
public static class Model {
private final ObservableList<CountryValue> values ;
public Model(CountryValue... countryValues) {
values = FXCollections.observableArrayList(countryValues) ;
}
public ObservableList<CountryValue> getValues() {
return values ;
}
}
Then the key parts look like:
Model model = new Model() ;
SortedList<CountryValue> sortedData = new SortedList<>(
model.getValues(),
Comparator.comparingDouble(CountryValue::getValue).reversed());
ObservableList<XYChart.Data<String, Number>> chartData = FXCollections.observableArrayList();
CategoryAxis countryAxis = new CategoryAxis();
countryAxis.setAutoRanging(false);
populateChartData(sortedData, chartData, countryAxis);
sortedData.addListener((Change<? extends CountryValue> c) -> {
Timeline timeline = new Timeline() ;
for (int newIndex = 0 ; newIndex < sortedData.size() ; newIndex++) {
CountryValue cv = sortedData.get(newIndex);
int currentIndex = indexByCountry(cv.getCountry(), chartData);
Data<String, Number> data = chartData.get(currentIndex);
double currentX = data.getNode().getBoundsInParent().getCenterX();
double targetX = chartData.get(newIndex).getNode().getBoundsInParent().getCenterX();
DoubleProperty translateXProperty = data.getNode().translateXProperty();
KeyValue kvx1 = new KeyValue(translateXProperty, 0);
KeyValue kvx2 = new KeyValue(translateXProperty, targetX - currentX);
ObjectProperty<Number> yValueProperty = data.YValueProperty();
KeyValue kvy1 = new KeyValue(yValueProperty, data.getYValue());
KeyValue kvy2 = new KeyValue(yValueProperty, cv.getValue());
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.ZERO, kvx1),
new KeyFrame(Duration.ZERO, kvy1),
new KeyFrame(animationDuration, kvx2),
new KeyFrame(animationDuration, kvy2)
);
}
timeline.setOnFinished(e -> populateChartData(sortedData, chartData, countryAxis));
timeline.play();
});
The utility populateChartData() method updates both the category axis and the data:
private void populateChartData(ObservableList<CountryValue> source,
ObservableList<XYChart.Data<String, Number>> chartData,
CategoryAxis countryAxis) {
countryAxis.getCategories().setAll(
source.stream()
.map(CountryValue::getCountry)
.collect(Collectors.toList())
);
chartData.setAll(
source.stream()
.map(cv -> new XYChart.Data<String, Number>(cv.getCountry(), cv.getValue()))
.collect(Collectors.toList())
);
}
Here's a complete example. The animation is a little "jerky"; I think because the y-axis scale changes in an unpredictable way. You could manage this yourself by turning off autoranging on the y-axis, computing the max y value from the new data, and animating the y-axis range as well as the bars. Also note that it's important no updates to the data happen while the animation is running (else you'll end up with multiple animations running at once). Here that's simply managed by timing, but a more robust solution would check for that and either throttle updates or just end the current animation before starting a new one.
import java.util.Comparator;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* JavaFX App
*/
public class FlourishChart extends Application {
private final Duration animationDuration = Duration.millis(250);
#Override
public void start(Stage stage) {
Model model = new Model() ;
Simulator simulator = new Simulator(model);
SortedList<CountryValue> sortedData = new SortedList<>(
model.getValues(),
Comparator.comparingDouble(CountryValue::getValue).reversed());
ObservableList<XYChart.Data<String, Number>> chartData = FXCollections.observableArrayList();
CategoryAxis countryAxis = new CategoryAxis();
countryAxis.setAutoRanging(false);
populateChartData(sortedData, chartData, countryAxis);
BarChart<String, Number> chart = new BarChart<>(countryAxis, new NumberAxis());
// turn off default animation:
chart.setAnimated(false);
Series<String, Number> series = new Series<>(chartData);
chart.getData().add(series);
// when sorted data change, animate bar chart nodes
// at end of animation, update chart data with new data
sortedData.addListener((Change<? extends CountryValue> c) -> {
Timeline timeline = new Timeline() ;
for (int newIndex = 0 ; newIndex < sortedData.size() ; newIndex++) {
CountryValue cv = sortedData.get(newIndex);
int currentIndex = indexByCountry(cv.getCountry(), chartData);
Data<String, Number> data = chartData.get(currentIndex);
double currentX = data.getNode().getBoundsInParent().getCenterX();
double targetX = chartData.get(newIndex).getNode().getBoundsInParent().getCenterX();
DoubleProperty translateXProperty = data.getNode().translateXProperty();
KeyValue kvx1 = new KeyValue(translateXProperty, 0);
KeyValue kvx2 = new KeyValue(translateXProperty, targetX - currentX);
ObjectProperty<Number> yValueProperty = data.YValueProperty();
KeyValue kvy1 = new KeyValue(yValueProperty, data.getYValue());
KeyValue kvy2 = new KeyValue(yValueProperty, cv.getValue());
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.ZERO, kvx1),
new KeyFrame(Duration.ZERO, kvy1),
new KeyFrame(animationDuration, kvx2),
new KeyFrame(animationDuration, kvy2)
);
}
timeline.setOnFinished(e -> populateChartData(sortedData, chartData, countryAxis));
timeline.play();
});
BorderPane root = new BorderPane(chart);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
new Thread(simulator).start();
}
private int indexByCountry(String country, ObservableList<Data<String, Number>> chartData) {
for (int index = 0 ; index < chartData.size(); index++) {
if (chartData.get(index).getXValue().equals(country))
return index ;
}
return -1 ;
}
private void populateChartData(ObservableList<CountryValue> source,
ObservableList<XYChart.Data<String, Number>> chartData,
CategoryAxis countryAxis) {
countryAxis.getCategories().setAll(
source.stream()
.map(CountryValue::getCountry)
.collect(Collectors.toList())
);
chartData.setAll(
source.stream()
.map(cv -> new XYChart.Data<String, Number>(cv.getCountry(), cv.getValue()))
.collect(Collectors.toList())
);
}
public static class Model {
private final ObservableList<CountryValue> values ;
public Model(CountryValue... countryValues) {
values = FXCollections.observableArrayList(countryValues) ;
}
public ObservableList<CountryValue> getValues() {
return values ;
}
}
// replace with record when they are standard in Java:
public static class CountryValue {
private final String country ;
private final double value ;
public CountryValue(String country, double value) {
super();
this.country = country;
this.value = value;
}
public String getCountry() {
return country;
}
public double getValue() {
return value;
}
}
// Not really relevant to problem; just simulates changing data in model
public class Simulator implements Runnable {
private final Model model ;
private final Random rng = new Random();
public Simulator(Model model) {
this.model = model ;
createData();
}
#Override
public void run() {
for (int i = 0 ; i < 10 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Platform.runLater(this::createData);
}
}
private void createData() {
model.getValues().setAll(
Stream.of("Austria", "Brazil", "France", "England", "Belgium")
.map(country -> new CountryValue(country, 50 * rng.nextDouble() + 50))
.collect(Collectors.toList())
);
}
}
public static void main(String[] args) {
launch();
}
}
I try to create a gridPane in JavaFx with a circle in it.I want the gridPane cells to use all the available space in the gridPane.(The GridPane is in the Center of a BorderPane) but the cells keep resizing to the dimensions of the inner objects.How do I get the cells to use all space available? (and how do I set the radius of the circle to a fraction of the space available in the Center of the BorderPane.
I am quite new to JavaFx but I tried to use Columnconstraints and RowConstraints to match my need. It didn't work.I tried also to bind the size of my objects in the GridPane to use a fraction of the stage size but it does not work properly as it does not correspond to the plane in the BorderPane.
public void start(Stage primaryStage) throws Exception{
BorderPane applicationLayout = new BorderPane();
primaryStage.setTitle("Multi-level feedback simulator");
Scene scene = new Scene(applicationLayout, 600, 600);
primaryStage.setScene(scene);
//Add the menu Bar
//MainMenuBar menuBar = new MainMenuBar(primaryStage);
//applicationLayout.setTop(menuBar);
//Add the main zone of drawing
TreeDrawingZone treeDrawingZone = new TreeDrawingZone(primaryStage,applicationLayout,3,3);
applicationLayout.setCenter(treeDrawingZone);
primaryStage.show();
primaryStage.setMaximized(true);
}
The GridPane code with the constraints.
The biggest part of the constructor creates lines dans circles to display a tree.
The drawings functions are createLine() and createCircle()
public class TreeDrawingZone extends Parent {
private GridPane drawingZoneLayout;
private Stage stage;
private int columnNumber;
private int rowNumber;
private Pane rootPane;
private List<Pair<Integer,Integer>> circlePositions;
public TreeDrawingZone(Stage stage,Pane rootPane, int treeHeight, int childrenPerNode){
this.stage = stage;
drawingZoneLayout = new GridPane();
columnNumber = 2*(int)Math.pow(childrenPerNode,treeHeight-1)-1;
rowNumber = 2*treeHeight-1;
circlePositions = new ArrayList<>();
this.rootPane = rootPane;
//TODO Use the correct height of the borderLayout (maybe with a upper level layout)
System.out.println(columnNumber);
System.out.println(rowNumber);
//column Constraints
for(int i = 1 ; i <= columnNumber ; i++){
ColumnConstraints columnConstraints = new ColumnConstraints();
columnConstraints.setPercentWidth((double) 100/columnNumber);
columnConstraints.setFillWidth(true);
drawingZoneLayout.getColumnConstraints().add(columnConstraints);
}
//row Constraints
for(int i = 1 ; i <= rowNumber ; i++){
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setPercentHeight((double) 100/rowNumber);
rowConstraints.setFillHeight(true);
drawingZoneLayout.getRowConstraints().add(rowConstraints);
}
//Tree Representation
//Base Line
List<Integer> circleLineRepartition = new ArrayList<>();
for(int i = 0 ; i < columnNumber; i ++){
if(i % 2 == 0){
circleLineRepartition.add(i);
}
}
System.out.println(circleLineRepartition);
//Creation of the grid line per line
for(int i = rowNumber-1 ; i >=0 ; i-=2){
if(i % 2 == 0) {
//Case of the line with circles
for (Integer circlePosition : circleLineRepartition) {
Pane circlePane;
if (i == 0) {
circlePane = createCircle(true, false);
} else if (i == rowNumber - 1) {
circlePane = createCircle(false, true);
} else {
circlePane = createCircle();
}
drawingZoneLayout.add(circlePane, circlePosition, i);
circlePositions.add(new Pair<>(circlePosition, i));
}
List<Integer> upperCircleLineRepartition;
//Create the lines
//The following block enumerates the different cases to create the lines between the dotes
try {
upperCircleLineRepartition = getoddlyRepartedCenters(childrenPerNode, circleLineRepartition);
if (i > 0) {
int minPosition = circleLineRepartition.get(0);
int maxPosition = circleLineRepartition.get(circleLineRepartition.size() - 1);
int position = 0;
boolean drawHorizontal = true;
int linkedNodeCount = 0;
for (int j = minPosition; j <= maxPosition; j++) {
Pane linesPane;
if (j == circleLineRepartition.get(position) && minPosition != maxPosition) {
//Update the number of linked Nodes
if(drawHorizontal) {
linkedNodeCount += 1;
if(linkedNodeCount == childrenPerNode)
drawHorizontal = false;
}else{
linkedNodeCount = 1;
drawHorizontal = true;
}
//First element
if (linkedNodeCount == 1) {
if(upperCircleLineRepartition.contains(j)){
linesPane = createLines(LineDirection.NORTH,LineDirection.SOUTH,LineDirection.EAST);
}else {
linesPane = createLines(LineDirection.SOUTH, LineDirection.EAST);
}
}
//Last element
else if (linkedNodeCount == childrenPerNode) {
if(upperCircleLineRepartition.contains(j)){
linesPane = createLines(LineDirection.NORTH,LineDirection.SOUTH,LineDirection.WEST);
}else {
linesPane = createLines(LineDirection.WEST, LineDirection.SOUTH);
}
}//bridge with under and upper level
else if(upperCircleLineRepartition.contains(j)) {
linesPane = createLines(LineDirection.SOUTH, LineDirection.NORTH, LineDirection.EAST, LineDirection.WEST);
}
//other children
else{
linesPane = createLines(LineDirection.SOUTH, LineDirection.EAST, LineDirection.WEST);
}
position++;
}
//Only one child
else if (minPosition == maxPosition) {
linesPane = createLines(LineDirection.SOUTH, LineDirection.NORTH);
}
//Bridge between children
else {
if(drawHorizontal) {
if (upperCircleLineRepartition.contains(j)) {
linesPane = createLines(LineDirection.NORTH, LineDirection.EAST, LineDirection.WEST);
} else {
linesPane = createLines(LineDirection.WEST, LineDirection.EAST);
}
}else{
linesPane = createLines();
}
}
drawingZoneLayout.add(linesPane, j, i - 1);
}
}
circleLineRepartition = new ArrayList<>(upperCircleLineRepartition);
} catch (Exception e) {
System.out.println("Invalid line given");
}
}
}
drawingZoneLayout.setMaxSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
//TODO remove GridLines after debug
drawingZoneLayout.setGridLinesVisible(true);
this.getChildren().add(drawingZoneLayout);
}
private Pane createCircle(){
return createCircle(false,false);
}
private Pane createCircle(boolean isRoot, boolean isLeaf){
Pane circlePane = new Pane();
Circle circle = new Circle();
circle.centerXProperty().bind(stage.widthProperty().divide(columnNumber).divide(2));
circle.centerYProperty().bind(stage.heightProperty().divide(rowNumber).divide(2));
circle.radiusProperty().bind(Bindings.min(stage.widthProperty().divide(columnNumber).divide(2),stage.heightProperty().divide(rowNumber).divide(2)));
circlePane.getChildren().add(circle);
if(!isLeaf) {
circlePane.getChildren().add(createLines(LineDirection.SOUTH));
}
if(!isRoot){
circlePane.getChildren().add(createLines(LineDirection.NORTH));
}
return circlePane;
}
private Pane createLines(LineDirection ... directions){
Pane linesGroup = new Pane();
for(LineDirection direction : directions){
linesGroup.getChildren().add(createLine(direction));
}
return linesGroup;
}
private Line createLine(LineDirection direction){
Line line = new Line();
if(direction == LineDirection.EAST || direction == LineDirection.WEST){
line.startYProperty().bind(stage.heightProperty().divide(rowNumber).divide(2));
line.endYProperty().bind(stage.heightProperty().divide(rowNumber).divide(2));
line.startXProperty().bind(stage.widthProperty().divide(columnNumber).divide(2));
if(direction == LineDirection.EAST){
line.endXProperty().bind(stage.widthProperty().divide(columnNumber));
}
else{
line.setEndX(0);
}
}
else{
line.startXProperty().bind(stage.widthProperty().divide(columnNumber).divide(2));
line.endXProperty().bind(stage.widthProperty().divide(columnNumber).divide(2));
line.startYProperty().bind(stage.heightProperty().divide(rowNumber).divide(2));
if(direction == LineDirection.NORTH){
line.setEndY(0);
}else{
line.endYProperty().bind(stage.heightProperty().divide(rowNumber));
}
}
line.setStrokeWidth(1);
line.setFill(null);
line.setStroke(Color.BLACK);
return line;
}
private int getCenter(List<Integer> childrenNodesPosition) throws Exception {
if (childrenNodesPosition.size() == 0){
throw new Exception("Tried to get the center of an empty list");
}else{
int sum = 0;
for(int childNodePosition : childrenNodesPosition){
sum += childNodePosition;
}
return sum/childrenNodesPosition.size();
}
}
private List<Integer> getoddlyRepartedCenters(int nodeNumberPerParent, List<Integer> childrenNodesPosition) throws Exception {
int parentNumber = childrenNodesPosition.size()/nodeNumberPerParent;
int nextPosition = 0;
List<Integer> regularParentCenters = new ArrayList<>(parentNumber);
for(int i = 0 ; i < parentNumber ; i++){
regularParentCenters.add(getCenter(childrenNodesPosition.subList(nextPosition,nextPosition + nodeNumberPerParent)));
nextPosition = nextPosition + nodeNumberPerParent;
}
return regularParentCenters;
}
}
The result that I want to correct
I get this error when I try to drag TreeView node
E r r o r : 8 0 d e 0 0 0 1 i n C a l l b a c k T o J a v a ( j a v a I D s . V i e w . n o t i
f y D r a g D r o p , g r f K e y S t a t e , p t , p d w E f f e c t )
C O M E r r o r : 8 0 d e 0 0 0 1 U n k n o w n e r r o r 0 x 8 0 D E 0 0 0 1
E r r o r : 8 0 d e 0 0 0 1 i n : : D o D r a g D r o p ( p c d , I D r o p S o u r c e P t
r ( n e w G l a s s D r o p S o u r c e ( o b j ) , f a l s e ) , g e t D R O P E F F E C T (
s u p p o r t e d A c t i o n s ) , & p e r f o r m e d D r o p E f f e c t )
C O M E r r o r : 8 0 d e 0 0 0 1 U n k n o w n e r r o r 0 x 8 0 D E 0 0 0 1
Test code:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application
{
private EventHandler<TreeModificationEvent<DynamicTreeNodeModel>> branchExpandedEventHandler;
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
Application.launch(MainApp.class, args);
}
#Override
public void start(Stage primaryStage)
{
primaryStage.setTitle("Example Dynamic Tree");
primaryStage.setResizable(true);
final VBox box = new VBox();
box.setFillWidth(false);
Scene scene = new Scene(box);
primaryStage.setScene(scene);
box.getChildren().add(this.getExampleTree());
primaryStage.show();
}
private TreeView<DynamicTreeNodeModel> getExampleTree()
{
DynamicTreeNodeModel rootNode = new RandomDynamicTreeNodeModel(null, "Root Node");
TreeView<DynamicTreeNodeModel> treeView = new TreeView<>();
treeView.setCellFactory(new Callback<TreeView<DynamicTreeNodeModel>, TreeCell<DynamicTreeNodeModel>>()
{
#Override
public TreeCell call(TreeView<DynamicTreeNodeModel> param)
{
return new DnDCell(param);
}
});
treeView.setPrefSize(1000, 750);
TreeItem rootItem = new TreeItem(rootNode);
branchExpandedEventHandler = new EventHandler<TreeModificationEvent<DynamicTreeNodeModel>>()
{
#Override
public void handle(TreeModificationEvent<DynamicTreeNodeModel> event)
{
// System.out.println("handling event " + event);
TreeItem<DynamicTreeNodeModel> item = event.getTreeItem();
populateTreeItem(item);
}
};
rootItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
treeView.setShowRoot(true);
treeView.setRoot(rootItem);
populateTreeItem(rootItem);
rootItem.setExpanded(true);
// treeView.setCellFactory(new LearningTreeCellFactory());
return treeView;
}
private void populateTreeItem(TreeItem<DynamicTreeNodeModel> item)
{
DynamicTreeNodeModel node = item.getValue();
boolean isPopulated = node.isPopulated();
boolean areGrandChildrenPopulated = node.areChildenPopulated();
node.populateToDepth(2);
if (!isPopulated)
{
for (DynamicTreeNodeModel childNode : node.getChildren())
{
TreeItem childItem = new TreeItem(childNode);
childItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
item.getChildren().add(childItem);
}
}
if (!areGrandChildrenPopulated)
{
int i = 0;
int size = node.getChildren().size();
for (TreeItem childItem : item.getChildren())
{
// get cooresponding node in the model
if (i < size)
{
DynamicTreeNodeModel childNode = node.getChildren().get(i);
i++;
for (DynamicTreeNodeModel grandChildNode : childNode.getChildren())
{
TreeItem grandChildItem = new TreeItem(grandChildNode);
grandChildItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
childItem.getChildren().add(grandChildItem);
}
}
}
}
}
private static interface DynamicTreeNodeModel
{
public String getName();
public void setName(String name);
public boolean isPopulated();
public boolean areChildenPopulated();
public List<DynamicTreeNodeModel> getChildren();
public void setChildren(List<DynamicTreeNodeModel> children);
public DynamicTreeNodeModel getParent();
public void setParent(DynamicTreeNodeModel parent);
public void populateToDepth(int depth);
#Override
public String toString();
}
private static class RandomDynamicTreeNodeModel implements DynamicTreeNodeModel
{
private DynamicTreeNodeModel parent;
private String name;
private List<DynamicTreeNodeModel> children = null;
public RandomDynamicTreeNodeModel(DynamicTreeNodeModel parent, String name)
{
this.parent = parent;
this.name = name;
}
#Override
public String getName()
{
return name;
}
#Override
public void setName(String name)
{
this.name = name;
}
#Override
public boolean isPopulated()
{
if (children == null)
{
return false;
}
return true;
}
#Override
public boolean areChildenPopulated()
{
if (!this.isPopulated())
{
return false;
}
for (DynamicTreeNodeModel child : this.children)
{
if (!child.isPopulated())
{
return false;
}
}
return true;
}
#Override
public List<DynamicTreeNodeModel> getChildren()
{
return children;
}
#Override
public void setChildren(List<DynamicTreeNodeModel> children)
{
this.children = children;
}
#Override
public DynamicTreeNodeModel getParent()
{
return parent;
}
#Override
public void setParent(DynamicTreeNodeModel parent)
{
this.parent = parent;
}
private static Random random = new Random();
#Override
public void populateToDepth(int depth)
{
if (depth <= 0)
{
return;
}
if (children == null)
{
int num = random.nextInt(5);
System.out.println("Random number " + num);
children = new ArrayList(num);
for (int i = 0; i < num; i++)
{
// children.add(new RandomDynamicTreeNodeModel(this, "child " + i));
children.add(new RandomDynamicTreeNodeModel(this, "child " + System.currentTimeMillis()));
try
{
Thread.sleep(100);
}
catch (InterruptedException ex)
{
Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
int childDepth = depth - 1;
for (DynamicTreeNodeModel child : children)
{
child.populateToDepth(childDepth);
}
}
#Override
public String toString()
{
return this.name;
}
}
public class DnDCell extends TreeCell<DynamicTreeNodeModel>
{
private TreeView<DynamicTreeNodeModel> parentTree;
public DnDCell(final TreeView<DynamicTreeNodeModel> parentTree)
{
this.parentTree = parentTree;
// ON SOURCE NODE.
setOnDragDetected(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
System.out.println("Drag detected on " + item);
if (item == null)
{
return;
}
Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, item.toString());
dragBoard.setContent(content);
event.consume();
}
});
setOnDragDone(new EventHandler<DragEvent>()
{
#Override
public void handle(DragEvent dragEvent)
{
System.out.println("Drag done on " + item);
dragEvent.consume();
}
});
// ON TARGET NODE.
// setOnDragEntered(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag entered on " + item);
// dragEvent.consume();
// }
// });
setOnDragOver(new EventHandler<DragEvent>()
{
#Override
public void handle(DragEvent dragEvent)
{
System.out.println("Drag over on " + item);
if (dragEvent.getDragboard().hasString())
{
String valueToMove = dragEvent.getDragboard().getString();
if (!valueToMove.matches(item.getName()))
{
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
}
dragEvent.consume();
}
});
// setOnDragExited(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag exited on " + item);
// dragEvent.consume();
// }
// });
setOnDragDropped(new EventHandler<DragEvent>()
{
#Override
public void handle(DragEvent dragEvent)
{
System.out.println("Drag dropped on " + item);
String valueToMove = dragEvent.getDragboard().getString();
TreeItem<DynamicTreeNodeModel> itemToMove = search(parentTree.getRoot(), valueToMove);
TreeItem<DynamicTreeNodeModel> newParent = search(parentTree.getRoot(), item.getName());
// Remove from former parent.
itemToMove.getParent().getChildren().remove(itemToMove);
// Add to new parent.
newParent.getChildren().add(itemToMove);
newParent.setExpanded(true);
dragEvent.consume();
}
});
}
private TreeItem<DynamicTreeNodeModel> search(final TreeItem<DynamicTreeNodeModel> currentNode, final String valueToSearch)
{
TreeItem<DynamicTreeNodeModel> result = null;
if (currentNode.getValue().getName().matches(valueToSearch))
{
result = currentNode;
}
else if (!currentNode.isLeaf())
{
for (TreeItem<DynamicTreeNodeModel> child : currentNode.getChildren())
{
result = search(child, valueToSearch);
if (result != null)
{
break;
}
}
}
return result;
}
private DynamicTreeNodeModel item;
#Override
protected void updateItem(DynamicTreeNodeModel item, boolean empty)
{
super.updateItem(item, empty);
this.item = item;
String text = (item == null) ? null : item.toString();
setText(text);
}
}
}
Can you help me to fix the issue?
Bug report: RT-38641 Unknown error 0x80DE0001.
Developer (Anthony Petrov) notes:
I can reproduce the bug on the latest 8u40 builds.
Suggested developer workaround:
Use TransferMode.COPY instead of TransferMode.MOVE when initiating a
DnD operation."
This is my code :
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
import org.nocrala.tools.gis.data.esri.shapefile.ShapeFileReader;
import org.nocrala.tools.gis.data.esri.shapefile.ValidationPreferences;
import org.nocrala.tools.gis.data.esri.shapefile.exception.InvalidShapeFileException;
import org.nocrala.tools.gis.data.esri.shapefile.header.ShapeFileHeader;
import org.nocrala.tools.gis.data.esri.shapefile.shape.AbstractShape;
import org.nocrala.tools.gis.data.esri.shapefile.shape.PointData;
import org.nocrala.tools.gis.data.esri.shapefile.shape.shapes.PolygonShape;
public class MakeMap extends Application
{
static FileInputStream is;
static ShapeFileReader read;
static ShapeFileHeader head;
static AbstractShape shape;
static ValidationPreferences prefs;
static double minLat;
static double minLon;
static double maxLat;
static double maxLon;
static double ratio;
static double bMinLat;
static double bMinLon;
static double bMaxLat;
static double bMaxLon;
int gapx = 0, gapy = 0;
public void makeRatio(double lon_max, double lon_min, double lat_max, double lat_min)
{
double x, y; x = lon_max - lon_min;
y = lat_max - lat_min;
x = 600 / x;
y = 600 / y; if (x < y)
{
ratio = x;
gapy = (int) ((600 - (lat_max - lat_min) * x) / 2);
} else {
ratio = y; gapx = (int) ((600 - (lon_max - lon_min) * y) / 2); } }
public void assignBoundary(double min_lon, double max_lon, double min_lat, double max_lat)
{
minLon = coActLon(min_lon) * ratio; maxLon = coActLon(max_lon) * ratio; minLat = coActLat(min_lat) * ratio;
maxLat = coActLat(max_lat) * ratio;
}
public double coActLat(double p)
{
p = bMaxLat - p; return p;
}
public double coActLon(double p)
{
p = p - bMinLon; return p;
}
public int assignBoundaryLon(double p)
{
p = coActLon(p) * ratio - minLon; p += gapx;
return (int) p;
}
public int assignBoundaryLat(double p)
{
p = coActLat(p) * ratio;
p = p - maxLat; p += gapy;
return (int) p;
}
#Override public void start(Stage stage) throws Exception
{ File file = new File("/home/ranu/world.shp");
Canvas canvas = new Canvas(600, 600);
GraphicsContext gc = canvas.getGraphicsContext2D();
try
{
is = new FileInputStream(file);
}
catch (FileNotFoundException e)
{ // TODO Auto-generated catch block
e.printStackTrace();
} prefs = new ValidationPreferences();
prefs.setMaxNumberOfPointsPerShape(33200);
try
{
read = new ShapeFileReader(is, prefs);
}
catch (InvalidShapeFileException | IOException e)
{ // TODO Auto-generated catch block e.printStackTrace(); }
head = read.getHeader();
bMaxLon = head.getBoxMaxX();
bMinLon = head.getBoxMinX();
bMaxLat = head.getBoxMaxY();
bMinLat = head.getBoxMinY();
makeRatio(head.getBoxMaxX(), head.getBoxMinX(), head.getBoxMaxY(), head.getBoxMinY());
assignBoundary(head.getBoxMinX(), head.getBoxMaxX(), head.getBoxMinY(), head.getBoxMaxY());
gc.setFill(Color.rgb(53, 153, 255));
gc.fillRect(gapx, gapy, (maxLon - minLon), (minLat - maxLat));
drawShape(gc);
StackPane root = new StackPane();
root.getChildren().add(canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public void drawShape(GraphicsContext gc) throws IOException, InvalidShapeFileException
{
int i = 0; gc.setFill(Color.WHITE);
gc.setStroke(Color.LIGHTGREY); gc.setLineWidth(1);
gc.strokeLine(10, 20, 30, 40);
Polygon polygon = new Polygon();
polygon.getPoints().add(new Double(20.0));
int count = 0;
double[] lat;
double[] lon;
Double[] data; gc.setStroke(Color.LIGHTGRAY);
while ((shape = read.next()) != null && count < 360)
{ // System.out.println(shape);
switch (shape.getShapeType())
{
case POINT: // PointShape aPoint = (PointShape) shape;
// Do something with the point shape... break;
case MULTIPOINT_Z: // MultiPointZShape aMultiPointZ = (MultiPointZShape) shape;
// Do something with the MultiPointZ shape... break;
case POLYGON: PolygonShape aPolygon = (PolygonShape) shape;
lat = new double[aPolygon.getNumberOfPoints()];
lon = new double[aPolygon.getNumberOfPoints()];
data = new Double[aPolygon.getNumberOfPoints() * 2];
int j = 0;
double x, y;
PointData[] point = aPolygon.getPoints();
for (i = 0; i < aPolygon.getNumberOfPoints(); i++)
{
x = point[i].getX();
y = point[i].getY();
j = 0; lat[j] = this.assignBoundaryLat(point[i].getY());
lon[j] = this.assignBoundaryLon(point[i].getX());
// data[j++] = new // Double(this.assignBoundaryLon(point[i].getX()));
// data[j] = new // Double(this.assignBoundaryLon(point[i].getY()));
for (j = j + 1, i = i + 1; (x != point[i].getX() || y != point[i] .getY()) && i < aPolygon.getNumberOfPoints(); i++, j++)
{
// lat[j] = this.assignBoundaryLat(point[i].getY());
// lon[j] = this.assignBoundaryLon(point[i].getX());
// data[j++] = new // Double(this.assignBoundaryLat(point[i].getX()));
// data[j] = new // Double(this.assignBoundaryLat(point[i].getY())); }
System.out.println(lat.length + " " + lon.length + " " + j);
// Polygon pg = new Polygon();
// pg.getPoints().addAll(data);
gc.strokePolygon(lon, lat, j);
System.out.println(count);
count ++; }
break; default: break; } } }
public static void main(String[] args)
{ Application.launch(args); }
Problem have in this code :
130 130 129
0
514 514 513
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:403)
at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 512
at javafx.scene.canvas.GraphicsContext.writePoly(GraphicsContext.java:326)
at javafx.scene.canvas.GraphicsContext.strokePolygon(GraphicsContext.java:1591)
at Experiment.MakeMap.drawShape(MakeMap.java:199)
at Experiment.MakeMap.start(MakeMap.java:129)
at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:319)
at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:216)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:76)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:89)
... 1 more
https://drive.google.com/file/d/0B9nTZs5-E8czSEl4aXpwYjZhdUE/edit?usp=sharing world.shp
https://drive.google.com/file/d/0B9nTZs5-E8czT2RITnY5ZjFmTnM/edit?usp=sharing shapefilereader.jar
I am trying to create a filled series over each region of a polar plot. But the colors are not filled out right. Here is an image of what I get: http://i1122.photobucket.com/albums/l539/jpo2/polar-1.gif
Here is the code I have to loop through a given section is as follows:
if (i < 8) {
for(int r = 0; r< 20; r+=(NumberAxis) plot.getAxis()).getTickUnit().getSize()){
for(int theta = 0; theta <= 180; theta+=30){
XYSeries series = new XYSeries(i + "π/8 " + "< θ < 2π+" + i + "π/8");
for (int e = theta; e < theta+30; e++) {
series.add(90-e-i*45, r);
series.add(90-e-i*45, r- ((NumberAxis) plot.getAxis()).getTickUnit().getSize());
}
result.addSeries(series);
setFilled(result);
i++;
}
}
}
private void setFilled(XYDataset dataset) {
for (int i = 0; i < dataset.getSeriesCount(); i++) {
renderers.setSeriesFilled(i, true);
}
}
This is a modification of #trashgod's draw method at JFreechart Loop through polar chart sectors
Please help.
Full code:
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTick;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.event.ChartProgressEvent;
import org.jfree.chart.event.ChartProgressListener;
import org.jfree.chart.plot.PolarPlot;
import org.jfree.chart.renderer.DefaultPolarItemRenderer;
import org.jfree.chart.renderer.PolarItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.TextAnchor;
public class tests extends JFrame implements ChartProgressListener {
private static final String title = "Archimedes' Spirals";
private XYSeriesCollection result = new XYSeriesCollection();
private DefaultPolarItemRenderer renderers = new DefaultPolarItemRenderer();
private int i;
public tests(String title) {
super(title);
JFreeChart chart = createChart(result);
ChartPanel panel = new ChartPanel(chart);
panel.setPreferredSize(new Dimension(500, 500));
panel.setMouseZoomable(false);
this.add(panel);
}
private JFreeChart createChart(XYDataset dataset) {
ValueAxis radiusAxis = new NumberAxis();
radiusAxis.setTickLabelsVisible(false);
PolarItemRenderer renderer = new DefaultPolarItemRenderer();
PolarPlot plot = new PolarPlot(dataset, radiusAxis, renderer) {
#Override
protected List refreshAngleTicks() {
List<NumberTick> ticks = new ArrayList<NumberTick>();
int delta = (int) this.getAngleTickUnit().getSize();
for (int t = 0; t < 360; t += delta) {
int tp = (360 + 90 - t) % 360;
NumberTick tick = new NumberTick(
Double.valueOf(t), String.valueOf(tp),
TextAnchor.CENTER, TextAnchor.CENTER, 0.0);
ticks.add(tick);
}
return ticks;
}
};
plot.setBackgroundPaint(new Color(0x00f0f0f0));
plot.setRadiusGridlinePaint(Color.gray);
plot.addCornerTextItem("r(θ) = θ; 0 < θ < 2π; +iπ/8");
setFilled(dataset);
plot.setRenderer(renderers);
JFreeChart chart = new JFreeChart(
title, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
chart.setBackgroundPaint(Color.white);
chart.addProgressListener(this);
return chart;
}
public static void main(String[] args) {
tests demo = new tests(title);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
demo.pack();
demo.setLocationRelativeTo(null);
demo.setVisible(true);
}
#Override
public void chartProgress(ChartProgressEvent e) {
if (e.getType() == ChartProgressEvent.DRAWING_FINISHED) {
System.out.println(e);
JFreeChart chart = e.getChart();
draw();
}
}
public void draw() {
if (i < 4) {
for (int g = 0; g < 30; g += 5) {
for (int h = 0; h < 180; h += 45) {
XYSeries series = new XYSeries(i + "π/8 " + "< θ < 2π+" + i + "π/8");
for (int t = h; t <= h + 45; t++) {
series.add(90 - t, g);
series.add(90 - t, g + 5);
}
result.addSeries(series);
setFilled(result);
i++;
}
}
}
}
private void setFilled(XYDataset dataset) {
for (int i = 0; i < dataset.getSeriesCount(); i++) {
renderers.setSeriesFilled(i, true);
}
}
}
Here's a simplified version the uses PolarPlot directly, without any transformation. It might be easier to experiment with.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PolarPlot;
import org.jfree.chart.renderer.DefaultPolarItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/** #see http://stackoverflow.com/questions/6669734 */
public class PolarArcs {
private static final String title = "PolarArcs";
private static final double PI2 = 90d; // π/2 radians = 90°
private void display() {
JFrame f = new JFrame(title);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ChartPanel panel = new ChartPanel(createChart(createDataset()));
panel.setPreferredSize(new Dimension(400, 400));
f.add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private JFreeChart createChart(XYDataset dataset) {
JFreeChart chart = ChartFactory.createPolarChart(
title, dataset, true, false, false);
PolarPlot plot = (PolarPlot) chart.getPlot();
plot.setBackgroundPaint(Color.white);
plot.setAngleGridlinesVisible(false);
plot.setRadiusGridlinesVisible(false);
DefaultPolarItemRenderer r = (DefaultPolarItemRenderer) plot.getRenderer();
for (int i = 0; i < dataset.getSeriesCount(); i++ ) {
r.setSeriesFilled(i, true);
}
NumberAxis rangeAxis = (NumberAxis) plot.getAxis();
rangeAxis.setTickLabelsVisible(false);
return chart;
}
private XYDataset createDataset() {
XYSeriesCollection result = new XYSeriesCollection();
for (int r = 8; r > 0; r--) {
XYSeries series = new XYSeries(title + String.valueOf(r));
for (int t = (int) -PI2; t <= PI2; t++) {
series.add(t, r);
}
result.addSeries(series);
}
return result;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new PolarArcs().display();
}
});
}
}