使用Mapbox动态更改基于圆的拉伸的半径

时间:2018-09-20 16:46:23

标签: javascript maps mapbox mapbox-gl-js

我想根据缩放级别使用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个对象完成准备数据

我遇到的第二个问题是,我不知道以后如何修改该拉伸层的数据。似乎没有任何功能可以更改我的挤出图层的数据,以仅更改其半径(似乎无法以图层样式动态完成)。

有人会继续吗?

0 个答案:

没有答案