浏览代码

Mejoras de usabilidad.

Daniel Libonati 8 年之前
父节点
当前提交
c7293d548a
共有 2 个文件被更改,包括 721 次插入109 次删除
  1. 28 33
      Resources/views/Leaflet/create_vector.html.twig
  2. 693 76
      Resources/views/Leaflet/show_map.html.twig

+ 28 - 33
Resources/views/Leaflet/create_vector.html.twig

@@ -22,20 +22,20 @@
 
     var uuid = {{uuid}};
     var layerId = {{layerId}};
-    
+
     var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
             osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
             osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib }),
             map = new L.Map('map', { center: new L.LatLng({{map['lat']}}, {{map['lng']}}), zoom: {{map['zoom']}} }),
             drawnItems = L.featureGroup().addTo(map);
-    
+
     var controlLayers = L.control.layers({
         'osm': osm.addTo(map),
         "google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {
             attribution: 'google'
         })
-    }, { '{{layerName}}': drawnItems }, { position: 'topleft', collapsed: false }).addTo(map);
-    
+    }, { '{{layerName}}': drawnItems }, { position: 'topleft', collapsed: true }).addTo(map);
+
     addElements(drawnItems);
 
     addLayersMap();
@@ -56,12 +56,12 @@
     }));
 
     map.on(L.Draw.Event.CREATED, function (event) {
-        
+
         var layer = event.layer;
-        
+
         layer.layerType = event.layerType;
         drawnItems.addLayer(layer);
-        
+
         drawnItems.eachLayer(function(layer) {
             if (typeof layer._uuid !== 'undefined') {
                 // nada
@@ -69,9 +69,14 @@
                 layer._uuid = uuid + "_" + layer._leaflet_id;
             }
         });
-        
+        saveData();
+
+    });
+
+    map.on('draw:edited', function (e) {
+        saveData();
     });
-    
+
     map.on(L.Draw.Event.DELETED, function (event) {
 
         var layers = event.layers;
@@ -81,18 +86,11 @@
         });
 
         sendData = {'layerId' : layerId, 'vectors' : deleteLayers};
-        var json = JSON.stringify(sendData); 
-
-        $.ajax({
-            type: "POST",
-            url: "{{ url (url_delete) }}",
-            data: json,
-            contentType: "application/json; charset=utf-8",
-            dataType: "json",
-            success: function(msg) {}
-        });
+        var json = JSON.stringify(sendData);
+
+        saveData();
+
 
