Skip to content

Annotation Bubble

本指南:介绍地图上常见的 Annotation 气泡(拥堵气泡、路线气泡、Smart Bubble 等)的产品含义、适用场景与 Android SDK 集成方式。

功能介绍

Annotation Bubble 是一类基于 Annotation 机制渲染的 屏幕坐标气泡,用于在地图或路线上叠加短文本、时间/距离信息、价格、路名等。与 转弯气泡TurnBubbleFeature,由导航 step 驱动)不同,Annotation Bubble 由业务通过 AnnotationsController 创建与管理。

视觉效果由 TSS 决定:文档中的截图仅为默认主题示意。气泡的形状、颜色、渐变、字体、把手、多子层布局、摆放方位与避让规则等,均在 TSS 的 annotation 图层中通过 annotation-datastepped(...) 及自定义变量(如 $main-text)配置。HMI 通过 API 传入 styleKey、文案与 updateFloatValue / updateStringValue,即可切换展示内容;换肤或 OEM 定制时修改 TSS 即可,无需改 Java/Kotlin 业务代码。详见 地图配色方案

常见类型包括:

类型 典型用途 SDK 创建方式
Traffic Congestion Bubble 沿路线展示轻度/重度拥堵及延误文案 Annotation.ExplicitStyle + create(ExplicitStyle, Location)
Route Bubble 路线上的 ETA / ETE、时差、距离、延误等 createRouteAnnotation(routeId, styleKey)
Smart Bubble 统一样式下的时间、路名、POI 价格、EV 电量等 TSS styleKey + updateFloatValue("smart-bubble-type", …)

气泡分类

1. Traffic Congestion Bubble(拥堵气泡)

导航路线 上标注拥堵区段,向驾驶员展示延误或拥堵等级文案(如「+5 分钟」)。

效果示意

拥堵气泡

产品侧通常区分:

等级 ExplicitStyle 说明
轻度拥堵 LightCongestionBubble 锚定在路线拥堵点;具体配色由 TSS 定义
重度拥堵 HeavyCongestionBubble 锚定在路线拥堵点;具体配色由 TSS 定义

TSS 定制说明

  • 在 TSS 中为 custom_congestion_bubbles.heavy_congestion_bubble / light_congestion_bubbleannotation-data 配置圆角、渐变、把手图标、文本对齐与优先级。
  • 气泡为引擎内置矢量形状,不依赖 HMI 传入 Bitmap;宽度可随文本自适应。
  • 可通过 text-colortext-sizegradient-start-color 等属性调整外观;路线上的路况着色与气泡样式相互独立。

2. Route Bubble(路线气泡)

算路结果路线全览导航中 为每条已绘制的路线挂载气泡,锚定在路线几何上,随地图缩放/平移更新位置。气泡 不自动读取 Route 上的时间字段,具体展示什么由 HMI 格式化后写入 displayText 或 TSS 变量(如 main-text)。

效果示意

路线气泡

常见展示内容

类型 含义 典型展示 数据来源(示例)
ETE Estimated Time En route,预计行驶/剩余时长 25 min1 h 12 min Routeduration(秒)格式化
ETA Estimated Time of Arrival,预计到达时刻 14:35下午 2:35 到达 当前时间 + duration,或 Route.getArrivalTimes() 末段到达时间
ETA / ETE 差值 相对主路线或当前路线的快慢 +3 min-5 min快 2 分钟 备选路线 etaDelta、两条路线 duration 之差
距离 全程或至分叉点距离 12.5 km至分叉 800 m Route 长度、备选路线 bifurcationDistance / distanceDelta
路况延误 相对畅通路线的额外耗时 Traffic delay: 8 min Route.trafficDelayduration - durationWithoutTraffic
分叉引导 驶入备选前的提示 To Bifurcation: 2 min 备选路线 bifurcationTime

同一气泡可组合多段文案(如「ETE + 距离 + ETA Delta」),具体排版由 TSS 决定;主行、单位可分别映射到 $main-text$unittext

典型场景

  • 路线选项 / 多路线全览:每条路线一个气泡,对比 ETE 或 ETA,并标注更快/更慢。
  • 选中与高亮:主路线与备选路线使用不同 smart-bubble-type,区分选中/未选中样式。
  • Better Route / 备选路线:展示 ETA Delta、至分叉时间/距离、距离差等。
  • 导航中:可仅展示延误、剩余 ETE 等动态信息。

通过 AnnotationFactory.createRouteAnnotation 创建,与 RoutesController.addRouteLine(或 add)返回的 routeId 绑定。style 为 TSS 的 annotation-data key(常用 smart-bubble);选中态、双行文案等由 smart-bubble-type 与 TSS 共同控制。


3. Smart Bubble(智能气泡)

Smart Bubble 是一套 多子层 气泡框架:在 TSS 中为同一 styleKey(如 smart-bubbleev-bubble)定义底板、阴影、主文本、单位、电量图标等子层;通过 smart-bubble-typeupdateFloatValue)在同一套样式下切换多种布局。HMI 传入的字符串/浮点属性对应 TSS 中的 $main-text$unittext 等变量;statebehaviour-settings(如 avoidRoutes)控制摆放方位与避让,均可按项目在 TSS 中调整。

效果示意

Smart Bubble

smart-bubble 常用 smart-bubble-type 取值(须与 TSS stepped(smart-bubble-type, …) 一致):

smart-bubble-type 名称 说明
0 default 默认 / 选中路线(如高亮 ETE/ETA)
1 unfocused 未选中路线
2 selected 高亮选中态
3 two_texts 双行文案
4 ftue 首次引导
5 low_battery 低电量(EV)

另可选用 TSS 样式 "ev-bubble",通过 ev-bubble-type 切换 simple / battery 等子类型。

Smart Bubble 还可扩展展示:

  • 主路线与备选路线的 ETE / ETA 对比
  • EV 电量 百分比(续航/充电规划);
  • 路口 下一 maneuver 相关提示(与 Turn Bubble 产品能力有交集,实现路径不同)。

与 Turn Bubble 的区别:Turn Bubble 由 TurnBubbleFeature + 导航 legIndex/stepIndex 驱动;Smart Bubble / Route Bubble 属于 Annotation,由业务创建并更新属性,外观完全由 TSS 配置。


核心接口

获取入口:

1
2
val annotationsCtrl = mapView.getAnnotationsController() ?: return
val factory = annotationsCtrl.factory()
方法 说明
factory.create(Annotation.ExplicitStyle, Location) 创建拥堵等 ExplicitStyle 气泡
factory.createRouteAnnotation(routeId, styleKey) 创建绑定路线的 Route Bubble
factory.create(POIAnnotationParams) 使用 TSS styleKey 创建 Smart Bubble 等通用标注
annotation.displayText 设置居中/偏移文本(拥堵气泡、路线气泡常用)
annotation.updateStringValue(key, value) 更新 TSS 自定义字符串(如 main-text
annotation.updateFloatValue(key, value) 更新 TSS 自定义浮点(如 smart-bubble-type
annotationsCtrl.add / update / remove 增删改地图上的标注

ExplicitStyle 创建的气泡 不会 再受 Annotation.Style 其它枚举影响。


接口详细说明

1. 拥堵气泡 — create(ExplicitStyle, Location)

1
2
3
4
fun create(
    explicitStyle: Annotation.ExplicitStyle,
    location: Location
): Annotation
参数 类型 说明
explicitStyle Annotation.ExplicitStyle HeavyCongestionBubbleLightCongestionBubble
location Location 气泡锚点经纬度,通常取拥堵区段代表点
返回值 Annotation 创建后需 add 到地图;通过 displayText 设置展示文案

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private fun createCongestionBubble(
    explicitStyle: Annotation.ExplicitStyle,
    location: Location
): Annotation {
    val factory = mapView.annotationsController().factory()
    return factory.create(explicitStyle, location).apply {
        displayText = Annotation.TextDisplayInfo.Centered("450 m | 10 min")
        displayText?.textColor = 0xffffffff.toInt()
        displayText?.textSize = 20.0f
    }
}

// 长按地图添加后:
mapView.annotationsController().add(listOf(createCongestionBubble(
    Annotation.ExplicitStyle.HeavyCongestionBubble,
    touchPosition.geoLocation!!
)))

轻度/重度外观请在 TSS 中分别配置 custom_congestion_bubbles.light_congestion_bubbleheavy_congestion_bubble


2. 路线气泡 — createRouteAnnotation

1
2
3
4
fun createRouteAnnotation(
    routeID: String,
    style: String
): Annotation
参数 类型 说明
routeID String RoutesController.addRouteLine 返回的路线 ID
style String TSS 中路线气泡的 styleKey / annotation-data(如 smart-bubble 或项目自定义 route-eta 样式)
返回值 Annotation 气泡沿路线锚定,路线移除后应同步 remove

示例代码 — 多路线全览(ETE / ETA 文案)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
companion object {
    const val STYLE_SMART_BUBBLE = "smart-bubble"
    const val BUBBLE_STYLE_KEY = "smart-bubble-type"
    const val MAIN_TEXT_STYLE_KEY = "main-text"
}

private fun formatEte(route: Route): String {
    val minutes = (route.duration / 60).toInt()
    return if (minutes < 60) "${minutes} min" else "${minutes / 60} h ${minutes % 60} min"
}

private fun formatEta(route: Route): String {
    val arrivalMillis = System.currentTimeMillis() + (route.duration * 1000).toLong()
    return SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date(arrivalMillis))
}

private fun showRouteAnnotation(
    routeId: String,
    route: Route,
    isSelected: Boolean,
    annotationsController: AnnotationsController
) {
    val factory = annotationsController.factory()
    val eteText = formatEte(route)
    val etaText = formatEta(route)
    val annotation = factory.createRouteAnnotation(routeId, STYLE_SMART_BUBBLE).apply {
        // 单行示例:仅 ETE;也可拼接 "ETE · ETA" 或分行交给 TSS two_texts
        displayText = Annotation.TextDisplayInfo.Centered(eteText)
        updateFloatValue(BUBBLE_STYLE_KEY, if (isSelected) 0f else 1f)
        updateStringValue(MAIN_TEXT_STYLE_KEY, eteText)
        // 若 TSS 支持单位行:updateStringValue("unittext", "到达 $etaText")
    }
    annotationsController.add(listOf(annotation))
    routeAnnotationsMap[routeId] = annotation
}

formatEtajava.text.SimpleDateFormat 等;若算路结果已提供 Route.getArrivalTimes(),可直接取目的地到达时刻作为 ETA 展示。

示例代码 — 点击路线切换高亮与气泡类型

1
2
3
4
5
6
7
mapView.setOnRouteTouchListener { _, _, routeID ->
    routesController.highlight(routeID)
    routeAnnotationsMap.forEach { (id, annotation) ->
        val type = if (id == routeID) selectedSmartBubbleType else defaultSmartBubbleType
        annotation.updateFloatValue("smart-bubble-type", type)
    }
}

示例代码 — 导航中展示路况延误(ETE 相关)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private fun showRouteAnnotation(routeId: String, route: Route) {
    clearRouteAnnotation()
    val delayMin = route.trafficDelay / 60  // 秒 → 分钟,亦可用 duration 与 durationWithoutTraffic 自行计算
    val annotation = mapView.annotationsController().factory()
        .createRouteAnnotation(routeId, STYLE_SMART_BUBBLE)
        .apply {
            displayText = Annotation.TextDisplayInfo.Centered("Traffic delay: ${delayMin} min")
        }
    routeAnnotations.add(annotation)
    mapView.annotationsController().add(routeAnnotations)
}

示例代码 — 备选路线(分叉时间、ETA Delta、距离差)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
val annotation = factory.createRouteAnnotation(alternativeRoute.route.id, "smart-bubble")

val bifTimeText = formatDurationSeconds(alternativeRoute.bifurcationTime)  // 至分叉 ETE
val etaDeltaText = formatDurationSeconds(alternativeRoute.etaDelta)         // 与主路线 ETA/到达差

annotation.displayText = Annotation.TextDisplayInfo.Centered(
    "To Bifurcation: $bifTimeText | " +
        "${alternativeRoute.bifurcationDistance.toInt()} m | " +
        "ETA Delta: $etaDeltaText | " +
        "${alternativeRoute.distanceDelta.toInt()} m"
)
mapView.annotationsController()?.add(listOf(annotation))

3. Smart Bubble — POIAnnotationParams + 自定义属性

1
fun create(params: POIAnnotationParams): Annotation
字段 说明
params.styleKey TSS 样式 key,Smart Bubble 常用 "smart-bubble"
params.location 锚点位置;路线类 Smart Bubble 仍可用 createRouteAnnotation
params.text 主文本(也可通过 updateStringValue("main-text", …) 更新)

Smart Bubble 常用自定义键(须与 TSS 中 $变量 一致):

类型 说明
smart-bubble-type Float 子类型:0 选中、1 未选中等(路线气泡常配合 ETE/ETA 样式)
main-text String 主文案(ETE、ETA、路名、价格等)
unittext String 单位(如 minkm

示例代码 — Smart / EV POI 气泡

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
fun createPOIAnnotation(
    annotationsController: AnnotationsController,
    location: Location,
    styleKey: String,  // "smart-bubble" 或 "ev-bubble"
    text: String
): Annotation {
    return annotationsController.factory().create(
        POIAnnotationParams(
            styleKey = styleKey,
            location = location,
            text = text
        )
    )
}

// 使用:
val bubble = createPOIAnnotation(
    annotationsController, location, "smart-bubble", "$3.49"
)
bubble.updateFloatValue("smart-bubble-type", 3f)  // 按 TSS 定义
annotationsController.add(listOf(bubble))

4. 导航沿线路况拥堵气泡 — onAlongRouteTrafficUpdated

导航中根据 AlongRouteTraffic 在拥堵区段中点创建气泡,驶过后按 legIndex / stepIndex / edgeIndex 移除。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
override fun onAlongRouteTrafficUpdated(alongRouteTraffic: AlongRouteTraffic) {
    refreshBubble(generatePoiModel(alongRouteTraffic))
}

private fun refreshBubble(models: List<PoiModel>) {
    annotationController?.remove(bubbleList)
    bubbleList.clear()
    models.forEach { model ->
        if (model.congestionLevel == -1 || model.latLon == null) return@forEach
        val location = Location("demo").apply {
            latitude = model.latLon.lat
            longitude = model.latLon.lon
        }
        val bubble = annotationController!!.factory()
            .create(Annotation.ExplicitStyle.HeavyCongestionBubble, location)
        bubble.extraInfo = Bundle().apply {
            putInt("legIndex", model.legIndex)
            putInt("stepIndex", model.stepIndex)
            putInt("edgeIndex", model.edgeIndex)
        }
        bubble.displayText = when (model.congestionLevel) {
            TrafficLevel.CONGESTED -> Annotation.TextDisplayInfo.Centered("CONGESTED")
            TrafficLevel.SLOW_SPEED -> Annotation.TextDisplayInfo.Centered("SLOW SPEED")
            TrafficLevel.HEAVY -> Annotation.TextDisplayInfo.Centered("HEAVY")
            TrafficLevel.FREE_FLOW -> return@forEach
            else -> Annotation.TextDisplayInfo.Centered("UNKNOWN")
        }
        bubble.displayText?.textSize = 10.0f
        bubbleList.add(bubble)
    }
    annotationController?.add(bubbleList)
}

// 车辆位置更新时移除已驶过区段的气泡
private fun updateCurrentPosition(legIndex: Int, stepIndex: Int, edgeIndex: Int) {
    val removeList = bubbleList.filter { annotation ->
        val leg = annotation.extraInfo?.getInt("legIndex") ?: -1
        val step = annotation.extraInfo?.getInt("stepIndex") ?: -1
        val edge = annotation.extraInfo?.getInt("edgeIndex") ?: -1
        legIndex > leg || (legIndex == leg && stepIndex > step) ||
            (legIndex == leg && stepIndex == step && edgeIndex > edge)
    }
    annotationController?.remove(removeList)
    bubbleList.removeAll(removeList)
}

NavigationEventListener.onAlongRouteTrafficUpdated 中刷新气泡,并配合 routesController.refreshAlongRouteTraffic 更新路线着色,见 路线渲染


TSS 样式定制

三类气泡的 最终显示效果均在 TSS 中定义;API 只负责创建标注、绑定路线/位置并传入动态文案与类型参数。换肤或 OEM 差异通过替换/修改 TSS 资源包完成,参见 地图配色方案

Smart / Route / EV 气泡

在 TSS 中使用 annotation 图层,以 annotation-data 区分样式 key,例如:

1
2
3
4
5
6
7
8
9
bubble-name[annotation-data="smart-bubble"] {
  state top { vertical: 1; diag: 0; handle-rotation: 270; ... };
  behaviour-settings: {
    "entries": [ { "behaviour-type": "avoidRoutes", "weight": 1, ... } ],
    "score-acceptance-threshold": 0.85
  };
  collision-enabled: disabled;
  // 子层示例:底板 shape-size / background-color、阴影、$main-text、$unittext、电量 icon-image ...
}
定制项 TSS 侧 API 侧
底板、圆角、阴影 shape-sizerounded-cornersshadow-*
主文案、单位 text: $main-texttext: $unittext updateStringValue("main-text", …)
选中/未选中/EV 等形态 stepped(smart-bubble-type, [...]) 或多套 annotation-data updateFloatValue("smart-bubble-type", …)
叠放顺序 各子层 priority(与书写顺序无关)
是否显示 layer_order 中注册图层名 add / remove

拥堵气泡(ExplicitStyle)

HeavyCongestionBubble / LightCongestionBubble 映射到 TSS 中 custom_congestion_bubbles.* 节点,可配置渐变(gradient-start-color / gradient-end-color)、把手(icon-image)、文本(texttext-alignment)等。业务侧用 displayText 或 TSS 变量传入延误文案;颜色与字号以 TSS 为准,displayText 上的 textColor / textSize 可按项目覆盖。


使用流程

  1. 地图 onReady 后获取 AnnotationsControllerAnnotationFactory
  2. 使用 路线渲染 绘制路线并取得 routeId
  3. 按类型调用 create / createRouteAnnotation,设置 displayTextupdateFloatValue / updateStringValue
  4. annotationsController.add 显示;路线点击时更新 smart-bubble-type(改 float 属性后引擎会刷新,通常无需再 update)。
  5. 路线移除、导航结束或重算路时 remove 对应标注。
  6. 需要交互时注册 setOnAnnotationTouchListenersetOnRouteTouchListener,见 触摸与手势

注意事项

  • ExplicitStyle 与 Style 互斥:拥堵气泡使用 HeavyCongestionBubble / LightCongestionBubble 后,不要再对其 setStyle 为普通 ScreenAnnotationPopup
  • 拥堵文案:示例统一用 Annotation.TextDisplayInfo.Centered(text) 设置 displayText,并可选 textColortextSize
  • routeId 生命周期createRouteAnnotationrouteID 必须在路线仍存在于 RoutesController 时有效;remove(routeId) 后应移除对应气泡。
  • TSS 与 API 键名一致styleKeysmart-bubble-type$变量 名须与当前加载的 TSS 文件一致;定制显示效果时优先改 TSS,再核对 API 传入的 key 与取值。
  • 勿与 clear() 混用AnnotationsController.clear() 会清除 全部 标注(含其他模块),多业务共存时优先对列表 remove
  • Turn Bubble:逐步转向引导请优先使用 转弯气泡;路名气泡若产品指定为 Smart Bubble,需与 Turn Bubble 在 UI 上避免重复堆叠。

相关指南