# BICMap-Python **Repository Path**: open_x_humanoid/BICMap-Python ## Basic Information - **Project Name**: BICMap-Python - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-29 - **Last Updated**: 2026-07-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # BICMap-Python 通过 **pywebview 桌面容器 + JS Bridge**,让 Python 代码直接控制 [BICMap](https://github.com/Open-X-Humanoid/BICMap) JS SDK 进行地图展示与数据渲染,无需编写任何前端代码。 在线文档 & 示例:[bicmap.x-humanoid-cloud.com](https://bicmap.x-humanoid-cloud.com/) ## 架构 项目采用三层分离设计,从上到下依次为:**视图容器层** → **桥接通信层** → **地图渲染层**。 ``` Python 用户代码 │ ▼ ┌─ BicMapView (视图容器层) ────────────────────────────┐ │ │ │ view = BicMapView(title, width, height) │ │ view.bridge.on_ready = on_ready ← 回调入口 │ │ view.bridge.on_click = on_click │ │ view.start() ← 阻塞启动 │ │ │ │ 职责: WebView 窗口 + HTTP 静态服务器 + 截图 IO │ └────────────────────┬──────────────────────────────────┘ │ │ 双向:invoke() 下行 / pywebview.api 上行 ▼ ┌─ BicMapClient (桥接通信层) ───────────────────────────┐ │ │ │ bridge.fly_to(center) ← 顶层操作 │ │ bridge.marker.add(lng, lat, label) ← 子命名空间 │ │ bridge.geometry.create_polygons(...) │ │ bridge.drawing.enable_polygon(...) │ │ │ │ │ │ invoke(ns, action, **params) │ │ │ → json.dumps → evaluate_js(code) │ │ ▼ │ │ window.__bicMapBridge.dispatch({ns,action,params}) │ │ │ │ 职责: 参数序列化 → JS 注入 → 回调解析 → 用户通知 │ └────────────────────┬──────────────────────────────────┘ │ │ 上行回调:on_map_ready / on_map_click / on_result ▼ ┌─ JS Bridge (JS 执行层) ───────────────────────────────┐ │ │ │ bridge-adapter.mjs │ │ createBridge(bicMap) → 构建 ROUTES 路由表 │ │ "" → createMap, flyTo, screenshot, ... │ │ geometry → createPointCloud, remove, update │ │ marker → add, addDirectional, addRobot │ │ drawing → enablePolygon, enableCircle │ │ layer → addGeoJSONSource, addFill │ │ resource → getAssetPath, getFontPath │ │ slam → load (异步) │ │ │ │ │ ▼ │ │ bic-map-plugin.mjs → bicMap.min.js │ │ (MapLibre GL + Three.js + Turf.js) │ │ │ │ 职责: 路由分发 → SDK 调用 → 事件回传 │ └───────────────────────────────────────────────────────┘ ``` **核心调用流程**(下行 Python → JS): ``` view.bridge.marker.add(116.39, 39.90, "北京") → _MarkerAPI.add() → client.invoke("marker", "add", lng=116.39, lat=39.90, label="北京") → json.dumps({ns:"marker", action:"add", params:{lng,lat,label}}) → window.evaluate_js("window.__bicMapBridge.dispatch(...)") → ROUTES["marker"]["add"](params) → new maplibregl.Marker().setLngLat([lng,lat])... ``` **核心回调流程**(上行 JS → Python): ``` map.on('click') → pywebview.api.on_map_click(JSON.stringify({lng,lat})) → BicMapView.on_map_click(json_str) ← pywebview 路由 → BicMapClient._on_map_click(json_str) → MapClickEvent(lng, lat) → user.on_click(event) ``` ## JS Bridge 设计说明 — `bridge-adapter.mjs` `bridge-adapter.mjs` 是连接 Python 侧与 bicMap JS SDK 的核心纽带,其设计必要性体现在以下方面: | 必要性 | 说明 | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **双向通信路由** | Python 通过 `invoke(ns, action, params)` 发送结构化消息,adapter 内部的 `ROUTES` 路由表按命名空间(`marker` / `geometry` / `drawing` / `slam` 等)统一分发到对应处理函数,Python 无需了解任何 MapLibre / bicMap API 细节 | | **控制器生命周期管理** | 对于创建型操作(如 `geometry.createPolygons`),adapter 通过 `_registry` 记录每个控制器实例的 `controllerId`,支持后续的 `update()` / `remove()` / `show()` / `hide()` 操作,避免 Python 侧维护 JS 对象引用 | | **异步操作同步化** | 对于 `slam.load()` 等 JS 异步操作,adapter 注入 `_asyncId` 并通过 `pywebview.api.on_result()` 回调将结果回传,使得 Python 侧可以用 `_invoke_async` + `Event` 实现同步等待 | | **模板与 SDK 解耦** | HTML 模板只需 `import { createBridge } from 'bridge-adapter.mjs'`,不直接依赖 bicMap SDK 的具体接口。SDK 版本升级、接口变化只需修改 adapter 内部实现,模板和 Python 代码无需变更 | | **事件反向桥接** | 地图事件(`click` / `load` / 绘制完成等)通过 `pywebview.api.on_xxx()` 回调回传 Python,adapter 负责将 SDK 事件格式规范化为统一的结构化数据后再传输 | **核心设计模式**:adapter 将双向通信抽象为 **下行路由 + 上行回调** 的对称模型。 ``` Python: bridge.marker.add(lng, lat, label) → invoke("marker", "add", {lng, lat, label}) → dispatch({ns:"marker", action:"add", params:{...}}) → ROUTES["marker"]["add"](params) ← 下行路由 → new maplibregl.Marker().addTo(_map) JS map.on('click', (e) => { → pywebview.api.on_map_click(JSON.stringify({lng,lat})) → Python: on_click(MapClickEvent(lng, lat)) ← 上行回调 ``` ## SDK 加载链路 ``` templates/map_template.html →