-       
     });
 
 function saveData()  {
@@ -116,16 +114,16 @@ function saveData()  {
 
     console.log(vectorData);
     sendData = {'layerId' : layerId, 'vectors' : vectorData};
-    var json = JSON.stringify(sendData); 
+    var json = JSON.stringify(sendData);
 
     $.ajax({
         type: "POST",
-        url: "{{ url (url_post) }}",
+        url: "{{ path('admin_map_vector_create_ajax') }}",
         data: json,
         contentType: "application/json; charset=utf-8",
         dataType: "json",
-        success: function(msg) {
-            alert(msg.msg);
+        error: function(msg) {
+            console.log(msg.msg);
         }
     });
 }
@@ -170,7 +168,7 @@ function addElements(layer) {
 
         {% endfor %}
     {% endif %}
-    
+
 }
 
 function addLayersMap() {
@@ -180,7 +178,7 @@ function addLayersMap() {
             {% set vectors = layer['vectors'] %}
             {% set layerName = layer['name'] %}
             {% if vectors|length > 0 %}
-                
+
                 newLayer = L.featureGroup();
 
                 {% for vector in vectors %}
@@ -222,7 +220,7 @@ function addLayersMap() {
                 controlLayers.addOverlay(newLayer, "{{layerName}}");
 
             {% endif %}
-        
+
         {% endfor %}
     {% endif %}
 }
@@ -237,10 +235,7 @@ function addLayersMap() {
             <div class="cms-block cms-block-element">
                 <div class="box">
                     <div class="box-body">
-                        <div id="map" style="margin:0 auto; width: 95%; height: 700px; border: 1px solid #ccc"></div>
-                        <br />
-                        <button onclick="saveData()" class="btn btn-block btn-primary" type="button">Guardar Elementos</button>
-                        <br />
+                        <div id="map" style="margin:0 auto; width: 100%; height: calc(100vh - 210px); border: 1px solid #ccc"></div>
                         <div id="datamaps">
                         </div>
                     </div>
@@ -248,7 +243,7 @@ function addLayersMap() {
             </div>
         </div>
     </div>
-    
+
 {% endblock %}
 
 
@@ -256,4 +251,4 @@ function addLayersMap() {
 {% endblock %}
 
 {% block list_filters %}
-{% endblock %}
+{% endblock %}

+ 693 - 76
Resources/views/Leaflet/show_map.html.twig

@@ -1,5 +1,16 @@
 {% extends 'SonataAdminBundle:CRUD:base_list.html.twig' %}
 
+{% block stylesheets %}
+{{ parent() }}
+<style>
+.no_checkbox>i.jstree-checkbox
+{
+    display:none
+}
+</style>
+<link rel="stylesheet" href="/css/tree/proton/style.css" />
+{% endblock %}
+
 {% block actions %}
 {% endblock %}
 
@@ -7,98 +18,201 @@
 {#{{ knp_menu_render(admin.sidemenu(action), {'currentClass' : 'active', 'template': sonata_admin.adminPool.getTemplate('tab_menu_template')}, 'twig') }}#}
 {% endblock %}
 
-{% block list_table %}
+{% block list_filters_actions %}
+{% endblock %}
 
-<div id="popups" style="display:none">
-    {% if layers|length > 0 %}
-        {% for layer in layers %}
-            {% set vectors = layer['vectors'] %}
-            {% if vectors|length > 0 %}
-                {% for vector in vectors %}
-                <div id="desc_vector_{{vector.getId()}}">{{vector.getDescription()|raw}}</div>
-                {% endfor %}
-            {% endif %}
-        {% endfor %}
-    {% endif %}
-</div>
+{% block list_filters %}
+{% endblock %}
+
+{% block list_table %}
 
 {% include 'LeafletBundle:Leaflet:resources.html.twig' %}
 
 <script type="text/javascript">
     window.SONATA_CONFIG.USE_ICHECK = false;
+    window.currentLayer = null; // Almacena el ID del layer actualmente seleccionado.
 
     var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
             osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
             osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib }),
             map = new L.Map('map', { center: new L.LatLng({{map['lat']}}, {{map['lng']}}), zoom: {{map['zoom']}} });
-    
+
     var controlLayers = L.control.layers({
-        'osm': osm.addTo(map),
-        "google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {
+        'OSM': osm.addTo(map),
+        "Google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {
             attribution: 'google'
         })
     }, {}, { position: 'topleft', collapsed: false });
-    
-    
+
     addLayersMap();
 
     controlLayers.addTo(map);
 
-    
+    // Agregar Clase para dibujo de vectores
+    window.drawControl = new L.Control.Draw();
+
+    map.on(L.Draw.Event.CREATED, function (event) {
+        /* Se dispara tras creación de vector */
+        var uuid = guid();
+        var layer = event.layer;
+        var currentLayer = layers[window.currentLayer];
+
+        layer.layerType = event.layerType;
+        layer.layerClass = "vector";
+        currentLayer.addLayer(layer);
+
+        currentLayer.eachLayer(function(layer) {
+            if (typeof layer._uuid === 'undefined') {
+                layer._uuid = uuid + "_" + layer._leaflet_id;
+            }
+        });
+
+        saveData();
+        refresh();
+    });
+
+    map.on(L.Draw.Event.DELETED, function (event) {
+        /* Se dispara tras borrado de vector */
+        var layers = event.layers;
+        var deleteLayers = new Array();
+        layers.eachLayer(function(layer) {
+            deleteLayers.push(layer._uuid);
+        });
+
+        sendData = {'layerId' : layerId, 'vectors' : deleteLayers};
+        var json = JSON.stringify(sendData);
+
+        saveData();
+        refresh()
+
+    });
+
+function saveData()  {
+   /*
+    * Guarda todos los layers presentes en window.layers
+    * y a sus correspondientes vectores
+    */
+    $.each(window.layers, function(id, myLayer) {
+        var vectorData = new Array();
+        if (myLayer) {
+            myLayer.eachLayer(function(layer) {
+
+                if(layer.layerType == "marker") {
+                    data = [layer._latlng];
+                } else if (layer.layerType == "circle") {
+                    data = [layer._latlng, layer._mRadius];
+                } else if (layer.layerType == "polyline") {
+                    data = [layer._latlngs];
+                } else {
+                    data = [layer._latlngs[0]];
+                }
+
+                object = {'_uuid': layer._uuid, 'layerType': layer.layerType, 'data': data, 'layerId': layer._id};
+                vectorData.push(object);
+            });
+
+            sendData = {'layerId' : id, 'vectors' : vectorData};
+            var json = JSON.stringify(sendData);
+
+            $.ajax({
+                type: "POST",
+                url: "{{ path('admin_map_vector_create_ajax') }}",
+                data: json,
+                contentType: "application/json; charset=utf-8",
+                dataType: "json",
+                error: function(msg) {
+                    console.log(msg.msg);
+                }
+            });
+        }
+    });
+}
 
 function addLayersMap() {
+   /*
+    * Obtiene todos los layers y vectores de un mapa.
+    * Luego los agrega al control Leaflet
+    */
+    window.layers = new Array();
+
+    var url = window.location.pathname;
+    var id = url.substring(url.lastIndexOf('/') + 1);
+    var mapData = getData('maps', id );
+
+    for(var i=0; i < mapData.layers.length; i++) {
+
+        var layer = mapData.layers[i];
+        newLayer = L.featureGroup();
+
+        for(var j=0; j < layer.vectors.length; j++ ) {
 
-    {% if layers|length > 0 %}
-        {% for layer in layers %}
-            {% set vectors = layer['vectors'] %}
-            {% set layerName = layer['name'] %}
-            {% if vectors|length > 0 %}
-                
-                newLayer = L.featureGroup();
-
-                {% for vector in vectors %}
-                    _id = {{vector.getId()}};
-                    name = "{{vector.getName()}}";
-                    type = "{{vector.getType()}}"
-                    _uuid = "{{vector.getUuid()}}"
-                    data = JSON.parse('{{vector.getData()}}'.replace(/&quot;/g,'"'));
-
-                    if(type == "marker") {
-                        style = JSON.parse('{{vector.getInlineStyle()}}'.replace(/&quot;/g,'"'));
-                        if (typeof style.icon !== 'undefined') {
-                            icon = style.icon;
-                        } else {
-                            icon = "miniBubbleBlueIcon";
+            var vector = layer.vectors[j];
+
+            _id = vector.id;
+            name = vector.name;
+            type = vector.type;
+            _uuid = vector.uuid;
+            data = JSON.parse(vector.data);
+            style = JSON.parse(vector.style);
+
+            if(type == "marker") {
+                if (typeof style.icon !== 'undefined') {
+                    icon = style.icon;
+                } else {
+                    icon = "miniBubbleBlueIcon";
+                }
+                object = new L.marker([data[0]['lat'], data[0]['lng']],{icon: eval(icon)});
+            } else if (type == "circle") {
+                object = new L.circle([data[0]['lat'], data[0]['lng']], data[1], style);
+            } else if (type == "polyline") {
+                object = new L.Polyline(data[0], style);
+            } else if (type == "rectangle"){
+                object = new L.Rectangle(data[0], style);
+            } else {
+                object = new L.Polygon(data[0], style);
+            }
+
+
+            object._id = _id;
+            object.name = name;
+            object._uuid = _uuid;
+            object.layerType = type;
+            object.layerClass = "vector";
+            var desc = $("#desc_vector_" + vector.id).html();
+
+            object.on("click", function (e) {
+                var layer = e.target
+
+                if (layer.class = 'vector') {
+                    // Deseleccionar todos los elementos.
+                    $('#jstree').jstree('deselect_all');
+                    $('#overlay_tree').jstree('deselect_all');
+
+                    for (key in layer._eventParents) {
+                        var leafletId = key;    // Debería iterar una sola vez
+                    }
+
+                    // Seleccionar elementos:
+                    for (key in window.layers) {
+                        if (window.layers[key]._leaflet_id == leafletId) {
+                            $('#jstree').jstree('select_node', key);
                         }
-                        object = new L.marker([data[0]['lat'], data[0]['lng']],{icon: eval(icon)});
-                    } else if (type == "circle") {
-                        object = new L.circle([data[0]['lat'], data[0]['lng']], data[1],{{vector.getInlineStyle()|raw}});
-                    } else if (type == "polyline") {
-                        object = new L.Polyline({{vector.getPolylinePoints()}},{{vector.getInlineStyle()|raw}});
-                    } else if (type == "rectangle"){
-                        object = new L.Rectangle({{vector.getPolylinePoints()}},{{vector.getInlineStyle()|raw}});
-                    } else {
-                        object = new L.Polygon({{vector.getPolylinePoints()}},{{vector.getInlineStyle()|raw}});
                     }
 
-                    object._id = _id;
-                    object.name = name;
-                    object._uuid = _uuid;
-                    object.layerType = type;
-                    desc = $("#desc_vector_{{vector.getId()}}").html();
-                    object.bindPopup("<b>" + name + "</b> <br />" + desc + " <div style='text-align:center;margin-top:5px'><a target='_blank'href='{{path('admin_map_vector_edit',{id: vector.getId()})}}'>EDIT</a></div>");
+                    /* Dar 200ms de espera, ya que esta instrucción puede ejecutarse
+                       incluso antes de que el árbol de vectores haya
+                       recibido la instrucción de recargarse. */
+                    setTimeout(function() { selectElement(layer._id) }, 200);
+                }
 
-                    newLayer.addLayer(object);
+            });
 
-                {% endfor %}
-                //newLayer.visible = true;
-                newLayer.addTo(map);
-                controlLayers.addOverlay(newLayer, "{{layerName}}");
+            newLayer.class = 'layer';
+            newLayer.addLayer(object);
+        }
 
-            {% endif %}
-        
-        {% endfor %}
-    {% endif %}
+        window.layers[layer.id] = newLayer.addTo(map);
+    }
 }
 
 </script>
@@ -106,23 +220,526 @@ function addLayersMap() {
 {% endblock %}
 
 {% block content %}
-    <div class="row">
-        <div class="col-md-12">
-            <div class="cms-block cms-block-element">
-                <div class="box">
-                    <div class="box-body">
-                        <div id="map" style="margin:0 auto; width: 95%; height: 700px; border: 1px solid #ccc"></div>
-                    </div>
+<div class="col-md-10">
+    <div class="cms-block cms-block-element">
+        <div class="box">
+            <div class="box-body">
+                <div id="map" style="margin:0 auto; width: 100%; height: calc(100vh - 190px); border: 1px solid #ccc"></div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="col-md-2">
+    <div class="box">
+        <div class="box-header">
+            <h4>Capas</h4>
+            <div class="box-body">
+                <div class="jstree-proton" id="jstree" style="max-height: calc(100vh - 250px); overflow-y: auto; overflow-x: hidden;"></div>
+            </div>
+            <button class="btn btn-default btn-xs" id="btnNewLayer"><i class="fa fa-file-o" data-toggle="modal" data-target="#modalNewLayer"></i></button>
+            <button onclick="refresh()" class="btn btn-default btn-xs"><i class="fa fa-refresh" aria-hidden="true"></i></button>
+            <button id="btnSave" class="btn btn-default btn-xs"><i class="fa fa-floppy-o" aria-hidden="true"></i></button>
+        </div>
+    </div>
+    <div class="box">
+        <div class="box-header">
+            <h4>Elementos</h4>
+            <br/>
+            <div class="btn-group">
+                <button class="btn btn-default btn-xs" onclick="new L.Draw.Polyline(map, drawControl.options.polyline).enable();"><i class="fa fa-share-alt" aria-hidden="true"></i></button>
+                <button class="btn btn-default btn-xs" onclick="new L.Draw.Polygon(map, drawControl.options.polygon).enable();"><i class="fa fa-star-o" aria-hidden="true"></i></button>
+                <button class="btn btn-default btn-xs" onclick="new L.Draw.Rectangle(map, drawControl.options.rectangle).enable();"><i class="fa fa-stop" aria-hidden="true"></i></button>
+                <button class="btn btn-default btn-xs" onclick="new L.Draw.Circle(map, drawControl.options.circle).enable();"><i class="fa fa-circle-o" aria-hidden="true"></i></button>
+                <button class="btn btn-default btn-xs" onclick="new L.Draw.Marker(map, drawControl.options.marker).enable();"><i class="fa fa-map-marker" aria-hidden="true"></i></button>
+            </div>
+
+        </div>
+        <div class="box-body">
+            <div class="jstree-proton" id="overlay_tree" style="max-height: calc(100vh - 450px); overflow-y: auto; overflow-x: hidden;"></div>
+        </div>
+    </div>
+</div>
+
+
+<!-- Modal Nueva Capa -->
+<div class="modal fade" id="modalNewLayer" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                <h4 class="modal-title" id="myModalLabel">Nueva Capa</h4>
+            </div>
+            <div class="modal-body">
+                <div class="container-fluid">
+                    <label class="control-label col-md-3">Nombre</label>
+                    <input class="form-control col-md-9" name="name" id="newLayerName"></input>
                 </div>
             </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-default" data-dismiss="modal">Cancelar</button>
+                <button onclick="createNewLayer()" type="submit" class="btn btn-primary">Crear</button>
+            </div>
         </div>
     </div>
-    
-{% endblock %}
+</div>
+<!-- Fin - Modal Nueva Capa -->
+
+<!-- Modal Renombrar -->
+<div class="modal fade" id="modalRename" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                <h4 class="modal-title" id="myModalLabel">Renombrar</h4>
+            </div>
+            <div class="modal-body">
+                <div class="container-fluid">
+                    <label class="control-label col-md-3">Nombre</label>
+                    <input class="form-control col-md-9" name="name" id="newName"></input>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-default" data-dismiss="modal">Cancelar</button>
+                <button onclick="renameItem()" type="submit" class="btn btn-primary">Renombrar</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!-- Fin - Modal Renombrar -->
 
+<!-- Modal Renombrar -->
+<div class="modal fade" id="modalDelete" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+    <div class="modal-dialog modal-danger" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                <h4 class="modal-title" id="myModalLabel">Borrar</h4>
+            </div>
+            <div class="modal-body">
+                <p>¿Está seguro de que desea borrar <span id="elementTypeCaption"?></span> "<span id="elementName"></span>"?</p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-danger" data-dismiss="modal">Cancelar</button>
+                <button onclick="deleteItem()" type="submit" class="btn btn-danger">Borrar</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!-- Fin - Modal Renombrar -->
 
-{% block list_filters_actions %}
 {% endblock %}
 
-{% block list_filters %}
-{% endblock %}
+
+
+
+
+
+{% block javascripts %}
+{{ parent() }}
+<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.4/jstree.min.js"></script>
+<script>
+
+$(document).ready(function() {
+
+    $('#btnSave').click(function() {
+        /*
+         * Desactiva la edición en todas las capas,
+         * luego postea los datos existentes.
+         */
+        $.each(window.layers, function(id, myLayer) {
+            if (myLayer) {
+                myLayer.eachLayer(function(layer) {
+                    layer.editing.disable();
+                });
+            }
+        });
+
+        saveData();
+    });
+
+    // Defaults para el plugin JSTree
+    $.jstree.defaults.core.dblclick_toggle = false;
+    $.jstree.defaults.core.multiple = false;
+    $.jstree.defaults.checkbox.three_state = false;
+    $.jstree.defaults.checkbox.whole_node = false;
+    $.jstree.defaults.checkbox.tie_selection = false;
+    $.jstree.defaults.contextmenu.select_node = true;
+
+    $('#jstree').on('check_node.jstree uncheck_node.jstree', function (e, data) {
+        if (data.node.type == 'layer') {
+            if (data.node.state.checked) {
+                map.addLayer(layers[data.node.id]);
+            } else {
+                map.removeLayer(layers[data.node.id]);
+            }
+        }
+    });
+
+    $('#jstree').on('select_node.jstree', function (e, data) {
+        if (data.node.type == 'layer') {
+            window.currentLayer = data.selected;
+            $('#overlay_tree').jstree(true).settings.core.data = makeOverlayTree(layers[data.selected]);
+            $('#overlay_tree').jstree(true).refresh();
+        }
+        toggleControls();
+    });
+
+    $('#overlay_tree').on('select_node.jstree', function (e, data) {
+        var vectorData = getData('vectors', data.node.id);
+        map.eachLayer(function(layer){
+            if (layer._id == data.node.id) {
+                var popupContent = "<b>" + vectorData.name + "</b><br/>" + vectorData.description + " <div style='text-align:center;margin-top:5px'><a target='_blank' href='/admin/map/vector/" + vectorData.id + "/edit'>EDITAR</a></div>";
+                layer.bindPopup(popupContent).openPopup();
+            }
+        });
+    })
+
+    initializeTrees();
+});
+
+function getData(entity, id=null) {
+
+    /*
+     * Dadas una entidad y opcionalmente un id, devuelve un JSON obtenido
+     * mediante una llamada AJAX a la API REST.
+     */
+
+    var result = null;
+
+    if (id == null) {
+        var url = "/api/" + entity
+    } else {
+        var url = "/api/" + entity + "/" + id
+    }
+
+    $.ajax({
+        type: "GET",
+        dataType: "json",
+        async: false,
+        cache: false,
+        url: url,
+        success: function(data){
+            result = data;
+        },
+        error: function(XMLHttpRequest, textStatus, errorThrown) {
+            console.log("Status: " + textStatus); alert("Error: " + errorThrown);
+        }
+    });
+
+    return result;
+
+}
+
+function makeTree() {
+
+    /*
+     * Función que obtiene la información de las capas y se la entrega
+     * procesada al JSTree de capas.
+     */
+
+    var url = window.location.pathname;
+    var id = url.substring(url.lastIndexOf('/') + 1);
+    var mapData = getData('maps', id );
+    var result = [];
+
+    result.push({ "id" : "map_" + mapData.id, "parent" : "#", "text" : mapData.name,
+                  "type": "map", a_attr: {class: "no_checkbox"}, state: { opened : true } });
+
+    for(var i = 0; i < mapData.layers.length; i++) {
+        var layer = mapData.layers[i];
+        result.push({ "id" : layer.id, "parent" : "map_" + mapData.id,
+                      "text" : layer.name, "type" : "layer", state: { checked : true } });
+    }
+
+    return result;
+}
+
+function makeOverlayTree(layer) {
+
+   /*
+    * Función que obtiene los vectores dentro de una capa y se los
+    * entrega procesados al JSTree de elementos.
+    */
+
+    result = [];
+
+    if (layer) {
+        var layerss = layer.getLayers();
+        for(var j = 0; j < layerss.length; j++) {
+            var vector = layerss[j];
+            result.push({ "id" :vector._id, "parent" : "#", "text" : vector.name,
+            "type": vector.layerType,  a_attr: {class: "no_checkbox"} });
+        };
+    }
+
+    return result;
+}
+
+function customMenu(node) {
+
+    var items = {
+        editItem: {
+            label: "Editar",
+            action: function () { treeMenuEdit(node) } // No pasar la función directamente, sino la ejecuta al hacer click.
+        },
+        renameItem: {
+            label: "Renombrar",
+            action: function () {
+                if (node.type == 'layer') {
+                    window.activeType = 'layer';
+                } else {
+                    window.activeType = 'vector';
+                }
+                window.activeId = node.id;
+                $("#newName").val(node.text);
+                $("#modalRename").modal();
+            }
+        },
+        deleteItem: {
+            label: "Borrar",
+            action: function () {
+                if (node.type == 'layer') {
+                    $("#elementTypeCaption").text('la capa');
+                    window.activeType = 'layer';
+                } else {
+                    $("#elementTypeCaption").text('el elemento');
+                    window.activeType = 'vector';
+                }
+                window.activeId = node.id;
+                $("#elementName").text(node.text);
+                $("#modalDelete").modal();
+            }
+        }
+    };
+
+    return items;
+}
+
+function treeMenuEdit(node) {
+    if (node.type == "layer") {
+        var editor = new L.EditToolbar.Edit(map, {
+                featureGroup: drawControl.options.featureGroup,
+                selectedPathOptions: drawControl.options.edit.selectedPathOptions
+            })
+    } else  {
+        var currentlayer = layers[[$('#jstree').jstree(true).get_selected()[0]]];
+        currentlayer.eachLayer(function(layer) {
+            if (layer._id == node.id) {
+                var vector = layer;
+                vector.editing.enable();
+            }
+        });
+    }
+}
+
+function guid() {
+  return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+    s4() + '-' + s4() + s4() + s4();
+}
+
+function s4() {
+  return Math.floor((1 + Math.random()) * 0x10000)
+    .toString(16)
+    .substring(1);
+}
+
+function initializeTrees() {
+
+   /*
+    * Inicializa los controles JSTree
+    */
+
+    $('#jstree').jstree({
+
+        'core' : {
+            "animation" : 200,
+            "check_callback" : true,
+            "data" : makeTree(),
+            "strings" : { 'Loading ...' : 'Cargando...' },
+        },
+
+        "types" : {
+            "map" : {
+              "icon" : "/images/tree/16x16/Map.png",
+              "valid_children" : ["layer"]
+            },
+            "layer" : {
+              "icon" : "/images/tree/16x16/Layers.png",
+              "valid_children" : ["vector","object"]
+            },
+            "#" : {
+                "valid_children" : []
+            }
+        },
+        "plugins" : [ "dnd", "contextmenu", "checkbox", "types", "changed", "wholerow" ],
+        contextmenu: {items: customMenu}
+    });
+
+    $('#overlay_tree').jstree({
+
+        'core' : {
+            "animation" : 0,
+            "check_callback" : false,
+            "data" : makeOverlayTree(),
+            "themes" : { "dots": false },
+            "strings" : { 'Loading ...' : 'Cargando...' },
+        },
+
+        "types" : {
+              "polygon" : {
+              "icon" : "/images/tree/16x16/Polygon.png",
+              "valid_children" : []
+            },
+            "circle" : {
+              "icon" : "/images/tree/16x16/Circle.png",
+              "valid_children" : []
+            },
+            "rectangle" : {
+              "icon" : "/images/tree/16x16/Rectangle.png",
+              "valid_children" : []
+            },
+            "polyline" : {
+              "icon" : "/images/tree/16x16/Polyline.png",
+              "valid_children" : []
+            },
+            "marker" : {
+              "icon" : "/images/tree/16x16/Marker.png",
+              "valid_children" : []
+            },
+            "#" : {
+                "valid_children" : []
+            }
+        },
+        "plugins" : [ "dnd", "contextmenu", "checkbox", "types", "changed", "wholerow" ],
+        contextmenu: {items: customMenu}
+    });
+}
+
+function createNewLayer() {
+
+    var url = window.location.pathname;
+    var mapid = parseInt(url.substring(url.lastIndexOf('/') + 1));
+    var name = $('#newLayerName').val();
+
+    sendData = {name: name, map: mapid};
+
+    $.ajax({
+        type: "POST",
+        url: "{{ path('post_layer') }}",
+        async: false,
+        data: JSON.stringify(sendData),
+        contentType: "application/json; charset=utf-8",
+        dataType: "json",
+        error: function(msg) {
+            console.log(msg.msg);
+        }
+    });
+
+    $('#modalNewLayer').modal('hide');
+    refresh();
+
+}
+
+function deleteItem() {
+   /*
+    * Dados el tipo de elemento y el id del mismo, realiza una petición HTTP
+    * a la API para que realice el borrado del mismo.
+    */
+
+    var itemId = window.activeId
+    var itemType = window.activeType
+
+    if (itemType == 'layer') {
+        var url = '/api/layers/' + itemId;
+    } else if (itemType == 'vector') {
+        var url = '/api/vectors/' + itemId;
+    }
+
+    $.ajax({
+        type: "DELETE",
+        url: url,
+        async: false,
+        contentType: "application/json; charset=utf-8",
+        dataType: "json",
+        error: function(msg) {
+            console.log(msg.msg);
+        }
+    });
+
+    $('#modalDelete').modal('hide');
+    refresh();
+}
+
+function renameItem() {
+   /*
+    * Dados el tipo de elemento y el id del mismo, realiza una petición HTTP
+    * a la API para cambiar el nombre.
+    */
+
+    var itemId = window.activeId
+    var itemType = window.activeType
+
+    if (itemType == 'layer') {
+        var url = '/api/layers/' + itemId;
+    } else if (itemType == 'vector') {
+        var url = '/api/vectors/' + itemId;
+    }
+
+    sendData = {id: itemId, name: $('#newName').val()};
+
+    $.ajax({
+        type: "PATCH",
+        url: url,
+        async: false,
+        data: JSON.stringify(sendData),
+        contentType: "application/json; charset=utf-8",
+        dataType: "json",
+        error: function(msg) {
+            console.log(msg.msg);
+        }
+    });
+
+    $('#modalRename').modal('hide');
+
+    refresh();
+}
+
+function refresh() {
+    /* Recarga todos los elementos */
+
+    map.eachLayer(function (layer) {
+        if (layer._url != "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") { //TODO: Buscar otra manera de matchear el mapa base.
+            map.removeLayer(layer);
+        }
+    });
+    window.layers = null;   /* Destruir global */
+    addLayersMap();
+
+    $('#jstree').jstree(true).settings.core.data = makeTree();
+    $('#overlay_tree').jstree(true).settings.core.data = makeOverlayTree(null);
+    $('#jstree').jstree(true).refresh();
+}
+
+function selectElement(elementId) {
+   /*
+    * Espera (preventivamente) a que los elementos se carguen e intenta
+    * seleccionar el elemento en cuestión
+    */
+
+    if ($('#overlay_tree').jstree().is_loading()) {
+        window.setTimeout(waitForLoad, 100); /* Reintenta en 100ms si está cargando */
+    } else {
+        $('#overlay_tree').jstree('select_node', elementId);
+    }
+}
+
+function toggleControls() {
+    if ($('#overlay_tree').jstree('get_selected') == '[]') {
+        $('#btnSave').prop('disabled', 'disabled');
+    } else {
+        $('#btnSave').removeProp('disabled');
+    }
+}
+
+</script>
+{% endblock %}