美文网首页GIS加油站
mapboxGL实现室内地图

mapboxGL实现室内地图

作者: 牛老师讲webgis | 来源:发表于2023-03-20 22:55 被阅读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} = feature
      const {floor, type} = properties
      if(!floors.includes(floor)) floors.push(floor)
      let height = (floor - 1) * floorHeight + buildHeight
      let base = height - buildHeight
      if(type === '999') height = base + baseHeight
      properties.height = height
      properties.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.children
      for (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-height
        map.setPaintProperty('building-height', 'fill-extrusion
        map.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-extrusion
        map.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} = properties
      id = type === '999' ? null : id
      map.setFilter('building-height', ['==', ['get', 'id'], id]);
    })
    

    相关文章

      网友评论

        本文标题:mapboxGL实现室内地图

        本文链接:https://www.haomeiwen.com/subject/sxmprdtx.html