SiLeBAT/FSK-Lab

View on GitHub
de.bund.bfr.knime.foodprocess.view/src/de/bund/bfr/knime/foodprocess/view/MyChartCreator.java

Summary

Maintainability
F
5 days
Test Coverage
/*******************************************************************************
 * Copyright (c) 2015 Federal Institute for Risk Assessment (BfR), Germany
 *
 * 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/>.
 *
 * Contributors:
 *     Department Biological Safety - BfR
 *******************************************************************************/
package de.bund.bfr.knime.foodprocess.view;

import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.xmlbeans.XmlCursor;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.data.xy.DefaultXYDataset;
import org.jfree.data.xy.XYDataset;
import org.knime.core.data.DataCell;
import org.knime.core.data.DataRow;
import org.knime.core.data.DataTable;
import org.knime.core.data.DataTableSpec;
import org.knime.core.data.def.DefaultRow;
import org.knime.core.data.def.DoubleCell;
import org.knime.core.data.def.StringCell;

import de.bund.bfr.knime.pcml.node.pcmltotable.PCMLDataTable;
import de.bund.bfr.knime.pmm.common.chart.ColorAndShapeCreator;
import de.bund.bfr.knime.pmm.common.pmmtablemodel.AttributeUtilities;
import de.bund.bfr.knime.util.FormulaEvaluator;
import de.bund.bfr.knime.util.NameAndDbId;
import de.bund.bfr.pcml10.PCMLDocument;
import de.bund.bfr.pcml10.ProcessDataDocument.ProcessData;
import de.bund.bfr.pcml10.ProcessNodeDocument.ProcessNode;
import de.bund.bfr.pcml10.RowDocument.Row;

public class MyChartCreator {

    private List<ProcessLegendElement> processLegend;
    private ViewUi vui;

    public MyChartCreator(ViewUi vui) {
        this.vui = vui;
    }

    public List<ProcessLegendElement> getProcessLegend() {
        return processLegend;
    }

    public JFreeChart createChart(PCMLDocument pcmlDoc, List<String> usedParameters, String xUnits) {
        return createChart(pcmlDoc, usedParameters, xUnits, false, false, false);
    }

    public JFreeChart createChart(PCMLDocument pcmlDoc, List<String> usedParameters, String xUnits,
            boolean equidistantProcesses, boolean plotLines, boolean plotPoints) {
        Map<NameAndDbId, Integer> columns = PCMLDataTable.createColumnMap(pcmlDoc);
        Map<String, ProcessNode> processNodes = PCMLDataTable.createProcessNodeMap(pcmlDoc);
        DataTableSpec spec = PCMLDataTable.createOutSpec(columns);

        Double timeDiff = null;
        List<DataRow> rowList = new ArrayList<DataRow>();
        for (ProcessData data : pcmlDoc.getPCML().getProcessChainData().getProcessDataArray()) {
            ProcessNode processNode = processNodes.get(data.getRef());
            double time = data.getTime();
            if (timeDiff == null)
                timeDiff = time;
            time = time - timeDiff;
            Map<String, NameAndDbId> columnList = PCMLDataTable.createColumnList(data.getDataTable().getColumnList());
            NameAndDbId c0 = columnList.get("c0");
            NameAndDbId timeColumn = (c0 != null && c0.getName().startsWith(AttributeUtilities.TIME)) ? c0 : null;
            double curTime = 0;
            for (Row row : data.getDataTable().getInlineTable().getRowArray()) {
                XmlCursor cursor = row.newCursor();
                cursor.toFirstChild();
                Map<NameAndDbId, String> rowData = new HashMap<NameAndDbId, String>();
                for (int i = 0; i < columnList.size(); i++) {
                    NameAndDbId column = columnList.get(cursor.getName().getLocalPart());
                    if (timeColumn == null || !timeColumn.equals(column))
                        rowData.put(column, cursor.getTextValue());
                    else
                        curTime = (cursor.getTextValue() != null && !cursor.getTextValue().equalsIgnoreCase("null"))
                                ? Double.valueOf(cursor.getTextValue()) : 0.0;
                    cursor.toNextSibling();
                }
                cursor.dispose();
                double theTime = timeColumn != null ? curTime : time;
                DataCell[] cells = PCMLDataTable.createDataCells(theTime, rowData, processNode, columns);
                DataRow dataRow = new DefaultRow(theTime + "_" + processNode.getId(), cells); // RowKey.createRowKey(counter)
                rowList.add(dataRow);

                if (timeColumn == null) {
                    // increment time by the time step of the process node
                    double stepWidth = processNode.getParameters().getDuration()
                            / processNode.getParameters().getNumberComputations();
                    time = time + stepWidth;
                }
            }
        }
        return createChart(spec, rowList, null, usedParameters, xUnits, equidistantProcesses, plotLines, plotPoints);
    }

    public JFreeChart createChart(DataTable table, List<String> usedParameters, String xUnits,
            boolean equidistantProcesses, boolean plotLines, boolean plotPoints) {
        return createChart(table.getDataTableSpec(), null, table, usedParameters, xUnits, equidistantProcesses, plotLines, plotPoints);
    }

    private Iterator<DataRow> getIterator(List<DataRow> rowList, DataTable table) {
        if (rowList != null)
            return rowList.iterator();
        else if (table != null)
            return table.iterator();
        else
            return null;
    }

    private JFreeChart createChart(DataTableSpec spec, List<DataRow> rowList, DataTable table,
            List<String> usedParameters, String xUnits, boolean equidistantProcesses, boolean plotLines, boolean plotPoints) {
        LinkedList<XYDataset> dataSets = new LinkedList<XYDataset>();
        LinkedList<XYDataset> equiDataSets = new LinkedList<XYDataset>();
        int timeIndex = spec.findColumnIndex(AttributeUtilities.TIME + " [s]");
        int processIndex = spec.findColumnIndex("process");
        int processIdIndex = spec.findColumnIndex("process id");

        LinkedList<Point2D.Double> ranges = new LinkedList<Point2D.Double>();
        LinkedList<String> processNames = new LinkedList<String>();
        double processStart = Double.NaN;
        double time = Double.NaN;
        boolean pFilled = false;

        for (String param : usedParameters) {
            int paramIndex = spec.findColumnIndex(param);
            if (paramIndex >= 0) {
                LinkedHashMap<Double, Double> timeSeries = new LinkedHashMap<Double, Double>();
                boolean paramDone = false;
                String process = null, lastP = null;

                double ds = FormulaEvaluator.getSeconds(xUnits);
                boolean param2Cumulate = !JCheckboxWithObject.isTemperature(param) && !JCheckboxWithObject.isPH(param)
                        && !JCheckboxWithObject.isAW(param) && !JCheckboxWithObject.isPressure(param)
                        && !JCheckboxWithObject.isAgent(param) && !JCheckboxWithObject.isWithUnit(param);
                Iterator<DataRow> iter = getIterator(rowList, table);
                while (iter.hasNext()) {
                    DataRow row = iter.next();
                    DataCell timeCell = row.getCell(timeIndex);
                    DataCell paramCell = row.getCell(paramIndex);
                    DataCell processCell = row.getCell(processIndex);
                    DataCell processIdCell = row.getCell(processIdIndex);

                    if (!timeCell.isMissing()) {
                        String p = ((StringCell) processIdCell).getStringValue();
                        boolean newP = false;
                        if (!p.equals(process)) {
                            newP = true;
                            if (process != null) {
                                if (!pFilled) {
                                    ranges.add(new Point2D.Double(processStart, time));
                                    processNames.add(lastP);
                                }
                            }
                            double newPS = ((DoubleCell) timeCell).getDoubleValue() / ds;
                            if (newPS <= processStart && timeSeries.size() > 0) {
                                // processflow splits into parallel processes
                                if (!param2Cumulate) {
                                    addDataset(dataSets, paramDone ? "" : param, timeSeries);
                                    addDataset(equiDataSets, paramDone ? "" : param, getEquiSeries(timeSeries, ranges));
                                    timeSeries = new LinkedHashMap<Double, Double>();
                                    paramDone = true;
                                }
                            }
                            processStart = newPS;
                            process = p;
                            lastP = ((StringCell) processCell).getStringValue();
                        }
                        time = ((DoubleCell) timeCell).getDoubleValue() / ds;

                        if (!paramCell.isMissing()) {
                            Double val = ((DoubleCell) paramCell).getDoubleValue();
                            if (param2Cumulate && !newP) {
                                if (timeSeries.containsKey(time))
                                    timeSeries.put(time, val + timeSeries.get(time));
                                else
                                    timeSeries.put(time, val);
                            } else if (!timeSeries.containsKey(time)) {
                                // System.err.println(param + "\t" + lastP +
                                // "\t" + time + "\t" + val);
                                timeSeries.put(time, val);
                            } else if (newP) {
                                // System.err.println(param + "\t" + lastP +
                                // "\t" + time + "\t" + val);
                                timeSeries.put(time + 0.00001, val);
                            }
                        }
                    }
                }
                if (!pFilled) {
                    ranges.add(new Point2D.Double(processStart, time));
                    processNames.add(lastP);
                    pFilled = true;
                }
                if (timeSeries.size() > 0) {
                    addDataset(dataSets, paramDone ? "" : param, timeSeries);
                    addDataset(equiDataSets, paramDone ? "" : param, getEquiSeries(timeSeries, ranges));
                }
            }
        }

        if (processLegend == null || processLegend.size() == 0) {
            processLegend = new ArrayList<ProcessLegendElement>();
            List<Color> colorList = new ColorAndShapeCreator(ranges.size()).getColorList();
            for (int i = 0; i < processNames.size(); i++) {
                String pn = processNames.get(i);
                ProcessLegendElement ple = new ProcessLegendElement(pn, colorList.get(i), vui);
                processLegend.add(ple);
            }
        }

        MyXAxis xaxis = new MyXAxis(AttributeUtilities.TIME + " [" + xUnits + "]", ranges, processNames,
                equidistantProcesses);
        XYPlot plot = new XYPlot(null, xaxis, null, null);
        plot.setDomainPannable(true);
        plot.setRangePannable(true);

        // ColorAndShapeCreator colorCreator = new
        // ColorAndShapeCreator(usedParameters.size());
        int lastI = 0;
        StandardXYItemRenderer renderer = null;
        Color lastBG = null;
        String lastParam = null;
        List<NumberAxis> matrixAxes = new ArrayList<NumberAxis>();
        List<NumberAxis> agentAxes = new ArrayList<NumberAxis>();
        if (equidistantProcesses)
            dataSets = equiDataSets;
        for (int i = 0; i < dataSets.size(); i++) {
            String param = dataSets.get(i).getSeriesKey(0).toString();
            Color color = getColor(param, lastBG, lastParam);
            lastBG = color;
            lastParam = param;
            plot.setDataset(i, dataSets.get(i));
            if (!param.isEmpty()) {
                lastI = i;
                // Only plots points (no lines)
                int rendererType;
                if (plotPoints && plotLines) {
                    rendererType = StandardXYItemRenderer.SHAPES_AND_LINES;
                } else if (plotPoints && !plotLines) {
                    rendererType = StandardXYItemRenderer.SHAPES;
                } else if (!plotPoints && plotLines) {
                    rendererType = StandardXYItemRenderer.LINES;
                } else {
                    rendererType = 0;
                }
                renderer = new StandardXYItemRenderer(rendererType);
                renderer.setSeriesPaint(0, color);
                plot.setRenderer(i, renderer);
                NumberAxis rangeAxis = getNumberAxis(param, color);
                plot.setRangeAxis(i, rangeAxis);
                plot.mapDatasetToRangeAxis(i, i);
                if (i > 0) {
                    double diffY = Math.random() * (rangeAxis.getUpperBound() - rangeAxis.getLowerBound()) / 5;
                    rangeAxis.setRange(rangeAxis.getLowerBound() - diffY, rangeAxis.getUpperBound() + diffY);
                }
                if (JCheckboxWithObject.isAgent(param))
                    agentAxes.add(rangeAxis);
                else if (JCheckboxWithObject.isMatrix(param))
                    matrixAxes.add(rangeAxis);
            } else {
                plot.setRenderer(i, renderer);
                plot.mapDatasetToRangeAxis(i, lastI);
            }
        }
        setBounds(agentAxes);
        setBounds(matrixAxes);

        JFreeChart jfc = new JFreeChart(null, JFreeChart.DEFAULT_TITLE_FONT, plot, false);
        return jfc;
    }

