| """Dark Bay Area map (Leaflet) with glow-orange markers for the transit mode.""" |
| from __future__ import annotations |
|
|
| import base64 |
| import json |
|
|
| _TEMPLATE = r"""<!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"/> |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/> |
| <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> |
| <style> |
| html,body{margin:0;height:100%;background:#000;font-family:'Courier New',monospace;} |
| #map{width:100%;height:100%;background:#000;} |
| .leaflet-container{background:#000;} |
| #hud{position:absolute;z-index:500;top:10px;left:12px;color:#FF9A1E; |
| text-shadow:0 0 8px #FF7A00;font-size:12px;letter-spacing:1px; |
| pointer-events:none;line-height:1.5;} |
| .lbl{color:#FFB347;text-shadow:0 0 6px #FF7A00;font-size:11px;} |
| </style> |
| </head> |
| <body> |
| <div id="map"></div> |
| <div id="hud">>> BAYLINE // 511 LIVE<br/><span id="sub"></span></div> |
| <script> |
| const DATA = /*__DATA__*/; |
| const map = L.map('map', {zoomControl:true, attributionControl:false}) |
| .setView([37.77,-122.30], 10); |
| L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', |
| {maxZoom:19}).addTo(map); |
| |
| const COLORS = {origin:'#FF9A1E', dest:'#19E0FF', incident:'#FF3B3B'}; |
| |
| // Dotted pathway between origin and destination (when both are present). |
| const _o = (DATA.markers||[]).find(m => m.kind === 'origin'); |
| const _d = (DATA.markers||[]).find(m => m.kind === 'dest'); |
| if (_o && _d) { |
| L.polyline([[_o.lat,_o.lon],[_d.lat,_d.lon]], { |
| color:'#FF9A1E', weight:3, opacity:0.95, dashArray:'2,9', lineCap:'round' |
| }).addTo(map); |
| } |
| |
| const pts = []; |
| (DATA.markers||[]).forEach(m => { |
| const c = COLORS[m.kind] || '#FFB347'; |
| const mk = L.circleMarker([m.lat, m.lon], { |
| radius: m.kind==='incident'?6:9, color:c, weight:2, |
| fillColor:c, fillOpacity:0.65 |
| }).addTo(map); |
| mk.bindTooltip(m.label||'', {className:'lbl'}); |
| // glow ring |
| L.circleMarker([m.lat,m.lon],{radius:(m.kind==='incident'?12:16),color:c, |
| weight:1,opacity:0.35,fill:false}).addTo(map); |
| pts.push([m.lat,m.lon]); |
| }); |
| if (pts.length===1){ map.setView(pts[0], 12); } |
| else if (pts.length>1){ map.fitBounds(pts, {padding:[40,40]}); } |
| document.getElementById('sub').textContent = DATA.stamp || ''; |
| </script> |
| </body> |
| </html>""" |
|
|
|
|
| def build_map_html(markers, stamp: str = "") -> str: |
| data = {"markers": markers or [], "stamp": stamp} |
| html = _TEMPLATE.replace("/*__DATA__*/", json.dumps(data)) |
| b64 = base64.b64encode(html.encode("utf-8")).decode("ascii") |
| return ( |
| f'<iframe src="data:text/html;base64,{b64}" ' |
| 'style="width:100%;height:560px;border:0;border-radius:10px;' |
| 'box-shadow:0 0 24px #ff7a0055, inset 0 0 40px #00000080;"></iframe>' |
| ) |
|
|