diff --git a/calibration-gui/pom.xml b/calibration-gui/pom.xml
index 024914f2..d52d506f 100644
--- a/calibration-gui/pom.xml
+++ b/calibration-gui/pom.xml
@@ -5,7 +5,7 @@
gov.llnl.gnem.apps.coda.calibration
coda-calibration
- 1.0.15
+ 1.0.16
calibration-gui
diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/AbstractMeasurementController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/AbstractMeasurementController.java
index 7136e1d2..ddf3ad63 100644
--- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/AbstractMeasurementController.java
+++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/AbstractMeasurementController.java
@@ -65,6 +65,7 @@
import gov.llnl.gnem.apps.coda.common.gui.plotting.PlotPoint;
import gov.llnl.gnem.apps.coda.common.gui.plotting.SymbolStyleMapFactory;
import gov.llnl.gnem.apps.coda.common.gui.util.CellBindingUtils;
+import gov.llnl.gnem.apps.coda.common.gui.util.HiddenHeaderTableView;
import gov.llnl.gnem.apps.coda.common.gui.util.MaybeNumericStringComparator;
import gov.llnl.gnem.apps.coda.common.gui.util.NumberFormatFactory;
import gov.llnl.gnem.apps.coda.common.gui.util.SnapshotUtils;
@@ -90,6 +91,7 @@
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
+import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
@@ -218,11 +220,14 @@ public abstract class AbstractMeasurementController implements MapListeningContr
@FXML
protected TableColumn bandCoverageCol;
+ @FXML
+ protected TableColumn likelyPoorlyConstrainedCol;
+
@FXML
protected TableColumn stationCol;
@FXML
- protected TableView> summaryTable;
+ protected HiddenHeaderTableView> summaryTable;
@FXML
protected TableColumn, String> summaryNameCol;
@@ -317,6 +322,7 @@ protected AbstractMeasurementController(final SpectraClient spectraClient, final
public void initialize() {
evidCombo.setItems(evids);
+ evidCombo.setVisibleRowCount(5);
configureAxisShrink(xAxisShrink, () -> {
shouldXAxisShrink = !shouldXAxisShrink;
@@ -335,6 +341,7 @@ public void initialize() {
final AxisLimits mwXaxis = new AxisLimits(Axis.Type.X, 0.0, 10.0);
final AxisLimits mwYaxis = new AxisLimits(Axis.Type.Y, 0.0, 10.0);
mwPlot.setAxisLimits(mwXaxis, mwYaxis);
+ mwPlot.setMargin(30, 40, 50, 50);
mwPlot.attachToDisplayNode(mwPlotPane);
stressPlot = plotFactory.basicPlot();
@@ -344,14 +351,16 @@ public void initialize() {
final AxisLimits stressXaxis = new AxisLimits(Axis.Type.X, 1.0, 1.0);
final AxisLimits stressYaxis = new AxisLimits(Axis.Type.Y, 1.0, 1.0);
stressPlot.setAxisLimits(stressXaxis, stressYaxis);
+ stressPlot.setMargin(30, 40, 70, 50);
stressPlot.attachToDisplayNode(stressPlotPane);
sdPlot = plotFactory.basicPlot();
- sdPlot.getTitle().setText("Site correction overview");
+ sdPlot.getTitle().setText("Site correction overview (# stations)");
sdPlot.getTitle().setFontSize(16);
sdPlot.addAxes(plotFactory.axis(Axis.Type.LOG_X, "Frequency (Hz)"), plotFactory.axis(Axis.Type.Y, "Standard Deviation"));
sdPlot.setAxisLimits(new AxisLimits(Axis.Type.LOG_X, 0.0, 1.0), new AxisLimits(Axis.Type.Y, 0.0, 2.0));
sdPlot.showLegend(false);
+ sdPlot.setMargin(30, 40, 50, null);
sdPlot.attachToDisplayNode(sdPlotPane);
energyVsMomentPlot = plotFactory.basicPlot();
@@ -362,6 +371,7 @@ public void initialize() {
rightAxis.setTickFormat(TickFormat.LOG10_DYNE_CM_TO_MW);
energyVsMomentPlot.addAxes(rightAxis);
energyVsMomentPlot.showLegend(false);
+ energyVsMomentPlot.setMargin(30, 40, 50, 50);
energyVsMomentPlot.attachToDisplayNode(energyVsMomentPane);
apparentStressVsMomentPlot = plotFactory.basicPlot();
@@ -372,6 +382,7 @@ public void initialize() {
rightAxis.setTickFormat(TickFormat.LOG10_DYNE_CM_TO_MW);
apparentStressVsMomentPlot.addAxes(rightAxis);
apparentStressVsMomentPlot.showLegend(false);
+ apparentStressVsMomentPlot.setMargin(30, 40, 50, 50);
apparentStressVsMomentPlot.attachToDisplayNode(apparentStressVsMomentPane);
cornerFreqVsMomentPlot = plotFactory.basicPlot();
@@ -382,6 +393,7 @@ public void initialize() {
rightAxis.setTickFormat(TickFormat.LOG10_DYNE_CM_TO_MW);
cornerFreqVsMomentPlot.addAxes(rightAxis);
cornerFreqVsMomentPlot.showLegend(false);
+ cornerFreqVsMomentPlot.setMargin(30, 40, 50, 50);
cornerFreqVsMomentPlot.attachToDisplayNode(cornerFreqVsMomentPane);
evidCombo.valueProperty().addListener((observable, oldValue, newValue) -> {
@@ -417,6 +429,7 @@ public void initialize() {
CellBindingUtils.attachTextCellFactories(measuredMwUq2LowCol, MeasuredMwDetails::getMw2Min, dfmt4);
CellBindingUtils.attachTextCellFactories(measuredMwUq2HighCol, MeasuredMwDetails::getMw2Max, dfmt4);
CellBindingUtils.attachTextCellFactories(bandCoverageCol, MeasuredMwDetails::getBandCoverage, dfmt4);
+ CellBindingUtils.attachTextCellFactoriesString(likelyPoorlyConstrainedCol, mw -> mw.isLikelyPoorlyConstrained().toString());
stationCountCol.setCellValueFactory(
x -> Bindings.createIntegerBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(MeasuredMwDetails::getStationCount).orElseGet(() -> 0)).asObject());
@@ -436,6 +449,16 @@ public void initialize() {
menu.getItems().add(exclude);
summaryTable.setItems(summaryValues);
+ summaryTable.setRowFactory(table -> new TableRow>() {
+ @Override
+ protected void updateItem(Pair entry, boolean b) {
+ super.updateItem(entry, b);
+ if (entry != null && entry.getLeft() != null && entry.getLeft().contains("(Model Fit)")) {
+ setStyle("-fx-font-weight:bold;");
+ }
+ }
+ });
+
CellBindingUtils.attachTextCellFactoriesString(summaryNameCol, Pair::getLeft);
CellBindingUtils.attachTextCellFactoriesString(summaryValueCol, Pair::getRight);
summaryNameCol.setCellFactory(param -> new TextWrappingTableCell());
@@ -469,6 +492,9 @@ private void plotSpectra() {
});
final List fittingSpectra;
+ boolean likelyPoorlyConstrained = false;
+ MeasuredMwDetails mwDetails = null;
+
if (evidCombo != null && evidCombo.getSelectionModel().getSelectedIndex() > 0) {
filteredMeasurements = filterToEvent(evidCombo.getSelectionModel().getSelectedItem(), spectralMeasurements);
fittingSpectra = getFitSpectra();
@@ -476,45 +502,59 @@ private void plotSpectra() {
fittingSpectra.add(referenceSpectra);
final Spectra validationSpectra = spectraClient.getValidationSpectra(evidCombo.getSelectionModel().getSelectedItem()).block(Duration.ofSeconds(2));
fittingSpectra.add(validationSpectra);
+
if (filteredMeasurements != null && !filteredMeasurements.isEmpty() && filteredMeasurements.get(0).getWaveform() != null) {
final Event event = filteredMeasurements.get(0).getWaveform().getEvent();
- summaryValues.add(new Pair<>("Date", DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneId.of("UTC")).format(event.getOriginTime().toInstant())));
- summaryValues.add(new Pair<>("JDay", Integer.toString(new TimeT(event.getOriginTime().toInstant().getEpochSecond()).getJDay())));
+ String date = DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneId.of("UTC")).format(event.getOriginTime().toInstant());
+ String jDay = Integer.toString(new TimeT(event.getOriginTime()).getJdate()).substring(4);
+ summaryValues.add(new Pair<>("Date", String.format("%s (%s)", date, jDay)));
summaryValues.add(new Pair<>("Origin Time", DateTimeFormatter.ISO_TIME.withZone(ZoneId.of("UTC")).format(event.getOriginTime().toInstant())));
-
summaryValues.add(new Pair<>("Latitude", dfmt4.format(event.getLatitude())));
summaryValues.add(new Pair<>("Longitude", dfmt4.format(event.getLongitude())));
- summaryValues.add(new Pair<>("Depth", dfmt2.format(event.getDepth())));
+ summaryValues.add(new Pair<>("Depth (km)", dfmt2.format(event.getDepth())));
+ summaryValues.add(null); // Adds space between sections
+ if (mwParameters != null && !mwParameters.isEmpty()) {
+ mwDetails = mwParameters.stream().filter(mwDetail -> event.getEventId().equalsIgnoreCase(mwDetail.getEventId())).findAny().orElseGet(MeasuredMwDetails::new);
+ likelyPoorlyConstrained = mwDetails.isLikelyPoorlyConstrained();
+ }
} else {
filteredMeasurements = Collections.emptyList();
}
final Spectra fitSpectra = fittingSpectra.get(0);
- summaryValues.add(new Pair<>("Observed Total Energy", dfmt2.format(fitSpectra.getLogTotalEnergy()) + " J"));
- summaryValues.add(new Pair<>("Observed Apparent Stress", dfmt2.format(fitSpectra.getObsAppStress()) + " MPa"));
- summaryValues.add(new Pair<>("Observed to Extrapolated energy", dfmt2.format(100.0 * (Math.pow(10, fitSpectra.getObsEnergy()) / Math.pow(10, fitSpectra.getLogTotalEnergy())))));
+ summaryValues.add(new Pair<>("Mw (Model Fit)", dfmt2.format(fitSpectra.getMw())));
- summaryValues.add(new Pair<>("Model Fit Mw", dfmt2.format(fitSpectra.getMw())));
- if (fitSpectra.getApparentStress() != 0.0) {
- summaryValues.add(new Pair<>("Model Fit Apparent Stress", dfmt2.format(fitSpectra.getApparentStress()) + " MPa"));
+ if (fitSpectra.getApparentStress() > 0.0) {
+ summaryValues.add(new Pair<>("Apparent Stress (Model Fit)", dfmt2.format(fitSpectra.getApparentStress()) + " MPa"));
}
- summaryValues.add(new Pair<>("Model Fit Energy", dfmt2.format(fitSpectra.getlogTotalEnergyMDAC()) + " J"));
+ summaryValues.add(new Pair<>("Energy (Model Fit)", dfmt2.format(fitSpectra.getlogTotalEnergyMDAC()) + " J"));
+
+ summaryValues.add(new Pair<>("Observed Total Energy", dfmt2.format(fitSpectra.getLogTotalEnergy()) + " J"));
+ summaryValues.add(new Pair<>("Observed Apparent Stress", dfmt2.format(fitSpectra.getObsAppStress()) + " MPa"));
+ summaryValues.add(new Pair<>("Observed / Total Energy", dfmt2.format(100.0 * (Math.pow(10, fitSpectra.getObsEnergy()) / Math.pow(10, fitSpectra.getLogTotalEnergy()))) + " %"));
+ summaryValues.add(
+ new Pair<>("Extrapolated / Total Energy", dfmt2.format(100.0 - (100.0 * (Math.pow(10, fitSpectra.getObsEnergy()) / Math.pow(10, fitSpectra.getLogTotalEnergy())))) + " %"));
if (referenceSpectra != null && SPECTRA_TYPES.REF.equals(referenceSpectra.getType())) {
summaryValues.add(new Pair<>("Reference Mw", dfmt2.format(referenceSpectra.getMw())));
- if (referenceSpectra.getApparentStress() != 0.0) {
+ if (referenceSpectra.getApparentStress() > 0.0) {
summaryValues.add(new Pair<>("Reference Apparent Stress", dfmt2.format(referenceSpectra.getApparentStress()) + " MPa"));
}
}
- if (validationSpectra != null && SPECTRA_TYPES.REF.equals(validationSpectra.getType())) {
+ if (validationSpectra != null && SPECTRA_TYPES.VAL.equals(validationSpectra.getType())) {
summaryValues.add(new Pair<>("Validation Mw", dfmt2.format(validationSpectra.getMw())));
- if (validationSpectra.getApparentStress() != 0.0) {
+ if (validationSpectra.getApparentStress() > 0.0) {
summaryValues.add(new Pair<>("Validation Apparent Stress", dfmt2.format(validationSpectra.getApparentStress()) + " MPa"));
}
}
+ if (mwDetails != null) {
+ summaryValues.add(null);
+ summaryValues.add(new Pair<>("Iterations", Integer.toString(mwDetails.getIterations())));
+ summaryValues.add(new Pair<>("Data Count", Integer.toString(mwDetails.getDataCount())));
+ }
} else {
filteredMeasurements = spectralMeasurements;
fittingSpectra = null;
@@ -522,6 +562,8 @@ private void plotSpectra() {
final List selectedEventMeasurements = filteredMeasurements;
+ final boolean showPoorlyConstrainedBanner = likelyPoorlyConstrained;
+
spectraControllers.forEach(spc -> {
if (fittingSpectra != null && spc.shouldShowFits()) {
spc.getSpectralPlot().plotXYdata(toPlotPoints(selectedEventMeasurements, spc.getDataFunc()), fittingSpectra);
@@ -529,6 +571,8 @@ private void plotSpectra() {
spc.getSpectralPlot().plotXYdata(toPlotPoints(selectedEventMeasurements, spc.getDataFunc()), null);
}
spc.getSpectraMeasurementMap().putAll(mapSpectraToPoint(selectedEventMeasurements, spc.getDataFunc()));
+
+ spc.showConstraintWarningBanner(showPoorlyConstrainedBanner);
});
mapMeasurements(selectedEventMeasurements);
@@ -669,8 +713,8 @@ protected void reloadData() {
double maxMw = 0.0;
double minEnergy = 10.0;
double maxEnergy = 0.0;
- double minStress = 1.0;
- double maxStress = 0.0;
+ double minStress = 0.01;
+ double maxStress = 100.0;
for (final MeasuredMwDetails ev : evs) {
mwParameters.add(ev);
@@ -712,7 +756,7 @@ protected void reloadData() {
maxMw = ref;
}
- final Symbol mwSym = plotFactory.createSymbol(SymbolStyles.CIRCLE, "Reference", mw, ref, Color.RED, Color.RED, Color.RED, ev.getEventId(), false);
+ final Symbol mwSym = plotFactory.createSymbol(SymbolStyles.CIRCLE, "Ref.", mw, ref, Color.RED, Color.RED, Color.RED, ev.getEventId(), false);
mwPlotSymbols.add(mwSym);
}
@@ -725,7 +769,7 @@ protected void reloadData() {
maxMw = valMw;
}
- final Symbol valSym = plotFactory.createSymbol(SymbolStyles.SQUARE, "Validation", mw, valMw, Color.BLACK, Color.BLACK, Color.BLACK, ev.getEventId(), false);
+ final Symbol valSym = plotFactory.createSymbol(SymbolStyles.SQUARE, "Val.", mw, valMw, Color.BLACK, Color.BLACK, Color.BLACK, ev.getEventId(), false);
valSym.setZindex(VALIDATION_Z_ORDER);
mwPlotSymbols.add(valSym);
}
@@ -753,7 +797,7 @@ protected void reloadData() {
maxStress = refStress;
}
- final Symbol stressSym = plotFactory.createSymbol(SymbolStyles.CIRCLE, "Reference", stress, refStress, Color.RED, Color.RED, Color.RED, ev.getEventId(), false);
+ final Symbol stressSym = plotFactory.createSymbol(SymbolStyles.CIRCLE, "Ref.", stress, refStress, Color.RED, Color.RED, Color.RED, ev.getEventId(), false);
stressPlotSymbols.add(stressSym);
}
@@ -766,7 +810,7 @@ protected void reloadData() {
maxStress = valStress;
}
- final Symbol valSym = plotFactory.createSymbol(SymbolStyles.SQUARE, "Validation", stress, valStress, Color.BLACK, Color.BLACK, Color.BLACK, ev.getEventId(), false);
+ final Symbol valSym = plotFactory.createSymbol(SymbolStyles.SQUARE, "Val.", stress, valStress, Color.BLACK, Color.BLACK, Color.BLACK, ev.getEventId(), false);
valSym.setZindex(VALIDATION_Z_ORDER);
stressPlotSymbols.add(valSym);
}
@@ -1067,38 +1111,47 @@ private void listener(final WaveformChangeEvent wce) {
protected void handlePlotObjectClicked(final PlotObjectClick poc, final Function measurementFunc) {
List points = poc.getPlotPoints();
Set waveforms = new HashSet<>();
+ List selectedData = spectraControllers.stream()
+ .flatMap(spc -> spc.getSpectralPlot().getSelectedPoints().stream())
+ .map(measurementFunc::apply)
+ .map(SpectraMeasurement::getWaveform)
+ .collect(Collectors.toList());
+
+ if (poc.getMouseEvent().isPrimaryButtonDown() || selectedData.isEmpty()) {
+ // FIXME: This entire scheme is tremendously inefficient and needs a rework at
+ // some point.
+ for (SpectraPlotController spc : spectraControllers) {
+ spc.getSpectralPlot().deselectAllPoints();
+ }
- // FIXME: This entire scheme is tremendously inefficient and needs a rework at
- // some point.
- for (SpectraPlotController spc : spectraControllers) {
- spc.getSpectralPlot().deselectAllPoints();
- }
-
- for (Point2D point : points) {
- SpectraMeasurement spectra = measurementFunc.apply(point);
- if (spectra != null && spectra.getWaveform() != null) {
- waveforms.add(spectra.getWaveform());
- for (SpectraPlotController spc : spectraControllers) {
- if (poc.getMouseEvent().isPrimaryButtonDown()) {
- final Map> symbolMap = spc.getSymbolMap();
- final SpectralPlot plot = spc.getSpectralPlot();
- //Dyne-cm to Newton-meter
- Point2D testPoint = new Point2D(point.getX(), point.getY() - 7.0);
- final boolean existsInPlot = symbolMap.containsKey(testPoint);
- if (existsInPlot) {
- plot.selectPoint(testPoint);
+ for (Point2D point : points) {
+ SpectraMeasurement spectra = measurementFunc.apply(point);
+ if (spectra != null && spectra.getWaveform() != null) {
+ waveforms.add(spectra.getWaveform());
+ for (SpectraPlotController spc : spectraControllers) {
+ if (poc.getMouseEvent().isPrimaryButtonDown()) {
+ final Map> symbolMap = spc.getSymbolMap();
+ final SpectralPlot plot = spc.getSpectralPlot();
+ //Dyne-cm to Newton-meter
+ Point2D testPoint = new Point2D(point.getX(), point.getY() - 7.0);
+ final boolean existsInPlot = symbolMap.containsKey(testPoint);
+ if (existsInPlot) {
+ plot.selectPoint(testPoint);
+ }
}
}
}
}
- }
- spectraControllers.forEach(spc -> spc.getSpectralPlot().getSubplot().replot());
-
- if (poc.getMouseEvent().isPrimaryButtonDown()) {
- showWaveformPopup(waveforms.stream().map(Waveform::getId).collect(Collectors.toSet()).toArray(new Long[0]));
- Platform.runLater(() -> menu.hide());
+ spectraControllers.forEach(spc -> spc.getSpectralPlot().getSubplot().replot());
+ if (poc.getMouseEvent().isPrimaryButtonDown()) {
+ showWaveformPopup(waveforms.stream().map(Waveform::getId).collect(Collectors.toSet()).toArray(new Long[0]));
+ Platform.runLater(() -> menu.hide());
+ } else {
+ showContextMenu(waveforms, points, poc.getMouseEvent(), this::setSymbolsActive);
+ }
} else if (poc.getMouseEvent().isSecondaryButtonDown()) {
+ waveforms.addAll(selectedData);
showContextMenu(waveforms, points, poc.getMouseEvent(), this::setSymbolsActive);
}
}
diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java
index 5445aa65..1491d690 100644
--- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java
+++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java
@@ -29,6 +29,7 @@
import java.util.stream.Collectors;
import javax.annotation.PreDestroy;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -56,7 +57,6 @@
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
-import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContextMenu;
@@ -72,374 +72,363 @@
@Component
public class DataController implements MapListeningController, RefreshableController {
- private static final Logger log = LoggerFactory.getLogger(DataController.class);
+ private static final Logger log = LoggerFactory.getLogger(DataController.class);
- @FXML
- private MenuItem importWaveforms;
+ @FXML
+ private MenuItem importWaveforms;
- @FXML
- private ScrollPane scrollPane;
+ @FXML
+ private ScrollPane scrollPane;
- @FXML
- private TableView tableView;
-
- @FXML
- private CheckBox selectAllCheckbox;
-
- @FXML
- private TableColumn usedCol;
-
- @FXML
- private TableColumn stationCol;
-
- @FXML
- private TableColumn networkCol;
-
- @FXML
- private TableColumn eventCol;
-
- @FXML
- private TableColumn lowFreqCol;
-
- @FXML
- private TableColumn highFreqCol;
-
- @FXML
- private TableColumn depthCol;
-
- private ObservableList listData = FXCollections
- .synchronizedObservableList(FXCollections.observableArrayList());
-
- private GeoMap mapImpl;
-
- private MapPlottingUtilities iconFactory;
-
- private WaveformClient client;
-
- private EventBus bus;
-
- private NumberFormat dfmt2 = NumberFormatFactory.twoDecimalOneLeadingZero();
-
- private EventStaFreqStringComparator eventStaFreqComparator = new EventStaFreqStringComparator();
-
- private ListChangeListener super Waveform> tableChangeListener;
-
- private final BiConsumer eventSelectionCallback;
- private final BiConsumer stationSelectionCallback;
- private ScheduledExecutorService scheduled = Executors.newSingleThreadScheduledExecutor(r -> {
- Thread thread = new Thread(r);
- thread.setName("Data-Scheduled");
- thread.setDaemon(true);
- return thread;
- });
-
- private List dataUpdateList = Collections.synchronizedList(new ArrayList<>());
- private List dataDeleteList = Collections.synchronizedList(new ArrayList<>());
- private List updatedData = Collections.synchronizedList(new ArrayList<>());
-
- private boolean isVisible = false;
-
- @Autowired
- public DataController(WaveformClient client, GeoMap mapImpl, MapPlottingUtilities iconFactory, EventBus bus) {
- super();
- this.client = client;
- this.mapImpl = mapImpl;
- this.iconFactory = iconFactory;
- this.bus = bus;
- bus.register(this);
- tableChangeListener = buildTableListener();
- scheduled.scheduleWithFixedDelay(() -> updateData(), 1000l, 1000l, TimeUnit.MILLISECONDS);
-
- eventSelectionCallback = (selected, eventId) -> {
- selectDataByCriteria(bus, selected,
- (w) -> w.getEvent() != null && w.getEvent().getEventId().equalsIgnoreCase(eventId));
- };
-
- stationSelectionCallback = (selected, stationId) -> {
- selectDataByCriteria(bus, selected, (w) -> w.getStream() != null && w.getStream().getStation() != null
- && w.getStream().getStation().getStationName().equalsIgnoreCase(stationId));
- };
- }
-
- private void selectDataByCriteria(EventBus bus, Boolean selected, Function matchCriteria) {
- List selection = new ArrayList<>();
- List selectionIndices = new ArrayList<>();
- tableView.getSelectionModel().clearSelection();
- if (selected) {
- tableView.getSelectionModel().getSelectedItems().removeListener(tableChangeListener);
- synchronized (listData) {
- for (int i = 0; i < listData.size(); i++) {
- Waveform w = listData.get(i);
- if (matchCriteria.apply(w)) {
- selection.add(w);
- tableView.getSelectionModel().select(i);
- }
- }
- }
- tableView.getSelectionModel().getSelectedItems().addListener(tableChangeListener);
- if (!selection.isEmpty()) {
- selection.sort(eventStaFreqComparator);
- Long[] ids = selection.stream().sequential().map(w -> w.getId()).collect(Collectors.toList())
- .toArray(new Long[0]);
- bus.post(new WaveformSelectionEvent(ids));
- }
- } else {
- selection.addAll(tableView.getSelectionModel().getSelectedItems());
- selectionIndices.addAll(tableView.getSelectionModel().getSelectedIndices());
- for (int i = 0; i < selection.size(); i++) {
- if (matchCriteria.apply(selection.get(i))) {
- tableView.getSelectionModel().clearSelection(selectionIndices.get(i));
- }
- }
- }
- }
-
- private ListChangeListener super Waveform> buildTableListener() {
- return (ListChangeListener) change -> {
- List selection = new ArrayList<>();
- selection.addAll(tableView.getSelectionModel().getSelectedItems());
- selection.sort(eventStaFreqComparator);
- Long[] ids = getIds(selection).toArray(new Long[0]);
- bus.post(new WaveformSelectionEvent(ids));
- };
- }
-
- private List getIds(List selection) {
- return selection.stream().sequential().map(w -> w.getId()).collect(Collectors.toList());
- }
-
- @FXML
- private void reloadTable(ActionEvent e) {
- CompletableFuture.runAsync(getRefreshFunction());
- }
-
- @FXML
- public void initialize() {
- tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
- eventCol.setCellValueFactory(
- x -> Bindings.createStringBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue)
- .map(Waveform::getEvent).map(Event::getEventId).orElseGet(String::new)));
- eventCol.comparatorProperty().set(new MaybeNumericStringComparator());
-
- CellBindingUtils.attachTextCellFactories(lowFreqCol, Waveform::getLowFrequency, dfmt2);
- CellBindingUtils.attachTextCellFactories(highFreqCol, Waveform::getHighFrequency, dfmt2);
-
- depthCol.setCellValueFactory(
- x -> Bindings.createStringBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue)
- .map(Waveform::getEvent).map(ev -> dfmt2.format(ev.getDepth())).orElseGet(String::new)));
- depthCol.comparatorProperty().set(new MaybeNumericStringComparator());
-
- usedCol.setCellValueFactory(x -> Bindings
- .createObjectBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(waveform -> {
- CheckBox box = new CheckBox();
- box.setSelected(waveform.isActive());
- if (!waveform.isActive()) {
- box.setStyle("-fx-background-color: red");
- } else {
- box.setStyle("");
- }
- box.selectedProperty().addListener((obs, o, n) -> {
- if (n != null && !o.equals(n)) {
- client.setWaveformsActiveByIds(Collections.singletonList(waveform.getId()), n).subscribe();
- }
- });
- return box;
- }).orElseGet(CheckBox::new)));
- usedCol.comparatorProperty().set((c1, c2) -> Boolean.compare(c1.isSelected(), c2.isSelected()));
-
- stationCol.setCellValueFactory(x -> Bindings.createStringBinding(
- () -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(Waveform::getStream)
- .map(Stream::getStation).map(Station::getStationName).orElseGet(String::new)));
-
- networkCol.setCellValueFactory(x -> Bindings.createStringBinding(
- () -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(Waveform::getStream)
- .map(Stream::getStation).map(Station::getNetworkName).orElseGet(String::new)));
-
- tableView.getSelectionModel().getSelectedItems().addListener(tableChangeListener);
- // Workaround for https://bugs.openjdk.java.net/browse/JDK-8095943, for now we
- // just clear the selection to avoid dumping a stack trace in the logs and
- // mucking up event bubbling
- tableView.setOnSort(event -> {
- if (tableView.getSelectionModel().getSelectedIndices().size() > 1) {
- tableView.getSelectionModel().clearSelection();
- }
- });
-
- ContextMenu menu = new ContextMenu();
- MenuItem include = new MenuItem("Include Selected");
- include.setOnAction(evt -> includeWaveforms());
- menu.getItems().add(include);
- MenuItem exclude = new MenuItem("Exclude Selected");
- exclude.setOnAction(evt -> excludeWaveforms());
- menu.getItems().add(exclude);
-
- tableView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() {
- @Override
- public void handle(MouseEvent t) {
-
- if (MouseButton.SECONDARY == t.getButton()) {
- menu.show(tableView, t.getScreenX(), t.getScreenY());
- } else {
- menu.hide();
- }
- }
- });
- tableView.setItems(listData);
-
- DataFilterController filterController = new DataFilterController<>(tableView);
-
- filterController.addFilterToColumn(eventCol, (data, item) -> data.getEvent().getEventId().equals(item));
- filterController.addFilterToColumn(depthCol,
- (data, item) -> dfmt2.format(data.getEvent().getDepth()).equals(item));
- filterController.addFilterToColumn(lowFreqCol, (data, item) -> data.getLowFrequency().toString().equals(item));
- filterController.addFilterToColumn(highFreqCol,
- (data, item) -> data.getHighFrequency().toString().equals(item));
- filterController.addFilterToColumn(stationCol,
- (data, item) -> data.getStream().getStation().getStationName().equals(item));
- filterController.addFilterToColumn(networkCol,
- (data, item) -> data.getStream().getStation().getNetworkName().equals(item));
- }
-
- @Override
- public void refreshView() {
- CompletableFuture.runAsync(() -> {
- synchronized (listData) {
- if (!listData.isEmpty()) {
- refreshMap();
- }
- }
- Platform.runLater(() -> {
- if (tableView != null) {
- tableView.refresh();
- }
- });
- });
- }
-
- private void refreshMap() {
- if (isVisible) {
- mapImpl.clearIcons();
- synchronized (listData) {
- mapImpl.addIcons(
- iconFactory.genIconsFromWaveforms(eventSelectionCallback, stationSelectionCallback, listData));
- }
- }
- }
-
- @Override
- public Runnable getRefreshFunction() {
- return () -> requestData();
- }
-
- private void requestData() {
- synchronized (listData) {
- listData.clear();
- }
- mapImpl.clearIcons();
- client.getUniqueEventStationMetadataForStacks().filter(Objects::nonNull).doOnComplete(() -> {
- tableView.sort();
- refreshView();
- }).subscribe(waveform -> {
- synchronized (listData) {
- listData.add(waveform);
- }
- }, err -> log.error(err.getMessage(), err));
- }
-
- private void requestUpdates() {
- List updates = new ArrayList<>();
- List deletes = new ArrayList<>();
- synchronized (dataUpdateList) {
- updates.addAll(dataUpdateList);
- dataUpdateList.clear();
- deletes.addAll(dataDeleteList);
- dataDeleteList.clear();
- }
-
- if (!deletes.isEmpty()) {
- synchronized (listData) {
- deletes.forEach(id -> {
- synchronized (listData) {
- int idx = -1;
- for (int i = 0; i < listData.size(); i++) {
- if (listData.get(i).getId().equals(id)) {
- idx = i;
- break;
- }
- }
- if (idx >= 0) {
- listData.remove(idx);
- }
- }
- });
- }
- }
-
- client.getWaveformMetadataFromIds(updates).filter(Objects::nonNull)
- .subscribe(waveform -> updatedData.add(waveform), err -> log.error(err.getMessage(), err));
- }
-
- private void updateData() {
- List updates = new ArrayList<>();
- synchronized (updatedData) {
- updates.addAll(updatedData);
- updatedData.clear();
- }
- if (!updates.isEmpty()) {
- synchronized (listData) {
- tableView.getSelectionModel().getSelectedItems().removeListener(tableChangeListener);
- updates.forEach(waveform -> {
- int idx = -1;
- for (int i = 0; i < listData.size(); i++) {
- if (listData.get(i).getId().equals(waveform.getId())) {
- idx = i;
- break;
- }
- }
- if (idx >= 0) {
- listData.set(idx, waveform);
- } else {
- listData.add(waveform);
- }
- });
- tableView.getSelectionModel().getSelectedItems().addListener(tableChangeListener);
- }
- refreshMap();
- }
- }
-
- private void excludeWaveforms() {
- client.setWaveformsActiveByIds(getSelectedWaveforms(), false).subscribe();
- }
-
- private void includeWaveforms() {
- client.setWaveformsActiveByIds(getSelectedWaveforms(), true).subscribe();
- }
-
- private List getSelectedWaveforms() {
- return getIds(tableView.getSelectionModel().getSelectedItems());
- }
-
- @Subscribe
- private void listener(WaveformChangeEvent wce) {
- List nonNull = wce.getIds().stream().filter(Objects::nonNull).collect(Collectors.toList());
- synchronized (dataUpdateList) {
- if (wce.isAddOrUpdate()) {
- dataUpdateList.addAll(nonNull);
- } else if (wce.isDelete()) {
- dataDeleteList.addAll(nonNull);
- }
- }
- requestUpdates();
- }
-
- @PreDestroy
- private void cleanup() {
- scheduled.shutdownNow();
- }
-
- @Override
- public void setVisible(boolean visible) {
- isVisible = visible;
- }
+ @FXML
+ private TableView tableView;
+
+ @FXML
+ private CheckBox selectAllCheckbox;
+
+ @FXML
+ private TableColumn usedCol;
+
+ @FXML
+ private TableColumn stationCol;
+
+ @FXML
+ private TableColumn networkCol;
+
+ @FXML
+ private TableColumn eventCol;
+
+ @FXML
+ private TableColumn lowFreqCol;
+
+ @FXML
+ private TableColumn highFreqCol;
+
+ @FXML
+ private TableColumn depthCol;
+
+ private ObservableList listData = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
+
+ private GeoMap mapImpl;
+
+ private MapPlottingUtilities iconFactory;
+
+ private WaveformClient client;
+
+ private EventBus bus;
+
+ private NumberFormat dfmt2 = NumberFormatFactory.twoDecimalOneLeadingZero();
+
+ private EventStaFreqStringComparator eventStaFreqComparator = new EventStaFreqStringComparator();
+
+ private ListChangeListener super Waveform> tableChangeListener;
+
+ private final BiConsumer eventSelectionCallback;
+ private final BiConsumer stationSelectionCallback;
+ private ScheduledExecutorService scheduled = Executors.newSingleThreadScheduledExecutor(r -> {
+ Thread thread = new Thread(r);
+ thread.setName("Data-Scheduled");
+ thread.setDaemon(true);
+ return thread;
+ });
+
+ private List dataUpdateList = Collections.synchronizedList(new ArrayList<>());
+ private List dataDeleteList = Collections.synchronizedList(new ArrayList<>());
+ private List updatedData = Collections.synchronizedList(new ArrayList<>());
+
+ private boolean isVisible = false;
+
+ @Autowired
+ public DataController(WaveformClient client, GeoMap mapImpl, MapPlottingUtilities iconFactory, EventBus bus) {
+ this.client = client;
+ this.mapImpl = mapImpl;
+ this.iconFactory = iconFactory;
+ this.bus = bus;
+ bus.register(this);
+ tableChangeListener = buildTableListener();
+ scheduled.scheduleWithFixedDelay(this::updateData, 1000l, 1000l, TimeUnit.MILLISECONDS);
+
+ eventSelectionCallback = (selected, eventId) -> {
+ selectDataByCriteria(bus, selected, w -> w.getEvent() != null && w.getEvent().getEventId().equalsIgnoreCase(eventId));
+ };
+
+ stationSelectionCallback = (selected, stationId) -> {
+ selectDataByCriteria(bus, selected, w -> w.getStream() != null && w.getStream().getStation() != null && w.getStream().getStation().getStationName().equalsIgnoreCase(stationId));
+ };
+ }
+
+ private void selectDataByCriteria(EventBus bus, Boolean selected, Function matchCriteria) {
+ List selection = new ArrayList<>();
+ List selectionIndices = new ArrayList<>();
+ tableView.getSelectionModel().clearSelection();
+ if (selected) {
+ tableView.getSelectionModel().getSelectedItems().removeListener(tableChangeListener);
+ synchronized (listData) {
+ for (int i = 0; i < listData.size(); i++) {
+ Waveform w = listData.get(i);
+ if (matchCriteria.apply(w)) {
+ selection.add(w);
+ tableView.getSelectionModel().select(i);
+ }
+ }
+ }
+ tableView.getSelectionModel().getSelectedItems().addListener(tableChangeListener);
+ if (!selection.isEmpty()) {
+ if (tableView.getSortOrder().isEmpty()) {
+ selection.sort(eventStaFreqComparator);
+ }
+ Long[] ids = selection.stream().sequential().map(Waveform::getId).collect(Collectors.toList()).toArray(new Long[0]);
+ bus.post(new WaveformSelectionEvent(ids));
+ }
+ } else {
+ selection.addAll(tableView.getSelectionModel().getSelectedItems());
+ selectionIndices.addAll(tableView.getSelectionModel().getSelectedIndices());
+ for (int i = 0; i < selection.size(); i++) {
+ if (matchCriteria.apply(selection.get(i))) {
+ tableView.getSelectionModel().clearSelection(selectionIndices.get(i));
+ }
+ }
+ }
+ }
+
+ private ListChangeListener super Waveform> buildTableListener() {
+ return (ListChangeListener) change -> {
+ if (!change.getList().isEmpty()) {
+ List selection = new ArrayList<>(tableView.getSelectionModel().getSelectedItems());
+ if (tableView.getSortOrder().isEmpty()) {
+ selection.sort(eventStaFreqComparator);
+ }
+ Long[] ids = getIds(selection).toArray(new Long[0]);
+ bus.post(new WaveformSelectionEvent(ids));
+ }
+ };
+ }
+
+ private List getIds(List selection) {
+ return selection.stream().sequential().map(Waveform::getId).collect(Collectors.toList());
+ }
+
+ @FXML
+ private void reloadTable(ActionEvent e) {
+ CompletableFuture.runAsync(getRefreshFunction());
+ }
+
+ @FXML
+ public void initialize() {
+ tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
+ eventCol.setCellValueFactory(
+ x -> Bindings.createStringBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(Waveform::getEvent).map(Event::getEventId).orElseGet(String::new)));
+ eventCol.comparatorProperty().set(new MaybeNumericStringComparator());
+
+ CellBindingUtils.attachTextCellFactories(lowFreqCol, Waveform::getLowFrequency, dfmt2);
+ CellBindingUtils.attachTextCellFactories(highFreqCol, Waveform::getHighFrequency, dfmt2);
+
+ depthCol.setCellValueFactory(
+ x -> Bindings.createStringBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(Waveform::getEvent).map(ev -> dfmt2.format(ev.getDepth())).orElseGet(String::new)));
+ depthCol.comparatorProperty().set(new MaybeNumericStringComparator());
+
+ usedCol.setCellValueFactory(x -> Bindings.createObjectBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(waveform -> {
+ CheckBox box = new CheckBox();
+ box.setSelected(waveform.isActive());
+ if (!waveform.isActive()) {
+ box.setStyle("-fx-background-color: red");
+ } else {
+ box.setStyle("");
+ }
+ box.selectedProperty().addListener((obs, o, n) -> {
+ if (n != null && !o.equals(n)) {
+ client.setWaveformsActiveByIds(Collections.singletonList(waveform.getId()), n).subscribe();
+ }
+ });
+ return box;
+ }).orElseGet(CheckBox::new)));
+ usedCol.comparatorProperty().set((c1, c2) -> Boolean.compare(c1.isSelected(), c2.isSelected()));
+
+ stationCol.setCellValueFactory(
+ x -> Bindings.createStringBinding(
+ () -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(Waveform::getStream).map(Stream::getStation).map(Station::getStationName).orElseGet(String::new)));
+
+ networkCol.setCellValueFactory(
+ x -> Bindings.createStringBinding(
+ () -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(Waveform::getStream).map(Stream::getStation).map(Station::getNetworkName).orElseGet(String::new)));
+
+ tableView.getSelectionModel().getSelectedItems().addListener(tableChangeListener);
+ // Workaround for https://bugs.openjdk.java.net/browse/JDK-8095943, for now we
+ // just clear the selection to avoid dumping a stack trace in the logs and
+ // mucking up event bubbling
+ tableView.setOnSort(event -> {
+ if (tableView.getSelectionModel().getSelectedIndices().size() > 1) {
+ tableView.getSelectionModel().clearSelection();
+ }
+ });
+
+ ContextMenu menu = new ContextMenu();
+ MenuItem include = new MenuItem("Include Selected");
+ include.setOnAction(evt -> includeWaveforms());
+ menu.getItems().add(include);
+ MenuItem exclude = new MenuItem("Exclude Selected");
+ exclude.setOnAction(evt -> excludeWaveforms());
+ menu.getItems().add(exclude);
+
+ tableView.addEventHandler(MouseEvent.MOUSE_CLICKED, t -> {
+
+ if (MouseButton.SECONDARY == t.getButton()) {
+ menu.show(tableView, t.getScreenX(), t.getScreenY());
+ } else {
+ menu.hide();
+ }
+ });
+
+ //The filter controller will manage the list so don't add the listData directly
+ //to the table, otherwise the event handlers will trip over themselves
+ DataFilterController filterController = new DataFilterController<>(tableView, listData);
+
+ filterController.addFilterToColumn(eventCol, (data, item) -> data.getEvent().getEventId().equals(item));
+ filterController.addFilterToColumn(depthCol, (data, item) -> dfmt2.format(data.getEvent().getDepth()).equals(item));
+ filterController.addFilterToColumn(lowFreqCol, (data, item) -> data.getLowFrequency().toString().equals(item));
+ filterController.addFilterToColumn(highFreqCol, (data, item) -> data.getHighFrequency().toString().equals(item));
+ filterController.addFilterToColumn(stationCol, (data, item) -> data.getStream().getStation().getStationName().equals(item));
+ filterController.addFilterToColumn(networkCol, (data, item) -> data.getStream().getStation().getNetworkName().equals(item));
+ }
+
+ @Override
+ public void refreshView() {
+ CompletableFuture.runAsync(() -> {
+ synchronized (listData) {
+ if (!listData.isEmpty()) {
+ refreshMap();
+ }
+ }
+ Platform.runLater(() -> {
+ if (tableView != null) {
+ tableView.refresh();
+ }
+ });
+ });
+ }
+
+ private void refreshMap() {
+ if (isVisible) {
+ mapImpl.clearIcons();
+ synchronized (listData) {
+ mapImpl.addIcons(iconFactory.genIconsFromWaveforms(eventSelectionCallback, stationSelectionCallback, listData));
+ }
+ }
+ }
+
+ @Override
+ public Runnable getRefreshFunction() {
+ return this::requestData;
+ }
+
+ private void requestData() {
+ synchronized (listData) {
+ listData.clear();
+ }
+ mapImpl.clearIcons();
+ client.getUniqueEventStationMetadataForStacks().filter(Objects::nonNull).doOnComplete(() -> {
+ tableView.sort();
+ refreshView();
+ }).subscribe(waveform -> {
+ synchronized (listData) {
+ listData.add(waveform);
+ }
+ }, err -> log.error(err.getMessage(), err));
+ }
+
+ private void requestUpdates() {
+ List updates = new ArrayList<>();
+ List deletes = new ArrayList<>();
+ synchronized (dataUpdateList) {
+ updates.addAll(dataUpdateList);
+ dataUpdateList.clear();
+ deletes.addAll(dataDeleteList);
+ dataDeleteList.clear();
+ }
+
+ if (!deletes.isEmpty()) {
+ synchronized (listData) {
+ deletes.forEach(id -> {
+ synchronized (listData) {
+ int idx = -1;
+ for (int i = 0; i < listData.size(); i++) {
+ if (listData.get(i).getId().equals(id)) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx >= 0) {
+ listData.remove(idx);
+ }
+ }
+ });
+ }
+ }
+
+ client.getWaveformMetadataFromIds(updates).filter(Objects::nonNull).subscribe(waveform -> updatedData.add(waveform), err -> log.error(err.getMessage(), err));
+ }
+
+ private void updateData() {
+ List updates = new ArrayList<>();
+ synchronized (updatedData) {
+ updates.addAll(updatedData);
+ updatedData.clear();
+ }
+ if (!updates.isEmpty()) {
+ synchronized (listData) {
+ tableView.getSelectionModel().getSelectedItems().removeListener(tableChangeListener);
+ updates.forEach(waveform -> {
+ int idx = -1;
+ for (int i = 0; i < listData.size(); i++) {
+ if (listData.get(i).getId().equals(waveform.getId())) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx >= 0) {
+ listData.set(idx, waveform);
+ } else {
+ listData.add(waveform);
+ }
+ });
+ tableView.getSelectionModel().getSelectedItems().addListener(tableChangeListener);
+ }
+ refreshMap();
+ }
+ }
+
+ private void excludeWaveforms() {
+ client.setWaveformsActiveByIds(getSelectedWaveforms(), false).subscribe();
+ }
+
+ private void includeWaveforms() {
+ client.setWaveformsActiveByIds(getSelectedWaveforms(), true).subscribe();
+ }
+
+ private List getSelectedWaveforms() {
+ return getIds(tableView.getSelectionModel().getSelectedItems());
+ }
+
+ @Subscribe
+ private void listener(WaveformChangeEvent wce) {
+ List nonNull = wce.getIds().stream().filter(Objects::nonNull).collect(Collectors.toList());
+ synchronized (dataUpdateList) {
+ if (wce.isAddOrUpdate()) {
+ dataUpdateList.addAll(nonNull);
+ } else if (wce.isDelete()) {
+ dataDeleteList.addAll(nonNull);
+ }
+ }
+ requestUpdates();
+ }
+
+ @PreDestroy
+ private void cleanup() {
+ scheduled.shutdownNow();
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ isVisible = visible;
+ }
}
diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataFilterController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataFilterController.java
index eda9188d..6102c21e 100644
--- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataFilterController.java
+++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataFilterController.java
@@ -13,7 +13,6 @@
*/
package gov.llnl.gnem.apps.coda.calibration.gui.controllers;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
@@ -24,16 +23,15 @@
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
+import javafx.collections.transformation.SortedList;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
-import javafx.scene.image.Image;
-import javafx.scene.image.ImageView;
class DataFilterController {
- TableView tableView;
// The list of unfiltered table items
ObservableList items;
FilterDialogController filterDialog;
@@ -41,38 +39,41 @@ class DataFilterController {
// The filter buttons attached to columns
private List