Jiajia Zhang
JIAJIA
Workflow & Product Systems
ON
返回项目

Owner-local 交通工具 · 哥本哈根 · 2026

Afgang

Afgang 回答两个日常交通问题:我应该什么时候出门,以及按照哥本哈根的计划时刻表,15、30、45 或 60 分钟能到哪里?

ScheduledOwner-local案例
Afgang 桌面可达范围地图,显示从 Nørreport Station 出发的四层计划时刻 contour
从 Nørreport Station 出发的四层计划时刻可达范围。
86
项自动化测试
4
层出行时间
12
条边界路线审计
0
个乐观 contour bucket
产品结果

通勤页把保存的起点、终点和到达目标转成出门时间决策,并提供可刷新的路线分段和 owner 控制的浏览器提醒。

可达范围页在本地计算计划时刻曲面。用户选择起点和整分钟出发时间后,可以在响应式地图上比较 15、30、45 和 60 分钟的嵌套区域。

系统设计

浏览器只和 Afgang 通信。FastAPI lifespan 加载一个长期存在的 R5 network,进程内锁串行化计算,有界 TTL cache 处理重复请求。

  1. 01官方 Rejseplanen GTFS + 丹麦 OSM
  2. 02哈希绑定的 manifest v2
  3. 03r5py 1.1.7 / R5 7.5.1
  4. 04保守的 250 m 六边形曲面
  5. 05Leaflet + MapLibre 界面
工程决策

替换失效的 routing dependency

首个验证基线使用 OTP 2.5 的 sandbox TravelTime API。OTP 2.6 删除了该 API,因此生产路径迁移到进程内 R5,没有保留失效兼容层,也没有继续暴露单独的服务端口。

优先选择保守几何

R5 出行时间被转换为 250 m 六边形,并加入 25 m 站点 anchor 和五分钟 anchor margin。地图可以低估边界,但经过审计的 contour 不会做乐观的站点级承诺。

把数据身份作为硬门槛

GTFS、OSM、序列化 network、runtime JAR、引擎版本、哈希和真实服务日期共同定义一个 bundle identity。身份无效或过期时 fail closed,不会手工延长时刻表覆盖。

数据生命周期

活跃 reachability 数据从不原地重建。每次 feed 更新都走一条可恢复事务:

  1. 1在不中断活跃应用的情况下准备隔离 candidate。
  2. 2构建 R5 network,并审计服务日期、警告、哈希和边界证据。
  3. 3短暂停止应用,通过原子目录重命名提升完整 bundle。
  4. 4运行 health 和 smoke checks;失败时自动恢复旧 bundle 和 freshness state。
验证

release gate 把单元测试和 failure injection 与真实引擎、真实浏览器检查结合起来。Chromium 和 WebKit 依次在桌面、平板、手机竖屏和手机横屏尺寸运行。

12 条代表性公交和铁路检查中,contour bucket 为 10 条精确、2 条保守、0 条乐观。点到点 bucket 有 11 条一致;København H–Køge 的一处差异被明确记录,没有重新包装成一致。

界面

两个页面使用同一套安静的 utility language:系统字体、紧凑控件、可见状态、响应式几何、键盘焦点、reduced-motion 支持,以及 vector basemap 初始化失败时的 raster fallback。

Afgang 通勤页,显示从 DTU 到 Nørreport Station 的出门时间决策和路线分段
通勤页把下一步决策及其证据放在同一条扫描路径中。
Afgang 手机可达范围地图,包含出行时间图例和计算控件
控制面板成为手机底部面板,同时保留可见地图。
声明边界
  • 可达范围只基于计划时刻表,不是 realtime。
  • 当前应用是绑定到 127.0.0.1 的 owner-only 服务,不是公开生产部署。
  • Contour 是概览几何;靠近边界时仍需检查具体路线。
  • 不声明商业用户、交通运营商采用或企业规模运行。
数据与地图来源

公共交通时刻来自 Rejseplanen Labs static GTFS,遵循 CC BY 4.0。道路 routing 数据独立来自 OpenStreetMap;展示的 vector basemap 使用 OpenFreeMap 和 OpenMapTiles。