import json import os def normalize_points(raw_points): """Return route points as [(x, y), ...], dropping malformed entries.""" points = [] if not isinstance(raw_points, list): return points for point in raw_points: if not isinstance(point, (list, tuple)) or len(point) < 2: continue try: points.append((float(point[0]), float(point[1]))) except (TypeError, ValueError): continue return points def _looks_like_points(raw_value): if not isinstance(raw_value, list) or not raw_value: return False first = raw_value[0] if not isinstance(first, (list, tuple)) or len(first) < 2: return False try: float(first[0]) float(first[1]) except (TypeError, ValueError): return False return True def normalize_route(raw_route, fallback_name=""): """Normalize {"name": ..., "points": ...} into a route dict.""" if raw_route is None: return None if isinstance(raw_route, dict): name = str(raw_route.get("name") or fallback_name or "未命名路线").strip() raw_points = ( raw_route.get("points") or raw_route.get("waypoints") or raw_route.get("path") or [] ) else: name = str(fallback_name or "未命名路线").strip() raw_points = raw_route points = normalize_points(raw_points) if not points: return None return {"name": name or fallback_name or "未命名路线", "points": points} def normalize_route_list(raw_routes, fallback_prefix): routes = [] if raw_routes is None: return routes if _looks_like_points(raw_routes): route = normalize_route(raw_routes, fallback_prefix) return [route] if route else [] if isinstance(raw_routes, dict): raw_routes = raw_routes.get("routes") or raw_routes.get("items") or [] if not isinstance(raw_routes, list): return routes for idx, raw_route in enumerate(raw_routes, start=1): route = normalize_route(raw_route, f"{fallback_prefix}{idx}") if route: routes.append(route) return routes def normalize_route_profile(data, fallback_name=""): if not isinstance(data, dict): raise ValueError("路线方案 JSON 必须是对象格式") name = str(data.get("name") or fallback_name or "未命名方案").strip() patrol_routes = normalize_route_list(data.get("patrol_routes"), "巡逻路线") if not patrol_routes: raise ValueError("路线方案必须包含至少一条 patrol_routes") return { "name": name or fallback_name or "未命名方案", "prepare_route": normalize_route(data.get("prepare_route"), "准备路线"), "patrol_routes": patrol_routes, "resurrection_routes": normalize_route_list(data.get("resurrection_routes"), "复活路线"), "vendor_route": normalize_route(data.get("vendor_route"), "修理商路线"), "mailbox_route": normalize_route(data.get("mailbox_route"), "邮箱路线"), } def load_route_profile(path): with open(path, "r", encoding="utf-8") as f: data = json.load(f) fallback_name = os.path.splitext(os.path.basename(path))[0] return normalize_route_profile(data, fallback_name=fallback_name)