    private void setBounds(List<NumberAxis> axes) {
        Double lb = null;
        Double ub = null;
        for (NumberAxis na : axes) {
            if (lb == null || na.getLowerBound() < lb)
                lb = na.getLowerBound();
            if (ub == null || na.getUpperBound() > ub)
                ub = na.getUpperBound();
        }
        if (lb != null && ub != null) {
            for (NumberAxis na : axes) {
                na.setRange(lb, ub);
            }
        }
    }

    private Color getColor(String param, Color lastBG, String lastParam) {
        Color bg = Color.WHITE;
        @SuppressWarnings("unused")
        Color fg = Color.BLACK;
        if (JCheckboxWithObject.isTemperature(param)) {
            bg = Color.BLUE;
            fg = Color.WHITE;
        } else if (JCheckboxWithObject.isAW(param)) {
            bg = new Color(160, 82, 45);
            fg = Color.WHITE;
        } else if (JCheckboxWithObject.isPH(param)) {
            bg = Color.GREEN;
        } else if (JCheckboxWithObject.isPressure(param)) {
            bg = Color.BLACK;
        } else if (JCheckboxWithObject.isAgent(param)) {
            if (lastParam != null && JCheckboxWithObject.isAgent(lastParam))
                bg = lastBG.darker();
            else
                bg = Color.RED;
            fg = Color.WHITE;
        } else if (JCheckboxWithObject.isMatrix(param)) {
            if (lastParam != null && JCheckboxWithObject.isMatrix(lastParam))
                bg = lastBG.brighter();
            else
                bg = Color.DARK_GRAY;
            fg = Color.WHITE;
        } else if (JCheckboxWithObject.isWithUnit(param)) {
            if (lastParam != null && JCheckboxWithObject.isWithUnit(lastParam))
                bg = lastBG.brighter();
            else
                bg = Color.LIGHT_GRAY;
            fg = Color.BLACK;
        }
        return bg;
    }

