Displaying photos within nodes

parent 1aac4d1c
/img/photos/*
\ No newline at end of file
......@@ -4,6 +4,24 @@ body {
font-family: Lato, sans-serif;
background-color: #455660;
}
#loader {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
position: absolute;
top: 50%;
left: 50%;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#graph-container {
top: 0;
bottom: 0;
......@@ -11,8 +29,23 @@ body {
right: 0;
position: absolute;
cursor:move;
display:none;
}
#webgl {
top: 0;
left: 0;
width:300px;
height:300px;
color: #fff;
background: #000;
position: absolute;
z-index:100;
display:none;
}
#control-pane {
display:none;
top: 10px;
right: 10px;
position: absolute;
......
This diff is collapsed.
img/capture.jpg

220 KB | W: | H:

img/capture.jpg

86.9 KB | W: | H:

img/capture.jpg
img/capture.jpg
img/capture.jpg
img/capture.jpg
  • 2-up
  • Swipe
  • Onion skin
......@@ -19,14 +19,25 @@
<link rel="stylesheet" href="css/modal.css">
</head>
<body>
<div id="loader">
</div>
<main class="js-document">
<div id="container">
<div id="webgl"></div>
<div id="graph-container"></div>
<div id="control-pane">
<h2 class="underline">Recherche</h2>
<div><select id="identities"><option value="0" selected="selected">Recherche</option></select></div>
<span class="line"></span>
<div>
<button id="reset-btn-cam" class="bloc-center">Recentrer la caméra</button>
</div>
<h2 class="underline"><span id="nb-partitions">0</span> communautés</h2>
<div>
<h3>niveaux</h3>
<select id="levels"></select>
<label for="levels">niveaux</label>
<select id="levels"></select><br /><br />
<input id="displayEdges" type="checkbox">
<label for="displayEdges">Afficher les liens</label>
</div>
<span class="line"></span>
<div>
......@@ -51,7 +62,10 @@
<button id="reset-btn-filters" class="bloc-center">Réinitialiser les filtres</button>
</div>
<h2 class="underline">capture</h2>
<div><button id="export" type="export" class="bloc-center">Exportation SVG</button></div>
<div>
<button id="export" type="export" class="bloc-center">Exportation SVG</button>
<button id="toggle-layout" class="bloc-center">Arrêter l'animation</button>
</div>
</div>
</div>
</main>
......@@ -126,6 +140,8 @@
<script src="js/sigmajs/misc/sigma.misc.bindDOMEvents.js"></script>
<script src="js/sigmajs/misc/sigma.misc.drawHovers.js"></script>
<!-- END SIGMA IMPORTS -->
<!-- Affichage personnalisé des noeuds -->
<script src="js/custom/sigma.renderers.img.js"></script>
<!-- Gestion du force layout -->
<script src="js/sigmajs/plugins/sigma.layout.forceAtlas2/worker.js"></script>
<script src="js/sigmajs/plugins/sigma.layout.forceAtlas2/supervisor.js"></script>
......@@ -139,10 +155,11 @@
<!-- Custom functions -->
<script src="js/custom/sigma.filter.functions.js"></script>
<script src="js/custom/custom.js"></script>
<!-- Lancement du graph -->
<script src="js/main.js"></script>
<!-- Fenêtre modale -->
<script src="js/custom/modal.js"></script>
<!-- Lancement du graph -->
<script src="js/graph.js"></script>
<script src="js/main.js"></script>
<script>
const dialog = document.getElementById('dialog');
window.setTimeout(() => {open(dialog);},100);
......
function colorScale(nbColours){
// Renvoie un tableau de couleurs en fonction du nombre de couleurs désirées
// Create an array of colors
var tab = [];
var red, green, blue;
var tmp = Math.ceil((nbColours-1)/6);
......@@ -42,4 +42,26 @@ function colorScale(nbColours){
};
tab.push("#00ff00");
return tab;
};
\ No newline at end of file
};
function tri(a,b)
{
return (a.name > b.name)?1:-1;
}
function loadJSON(file,callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', file, true);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
// Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
callback(xobj.responseText);
}
};
xobj.send(null);
}
function isInArray(tableau, objet){
return (tableau.indexOf(objet) != -1);
}
\ No newline at end of file
......@@ -54,27 +54,5 @@ var _ = {
}
};
function updatePane (graph, filter) {
// get max degree
var maxDegree = 0,
categories = {};
// read nodes
graph.nodes().forEach(function(n) {
maxDegree = Math.max(maxDegree, graph.degree(n.id));
})
// min degree
_.$('min-degree').max = maxDegree;
_.$('max-degree-value').textContent = maxDegree;
// reset button
_.$('reset-btn-filters').addEventListener("click", resetFilters);
}
function resetFilters(){
_.$('min-degree').value = 0;
_.$('min-degree-val').textContent = '0';
_.$('node-category').selectedIndex = 0;
filter.undo().apply();
}
\ No newline at end of file
sigma.canvas.nodes.image = (function() {
var _cache = {},
_loading = {},
_callbacks = {};
// Return the renderer itself:
var renderer = function(node, context, settings) {
var args = arguments,
prefix = settings('prefix') || '',
size = node[prefix + 'size'],
color = node.color || settings('defaultNodeColor'),
url = node.url;
if (_cache[url]) {
context.save();
// Draw the clipping disc:
context.beginPath();
context.arc(
node[prefix + 'x'],
node[prefix + 'y'],
node[prefix + 'size'],
0,
Math.PI * 2,
true
);
context.closePath();
context.clip();
// Draw the image
context.drawImage(
_cache[url],
node[prefix + 'x'] - size,
node[prefix + 'y'] - size,
2 * size,
2 * size
);
// Quit the "clipping mode":
context.restore();
// Draw the border:
context.beginPath();
context.arc(
node[prefix + 'x'],
node[prefix + 'y'],
node[prefix + 'size'],
0,
Math.PI * 2,
true
);
context.lineWidth = size / 5;
context.strokeStyle = node.color || settings('defaultNodeColor');
context.stroke();
} else {
sigma.canvas.nodes.image.cache(url);
sigma.canvas.nodes.def.apply(
sigma.canvas.nodes,
args
);
}
};
// Let's add a public method to cache images, to make it possible to
// preload images before the initial rendering:
renderer.cache = function(url, callback) {
if (callback)
_callbacks[url] = callback;
if (_loading[url])
return;
var img = new Image();
img.onload = function() {
_loading[url] = false;
_cache[url] = img;
if (_callbacks[url]) {
_callbacks[url].call(this, img);
delete _callbacks[url];
}
};
_loading[url] = true;
img.src = url;
};
return renderer;
})();
\ No newline at end of file
This diff is collapsed.
var filter;
var json_url = 'data/wot.json';
var loaded = 0;
// Add a method to the graph model that returns an
// object with every neighbors of a node inside:
......@@ -13,187 +14,24 @@ for (k in index)
return neighbors;
});
// On instancie le graph à l'aide du parser
sigma.parsers.json(
'data/wot.json',
{
renderer: {
container: 'graph-container'
},
settings: {
minNodeSize: 2,
maxNodeSize: 20,
minEdgeSize: 0.01,
maxEdgeSize: 5,
edgeColor: "default",
defaultEdgeColor: "#14191C",
minArrowSize: 10,
labelThreshold: 11,
labelSize: "proportional",
defaultLabelColor: "#fff",
borderSize: 2,
defaultNodeBorderColor: "#fff",
zoomingRatio: 1.5
}
},
function(s){
// Initialize the Filter API
filter = new sigma.plugins.filter(s);
resetFilters();
updatePane(s.graph, filter);
function applyMinDegreeFilter(e) {
var v = e.target.value;
_.$('min-degree-val').textContent = v;
filter
.undo('min-degree')
.nodesBy(function(n) {
return this.degree(n.id) >= v;
}, 'min-degree')
.apply();
// We store all the img urls in an array to load them before displaying the graph
var tab_urls = [];
loadJSON(json_url, function(response) {
var actual_JSON = JSON.parse(response);
actual_JSON.nodes.forEach(function(n){
if (!isInArray(tab_urls,n.url)){
tab_urls.push(n.url);
}
function applyCategoryFilter(e) {
var c = e.target[e.target.selectedIndex].value;
filter
.undo('node-category')
.nodesBy(function(n) {
return !c.length || n.attributes.referent == c;
}, 'node-category')
.apply();
});
tab_urls.forEach(function(url) {
sigma.canvas.nodes.image.cache(
url,
function() {
if (++loaded === tab_urls.length)
// Instantiate sigma:
displayGraph(json_url);
}
_.$('min-degree').addEventListener("input", applyMinDegreeFilter); // for Chrome and FF
_.$('min-degree').addEventListener("change", applyMinDegreeFilter); // for IE10+, that sucks
_.$('node-category').addEventListener("change", applyCategoryFilter);
// Placement des noeuds en cercle pour l'algo de forces
s.graph.nodes().forEach(function(node, i, a) {
node.x = Math.cos(Math.PI * 2 * i / a.length);
node.y = Math.sin(Math.PI * 2 * i / a.length);
});
s.startForceAtlas2({outboundAttractionDistribution:true});
// Lorsqu'on clique sur un noeud
s.bind('clickNode', function(e) {
if ((typeof test !== 'undefined') && (test.data.node.id==e.data.node.id)){
// On reset l'affichage
resetFilters();
delete test;
s.graph.nodes().forEach(function(n) {
n.hidden = false;
});
s.graph.edges().forEach(function(e) {
e.hidden = false;
});
s.refresh();
}else{
// On isole le noeud et ses voisins
test = e;
resetFilters();
var nodeId = e.data.node.id,
toKeep = s.graph.neighbors(nodeId);
toKeep[nodeId] = e.data.node;
s.graph.nodes().forEach(function(n) {
if (toKeep[n.id])
n.hidden = false;
else
n.hidden = true;
});
s.graph.edges().forEach(function(e) {
if ((e.source==nodeId) || (e.target==nodeId))
e.hidden = false;
else
e.hidden = true;
});
// Since the data has been modified, we need to
// call the refresh method to make the colors
// update effective.
s.refresh();
}
});
// Lorsqu'on survole un noeud
s.bind('overNode', function(e) {
document.getElementById("graph-container").style.cursor = "pointer";
});
s.bind('outNode', function(e) {
document.getElementById("graph-container").style.cursor = "move";
});
// Bouton pour exporter le graph en SVG
document.getElementById('export').onclick = function() {
var output = s.toSVG({download: true, filename: 'wot.svg', size: 1000});
};
var louvainInstance,
colors = ["#00ff00"];
// Detect button
document.getElementById('run-btn').addEventListener("click", function(e) {
// Detect communities using the Louvain algorithm:
louvainInstance = sigma.plugins.louvain(s.graph, {
setter: function(communityId) { this.my_community = communityId; }
});
var nbLevels = louvainInstance.countLevels();
var partitions = louvainInstance.getPartitions();
var nbPartitions = louvainInstance.countPartitions(partitions);
colors = colorScale(nbPartitions);
// Color nodes based on their community
s.graph.nodes().forEach(function(node) {
node.color = colors[node.my_community];
});
s.refresh({skipIndexation: true});
document.getElementById('nb-partitions').textContent = nbPartitions;
// Fill level selector
var levelElt = document.getElementById('levels');
levelElt.innerHTML = '';
for (var i = 0; i < nbLevels; i++) {
var optionElt = document.createElement("option");
optionElt.text = i + 1;
if (i === nbLevels - 1) {
optionElt.selected = true;
}
levelElt.add(optionElt);
}
levelElt.addEventListener("change", function(e) {
var level = +e.target[e.target.selectedIndex].value;
louvainInstance.setResults({level: level});
// Partition count
partitions = louvainInstance.getPartitions(level);
nbPartitions = louvainInstance.countPartitions(partitions);
colors = colorScale(nbPartitions);
document.getElementById('nb-partitions').textContent = nbPartitions;
// Color nodes based on their community
s.graph.nodes().forEach(function(node) {
node.color = colors[node.my_community];
});
s.graph.edges().forEach(function(edge) {
edge.color = colors[edge.my_community];
});
s.refresh({skipIndexation: true});
});
});
document.getElementById('reset-btn').addEventListener("click", function(e) {
// Reset colors
s.graph.nodes().forEach(function(node) {
if (node.attributes.referent){node.color="#008000"}else{node.color="#ff0000"};
});
s.refresh({skipIndexation: true});
document.getElementById('nb-partitions').textContent = 0;
document.getElementById('levels').innerHTML = '';
louvainInstance = null;
});
}
);
\ No newline at end of file
);
});
});
\ No newline at end of file
<?php
$datum = date("d­m­Y H:i");
$stepmax = 5;
// Definit le serveur elasticSearch à interroger
define("URL_ES","https://g1.data.le-sou.org");
// Definit l'emplacement du fichier de base de données
define("PATH","/var/lib/duniter/.config/duniter/duniter_default/duniter.db");
// Definit l'endroit où le fichier de sortie sera enregistré
......@@ -24,31 +26,53 @@ try{
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // ERRMODE_WARNING | ERRMODE_EXCEPTION | ERRMODE_SILENT
} catch(Exception $e) {
echo $datum . " : Impossible d'accéder à la base de données SQLite : ".$e->getMessage();
echo $datum . " : Impossible d'accéder à la base de données SQLite : ".$e->getMessage().PHP_EOL;
die();
}
// Permet d'éviter une erreur avec getimagesize si la photo n'existe pas
function exception_error_handler( $errno, $errstr, $errfile, $errline ) {
throw new Exception($errstr);
}
set_error_handler("exception_error_handler");
$i=0;
while (array_key_exists($i,$tab_tmp)){
//echo $datum . " : " . $i.'. On recupere les voisins de '.$tab_tmp[$i]['label'].chr(13).chr(10);
//echo $datum . " : " . $i.'. On recupere les voisins de '.$tab_tmp[$i]['label'].PHP_EOL;
$img_path_json = 'img/photos/' . $tab_tmp[$i]['pubkey'] . '.png';
$img_path = dirname( __FILE__ ) . '/../' . $img_path_json;
if (!file_exists($img_path)){
$url_image = URL_ES . '/user/profile/' . $tab_tmp[$i]['pubkey'] . '/_image/avatar.png';
try {
$imageinfo = getimagesize($url_image);
} catch (Exception $e) {
$imageinfo = false;
}
if (!$imageinfo) {
$img_path_json='img/logo-g1.png';
}else{
copy($url_image, $img_path);
}
}
$objmember = ['id'=>strval($i+1),
'label'=>$tab_tmp[$i]['label'],
'x'=>0.0,
'y'=>0.0,
'size'=>0,
'label' => $tab_tmp[$i]['label'],
'type' => 'image',
'url' => $img_path_json,
'x' => 0.0,
'y' => 0.0,
'size' => 0,
'color' => 'rgb(255,0,0)',
'attributes'=>['pubkey'=>$tab_tmp[$i]['pubkey'],
'degree'=>0,
'referent'=>false]
'attributes' => ['pubkey' => $tab_tmp[$i]['pubkey'],
'degree' => 0,
'referent' => false]
];
$voisins = recuperer_voisins($objmember);
$objmember['size'] = count($voisins);
if ($objmember['size']>=9){
$objmember['attributes']['referent']=true;
$objmember['color']='rgb(0,128,0)';
}
$objmember['attributes']['degree'] = $objmember['size'];
$tab_final['nodes'][] = $objmember;
$i++;
......@@ -63,10 +87,11 @@ $crit_referent = ceil(pow($nb_nodes,1/$stepmax));
foreach ($tab_final['nodes'] as $key => $value) {
$isreferent = ($tab_final['nodes'][$key]['attributes']['inDegree']>=$crit_referent) && ($tab_final['nodes'][$key]['attributes']['outDegree']>=$crit_referent);
$tab_final['nodes'][$key]['attributes']['referent'] = $isreferent;
if ($isreferent){$tab_final['nodes'][$key]['color']='rgb(0,128,0)';}
}
// Ecriture du résultat dans un fichier
$json = json_encode($tab_final, JSON_PRETTY_PRINT);
$json = json_encode($tab_final, JSON_UNESCAPED_SLASHES);
try{
$file = fopen(OUTPATH.'wot.json', 'w+');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment