Skip to content

Open-X-Humanoid/BICMap-Python

Repository files navigation

BICMap-Python

通过 pywebview 桌面容器 + JS Bridge,让 Python 代码直接控制 BICMap JS SDK 进行地图展示与数据渲染,无需编写任何前端代码。

在线文档 & 示例: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
  → <script type="module"> import bicMap from 'bic-map-plugin.mjs'
    → import { createBridge } from 'bridge-adapter.mjs'
    → await bridge.initMap({baseUrl, cssPath, jsPath, ...})
      → bicMap.init({cssUrl, jsUrl, assetsUrl, fontsUrl})
        → 动态加载 bicMap.min.js (MapLibre + Three.js + Turf.js)
        → 动态加载 bicMap.css
      → bicMap.createMap({container, center, zoom})
        → 基于 bicMap.maplibregl 创建地图实例
    → map.on('load') → pywebview.api.on_map_ready()
    → Python BicMapClient._ready.set() → 触发 on_ready 回调

项目结构

bic-map-python/
├── bicmap_view.py            # 视图容器:WebView + HTTP 服务器管理
├── bicmap_bridge/            # 桥接核心包
│   ├── __init__.py           # 包导出
│   ├── client.py             # BicMapClient:JS 通信调度 + 子命名空间 API
│   ├── types.py              # 数据类型 (MapClickEvent, MapOptions, etc.)
│   └── errors.py             # 异常定义
├── templates/
│   └── map_template.html     # HTML 模板,ES Module 自动初始化 SDK
├── web/
│   └── bridge-adapter.mjs    # JS 侧桥接:路由表 + dispatch + 回调
├── examples/
│   └── slam_drawing_demo.py   # SLAM 地图绘制 Demo
├── requirements.txt
└── screenshot.png            # 示例运行截图输出

快速开始

# 1. 创建虚拟环境
python3 -m venv venv

# 2. 激活
source venv/bin/activate

# 3. 安装依赖
pip install -r requirements.txt

# 4. 启动 Demo
python examples/slam_drawing_demo.py

# 完成后退出虚拟环境
# deactivate

基本用法

from bicmap_view import BicMapView

# 定义事件回调
def on_ready():
    print("地图就绪,开始操作")
    view.bridge.marker.add(116.397428, 39.90923, "天安门")
    view.bridge.invoke("", "polyline",
        coordinates=[[116.38, 39.90], [116.42, 39.92]],
        color="#FF0000", width=4)

def on_click(event):
    print(f"点击: lng={event.lng}, lat={event.lat}")

# 创建并启动
view = BicMapView(title="My Map", width=1024, height=768)
view.bridge.on_ready = on_ready
view.bridge.on_click = on_click
view.start()  # 阻塞,直到用户关闭窗口

API 参考

BicMapView

视图容器,管理 WebView 窗口 + HTTP 服务器,持有 bridge 客户端。

方法 参数 说明
__init__ title, width, height, web_dir, template_path 创建地图视图
start() 启动 HTTP 服务器和 WebView 窗口(阻塞
screenshot(save_path) 保存路径 截图保存为本地 PNG 文件
close() 关闭窗口和 HTTP 服务器

BicMapClient (view.bridge)

核心桥接客户端,所有地图操作都通过 view.bridge.xxx() 访问。

顶层方法

方法 参数 说明
fly_to(center, zoom) [lng, lat] + 缩放级别 平滑飞行动画
fit_bounds(bounds, padding) [[lng1,lat1],[lng2,lat2]] + padding 自适应边界
get_state() 返回 {center, zoom, bearing, pitch}
screenshot() 返回 canvas 截图的 base64 data URL
clear_layers(prefixes) 图层 ID 前缀列表 清除指定前缀的图层
add_zoom_control(position) 位置如 "bottom-right" 添加缩放控件
wait_ready(timeout) 超时秒数 (默认 30) 阻塞等待地图就绪

bridge.geometry — 几何图形

所有创建方法返回 controllerId,可用于后续的 update/remove/show/hide 操作。

方法 说明
create_point_cloud(points, options) 创建 2D 点云
create_point_cloud_3d(points, options) 创建 3D 点云
create_rectangles(rects, options) 创建矩形
create_polygons(polygons, options) 创建多边形
create_circles(circles, options) 创建圆形
create_polylines(lines, options) 创建折线
create_wide_lines(lines, options) 创建宽线
update(controller_id, data) 更新图形数据
remove(controller_id) 移除图形
show(controller_id) 显示图形
hide(controller_id) 隐藏图形

bridge.marker — 标记

方法 说明
add(lng, lat, label) 添加简单标记点(原生 Popup)
add_directional(lng_lat, options) 添加可旋转方向标记
add_batch_poi(points, options) 批量添加 POI 标记
add_robot(robots, options) 批量添加机器人标记

bridge.drawing — 绘制工具

方法 说明
enable_rectangle(options) 启用矩形绘制
enable_circle(options) 启用圆形绘制
enable_polygon(options) 启用多边形绘制(完成后触发 on_draw_complete
enable_polyline(options) 启用折线绘制(完成后触发 on_draw_complete
disable(controller_id) 停止绘制工具
finish(controller_id) 完成绘制

bridge.layer — 图层

方法 说明
add_geojson_source(source_id, data) 添加 GeoJSON 数据源
add_fill(layer_id, source_id, paint) 添加填充图层
add_line(layer_id, source_id, paint) 添加线图层

bridge.resource — 资源

方法 说明
get_asset_path(relative_path) 获取资源文件 URL
get_font_path(font_file) 获取字体文件 URL
get_cursor_style(path, x, y, fallback) 获取自定义光标样式

bridge.slam — SLAM 地图

方法 说明
load(options) 异步加载 SLAM 地图,返回结果 dict

回调属性

通过 view.bridge.on_xxx 设置:

属性 触发时机 参数类型
on_ready 地图 load 事件完成 无参数
on_click 用户点击地图 MapClickEvent(lng, lat)
on_draw_complete 绘制工具完成绘制 DrawCompleteEvent(type, geojson, ...)

数据类型 (bicmap_bridge.types)

类型 说明
LngLat Tuple[float, float]
MapClickEvent lng, lat
DrawCompleteEvent type, geojson, info, data
MapState center, zoom, bearing, pitch
MapOptions container, center, zoom, pitch, bearing
PolygonStyle fill_color, fill_opacity, outline_color, outline_width
PolylineStyle color, width, opacity, show_arrow
DirectionalMarkerOptions image_path, size, initial_rotation, draggable
POIMarkerData lng_lat, rotation, name
RobotMarkerData lng_lat, rotation, name, robot_id

异常类型 (bicmap_bridge.errors)

异常 说明
BridgeError 桥接基础异常
BridgeNotReadyError 桥接未就绪——地图尚未加载完成时调用 API
BridgeEvalError JS 执行错误——dispatch 内部异常

相关资源

BICMap-Python 基于以下项目构建:

资源 链接
BICMap GL 主工程 GitHub
在线文档 & 示例门户 bicmap.x-humanoid-cloud.com

Contributors

感谢以下开发者对 BicMap 的贡献:

lxycreate
lxycreate

License

MIT © 2024-2026 北京人形机器人创新中心 (Beijing Innovation Center of Humanoid Robotics)

本项目打包并再分发了若干第三方开源库,其版权与许可证声明见 THIRD-PARTY-LICENSES.md


技术要点

  • pywebview 版本: >= 5.0,macOS 使用 Cocoa/WKWebView 后端
  • HTTP 服务器: Python 内置 http.server,动态端口,后台 daemon 线程运行
  • CORS: 所有响应自动添加 Access-Control-Allow-Origin: *
  • JS Bridge: bridge-adapter.mjs 通过 window.__bicMapBridge.dispatch() 统一路由,按 ns.action 分发到对应处理函数
  • Python 桥接: BicMapClient 通过 evaluate_js 注入 JS,invoke(ns, action, **params) 序列化为 JSON 调用 dispatch
  • 异步操作: slam.load() 通过 _invoke_async + on_result 回调实现异步等待
  • 回调流转: JS pywebview.api.on_xxx()BicMapView.on_xxx()BicMapClient._on_xxx() → 用户回调
  • webview.start() 是阻塞调用,操作逻辑需放在 on_ready 回调中执行

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors