fenghantong
Deploy RouteOpt Agent
ffda755
Raw
History Blame Contribute Delete
2.57 kB
from __future__ import annotations
import html
from .models import GeoPoint
def build_route_svg(points: list[GeoPoint], route_indices: list[int]) -> str:
if not points or not route_indices:
return ""
width = 760
height = 420
padding = 46
lats = [point.lat for point in points]
lons = [point.lon for point in points]
min_lat, max_lat = min(lats), max(lats)
min_lon, max_lon = min(lons), max(lons)
lat_span = max(max_lat - min_lat, 0.001)
lon_span = max(max_lon - min_lon, 0.001)
def project(point: GeoPoint) -> tuple[float, float]:
x = padding + (point.lon - min_lon) / lon_span * (width - 2 * padding)
y = height - padding - (point.lat - min_lat) / lat_span * (height - 2 * padding)
return x, y
projected = [project(point) for point in points]
route_points = [projected[idx] for idx in route_indices]
polyline = " ".join(f"{x:.1f},{y:.1f}" for x, y in route_points)
marker_svg = []
first_visit_number: dict[int, int] = {}
for order, idx in enumerate(route_indices, start=1):
first_visit_number.setdefault(idx, order)
for idx, point in enumerate(points):
x, y = projected[idx]
order = first_visit_number.get(idx, idx + 1)
is_start = idx == 0
fill = "#0f766e" if is_start else "#2563eb"
marker_svg.append(
f"""
<g>
<circle cx="{x:.1f}" cy="{y:.1f}" r="15" fill="{fill}" stroke="#ffffff" stroke-width="3"/>
<text x="{x:.1f}" y="{y + 4:.1f}" text-anchor="middle" font-size="12" font-weight="700" fill="#ffffff">{order}</text>
<text x="{x + 19:.1f}" y="{y - 16:.1f}" font-size="12" font-weight="600" fill="#111827">{html.escape(point.name)}</text>
</g>
"""
)
return f"""
<div style="width: 100%;">
<svg viewBox="0 0 {width} {height}" role="img" aria-label="Route diagram" style="width:100%;height:auto;border:1px solid #d9e2ec;border-radius:8px;">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L8,3 z" fill="#334155" />
</marker>
</defs>
<rect x="0" y="0" width="{width}" height="{height}" rx="8" fill="#f8fafc"/>
<path d="M {polyline.replace(' ', ' L ')}" fill="none" stroke="#334155" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow)"/>
{''.join(marker_svg)}
</svg>
</div>
"""