0

I have several 3D models (animated arrows) that I dynamically add to the map as a user clicks on buttons. Some buttons may have 3D models, while others do not.

Whenever I click on a button that does not have any 3D model, I remove all previously added models:

    function clearMapAnimations() {
        console.log("CLEARING MAP ANIMATIONS");
        // Loop through all layers with arrows and hide them
        toggleableLayerIds.forEach(layer => {
    
            const layer_name = "layer_" + layer;
            console.log ("Layer visibility is none: " + layer_name);
            map.setLayoutProperty(layer_name, 'visibility', 'none');
        });
    }

The toggleableLayerIds is an array that holds all of the ids of the layers previously added.

If I comment out the map.setLayoutProperty(layer_name, 'visibility', 'none'); the expected behaviour exists.... the arrows/layers remain on the map and the map does not turn white. So, I have narrowed down the problem to this line.

I should also note that when I add a custom layer to the map I make its 'visibility' property 'visible'.

        const arrow_layer = addArrow(lyr_id, map, lng_lat, rot, arrow_length, arrow_type);
        map.addLayer(arrow_layer, 'waterway-label');
        map.setLayoutProperty(lyr_id, 'visibility', 'visible');

The addArrow function does a lot of heavy lifting in terms of assigning the origin, rotation, length of the model, and also deals with the animation.

There is an onRemove: function (map, gl) { } that is not currently implemented. Do I need to do something there if I'm hiding the layer and not removing it?

I appreciate any help you can give me! Thanks

M -
  • 26,908
  • 11
  • 49
  • 81
M. Black
  • 353
  • 1
  • 4
  • 20
  • Sorry, but Mapbox is not Three.js – M - Feb 27 '23 at 19:43
  • I am using three.js and GLTFLoader to load the 3D models into a custom layer on the map. I didn't add that part of the code because I didn't think it was relevant. Mostly it deals with handling the parameters passed to the addArrow() function in handling the model as Mercator coordinates, animation, rotating and sizing of the model before it's displayed. – M. Black Feb 28 '23 at 17:38

2 Answers2

0

I think your problem might be that you're appending "layer_" to the layer ID. Have you tried passing just the ID without that prefix on the string? That's what the API documentation recommends

toggleableLayerIds.forEach(layer => {
    console.log ("Layer visibility is none: " + layer);
    map.setLayoutProperty(layer, 'visibility', 'none');
});
M -
  • 26,908
  • 11
  • 49
  • 81
  • When I create a layer for the arrow, the name/id is set to "layer_1", "layer_3", etc. This is consistent for both setting the layer's visibility property to 'none' or 'visible'. I know that part is working because I can see the arrows being visible or hidden when they should be. However, when the map finished the fly-in/out effect, the entire map turns white. – M. Black Feb 28 '23 at 17:33
  • It looks like you're iterating through an array and indiscriminately turning visibility off on all elements in that array. Are you sure you're not turning off all layers and that's why you're not seeing any of them? What's the contents of `toggleableLayerIds`? – M - Feb 28 '23 at 17:36
  • It is probably not the most efficient method, but I'm using toggleableLayerIds to keep track of the arrows/layers ids that are being added to the map. The only values added are arrows. Any time I need to clearMapAnimations() I hide all arrow layers (if they exist). Afterwards, if needed, new arrow layers will be created or hidden layers that need to be visible, will be appropriately changed. – M. Black Feb 28 '23 at 17:43
0

This took entirely too long for a solution that was quite simple.

After you set the visibility of the layer to 'none' you must repaint the map.

    map.setLayoutProperty(layer, 'visibility', 'none');
    map.repaint = true;

For some reason even map.triggerRepaint() did not work, however setting the repaint property to true does.

Some other things I tried that did not work: When dealing with configuration of the custom layer for a 3D model per the CustomLayerInterface, you must make sure your map's projection is in 'mercator' projection. Otherwise you will get some strange behaviours including your entire map going blank. I tried changing the projection to 'mercator' when the 3D models were on the map, and when those layers were removed changing it to a different type of projection (did not work).

I also read somewhere that the custom layers must have IDs that can be cast into integers (even though they are stored as strings), but I have found no difference when doing that with strings ('layer_1') as opposed to integers ('1').

One strange behaviour is that when I printed out the layers right after I added a custom layer, the custom layer was not printed out in the list:

    var layers = map.getStyle().layers;

    var layerIds = layers.map(function (layer) {
        return layer.id;
    });
    console.log("Map Layer IDs: ");
    console.log(layerIds);

This will list all layers, except the custom layers that have been added.

M. Black
  • 353
  • 1
  • 4
  • 20