Git Product home page Git Product logo

leaflet.control.layers.tree's Introduction

Leaflet.Control.Layers.Tree

CI NPM version License Leaflet 1.x compatible!

A Tree Layers Control for Leaflet.

Description

This plugin extends Control.Layers allowing a tree structure for the layers layout. In Control.Layers you can only display a flat list of layers (baselayers and overlays), that is usually enough for small sets. If you have a long list of baselayers or overlays, and you want to organize them in a tree (allowing the user collapse and expand branches), this is a good option.

Some live examples here

Installation

Using npm for browserify npm install leaflet.control.layers.tree (and require('leaflet.control.layers.tree')), or just download L.Control.Layers.Tree.js and L.Control.Layers.Tree.css and add a script and link tag for it in your html.

Compatibility

This plugin has been tested with Leaflet 1.0.3, 1.1.0, 1.2.0, 1.3.1., 1.4.0, 1.5.1, 1.6.0 and 1.7.1

This plugin supports TypeScript. See file L.Control.Layers.Tree.d.ts

Usage

  1. Create your layers. Do this as usual.
  2. Create your layers tree, like the one just below.
  3. Create the control and add to the map: L.control.layers.tree(baseTree, overlaysTree, options).addTo(map);
  4. Voilà!
var baseTree = {
    label: 'Base Layers',
    children: [
        {
            label: 'World 🗺',
            children: [
                { label: 'OpenStreetMap', layer: osm },
                { label: 'Esri', layer: esri },
                { label: 'Google Satellite', layer: g_s },
                /* ... */
            ]
        },
        {
            label: 'Europe',
            children: [
                { label: 'France', layer: france },
                { label: 'Germany', layer: germany },
                { label: 'Spain', layer: spain },
                /* ... */
            ]
        },
        {
            label: 'USA',
            children: [
                {
                    label: 'General',
                    children: [
                        { label: 'Nautical', layer: usa_naut },
                        { label: 'Satellite', layer: usa_sat },
                        { label: 'Topographical', layer: usa_topo },
                    ]
                },
                {
                    label: 'States',
                    children: [
                        { label: 'CA', layer: usa_ca },
                        { label: 'NY', layer: usa_ny },
                        /* ... */
                    ]
                }
            ]
        },
    ]
};

small tree sample

var overlaysTree = {
    label: 'Points of Interest',
    selectAllCheckbox: 'Un/select all',
    children: [
        {
            label: 'Europe',
            selectAllCheckbox: true,
            children: [
                {
                    label: 'France',
                    selectAllCheckbox: true,
                    children: [
                        { label: 'Tour Eiffel', layer: L.marker([48.8582441, 2.2944775]) },
                        { label: 'Notre Dame', layer: L.marker([48.8529540, 2.3498726]) },
                        { label: 'Louvre', layer: L.marker([48.8605847, 2.3376267]) },
                    ]
                }, {
                    label: 'Germany',
                    selectAllCheckbox: true,
                    children: [
                        { label: 'Branderburger Tor', layer: L.marker([52.5162542, 13.3776805])},
                        { label: 'Kölner Dom', layer: L.marker([50.9413240, 6.9581201])},
                    ]
                }, {label: 'Spain',
                    selectAllCheckbox: 'De/seleccionar todo',
                    children: [
                        { label: 'Palacio Real', layer: L.marker([40.4184145, -3.7137051])},
                        { label: 'La Alhambra', layer: L.marker([37.1767829, -3.5892795])},
                    ]
                }
            ]
        }, {
            label: 'Asia',
            selectAllCheckbox: true,
            children: [
                {
                    label: 'Jordan',
                    selectAllCheckbox: true,
                    children: [
                        { label: 'Petra', layer: L.marker([30.3292215, 35.4432464]) },
                        { label: 'Wadi Rum', layer: L.marker([29.6233486, 35.4390656]) }
                    ]
                }, {
                /* ... */
                }
            ]
        }
    ]
}

smalloverlay sample

API

L.Control.Layers.Tree

The main (and only) 'class' involved in this plugin. It exteds L.Control.Layers, so most of its methods are available. addBaseLayer, addOverlay and removeLayer are non usable in L.Control.Layers.Tree.

L.control.layers.tree(baseTree, overlayTree, options)

Creates the control. The arguments are:

  • baseTree: <Object> or <Array> Tree defining the base layers (like the one above). You can also provide an Array of nodes, if you want to start with a flat level.
  • overlayTree: <Object> or <Array> Similar than baseTree, but for overlays.
  • options: <Object> specific options for the tree. See that it includes L.Control.Layer options
constructor options
  • closedSymbol: <String> Symbol displayed on a closed node (that you can click to open). Default '+'.
  • openedSymbol: <String> Symbol displayed on a opened node (that you can click to close). Default '−' (&minus;).
  • spaceSymbol: <String> Symbol between the closed or opened symbol, and the text. Default ' ' (a normal space).
  • selectorBack: <Boolean> Flag to indicate if the selector (+ or −) is after the text. Default 'false'.
  • namedToggle: <Boolean> Flag to replace the toggle image (box with the layers image) with the 'name' of the selected base layer. If the name field is not present in the tree for this layer, label is used. See that you can show a different name when control is collapsed than the one that appears in the tree when it is expanded. Your node in the tree can be { label: 'OSM', name: 'OpenStreetMap', layer: layer }. Default 'false'.
  • collapseAll: <String> Text for an entry in control that collapses the tree (baselayers or overlays). If empty, no entry is created. Default ''.
  • expandAll: <String> Text for an entry in control that expands the tree. If empty, no entry is created. Default ''.
  • labelIsSelector: <String> Controls if a label or only the checkbox/radiobutton can toggle layers. If set to both, overlay or base those labels can be clicked on to toggle the layer. Default 'both'.

See that those strings can be html code, with unicode, images or whatever you want.

setBaseTree(tree)

Resets the base layers tree (like in constructor, an <Object> or <Array>). Internally removes and adds all the layers, so you may be notified if you registered those events. Returns this.

setOverlayTree(tree)

Resets the overlay layers tree (like in constructor, an <Object> or <Array>). Internally removes and adds all the layers, so you may be notified if you registered those events. Returns this.

expandTree(overlays)

This method expands the tree. When overlays is true expands the overlays tree. Otherwise expands the baselayers tree. Returns this.

collapseTree(overlays)

This method collapses the tree. When overlays is true collapses the overlays tree. Otherwise collapses the baselayers tree. Returns this.

expandSelected(overlays)

This method expands only the selected item in the tree. When overlays is true affects the overlays tree. Otherwise affects the baselayers tree. Returns this.

Tricks about the tree

The layers tree is a normal Objects tree like in the example above. The valid elements are:

  • children: <Array> Array of children nodes for this node. Nothing special.
  • label: <String> Text displayed in the tree for this node. It may contain HTML code.
  • layer: <L.Layer> The layer itself. You can create with L.tileLayer, L.marker, or however you want.
  • name: <String> Text displayed in the toggle when control is minimized. If not present, label is used. It makes sense only when namedToggle is true, and with base layers.
  • radioGroup: <String> Text to identify different radio button groups. It is used in the name attribute in the radio button. It is used only in the overlays layers (ignored in the base layers), allowing you to have radio buttons instead of checkboxes. See that radio groups cannot be unselected, so create a 'fake' layer (like L.layersGroup([])) if you want to disable it. Deafult '' (that means checkbox).
  • collapsed: <Boolean> Indicate whether this tree node should be collapsed initially, useful for opening large trees partially based on user input or context. Disabled by default.
  • selectAllCheckbox: <Boolean> or <String> Displays a checkbox to select/unselect all overlays in the sub-tree. In case of being a <String>, that text will be the title (tooltip). When any overlay in the sub-tree is clicked, the checkbox goes into indeterminate state (a dash in the box).
  • eventedClasses: <Array> or <Object>. [Advanced functionality to access nodes of the tree. Use carefully] Object (or array of objects) to define events on the label. Now you can specify a way to select all the sub-nodes in the overlay tree inspecting the tree. The object contains:
    • className: <String>. Will add an event on the first element with this class name in this node.
    • event: <String> (optional). Event to trigger. By default is click.
    • selectAll: <Boolean> or <Function>. true selects all the checkboxes in the subnodes. false unselects all. In case of using a <Function> (that should return a Boolean or undefined), it will be called with (ev, domNode, treeNode, map), where:
      • ev: event triggered.
      • domNode: DOM node that includes the label and all the children.
      • treeNode: node of this tree affected. Be careful. Modifying it may produce undefined behaviours.
      • map: map associated with the control.

You can see an example of a baselayers tree (the javascript code) above. You can provide a tree, or an array of trees.

Non leaf nodes (that is, those with children) can also have a layer. In this case you will be able to select the layer, and only the icon will collapse or expand this branch.

You can include HTML code, not only ascii chars, in the label attribute. It will be included as innerHTML. Be carefull with unicodes, because not every browser supports them all.

A leaf node without layer attribute is also posible. Only with label. This can be used to include special commands calling a javascript function, or a separator, or whatever you like. An example of separator node is

{label: '<div class="leaflet-control-layers-separator"></div>'}

leaflet.control.layers.tree's People

Contributors

albfan avatar dependabot[bot] avatar falke-design avatar jjimenezshaw avatar michiel-2 avatar obdulia-losantos avatar ybasket avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

leaflet.control.layers.tree's Issues

Issues with collapse and addLayer

There is such a code(it is taken from the example):

var granada = L.marker([37.133, -3.636]);
        var malaga = L.marker([36.674, -4.499]);
        var sevilla = L.marker([37.418, -5.893]);

        malaga.addTo(map);
        granada.addTo(map);
        sevilla.addTo(map);

        var overlaysTree = {
            label: 'Some cities',
            selectAllCheckbox: 'Un/select all',
	collapsed: true,
            children: [
                {label: '<div id="onlysel">-Show only selected-</div>'},
                {label: 'France', children: [
                    {label: 'Lyon', layer: L.marker([45.728, 4.945])},
                    {label: 'Paris', layer: L.marker([48.725, 2.359])},
                    {label: 'Toulouse', layer: L.marker([43.629, 1.364])},
                ]},
                {label: 'Germany', selectAllCheckbox: true,
	collapsed: true, children: [
                    {label: 'Berlin', layer: L.marker([52.559, 13.287])},
                    {label: 'Cologne', layer: L.marker([50.866, 7.143])},
                    {label: 'Hamburg', layer: L.marker([53.630, 9.988])},
                    {label: 'Munich', layer: L.marker([48.354, 11.786])},
                ]},
                {label: 'Spain',
                    selectAllCheckbox: 'De/seleccionar todo',
	collapsed: true,
                    children: [
                        {label: 'Madrid', layer: L.marker([40.472, -3.561])},
                        {label: 'Andalucia', selectAllCheckbox: true,
	collapsed: true, children: [
                            {label: 'Granada', layer: granada},
                            {label: 'Málaga', layer: malaga},
                            {label: 'Sevilla', layer: sevilla},
                        ]},
                        {label: 'Bask Country', children: [
                            {label: '---', layer: L.layerGroup([]), radioGroup: 'bc'},
                            {label: 'Bilbao', layer: L.marker([43.301, -2.911]), radioGroup: 'bc'},
                            {label: 'San Sebastian', layer: L.marker([43.356, -1.791]), radioGroup: 'bc'},
                            {label: 'Vitoria', layer: L.marker([42.883, -2.724]), radioGroup: 'bc'},
                        ]},
                        {label: 'Catalonia', children: [
                            {label: 'Barcelona', layer: L.marker([41.297, 2.078])},
                            {label: 'Gerona', layer: L.marker([41.901, 2.760])},
                        ]},
                    ],
                },
            ]
        }

        var lay = L.control.layers.tree(baseTree, overlaysTree,
            {
                namedToggle: true,
                selectorBack: false,
                closedSymbol: '&#8862; &#x1f5c0;',
                openedSymbol: '&#8863; &#x1f5c1;',
                collapsed: false,
            });

        lay.addTo(map);

The problem is that when I enter map.addLayer(granada); , then layer.control changes its state, collapse occurs somewhere, and expand somewhere. I noticed that in the control layer tree I used collapse:true, because of this, it collapses, while others expand.
What do I need to do so that when using map.addLayer(granada); layer.control state didn't change?

Update dinamically the menu

Hi, I am currently working on a GIS web app that uses this plugin to create a dynamic menu of WMS layers.
I have encountered a problem regarding updating this menu dynamically from different components (I'm using Svelte).
To resolve this issue, I have created baseOverlays in the window, and then I update the window.baseOverlays object for any wms that the user adds dinamically from a different component, so i walk inside the object to find the correct subsesction of the wms then i add it to the children array of the subsection, then I call the setOverlayTree(window.baseOverlays) function to refresh the menu with the new layer.
It works fine, but I am wondering if it's possible to create a function that can dynamically update the menu, i can work on that.

flashing bug

When I place a tree on the map, the element that is responsible for expanding the tree blinks when I hover over it. in this case, the tree elements are also not displayed. do i have to specify tree.layer?

Have all trees collapsed by default?

Sorry. I should have tested using the examples provided.
Example:
var layerControl = L.control.layers.tree(baseTree).addTo(map); layerControl.collapseTree(false);

Getting layer states and switching layer states by js

Hi,

my old application used to save the layer tree state (which layers are selected, which submenues have been opened or closed, which children are open, the complete tree layout) using cookies or localStorage.
This happens when the user is closing the page (something like "$(window).on("unload", function(e){ .. save tree state });")
and the next time the tree will be opened like before.

You may check the old project https://wambachers-osm.website/Emergency.html to see, what i'm talking about. (Only data for Germany, Austria and Switzerland)

Tracking, which base layer or overlay layer is changing, is quite simple using map.on(baselayerchange) or map.on (overlayadd, overlayremove), but i need the full state of the tree.

Is there any way to do this using Leaflet.Control.Layers.Tree?

Regards
walter

Feature Request: Checkbox on non-leaf, to uncheck/check all descendants.

When there are many leaf nodes, simply collapsing/expanding the tree helps manage them, but doesn't make it easier to turn on/off many nodes; AFAIK, they have to be checked/unchecked one at a time.

Would be great if each non-leaf also had a checkbox. This has 3 possible states: all children are checked => checked, all children are unchecked => unchecked, some children are checked => a symbol representing that (e.g. Microsoft's "square inside the checkbox").

Clicking on checked => unchecked, and unchecks all descendents.
Clicking on unchecked => checked, and checks all descendents.
Clicking on some-checked => checked, and checks all descendents. (I think - one could argue in favor of "=> unchecked")

The initial state should be based on the children. E.g., if they are all checked, then the group checkbox should also be checked. So that the next click on it performs "uncheck all".

A feature like this exists in https://github.com/ismyrnow/leaflet-groupedlayercontrol - options [ groupCheckboxes: true ] - but that control lacks the ability to collapse a tree branch. The option adds an input checkbox, with a click event, to every non-leaf node. It doesn't support the tri-state logic I describe above; just checked/unchecked (no "some-checked" display state). That's good enough - though it is ambiguous when you collapse a node which has some children checked: user doesn't know exactly what will happen when they click on checkbox of a collapsed node.

I would use this control, if it had this feature. I'd add the feature myself, but I don't know enough yet about leaflet internals, nor manipulating the DOM, to fully grasp the coding needed.


I see that the group node does have a checkbox, if it has a layer attached to it.
So I am wanting a different option, where group node has a checkbox, but no layer, that behaves as described above.


As a hack, I tried adding all the children to a LayerGroup, and making that LayerGroup the layer that is attached to the group node. This partially works:

  • If all children are unchecked, then checking the group node makes them all appear, unchecking makes them all disappear.
  • However, not surprisingly, if some or all children are checked, their checked state keeps them visible, when uncheck the group node.

Now I just need to figure out what edits would keep the children checked states in sync with the group node...

base layer attributions being appended to other layers

Please see attached images,
The first shows the map as loaded without using the layer selector (OpenStreetMap)
The second shows the position after selecting an Esri layer
dump1
dump2
The box outlined in red shows the OpenStreetMap attribution that should have been replaced.

If I remove the OpenStreetMap attribution then everything works OK

Edit: using leaflet 1.6 with the markercluster library

Prevent adding layer while expanding Layer-Control on mobile

Hi @jjimenezshaw

This issue is related to Leaflet/Leaflet#8778 that was fixed in Leaflet 1.9.4 with Leaflet/Leaflet#8910

However, the PR only fix the L.control.layers. Using Leaflet.Control.Layers.Tree it keeps with the same behaviour, as you can test here:

https://plnkr.co/edit/W135NFAEtQV0a3ZK

https://run.plnkr.co/plunks/W135NFAEtQV0a3ZK

It seems that the fix applied to L.control.layers also needs to be added in Leaflet.Control.Layers.Tree

Add Dynamic Child Layers

Hi,

In Control.Layers.Tree, how to add the children layers dynamically?

After the service call only, I am getting the layer data. Service returning the data, but not shown in the Map.

How to reference DOM label selector in tree [upon selection]

Is there a way to have the tree selection reference an object request? Right now I have an HTML selection that first requests a json file through an click() event in javascript that parses the data to create circle markers with attributes sorted into layerGroups that are filtered through an overlay layer control module upon selection from the tree.

What I would like to do is instead trigger the request for the object when it is selected through the tree to then subsequently parse the data in the function that creates the circle markers and sorts them into layerGroups.

Add typings for Typescript users

I love your tree control, but since I'm using Typescript, it is not so easy to properly use it. Of course, you can use a hack like

(L.control.layers as any).tree...

but proper typings would be better.

Radiogroup for subgroups

Hi, firts of all thank you very much for this excelent work, my proposal is, if possible, create mutually exclusive subgroups of layers, that is, subgroups with radiogroups

Example: 
var overlayLayers = {
label: 'Group 1',
children: [
  {  label: 'SubGroup 1',
      radioGroup: 'bc',
      children: [
         {  label: 'Layer 101',
            layer: lay101-example
         }, 
         ...
   },
  {  label: 'SubGroup 2',
      radioGroup: 'bc',
      children: [
         {  label: 'Layer 201',
            layer: lay202-example
         },
         ...
   }
 ]
};

I hope i have explained myself, i know my english is not good but im working on it

Icons/ Symbols for each Overlay's children

Hi,

Thanks for the Control.Layers.Tree.

I am new to Leaflet. In the Control.Layers.Tree, is it possible to add the Icons/ Symbols for each Overlay's children? I need different Symbols will define for each Overlay's childer layer.

eventedClasses

Can you give an example using eventedClasses? I'm confused where to put it and how to use it.

A question re collapsing/expanding

First thanks for a very nice plugin (now I've got it working 👍 )
I'm trying to get to the situation whereby clicking on a group expands its children but closes any other already expanded groups, I've tried using collapseTree and expandSelected but it seems to work once then locks up.
It may be that I'm not sure what to use as the trigger selector (I'm using leaflet-layerstree-header)
Thanks for any help

L.Control.extend?

Hi Javier, I want to use your control with the Layer Icon expanded by default (without the need to hover the mouse over it to see layers). I think I need to include something like this: L.control.extend({ options: { collapsed: false }}) but I have no clue how to add it to your code. Could you give me a hand with this?
This is what I want to see when I open my toolbar...
capture
Thank you in advance for your help!

How to add Layer on non Leaf node

As per the documentation of the plugin 'Non leaf nodes (that is, those with children) can also have a layer. In this case you will be able to select the layer, and only the icon will collapse or expand this branch'

When I tried to add layer to non leaf node...then instead of one checkbox ..2 checkbox are rendering for that node....
please help...thanks in advance

Referencing text Labels of children selected in "SelectAllCheckbox"

I'm using this code to reference the labels for children layers upon selection.

var htmlObject = layerControl.getContainer().querySelectorAll('input');
$(htmlObject).on("change", function(e) {
  if ($(this).is(':checked'))
    console.log($(this).siblings('span').text());
});

However, when using the SelectAllCheckbox, only the text label for SelectAllCheckbox is logged. How can I reference the text for all of the child layers upon selection of SelectAllCheckbox of the parent label?

Show intersection of overlays

I use Leaflet.Control.Layers.Tree to display properties that I import via a GeoJSON file. That you very much for this.

I create an overlay that contains for example all houses that are for sale. Another overlay shows all objects that are suitable for living in.
So far so good.

  • Via Radio Group a user can display either all houses for sale or all houses that can be lived in.
  • With the Checkbox he can display the sum of all saleable and livable houses, if he activates both overlays.

Now I would like to display the intersection. So I want that if both checkboxes (sellable and livable) are checked, only houses that are sellable and livable are shown. Properties that are sellable but not livable should not shown.

At first glance, it looks to me like it is mandatory that I create a separate layer for this and add it to the tree. But I have the feeling, that others have this issue too and there must be a better solution for this!

It would be kind, if someone can point me to a better solution.

Adding Icons to Layers

Hello,

I noticed there is an icon of the continents being used in the initial example, but no demonstration for how to implement the use of icons in the layers tree. Any code examples on how to incorporate icons in next to the layers? Thanks.

Layer ordering issue ?

Hi,
I create 3 LayerGroup (L1, L2, L3). I would like that L3 is over L2 and L2 over L1.
In pure leaflet code, if create the 3 layergroups and add them to the map in this order 1->2->3 and the result is OK.
But if then I create a ControlTreeLayer with these 3 layers, if I deselect then reselect L1, it goes over L2 and L3.
-> how can I maintain the order of the layers, whatever I do with selection/deselection ?
I forced the option autoZIndex:true (which is true by default anyway) but it doesn't seem to work.
Thanks a lot for your help !
Olivier

Request - use click instead of hover to open/close the control

At present, the control is opened/closed using mouseover/mouseout events.

This makes it difficult to operate by users with some disabilities and/or assistive technology.

Can you provide an option to open/close this control using mouse-click and/or keyboard?

markers with checkboxes in the basemap

Hi and thank you for a great library!
Two questions:
1)
I'm looking for a way to have check boxes in one of the branches of the baseTree, but even if I add the 'selectAllCheckbox: true' attribute, they still turn up as radio boxes. Is it just me not being able to figure out of it or is it impossible to have check boxes in the baseTree? If impossible at the moment, would it be possible to have in the next release? Else if possible, do you have the possibility to give me an example structure to see how it should be done?
2)
When I call the 'setBaseTree' function again (after it's already been set initially) to update the contents of it, it expands the tree. Is there a way to avoid that the tree gets expanded when I call that function?

Legend Control

Hi

Thank you for the great function.

When you say: "addBaseLayer, addOverlay and removeLayer are non usable in L.Control.Layers.Tree.", do you mean I cannot control the legends of the map for each layer?

Thank you

Rendering problem

Hi,
When I try your exemple, it seems that there is a problem with the rendering...
Capture d’écran 2020-06-15 à 23 47 12

I have the same problem if I try on my data...

Any idee?

Exclusive Group Layers

Hi Javier! I am making the last adjustments to my map and I realized that your tool does not allow define a group of exclusive layers that can be selected one at a time (as the basemaps does...). I have a bunch of layers with wind forecast every three hours and I want to select only one each time. Could you please include this functionality in next releases? Many thanks for your time, M.

Add WMS and WFS to Control Layers Tree

Your tree control is amazing, you did a great job but can you please tell me how to add WMS and WFS? I'm following your examples but I'm a bit stuck in the overlays section because there are only place markers.

External layers file definition

Hi everyone! I'm making a web mapping page for my job. I have an extensive layers list, and my Index.html is also huge. I want to put the layers list in a separate file, so I can call this file later from my index.html and choose the layers I want to display. I'm using leaflet.layers.tree as the plugin to manage my layers. In this fiddle you can see a quick and dirty example of what I'm doing (https://jsfiddle.net/mbotti/z3f4x0w7/696/). Any suggestion will be well received. And a working example much more! Regards, M.

4 control panel shows when the layer is added to the map

hi,

Thank for the library! I am new to leaflet and still trying to wrap my head around the implementation/plugins.
I am trying to use Leaflet.Control.Layers.Tree package in Next.js application. Using the following versions:

  • next: 14.0.4
  • react-leaflet: 4.2.1
  • leaflet: 1.9.4
  • @types/leaflet: 1.9.8
  • leaflet.control.layers.tree: 1.1.0

When I create a base tree and try to add it to the map, I see 4 control panels. In addition, the controls also doesn't work. I am not sure if I am missing something in the implementation. Any help is much appreciated. Thank you!

Result:

Screenshot 2024-02-14 at 11 56 44 AM

Screenshot 2024-02-14 at 12 02 58 PM

let esri = L.tileLayer(
    'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
)

let baseTree = {
    label: 'Base Layers',
    name: 'Base Layers Name',
    layer: france,
    children: [
        {
            label: 'Terminal',
            name: 'Terminal Name',
            layer: germany,
            children: [
                { label: 'Level 1', name: 'Level 1 Name', layer: osm },
                { label: 'Level 2', name: 'Level 2 Name', layer: esri },
            ],
        },
    ],
}

var ctl = L.control.layers.tree(baseTree, undefined, options)
ctl.addTo(map)

WFS issue

` <!doctype html>

  <head>

 <meta charset="utf-8" />

<!--Let browser know website is optimized for mobile-->
 <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Webmap Comune di Colleferro</title>

 <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
 <script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
 <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
 <script src="src/plugins/leaflet-bing-layer.js"></script>
 <script src="https://unpkg.com/leaflet.vectorgrid@latest/dist/Leaflet.VectorGrid.bundled.js"></script>
 <script src="src/plugins/leaflet-providers.js"></script>
 <script src="src/plugins/leaflet.ajax.min.js"></script>
 <script src="src/plugins/proj4.js"></script>
 <script src="src/plugins/proj4leaflet.js"></script>
 <script src="src/plugins/leaflet.wms.js"></script>
 <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise"></script>


 <style type="text/css">
    html,
    body {
        width: 100%;
        height: 100%;
        margin: 0;
   }

   #map {
       width: 100%;
      height: 100%;
   }    

   </style>
</head>

    <body>
        <div id="map">
            </div>
                <link rel="stylesheet" href="src/css/L.Control.Layers.Tree.css" crossorigin="" />
                    <script src="src/plugins/L.Control.Layers.Tree.js">
                        </script>




<script type="text/javascript">
 var center = [41.72707235373704, 13.004139599641844];

 //inserimento iniziale Layers

var BING_KEY = 'AljSdfpCwtR7wFswYzmgqltJa9xeQvVtoYb1bEuV7jVHowADqs76GPBdEVoAxvuP'
var binglayer = L.tileLayer.bing(BING_KEY)



//come inserire WMS ((DA PROVARE CON SERVIZIO WFS))


   var osm = L.tileLayer(
    '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '© OpenStreetMap contributors'
   }
);


   var thunderAttr = {
    attribution: '© OpenStreetMap contributors. Tiles courtesy of Andy Allan'
   }
  var transport = L.tileLayer(
   '//{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png',
   thunderAttr
);

  var cycle = L.tileLayer(
   '//{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png',
    thunderAttr
  );



  var stadiadark = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png', {
    attribution: '&copy; <a href="https://stadiamaps.com/">Stadia Maps</a>, &copy; <a 
    href="https://openmaptiles.org/">OpenMapTiles</a> &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> 
    contributors'
  });


  var boundaries = L.tileLayer.wms('http://localhost:8080/geoserver/colleferro.territorio/wms', {
    layers: 'colleferro.territorio:confini_amministrativi',
    format: 'image/png',
    transparent: true,
   });

  var owsrootUrl = 'http://localhost:8080/geoserver/colleferro.territorio/ows';

  var defaultParameters = {
     service: 'WFS',
     version: '2.0',
    request: 'GetFeature',
    typeName: 'colleferro.territorio:strade_colleferro',
    outputFormat: 'text/javascript',
    format_options: 'callback:getJson',
    SrsName: 'EPSG:4326'
   };


  var parameters = L.Util.extend(defaultParameters);
  var URL = owsrootUrl + L.Util.getParamString(parameters);

 var WFSLayer = L.geoJson(null, {
        style: function(feature) {
        return {
        stroke: true,
        fillColor: '#B04173',
        fillOpacity: 2,
        color: '#000000',
        weight: 1,
      };
    }
 });

var ajax = $.ajax({
    url: URL,
   dataType: 'jsonp',
   jsonpCallback: 'getJson',
   success: function(response) {
      WFSLayer.addData(response);
      WFSLayer.addTo(baseTree);
    }
});




  var buildingsowsrootUrl = 'http://localhost:8080/geoserver/colleferro.territorio/ows';

 var buildingsdefaultParametersdue = {
     service: 'WFS',
     version: '2.0',
     request: 'GetFeature',
    typeName: 'colleferro.territorio:edifici_colleferro',
    outputFormat: 'text/javascript',
    format_options: 'callback:getJson',
    SrsName: 'EPSG:4326'
 };

var buildingsparameters = L.Util.extend(BuildingsdefaultParameters);
var buildingsURL = buildingsowsrootUrl + L.Util.getParamString(buildingsparameters);

var WFSbuildingsLayer = L.geoJson(null, {
    style: function(feature) {
        return {
       stroke: true,
        fillColor: '#B04173',
        fillOpacity: 2,
        color: '#000000',
        weight: 1,
      };
    }
 });

 var ajax = $.ajax({
     url: buildingsURL,
    dataType: 'jsonp',
    jsonpCallback: 'getJson',
   success: function(response) {
    WFSbuildingsLayer.addData(response);
    WFSbuildingsLayer.addTo();
    }
});


//opzioni mappa

   var map = L.map('map', {
      renderer: L.canvas(),
          layers: [osm],
         center: center,
          zoom: 13
   });


  L.control.scale().addTo(map);


   var baseTree = {
    label: 'BaseLayers',
    noShow: true,
    children: [
        {
            label: 'OpenStreeMap',

            children: [
                {label: 'OpenStreetMap Standard', layer: osm},
            ]
        },
        {
            label: 'Stadia Basemaps',
            children: [
                {label: 'Stadia Alidade Smooth Dark', layer: stadiadark},
            ]
        },
        {
            label: 'Thunder',
            children: [
                {label: 'Cycle', layer: cycle},
                {label: 'Transport', layer: transport},
            ]

        },
        {
                     label: 'Bing',
                     children: [
                    {label: 'Bing Satellite', layer: binglayer}
                     ]
                 },
            ]
       };



       var overlaysTree = {
        label: 'City',
        selectAllCheckbox: 'Un/select all',
        children: [
 {

        label: 'Territorio',
        selectAllCheckbox: true,
        children: [
        {
            label: 'Test',
            selectAllCheckbox: true,
            children: [
                { label: 'Boundaries', layer: boundaries },
                { label:'Streets', layer: WFSLayer},
                {label: 'Buildings', layer: buildingsWFSLayer}


            ]
        },
        /* ... */

      ]
    }
  ]
 }





 var lay = L.control.layers.tree(baseTree, overlaysTree, {
      namedToggle: true,
      selectorBack: false,
      closedSymbol: '&#8862; &#x1f5c0;',
     openedSymbol: '&#8863; &#x1f5c1;',
     collapseAll: 'Collapse all',
     expandAll: 'Expand all',
     collapsed: false,
  });



 lay.addTo(map).collapseTree().expandSelected().collapseTree(true);
 L.DomEvent.on(L.DomUtil.get('onlysel'), 'click', function() {
 lay.collapseTree(true).expandSelected(true);

});




</script>
</body>

</html>`

potential bug in selectAllCheckbox

I have a layer control tree with selectAllCheckbox set to true for subgroups of child layers. I noticed a bug that could be in part due to my app configuration. All computable reference layers are acknowledged in the tree but the data for each is loaded in separately based on user requests. I noticed it happens that if I select a layer in the tree for data that has not yet been loaded, I get the obvious error Cannot read properties of undefined (reading 'options') - and subsequently after selecting/deselecting the selectAllCheckbox for layers that with data that had been added in and previously had not presented to be a problem that it will only compute one layer at a time - either deselecting one until none, or switching one sole layer on back and forth - with the error of Uncaught TypeError: Cannot read properties of undefined (reading 'parentNode').

Perhaps the solution is that theses children within the selectAllCheckbox subGroups need to be referenced in a separate 'overlayadd' 'overlayremove' function as a group? I do not have an issue with the selectAllCheckbox if the data is all loaded in, it only seems to occur when I have selected a ghost layer (no data underneath) and go on to select/deselect options that are available.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.