我想根据缩放级别使用Mapbox动态调整基于圆的拉伸的半径。
我已将@stdob提供的解决方案用于玩具数据集-here 并为其提供JS小提琴here。
该解决方案的问题在于它在计算上非常昂贵,而对于我的真实数据集(超过一百万个点),这不是一个可行的解决方案。因此,我考虑过按照之前的SO帖子的建议使用queryRenderedFeatures()
。但是,即使那样也不能给我足够好的交互式可视化效果。
因此,我希望最初加载所有数据集和图层(包括3D拉伸),然后在map-zoom事件中仅重新计算将用于3D拉伸的半径。
这是我使用的代码:
这是一个简单的geojson文件,用于重现错误
{"type": "FeatureCollection", "features": [{"id": 1, "type": "Feature", "properties": {"x": 1.0, "group": 1, "my_property": 217}, "geometry": {"type": "Point", "coordinates": [8.539961, 47.37347]}}, {"id": 2, "type": "Feature", "properties": {"x": 2.0, "group": 1, "my_property": 520}, "geometry": {"type": "Point", "coordinates": [8.517961, 47.37520]}}]}
以下代码:
HTML:
<html>
<head>
<meta charset='utf-8' />
<title>Display buildings in 3D</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.css' rel='stylesheet' />
<script src='https://npmcdn.com/@turf/turf/turf.min.js'></script>
<script src="https://unpkg.com/supercluster@4.1.1/dist/supercluster.min.js"></script>
</head>
<body>
<div id='map'></div>
<script>
</script>
</body>
</html>
CSS:
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
JS:
mapboxgl.accessToken = 'pk.eyJ1IjoibG9ubmliZXNhbmNvbiIsImEiOiJjamxjaWNpOHQwMHV0M3FwaHhneGhvY2l2In0.7GxI8W_dnTKITNF4hEvZeQ';
var map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/light-v9',
center:[8.538961, 47.37247],
zoom: 10,
pitch: 20,
bearing: 0,
container: 'map'
});
var url = "REPLACE WITH GEOJSON LOCATION"
//
var zoom_level_3D_bars = 14
var radius_zoom_d = 10
var map_zoom = 10
map.on('load', function() {
// Insert the layer beneath any symbol layer.
var layers = map.getStyle().layers;
var labelLayerId;
for (var i = 0; i < layers.length; i++) {
if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
labelLayerId = layers[i].id;
break;
}
}
map.addSource("data", {
type: "geojson",
data: url,
});
map.addLayer({
'id': 'extrusion',
'type': 'fill-extrusion',
'minzoom': zoom_level_3D_bars,
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": []
}
},
'source': 'data',
'paint': {
'fill-extrusion-height': ['/', ['number', ['get', 'my_property'],0], 10],
'fill-extrusion-base': 0,
'fill-extrusion-opacity': 0.5
}
});
map.addLayer({
'id': 'population',
'type': 'circle',
'source': 'data',
'paint': {
'circle-color': {
'property': 'group',
'type': 'categorical',
stops: [
[1, 'rgba(252,141,98,1)'],
[2, 'rgba(102,194,165,1)'],
[3, 'rgba(102,194,165,1)'],
[4, 'rgba(102,194,165,1)'],
[5, 'rgba(102,194,165,1)'],
[6, 'rgba(102,194,165,1)'],
//'4', '#3bb2d0',
/* other 'rgba(102,194,165,0.1)'*/
]
},
}
});
map.on('data', function() {
//if (!firstTower) updateTower();
//});
//console.log("Initialize")
//initializeTower();
})
map.on('zoom', function() {
map_zoom = map.getZoom();
if(map.isSourceLoaded('data') == false){
return
}
if(map_zoom < zoom_level_3D_bars){
map.setPaintProperty('population', 'circle-radius', radius_zoom_d);
if(map.getPaintProperty('population','circle-opacity') != 1){
map.setPaintProperty('population', 'circle-opacity', 1)
}
}
radius_zoom_d = 10 - (map_zoom/2)
if(map_zoom >= zoom_level_3D_bars){
opacity_point = 0
console.log("Update tower bc zoom = "+map_zoom)
if(map.getPaintProperty('population','circle-opacity') != 0){
map.setPaintProperty('population', 'circle-opacity', 0)
}
updateTower();
}
})
function updateTower() {
var radiusPX = false;
var layer = map.getLayer('population')
if (layer.paint) radiusPX = map.getLayer('population').paint.get('circle-radius').evaluate();
if (radiusPX === false) return;
var data = {
"type": "FeatureCollection",
"features": []
}
//HERE IS THE PART where I would like to change the radius without having to take
// all the querySourceFeatures or queryRenderedFeatures for performance issues
//But I don't know how to just go through the dataset of the layer extrusion
}
map.on('data', function(e) {
// if (e.sourceId !== 'total') return
if (e.sourceId !== 'data') return
if (e.isSourceLoaded !== true) return
initializeTower()
})
//map.on('sourcedata', sourceCallback);
function initializeTower(){
if (layer.paint) radiusPX = map.getLayer('population').paint.get('circle-radius').evaluate();
if (radiusPX === false) return;
var nb_of_objects = 0
var data = {
"type": "FeatureCollection",
"features": []
}
map.querySourceFeatures('data').forEach(function(f) {
var object = turf.centerOfMass(f)
var center = object.geometry.coordinates
var xy = map.project(center)
xy.x += radiusPX;
var LL = map.unproject(xy)
LL = turf.point([LL.lng, LL.lat])
//var radius = turf.distance(center, LL, {
// units: 'meters'
//}) + 0.00000001
var radius = radius_zoom_d ;
var options = {
steps: 16,
units: 'meters',
properties: object.properties
};
data.features.push(turf.circle(center, radius, options))
nb_of_objects +=1
})
console.log("Finished preparing data for "+nb_of_objects+" objects")
map.getSource('extrusion').setData(data);
}
});
我遇到的第一个问题是它触发了ReferenceError: layer is not defined
行上的if (layer.paint) radiusPX = map.getLayer('my_initial_2D_layer').paint.get('circle-radius').evaluate();
。这可能是由于尚未渲染该图层的样式,但从文档以及SO及其GitHub上的一些Mapbox问题看来,没有办法对其进行检查。
如果我注释此行,则稍后会在代码Cannot read property 'setData' of undefined
上的代码map.getSource('extrusion').setData(data);
中触发,并且还会打印出已处理的0个对象,这很成问题。我从console.log()
获得了输出。
为0个对象完成准备数据
我遇到的第二个问题是,我不知道以后如何修改该拉伸层的数据。似乎没有任何功能可以更改我的挤出图层的数据,以仅更改其半径(似乎无法以图层样式动态完成)。
有人会继续吗?