/*
* This file is part of ADDIS (Aggregate Data Drug Information System).
* ADDIS is distributed from http://drugis.org/.
* Copyright © 2009 Gert van Valkenhoef, Tommi Tervonen.
* Copyright © 2010 Gert van Valkenhoef, Tommi Tervonen, Tijs Zwinkels,
* Maarten Jacobs, Hanno Koeslag, Florin Schimbinschi, Ahmad Kamal, Daniel
* Reid.
* Copyright © 2011 Gert van Valkenhoef, Ahmad Kamal, Daniel Reid, Florin
* Schimbinschi.
* Copyright © 2012 Gert van Valkenhoef, Daniel Reid, Joël Kuiper, Wouter
* Reckman.
* Copyright © 2013 Gert van Valkenhoef, Joël Kuiper.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.drugis.addis.gui.builder;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import org.drugis.addis.entities.Arm;
import org.drugis.addis.entities.Study;
import org.drugis.addis.entities.analysis.MetaBenefitRiskAnalysis;
import org.drugis.addis.entities.analysis.StudyBenefitRiskAnalysis;
import org.drugis.addis.entities.treatment.TreatmentDefinition;
import org.drugis.addis.gui.AddisWindow;
import org.drugis.addis.gui.AuxComponentFactory;
import org.drugis.addis.gui.LyndOBrienChartFactory;
import org.drugis.addis.presentation.AbstractBenefitRiskPresentation;
import org.drugis.addis.presentation.LyndOBrienPresentation;
import org.drugis.addis.presentation.ModifiableHolder;
import org.drugis.common.gui.BuildViewWhenReadyComponent;
import org.drugis.common.gui.ViewBuilder;
import org.drugis.common.gui.task.TaskProgressBar;
import org.drugis.common.threading.TaskListener;
import org.drugis.common.threading.event.TaskEvent;
import org.drugis.common.threading.event.TaskEvent.EventType;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.annotations.XYLineAnnotation;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.Range;
import org.jfree.ui.RectangleEdge;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
public class LyndOBrienView implements ViewBuilder {
LyndOBrienPresentation<?,?> d_pm;
AbstractBenefitRiskPresentation<?, ?> d_BRpm;
private JPanel d_panel;
private JLabel d_pvalueLabel;
public LyndOBrienView(AbstractBenefitRiskPresentation<?,?> pm, AddisWindow mainWindow) {
d_pm = pm.getLyndOBrienPresentation();
d_BRpm = pm;
d_pvalueLabel = new JLabel();
}
public JPanel buildPanel() {
FormLayout layout = new FormLayout(
"fill:0:grow",
"p, 3dlu, p, 3dlu, p, 3dlu, " +
"p, 3dlu, p, 3dlu, p, 3dlu, p");
PanelBuilder builder = new PanelBuilder(layout);
builder.setDefaultDialogBorder();
CellConstraints cc = new CellConstraints();
builder.addSeparator("Benefit-risk plane");
JScrollPane scatter = new JScrollPane(createWaiter(new ScatterplotBuilder()));
scatter.setViewportBorder(null);
builder.add(scatter, cc.xy(1,3));
String alternativeName = "";
String baselineName = "";
Object baseline = d_BRpm.getBean().getBaseline();
Object alternative = d_BRpm.getBean().getNonBaselineAlternatives().get(0);
if(d_BRpm.getBean() instanceof StudyBenefitRiskAnalysis) {
Study s = ((StudyBenefitRiskAnalysis) d_BRpm.getBean()).getStudy();
Arm baseArm = (Arm)baseline;
Arm altArm = (Arm)alternative;
baselineName = s.getTreatment(baseArm).getLabel();
alternativeName = s.getTreatment(altArm).getLabel();
} else if (d_BRpm.getBean() instanceof MetaBenefitRiskAnalysis) {
baselineName = ((TreatmentDefinition)baseline).getLabel();
alternativeName = ((TreatmentDefinition)alternative).getLabel();
}
builder.add(AuxComponentFactory.createTextPane("Results of Monte Carlo simulations based on the difference-distributions of" +
" the alternatives and criteria. Results in the NW quadrant indicate that " +
baselineName +" is better and" +
" results in the SE quadrant indicate that "+ alternativeName + " is better."), cc.xy(1,7));
builder.addSeparator("Benefit-Risk Aceptability curve", cc.xy(1, 9));
JScrollPane pvalue = new JScrollPane(createWaiter(new PvalueplotBuilder()));
pvalue.setViewportBorder(null);
builder.add(pvalue, cc.xy(1,11));
builder.add(AuxComponentFactory.createTextPane("Probability for a given acceptability threshold " +
"\u03BC that " + alternativeName + " is superior to " + baselineName + ". Indicates the" +
" proportion of datapoints in the Benefit-Risk" +
" plane that lie below the line y = \u03BC x"), cc.xy(1,13));
d_panel = builder.getPanel();
return d_panel;
}
private class ScatterplotBuilder implements ViewBuilder, TaskListener {
public JComponent buildPanel() {
FormLayout layout = new FormLayout(
"pref",
"p, 3dlu, p, 3dlu, p");
PanelBuilder builder = new PanelBuilder(layout);
CellConstraints cc = new CellConstraints();
JProgressBar bar = new TaskProgressBar(d_pm.getProgressModel());
builder.add(bar,cc.xy(1, 1));
final draggableMuChartPanel component = new draggableMuChartPanel(LyndOBrienChartFactory.buildScatterPlot(d_pm.getModel()));
d_pm.getModel().getTask().addTaskListener(this);
component.addListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
Double mu = component.getMu();
setMuAndPValueLabel(mu);
}
});
d_pm.getModel().getTask().addTaskListener(component);
builder.add(component, cc.xy(1,3));
setMuAndPValueLabel(1.0);
builder.add(d_pvalueLabel, cc.xy(1,5));
return builder.getPanel();
}
public void taskEvent(TaskEvent event) {
if(event.getType() == EventType.TASK_PROGRESS || event.getType() == EventType.TASK_FINISHED) {
Double mu = 1.0;
setMuAndPValueLabel(mu);
}
}
private void setMuAndPValueLabel(Double mu) {
DecimalFormat df = new DecimalFormat("0.00");
d_pvalueLabel.setText("\u03BC = " + df.format(mu) + ", P-value: " + df.format(d_pm.getModel().getPValue(mu)));
}
}
private class PvalueplotBuilder implements ViewBuilder {
public JComponent buildPanel() {
return new ChartPanel(LyndOBrienChartFactory.buildRiskAcceptabilityCurve(d_pm.getModel()));
}
}
@SuppressWarnings("serial")
private class draggableMuChartPanel extends ChartPanel implements TaskListener {
private XYAnnotation d_prevAnnotation = null;
private ModifiableHolder<Double> d_mu;
public draggableMuChartPanel(JFreeChart chart) {
super(chart);
d_mu = new ModifiableHolder<Double>(1.0);
drawMuLine(chart.getXYPlot(), d_mu.getValue());
}
public void addListener(PropertyChangeListener l) {
d_mu.addValueChangeListener(l);
}
Double getMu() {
return d_mu.getValue();
}
private Point2D.Double convertToChartCoordinates(Point point) {
ChartRenderingInfo info = getChartRenderingInfo();
Rectangle2D dataArea = info.getPlotInfo().getDataArea();
Point2D p = translateScreenToJava2D(
new Point(point.x, point.y));
XYPlot plot = getChart().getXYPlot();
RectangleEdge domainAxisEdge = plot.getDomainAxisEdge();
RectangleEdge rangeAxisEdge = plot.getRangeAxisEdge();
ValueAxis domainAxis = plot.getDomainAxis();
ValueAxis rangeAxis = plot.getRangeAxis();
return new Point2D.Double(domainAxis.java2DToValue(p.getX(), dataArea,
domainAxisEdge), rangeAxis.java2DToValue(p.getY(), dataArea,
rangeAxisEdge));
}
public void mouseDragged(MouseEvent e){
Point point = e.getPoint();
Point2D.Double chartXY = convertToChartCoordinates(point);
// if the mouse is in the SW or NE quadrant, change mu so that it is drawn through the mouse position
if ((chartXY.x > 0 && chartXY.y > 0) ||
(chartXY.x < 0 && chartXY.y < 0)) {
double mu = chartXY.y / chartXY.x;
drawMuLine(getChart().getXYPlot(), mu);
}
}
private void drawMuLine(XYPlot plot, double mu) {
Point2D.Double start;
Point2D.Double end;
d_mu.setValue(mu);
Range d = plot.getDomainAxis().getRange();
Range r = plot.getRangeAxis().getRange();
double lowerX = d.getLowerBound();
double upperX = d.getUpperBound();
double lowerY = r.getLowerBound();
double upperY = r.getUpperBound();
end = new Point2D.Double(Math.min(upperY / mu, upperX), Math.min(mu * upperX, upperY));
start = new Point2D.Double(Math.max(lowerY / mu, lowerX), Math.max(mu * lowerX, lowerY));
if(d_prevAnnotation != null) {
plot.removeAnnotation(d_prevAnnotation);
}
d_prevAnnotation = new XYLineAnnotation(start.x, start.y,end.x, end.y);
plot.addAnnotation(d_prevAnnotation);
}
public void taskEvent(TaskEvent event) {
if(event.getType() == EventType.TASK_PROGRESS) {
drawMuLine(getChart().getXYPlot(), d_mu.getValue());
}
}
};
protected BuildViewWhenReadyComponent createWaiter(ViewBuilder builder) {
return new BuildViewWhenReadyComponent(builder, d_BRpm.getMeasurementsReadyModel(), SMAAView.WAITING_MESSAGE);
}
}