/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools 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.jgrasstools.gears.ui;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_AUTHORCONTACTS;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_AUTHORNAMES;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DOCUMENTATION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_KEYWORDS;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_LABEL;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_LICENSE;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_NAME;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_STATUS;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_UI;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DO_CHART_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DO_CUMULATE_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DO_DUMP_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DO_LEGEND_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DO_NORMALIZE_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_DO_POINTS_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_CHARTPATH_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_COLORS_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_DATA_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_FORMATS_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_LABELS_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_SERIES_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_SUBTITLE_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_TITLE_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_IN_TYPES_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_P_HEIGHT_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_P_TYPE_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSMATRIXCHARTER_P_WIDTH_DESCRIPTION;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.DecimalFormat;
import java.util.List;
import javax.imageio.ImageIO;
import oms3.annotations.Author;
import oms3.annotations.Description;
import oms3.annotations.Documentation;
import oms3.annotations.Execute;
import oms3.annotations.In;
import oms3.annotations.Keywords;
import oms3.annotations.Label;
import oms3.annotations.License;
import oms3.annotations.Name;
import oms3.annotations.Status;
import oms3.annotations.UI;
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.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYBarDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
import org.jgrasstools.gears.libs.exceptions.ModelsIllegalargumentException;
import org.jgrasstools.gears.libs.modules.JGTModel;
import org.jgrasstools.gears.utils.files.FileUtilities;
@Description(OMSMATRIXCHARTER_DESCRIPTION)
@Documentation(OMSMATRIXCHARTER_DOCUMENTATION)
@Author(name = OMSMATRIXCHARTER_AUTHORNAMES, contact = OMSMATRIXCHARTER_AUTHORCONTACTS)
@Keywords(OMSMATRIXCHARTER_KEYWORDS)
@Label(OMSMATRIXCHARTER_LABEL)
@Name(OMSMATRIXCHARTER_NAME)
@Status(OMSMATRIXCHARTER_STATUS)
@License(OMSMATRIXCHARTER_LICENSE)
@UI(OMSMATRIXCHARTER_UI)
public class OmsMatrixCharter extends JGTModel {
@Description(OMSMATRIXCHARTER_IN_DATA_DESCRIPTION)
@In
public double[][] inData;
@Description("A list of data to chart, in the case the xy data ar different for each series.")
@In
public List<double[][]> inDataXY;
@Description(OMSMATRIXCHARTER_IN_TITLE_DESCRIPTION)
@In
public String inTitle;
@Description(OMSMATRIXCHARTER_IN_SUBTITLE_DESCRIPTION)
@In
public String inSubTitle;
@Description(OMSMATRIXCHARTER_IN_SERIES_DESCRIPTION)
@In
public String[] inSeries;
@Description(OMSMATRIXCHARTER_IN_COLORS_DESCRIPTION)
@In
public String inColors;
@Description(OMSMATRIXCHARTER_IN_LABELS_DESCRIPTION)
@In
public String[] inLabels;
@Description(OMSMATRIXCHARTER_IN_FORMATS_DESCRIPTION)
@In
public String[] inFormats;
@Description(OMSMATRIXCHARTER_IN_TYPES_DESCRIPTION)
@In
public String[] inTypes;
@Description(OMSMATRIXCHARTER_P_TYPE_DESCRIPTION)
@In
public int pType = 0;
@Description(OMSMATRIXCHARTER_DO_CHART_DESCRIPTION)
@In
public boolean doChart;
@Description(OMSMATRIXCHARTER_DO_DUMP_DESCRIPTION)
@In
public boolean doDump;
@Description(OMSMATRIXCHARTER_DO_LEGEND_DESCRIPTION)
@In
public boolean doLegend;
@Description(OMSMATRIXCHARTER_DO_POINTS_DESCRIPTION)
@In
public boolean doPoints;
@Description(OMSMATRIXCHARTER_DO_CUMULATE_DESCRIPTION)
@In
public boolean doCumulate;
@Description(OMSMATRIXCHARTER_DO_NORMALIZE_DESCRIPTION)
@In
public boolean doNormalize;
@Description("Make chart horizontal.")
@In
public boolean doHorizontal;
@Description(OMSMATRIXCHARTER_P_WIDTH_DESCRIPTION)
@In
public int pWidth = 800;
@Description(OMSMATRIXCHARTER_P_HEIGHT_DESCRIPTION)
@In
public int pHeight = 600;
@Description(OMSMATRIXCHARTER_IN_CHARTPATH_DESCRIPTION)
@In
public String inChartPath;
private double max = Double.NEGATIVE_INFINITY;
private double min = Double.POSITIVE_INFINITY;
private double minInterval = 1000;
@Execute
public void chart() throws Exception {
if (inData == null && inDataXY == null) {
throw new ModelsIllegalargumentException("At least one of the datasets need to be valid.", this, pm);
}
if (doDump) {
checkNull(inChartPath);
}
JFreeChart chart = null;
if (pType == 0) {
chart = doLineChart();
} else {
chart = doBarChart();
}
if (inSubTitle != null) {
TextTitle subTitle = new TextTitle(inSubTitle);
chart.addSubtitle(subTitle);
}
chart.setTextAntiAlias(true);
if (doDump) {
File chartFile = new File(inChartPath);
if (!chartFile.getName().endsWith(".png")) {
chartFile = FileUtilities.substituteExtention(chartFile, "png");
}
BufferedImage bufferedImage = chart.createBufferedImage(pWidth, pHeight);
ImageIO.write(bufferedImage, "png", chartFile);
}
if (doChart) {
ChartPanel cp = new ChartPanel(chart);
cp.setDomainZoomable(true);
cp.setRangeZoomable(true);
ApplicationFrame af = new ApplicationFrame("");
af.setContentPane(cp);
af.setPreferredSize(new Dimension(pWidth, pHeight));
af.pack();
af.setVisible(true);
RefineryUtilities.centerFrameOnScreen(af);
}
}
private XYSeriesCollection getSeriesCollection() {
XYSeriesCollection collection = new XYSeriesCollection();
for( int i = 0; i < inSeries.length; i++ ) {
int col = i + 1;
if (inDataXY != null) {
inData = inDataXY.get(i);
col = 1;
}
String seriesName = inSeries[i];
XYSeries series = new XYSeries(seriesName);
double previous = 0;
double[] x = new double[inData.length];
double[] y = new double[inData.length];
for( int j = 0; j < inData.length; j++ ) {
double value;
if (!doCumulate) {
value = inData[j][col];
} else {
value = previous + inData[j][col];
}
x[j] = inData[j][0];
y[j] = value;
max = Math.max(max, value);
min = Math.min(min, value);
previous = value;
if (j > 1) {
minInterval = Math.min(minInterval, inData[j - 1][0] - inData[j][0]);
}
}
if (doNormalize) {
for( int k = 0; k < y.length; k++ ) {
y[k] = y[k] / max;
}
max = 1.0;
min = 0.0;
}
for( int k = 0; k < y.length; k++ ) {
series.add(x[k], y[k]);
}
collection.addSeries(series);
}
return collection;
}
private JFreeChart doBarChart() {
XYSeriesCollection collection = getSeriesCollection();
XYBarDataset xyBarDataset = new XYBarDataset(collection, minInterval);
PlotOrientation orientation = PlotOrientation.VERTICAL;
if (doHorizontal) {
orientation = PlotOrientation.HORIZONTAL;
}
JFreeChart chart = ChartFactory.createHistogram(inTitle, inLabels[0], inLabels[1], xyBarDataset, orientation, doLegend,
true, false);
XYPlot plot = (XYPlot) chart.getPlot();
plot.setForegroundAlpha(0.85f);
NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
yAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits());
double delta = (max - min) * 0.1;
yAxis.setRange(min, max + delta);
yAxis.setMinorTickCount(4);
yAxis.setMinorTickMarksVisible(true);
if (inFormats != null && inFormats.length > 0 && inFormats[1].trim().length() > 0) {
yAxis.setNumberFormatOverride(new DecimalFormat(inFormats[1]));
}
if (inFormats != null && inFormats.length > 0 && inFormats[0].trim().length() > 0) {
ValueAxis domainAxis = plot.getDomainAxis();
if (domainAxis instanceof NumberAxis) {
NumberAxis xAxis = (NumberAxis) domainAxis;
xAxis.setNumberFormatOverride(new DecimalFormat(inFormats[0]));
}
}
XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer();
renderer.setDrawBarOutline(false);
renderer.setBarPainter(new StandardXYBarPainter());
renderer.setShadowVisible(false);
if (inColors != null) {
String[] colorSplit = inColors.split(";");
for( int i = 0; i < colorSplit.length; i++ ) {
String[] split = colorSplit[i].split(",");
int r = (int) Double.parseDouble(split[0]);
int g = (int) Double.parseDouble(split[1]);
int b = (int) Double.parseDouble(split[2]);
renderer.setSeriesPaint(i, new Color(r, g, b));
}
}
return chart;
}
@SuppressWarnings("deprecation")
private JFreeChart doLineChart() {
XYSeriesCollection collection = getSeriesCollection();
PlotOrientation orientation = PlotOrientation.VERTICAL;
if (doHorizontal) {
orientation = PlotOrientation.HORIZONTAL;
}
JFreeChart chart = ChartFactory.createXYLineChart(inTitle, inLabels[0], inLabels[1], collection, orientation, doLegend,
true, false);
XYPlot plot = (XYPlot) chart.getPlot();
// plot.setDomainGridlinePaint(Color.red);
// plot.setRangeGridlinePaint(Color.cyan);
// plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
XYItemRenderer plotRenderer = plot.getRenderer();
if (plotRenderer instanceof XYLineAndShapeRenderer) {
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plotRenderer;
if (doPoints) {
renderer.setShapesVisible(true);
renderer.setShapesFilled(true);
}
if (inColors != null) {
String[] colorSplit = inColors.split(";");
for( int i = 0; i < colorSplit.length; i++ ) {
String[] split = colorSplit[i].split(",");
int r = (int) Double.parseDouble(split[0]);
int g = (int) Double.parseDouble(split[1]);
int b = (int) Double.parseDouble(split[2]);
renderer.setSeriesPaint(i, new Color(r, g, b));
}
}
}
NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
yAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits());
double delta = (max - min) * 0.1;
yAxis.setRange(min, max + delta);
yAxis.setMinorTickCount(4);
yAxis.setMinorTickMarksVisible(true);
if (inFormats != null && inFormats.length > 1 && inFormats[1].trim().length() > 0) {
yAxis.setNumberFormatOverride(new DecimalFormat(inFormats[1]));
}
if (inFormats != null && inFormats.length > 0 && inFormats[0].trim().length() > 0) {
ValueAxis domainAxis = plot.getDomainAxis();
if (domainAxis instanceof NumberAxis) {
NumberAxis xAxis = (NumberAxis) domainAxis;
xAxis.setNumberFormatOverride(new DecimalFormat(inFormats[0]));
}
}
return chart;
}
}