From 63e67d580739079dd1bf87ed674032d97597bcd3 Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期五, 09 十月 2026 11:27:38 +0800
Subject: [PATCH] docs(交接): 记录 2026 十一假期人员流动量情况 Excel 制作与口径
---
traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java | 96 +++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 90 insertions(+), 6 deletions(-)
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java
index 0f06317..a7c8e83 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java
@@ -103,6 +103,23 @@
public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis,
int valueDecimals, int width, int height) {
+ return lineChart(yAxisLabel, series, percentAxis, valueDecimals, DAY_LABELS, width, height);
+ }
+
+ public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis,
+ int valueDecimals, List<String> dayLabels, int width, int height) {
+ return lineChart(yAxisLabel, series, percentAxis, valueDecimals, dayLabels, width, height,
+ Collections.emptyList());
+ }
+
+ /**
+ * Renders a line chart and pins the listed labels to a fixed anchor. The overrides are
+ * opt-in: a chart that passes an empty list keeps the automatic placement unchanged.
+ */
+ public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis,
+ int valueDecimals, List<String> dayLabels, int width, int height,
+ List<LineLabelOverride> labelOverrides) {
+ List<String> axisLabels = (dayLabels == null || dayLabels.isEmpty()) ? DAY_LABELS : dayLabels;
XYSeriesCollection dataset = new XYSeriesCollection();
for (int seriesIndex = 0; seriesIndex < series.size(); seriesIndex++) {
ChartSeries item = series.get(seriesIndex);
@@ -132,7 +149,7 @@
plot.setRangeGridlinesVisible(false);
plot.setDomainTickBandPaint(null);
plot.setRangeTickBandPaint(null);
- SymbolAxis domainAxis = new SymbolAxis(null, DAY_LABELS.toArray(new String[0]));
+ SymbolAxis domainAxis = new SymbolAxis(null, axisLabels.toArray(new String[0]));
domainAxis.setGridBandPaint(new Color(0, 0, 0, 0));
domainAxis.setGridBandAlternatePaint(new Color(0, 0, 0, 0));
domainAxis.setLowerMargin(0.04);
@@ -175,7 +192,7 @@
}
plot.setRenderer(renderer);
return render(chart, width, scaleLineChartHeight(height),
- yAxisLabel, true, percentAxis, valueDecimals);
+ yAxisLabel, true, percentAxis, valueDecimals, labelOverrides);
}
public byte[] pieChart(String valueSuffix, List<ChartCategory> categories,
@@ -455,6 +472,13 @@
private byte[] render(JFreeChart chart, int width, int height, String unitLabel,
boolean axisChart, boolean percentItemLabels, int valueDecimals) {
+ return render(chart, width, height, unitLabel, axisChart, percentItemLabels,
+ valueDecimals, Collections.emptyList());
+ }
+
+ private byte[] render(JFreeChart chart, int width, int height, String unitLabel,
+ boolean axisChart, boolean percentItemLabels, int valueDecimals,
+ List<LineLabelOverride> labelOverrides) {
try {
reserveUnitLabelBand(chart, axisChart, unitLabel);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -474,7 +498,8 @@
drawDateLabel(graphics, info.getPlotInfo().getDataArea());
}
drawXYItemLabels(graphics, image, chart,
- info.getPlotInfo().getDataArea(), percentItemLabels, unitLabel, valueDecimals);
+ info.getPlotInfo().getDataArea(), percentItemLabels, unitLabel, valueDecimals,
+ labelOverrides);
drawStackedBarLabels(graphics, image, chart,
info.getPlotInfo().getDataArea(), percentItemLabels);
} finally {
@@ -890,7 +915,8 @@
private void drawXYItemLabels(Graphics2D graphics, BufferedImage image, JFreeChart chart,
Rectangle2D dataArea, boolean percentItemLabels,
- String unitLabel, int valueDecimals) {
+ String unitLabel, int valueDecimals,
+ List<LineLabelOverride> labelOverrides) {
if (!(chart.getPlot() instanceof XYPlot) || dataArea == null
|| dataArea.getWidth() <= 0 || dataArea.getHeight() <= 0) {
return;
@@ -932,7 +958,8 @@
List<List<LineLabelCandidate>> candidateSets = new ArrayList<>();
for (DayLabel label : labels) {
List<LineLabelCandidate> candidates = lineLabelCandidates(
- label, metrics, dataArea);
+ label, metrics, dataArea,
+ forcedAnchor(labelOverrides, label.getSeriesIndex(), itemIndex));
if (candidates.isEmpty()) {
candidateSets.clear();
break;
@@ -1051,7 +1078,8 @@
}
private List<LineLabelCandidate> lineLabelCandidates(DayLabel label, FontMetrics metrics,
- Rectangle2D dataArea) {
+ Rectangle2D dataArea,
+ LineLabelAnchor forcedAnchor) {
List<LineLabelCandidate> result = new ArrayList<>();
int width = metrics.stringWidth(label.getText());
int height = metrics.getHeight();
@@ -1061,6 +1089,25 @@
double farHorizontalOffset = horizontalOffset + 18.0;
double verticalOffset = ITEM_LABEL_GAP + height / 2.0;
double farVerticalOffset = verticalOffset + height + 6.0;
+ if (forcedAnchor != null) {
+ switch (forcedAnchor) {
+ case ABOVE:
+ addLabelCandidate(result, x, y, width, height, 0.0, -verticalOffset, dataArea);
+ break;
+ case BELOW:
+ addLabelCandidate(result, x, y, width, height, 0.0, verticalOffset, dataArea);
+ break;
+ case LEFT:
+ addLabelCandidate(result, x, y, width, height, -horizontalOffset, 0.0, dataArea);
+ break;
+ case RIGHT:
+ addLabelCandidate(result, x, y, width, height, horizontalOffset, 0.0, dataArea);
+ break;
+ default:
+ break;
+ }
+ return result;
+ }
addLabelCandidate(result, x, y, width, height, 0.0, -verticalOffset, dataArea);
addLabelCandidate(result, x, y, width, height, 0.0, verticalOffset, dataArea);
addLabelCandidate(result, x, y, width, height, horizontalOffset, 0.0, dataArea);
@@ -1104,6 +1151,43 @@
return result;
}
+ /**
+ * Fixed positions a data label can be pinned to, relative to its own point.
+ */
+ public enum LineLabelAnchor {
+ ABOVE, BELOW, LEFT, RIGHT
+ }
+
+ /**
+ * Opt-in request that pins one data label (series + item) to a fixed anchor.
+ */
+ public static class LineLabelOverride {
+ private final int seriesIndex;
+ private final int itemIndex;
+ private final LineLabelAnchor anchor;
+
+ public LineLabelOverride(int seriesIndex, int itemIndex, LineLabelAnchor anchor) {
+ this.seriesIndex = seriesIndex;
+ this.itemIndex = itemIndex;
+ this.anchor = anchor;
+ }
+
+ public int getSeriesIndex() { return seriesIndex; }
+ public int getItemIndex() { return itemIndex; }
+ public LineLabelAnchor getAnchor() { return anchor; }
+ }
+
+ private static LineLabelAnchor forcedAnchor(List<LineLabelOverride> labelOverrides,
+ int seriesIndex, int itemIndex) {
+ if (labelOverrides == null) return null;
+ for (LineLabelOverride override : labelOverrides) {
+ if (override.getSeriesIndex() == seriesIndex && override.getItemIndex() == itemIndex) {
+ return override.getAnchor();
+ }
+ }
+ return null;
+ }
+
private void addLabelCandidate(List<LineLabelCandidate> result, double x, double y,
int width, int height, double dx, double dy,
Rectangle2D dataArea) {
--
Gitblit v1.9.1