Showing
1 changed file
with
81 additions
and
0 deletions
| @@ -355,6 +355,57 @@ class ChartToSVGConverter: | @@ -355,6 +355,57 @@ class ChartToSVGConverter: | ||
| 355 | 355 | ||
| 356 | return colors | 356 | return colors |
| 357 | 357 | ||
| 358 | + def _align_labels_and_data( | ||
| 359 | + self, | ||
| 360 | + labels: Any, | ||
| 361 | + dataset_data: Any, | ||
| 362 | + chart_type: str, | ||
| 363 | + require_positive_sum: bool = False | ||
| 364 | + ) -> Tuple[List[str], List[float]]: | ||
| 365 | + """ | ||
| 366 | + 对齐类别型图表的标签与数据长度,并清理非数值值。 | ||
| 367 | + | ||
| 368 | + Matplotlib的饼图/圆环图要求labels与数据长度一致,否则会抛出错误。 | ||
| 369 | + """ | ||
| 370 | + original_label_len = len(labels) if isinstance(labels, list) else 0 | ||
| 371 | + original_data_len = len(dataset_data) if isinstance(dataset_data, list) else 0 | ||
| 372 | + | ||
| 373 | + aligned_labels = [str(label) for label in labels] if isinstance(labels, list) else [] | ||
| 374 | + raw_data = dataset_data if isinstance(dataset_data, list) else [] | ||
| 375 | + | ||
| 376 | + cleaned_data: List[float] = [] | ||
| 377 | + for value in raw_data: | ||
| 378 | + try: | ||
| 379 | + numeric = float(value) if value is not None else 0.0 | ||
| 380 | + except (TypeError, ValueError): | ||
| 381 | + numeric = 0.0 | ||
| 382 | + if numeric < 0: | ||
| 383 | + numeric = 0.0 | ||
| 384 | + cleaned_data.append(numeric) | ||
| 385 | + | ||
| 386 | + target_len = max(len(aligned_labels), len(cleaned_data)) | ||
| 387 | + if target_len == 0: | ||
| 388 | + return [], [] | ||
| 389 | + | ||
| 390 | + if len(aligned_labels) < target_len: | ||
| 391 | + start = len(aligned_labels) | ||
| 392 | + aligned_labels.extend([f"未命名{start + idx + 1}" for idx in range(target_len - start)]) | ||
| 393 | + | ||
| 394 | + if len(cleaned_data) < target_len: | ||
| 395 | + cleaned_data.extend([0.0] * (target_len - len(cleaned_data))) | ||
| 396 | + | ||
| 397 | + if original_label_len != original_data_len: | ||
| 398 | + logger.warning( | ||
| 399 | + f"{chart_type}图labels长度({original_label_len})与data长度({original_data_len})不一致," | ||
| 400 | + f"已对齐为{target_len}" | ||
| 401 | + ) | ||
| 402 | + | ||
| 403 | + if require_positive_sum and not any(value > 0 for value in cleaned_data): | ||
| 404 | + logger.warning(f"{chart_type}图数据为空,跳过渲染") | ||
| 405 | + return [], [] | ||
| 406 | + | ||
| 407 | + return aligned_labels[:target_len], cleaned_data[:target_len] | ||
| 408 | + | ||
| 358 | def _figure_to_svg(self, fig: Any) -> str: | 409 | def _figure_to_svg(self, fig: Any) -> str: |
| 359 | """ | 410 | """ |
| 360 | 将matplotlib图表转换为SVG字符串 | 411 | 将matplotlib图表转换为SVG字符串 |
| @@ -705,6 +756,16 @@ class ChartToSVGConverter: | @@ -705,6 +756,16 @@ class ChartToSVGConverter: | ||
| 705 | dataset = datasets[0] | 756 | dataset = datasets[0] |
| 706 | dataset_data = dataset.get('data', []) | 757 | dataset_data = dataset.get('data', []) |
| 707 | 758 | ||
| 759 | + labels, dataset_data = self._align_labels_and_data( | ||
| 760 | + labels, | ||
| 761 | + dataset_data, | ||
| 762 | + chart_type="饼", | ||
| 763 | + require_positive_sum=True | ||
| 764 | + ) | ||
| 765 | + | ||
| 766 | + if not labels or not dataset_data: | ||
| 767 | + return None | ||
| 768 | + | ||
| 708 | title = props.get('title') | 769 | title = props.get('title') |
| 709 | fig, ax = self._create_figure(width, height, dpi, title) | 770 | fig, ax = self._create_figure(width, height, dpi, title) |
| 710 | 771 | ||
| @@ -764,6 +825,16 @@ class ChartToSVGConverter: | @@ -764,6 +825,16 @@ class ChartToSVGConverter: | ||
| 764 | dataset = datasets[0] | 825 | dataset = datasets[0] |
| 765 | dataset_data = dataset.get('data', []) | 826 | dataset_data = dataset.get('data', []) |
| 766 | 827 | ||
| 828 | + labels, dataset_data = self._align_labels_and_data( | ||
| 829 | + labels, | ||
| 830 | + dataset_data, | ||
| 831 | + chart_type="圆环", | ||
| 832 | + require_positive_sum=True | ||
| 833 | + ) | ||
| 834 | + | ||
| 835 | + if not labels or not dataset_data: | ||
| 836 | + return None | ||
| 837 | + | ||
| 767 | title = props.get('title') | 838 | title = props.get('title') |
| 768 | fig, ax = self._create_figure(width, height, dpi, title) | 839 | fig, ax = self._create_figure(width, height, dpi, title) |
| 769 | 840 | ||
| @@ -941,6 +1012,16 @@ class ChartToSVGConverter: | @@ -941,6 +1012,16 @@ class ChartToSVGConverter: | ||
| 941 | dataset = datasets[0] | 1012 | dataset = datasets[0] |
| 942 | dataset_data = dataset.get('data', []) | 1013 | dataset_data = dataset.get('data', []) |
| 943 | 1014 | ||
| 1015 | + labels, dataset_data = self._align_labels_and_data( | ||
| 1016 | + labels, | ||
| 1017 | + dataset_data, | ||
| 1018 | + chart_type="极地区域", | ||
| 1019 | + require_positive_sum=False | ||
| 1020 | + ) | ||
| 1021 | + | ||
| 1022 | + if not labels or not dataset_data: | ||
| 1023 | + return None | ||
| 1024 | + | ||
| 944 | title = props.get('title') | 1025 | title = props.get('title') |
| 945 | fig = plt.figure(figsize=(width/dpi, height/dpi), dpi=dpi) | 1026 | fig = plt.figure(figsize=(width/dpi, height/dpi), dpi=dpi) |
| 946 | ax = fig.add_subplot(111, projection='polar') | 1027 | ax = fig.add_subplot(111, projection='polar') |
-
Please register or login to post a comment