MapboxGL添加三维柱状图效果

效果

三维地图效果

准备

我们这里只以html+jQuery+css纯demo写法展示如何叠加三维柱状图图层效果!
我们需要mapboxGL的js、css和jQuery.js

    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="mapbox-gl.min.js"></script>
    <link type="text/css" rel="stylesheet" href="mapbox-gl.min.css">

还有自己定义的js文件

	 <script type="text/javascript" src="decklayer.js"></script>
	 <script type="text/javascript">
	     styleLoad();
	 </script>

页面添加图层

定义好map容器后在js里将地图添加到容器中

    <div id="map"></div>
 	var url = "https://iserver.supermap.io/iserver/services/map-china400/rest/maps/China_4326";
    var map, popup;
    var attribution = "3Dbar-demo";

    map = new mapboxgl.Map({
        container: 'map',
        style: {
            "version": 8,
            "sources": {
                "raster-tiles": {
                    "attribution": attribution,
                    "type": "raster",
                    "tiles": [url + '/zxyTileImage.png?z={z}&x={x}&y={y}'],
                    "tileSize": 256,
                },
            },
            "layers": [{
                "id": "simple-tiles",
                "type": "raster",
                "source": "raster-tiles",
                "minzoom": 0,
                "maxzoom": 22
            }]
        },
        center: [104.038567,30.650149],
        zoom: 13,
        pitch: 40,
        bearing: 0
    });
    map.addControl(new mapboxgl.NavigationControl(), 'top-left');

点数据转换矩形面数据

我的数据集是geojson点数据集格式

  	/**
     * 初始化数据
     */
    var geometry = {
        "features":[
            {
                "geometry":{
                    "coordinates":[
                        []
                    ],
                    "type":"Polygon"
                },
                "type":"Feature",
                "properties":{
                    "id": 1,
                    "name": "entity1",
                    "height": 100,
                    "smx":104.038679,
                    "smy":30.650071
                }
            },
            {
                "geometry":{
                    "coordinates":[
                        []
                    ],
                    "type":"Polygon"
                },
                "type":"Feature",
                "properties":{
                    "id": 2,
                    "name": "entity2",
                    "height": 500,
                    "smx":104.036025,
                    "smy":30.640793
                }
            },
            {
                "geometry":{
                    "coordinates":[
                        []
                    ],
                    "type":"Polygon"
                },
                "type":"Feature",
                "properties":{
                    "id": 3,
                    "name": "entity3",
                    "height": 300,
                    "smx":104.072101,
                    "smy":30.639628
                }
            },
            {
                "geometry":{
                    "coordinates":[
                        []
                    ],
                    "type":"Polygon"
                },
                "type":"Feature",
                "properties":{
                    "id": 4,
                    "name": "entity4",
                    "height": 700,
                    "smx":104.071181,
                    "smy":30.664718
                }
            },
            {
                "geometry":{
                    "coordinates":[
                        []
                    ],
                    "type":"Polygon"
                },
                "type":"Feature",
                "properties":{
                    "id": 5,
                    "name": "entity5",
                    "height": 1000,
                    "smx":104.091878,
                    "smy":30.680124
                }
            },
            {
                "geometry":{
                    "coordinates":[
                        []
                    ],
                    "type":"Polygon"
                },
                "type":"Feature",
                "properties":{
                    "id": 6,
                    "name": "entity6",
                    "height": 200,
                    "smx":104.099927,
                    "smy":30.659747
                }
            }
        ],
        "type":"FeatureCollection"
    };

如果你的柱状图采用的是点数据集,(面数据集可以忽略此步骤)那么你得定义一个转换数据集的方式

 	/**
     * 根据点数据生成矩形面数据
     * @param {点数据对象} properties object
     * @param {偏移量} offset number
     */
    function createSquaredByPoint(properties,offset){
        var smx = properties.smx;
        var smy = properties.smy;
        var offset_int = parseFloat(offset);
        return [[smx,smy],[smx,smy + offset_int],[smx - offset_int,smy + offset_int],[smx - offset_int,smy],[smx,smy]];
    }

其中,偏移量(offset)代表你希望你的面数据矩形在地图上有多宽。
然后用上面的方式,将点数据集转换成面数据集:


    /**
     * 生成面数据
     * @param {原始geometry数据} feature 
     * @param {高度加权值} weight 
     * @param {宽度加权值} breadth 
     */
    function getGeometry(feature,weight,breadth){
        feature.features.forEach(item => {
            if (item.geometry && item.geometry.coordinates) {
                item.geometry.coordinates[0] = createSquaredByPoint(item.properties,breadth);
                item.properties.height = item.properties.height * weight;
            } else {
                console.log("item.geometry.coordinates 不存在");
            }
            
        })
        console.log(feature);
    }

