效果
准备
我们这里只以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)
}