Added weight chart renderer, infrastructure for more dynamic charts.
This commit is contained in:
parent
cc91f617c9
commit
2858b83a1a
|
@ -2,6 +2,8 @@ package com.github.andrewlalis.running_every_day.data.db;
|
|||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class Queries {
|
||||
|
@ -66,6 +68,18 @@ public final class Queries {
|
|||
return pageCount;
|
||||
}
|
||||
|
||||
public static <T> List<T> findAll(Connection c, String query, ResultSetMapper<T> mapper) throws SQLException {
|
||||
try (var stmt = c.prepareStatement(query)) {
|
||||
try (var rs = stmt.executeQuery()) {
|
||||
List<T> items = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
items.add(mapper.map(rs));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int update(Connection c, String query) throws SQLException {
|
||||
try (var stmt = c.prepareStatement(query)) {
|
||||
return stmt.executeUpdate();
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.github.andrewlalis.running_every_day.view;
|
||||
|
||||
import com.github.andrewlalis.running_every_day.data.db.DataSource;
|
||||
import com.github.andrewlalis.running_every_day.view.chart.ChartRenderingPanel;
|
||||
import com.github.andrewlalis.running_every_day.view.chart.WeightChartRenderer;
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.data.category.DefaultCategoryDataset;
|
||||
|
@ -13,36 +16,18 @@ import java.awt.*;
|
|||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public class ChartsPanel extends JPanel {
|
||||
private final JPanel drawingPanel = new JPanel();
|
||||
private final DataSource dataSource;
|
||||
|
||||
public ChartsPanel() {
|
||||
public ChartsPanel(DataSource dataSource) {
|
||||
super(new BorderLayout());
|
||||
this.dataSource = dataSource;
|
||||
var drawingPanel = new ChartRenderingPanel(new WeightChartRenderer(dataSource));
|
||||
this.add(drawingPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel buttonPanel = new JPanel();
|
||||
JButton drawButton = new JButton("Draw");
|
||||
drawButton.addActionListener(e -> draw());
|
||||
// drawButton.addActionListener(e -> draw());
|
||||
buttonPanel.add(drawButton);
|
||||
this.add(buttonPanel, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
TimeSeriesCollection ds = new TimeSeriesCollection();
|
||||
TimeSeries ts = new TimeSeries("Data");
|
||||
ts.add(new Day(16, 4, 2023), 45);
|
||||
ts.add(new Day(17, 4, 2023), 50);
|
||||
ts.add(new Day(18, 4, 2023), 52);
|
||||
ts.add(new Day(19, 4, 2023), 65);
|
||||
ds.addSeries(ts);
|
||||
|
||||
JFreeChart chart = ChartFactory.createXYLineChart(
|
||||
"Test XY Line Chart",
|
||||
"Date",
|
||||
"Value",
|
||||
ds
|
||||
);
|
||||
Graphics2D g2 = (Graphics2D) drawingPanel.getGraphics();
|
||||
Rectangle2D area = new Rectangle2D.Float(0, 0, drawingPanel.getWidth(), drawingPanel.getHeight());
|
||||
chart.draw(g2, area);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ public class RecorderAppWindow extends JFrame {
|
|||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
tabbedPane.addTab("Run Records", new RunRecordsPanel(dataSource));
|
||||
tabbedPane.addTab("Aggregate Statistics", new AggregateStatisticsPanel(dataSource));
|
||||
tabbedPane.addTab("Charts", new ChartsPanel());
|
||||
tabbedPane.addTab("Charts", new ChartsPanel(dataSource));
|
||||
return tabbedPane;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package com.github.andrewlalis.running_every_day.view.chart;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public interface ChartRenderer {
|
||||
void render(Graphics2D graphics, Rectangle2D area);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.github.andrewlalis.running_every_day.view.chart;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public class ChartRenderingPanel extends JPanel {
|
||||
private final ChartRenderer renderer;
|
||||
|
||||
public ChartRenderingPanel(ChartRenderer renderer) {
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
Rectangle2D area = new Rectangle2D.Float(0, 0, this.getWidth(), this.getHeight());
|
||||
renderer.render(g2, area);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.github.andrewlalis.running_every_day.view.chart;
|
||||
|
||||
import org.jfree.chart.JFreeChart;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public abstract class JFreeChartRenderer implements ChartRenderer {
|
||||
protected abstract JFreeChart getChart() throws Exception;
|
||||
|
||||
@Override
|
||||
public void render(Graphics2D graphics, Rectangle2D area) {
|
||||
try {
|
||||
var chart = getChart();
|
||||
// Apply theme to the chart.
|
||||
chart.draw(graphics, area);
|
||||
} catch (Exception e) {
|
||||
graphics.setColor(Color.BLACK);
|
||||
graphics.setBackground(Color.WHITE);
|
||||
graphics.fill(area);
|
||||
graphics.drawString("Error: " + e.getMessage(), 20, 40);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.github.andrewlalis.running_every_day.view.chart;
|
||||
|
||||
import com.github.andrewlalis.running_every_day.data.db.DataSource;
|
||||
import com.github.andrewlalis.running_every_day.data.db.Queries;
|
||||
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.XYItemRenderer;
|
||||
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
|
||||
import org.jfree.data.time.Day;
|
||||
import org.jfree.data.time.TimeSeries;
|
||||
import org.jfree.data.time.TimeSeriesCollection;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public class WeightChartRenderer extends JFreeChartRenderer {
|
||||
private record WeightDatapoint(int weightGrams, LocalDate date){}
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
public WeightChartRenderer(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JFreeChart getChart() throws Exception {
|
||||
TimeSeries series = new TimeSeries("Weight", "Date", "Weight (Kg)");
|
||||
List<WeightDatapoint> datapoints = Queries.findAll(
|
||||
dataSource.conn(),
|
||||
"SELECT date, weight FROM run ORDER BY date ASC",
|
||||
rs -> new WeightDatapoint(
|
||||
rs.getInt(2),
|
||||
LocalDate.parse(rs.getString(1))
|
||||
)
|
||||
);
|
||||
for (var dp : datapoints) {
|
||||
series.add(new Day(dp.date.getDayOfMonth(), dp.date.getMonthValue(), dp.date.getYear()), dp.weightGrams);
|
||||
}
|
||||
TimeSeriesCollection dataset = new TimeSeriesCollection(series);
|
||||
|
||||
DateAxis domainAxis = new DateAxis("Date");
|
||||
domainAxis.setVerticalTickLabels(true);
|
||||
domainAxis.setDateFormatOverride(DateFormat.getDateInstance());
|
||||
NumberAxis rangeAxis = new NumberAxis("Weight (Kg)");
|
||||
rangeAxis.setAutoRange(true);
|
||||
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
|
||||
|
||||
XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, renderer);
|
||||
|
||||
return new JFreeChart(plot);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue