My leaflet map has several overlay layers. When the map is displayed, all the overlays are shown (ie they're all enabled in the layer switcher). Instead I'd like to have all the overlays hidden initially, then allow the user to show one or more as needed.

Please does anyone have any suggestions for how to do this?

The initial appearance will be the usual base layer showing a modern streetmap, and markers. The user will have the option to add various scanned old paper maps as overlays to see how the area used to look. Opening the map with all the scanned maps visible makes it look very confusing.

Thanks & regards, David

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

davidhk created an issue. See original summary.

robertwb’s picture

Hey @davidhk can you provide a bit mroe info? How are you definining your map information? Are you using code to pass in a map array (), are you using LeafletMoreMaps admin screen to define it?

davidhk’s picture

Hi Robert,

I build an array, based on the format from LeafletMoreMaps, then call leaflet_render_map(). It works fine, I'd just like to hide the overlays when the map is first shown.

I'm hoping there is a setting for a layer that lets me choose whether it is enabled or not in the layer switcher at startup. Something like the way 'layer_type' indicates whether a layer is a base layer or an overlay.

Or perhaps a hook in the render process that lets me get in and hide the layers before the map is displayed?

But any suggestions gratefully received.

Regards, David

robertwb’s picture

The initial visibility is handled by the javascript code, and there does not seem to be a setting to pass in to *explicitly* establish a layers initial visibility, but there is an *implicit* setting, that is, declaring layer_type as an "overlay" or "base". If it is given as type "overlay", then it is automatically added via "addLayer()" to the map. If it is some other type of layer, only the first one encountered is added, all others are left invisible. It's an interesting thing to note that when layers are created in Leaflet, they are "added" to the map in that the map knows about them, but you have to add an explicit call to "addLayer" or "addTo" to make them visible.

----Excerpt from leaflet.drupal.js----
          switch(layer.layer_type) {
            case 'overlay':
              lMap.addLayer(map_layer);
              overlays[key] = map_layer;
              break;
            case 'base':
            default:
              if (i == 0 || !this.map.settings.layerControl) {
                lMap.addLayer(map_layer);
                i++;
              }
              layers[key] = map_layer;
              break;
          }

Code 1: Layer definition types for "Google high-res road & terrain" from LeafletMoreMaps.

$map_info['google-high-res'] = array(
'label' => 'Google high-res road & terrain (zoom 0..18)',
'description' => t('Google road & terrain layers, with high-res (Retina) support'),
'settings' => array('layerControl' => TRUE) + $default_settings,
'layers' => array(
'terrain' => array(
'type' => 'google',
'urlTemplate' => $prot . 'mt{s}.googleapis.com/vt?lyrs=t,r&x={x}&y={y}&z={z}',
'options' => array(
'attribution' => $attr_google,
'detectRetina' => TRUE,
'subdomains' => array(0, 1, 2, 3),
),
'layer_type' => 'base', // Allowed values "base" or "overlay"
),
'roadmap' => array(
'type' => 'google',
// For retina displays we append '&style=high_dpi&w=512',
// see leaflet_more_maps_preprocess_leaflet_map()
'urlTemplate' => $prot . 'mt{s}.googleapis.com/vt?x={x}&y={y}&z={z}',
'options' => array(
'attribution' => $attr_google,
'detectRetina' => TRUE,
'subdomains' => array(0, 1, 2, 3),
),
'layer_type' => 'overlay', // Allowed values "base" or "overlay"
),
),
);

davidhk’s picture

Here's my workaround. I allow an extra setting 'layer_hidden', true / false, to be added to a layer in the array, in the same way we add 'layer_type'. eg:

        'My overlay layer' => array(                                    
          'urlTemplate' => '//mysite.com/myfiles/{z}/{x}/{y}.png',
          'options' => array(
            'attribution' => 'mysite',
            'minZoom' => 12, 
            'maxZoom' => 18, 
          ),
          'layer_type' => 'overlay', // "base" or "overlay" are accepted values
          'layer_hidden' => true,
        ), 

Then I made a small js file that handles the leaflet.map event to update the map before it is displayed:

(function ($) {

  $(document).bind('leaflet.map', function(e, map, lMap) {
    // The leaflet.map event is fired after the map is built in leaflet.drupal.js
    // 'map' is the object built from the php array we passed in
    // 'lMap' is the Laflet map opject built from 'map'
    // The following code hides any overlay layers that have 'layer_hidden' set true. 
    // They're still there but not visible, so you can see them
    // again by ticking their checkbox in the layer control.
    
    for (var key in map.layers) {
      // default the setting to false if not set
      var layer_hidden = (typeof map.layers[key].layer_hidden === "undefined") ? false : map.layers[key].layer_hidden ;
      
      if ((map.layers[key].layer_type == 'overlay') && layer_hidden) {
        lMap.removeLayer(lMap._layers[key]);
      }
    }
  });

})(jQuery);

No problems noticed so far.

If the setting is useful for others, I think it can be handled more easily in the leaflet.drupal.js. I haven't tried it but I think this would work:

          switch (layer.layer_type) {
            case 'overlay':
              var layer_hidden = (typeof layer.layer_hidden === "undefined") ? false : layer.layer_hidden ;     
              if (!layer_hidden) {
                lMap.addLayer(map_layer);
              }
              overlays[key] = map_layer;
              break;;

This page talks about creating a layer control but without having all layers displayed at startup: http://leafletjs.com/examples/layers-control.html

Note that we added grayscale and cities layers to the map but didn’t add streets. The layers control is smart enough to detect what layers we’ve already added and have corresponding checkboxes and radioboxes set.

robertwb’s picture

Category: Support request » Feature request
Status: Active » Needs review
FileSize
1014 bytes

hey @davidhk - I finally got around to spinning a patch for this based on your code. The attached patch is against the latest dev branch, but also applies successfully against the 2015-Oct-09 release of 7-x.

robertwb’s picture

Relating the D8 issue for this, so this can effectively be considered a back port.

RdeBoer’s picture

Assigned: Unassigned » RdeBoer
Status: Needs review » Fixed

Darn... only saw this now. I came across the same issue yesterday, whereby the 'overlays' from Leaflet More Maps weren't handled gracefully (anymore?).

My fix is aimed at making layers appear via RADIO BOXES (mutually exclusive) while overlays will appear through CHECKBOX selection (i.e. as many as the user ticks).

This fix initially displays the first base layer, hiding any overlays by default.
It does not allow for the overlay default to be set to 'visible on initial display'.

For others that scan straight to the bottom issue...
The key in defining the 'overlays' is to add 'layer_type' => 'overlay'.
That's all you have to do. See Leaflet More Maps module code for examples (shown also above).

robertwb’s picture

Hey @RDeBoer - where is your fix - is it directly in the code base - or does it already exist in the current behavior? Is there another issue that we can view? Does the current module not alrady do exzactly as you propose, that is types "base" are in radio buttons, and type "layer" are in checkboxes - or is that something provided by LMM and I have just been enjoying it so long that it seems like something the base Leaflet module provides? :)

As for the behavior of this patch/fix:

> This fix initially displays the first base layer, hiding any overlays by default.

I believe this is not the case, the code below should insure that if there is no "layer_hidden" setting, the overlays will appear.

var layer_hidden = (typeof this.map.layers[key].layer_hidden === "undefined") ? false : this.map.layers[key].layer_hidden ;

Now - the choice of "layer_hidden = false" as opposed to "visible = true" might be a counterintuitive way to handle things...

RdeBoer’s picture

@RobertWB #9

I tought it used to work too. But when I tried LeafletJS 1.0.2 plus latest devs of Leaflet, Leaflet MarkerCluster and Leaflet More Maps, it didn't.
Some of the overlays were repeated as base layers.
Here's the check-in that fixed it: http://cgit.drupalcode.org/leaflet/commit/?id=3dff674

'overlay' is not a LeafletJS thing. In LeafletJS you need to add layers and overlays programmatically, see http://leafletjs.com/examples/layers-control That's why LMM introduced the 'overlay' keyword so you can add overlays through pseudo-configuration, rather then full-on code.
Leaflet.drupal.js translates that into the programmatic LeafletJS way (see above check-in).

It appears that independently someone introduced a second feature regarding layers, 'switchLayer' which is also not a LeafletJS thing. It seems to aim at switching the map to a different base layer (eg. road to satellite) once a particular zoom level is reached. Maybe that's when the LMM 'overlay' function broke.

Sorry I haven't gone through raising it as an issue first and then waiting months for a reaction.

These changes are in response to changes in two popular JS Libraries (Leaflet JS and Leaflet Markercluster JS) and at least 4 modules (Leaflet, Leaflet MarkerCluster, Leaflet More Maps, IPGV&M) all at once. It's important to coordinate any changes in these modules, so that they continue to work together and behave consistently. I spent over a day applying recent patches from the combined issue queues and this additional bug fix I found in the process slipped in without being raised as a separate issue first.

I am in a unique position that I have co-maintainer rights to the 4 modules mentioned above, so I can make things happen in a consistent way in all modules at the same time.

It's also important that this gets tested. That's why I made official releases of all 3 Leaflet modules at the same time. Experience has taught us that changes in dev go largely unnoticed, but when there's an error in an official release people scream (as they should).

I only do this when I'm in a position to keep a close eye on any disasters that may be reported and can respond immediately. Keeping my fingers crossed now!

When the dust is settled, let's have another look at this 'layer_hidden' overlay [like you, I prefer 'visbile=true']

robertwb’s picture

>Experience has taught us that changes in dev go largely unnoticed

They are guaranteed to go unnoticed if there is no entry in the issue queue for them. As someone who regularly follows the issue queue and applies patches and reports back on their performance I can tell you for certain that this reduces participation.

I simply disagree with the notion that it is preferable to make commits without tracking them - and I think the only outcome from pushing something broken to a release is that module adoption declines - yes, eventually the issue comes to light, but not until much damage to the user base occurs.

> 'overlay' is not a LeafletJS thing

But it IS a leaflet.drupal.js thing, having been in the js code for quite some time.

          switch (layer.layer_type) {
            case 'overlay':

For this specific issue I think it's critical to identify where default layer visibility should be processed - my opinion is that it's such a fundamental aspect of mapping that the base Leaflet drupal module is the only logical choice.

I think generally a source of trouble is that the interacting ecosystem of leaflet related modules has overlapping javascript rather than using a plugin architecture (related issue attached). It is unclear to me if the 8x branch has added this or not.

robertwb’s picture

Status: Fixed » Needs work

Changing this to "Needs Work" because the original issue intent is to establish a means of controlling overlay visibility - so while the related commit linked by @Rdeboer may fix some ambiguity in the default behavior, I don't believe it addresses the original intent which is to be able to specify either on or off by default.

RdeBoer’s picture

@robertwb #11

Yes, I should have raised it as an issue, so it's easier to track.
Have logged the issue now: #2845636: Overlays appear as base layers AND overlays at same time.

Didn't mean to decrease participation, on the contrary.
One can talk till the cows come home, but a patch cannot be enjoyed by the greater community until it is committed.
We haven't seen enough of the latter with this module.
But I accept I was over-zealous and a little too quick.

You are also right to mark the issue as "Needs work". There's more to this issue than I thought at first glance.
After a day and a half of fixing bugs and applying patches to 4 modules, my eye for detail got a little weary.

As far as 'overlay' is concerned, I believe we are in agreement there.

robertwb’s picture

Thanks @rdeboer for the consideration - and patch - as someone who runs leaflet on the bleeding edge, I am almost always on a dev install so this really helps me get my head around what I need to do next. Thanks for all the commits as well - looking forward to testing.

arx-e’s picture

It seems since this isssue was opened the default behaviour has changed and now all overlay layers are by default invisible.
So I can find no way to have a google sat map base + roads overlay by default on and this is a problem because many casual users will not notice and reach for the layer switcher.

Is there any way to do this manually? (make an overlay layer visible by default)

(I am using IPGM&V, Leaflet and Leaflet More Maps.)