业务故事站
P4 Gotham

时空分析:把事件放到地图和时间轴上

一张 Leaflet 地图看 6 个地理要素(事件点 + 航线),bbox / 半径空间查询锁定区域;一条时间轴看 2026-07~08 的 9 条事件,范围查询 + 日 / 周 / 月桶聚合看事件节奏。看完这 4 个故事,你就能把"哪段时间、哪个区域发生了什么"讲清楚。

情报分析员 作战值班员 地图要素 空间查询 时间轴 桶聚合 共 4 个故事

能 / 不能速览

✅ 这个主题能做
  • 地图查看点 / 线要素(GeoJSON),Leaflet 前端渲染,popup 查看详情
  • bbox 包围盒查询(minLng / minLat / maxLng / maxLat)锁定矩形区域
  • radius 半径查询(圆心 + 半径米数,100km 硬上限),Haversine 距离判定
  • 区域聚合(/map/aggregate/regions)按行政区标签统计要素数量
  • 时间轴事件 CRUD + 范围查询(start / end)+ day / week / month / year 桶聚合
  • 事件按 event_type / severity / entity_id / location 过滤,severity 含 critical / high / medium / low
⛔ 这个主题做不了
  • 未接真实时序库与真实底图服务:Leaflet 用静态瓦片底图,时间桶聚合为内存 / SQL 计算
  • 半径查询有 100km 硬上限,超过返回错误
  • 要素几何类型仅 point / linestring / polygon,无复杂几何(multi-polygon 等)
  • 区域聚合的 region 是业务标签(如"北京"),不是真实行政区划数据,统计口径由数据方决定

适用角色

本主题面向两类角色:

  • 情报分析员:核心使用者,把事件落到地图与时间轴,做时空关联研判。
  • 作战值班员:用区域聚合与桶聚合快速掌握"哪里、哪段时间最热",支撑调度决策。

地图与时间轴上的要素 / 事件都支持 CRUD,情报分析员可自行补充,改动落库。

能力速览(能做什么)

地图要素

GeoJSON 点 / 线要素:5 个事件点(北京 / 上海 / 广州 / 青岛 / 深圳)+ 1 条航线线要素,Leaflet 渲染 + popup 详情。

空间查询

bbox 包围盒与 radius 半径两类查询:bbox 判点在框内 / 线面相交,radius 用 Haversine 距离判定(100km 上限)。

时间轴事件

9 条 2026-07~08 事件(meeting / movement / transaction / incident / registration),支持 event_type / severity / entity / location 过滤。

聚合统计

时间桶聚合 day / week / month / year 看事件节奏;区域聚合(regions)按区域统计要素数量。

调整指南(怎么调整)

  • 改查询范围:bbox 参数是 minLng / minLat / maxLng / maxLat;radius 参数是 lng / lat / radiusMeters(米),别超过 100000。
  • 改时间粒度:/timeline/aggregate 的 bucket 支持 day / week / month / year;start / end 支持 RFC3339 或 yyyy-MM-dd。
  • 改过滤口径:事件按 event_type 过滤看某类事件(如只查 incident);地图按 geometry_type / region / entity_id 过滤。
  • 改要素:地图支持新增要素(point / linestring / polygon),新情报随时上图;时间轴支持新增事件,改动落库。

做得好的场景

时空分析最擅长"用位置和时间锁定线索":
  • 空间聚焦:bbox / 半径查询几秒圈出目标区域内的全部要素,替代手工翻地图定位。
  • 时间节奏一眼看:周 / 月桶聚合柱状图直接反映事件是变密还是变疏。
  • 事件可回放:范围查询把某段时间内的事件按时间排开,串成一条可汇报的脉络。
  • 轻量自维护:要素与事件都支持页面新增,情报员自己上图、上事件,不用等开发。

限制与不足

以下是明确的边界,使用前先知道:
  • 底图与时序库:Leaflet 用静态瓦片底图,未接真实地图服务;时间轴未接真实时序库,聚合为内存 / SQL 时间桶计算。
  • 半径上限:radius 查询 100km 硬上限,超出直接拒绝。
  • 几何类型受限:仅 point / linestring / polygon,无复杂几何类型。
  • 区域是标签:region 是业务标签("北京""沿海"),不是真实行政区划统计,口径由录入方决定。

场景故事

故事 1 打开地图,6 个地理要素(事件点 + 航线)一次看全
背景
苏分析师打开 /gotham/map(Leaflet 地图),演示数据里已 seed 好 6 个地理要素:北京联络点、上海货栈、广州港口、青岛锚地、深圳观察点 5 个事件点,外加一条"华北-华东航线"线要素。她要先在地图上建立全局空间认知。
传统做法对比
以前要把各条情报里的地名一个个在地图软件里手查坐标、标点,标完还要人肉记"哪个点对应哪个实体"。现在打开页面就是一张标好要素的地图,点一下就弹详情。
角色
苏分析师(情报分析员,只读浏览即可)。
操作步骤
  1. 打开地图页 /gotham/map
  2. 确认 5 个点要素:北京联络点 / 上海货栈 / 广州港口 / 青岛锚地 / 深圳观察点
  3. 确认 1 条线要素:华北-华东航线(北京 → 上海 → 青岛)
  4. 点击北京联络点 popup,看关联实体与属性
