mapboxGL实现室内地图
迪丽瓦拉
2025-06-01 07:44:45
0

概述

本文分享如何通过mapboxGL实现三维的室内地图的展示。

效果

全部楼层

单楼层

高亮选中

实现

1. 数据

精确的数需要通过CAD转换,本文为简单演示,是通过qgis中绘制的,数据主要包括如下字段:

{ "id": 1, "name": "type1","floor": 1, "type": "1" }

其中:

  • floor为楼层数据,建议为数字,方便排序;
  • type为类型,为商户的类型,通过type渲染不同的颜色;文中为了展示楼层地面,加了一个特殊的类型999,;
    示例中用的完整数据如下:
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "id": 1, "name": "type1","floor": 1, "type": "1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.88576766738845, 22.55132808900947 ], [ 113.88689629860265, 22.55132808900947 ], [ 113.888041775058838, 22.550081541101257 ], [ 113.887629066629771, 22.549550915978166 ], [ 113.887283739168709, 22.549525648115164 ], [ 113.886070881744502, 22.549727791019198 ], [ 113.885708709041452, 22.550089963722261 ], [ 113.88576766738845, 22.55132808900947 ] ] ] } },
{ "type": "Feature", "properties": { "id": 2, "name": "type3","floor": 1, "type": "3" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.888269185825877, 22.550536362635334 ], [ 113.888134423889866, 22.550511094772332 ], [ 113.887805941670806, 22.550789041265379 ], [ 113.887014215296674, 22.55160603550252 ], [ 113.886129840091513, 22.551597612881515 ], [ 113.885936119808491, 22.551841868890559 ], [ 113.885986655534495, 22.552692553611703 ], [ 113.886331982995557, 22.552540946433677 ], [ 113.887090018885687, 22.552321958287639 ], [ 113.887511149935747, 22.551892404616567 ], [ 113.887991239332834, 22.551437583082489 ], [ 113.888505019213923, 22.550949071064405 ], [ 113.888715584738961, 22.550957493685406 ], [ 113.888724007359954, 22.55067954719236 ], [ 113.8883871025199, 22.550452136425321 ], [ 113.888269185825877, 22.550536362635334 ] ] ] } },
{ "type": "Feature", "properties": { "id": 3, "name": "type2","floor": 1, "type": "2" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.886171953196524, 22.55290311913674 ], [ 113.887014215296674, 22.553736958615882 ], [ 113.887300584410724, 22.553602196679858 ], [ 113.887595376145768, 22.55366115502687 ], [ 113.889541001597095, 22.551791333164548 ], [ 113.889322013451064, 22.551504964050501 ], [ 113.88943150752408, 22.551353356872475 ], [ 113.889119870547034, 22.551142791347438 ], [ 113.888841924053978, 22.551125946105437 ], [ 113.887704870218784, 22.552246154698626 ], [ 113.887755405944802, 22.552473565465665 ], [ 113.887477459451745, 22.552642017885695 ], [ 113.886972102191663, 22.552515678570675 ], [ 113.886281447269539, 22.552768357200716 ], [ 113.886171953196524, 22.55290311913674 ] ] ] } },
{ "type": "Feature", "properties": { "id": 4, "name": "type3","floor": 2, "type": "3"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.88576766738845, 22.55132808900947 ], [ 113.88689629860265, 22.55132808900947 ], [ 113.888041775058838, 22.550081541101257 ], [ 113.887629066629771, 22.549550915978166 ], [ 113.887283739168709, 22.549525648115164 ], [ 113.886070881744502, 22.549727791019198 ], [ 113.885708709041452, 22.550089963722261 ], [ 113.88576766738845, 22.55132808900947 ] ] ] } },
{ "type": "Feature", "properties": { "id": 5, "name": "type2","floor": 2, "type": "2"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.888269185825877, 22.550536362635334 ], [ 113.888134423889866, 22.550511094772332 ], [ 113.887805941670806, 22.550789041265379 ], [ 113.887014215296674, 22.55160603550252 ], [ 113.886129840091513, 22.551597612881515 ], [ 113.885936119808491, 22.551841868890559 ], [ 113.885986655534495, 22.552692553611703 ], [ 113.886331982995557, 22.552540946433677 ], [ 113.887090018885687, 22.552321958287639 ], [ 113.887511149935747, 22.551892404616567 ], [ 113.887991239332834, 22.551437583082489 ], [ 113.888505019213923, 22.550949071064405 ], [ 113.888715584738961, 22.550957493685406 ], [ 113.888724007359954, 22.55067954719236 ], [ 113.8883871025199, 22.550452136425321 ], [ 113.888269185825877, 22.550536362635334 ] ] ] } },
{ "type": "Feature", "properties": { "id": 6, "name": "type1","floor": 2, "type": "1"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.886171953196524, 22.55290311913674 ], [ 113.887014215296674, 22.553736958615882 ], [ 113.887300584410724, 22.553602196679858 ], [ 113.887595376145768, 22.55366115502687 ], [ 113.889541001597095, 22.551791333164548 ], [ 113.889322013451064, 22.551504964050501 ], [ 113.88943150752408, 22.551353356872475 ], [ 113.889119870547034, 22.551142791347438 ], [ 113.888841924053978, 22.551125946105437 ], [ 113.887704870218784, 22.552246154698626 ], [ 113.887755405944802, 22.552473565465665 ], [ 113.887477459451745, 22.552642017885695 ], [ 113.886972102191663, 22.552515678570675 ], [ 113.886281447269539, 22.552768357200716 ], [ 113.886171953196524, 22.55290311913674 ] ] ] } },
{ "type": "Feature", "properties": { "id": 7, "name": "type2","floor": 3, "type": "2"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.88576766738845, 22.55132808900947 ], [ 113.88689629860265, 22.55132808900947 ], [ 113.888041775058838, 22.550081541101257 ], [ 113.887629066629771, 22.549550915978166 ], [ 113.887283739168709, 22.549525648115164 ], [ 113.886070881744502, 22.549727791019198 ], [ 113.885708709041452, 22.550089963722261 ], [ 113.88576766738845, 22.55132808900947 ] ] ] } },
{ "type": "Feature", "properties": { "id": 8, "name": "type3","floor": 3, "type": "3"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.888269185825877, 22.550536362635334 ], [ 113.888134423889866, 22.550511094772332 ], [ 113.887805941670806, 22.550789041265379 ], [ 113.887014215296674, 22.55160603550252 ], [ 113.886129840091513, 22.551597612881515 ], [ 113.885936119808491, 22.551841868890559 ], [ 113.885986655534495, 22.552692553611703 ], [ 113.886331982995557, 22.552540946433677 ], [ 113.887090018885687, 22.552321958287639 ], [ 113.887511149935747, 22.551892404616567 ], [ 113.887991239332834, 22.551437583082489 ], [ 113.888505019213923, 22.550949071064405 ], [ 113.888715584738961, 22.550957493685406 ], [ 113.888724007359954, 22.55067954719236 ], [ 113.8883871025199, 22.550452136425321 ], [ 113.888269185825877, 22.550536362635334 ] ] ] } },
{ "type": "Feature", "properties": { "id": 9, "name": "type1","floor": 3, "type": "1"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.886171953196524, 22.55290311913674 ], [ 113.887014215296674, 22.553736958615882 ], [ 113.887300584410724, 22.553602196679858 ], [ 113.887595376145768, 22.55366115502687 ], [ 113.889541001597095, 22.551791333164548 ], [ 113.889322013451064, 22.551504964050501 ], [ 113.88943150752408, 22.551353356872475 ], [ 113.889119870547034, 22.551142791347438 ], [ 113.888841924053978, 22.551125946105437 ], [ 113.887704870218784, 22.552246154698626 ], [ 113.887755405944802, 22.552473565465665 ], [ 113.887477459451745, 22.552642017885695 ], [ 113.886972102191663, 22.552515678570675 ], [ 113.886281447269539, 22.552768357200716 ], [ 113.886171953196524, 22.55290311913674 ] ] ] } },
{ "type": "Feature", "properties": { "id": 10, "name": "","floor": 1, "type": "999"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.885632905452397, 22.552818892926712 ], [ 113.885771878698918, 22.553046303693751 ], [ 113.886302503822009, 22.553484279985827 ], [ 113.887106864127674, 22.554212836702447 ], [ 113.887443768967728, 22.554246527186454 ], [ 113.888075465542826, 22.55364430978485 ], [ 113.888214438789362, 22.553412687707311 ], [ 113.890008457062649, 22.551715529575528 ], [ 113.889991611820648, 22.55150075273999 ], [ 113.88778909642879, 22.549538282046655 ], [ 113.88754484041975, 22.549454055836641 ], [ 113.887182667716687, 22.549407731421134 ], [ 113.886353039548055, 22.54956776122016 ], [ 113.885784512630451, 22.549727791019198 ], [ 113.88556552448442, 22.550098386343258 ], [ 113.885632905452397, 22.552818892926712 ] ] ] } },
{ "type": "Feature", "properties": { "id": 11, "name": "","floor": 2, "type": "999"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.885632905452397, 22.552818892926712 ], [ 113.885771878698918, 22.553046303693751 ], [ 113.886302503822009, 22.553484279985827 ], [ 113.887106864127674, 22.554212836702447 ], [ 113.887443768967728, 22.554246527186454 ], [ 113.888075465542826, 22.55364430978485 ], [ 113.888214438789362, 22.553412687707311 ], [ 113.890008457062649, 22.551715529575528 ], [ 113.889991611820648, 22.55150075273999 ], [ 113.88778909642879, 22.549538282046655 ], [ 113.88754484041975, 22.549454055836641 ], [ 113.887182667716687, 22.549407731421134 ], [ 113.886353039548055, 22.54956776122016 ], [ 113.885784512630451, 22.549727791019198 ], [ 113.88556552448442, 22.550098386343258 ], [ 113.885632905452397, 22.552818892926712 ] ] ] } },
{ "type": "Feature", "properties": { "id": 12, "name": "","floor": 3, "type": "999"}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 113.885632905452397, 22.552818892926712 ], [ 113.885771878698918, 22.553046303693751 ], [ 113.886302503822009, 22.553484279985827 ], [ 113.887106864127674, 22.554212836702447 ], [ 113.887443768967728, 22.554246527186454 ], [ 113.888075465542826, 22.55364430978485 ], [ 113.888214438789362, 22.553412687707311 ], [ 113.890008457062649, 22.551715529575528 ], [ 113.889991611820648, 22.55150075273999 ], [ 113.88778909642879, 22.549538282046655 ], [ 113.88754484041975, 22.549454055836641 ], [ 113.887182667716687, 22.549407731421134 ], [ 113.886353039548055, 22.54956776122016 ], [ 113.885784512630451, 22.549727791019198 ], [ 113.88556552448442, 22.550098386343258 ], [ 113.885632905452397, 22.552818892926712 ] ] ] } }
]
}

