Dynamically change the radius of circle-based extrusion with Mapbox

早过忘川 提交于 2019-12-08 11:54:06

问题


I want to dynamically adjust the radius of circle-based extrusions with Mapbox based on the zoom level.

I have used for a toy dataset the solution provided by @stdob-- here and for which the JS Fiddle is available here.

The problem with that solution is that it is computationally very expensive and with my real dataset (more than a million point) this is not a viable solution. I therefore thought about using queryRenderedFeatures() as suggested in the comments of the previous SO posts. However even that is not giving me a good enough interactive visualization.

Instead, I therefore wanted to initially load all of my dataset and layers (including the 3D extrusions) and then on map-zoom events only recompute the radius that is going to be used for the 3D extrusions.

Here is the code I used:

Here is simple geojson file to reproduce the error with

{"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]}}]}

the following code:

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);
    }






});

The first issue I have is that it triggers a ReferenceError: layer is not defined on the line if (layer.paint) radiusPX = map.getLayer('my_initial_2D_layer').paint.get('circle-radius').evaluate();. This is probably due to the layer's style not being rendered yet, but it seems from the documentation and few Mapbox Questions on SO and on their GitHub that there is no way to check for that.

If I comment this line, this triggers later on in the code a Cannot read property 'setData' of undefined on the line map.getSource('extrusion').setData(data); and also that it prints it processed 0 objects which is quite problematic. I get the output from my console.log().

Finished preparing data for 0 objects

The second issue that I have is that I don't know how I could later modify the data of this extrusion layer. It seems that there is no function to get the data for my extrusion layer in order to just change its radius (as it seems that this cannot be done dynamically in the layer style).

Would anyone know how to proceed?

来源:https://stackoverflow.com/questions/52429753/dynamically-change-the-radius-of-circle-based-extrusion-with-mapbox

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!