//Width y height var w = 1340; var h = 620; //Zoom del mapa porque panamá es muy peque en la aproyección var zoomOffset = 105000; var wOffset = 145000; var hOffset = 16200; var escala = 0.50; var radio = 6; //Tipo de proyección del mapa escalado y transladado //posicion del mapa var projection = d3.geoMercator() .translate([w + wOffset, h + hOffset]) .scale([zoomOffset]) ; //Los paths que toman el tipo de proyección var path = d3.geoPath().projection(projection); //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(w / 2).strength(.2)) .force("y", d3.forceY(h / 2).strength(.2)) //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.queue() .defer(d3.json, 'proyectos_v5.json') .defer(d3.json, 'panama.json') .awaitAll(dibujar) ; //Leer los datos y dibujar los assets y el mapa function dibujar (error, data){ if (error) {throw error} //Leer los datos de los json y ponerlos en arreglos distintos var graph = data[0]; var features = data[1].features; //Printea los datos para verificar console.log(graph); console.log(features); //Le dice a la simulación cuales son los nodos y los links //fuerza.nodes(graph.nodes); //fuerza.force("link").links(graph.edges); //svg en donde dibujar var svg = d3.selectAll("body") .append("svg") .attr('width', w) .attr('height', h) ; //grupo en donde esten todos los objetos draggeables var mapa = svg.append("g") //para luego dibujar los circulos y el mapa .attr('id', "mapa") //dibuja el mapa, sin zoom porque no se necesita .selectAll("path") .data(features) .enter() .append("path") .attr("d", path) .style('fill', "#EDEDED") ; //crea las lineas con un svg y los datos de "edges" var lineas = svg.append('g') .selectAll("line") .data(graph.edges) .enter() .append("line") .attr('class', "link") ; //crea los nodos de acuerdo a los nombres var nodos = svg.append('g') .selectAll("circle") .data(graph.nodes) //clase nodos en el CSS .attr("class", "nodos") .enter() .append("circle") //dependiendo del tipo o el area le da una clase del CSS .attr('class', function(d){ if (d.area == "urbano"){ return "nodos " + d.area; } if (d.area == "ambiental"){ return "nodos " + d.area; } if (d.area == "inclusion"){ return "nodos " + d.area; } if (d.tipo == "actor"){ return "nodos " + d.tipo; } if (d.tipo == "colaborador"){ return "nodos " + d.tipo; } if (d.tipo == "financiador"){ return "nodos " + d.tipo; } }) //dependiendo del tipo se le da un borde .style('stroke', function(d){ var borde = ""; if (d.tipo == "proyecto"){ borde = "#000000"; } if (d.tipo == "plan"){ borde = "#585858"; } if (d.tipo == "programa"){ borde = "#C0C0C0"; } return borde; }) .attr('stroke-width', 2) .attr('r',5 ) //si se hace doble click se muestran las conexiones .on("click", connectedNodes) //llama la el metodo de nodos dragg y le dice que hacer en cada momento .call(d3.drag() .on("start", dragInicia) .on("drag", dragging) .on("end", dragTermina)) ; //filtra los nodos por actor para colocarles una opacidad inicial de 0... nodos .filter(function(d){ return d.tipo == "actor" || d.tipo == "colaborador" || d.tipo == "financiador" ; }) .style("opacity", 0.1) ; nodos.append("title") .text(function(d){ return d.id; }); //le dice a la simulacion cuales son los nodos y los links fuerza.nodes(graph.nodes); fuerza.force("link").links(graph.edges); //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) { var p = projection([d.lon,d.lat]); d.fx = p[0]; d.fy = p[1]; } }) //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 = Math.max(radio, Math.min(w - radio, d.x)); }) .attr("cy", function(d) { return d.y = Math.max(radio, Math.min(h - radio, 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.style("opacity", function (o) { return neighboring(d, o) | neighboring(o, d) ? 1 : 0; }); lineas.style("opacity", function (o) { return d.index==o.source.index | d.index==o.target.index ? 1 : 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"; }) .style("opacity", 1) ; nodos .filter(function(d){ return d.tipo == "actor" || d.tipo == "colaborador" || d.tipo == "financiador" ; }) .style("opacity", 0.1) ; // y las lineas a 0 lineas .style("opacity", 0.1) ; toggle = 0; } } //crea las funciones para saber qué hacer en cada momento del dragging // si tiene lon y lat dejelos donde están, los demas si se pueden draggear function dragInicia(d){ if (!d.lon || !d.lat) { if (!d3.event.active) fuerza.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } } function dragging(d){ if (!d.lon || !d.lat) { d.fx = d3.event.x; d.fy = d3.event.y; } } function dragTermina(d){ if (!d.lon ||!d.lat) { if(!d3.event.active) fuerza.alphaTarget(0); d.fx = null; d.fy = null; } } }