visgl / deck.gl

WebGL2 powered visualization framework
https://deck.gl
MIT License
12.18k stars 2.09k forks source link

adding/deleting geojson layers, setProps method #3236

Closed bonellia closed 5 years ago

bonellia commented 5 years ago

To briefly provide context of my use case, I should mention that I'm trying to use a deck view in a country map where I dynamically add layers using geojson. There are two dropdown menus, one has cities and the other one has towns. I am initializing the map as it is done on following example: https://deck.gl/showcases/gallery/geojson-layer In that example, it generates the layer first and then initializes the map with the newly generated layer. My problem is, when I need to update map (where it zooms, with new layers that shows towns of cities) I simply can't get rid of previous city layers. As you can guess, I had success by generating a new map/layer pair but it's just inconvenient and plain bad practice.

In the following link, indeed there is a usage as such: https://deck.gl/showcases/gallery/highway However, I couldn't get my map to render new layers when I used it like "deckgl.setProps({layers});"

I'd like to have some tips regarding adding/deleting geojson layers and redrawing decks with different values. I searched various keywords and I failed to find examples that demonstrate usage of setProps() method.

In case the post is not relevant enough (given that it doesn't particularly point out any bugs), you could just take this as a request for a more detailed documentation.

I realize that I am struggling probably because I'm not very familiar with "The Reactive Programming Paradigm". While waiting for some tips, I'll be trying to update my layers by changing my geojson variables. Hopefully that'll do the trick.

Thanks in advance, Taha.

xintongxia commented 5 years ago

You can listen to onViewStateChange callback


const deckgl = new deck.new DeckGL({
  onViewStateChange: onViewStateChange 
  layers: [...]
});

function onViewStateChange({viewState}) {
   // decide which layers you would like to display
   const layers = [...];
   deckgl.setProps({layers});
}

Also you can toggle the visible prop of a layer.

Pessimistress commented 5 years ago

@bonellia deckgl.setProps({layers}) should work. Items added/removed from the layers array are then added/removed from deck. Can you share your code if you are seeing different result?

bonellia commented 5 years ago

I've managed to update deck object's layers props. By using geojson.io, I also confirmed that layers render on their tool. So the argument seems to be correct. The code is as follows:

    var tarimDeck;
    var typeColorList = {};
    $(document).ready(function () {

    // Set the map view zone.
    $("#TarlaMapView").css("height", $(window).height() - 200 + "px").css("width", "100%");

    const LIGHT_SETTINGS = {
        lightsPosition: [-125, 50.5, 5000, -122.8, 48.5, 8000],
        ambientRatio: 0.2,
        diffuseRatio: 0.5,
        specularRatio: 0.3,
        lightsStrength: [1.0, 0.0, 2.0, 0.0],
        numberOfLights: 2
    };

    // Get cities with geojson data to place initial layers.
    // Currently cities are pulled from a local file.
    var data = {};
    $.getJSON("./tr_iller_geo.json", function (file_content) {
        data = file_content;

        const visibleLayers = new deck.GeoJsonLayer({
            id: "current-layers",
            data,
            opacity: 0.6,
            stroked: false,
            filled: true,
            extruded: true,
            wireframe: true,
            fp64: true,
            lightSettings: LIGHT_SETTINGS,
            getElevation: f => (/* f.properties.alan ? (5 + 20 * f.properties
                    .ekiliAlan / f.properties.alan) :  */5555),
            getFillColor: f => /*(colorSetter(f.properties.urunType) ? colorSetter(f
                        .properties.urunType) : [0, 0, 0]) */random_rgba(),
            getLineColor: f => [0, 0, 0],
            pickable: true,
            onHover: updateTooltip
        });

        // Load the initial map view with city layers.
        tarimDeck = new deck.DeckGL({
            mapboxAccessToken: 'pk.eyJ1IjoiaGFiaWwyNCIsImEiOiJjanU5cHk1a3QwbGZwNGRuMHc4dHZsMGJwIn0.Yrkp8-SSLDqHTRCKzXd8DA',
            mapStyle: 'https://free.tilehosting.com/styles/positron/style.json?key=2OrAmqAgbK4HwBOq6vWN',
            container: 'TarlaMapView',
            latitude: 38,
            longitude: 35.8,
            zoom: 5.9,
            maxZoom: 16,
            pitch: 45,
            layers: [visibleLayers]
        });

        $("#il-dropdown").dropdown('setting', 'onChange', function () {
            // Actions Here
            var ilID = $("#il-dropdown").dropdown('get value');
            if (ilID) {
                $.ajax({
                    type: "POST",
                    url: 'getIlCEs',
                    dataType: 'json',
                    data: {
                        ilID: ilID
                    },
                    success: function (response) {
                        console.log("ilceler geldi", response);
                        var ilceFeatures = [];
                        response.forEach(x => {
                            if (x.geo) {
                                ilceFeatures.push(
                                    {
                                        "type": "Feature",
                                        "geometry": x.geo,
                                        "properties": {
                                            ilce_name: "salla_bisi"
                                        }
                                    });
                            }
                        });

                        var ilceFeatureCollection = {
                            "type": "FeatureCollection",
                            "features": ilceFeatures
                        };
                        console.log("ilce gejson: ", ilceFeatureCollection);
                        // Create new layers for  "ilce" geojson.
                        const newLayers = [new deck.GeoJsonLayer({
                            id: "current-layers",
                            data: ilceFeatureCollection,
                            opacity: 0.8,
                            stroked: false,
                            filled: true,
                            extruded: true,
                            wireframe: true,
                            fp64: true,
                            lightSettings: LIGHT_SETTINGS,
                            getElevation: f => (/* f.properties.alan ? (5 + 20 * f.properties
                    .ekiliAlan / f.properties.alan) :  */5555),
                            getFillColor: f => /*(colorSetter(f.properties.urunType) ? colorSetter(f
                        .properties.urunType) : [0, 0, 0]) */random_rgba(),
                            getLineColor: f => [0, 0, 0],
                            pickable: true,
                            onHover: updateTooltip

                        })];

                        tarimDeck.setProps(newLayers);
                        console.log("tarimdeck: ",tarimDeck);

                    },
                    error: function (err) {
                        console.log(err);
                    }
                });
            }

        });

    });

According to the documentation;

Layer ID

The id prop is the unique identifier of this layer among all layers. Constructing a new layer instance in its own does not have any performance impact, as deck.gl only does the expensive calculations when a layer is created (an id appearing for the first time) or updated (different props are passed in for the same id). Read more about this in layer lifecycle.

It is recommend that this prop is set explicitly to avoid collision.

I have generated one layer instance for cities (and initialized the deck with it) then on my city drop down menus' change listener, I generated a new layer instance with same id. This seems to suffice for updating my deck's layer props. However, it still shows city layers. I tried using redraw function, which also didn't help. Added true as "force" parameter.

bonellia commented 5 years ago

@Pessimistress one thing I noticed is that, after I update layer props, I am inspecting the deck object and on layers, state is "awaiting-state". Is that expected? I've read about layer lifecycles and I am a bit riddled when is it supposed to update the layers considering stuff I've already done. It seems to me like I'm updating it as intended, still the map won't update.

@xintongxia I have added your suggestion into my code (hopefully properly) in order to at least trigger the change when map is interacted (hover, drag, any of them).

            tarimDeck = new deck.DeckGL({
            mapboxAccessToken: 'pk.eyJ1IjoiaGFiaWwyNCIsImEiOiJjanU5cHk1a3QwbGZwNGRuMHc4dHZsMGJwIn0.Yrkp8-SSLDqHTRCKzXd8DA',
            mapStyle: 'https://free.tilehosting.com/styles/positron/style.json?key=2OrAmqAgbK4HwBOq6vWN',
            container: 'TarlaMapView',
            latitude: 38,
            longitude: 35.8,
            zoom: 5.9,
            maxZoom: 16,
            pitch: 45,
            onViewStateChange: onViewStateChange,
            layers: currentLayers
        });

        function onViewStateChange({ viewState }) {
            // decide which layers you would like to display
            tarimDeck.setProps({ currentLayers });
        }

Please note that I change the array "currentLayers" somewhere below in my code where a city is chosen on my drop-down list. Indeed it shows that city layers are not the current layers props of the deck, but district layers is set as the current layer props. But as I said above (I've been repeating myself too much) the view doesn't show the new layers as expected.

Screenshot from 2019-06-18 12-19-13

Pessimistress commented 5 years ago

tarimDeck.setProps(newLayers);

Should be

tarimDeck.setProps({layers: newLayers});

bonellia commented 5 years ago

@Pessimistress I don't know why I assumed the setProps function would understand what "key: value" pair is intended to be updated by just having the value. It's one of those situations where you are a bit embarrassed that the mistake is that simple, but you are also glad that you can fix it easily as well. Thanks for the help, this seems to solve my problem.