WebGIS室内地图怎么做?导航功能如何实现?
引言:WebGIS室内地图怎么做?导航功能如何实现?
很多做校园、商场、医院、园区项目的同学都会遇到同一个问题:WebGIS室内地图怎么做?导航功能如何实现? 室外 WebGIS 通常依赖天地图、高德、OSM 或自建矢量瓦片,但室内地图更关注楼层、房间、通道、电梯、楼梯、出入口和 POI 点位,数据组织和导航算法都不一样。
本文按一个可落地的 WebGIS 室内地图项目来讲:先说明室内地图数据怎么建,再讲前端如何展示多楼层地图,最后讲室内导航功能如何用网络图和最短路径实现。适合 GIS 学生、WebGIS 开发者、GIS 工程实施人员参考。

背景:室内地图和普通 WebGIS 地图有什么不同
普通 WebGIS 地图通常解决“在城市或区域范围内显示地理要素”的问题,核心对象是道路、建筑、河流、行政区、影像底图等。室内地图则解决“在一栋建筑内部定位和引导”的问题,核心对象变成了楼层、房间、走廊、门、电梯、楼梯、扶梯、卫生间、服务台和商铺。
做 WebGIS 室内地图时,常见需求包括:
- 展示某栋楼不同楼层的平面图。
- 点击楼层按钮切换 F1、F2、B1 等楼层。
- 搜索房间、商铺、科室或设备点位。
- 点击起点和终点后生成室内导航路线。
- 跨楼层时自动经过楼梯、电梯或扶梯。
- 在移动端或大屏端显示当前路径和文字引导。
因此,WebGIS 室内地图不是简单把 CAD 平面图放到网页上,而是要把室内空间变成可查询、可渲染、可计算路径的 GIS 数据。
原理:WebGIS室内地图怎么做的核心是数据模型
WebGIS室内地图怎么做,关键不在前端用了 Leaflet、OpenLayers 还是 Mapbox GL,而在于室内数据是否结构化。只要数据模型清楚,前端展示和导航计算都会变得可控。
1. 室内地图至少需要三类数据
| 数据类型 | 常见几何类型 | 用途 |
|---|---|---|
| 空间底图数据 | 面、线 | 表示房间、墙体、走廊、楼梯、电梯、建筑边界 |
| POI 数据 | 点 | 表示商铺、办公室、科室、设备、卫生间、出入口 |
| 导航网络数据 | 线、点 | 用于计算起点到终点的最短路径或最优路径 |
2. 楼层字段是室内地图的关键字段
室内地图必须明确楼层。建议每个图层都保留统一的楼层字段,例如 floor_id、building_id、level。否则后期做楼层切换、跨楼层导航、按楼层查询时会非常麻烦。
一个简单的 POI 属性表可以设计为:
| 字段 | 示例 | 说明 |
|---|---|---|
| poi_id | P001 | POI 唯一编号 |
| name | 急诊科 | 显示名称 |
| building_id | A | 建筑编号 |
| floor_id | F2 | 楼层编号 |
| category | department | POI 类型 |
| geom | Point | 点几何 |
3. 室内导航功能实现依赖“可通行网络”
室内导航功能不是直接沿房间边界计算,而是沿“可通行网络”计算。可通行网络可以理解为走廊中心线、门口连接线、楼梯电梯连接线组成的一张图。
在图论中,通道交叉点、门口、电梯口可以视为节点,走廊中心线可以视为边。边有长度、通行类型、楼层、是否可通行等属性。导航时通过 Dijkstra 或 A* 等最短路径算法,从起点节点计算到终点节点。
步骤:从零搭建 WebGIS 室内地图
步骤一:整理原始 CAD 或平面图资料
多数室内地图项目的原始资料来自 CAD,例如 DWG 或 DXF。第一步不要急着写前端代码,而是先清理数据。
- 删除无关图层,例如标注框、施工说明、尺寸线、图签。
- 保留房间边界、走廊、楼梯、电梯、出入口等核心要素。
- 统一单位,确认 CAD 坐标单位是米、毫米还是图纸单位。
- 按楼层拆分数据,避免多个楼层叠在一个图层里。
- 给每个楼层设置明确的
floor_id。
如果 CAD 没有真实坐标,可以先使用局部坐标系做室内展示;如果要和室外地图叠加,则需要做配准,把建筑平面图放到真实地理坐标位置。
步骤二:将室内数据转换为 GIS 格式
WebGIS 前端更适合读取 GeoJSON、矢量瓦片、PostGIS 服务或 WFS 服务。常见处理路线有两种:
- 小型项目:CAD 转 GeoJSON,前端直接加载。
- 中大型项目:CAD 转 PostGIS,再发布为矢量瓦片或接口服务。
使用 QGIS 可以完成基础转换:
- 打开 QGIS,加载 DXF 数据。
- 检查图层几何类型和坐标系。
- 按房间、走廊、POI、导航线分别导出。
- 导出格式选择 GeoJSON、GeoPackage 或 PostGIS。
- 检查属性字段是否包含
building_id和floor_id。
如果数据量较大,不建议前端一次性加载所有楼层 GeoJSON。可以按建筑和楼层拆分文件,例如 building-a-f1.geojson、building-a-f2.geojson。
步骤三:设计室内地图图层结构
一个实用的 WebGIS 室内地图图层结构可以这样设计:
| 图层名 | 几何类型 | 建议用途 |
|---|---|---|
| building_floor | Polygon | 楼层范围或建筑轮廓 |
| room | Polygon | 房间、商铺、科室 |
| corridor | Polygon 或 LineString | 走廊和通道区域 |
| facility | Point | 电梯、楼梯、卫生间、服务设施 |
| poi | Point | 可搜索目标点 |
| nav_node | Point | 导航网络节点 |
| nav_edge | LineString | 导航网络边 |
前端显示时,通常先显示楼层底图,再显示房间和设施,最后叠加 POI 标注和导航路线。这样层级清晰,也方便控制样式。
步骤四:实现楼层切换
WebGIS室内地图怎么做楼层切换,思路其实很直接:所有室内图层都带楼层字段,前端只显示当前楼层的数据。
如果使用 GeoJSON 文件,可以按楼层加载:
const currentFloor = 'F2';
async function loadFloor(buildingId, floorId) {
const url = `/data/indoor/${buildingId}-${floorId}.geojson`;
const response = await fetch(url);
const geojson = await response.json();
// 清空旧楼层图层
indoorSource.clear();
// 添加当前楼层数据
const features = new ol.format.GeoJSON().readFeatures(geojson, {
featureProjection: 'EPSG:3857'
});
indoorSource.addFeatures(features);
}
如果数据在 PostGIS 中,可以通过接口传参查询:
SELECT id, name, floor_id, category, ST_AsGeoJSON(geom) AS geometry
FROM indoor_poi
WHERE building_id = 'A'
AND floor_id = 'F2';
核心原则是:不要让前端靠图层名称猜楼层,而要让数据字段明确表达楼层关系。
步骤五:构建室内导航网络
室内导航功能实现的核心,是把人可以走的路线抽象为网络。不要直接用房间面、墙线或 CAD 线作为导航网络,因为这些数据通常不具备拓扑关系。
建议按下面步骤构建导航网络:
- 沿走廊中心绘制可通行线。
- 在走廊交叉口、门口、电梯口、楼梯口打断线段。
- 为每条线添加起点节点、终点节点、长度、楼层、通行类型。
- 跨楼层位置用特殊边连接,例如电梯边、楼梯边。
- 检查线段是否真正相交和连通,避免视觉相交但拓扑不连通。
导航边表可以设计为:
| 字段 | 示例 | 说明 |
|---|---|---|
| edge_id | E1001 | 边编号 |
| source | N001 | 起点节点 |
| target | N002 | 终点节点 |
| floor_id | F1 | 所属楼层 |
| length | 12.6 | 通行距离 |
| walk_type | corridor | 走廊、电梯、楼梯等类型 |
| cost | 12.6 | 路径计算权重 |
步骤六:实现起点和终点匹配
用户点击地图上的某个房间或 POI 后,不能直接把房间中心点当作导航网络节点。正确做法是把起点和终点吸附到最近的可通行节点或边上。
在 PostGIS 中,可以用距离查询找到最近节点:
SELECT node_id, floor_id
FROM nav_node
WHERE building_id = 'A'
AND floor_id = 'F2'
ORDER BY geom <-> ST_SetSRID(ST_MakePoint(121.5001, 31.2302), 4326)
LIMIT 1;
如果室内使用的是局部平面坐标,也可以把点击点的 x、y 坐标传给后端,然后在同一坐标系中查最近节点。注意不要混用经纬度坐标和米制平面坐标。
步骤七:计算最短路径
室内导航功能如何实现,常用方法是把导航网络交给后端计算。PostGIS 可以存储空间数据,pgRouting 可以进行路径分析。对于轻量项目,也可以在 Node.js、Python 或前端内存中实现 Dijkstra 算法。
使用 pgRouting 的思路如下:
SELECT *
FROM pgr_dijkstra(
'SELECT edge_id AS id, source, target, cost, cost AS reverse_cost
FROM nav_edge
WHERE building_id = ''A''',
101,
205,
directed := false
);
上面 SQL 中,101 是起点节点编号,205 是终点节点编号。返回结果通常还需要关联 nav_edge 表,把路径边的几何取出来,再返回给前端高亮显示。
WITH route AS (
SELECT * FROM pgr_dijkstra(
'SELECT edge_id AS id, source, target, cost, cost AS reverse_cost
FROM nav_edge
WHERE building_id = ''A''',
101,
205,
directed := false
)
)
SELECT e.edge_id, e.floor_id, ST_AsGeoJSON(e.geom) AS geometry
FROM route r
JOIN nav_edge e ON r.edge = e.edge_id
WHERE r.edge <> -1
ORDER BY r.seq;
步骤八:前端高亮显示导航路线
前端拿到路径 GeoJSON 后,可以在 OpenLayers、Leaflet 或 Mapbox GL 中单独创建一个路线图层。建议路线样式和普通室内底图区分明显,例如使用蓝色或橙色粗线。
function showRoute(routeGeojson) {
routeSource.clear();
const features = new ol.format.GeoJSON().readFeatures(routeGeojson, {
featureProjection: 'EPSG:3857'
});
routeSource.addFeatures(features);
}
如果路径跨楼层,前端还需要把路线按 floor_id 分组。当前显示 F1 时只显示 F1 的路线,切换到 F2 时显示 F2 的路线,同时在电梯或楼梯位置提示“上到 2 层”或“下到 B1”。
常见坑:室内地图导航功能最容易出错的地方
1. CAD 看起来能用,但实际没有 GIS 拓扑
CAD 线条主要服务于制图和施工表达,不一定适合 GIS 分析。很多线段只是视觉上接触,实际端点没有重合。做室内导航功能实现时,这会导致路径中断。
- 检查线段端点是否真正吸附。
- 检查节点编号是否正确。
- 检查交叉口是否打断线段。
- 检查跨楼层连接点是否成对出现。
2. 楼层编码不统一
同一项目中不要同时出现 1F、F1、floor1、一层。楼层编码不统一会导致查询、渲染和路径分组全部出问题。
建议统一采用一种编码方式,例如地上楼层用 F1、F2,地下楼层用 B1、B2。
3. 坐标系混乱
WebGIS室内地图怎么做都绕不开坐标问题。如果室内数据是局部坐标,前端可以用自定义坐标系或简单二维坐标显示;如果需要叠加室外底图,则必须配准到真实坐标系。
常见错误包括:
- 把 CAD 毫米单位当作米使用。
- 把经纬度坐标直接用于米制距离计算。
- GeoJSON 坐标顺序写反。
- 前端以 EPSG:3857 显示,但后端返回 EPSG:4326 后没有转换。
4. 只做视觉路线,不做真实可通行网络
有些项目直接在前端画一条折线作为导航路线,看起来像导航,但不能自动避开墙体、门禁或不可通行区域。真正的室内导航功能实现必须依赖可通行网络和路径算法。
5. 跨楼层导航没有成本控制
电梯、楼梯、扶梯都可以连接不同楼层,但通行成本不一定相同。比如无障碍导航应优先使用电梯,普通导航可以使用楼梯。建议为不同通行类型设置不同 cost。
方法比较:室内地图前端和导航实现方案怎么选
| 方案 | 适合场景 | 优点 | 限制 |
|---|---|---|---|
| GeoJSON + Leaflet | 小型楼宇、教学演示、数据量较小 | 上手快,部署简单 | 大数据量性能一般,复杂样式能力有限 |
| GeoJSON + OpenLayers | 常规 WebGIS 室内地图项目 | GIS 能力强,坐标系和图层控制灵活 | 需要一定 WebGIS 基础 |
| PostGIS + 接口服务 | 多建筑、多楼层、需要查询统计 | 数据管理规范,便于扩展 | 需要后端开发和数据库维护 |
| PostGIS + pgRouting | 需要真实室内导航功能 | 路径计算能力成熟,可处理网络分析 | 导航网络建模要求高 |
| 矢量瓦片 + 前端渲染 | 大型商场、园区、机场、医院 | 加载快,适合大规模数据 | 制瓦片和样式维护成本更高 |
如果只是展示一栋楼的简单室内图,GeoJSON 加 OpenLayers 就够用。如果要做搜索、权限、导航、跨楼层、多建筑管理,建议使用 PostGIS 管理数据,并用后端接口提供楼层数据和路径计算服务。
检查清单:上线前逐项检查
- 是否为每个图层设置了统一的
building_id和floor_id。 - 房间、走廊、POI、导航网络是否分层存储。
- 楼层切换时是否只显示当前楼层数据。
- POI 搜索结果是否能定位到正确楼层。
- 导航网络线段是否在交叉口打断。
- 起点和终点是否能吸附到最近导航节点。
- 跨楼层路径是否经过电梯、楼梯或扶梯连接点。
- 路径结果是否按楼层分组显示。
- 坐标系、单位和前端投影是否一致。
- 移动端点击、缩放、标注遮挡是否测试过。
FAQ:WebGIS室内地图和导航功能常见问题
Q1:WebGIS室内地图怎么做,必须使用真实地理坐标吗?
不一定。如果只是展示建筑内部,不和室外地图叠加,可以使用局部平面坐标。但如果要在城市底图上显示建筑位置,或需要室内外一体化导航,就应该把室内地图配准到真实坐标系。
Q2:室内地图导航功能实现一定要用 pgRouting 吗?
不一定。pgRouting 适合已经使用 PostGIS 的项目。如果数据量很小,也可以用 Python、Node.js 或前端 JavaScript 实现 Dijkstra 算法。但从工程维护角度看,PostGIS 加 pgRouting 更适合多楼层、多建筑、可持续更新的项目。
Q3:CAD 平面图能不能直接作为 WebGIS 室内地图使用?
不建议直接使用。CAD 图纸通常缺少统一属性、楼层字段和导航拓扑。更稳妥的做法是先清理 CAD,再转换为 GeoJSON、GeoPackage 或 PostGIS 数据,并补充 POI 和导航网络。
Q4:室内导航为什么算出来的路径会穿墙?
通常是因为导航网络画错了,或者起终点吸附到了错误的节点。路径算法只认识网络边,不理解墙体含义。要避免穿墙,必须保证导航边只画在真实可通行区域,并检查门口、走廊、电梯和楼梯连接关系。
Q5:多楼层室内导航怎么显示比较合理?
建议把路径按楼层拆分显示。当前楼层显示当前楼层路线,跨楼层节点用电梯或楼梯图标提示。用户切换楼层时,再显示对应楼层的路径段。不要把所有楼层路线叠在同一个平面上,否则容易误导用户。
Q6:WebGIS室内地图适合用三维吗?
如果只是找房间、找店铺、找科室,二维室内地图通常更清晰。如果要展示大型综合体、地下空间、管线或数字孪生场景,可以考虑 Cesium、Three.js 或其他三维方案。但三维会增加建模、性能和交互成本。
结论:先做好数据,再实现导航
WebGIS室内地图怎么做,核心顺序是:先整理 CAD 或平面图,再建立楼层、房间、POI 和导航网络数据,最后用 WebGIS 前端进行楼层展示、搜索和路径高亮。
室内导航功能如何实现,关键不是画一条好看的线,而是构建真实可通行的网络,并用最短路径算法计算起点到终点的路线。对于小型项目,可以用 GeoJSON 加 OpenLayers 快速实现;对于正式工程,建议使用 PostGIS 管理室内数据,并结合 pgRouting 或后端路径服务完成导航计算。
只要楼层字段统一、坐标系清楚、导航网络连通,WebGIS 室内地图就能从“能看”进一步做到“能查、能搜、能导航”。