在JFreeChart中生成平滑温湿度曲线:XYBezierRenderer效果不佳求解决方案
温湿度数据曲线绘制问题(JFreeChart)
我尝试用JFreeChart绘制温湿度数据图表,最初使用XYSplineRenderer时出现了越界问题。改用XYBezierRenderer后,即使设置precision=5、tension=0.05,曲线仍存在尖锐转折,效果不尽人意。
我试过调整precision和tension的各类参数,最终XYBezierRenderer(5,0.05)时效果稍好,但还是不理想。另外测试过自定义类XYCardinalSplineRenderer(设置tension=0.05、segments=16)效果稍好,但该类并未包含在JFreeChart官方包中,恳请提供解决方案。
创建图表的代码
private JFreeChart createMultiAxisChart(List<EntityAuditDto> reportsData, Map<String, String> attributes, TemperatureStatistics temperatureStats, HumidityStatistics humidityStatistics) { List<EntityAuditDto> reportData = generateDummyData(); XYDataset temperatureDataset = createMultiTemperatureDataset(reportData); JFreeChart chart = ChartFactory.createTimeSeriesChart( "Temperature & Humidity", "Time", "Temperature (°C)", temperatureDataset,true,true,false); XYPlot plot = chart.getXYPlot(); plot.setBackgroundPaint(Color.WHITE); plot.setDomainGridlinesVisible(true); plot.setRangeGridlinesVisible(true); plot.setDomainGridlinePaint(Color.GRAY); plot.setRangeGridlinePaint(Color.GRAY); Font axisLabelFont = new Font("SansSerif", Font.BOLD, 12); Font tickLabelFont = new Font("SansSerif", Font.BOLD, 10); Color axisLabelColor = Color.BLACK; DateAxis dateAxis = new DateAxis("Time"); dateAxis.setDateFormatOverride(new SimpleDateFormat("yyyy-MM-dd")); dateAxis.setAutoTickUnitSelection(true); dateAxis.setAutoRange(true); dateAxis.setLabelFont(axisLabelFont); dateAxis.setTickLabelFont(tickLabelFont); dateAxis.setLabelPaint(axisLabelColor); dateAxis.setTickLabelPaint(axisLabelColor); plot.setDomainAxis(dateAxis); XYBezierRenderer tempRenderer = new XYBezierRenderer(5,0.05); for (int i = 0; i < temperatureDataset.getSeriesCount(); i++) { tempRenderer.setSeriesPaint(i, getSeriesColor(i)); tempRenderer.setSeriesShapesVisible(i, false); } plot.setRenderer(0, tempRenderer); NumberAxis tempAxis = (NumberAxis) plot.getRangeAxis(); tempAxis.setLabelFont(axisLabelFont); tempAxis.setTickLabelFont(tickLabelFont); tempAxis.setLabelPaint(axisLabelColor); tempAxis.setTickLabelPaint(axisLabelColor); double minTemperature = calculateMinTemperature(temperatureStats); double maxTemperature = calculateMaxTemperature(temperatureStats); tempAxis.setRange(0, 100); tempAxis.setAutoRangeIncludesZero(false); tempAxis.setLowerMargin(0.05); tempAxis.setUpperMargin(0.05); XYDataset humidityDataset = createMultiHumidityDataset(reportData); NumberAxis humidityAxis = new NumberAxis("Humidity (%)"); double minHumidity = calculateMinHumidity(humidityStatistics); double maxHumidity = calculateMaxHumidity(humidityStatistics); humidityAxis.setLabelFont(axisLabelFont); humidityAxis.setTickLabelFont(tickLabelFont); humidityAxis.setLabelPaint(axisLabelColor); humidityAxis.setTickLabelPaint(axisLabelColor); humidityAxis.setRange(0, 100); humidityAxis.setTickUnit(new NumberTickUnit(10)); humidityAxis.setUpperMargin(0.05); humidityAxis.setLowerMargin(0.05); humidityAxis.setAutoRangeIncludesZero(false); plot.setRangeAxis(1, humidityAxis); plot.setDataset(1, humidityDataset); plot.mapDatasetToRangeAxis(1, 1); XYBezierRenderer humidityRenderer = new XYBezierRenderer(5,0.05); for (int i = 0; i < humidityDataset.getSeriesCount(); i++) { humidityRenderer.setSeriesPaint(i, getSeriesColor(i + temperatureDataset.getSeriesCount())); humidityRenderer.setSeriesShapesVisible(i, false); } plot.setRenderer(1, humidityRenderer); LegendItemCollection legendItems = new LegendItemCollection(); XYItemRenderer renderer0 = plot.getRenderer(0); XYItemRenderer renderer1 = plot.getRenderer(1); for (int i = 0; i < temperatureDataset.getSeriesCount(); i++) { Paint seriesPaint = renderer0.getSeriesPaint(i); LegendItem legendItem = new LegendItem( temperatureDataset.getSeriesKey(i).toString(), "", "", "", new Rectangle(10, 10), seriesPaint ); legendItem.setLabelPaint(seriesPaint); legendItems.add(legendItem); } for (int i = 0; i < humidityDataset.getSeriesCount(); i++) { Paint seriesPaint = renderer1.getSeriesPaint(i); LegendItem legendItem = new LegendItem( humidityDataset.getSeriesKey(i).toString(), "", "", "", new Rectangle(10, 10), seriesPaint ); legendItem.setLabelPaint(seriesPaint); legendItems.add(legendItem); } plot.setFixedLegendItems(legendItems); return chart; }
模拟数据生成代码
private List<EntityAuditDto> generateDummyData() { List<EntityAuditDto> dummyData = new ArrayList<>(); Random random = new Random(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_YEAR, -30); // Start 30 days ago // Generate data every 7.5 hours (96 points over 30 days) int hoursPerReading = 7; int minutesPerReading = 30; for (int i = 0; i < 96; i++) { EntityAuditDto dto = new EntityAuditDto(); // Generate timestamp calendar.add(Calendar.HOUR, hoursPerReading); calendar.add(Calendar.MINUTE, minutesPerReading); dto.setDateTime(dateFormat.format(calendar.getTime())); // Generate data with realistic variations double timeProgress = i / 95.0; // Normalized time progress (0 to 1) double dailyCycle = Math.sin(2 * Math.PI * timeProgress * 4); // 4 complete cycles over the period double weeklyCycle = Math.sin(2 * Math.PI * timeProgress * (4.0/7)); // 4/7 cycles (4 weeks) over the period // Generate values for each sensor within specified ranges double t1Base = 55 + 35 * dailyCycle + 5 * weeklyCycle; // 10 to 100 double t2Base = 45 + 10 * dailyCycle + 3 * weeklyCycle; // 30 to 60 double t3Base = 15 + 10 * dailyCycle + 3 * weeklyCycle; // 0 to 30 double h1Base = 75 + 20 * dailyCycle + 3 * weeklyCycle; // 50 to 100 double h2Base = 35 + 10 * dailyCycle + 3 * weeklyCycle; // 20 to 50 // Add random noise and ensure values are within specified ranges dto.setT1(String.format("%.2f", clamp(t1Base + random.nextGaussian() * 3, 10, 100))); dto.setT2(String.format("%.2f", clamp(t2Base + random.nextGaussian() * 2, 30, 60))); dto.setT3(String.format("%.2f", clamp(t3Base + random.nextGaussian() * 2, 0, 30))); dto.setH1(String.format("%.2f", clamp(h1Base + random.nextGaussian() * 3, 50, 100))); dto.setH2(String.format("%.2f", clamp(h2Base + random.nextGaussian() * 2, 20, 50))); dummyData.add(dto); } return dummyData; } // Helper method to clamp values between min and max private double clamp(double value, double min, double max) { return Math.max(min, Math.min(max, value)); }
当前图表效果

内容的提问来源于stack exchange,提问作者Kkv
相关产品推荐
相关产品推荐