加载三维柱形图层

有了面数据集,那我们直接用mapbox的 拉面图层 fill-extrusion 拉起面数据集

		// 这里我们高度加权值为5倍,宽度加权值为0.0025经纬度(大约为经度0.21km-纬度0.27km)
 		getGeometry(feature,5,0.0025);
        var BoxMap = map;

        if(BoxMap.getLayer('entity_layer')){
            BoxMap.removeLayer('entity_layer');
        };
        if(BoxMap.getLayer('entity_borders')){
            BoxMap.removeLayer('entity_borders');
        };

        feature.features.forEach(item => {
            let he = 0;  
        if (item.properties.height === "" || item.properties.height == 0) {
            he = 5;   //默认高度
        }else {
            he = item.properties.height;
        }
        //属性数据
        item.properties.id = parseInt(item.properties.id);
        item.properties.height = parseInt(he);
        });

        if(BoxMap.getSource("states")){
            BoxMap.getSource("states").setData(feature)
        }else{
            BoxMap.addSource("states", {
                "type": "geojson",
                "data": feature
            });
        }

        BoxMap.addLayer({
            'id': 'entity_layer',
            'source': 'states',
            'type': 'fill-extrusion',
            'layout': {},
            'minzoom': 2,       //最小可见级别
            'paint': {
                //设置主体默认颜色
                'fill-extrusion-color': [
                    "interpolate",
                    ["exponential",1],
                    ["get", "height"],
                    //分类颜色设置(必须按顺序)
                    500,
                    '#d6debf',
                    1000,
                    '#aecea1',
                    1500,
                    '#82bb92',
                    2500,
                    '#49838a',
                    3500,
                    '#383c65',
                    5000,
                    '#2b1e3d'
                ],
                'fill-extrusion-height': [
                    "interpolate", ["linear"], ["zoom"],4, 0,14.05, ["get", "height"]
                   
                ],
                'fill-extrusion-base': [
                    "interpolate", ["linear"], ["zoom"],4, 0,14.05, ["get", "base_height"]
                ],
                'fill-extrusion-opacity': .8         //透明度
            }
        });

		//添加一个鼠标点击变色的同数据图层(这个默认隐藏)
        BoxMap.addLayer({
            'id': 'entity_borders',
            'source': 'states',
            'type': 'fill-extrusion',
            'layout': {},
            'minzoom': 2,         //最小可见级别
            'paint': {
                'fill-extrusion-color': '#b8c9dd',
                'fill-extrusion-height': ['get', 'height'],
                'fill-extrusion-base': ['get', 'base_height'],
                'fill-extrusion-opacity': 0.7       //图层透明度
            },
            "filter": ["in", "id", ""]
        });

加入事件和气泡

 		let popups = new mapboxgl.Popup({closeButton: false,closeOnClick: false});

        BoxMap.on("mousemove", "entity_layer", function(e) {
            BoxMap.getCanvas().style.cursor = 'pointer';
            let feature = e.features[0];
    
            let relatedFeatures = BoxMap.querySourceFeatures('states', {
              filter: ['in', 'id', feature.properties.id]
            });
    
            let filter = relatedFeatures.reduce(function(memo, feature) {
              memo.push(feature.properties.id);
              return memo;
            }, ['in', 'id']);
    
            BoxMap.setFilter("entity_borders", filter);
    
            //气泡信息(自定义html样式)
            let html = 
              "<h2 style='color: white'> 主键:<a style='color: orange'>"+feature.properties.id+"</a> </h2>" +
              "<h2 style='color: white'> 名称:<a style='color: orange'>" +  feature.properties.name + "</a></h2>" +
              "<h2 style='color: white'> 数量:<a style='color: orange'>" +  feature.properties.height + " 米</a></h2>";
    
            popups.setLngLat([feature.properties.smx, feature.properties.smy])
              .setHTML(html)
              .addTo(BoxMap);
    
          });

          BoxMap.on("mouseleave", "entity_layer", function() {
            BoxMap.getCanvas().style.cursor = '';
            BoxMap.setFilter('entity_borders', ['in', 'id', '']);
            popups.remove();
    
          });

还有一个小问题

我们发现,每次页面刷新的时候地图style并不是马上就加载完成,但是此图层需要等到style加载完成后才能显示出来,不然会报style not loaded错误!所有我们需要一个监听(递归)图层样式加载完成方法,等style加载完成后再来加载图层!


    /**
     * 等mapbox.style加载完再执行添加图层
     */
    function styleLoad(){
        setTimeout(function(){
            if (map.isStyleLoaded()) {
                addLayer(geometry);
            } else {
                console.log("wait...");
                styleLoad();
            }
        },1000)
    }

下载

demo地址

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sun_falls

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值