diff -r efd9c589177a -r c0b4a8b5a012 toolkit/javascript/nco/cartographie.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolkit/javascript/nco/cartographie.js Thu Apr 10 14:20:23 2014 +0200 @@ -0,0 +1,1090 @@ +function Carte(parametres, articles, links) { + + var object = this; + + this.parametres = { + + isPositions: false, + + width: 1440, + height: 800, + top: 50, + left: -240, + boxID: "box", + selectorID: "carto", + canvasID: "canvas", + info: "infos", + + couleurs: null, + relations: null, + + charge: -30, + gravity: .13, + theta: 3, + + nodeSizeInterval: [3, 15], + nodeGrowth: "linear", //value "linear", "log", or "exp" + layerStyle: { + opacity: 0.6 + }, + handle_len_rate: 2.2, + maxDistance: 35, + + scaleInterval: [0.5, 10], + activerZoom: true, + + infobulleHtml: function(node) { + return node.titre + "

cluster: " + node.group +"

"; + }, + + infoDisplay: "hover", //valeur "hover" ou "click" + occlusionMgmt: false, + nodeHighlight: "darken", //valeur "darken" ou "grow" + + zoomNode: true, + logZoomNode: true, + logZoomNodeParameter: 2, + selection: true, + clustering: false + }; + + this.beginDate = new Date(); + this.etendre(parametres); + + //cr√©ation du contenant sur la page, si inexistant + + + + if (null == (document.getElementById(this.parametres.boxID))) { + d3.select('.content').append('div') + .attr("id", this.parametres.boxID) + .classed("gallery", true) + .style("position", "absolute") + .style("border", "1 px dashed"); + } + d3.select('#' + this.parametres.boxID) + .style("width", this.parametres.width) + .style("height", this.parametres.height) + .style("position", "absolute") + .style("top", this.parametres.top) + .style("left", this.parametres.left) + .style("overflow", "hidden") + .style('z-index', 0); + + + if (null == (document.getElementById(this.parametres.selectorID))) { + d3.select('.gallery').append('div') + .attr("id", this.parametres.selectorID); + } + d3.select('#' + this.parametres.selectorID) + .attr("width", this.parametres.width) + .attr("height", this.parametres.height) + .style("position", "absolute") + .style('z-index', 2); + + this.drag = d3.behavior.drag() + .on("dragstart", function(d, i) {object.dragstart(d, i, object);}) + .on("drag", object.dragmove) + .on("dragend", function(d, i) {object.dragend(d, i, object);}); + + this.static = false; + + + this.nodeMouseover = function(d) { + + if (object.parametres.nodeHighlight == "darken") { + object.colorBrighten(d3.select(this)); + } + else if (object.parametres.nodeHighlight == "grow") { + scale = object.getScale(object.rect); + object.grow(this, scale); + } + if (object.parametres.infoDisplay == "hover") { + object.highlight(d); + } + }; + + this.nodeMouseout = function(d) { + + if (object.parametres.nodeHighlight == "darken") { + object.colorDarken(d3.select(this)); + } + else if (object.parametres.nodeHighlight == "grow") { + scale = object.getScale(object.rect); + object.shrink(object.radius(d.scaledPoids), this); + } + if (object.parametres.infoDisplay == "hover") { + object.blur(); + } + + }; + + this.nodeClick = function(d) { + if (object.parametres.infoDisplay == "click") { + object.highlight(d); + } + if (object.parametres.occlusionMgmt) { + object.replaceAll(); + object.noOcclusions(d, d3.select(this)); + } + object.displayInfos(d, d3.select(this)); + } + + if (this.parametres.infoDisplay == "click") { + + this.infoClick = function(d) { + object.blur(); + }; + } + + //cr√©ation de l'infobulle, si inexistante + if (null == (this.infoBulle = d3.select('#' + this.parametres.info)[0][0])) { + this.infoBulle = d3.select('#' + this.parametres.selectorID).append('div') + .attr("id", this.parametres.info) + .style("top", this.parametres.height) + .style("opacity", 0) + .style("z-index", 1); + } + this.infoBulle.on("click", this.infoClick); + + //cr√©ation du canvas sur la page, si inexistant + if (null == (this.canvas = document.getElementById(this.parametres.canvasID))) { + d3.select('.gallery').insert('canvas', '#' + this.parametres.selectorID) + .attr("id", this.parametres.canvasID) + .attr("resize", false); + this.canvas = document.getElementById(this.parametres.canvasID); + } + d3.select('#' + this.parametres.canvasID) + .attr("width", d3.select('#' + this.parametres.selectorID).attr("width")) + .attr("height", d3.select('#' + this.parametres.selectorID).attr("height")) + .style("position", "absolute") + .style("z-index", 1); + + //initialisation du drag'n'drop + object.mouse = {}; + object.isDown = false; + object.drag2 = false; + this.svg = d3.select('#' + this.parametres.selectorID).append('svg') + .attr("width", this.parametres.width) + .attr("height", this.parametres.height) + .on("mousedown", function() {object.mouseDown(event);}) + .on("mousemove", function() {object.mouseMove(event);}) + .on("mouseup", function() {object.mouseUp(event);}); + + paper.setup(this.canvas); + paper.view.viewSize = new paper.Size(object.parametres.width, object.parametres.height); + + this.clusters = articles.clusters; + + //g√©n√©ration d'une palette de couleurs + if (null == this.parametres.couleurs) { + this.parametres.couleurs = this.selectColors(this.clusters.length); + } + + //initialisation du panneau d'informations latéral + this.lateral = d3.select("#infosLaterales"); + this.lateral.select("button.close").on("click", function() {object.hideLateral();}); + + //initialisation du panneau de sélection de clusters + if (this.parametres.selection) { + this.choice = d3.select("#choixLateral"); + this.choice.left = this.parametres.width + 50; + d3.select("h1 button.select").on("click", function() {object.displayChoice();}); + this.fillChoices(); + this.choice.select("button.spread").on("click", function() {object.spread();}); + } + else { + d3.select("h1 button.select").style("opacity", 0); + } + + console.log("Environnement OK."); + + this.data = {}; + this.zones = []; + + //affichage des points pré-calculés + if (this.parametres.isPositions) { + this.data.nodes = this.generateNodes(articles, this.parametres.isPositions); + this.length = articles.nbItems; + this.display(true); + + } + + //initialisation du Force Directed Graph + else { + this.data.nodes = this.generateNodes(articles, this.parametres.isPositions); + this.data.links = this.generateLinks(links, this.data.nodes); + + this.force = d3.layout.force() + .charge(this.parametres.charge) //-20 + .nodes(this.data.nodes) + .links(this.data.links) + .linkDistance(function(link) {return link.linkDistance;}) //20 + .linkStrength(function(link) {return link.linkStrength;}) + .size([this.parametres.width, this.parametres.height]) + .gravity(this.parametres.gravity) //.1 + .theta(this.parametres.theta) + .start(); + + console.log("Graphe en cours de stabilisation . . ."); + + this.force.on("tick", function() { + console.log("Stabilisation OK."); + object.display(true); + }); + + } + + +}; + + + +Carte.prototype = { + + //gestion des param√®tres + etendre: function(parametres) { + if (null==parametres || "object"!=typeof(parametres)) { + return; + } + + for (var cle in parametres) { + this.parametres[cle] = parametres[cle]; + } + }, + + display: function (init) { + + if (this.parametres.clustering) { + this.clusterData = this.clusterize(this.data.nodes, this.clusters.length); + this.showNodes(this.clusterData); + } + else { + this.showNodes(this.data.nodes); + } + + if (this.parametres.activerZoom && init) { + this.activateZoom(); + } + paper.setup(this.canvas); + paper.view.viewSize = new paper.Size(this.parametres.width, this.parametres.height); + + this.createBackground(); + + }, + + clusterize: function(data, nb) { + clusterd = []; + for (var i = 0 ; ipoidsMax) { + poidsMax = poids; + } + else if (poids 0) && (circle.position.x < this.parametres.width + maxDistance) && (circle.position.y + maxDistance > 0) && (circle.position.y < this.parametres.height + maxDistance)) { + layer.appendTop(circle); + for (var j = 0; j maxDistance || d <= Math.abs(radius1 - radius2)) { + return; + } else if (d < radius1 + radius2) { // case circles are overlapping + return null; + } else { + u1 = 0; + u2 = 0; + } + + var center3 = center2.clone(); + center3.x = center2.x - center1.x; + center3.y = center2.y - center1.y; + + var angle1 = center3.getAngleInRadians(); + var angle2 = Math.acos((radius1 - radius2) / d); + var angle1a = angle1 + u1 + (angle2 - u1) * v; + var angle1b = angle1 - u1 - (angle2 - u1) * v; + var angle2a = angle1 + Math.PI - u2 - (Math.PI - u2 - angle2) * v; + var angle2b = angle1 - Math.PI + u2 + (Math.PI - u2 - angle2) * v; + var p1a = center1.add(this.getVector(angle1a, radius1)); + var p1b = center1.add(this.getVector(angle1b, radius1)); + var p2a = center2.add(this.getVector(angle2a, radius2)); + var p2b = center2.add(this.getVector(angle2b, radius2)); + + // define handle length by the distance between + // both ends of the curve to draw + var totalRadius = (radius1 + radius2); + var p3a = p2a.clone(); + p3a.x = p1a.x - p2a.x; + p3a.y = p1a.y - p2a.y; + var d2 = Math.min(v * handle_len_rate, p3a.length / totalRadius); + + // case circles are overlapping: + d2 *= Math.min(1, d * 2 / (radius1 + radius2)); + + radius1 *= d2; + radius2 *= d2; + + var path = new paper.Path([p1a, p2a, p2b, p1b]); + path.style = ball1.style; + path.closed = true; + var segments = path.segments; + segments[0].handleOut = this.getVector(angle1a - pi2, radius1); + segments[1].handleIn = this.getVector(angle2a + pi2, radius2); + segments[2].handleOut = this.getVector(angle2b - pi2, radius2); + segments[3].handleIn = this.getVector(angle1b + pi2, radius1); + return path; + }, + + // ------------------------------------------------ + getVector: function(radians, length) { + var angle = radians * 180 / Math.PI; + return new paper.Point({"angle": angle, + "length": length + }); + }, + + //apparition de l'infoBulle + highlight: function(node) { + scale = this.parametres.activerZoom ? this.scale(this.rect[0][0].getAttribute("x")) : 1; + this.infoBulle.html(this.parametres.infobulleHtml(node)); + if (this.infoBulle.classed("selected")) { + this.infoBulle.transition() + .duration(500) + .style("left", node.x + 10 + "px") + .style("top", node.y + 10 + "px"); + } + else { + this.infoBulle.classed("selected", true) + .style("left", node.x +10 + "px") + .style("top", node.y +10 + "px") + .transition() + .duration(300) + .style("opacity", 0.5); + } + }, + + //disparition de l'infoBulle + blur: function() { + this.infoBulle.classed("selected", false) + .transition() + .duration(400) + .style("opacity", 0); + this.infoBulle.style("top", this.parametres.height); + }, + + //zoom sur un point + grow: function(node, scale) { + if (!this.static) { + d3.select(node) + .attr("z-index", 1) + .transition() + .duration(200) + .attr("r", this.parametres.nodeSizeInterval[1]*scale); + } + }, + + //d√©zoom sur un point + shrink: function(radius, node) { + if (!this.static) { + d3.select(node) + .transition() + .duration(70) + .attr("z-index", 0) + .attr("r", radius); + } + + }, + + //r√©affichage du fond sur recadrage du navigateur + onResize: function(event) { + paper.view.draw(); + d3.select('#' + this.parametres.canvasID) + .attr("width", d3.select('#' + this.parametres.selectorID).attr("width")) + .attr("height", d3.select('#' + this.parametres.selectorID).attr("height")); + }, + + dragstart: function(d,i, object) { + object.rect.xi = object.scale(d3.event.x); + object.static = true; + }, + + dragmove: function(d, i) { + d.x += d3.event.dx; + d3.select(this).attr("x", d.x); + }, + + dragend: function(d, i, object) { + + ratio = object.scale(d3.event.x)/object.rect.xi; + object.infoBulle.classed("selected", false) + .style("opacity", 0); + + object.zoom(ratio); + object.resetCanvas(); + + object.zoomTransition(ratio); + object.static = false; + var timer = new Date().getTime() - object.beginDate.getTime(); + console.log(timer); + }, + + resetCanvas: function() { + paper.view.remove(); + ctx = this.canvas.getContext('2d'); + ctx.fillStyle = 'rgb(0,0,0)'; + ctx.fillRect(this.canvas.left, this.canvas.top, this.canvas.width, this.canvas.height); + paper.setup(this.canvas); + paper.view.viewSize = new paper.Size(this.parametres.width, this.parametres.height); + }, + + scale: function(x) { + return (this.parametres.scaleInterval[0] + (this.parametres.scaleInterval[1] - this.parametres.scaleInterval[0])*(x/this.parametres.width)); + }, + + getScale: function(rect) { + if (rect == undefined || !this.parametres.activerZoom) { + return 1; + } + else { + if (this.parametres.zoomNode) { + if (this.parametres.logZoomNode) { + return Math.log(1 + this.scale(rect[0][0].getAttribute("x"))*this.parametres.logZoomNodeParameter)/(Math.log(1 + this.parametres.logZoomNodeParameter)); + } + else { + return this.scale(rect[0][0].getAttribute("x")); + } + } + else { + return 1; + } + } + }, + + +zoom: function(ratio) { + + object = this; + + scale = this.getScale(this.rect); + + this.svg.selectAll("circle.node") + .each(function(d) { + var distx = object.parametres.width/2- d.x; + distx *= ratio; + d.x = object.parametres.width/2 - distx; + + var disty = object.parametres.height/2 - d.y; + disty *= ratio; + d.y = object.parametres.height/2 - disty; + d.scaledPoids = d.poids*scale; + + }); + }, + + zoomTransition: function() { + var object = this; + var c = 0; + this.svg.selectAll("circle.node") + .transition() + .duration(Math.max(400*ratio, 400/ratio)) + + .attr("cx", function(d) { + return d.x; + }) + .attr("cy", function(d) { + return d.y; + }) + .attr("r", function(d) { return object.radius(d.scaledPoids);}) + .each("end", function() { + c++; + if (c == object.length) { + object.static = false; + object.createBackground(); + } + }); + }, + + mouseDown: function(e) { + this.isDown = true; + this.mouse.x = e.x; + this.mouse.y = e.y; + this.center = {}; + /*this.center.x = paper.view.center.x; + this.center.y = paper.view.center.y;*/ + + var object = this; + setTimeout(function() { + if (object.isDown && !object.drag2) { + object.blur(); + //d3.select(object.canvas).style("visibility", "hidden"); + object.resetCanvas(); + + object.static = true; + object.drag2 = true; + } + }, 300); + + }, + + mouseMove: function(e) { + if (this.drag2) { + diff = {}; + diff.x = e.x - this.mouse.x; + diff.y = e.y - this.mouse.y; + /*this.center.x -= diff.x; + this.center.y -= diff.y;*/ + this.svg.selectAll('circle.node') + .attr("cx", function(d) { + d.x += diff.x; + return d.x; + }) + .attr("cy", function(d) { + d.y += diff.y; + return d.y; + }); + this.mouse.x = e.x; this.mouse.y = e.y; + } + }, + + mouseUp: function(e) { + if (this.drag2) { + //paper.view.center = new paper.Point(this.center.x, this.center.y); + //d3.select(object.canvas).style("visibility", "visible"); + this.createBackground(); + this.static = false; + this.drag2 = false; + } + this.isDown = false; + this.mouse = {}; + }, + + colorBrighten: function(node) { + + //node.style("opacity", 0.5); + node.style("stroke", "#000") + .style("stroke-width", 1); + }, + + colorDarken: function(node) { + + //node.style("opacity", 1); + node.style("stroke", "#fff") + .style("stroke-width", 0.75); + }, + + noOcclusions: function(data, node) { + + var allNodes = this.svg.selectAll("circle.node"); + var x1 = data.x, y1 = data.y, r1 = parseFloat(node.attr("r")); +// var x1 = parseFloat(node.attr("cx")), y1 = parseFloat(node.attr("cy")), r1 = parseFloat(node.attr("r")); + + allNodes.each(function(d) { + var x2 = d.x, y2 = d.y, r2 = parseFloat(d3.select(this).attr("r")); + var dist = Math.sqrt(Math.pow((x2 - x1),2) + Math.pow((y2 - y1),2)); + + if (dist <= r1 + r2 && dist != 0) { + x2 = x2 + (x2-x1)*(r1+r2+1-dist)/dist; + y2 = y2 + (y2-y1)*(r1+r2+1-dist)/dist; + d3.select(this).transition() + .delay(50) + .attr("cx", x2) + .attr("cy", y2); + } + }); + + + for (var i = 0; i < allNodes[0].length; i++) { + + otherNode = d3.select(allNodes[0][i]); + var x2 = parseFloat(otherNode.attr("cx")), y2 = parseFloat(otherNode.attr("cy")), r2 = parseFloat(otherNode.attr("r")); + var dist = Math.sqrt(Math.pow((x2 - x1),2) + Math.pow((y2 - y1),2)); + + if (dist <= r1 + r2 && dist != 0) { + x2 = x2 + (x2-x1)*(r1+r2+1-dist)/dist; + y2 = y2 + (y2-y1)*(r1+r2+1-dist)/dist; + otherNode.transition() + .delay(50) + .attr("cx", x2) + .attr("cy", y2); + } + } + }, + + replaceAll: function() { + this.svg.selectAll("circle.node") + .transition() + .delay(50) + .attr("cx", function(d) {return d.x;}) + .attr("cy", function(d) {return d.y;}); + }, + + displayInfos: function(node, trueNode) { + // Calcul du temps de l'image + if (node.type == "cluster") { + this.lateral.node = trueNode; + this.lateral.cluster = this.clusters[node.group]; + var clusterInfos = "Ce cluster correspond au thème " + this.lateral.cluster.id + " et contient " + this.lateral.cluster.items.length + " éléments." + object = this; + this.lateral.select("p.infosCluster").html(clusterInfos); + this.lateral.select("span.descriptif").html("Plus d'infos sur un élément-clé du cluster:"); + var maxWeight = 0, rep = 0; + for (i = 0; i < this.lateral.cluster.items.length; i++) { + if (this.lateral.cluster.items[i].value > maxWeight) { + maxWeight = this.lateral.cluster.items[i].value; + rep = i; + } + } + key = this.lateral.cluster.items[rep].key; + } + else { + this.lateral.select("span.descriptif").html("Plus d'infos sur l'élément:"); + this.lateral.select("p.infosCluster").html(""); + key = node.key; + } + var infos = /I_am_a_legend_([0-9]+)\.jpg/.exec(images[key]); + var temps = parseInt(infos[1]); + + var heures = parseInt(temps/90000); + var temps = temps%90000; + var minutes = parseInt(temps/1500); + temps = temps%1500; + var secondes = parseInt(temps/25); + this.lateral.select("span.tempsScene").html(heures + ":" + minutes + ":" + secondes); + + this.lateral.select("img.image").attr("src", '../images/' + images[key]); + + this.lateral.select("span.groupeId").html(node.group); + + // trouver ses potes de groupes + var liste = "", first = true, cpt = 0; + if (node.group!=carto.clusters.length) { + for (var i=0; i -1) { + var selectedCluster = selection.children[selection.selectedIndex]; + clusters.push(selectedCluster.id); + selectedCluster.selected = false; + } + + var dataset = this.clusterDatas(clusters); + + this.showNodes(dataset); + this.createBackground(); + + object = this; + d3.select("h1 button.select").text("Revenir à la vue globale") + .on("click", function() {object.globalize();}); + } + }, + + globalize: function() { + this.svg.selectAll("circle.node").remove(); + paper.view.remove(); + this.display(false); + d3.select("h1 button.select").text("Faire une selection") + .on("click", function() {object.displayChoice();}); + } + + +}; \ No newline at end of file