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.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class Queries {
|
public final class Queries {
|
||||||
|
@ -66,6 +68,18 @@ public final class Queries {
|
||||||
return pageCount;
|
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 {
|
public static int update(Connection c, String query) throws SQLException {
|
||||||
try (var stmt = c.prepareStatement(query)) {
|
try (var stmt = c.prepareStatement(query)) {
|
||||||
return stmt.executeUpdate();
|
return stmt.executeUpdate();
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package com.github.andrewlalis.running_every_day.view;
|
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.ChartFactory;
|
||||||
import org.jfree.chart.JFreeChart;
|
import org.jfree.chart.JFreeChart;
|
||||||
import org.jfree.data.category.DefaultCategoryDataset;
|
import org.jfree.data.category.DefaultCategoryDataset;
|
||||||
|
@ -13,36 +16,18 @@ import java.awt.*;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
|
||||||
public class ChartsPanel extends JPanel {
|
public class ChartsPanel extends JPanel {
|
||||||
private final JPanel drawingPanel = new JPanel();
|
private final DataSource dataSource;
|
||||||
|
|
||||||
public ChartsPanel() {
|
public ChartsPanel(DataSource dataSource) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
var drawingPanel = new ChartRenderingPanel(new WeightChartRenderer(dataSource));
|
||||||
this.add(drawingPanel, BorderLayout.CENTER);
|
this.add(drawingPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
JPanel buttonPanel = new JPanel();
|
JPanel buttonPanel = new JPanel();
|
||||||
JButton drawButton = new JButton("Draw");
|
JButton drawButton = new JButton("Draw");
|
||||||
drawButton.addActionListener(e -> draw());
|
// drawButton.addActionListener(e -> draw());
|
||||||
buttonPanel.add(drawButton);
|
buttonPanel.add(drawButton);
|
||||||
this.add(buttonPanel, BorderLayout.NORTH);
|
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();
|
JTabbedPane tabbedPane = new JTabbedPane();
|
||||||
tabbedPane.addTab("Run Records", new RunRecordsPanel(dataSource));
|
tabbedPane.addTab("Run Records", new RunRecordsPanel(dataSource));
|
||||||
tabbedPane.addTab("Aggregate Statistics", new AggregateStatisticsPanel(dataSource));
|
tabbedPane.addTab("Aggregate Statistics", new AggregateStatisticsPanel(dataSource));
|
||||||
tabbedPane.addTab("Charts", new ChartsPanel());
|
tabbedPane.addTab("Charts", new ChartsPanel(dataSource));
|
||||||
return tabbedPane;
|
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