当使用XYSplineRender作为渲染时,JFreeChart设置AutoRange [英] JFreeChart setAutoRange when XYSplineRenderer is used as rendered
问题描述
图表需要绘制为具有平滑形状,因此使用XYSplineRender。 此外,NumberAxis需要自动调整到数据段的范围。
但在某些情况下,当计算样条线时,某些样条值不在自动范围段之外,并且曲线不完全绘制。
似乎是在计算样条线之前计算自动范围。
为了缓解它,我调整了垂直轴的范围,将此范围增加了范围限制的百分比。但这会导致对图表的曲线拟合不准确,因为根据数据输入的不同,百分比可能高达25%。
double percentOverRange = 0.05;//2%
double initalRange = series.getMaxY() - series.getMinY();
double increase = initalRange*percentOverRange;
verticalAxis.setRange(new Range(series.getMinY()-increase, series.getMaxY()+increase));
此代码创建了上图,并演示了曲线如何不完全绘制在前两个数据点之间。 请注意,域轴是DateAxis(每日数据),在周末不带值
public class MyPlotChart {
private static Color MetalColor = new Color(255, 152, 0);
static double[] yData = new double[] { 0.67, 0.67, 0.69, 0.70, 0.70, 0.71, 0.71 };
static String[] labels = new String[] { "2021-11-09", "2021-11-10", "2021-11-11", "2021-11-12", "2021-11-15", "2021-11-16", "2021-11-17" };
public static void plot(String metal, int samples) throws IOException, ParseException {
SimpleDateFormat dateformatyyyy_MM_dd = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dateformatdd_MM_yyyy = new SimpleDateFormat("dd-MM-yyyy");
XYSeries series = new XYSeries(metal);
for (int i = 0; i < yData.length; i++) {
Date date = dateformatyyyy_MM_dd.parse(labels[i]);
series.add(date.getTime(), yData[i]);
}
//Configure Vertical Axis
NumberAxis verticalAxis = new NumberAxis(null);
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
numberFormat.setRoundingMode(RoundingMode.HALF_DOWN);
numberFormat.setMinimumFractionDigits(2);
numberFormat.setMaximumFractionDigits(2);
double vericalTickUnit = (series.getMaxY() - series.getMinY()) / 7;
NumberTickUnit nt = new NumberTickUnit(vericalTickUnit, numberFormat);
verticalAxis.setTickUnit(nt);
double percentOverRange = 0.05;// 2%
double initalRange = series.getMaxY() - series.getMinY();
double increase = initalRange * percentOverRange;
verticalAxis.setRange(new Range(series.getMinY()-increase, series.getMaxY()+increase));
verticalAxis.setAutoRange(true);
verticalAxis.setAutoRangeIncludesZero(false);
verticalAxis.setTickMarksVisible(true);
verticalAxis.setTickMarkInsideLength(3f);
//Configure Domain Axis
DateAxis domainAxis = new DateAxis(null);
domainAxis.setTickUnit(new DateTickUnit(DateTickUnitType.DAY, 1, dateformatdd_MM_yyyy));
//Configure Renderer
XYSplineRenderer r = new XYSplineRenderer(10);
r.setSeriesPaint(0, MetalColor);
r.setDefaultShapesVisible(true);
r.setSeriesStroke(0, new BasicStroke(3.0f));
XYDataset dataset = new XYSeriesCollection(series);
XYPlot xyplot = new XYPlot(dataset, domainAxis, verticalAxis, r);
xyplot.getDomainAxis().setVerticalTickLabels(true);
xyplot.setDomainGridlinesVisible(false);
xyplot.setBackgroundImage(null);
xyplot.setBackgroundPaint(Color.WHITE);
Font font = xyplot.getDomainAxis().getTickLabelFont();
Font fontnew = new Font(font.getName(), Font.BOLD, 14);
xyplot.getDomainAxis().setTickLabelFont(fontnew);
xyplot.getRangeAxis().setTickLabelFont(fontnew);
JFreeChart chart = new JFreeChart(xyplot);
chart.removeLegend();// Remove legend
chart.setBackgroundPaint(Color.WHITE);
String fileName = "myChart" + metal + samples + "TEST.png";
ChartUtils.saveChartAsPNG(new File(fileName), chart, 600, 600);
}
public static void main(String[] args) throws IOException, ParseException {
MyPlotChart.plot("metal", 7);
}
}
编辑
以下绘图来自上面的代码,只是更改了XYSplineRender的精度。
如javadoc中所定义:
XYSplineReneller:将数据点与自然数据连接的呈现器 在每个数据点使用三次样条线和/或绘制形状。PUBLIC XYSplineRenender(int精度)
创建具有指定精度的新呈现器,并且不填充 样条线下的区域(在‘0’和之间)。参数: 精度-数据项之间的点数。
这意味着自然三次样条线是根据数据点计算的。
另一方面,精度用于定义每对数据点之间的插值点数量。
精度=N-1,其中N=每个数据点段之间的插值点数量
我只能看到两个选项:
- XYSplineRender应该有一个返回自然三次样条线集的方法,这样就可以计算每段的最大值,从而可以相应地设置AutoRange
- JFree Chart应该实现基于NURBS(非均匀有理基样条线)的呈现器,它通过一组控制点(see)控制曲线的形状,而不是自然三次样条线。
编辑%2
当没有可用的数据(周末)并且DateAxis在星期五和星期一之间插入两天时,问题会加剧:值之间的差距更大,因此样条线也更长。
推荐答案
如JFreeChart adding trend-line outside of actual values中所述,对于不是严格单调的函数,这种异常是不可避免的。如果没有更详细的样条线控制,您可以通过在有问题的轴上启用自动范围(默认设置)并根据经验调整轴边距来获得更好的结果。
rangeAxis.setAutoRange(true); // true by default
rangeAxis.setLowerMargin(0.08); // 8% lower margin
在下面的变体中,请注意以下事项:
setLowerMargin()
将范围轴的下边距减少轴范围的8%。setNumberFormatOverride()
和setDateFormatOverride
用于设置刻度标签的格式。XYSplineRenderer
由15的任意precision
构成。deriveFont()
用于更改轴刻度标签字体属性。
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.io.IOException;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYSplineRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* @see https://stackoverflow.com/q/70021577/230513
*/
public class SplineTest {
public static void main(String[] args) throws ParseException, IOException {
SimpleDateFormat formatyyyy_MM_dd = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat formatdd_MM_yyyy = new SimpleDateFormat("dd-MM-yyyy");
double[] yData = new double[]{0.67, 0.67, 0.69, 0.70, 0.70, 0.71, 0.71};
String[] labels = new String[]{"2021-11-09", "2021-11-10", "2021-11-11",
"2021-11-12", "2021-11-15", "2021-11-16", "2021-11-17"};
XYSeries series = new XYSeries("Series");
for (int i = 0; i < yData.length; i++) {
Date date = formatyyyy_MM_dd.parse(labels[i]);
series.add(date.getTime(), yData[i]);
}
NumberAxis rangeAxis = new NumberAxis(null);
NumberFormat numberFormat = NumberFormat.getInstance();
numberFormat.setRoundingMode(RoundingMode.HALF_DOWN);
numberFormat.setMinimumFractionDigits(2);
numberFormat.setMaximumFractionDigits(2);
rangeAxis.setNumberFormatOverride(numberFormat);
rangeAxis.setTickMarksVisible(true);
rangeAxis.setAutoRange(true); // true by default
rangeAxis.setLowerMargin(0.08); // 8% lower margin
rangeAxis.setAutoRangeIncludesZero(false);
DateAxis domainAxis = new DateAxis(null);
domainAxis.setDateFormatOverride(formatdd_MM_yyyy);
domainAxis.setVerticalTickLabels(true);
Font font = domainAxis.getTickLabelFont().deriveFont(Font.BOLD, 10);
domainAxis.setTickLabelFont(font);
rangeAxis.setTickLabelFont(font);
XYSplineRenderer r = new XYSplineRenderer(15);
r.setSeriesPaint(0, new Color(255, 152, 0));
r.setDefaultShapesVisible(true);
r.setSeriesStroke(0, new BasicStroke(3.0f));
XYDataset dataset = new XYSeriesCollection(series);
XYPlot xyplot = new XYPlot(dataset, domainAxis, rangeAxis, r);
xyplot.setDomainGridlinesVisible(false);
xyplot.setBackgroundImage(null);
xyplot.setBackgroundPaint(Color.WHITE);
JFreeChart chart = new JFreeChart(null, null, xyplot, false);
chart.setBackgroundPaint(Color.WHITE);
ChartUtils.saveChartAsPNG(new File("temp.png"), chart, 400, 300);
}
}
这篇关于当使用XYSplineRender作为渲染时,JFreeChart设置AutoRange的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!