//Tamaño de la viz var width = screen.width; var height = screen.height; var ancho_convenciones = 760; var alto_convenciones = (width / 100) * 2.5; // ancho y alto cuadro actores var anchoCuadroActores = (width / 100) * 16; var altoCuadroActores = (height / 100) * 50; //Setup del mapa leaflet en un centro específico y un "estilo" (layer) b/n var map = L.map('map', { zoomControl:false }).setView([4.624565, -74.101109], 12); //mapLink = L.tileLayer('https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png').addTo(map); mapLink = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png').addTo(map); //mapLink = L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png').addTo(map); //mapLink = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png').addTo(map); map.doubleClickZoom.disable(); //map.dragging.disable(); //map.scrollWheelZoom.disable(); //cuando ya está el mapa se agrega un svg en donde dibujar var svgLayer = L.svg(); svgLayer.addTo(map); //seleccion el svg del mapa y crea un grupo 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', ancho_convenciones) .attr('height', alto_convenciones) .style("top", svg.node().parentNode.offsetTop + (height / 100) * 80 + "px") .style("left", svg.node().parentNode.offsetLeft + (width / 100) * 3 + "px") ; // crea el tooltip y le da las características var tooltip = d3.select("body") .append("div") .style("top", ((height / 100) * 15) + "px") .style("left", ((width / 100) * 3) + "px") .attr("class", "tooltip") .style("opacity", 0) ; // crea el tooltip y le da las características var tooltip_2 = d3.select("body") .append("div") .style("top", ((height / 100) * 50) + "px") .style("left", ((width / 100) * 3) + "px") .attr("class", "tooltip") .style("opacity", 0) ; //Define la simulación de fuerza var fuerza = d3.forceSimulation() .force("link", d3.forceLink() .id(function(d){ return d.id; }) ) ; //Leer datos de ambos json y llamar la funcion que dibuja todo d3.json('proyectos y actores_v4.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(); // cuadro debajo de los actores, la posicion se coloca en la funcion que actualiza todo var cuadroActores = g.append("rect") .attr('width', anchoCuadroActores) .attr('height', altoCuadroActores) .style('fill', "#fafafa") .style('opacity', 1) .attr("rx", 2) .attr("ry", 2) //.style("filter", "url(#drop-shadow)") ; //crea los links entre nodos con un svg y los datos de "edges" var lineas = g.append('g') .selectAll("line") .data(data.edges) .enter() .append("path") .attr('class', "link") ; // decide cuales nodos van en el proceso, cuales en el mapa y cuales de los actores function isNodeOnLegend(d) { return d.tipo === "actor"; } function isNodeOnMap(d){ return d.tipo === "iniciativa" || d.tipo === "iniciativa_juvenil"; } function isNodeOnBottom(d){ return d.tipo === "penal"; } //funcion donde se pintan los nodos function constructNodes(nodeList, className) { //crea el grupo donde pintar los nodos var nodes = g .append("g") .attr("class", className) .selectAll(".nodos") .data(nodeList) .enter() .append("g") .attr("class", "gnode") ; //pinta la nodos, características y tooltip nodes.append("circle") .attr('class', function(d){ if (isNodeOnBottom(d)) { return "nodos " + d.proceso; } if (isNodeOnLegend(d)) { return "nodos " + d.bloque; } if (isNodeOnMap(d)) { return "nodos " + d.tipo; } }) .attr('r', 6) .attr("pointer-events","visible") .on("click", connectedNodes) .on('mouseover', function(d){ tooltip .html(function(){ if (d.tipo == "iniciativa" || d.tipo == "iniciativa_juvenil"){ return d.id + "
" + d.descripción; } else { return d.descripción; } }) .transition() .style("opacity", 1) ; tooltip_2 .html(function(){ return "VÍCTIMA" + "
" + d.víctima + "
" + "OFENSOR" + "
" + d.ofensor; }) .transition() .style("opacity", 1) ; }) .on('mouseout', function() { tooltip .transition() .style("opacity", 0) ; tooltip_2 .transition() .style("opacity", 0) ; }) .attr('opacity', 1) ; return nodes; } function dibujar_tooltip(){ tooltip .html(function(){ return d.descripción; }) .transition() .style("opacity", 1) ; tooltip_2 .html(function(){ return "VÍCTIMA" + "
" + d.víctima + "
" + "OFENSOR" + "
" + d.ofensor; }) .transition() .style("opacity", 1) ; } //guarda los nodos en una variable dependiendo de un filtro var nodesOnMap = constructNodes(graph.nodes.filter(isNodeOnMap), "onmap"); var nodesOnLegend = constructNodes(graph.nodes.filter(isNodeOnLegend), "onlegend"); var nodesOnBottom = constructNodes(graph.nodes.filter(isNodeOnBottom), "onbottom"); var nodesAll = g.selectAll(".gnode"); // agrega los textos de los nodos de los actores var textoActores = svg .append("g") .selectAll("text") .data(graph.nodes.filter(isNodeOnLegend)) .enter() .append("text") .text(function(d) { return d.id; }) .attr('class', "text") .attr('dx', "1em") .attr('dy', "0.3em") .attr('font-size', '11') ; // agrega los textos de el proceso penal var textoProceso = svg .append("g") .selectAll("text") .data(graph.nodes.filter(isNodeOnBottom)) .enter() .append("text") .text(function(d) { return d.id; }) .attr('class', "text") .attr('dy', "2.5em") .attr("text-anchor", "middle") .attr('font-size', '9') ; //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("d", function(d) { return `M${d.source.x},${d.source.y}Q${width*0.7},${height*0.7} ${d.target.x},${d.target.y}`; }); //funcion para actualizar la posicion de los nodos (rioV8) function nodeUpdate(nodes) { nodes.attr("transform", function (d) { return `translate(${d.x},${d.y})`; }); } nodeUpdate(nodesOnMap); nodeUpdate(nodesOnLegend); nodeUpdate(nodesOnBottom); }); //saber si las conexiones se ven o no var toggle = 0; //Crea un arreglo de las cosas que estan conectadas entre si 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; }); //Funcion para determinar si dos nodos estan conectados function neighboring(a, b) { return linkedByIndex[a.index + "," + b.index]; } // Función para saber que hacer cuando se haga click function connectedNodes() { if (toggle == 0) { //Reduce la opacidad de todo menos de los que esten conectados d = d3.select(this.parentNode).datum(); nodesAll .transition() .style("opacity", function (o) { return neighboring(d, o) | neighboring(o, d) ? 1 : 0; }) ; lineas .transition() .style("opacity", function (o) { return d.index==o.source.index | d.index==o.target.index ? 1 : 0; }) ; textoActores .transition() .style('opacity', function(o){ return neighboring(d, o) | neighboring(o, d) ? 1 : 0; }) textoProceso .transition() .style('opacity', function(o){ return neighboring(d, o) | neighboring(o, d) ? 1 : 0; }) d3.selectAll('.lineasProceso') .transition() .style('opacity', 0) ; d3.selectAll('.tooltip') .style("opacity", 1) ; tooltip .style('opacity', 1) ; toggle = 1; } else { // devuelve los nodos a la normalidadlos links invisibles nodesAll .transition() .style("opacity", 1) ; lineas .transition() .style("opacity", 0) ; textoActores .transition() .style('opacity', 1) ; textoProceso .transition() .style('opacity', 1) ; d3.selectAll('.lineasProceso') .transition() .style('opacity', 1) ; toggle = 0; } } //Function which sets the transformation attribute to move the circles to the correct location on the map function drawAndUpdateCircles(){ //Ubicación de los nodos en el mapa var cuadroActoresLatLon = map.latLngToLayerPoint(map.getBounds().getNorthEast()); var cuadroProcesoLatLon = map.latLngToLayerPoint(map.getBounds().getNorthWest()); //Numero de nods de actores y del proceso var numeroNodosActores = graph.nodes.filter(isNodeOnLegend); // variable del espaciado entre Actores var espaciadoActores = (altoCuadroActores) / numeroNodosActores.length; //si tiene ubicacion, anclelos al mapa nodesOnMap.each(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; } }); // posicion arreglada del cuadro de actores cuadroActores .attr('x', cuadroActoresLatLon.x - (anchoCuadroActores * 1.1)) .attr('y', cuadroActoresLatLon.y + ((height / 100) * 20)) ; // nodos del cuadro de actores nodesOnLegend.each( function (d, i) { d.fx = cuadroActoresLatLon.x - (anchoCuadroActores * 1.025); d.fy = cuadroActoresLatLon.y + ((height / 100) * 22) + i * espaciadoActores; }); //textos de los actores textoActores.each(function(d, i, nodesOnLegend){ d3.select(nodesOnLegend[i]) .attr('x', cuadroActoresLatLon.x - (anchoCuadroActores * 1) ) .attr('y', cuadroActoresLatLon.y + ((height / 100) * 22) + i * espaciadoActores) }); fuerza .alpha(1) .restart() ; } // Función para saber cómo hacer lo de las convenciones function convenciones(){ //cuadro de las convenciones var cuadroConv = svg2.append("rect") .attr('x', 0) .attr('y', 0) .attr('width', ancho_convenciones) .attr('height', alto_convenciones) .style('fill', "#fafafa") .style('opacity', 1) //.style("filter", "url(#drop-shadow)") ; var convencion_tipo = d3.nest() .key(function(d){ return d.tipo; }) .rollup(function(d) { return d.length; }) .entries(graph.nodes) ; // crea el nido donde se miran las categorias de los nodos var convenciones_bloque = d3.nest() .key(function(d) { return d.bloque; }) .rollup(function(d) { return d.length; }) .entries(graph.nodes) ; var radio = 5; var offset_texto = 0.7; var espaciado = 120 ; // Circulos del Tipo var tipoX = 20; var tipoY = alto_convenciones / 2; //Circulos de los bloques var bloqueX = 500; var bloqueY = alto_convenciones / 2; var legend = svg2.append("g") .append("svg") .attr("class", "legend") .selectAll("g") .data(convenciones_bloque) .enter() .append("g") .attr("transform", function(d, i) { return "translate(" + i * espaciado + ",0)"; ; }); legend.append("circle") .attr('cx', (bloqueX + radio) - 170) .attr('cy', bloqueY) .attr('r', radio) .attr('class', function (d) { return "nodos" + (d.key ? " " + d.key: ""); }) ; legend.append("text") .attr("x", (bloqueX + 15) - 170) .attr('y', bloqueY) .attr("dy", ".35em") .style('font-size', 10 + "px") .text(function(d) { return d.key; }) ; 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(" + i * espaciado + ",0)"; }) ; legend_2.append("circle") .attr('cx', (tipoX + radio) - 120) .attr('cy', tipoY) .attr('r', radio) .attr('class', function (d) { return "nodos" + (d.key ? " " + d.key: ""); }) ; legend_2.append("text") .attr("x", (tipoX + 15) - 120) .attr('y', tipoY) .attr("dy", ".35em") .style('font-size', 10 + "px") .text(function(d) { if(d.key == "iniciativa_juvenil"){ return "SRPA"; } else if (d.key == "iniciativa"){ return "Sistema Penal" } }) .attr('class', 'text') ; } //Dibuja los circulos actualizados en el mapa drawAndUpdateCircles(); map.on("moveend", drawAndUpdateCircles); });