Skip to main content

Command Palette

Search for a command to run...

Hiding multiple map Layers simultaneously

Published
5 min read
Hiding multiple map Layers simultaneously
T

Oracle Developer for over 25 years. Has worked with SQL, PL/SQL, Forms, Reports and Designer but also Java, BPEL and webservices. Currently focusing on APEX.

The case

Recently we had a requirement for a spatial map where hiding one layer would have to hide 2 more at the same time. In this case the 3 layers would show work-items of a certain type in an area for our teams in the field. The difficulty came because every work-item could have either a point, line or polygon geometry associated with it. As it’s not possible to show points, lines and polygons in one layer and we didn’t want users to have to hide or show certain types of work-items by clicking 3 checkboxes I went searching for a solution. I did get a bit lucky here because the point-layer would always contain data as it also contained the centre-points of the features in the line and polygon layer. This ensured I’d always have a single point of triggering I could use.

Finding a solution

To start of, I created a map on my page and added 3 layers for every geometry-type. Note the order of the layers with the polygons placed above the lines and points in the builder. This way the order of the layers in the actual map will make sure the polygons don’t “cover” the points/lines making it impossible to click on either of them.

Then I disabled the “legend” for the lines and polygon layers.

This way I will end up with only 1 checkbox in the legend of this map for the layer with “Work items points” (note I gave this layer a label “Work items” hence no “points” in the legend).

Now the trick was to find a way to (1) hide other specific layers AND (2) find a trigger that will fire when a layer is hidden or shown.

Hiding or showing a map-layer with JavaScript.

First of all I needed a way to hide or show a specific layer on the map through JavaScript. Eventually I found the setLayoutProperty() function of a layer which changes specific properties, including the “visibility”. Allowed values are “none” and “visible”. Below code snippet changes the visibility of the layer “Work items lines” to the value “none”, thus hiding it.

let lMapRegion = apex.region('map-region');  // "map-region" is the static ID of the map region
let lLayerId = lMapRegion.getLayerIdByName("Work items lines");
let lLayer = lMapRegion.getMapObject().getLayer(lLayerId);

lLayer.setLayoutProperty('visibility', 'none');

We will use this snippet later when we put everything together.

If you’re interested in how I found this solution: it involves a lot of apex.debug() usages and analysing what map elements contain what data and functions and then try them out. Basically trial-and-error problem-solving!

Catching a layer change

Second problem to solve was finding a trigger that will fire when hiding or showing a layer. I found that in the dynamic action “Map Changed [Map]”. This will fire when a layer is hidden or shown (and on other moments as well, see the APEX documentation here).

When this dynamic action fires it will emit data we will need. As with many other dynamic actions this can be used by the JavaScript “variable” this.data. One of the elements in this variable is changeType. In this case we are only interested when this has the value “toggle-layer” to restrict the triggering of this dynamic action. Another element is layers, which contains an array of all the layers that are on the map, including wether it is visible or not after the layer-toggle has finished.

Here is the relevant part of the this.data when logged through apex.debug():

So, with this we can catch a layer change AND check if a layer is visible or not. We should now have al the ingredients to build a solution.

Putting it together

First of all I made 2 functions for hiding and showing a set of layers. As I generally prefer to not use seperate functions but package them I will use this code (basically using a namespace):

let demoMap = (function ($) {
  let gMapRegion;

  return {
    init: function (pStaticId) {
      gMapRegion = apex.region(pStaticId);
    },
    hideLayers: function (pLayerNamesArray) {
      pLayerNamesArray.forEach(function (layerName, index) {
        let lLayerId = gMapRegion.getLayerIdByName(layerName);
        let lLayer = gMapRegion.getMapObject().getLayer(lLayerId);
        if (lLayer) {
          lLayer.setLayoutProperty('visibility', 'none');
        }
      });
    },
    showLayers: function (pLayerNamesArray) {
      pLayerNamesArray.forEach(function (layerName, index) {
        let lLayerId = gMapRegion.getLayerIdByName(layerName);
        let lLayer = gMapRegion.getMapObject().getLayer(lLayerId);
        if (lLayer) {
          lLayer.setLayoutProperty('visibility', 'visible');
        }
      });
    }
  }
})(apex.jQuery);

For this demo I put this code in the Function and Global Variable Declaration of the page but if you want to use this on other pages as well I would advice to put it in a static file in your application or on your webserver (and minify it as well) and include that in your page.

Now to make use of this code I will need to initialize it with the map. This was done by adding a dynamic action “Map Initialized [Map]” to my map region and let it execute the following line of JavaScript:

demoMap.init(this.triggeringElement.id);

Then I added another dynamic action of type “Map Changed [Map]”. As I only want this to fire when a layer is hidden or shown I added a Client-side Condition of type “JavaScript Expression” with the following line:

this.data.changeType == 'toggle-layer'

Below this dynamic action I add the following JavaScript code in the “true”-section:

let isLayerVisible = this.data.layers.find(el => el.name == 'Work items points').visible,
  layersToHideOrShow = ['Work items lines', 'Work items polygons']
  ;

if (isLayerVisible) {
  demoMap.showLayers(layersToHideOrShow);
} else {
  demoMap.hideLayers(layersToHideOrShow);
}

This will check wether the layer named “Work items points” is visible or not and then hide or show the other 2 layers correspondingly.

The result

If everything was done correctly you will now be able to show or hide multiple layers with one click. Below is a small video of what it looks like. As you can see all polygons, lines and points, which are on different layers, are hidden or shown with a single click!

A few mumblings

I’m not a JavaScript expert so I’m sure there is improvements to be found in the above code, but for the sake of this blog it will suffice.

Of course it would also have been possible to make 1 function to toggle the visibility of a layer based on the visiblity of the main layer. Again, for the sake of this demo etc. etc. etc….

Using part of the above solution it would also be possible to make a custom legend and show and hide any number of layers if you want to.

(The above solution was made using Oracle APEX 24.2.6 and might or might not work on other versions)

More from this blog

Tinkering with APEX

6 posts

This blog is my personal vault of stuff I made in APEX. The main reason to do this is to have a central spot for myself to store solutions AND to spread the knowledge! So feel free to use and share.