    private NumberAxis getNumberAxis(String param, Paint axisColor) {
        NumberAxis rangeAxis = new NumberAxis(param);
        // set colors
        if (axisColor != null) {
            rangeAxis.setAxisLinePaint(axisColor);
            rangeAxis.setLabelPaint(axisColor);
            rangeAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 12));
            // rangeAxis.setTickLabelPaint(axisColor);
            // rangeAxis.setTickMarkPaint(axisColor);
        }
        return rangeAxis;
    }

    private void addDataset(List<XYDataset> dataSets, String param, LinkedHashMap<Double, Double> timeSeries) {
        int n = timeSeries.size();
        double[][] data = new double[2][n];

        int i = 0;
        for (Double time : timeSeries.keySet()) {
            data[0][i] = time;
            data[1][i] = timeSeries.get(time);
            i++;
        }

        DefaultXYDataset dataSet = new DefaultXYDataset();

        dataSet.addSeries(param, data);
        dataSets.add(dataSet);
    }

    private LinkedHashMap<Double, Double> getEquiSeries(LinkedHashMap<Double, Double> timeSeries,
            LinkedList<Point2D.Double> ranges) {
        LinkedHashMap<Double, Double> result = new LinkedHashMap<Double, Double>();
        for (Double time : timeSeries.keySet()) {
            Double value = timeSeries.get(time);
            int p2dLfd = 0;
            for (Point2D.Double p2d : ranges) {
                if (time >= p2d.x && time <= p2d.y) {
                    result.put(p2dLfd + (time - p2d.x) / (p2d.y - p2d.x), value);
                    /*
                     * if (processLegend != null && processLegend.size() >
                     * p2dLfd) { if (processLegend.get(p2dLfd).isSelected()) {
                     * //System.err.println(p2dLfd + "\t" +
                     * processLegend.get(p2dLfd).isSelected()); } }
                     */
                }
                p2dLfd++;
            }
        }
        return result;
    }
}