Unreal Admin
Unreal Admin

Reputation: 51

3D force directed graph replacing nodes with images

Ref: 3d Force Directed Graph - Replacing Nodes with Images

How might I add the images to the following excellent code in the same manner as the Stack Overflow answer above?

https://github.com/jexp/neo4j-3d-force-graph/blob/master/particles.html

Assuming that each node may have a property of n.image=/images/imagexxx.jpg how might I apply this image from a local filesystem to its respective node ? If the property isn't present then render the node as the normal sphere.

Here is my sample code which just renders all nodes as small_image.jpg :

const elem = document.getElementById('3d-graph');
const driver = neo4j.v1.driver("bolt://192.168.1.251", neo4j.v1.auth.basic("neo4j", "test"));
const session = driver.session();
const start = new Date()
session
  .run('MATCH (n)-[r]->(m) RETURN { id: id(n), label:head(labels(n)), community:n.name, caption:n.name, size:log(n.links_from+n.links_to)} as source, { id: id(m), label:head(labels(m)), community:m.name, caption:m.name, size:log(m.links_from+m.links_to)} as target, {weight:r.weight, type:type(r), community:case when n.community < m.community then n.community else m.community end} as rel LIMIT $limit', {limit: 5000})
  .then(function (result) {
    const nodes = {}
    const links = result.records.map(r => {
           var source = r.get('source');source.id = source.id.toNumber();
       nodes[source.id] = source;
           var target = r.get('target');target.id = target.id.toNumber();
       nodes[target.id] = target;
       var rel = r.get('rel'); if (rel.weight) { rel.weight = rel.weight.toNumber(); }
           return Object.assign({source:source.id,target:target.id}, rel);
        });
    session.close();
    console.log(links.length+" links loaded in "+(new Date()-start)+" ms.")
    const gData = { nodes: Object.values(nodes), links: links}

const Graph = ForceGraph3D()(elem)
                  .graphData(gData)
                  .nodeAutoColorBy('community')
                  .nodeVal('size')
                  .linkAutoColorBy('community')
                  .linkWidth(0)
                  .linkDirectionalParticles('weight')
                  .linkDirectionalParticleSpeed(0.001)
                  .nodeLabel(node => `${node.label}: ${node.caption}`)
                  .onNodeHover(node => elem.style.cursor = node ? 'pointer' : null)

    .nodeThreeObject(node => {
      var map = new THREE.TextureLoader().load( "small_image.jpg" );
      map.minFilter = THREE.LinearFilter;
      var material = new THREE.SpriteMaterial( { map: map } );
      var sprite =  new THREE.Sprite( material );
      sprite.scale.set(32,32,1);
      return sprite;
    });
// Spread nodes a little wider
Graph.d3Force('charge').strength(-150);
  })
  .catch(function (error) {
    console.log(error);
  });

Upvotes: 2

Views: 541

Answers (1)

Unreal Admin
Unreal Admin

Reputation: 51

const elem = document.getElementById('3d-graph');
const driver = neo4j.v1.driver("bolt://localhost", neo4j.v1.auth.basic("neo4j", "test"));
const session = driver.session();
const start = new Date()
session
  .run('MATCH (n:Entity)-[r]->(m:Entity) WHERE n.name="new york" RETURN { id: id(n), label:head(labels(n)), community:n.name, caption:n.name, image:n.image, size:log(n.links_from+n.links_to)} as source, { id: id(m), label:head(labels(m)), community:m.name, caption:m.name, image:m.image, size:log(m.links_from+m.links_to)} as target, {weight:r.weight, type:type(r), community:case when n.community < m.community then n.community else m.community end, image:case when n.image < m.image then n.image else m.image end} as rel LIMIT $limit', {limit: 5000})
  .then(function (result) {
    const nodes = {}
    const links = result.records.map(r => {
           var source = r.get('source');source.id = source.id.toNumber();
       nodes[source.id] = source;
           var target = r.get('target');target.id = target.id.toNumber();
       nodes[target.id] = target;
       var rel = r.get('rel'); if (rel.weight) { rel.weight = rel.weight.toNumber(); }
           return Object.assign({source:source.id,target:target.id}, rel);
        });
    session.close();
    console.log(links.length+" links loaded in "+(new Date()-start)+" ms.")
    const gData = { nodes: Object.values(nodes), links: links}


const Graph = ForceGraph3D()(elem)
                  .graphData(gData)
                  .nodeAutoColorBy('community')
                  .nodeVal('size')
                  .linkAutoColorBy('community')
                  .linkWidth(0)
                  .linkDirectionalParticles('weight')
                  .linkDirectionalParticleSpeed(0.001)
                  .nodeLabel(node => `${node.label}: ${node.caption}`)
                  .onNodeHover(node => elem.style.cursor = node ? 'pointer' : null)
    .nodeThreeObject(node => {
            var map = new THREE.TextureLoader().load((node.image != null ? node.image : ""));
      map.minFilter = THREE.LinearFilter;
      var material = new THREE.SpriteMaterial( { map: map } );
      var sprite =  new THREE.Sprite( material );
      sprite.scale.set(32,32,1);

    if (node.image){
            return sprite; }
    else  return false;
    });

// Spread nodes a little wider
Graph.d3Force('charge').strength(-150);
  })
  .catch(function (error) {
    console.log(error);
  });

Upvotes: 1

Related Questions