系统响应
要素接口返回结构示例:
{
  "name": "北京联络点",
  "geometry_type": "point",
  "coordinates": [116.40, 39.90],
  "properties": { "entity_type": "person" },
  "entity_id": "graph:person:zhy",
  "region": "北京"
}
线要素 coordinates 为 [[lng,lat], ...] 数组。
结果洞察
北京联络点关联核心人物张远(entity_id=graph:person:zhy),上海货栈关联天河贸易,广州港口关联青龙航运,青岛锚地是远航号所在——地图上的点与图上的节点一一对应,空间分布勾勒出"华北人、华东货、华南船"的格局。
调整建议
用 region 过滤要素(北京 / 上海 / 广州 / 山东 / 广东 / 沿海);点要素 popup 里有"在图谱中查看"入口,可跳到对应图节点。
动手试一试
操作:/gotham/map 地图页 + /map/features。预期结果:地图上出现 5 个点要素与 1 条线要素,北京联络点 popup 显示 entity_id=graph:person:zhy、region=北京。
限制提示
Leaflet 使用静态瓦片底图,未接真实底图服务;要素几何类型仅 point / linestring / polygon;region 是业务标签不是真实行政区划数据。
故事 2 bbox / 半径空间查询,把华东区域和上海周边一次锁定
背景
苏分析师要聚焦华东方向:先用 bbox 圈出华东矩形区域看哪些要素在里面,再用半径查询看上海货栈周边 50 公里内有什么。两次查询都要在几秒内完成。
传统做法对比
以前要在地图上人工框选、逐点量距离,一次空间筛选耗掉半小时,还容易把区域边界算错。现在 bbox / 半径两个接口秒出结果,框选范围随意调整。
角色
苏分析师(情报分析员,只读查询)。
操作步骤
  1. 调用 bbox 查询:/map/features/bbox?minLng=118&minLat=29&maxLng=123&maxLat=33
  2. 查看落在华东框内的要素
  3. 调用半径查询:/map/features/radius?lng=121.47&lat=31.23&radiusMeters=50000
  4. 对比两次结果,确认"上海周边"的覆盖范围
系统响应
bbox 返回框内要素(上海货栈、华北-华东航线的华东段等):
[
  { "name": "上海货栈", "geometry_type": "point", "coordinates": [121.47, 31.23], "region": "上海" }
]
radius 返回距 (121.47, 31.23) 50km 内的要素,判定用 Haversine 大圆距离。
结果洞察
华东 bbox 内命中上海货栈;半径 50km 内除了货栈还覆盖到华北-华东航线线段——空间关系直接提示"天河贸易的货栈正处在重点航线的中继位置上",为后续研判提供了方向。
调整建议
bbox 用 minLng / minLat / maxLng / maxLat 四个角点;radius 的 radiusMeters 单位是米,想圈更大区域先确认在 100km 上限内;点 / 线判定规则不同(点看圆心距离,线按任意点在半径内)。
动手试一试
输入内容:bbox 用 118,29,123,33;radius 用 lng=121.47、lat=31.23、radiusMeters=50000。预期结果:bbox 命中华东要素;radius 命中距上海货栈 50km 内的要素(含航线线段)。
限制提示
半径查询硬上限 100km(MaxRadiusMeters=100000),超过返回错误;线 / 面要素按"任意点在查询范围内"判定,细长线要素跨区域时注意边界口径。
故事 3 时间轴查 9 条事件 + 范围查询,串出 7-8 月的活动脉络
背景
作战值班员王涛要复盘 2026 年 7 月到 8 月的活动脉络。演示时间轴里有 9 条事件:会晤、船舶离港、大宗交易、登记、货栈异常、靠泊、港口检查、集会、航线异常。他先看全量,再按时间段范围查询,再过滤高严重度事件。
传统做法对比
以前把 9 条事件从不同通报里摘出来按时间排 Excel,光排序对日期就要半天,还容易漏掉某一条。现在时间轴默认按时间排好,范围查询 + 类型过滤几秒钟搞定。
角色
王涛(作战值班员,只读查询)。
操作步骤
  1. 打开时间轴页 /gotham/timeline,查看 9 条事件的默认列表
  2. 调用范围查询:/timeline/events/range?start=2026-07-20&end=2026-08-10
  3. 按 event_type=incident 过滤看异常事件
  4. 按 severity 排序看高危 / 危重事件
系统响应
事件结构示例:
{
  "title": "上海货栈异常",
  "event_time": "2026-07-25T16:45:00Z",
  "event_type": "incident",
  "severity": "high",
  "entity_id": "graph:org:tianhe",
  "location": "上海"
}
范围查询 start / end 支持 RFC3339 或 yyyy-MM-dd。
结果洞察
7-20 至 8-10 区间内命中:7-25 上海货栈异常(incident / high)、8-1 远航号靠泊青岛(movement / low)、8-6 广州港口检查(incident / medium);全量 9 条里 3 条 incident,8-15 华南航线异常是唯一的 critical。活动脉络:7 月上旬节奏平稳,8 月中旬异常集中。
调整建议
用 event_type 过滤聚焦某类事件(incident / meeting / movement / transaction / registration);用 entity_id 看单实体的事件史(如只查天河贸易相关的 3 条)。
动手试一试
输入内容:range?start=2026-07-20&end=2026-08-10;再试 event_type=incident。预期结果:范围查询返回 3 条事件;incident 过滤返回上海货栈异常、广州港口检查、华南航线异常 3 条,其中华南航线异常 severity=critical。
限制提示
时间范围 start / end 需按 RFC3339 或 yyyy-MM-dd 传;未接真实时序库,列表与过滤为 SQL 查询;severity 可为空,空值事件不参与严重度排序。
故事 4 日 / 周 / 月桶聚合看事件节奏,区域聚合看哪里最热
背景
王涛要给指挥员汇报"近期态势"。他要用时间桶聚合回答"事件是变密还是变疏",用区域聚合回答"哪里最热"。两张图就能把 7-8 月态势讲清楚。
传统做法对比
以前按月手工数事件、按地区数通报,两张统计表要花一个上午。现在两个聚合接口一次出数,柱状图直接上图。
角色
王涛(作战值班员,只读查询)。
操作步骤
  1. 调用周桶聚合:/timeline/aggregate?bucket=week&start=2026-07-01&end=2026-08-31
  2. 调用月桶聚合:/timeline/aggregate?bucket=month&start=2026-07-01&end=2026-08-31
  3. 调用区域聚合:/map/aggregate/regions
  4. 把两组数字整理进汇报材料
系统响应
时间桶聚合返回 [{bucket, count}](bucket 按 ISO 周 / 自然月格式化):
[
  { "bucket": "2026-W27", "count": 2 },
  { "bucket": "2026-W28", "count": 1 },
  { "bucket": "2026-W29", "count": 1 },
  { "bucket": "2026-W33", "count": 2 }
]
区域聚合返回(按要素 region 计数):[{ "region": "北京", "count": 1 }, { "region": "上海", "count": 1 }, { "region": "广州", "count": 1 }, { "region": "山东", "count": 1 }, { "region": "广东", "count": 1 }, { "region": "沿海", "count": 1 }]
结果洞察
时间上:7 月上旬(W27,含会晤与离港)与 8 月中旬(W33,含集会和航线异常)各 2 起最密,7 月共 5 起、8 月共 4 起;3 条 incident 分布在 W30(7-25 货栈异常)、W32(8-6 港口检查)、W33(8-15 航线异常),30 天内 incident≥3 正好触发"高频事件预警"规则。空间上:6 个要素覆盖 6 个区域(北京 / 上海 / 广州 / 山东 / 广东 / 沿海),沿海航线把 5 个事件点串成一条线——"点线结合"的格局说明活动沿重点航线展开。结论:态势重心在华南—华东一线,近期异常频次在抬升。
调整建议
bucket 支持 day / week / month / year,看短期节奏用 day / week,看趋势用 month;聚合可按 type 过滤(如只统计 incident);区域聚合口径由要素的 region 标签决定。
动手试一试
输入内容:aggregate?bucket=month&start=2026-07-01&end=2026-08-31;再试 bucket=week 同范围;/map/aggregate/regions。预期结果:月桶返回 2026-07:5、2026-08:4;周桶返回 2026-W27:2、2026-W33:2 等 7 个桶;区域聚合返回 6 个区域各 1(北京 / 上海 / 广州 / 山东 / 广东 / 沿海)。
限制提示
聚合是内存 / SQL 时间桶计算,大数据量性能有限;region 是业务标签非真实行政区划统计,跨源数据 region 口径不一致时汇总数字仅供参考。

常见问题

地图底图是真实的吗?

地图用 Leaflet + 静态瓦片底图渲染,方便离线演示;未接真实地图服务。地理要素(点 / 线 / 面)坐标与属性是本系统数据,可在页面新增维护。

半径查询最大能查多大?

radiusMeters 上限 100km(100000 米),超过直接拒绝。bbox 没有类似上限,但框选范围过大时返回全量要素,注意过滤。

时间轴的时间格式要求?

范围查询的 start / end 支持 RFC3339(如 2026-08-01T00:00:00Z)或 yyyy-MM-dd(如 2026-08-01)。聚合的 bucket 取 day / week / month / year。

区域聚合的数字怎么来的?

/map/aggregate/regions 按要素的 region 字段分组计数(region 为空归入"未标注")。region 是录入方打的业务标签,不是真实行政区划,口径需要数据提供方对齐。

要素和事件能自己加吗?

能。地图支持新增要素(point / linestring / polygon),时间轴支持新增事件(标题 / 时间 / 类型 / 严重度 / 关联实体 / 位置),页面操作即可,改动落库,重启保留。

主题小结

一句话:时空分析把"事件"放进地图和时间轴两个坐标系里——bbox / 半径圈空间,范围查询 + 桶聚合看时间节奏,区域聚合看热度分布。记住边界:Leaflet 静态底图、未接真实时序库、半径 100km 上限、几何仅点线面、region 是业务标签。