var width = 1300; var height = 680; //Width y height de las convenciones var w = 200; var h = 115; //Setup del mapa leaflet en un centro específico y un "estilo" b/n var map = L.map('map').setView([8.969970, -79.494529], 12); mapLink = L.tileLayer('https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png').addTo(map); // sin doble click para zoom map.doubleClickZoom.disable(); //cuando ya está el mapa se agrega un svg en donde dibujar var svgLayer = L.svg(); svgLayer.addTo(map); //svg en donde dibujar var svg = d3.select("#map").select("svg"); var g = svg.select('g'); //svg en donde dibujar las convenciones var svg2 = d3.selectAll("#map") .append("svg") .attr('class', "convenciones") .attr('width', w) .attr('height', h) .style("top", svg.node().parentNode.offsetTop + 500 + "px") .style("left", svg.node().parentNode.offsetLeft + 40 + "px") ; svg2.append("rect") .attr('x', 0) .attr('y', 0) .attr('width', w) .attr('height', h) .style('fill', "#fafafa") .style('opacity', .9) ; var tooltip = d3.select("body") .append("div") .attr("class", "tooltip") .style("opacity", 0) ; //Define la siulación de fuerza var fuerza = d3.forceSimulation() //fuerza atractora hacia el centro de los nodos que no están conectados .force("x", d3.forceX(width / 2).strength(.3)) .force("y", d3.forceY(height / 2).strength(.3)) //link con el id del json .force("link", d3.forceLink() .id(function(d){ return d.id; }) //distancia de los links .distance(50)) //fuerza entre los nodos .force("charge", d3.forceManyBody().strength(-150)) ; //Leer datos de ambos json y llamar la funcion que dibuja todo d3.json('proyectos_v9.json', function(data){ //pasar los datos a una variable var graph = data; //Printea los datos para verificar console.log("Número de Actores y Proyectos: " + graph.nodes.length) console.log(graph.nodes); console.log("Número de links: " + graph.edges.length) console.log(graph.edges); //dibuja las convenciones convenciones(); //crea las lineas con un svg y los datos de "edges" var lineas = g.selectAll("line") .data(graph.edges) .enter() .append("line") .attr('class', "link") ; //crea los nodos de acuerdo a los nombres var nodos = g.selectAll("circle") .data(graph.nodes) .enter() .append("circle") //dependiendo del tipo o el area le da una clase del CSS .attr('class', function(d){ if (d.area == "urbano" || d.area == "ambiental" || d.area == "inclusion"){ return "nodos " + d.area; } if (d.tipo == "actor" || d.tipo == "colaborador" || d.tipo == "financiador"){ return "nodos " + d.tipo; } }) .style("stroke", function(d){ if (d.tipo == "proyecto" || d.tipo == "programa" || d.tipo == "plan"){ return "black"; } }) .style('stroke-width', 2) .attr('stroke-dasharray', function(d){ if(d.tipo == "programa"){ return ("1,1"); } else if(d.tipo == "plan"){ return ("2,4") } }) .attr('r',5 ) .attr("pointer-events","visible") //si se hace click se muestran las conexiones .on("click", connectedNodes) .on("mouseover", function(d){ tooltip .html(function(){ return "
" + d.id + "
" + "
" + d.descripcion ; }) .style("top", svg.node().parentNode.offsetTop + height - 260 + "px") .style("left", svg.node().parentNode.offsetLeft + width - 350 + "px") .style("opacity", .9) ; }) .on("mouseout", function(){ tooltip.style("opacity", 0); }) ; //le dice a la simulacion cuales son los nodos y los links fuerza.nodes(graph.nodes); fuerza.force("link").links(graph.edges); //simulación y actualizacion de la posicion de los nodos en cada "tick" fuerza.on("tick", function (){ lineas .attr('x1', function(d){ return d.source.x; }) .attr('y1', function(d){ return d.source.y; }) .attr('x2', function(d){ return d.target.x; }) .attr('y2', function(d){ return d.target.y; }) ; //pinta los nodos dentro del radio y el W y Y nodos .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) ; }); //saber si las conexiones se ven o no var toggle = 0; //Create an array logging what is connected to what var linkedByIndex = {}; for (i = 0; i < graph.nodes.length; i++) { linkedByIndex[i + "," + i] = 1; }; graph.edges.forEach(function (d) { linkedByIndex[d.source.index + "," + d.target.index] = 1; }); //This function looks up whether a pair are neighbours function neighboring(a, b) { return linkedByIndex[a.index + "," + b.index]; } function connectedNodes() { if (toggle == 0) { //Reduce the opacity of all but the neighbouring nodes d = d3.select(this).node().__data__; nodos .transition() .style("opacity", function (o) { return neighboring(d, o) | neighboring(o, d) ? 1 : 0; }) .attr('r', function(o){ return neighboring(d, o) | neighboring(o, d) ? 10 : 5; }) .style('stroke-width', function(o){ return neighboring(d, o) | neighboring(o, d) ? 4 : 2; }) ; lineas .transition() .style("opacity", function (o) { return d.index==o.source.index | d.index==o.target.index ? 0.5 : 0; }) ; //Reduce the opacity toggle = 1; }else { //vuelve a poner la opacidad en 1 de los proyectos y 0.1 de los actores, etc nodos .filter(function(d){ return d.tipo == "proyecto" || d.tipo == "programa" || d.tipo == "plan"; }) .transition() .style("opacity", 1) .attr('r', 5) .style('stroke-width', 2) ; nodos .filter(function(d){ return d.tipo == "actor" || d.tipo == "colaborador" || d.tipo == "financiador" ; }) .transition() .style("opacity", 1) .attr('r', 5) ; // y las lineas a 0 lineas .transition() .style("opacity", 0) ; toggle = 0; } } //Function which sets the transformation attribute to move the circles to the correct location on the map function drawAndUpdateCircles() { //si tiene lon y lat clavelos al punto en el mapa //gracias a Andrew Reid (user:7106086 en stackoverflow) graph.nodes.forEach(function(d) { if(d.lon && d.lat) { p = new L.LatLng(d.lat, d.lon); var layerPoint = map.latLngToLayerPoint(p); d.fx = layerPoint.x; d.fy = layerPoint.y; } else { d.fx = width - 100; } }) // reinicie la simulación para que los puntos puedan quedar en donde son si se hace zoom o drag fuerza .alpha(1) .restart() ; } function convenciones(){ //CONVENCIONES // crea el nido donde se miran las categorias de los nodos var convencion_area = d3.nest() .key(function(d) { return d.area; }) .rollup(function(d) { return d.length; }) .entries(graph.nodes) ; var convencion_tipo = d3.nest() .key(function(d) { return d.tipo; }) .rollup(function(d) { return d.length; }) .entries(graph.nodes) ; // area var areaX = 15; var areaY = 20; var radio = 5; var offset_texto = 0.7; var espaciado = 15; var legend = svg2.append("g") .append("svg") .attr("class", "legend") .selectAll("g") .data(convencion_area) .enter() .append("g") .attr("transform", function(d, i) { return "translate(0," + i * espaciado + ")"; }) ; legend.append("circle") .attr('cx', areaX + radio) .attr('cy', areaY) .attr('r', radio) .attr('class', function (d) { return "nodos" + (d.key ? " " + d.key: ""); }) .on("click", function(d){ // determine if current line is visible var active = nodos.active ? false : true var newOpacity = active ? 0 : 1; if(d.key == "urbano"){ d3.selectAll(".nodos.urbano") .style("opacity", newOpacity); } else if (d.key == "ambiental"){ d3.selectAll(".nodos.ambiental") .style("opacity", newOpacity); }else if (d.key == "inclusion") { d3.selectAll(".nodos.inclusion") .style("opacity", newOpacity); } nodos.active = active; }) ; legend.append("text") .attr("x", areaX + 15) .attr('y', areaY - offset_texto) .attr("dy", ".35em") .text(function(d) { return d.key; }) ; // tipo var tipoX = 100; var tipoY = 20; var legend_2 = svg2.append("g") .append("svg") .attr("class", "legend") .selectAll("g") .data(convencion_tipo) .enter() .append("g") .attr("transform", function(d, i) { return "translate(0," + i * espaciado + ")"; }) ; legend_2.append("circle") .attr('cx', tipoX + radio) .attr('cy', tipoY) .attr('r', radio) .attr('class', function (d) { return "nodos" + (d.key ? " " + d.key: ""); }) .filter(function(d){ return d.key == "proyecto" || d.key == "programa" || d.key == "plan" ; }) .style('fill-opacity', 0) .style('stroke', "black") .style("stroke-width", 2) .attr('stroke-dasharray', function(d){ if(d.key == "programa"){ return ("1,1"); } else if(d.key == "plan"){ return ("2,4") } }) ; legend_2.append("text") .attr("x", tipoX + 15) .attr('y', tipoY - offset_texto) .attr("dy", ".35em") .text(function(d) { return d.key; }) ; } //Dibuja los circulos actualizados en el mapa drawAndUpdateCircles(); map.on("moveend", drawAndUpdateCircles); });