对上面的数据加以处理,添加heightbase字段,用以展示楼层3d效果。

let floors = []
const floorHeight = 55, buildHeight = 7, baseHeight = 1
res.features.forEach(feature => {const {properties} = featureconst {floor, type} = propertiesif(!floors.includes(floor)) floors.push(floor)let height = (floor - 1) * floorHeight + buildHeightlet base = height - buildHeightif(type === '999') height = base + baseHeightproperties.height = heightproperties.base = base
})

2. 添加图层

通过mapboxGL中的fill-extrusion图层实现楼层和商户的展示,初始化style如下:

const style = {version: 8,glyphs: "https://lzugis.cn/fonts/{fontstack}/{range}.pbf",sources: {'building': {type: 'geojson',data: res}},layers: [{"id": "sky","type": "sky","paint": {"sky-gradient": "rgba(0, 0, 0, 1)","sky-opacity": 0.1}},{'id': 'building','source': 'building','type': 'fill-extrusion','filter': ["==", ['get', 'floor'], 0],'paint': {'fill-extrusion-color': ['match',['get', 'type'],'999', '#ccc','1', '#FFD273','2', '#E86D68','#A880FF'],'fill-extrusion-base': ['get', 'base'],'fill-extrusion-height': ['get', 'height'],'fill-extrusion-opacity': 0.45}},{'id': 'building-height','source': 'building','type': 'fill-extrusion','filter': ["==", ['get', 'floor'], 0],'paint': {'fill-extrusion-color': '#0ff','fill-extrusion-base': ['get', 'base'],'fill-extrusion-height': ['get', 'height'],'fill-extrusion-opacity': 0.45}},{'id': 'building-label','type': 'symbol','source': 'building','filter': ["==", ['get', 'floor'], 0],'layout': {'text-field': ['get', 'name'],"text-size": 14},paint: {'text-color': '#999'}}]
}

地图的初始化配置如下:

{container: 'map',center: [113.88768248794844, 22.551640034479163],hash: false,zoom: 16.6,style,pitch: 65,bearing: 120
}

3. 添加楼层控制UI

此处,单楼层和展示全部楼层的fill-extrusion-basefill-extrusion-height配置有区别。

const ul = document.createElement('ul')
ul.classList.add('floor-control')
const showFloor = (floor = 'All') => {floor = floor === 'All' ? floor : parseInt(floor)const lis = ul.childrenfor (let i = 0; i < lis.length; i++) {const li = lis[i]li.classList.remove('active')if(floors.indexOf(floor) === i) li.classList.add('activ}map.setFilter('building', [floor === 'All' ? '!=' : '==',map.setFilter('building-label', ['==', ['get', 'floor'], if(floor === 'All') {map.setPaintProperty('building', 'fill-extrusion-base',map.setPaintProperty('building', 'fill-extrusion-heightmap.setPaintProperty('building-height', 'fill-extrusionmap.setPaintProperty('building-height', 'fill-extrusion} else {map.setPaintProperty('building', 'fill-extrusion-base',map.setPaintProperty('building', 'fill-extrusion-height'match',['get', 'type'],'999', baseHeight,buildHeight]);map.setPaintProperty('building-height', 'fill-extrusionmap.setPaintProperty('building-height', 'fill-extrusion'match',['get', 'type'],'999', baseHeight,buildHeight]);}
}
const addFloorsUI = function () {floors.splice(0, 0, 'All')floors.forEach(floor => {const li = document.createElement('li')ul.appendChild(li)li.innerText = floor === 'All' ? floor : floor + 'F'li.setAttribute('floor', floor)li.onclick = function () {const floor = this.getAttribute('floor')showFloor(floor)}})document.body.appendChild(ul)showFloor()
}
map.on('load', addFloorsUI)

4. 添加点击交互

注册地图的click事件,用以取消选中,注册图层的click事件,用以选中。

map.on('click', e => {map.setFilter('building-height', ['==', ['get', 'id'], null]);
})
map.on('click', 'building', e => {const { properties } = e.features[0]let {id, type} = propertiesid = type === '999' ? null : idmap.setFilter('building-height', ['==', ['get', 'id'], id]);
})

相关内容