d3.jsd3-force-directed

image as circle background (d3.js svg)


UPDATED I have made a force directed graph using D3.js. Each node corresponds to a company, and each link corresponds how they are related to each other according to the link color. What I would like to achieve is to use the image URLs within "nodes" data and show a different image for each bubble. Currently I was able to set a fixed static/identical image for all of my bubbles. I tried to connect the pattern to my "nodes" data, but unsuccessfully which ended up in an infinite loop.

enter image description here

Simple HTML canvas for my svg and two buttons for the zoom in and zoom out by click.

    <div class="main-map-container" style="overflow: hidden">
        <div class="canvas">
            <div>
                <button id="zoom_in">+</button>
                <button id="zoom_out">-</button>
            </div>
        </div>
    </div> 

My datasets (links and nodes), note the image attribute on nodes. it refers to the image I wish to set as node background


    const links = [
        {src:"Amazon",target:"Aurora",link:"Investment",market:"Semi_Truck",tags:"AD_L3-4|AD_Driverless",technology:"",aces:"Automated",amount:"",region:"EU| US",date:"2019-02-01",description:"Amazon Investing in Aurora in an $530 million Financing Round",source:"https://drive.google.com/open?id=1aLuuUqXKSQcGzivH21CSuO7204pdyP7K",years:"2019"},
        {src:"Amazon",target:"Aurora",link:"Investment",market:"Robotaxi | Semi_Truck",tags:"AD_Driverless",technology:"",aces:"Automated",amount:"$530",region:"EU| US",date:"2019-02-01",description:"Aurora announced that they have secured over $530 million in Series B financing led by Sequoia. This funding and partnership will accelerate their mission of delivering the benefits of self-driving technology safely, quickly, and broadly.\r\n\r\nIn addition to Sequoia, Amazon, and funds and accounts advised by T. Rowe Price Associates, Inc. are making significant investments in Aurora. Amazon’s unique expertise, capabilities, and perspectives will be valuable for us as we drive towards our mission.",source:"https://drive.google.com/open?id=1aLuuUqXKSQcGzivH21CSuO7204pdyP7K",years:"2019"},
        {src:"Amazon",target:"Zoox",link:"Acquisition",market:"Robotaxi",tags:"AD_Driverless",technology:"",aces:"Automated",amount:"$1,200",region:"US",date:"2020-06-01",description:"Amazon acquires Zoox to help bring their vision of autonomous ride-hailing to reality. [The Financial Times reports that Amazon spends $1.2bn on the acquisition.]",source:"https://drive.google.com/open?id=18-zXDSHy9_o0OrIwYFfb5FdyMxMzgtHH",years:"2020"},
        {src:"Amazon",target:"Rivian",link:"Investment",market:"Light_CV",tags:"Other",technology:"",aces:"Automated",amount:"",region:"US",date:"2020-10-01",description:"Amazon unveils its new electric delivery vans built by Rivian\nThe delivery giant aims to have 10,000 vehicles on the road by 2022, 100,000 vehicles expected by 2030.",source:"https://www.theverge.com/2020/10/8/21507495/amazon-electric-delivery-van-rivian-date-specs",years:"2020"},
        {src:"Amazon",target:"Torc",link:"Partnership",market:"Semi_Truck",tags:"AD_L3-4| Edge/Cloud_computing",technology:"",aces:"Automated| Connected",amount:"",region:"US",date:"2021-02-17",description:"Torc selects AWS as preferred cloud provider for self-driving truck fleet",source:"https://drive.google.com/open?id=1QDHZIyUHpAY4oKEqmeM9cngPIWZWV2doE0r1emQZuNY",years:"2021"},
        {src:"Amazon",target:"Intel",link:"Partnership",market:"PoV",tags:"Edge/Cloud_computing",technology:"",aces:"Connected",amount:"",region:"EU| US| China| Korea| Japan| Others",date:"2021-03-01",description:"Intel Achieves AWS High Performance Computing Competency Status",source:"https://drive.google.com/open?id=1MvhLikyzwbu-Mz_OuXUvytAuqXNEnUjHdSxMGXcKLok",years:"2021"},
        {src:"Aurora",target:"Blackmore",link:"Acquisition",market:"Semi_Truck | Robotaxi",tags:"AD_Driverless",technology:"LiDAR",aces:"Automated",amount:"",region:"US",date:"2019-01-09",description:"Yesterday announce that they plan to acquire the industry-leading lidar company Blackmore, located in Bozeman, Montana.\r\n\r\nThe Bozeman, Montana–based outfit, which started up a decade ago to do work for the defense industry, uses a “frequency modulated continuous wave” system, also known as a Doppler lidar. When the infrared light hits an object and bounces back, the system determines both how far away it is and its velocity. Knowing where something is headed and how fast is prized data. Blackmore has at least one Doppler lidar competitor in Aeva, founded in early 2017 by a pair of former Apple engineers. [...]",source:"https://drive.google.com/open?id=1UkF-sxgA1AgpgWoRCd2qgWjovLQULoAP",years:"2019"},
        {src:"Aurora",target:"Uber ATG",link:"Acquisition",market:"Robotaxi | Semi_Truck",tags:"AD_Driverless",technology:"",aces:"Automated",amount:"$4,000",region:"US",date:"2020-12-01",description:"Aurora is acquiring Uber's self-driving unit, ATG, accelerating the first Aurora Driver applications for heavy-duty trucks while allowing us to continue and accelerate our work on light-vehicle products. \"Simply put, Aurora will be the company best positioned to deliver the self-driving products necessary to make transportation and logistics safer, more accessible, and less expensive,\" said Chris Urmson, co-founder and CEO of Aurora.",source:"https://drive.google.com/open?id=1rc-_3VLlITe2bdxBgws3Bvb2rPvCluMfsMkFXiwbg98",years:"2020"},
        {src:"Aurora",target:"Paccar",link:"Partnership",market:"Semi_Truck",tags:"AD_L3-4",technology:"",aces:"Automated",amount:"",region:"US",date:"2021-01-19",description:"Aurora teams up with PACCAR to build and deploy self-driving trucks",source:"https://drive.google.com/open?id=1TfA9U81CbyNwQc4Xfg2iJ9wqmka7RLqVEIsES2-62Ww",years:"2021"},
        {src:"Aurora",target:"Denso",link:"Partnership",market:"Robotaxi",tags:"AD_Driverless",technology:"",aces:"Automated",amount:"",region:"EU| US| China| Korea| Japan| Others",date:"2021-02-09",description:"Aurora enters into long-term, global, and strategic collaboration with Toyota and Denso",source:"https://drive.google.com/open?id=1QnlNnMMxbcPdnbi4AcBo36_UWM9TuMrNqItZHfPDUos",years:"2021"},
        {src:"Aurora",target:"Toyota",link:"Partnership",market:"Robotaxi",tags:"AD_Driverless",technology:"",aces:"Automated",amount:"",region:"EU| US| China| Korea| Japan| Others",date:"2021-02-09",description:"Aurora enters into long-term, global, and strategic collaboration with Toyota and Denso",source:"https://drive.google.com/open?id=1QnlNnMMxbcPdnbi4AcBo36_UWM9TuMrNqItZHfPDUos",years:"2021"},
        {src:"Aurora",target:"OURS-Tech",link:"Acquisition",market:"Semi_Truck| Robotaxi",tags:"",technology:"LiDAR",aces:"Automated",amount:"",region:"US",date:"2021-02-26",description:"Aurora acquires OURS lidar (FMCW), unlocking the commercialization of its Aurora Driver",source:"https://drive.google.com/open?id=1TWEII1c0jQk26DcCC2WXw3plT0XTnfDzmD-e551VWSQ",years:"2021"},
        {src:"Zoox",target:"ZadarLabs",link:"Investment",market:"PoV| Robotaxi",tags:"AD_L3-4| AD_Driverless",technology:"Radar",aces:"Automated",amount:"",region:"US",date:"2021-01-26",description:"Zadar Labs, Developing Next Generation Imaging Radar, Closes $5.6M in Seed Funding",source:"https://drive.google.com/open?id=1fz3MhS4P_Cmq2VmT9crQztUBd9W5Pj-IWEL9ABq3ySg",years:"2021"},
        {src:"Intel",target:"Mobileye",link:"Acquisition",market:"PoV",tags:"AD_L3-4",technology:"",aces:"Automated",amount:"$15,000",region:"Worldwide",date:"2017-01-14",description:"Instead of Mobileye being integrated into Intel, Intel’s AD Group (ADG) will be integrated into Mobileye.",source:"https://www.engadget.com/2017-08-08-intel-acquisition-Mobileye-complete.html",years:"<2017"},
        {src:"Intel",target:"Infineon",link:"Acquisition",market:"",tags:"",technology:"",aces:"Automated|Experience",amount:"1400",region:"US|EU",date:"2010-01-01",description:"Intel to Acquire Infineon’s Wireless Solutions Business",source:"https://www.infineon.com/cms/en/about-infineon/press/press-releases/2010/INFXX201008-069.html",years:"<2017"},
    ]

    const nodes = [
        {id:"Amazon",type:"Tech",tags:"",technology:"",market:"Droid_Goods|Robotaxi",valuation:"",description:"Amazon is an international e-commerce website for consumers, sellers, and content creators.",country:"US",region:"North America",image:"https://images-eu.ssl-images-amazon.com/images/G/02/gc/designs/livepreview/a_generic_10_uk_noto_email_v2016_uk-main._CB485921599_.png"},
        {id:"Aurora",type:"Tier1",tags:"AD_Driverless",technology:"",market:"Robotaxi|Semi_Truck",valuation:"1100",description:"Aurora is delivering the benefits of self-driving technology safely, quickly, and broadly.",country:"US",region:"North America",image:"https://res-3.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/oeagjbu7wau6o16zdhb1"},
        {id:"Zoox",type:"Tier1",tags:"AD_Driverless",technology:"",market:"Robotaxi",valuation:"",description:"Zoox is an AI robotics company that provides mobility as-a-service and self-driving car services.",country:"US",region:"North America",image:"https://res-4.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/kpc7mmk886nbbipbeqau"},
        {id:"Rivian",type:"OEM",tags:"",technology:"",market:"PoV",valuation:"8200",description:"Rivian is an electric vehicle manufacturer that develops products and services to advance the shift to sustainable mobility.",country:"US",region:"North America",image:"https://res-4.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/sdz7aspakybuxp7oojvh"},
        {id:"Torc",type:"Tier1",tags:"AD_Driverless|AD_L3-4",technology:"",market:"Semi_Truck|Mining",valuation:"",description:"Torc provides L4 end-to-end self-driving software for mobility, trucking, mining, and defense markets through strategic partnerships",country:"US",region:"North America",image:"https://res-2.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/smfgyxvfot1dwq4vqvo7"},
        {id:"Intel",type:"Tech",tags:"",technology:"",market:"PoV | Robotaxi",valuation:"",description:"Intel designs, manufactures, and sells integrated digital technology platforms worldwide.",country:"US",region:"North America",image:"https://res-3.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/v1505768454/wrhwa2ogipbd1o6tgggi.png"},
        {id:"Blackmore",type:"Tier2",tags:"",technology:"LiDAR",market:"PoV|Other",valuation:"21.5",description:"Blackmore Sensors and Analytics develops frequency-modulated continuous wave lidar imaging and its supporting analytic tools software.",country:"US",region:"North America",image:"https://res-2.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/v1497015437/tqor2yimyfapcsn5uyaj.png"},
        {id:"Uber ATG",type:"Tier1",tags:"AD_Driverless",technology:"",market:"Robotaxi",valuation:"1000",description:"Uber Advanced Technologies Group focuses on autonomous vehicles and the self-driving car business.",country:"US",region:"North America",image:"https://res-5.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/dlzugstswtlgq9wejmfi"},
        {id:"Paccar",type:"OEM",tags:"",technology:"",market:"Semi_Truck",valuation:"",description:"Global technology leader in the design, manufacture and customer support of high-quality premium trucks.",country:"US",region:"North America",image:"https://res-2.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/v1404996232/nmvcucjqpgpnhrolbnnk.png"},
        {id:"Denso",type:"Tier1",tags:"",technology:"",market:"PoV",valuation:"",description:"Supplier of advanced automotive technology",country:"Japan",region:"Japan",image:"https://res-5.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/v1398234958/fnjwkbbov8sivhs79wta.png"},
        {id:"Toyota",type:"OEM",tags:"",technology:"",market:"PoV",valuation:"",description:"Toyota is a Japanese automotive company that manufactures and markets vehicles to over 170 countries and regions.",country:"Japan",region:"Japan",image:"https://res-3.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/v1425366959/cleoo1wmofcfxxspobul.jpg"},
        {id:"OURS-Tech",type:"Tier2",tags:"",technology:"LiDAR",market:"Semi_Truck| Robotaxi",valuation:"",description:"Enabling mass-production 5D LiDAR for the autonomous future",country:"US",region:"North America",image:"https://static.wixstatic.com/media/c668c3_f1f58317e99d41deac4152d212fcdbc2~mv2.gif"},
        {id:"ZadarLabs",type:"Tier2",tags:"",technology:"Radar",market:"PoV| Robotaxi",valuation:"",description:"Zadar Labs brings radar intelligence to autonomous systems",country:"US",region:"North America",image:"https://res-1.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/g3kgj9vsfjzlpbichdml"},
        {id:"Mobileye",type:"Tech",tags:"AS_NCAP|ADAS_uptoL2+|AD_Driverless",technology:"ECU_ADAS",market:"PoV|Robotaxi|Semi_Truck",valuation:"515",description:"Mobileye is a technology company developing vision-based advanced driver assistance systems that help prevent and mitigate collisions.",country:"Israel",region:"Others",image:"https://res-3.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/bcuhhoflftvvdbpeaarx"},
        {id:"Infineon",type:"Tier2",tags:"Interior_cocoon",technology:"",market:"PoV",valuation:"",description:"Infineon Technologies offers semiconductor solutions, microcontrollers, LED drivers, sensors and Automotive & Power Management ICs.",country:"Germany",region:"EU",image:"https://res-2.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/v1397183702/79bc5b009247cfd1a274f2a328ff17bf.png"},
    
    ]

As for my D3 javascript code here is a section with the most relevant :

    const svg = d3.create("svg")
        .attr("preserveAspectRatio", "xMinYMin meet")
        .attr("viewBox", [-width/2,-height/2, width,height])
        .classed("svg-content-responsive", true)

    const defs = svg.append('svg:defs');
    

    defs.append("svg:pattern")
    .attr("id", "grump_avatar")
    .attr("width", 1)
    .attr("height", 1)
    //.attr("patternUnits", "userSpaceOnUse")
    .append("svg:image")
    .attr("xlink:href", 'http://placekitten.com/g/48/48')
    .attr("width",40)
    .attr("height",40)
    .attr("x",0)
    .attr("y",0)

   
    const link = svg.append("g")
        .attr("stroke-opacity", 0.6)
        .attr("stroke-width",  7)
        .attr("stroke-linecap", "round")
        .selectAll("line")
        .data(links)
        .join("line")
        
    if (l) link.attr("stroke", ({index: i}) => setLinkColor(l[i])); // set conditional link color

    
    const node = svg.append("g")
        .attr("stroke", "teal")
        .attr("stroke-opacity", 1)
        .attr("stroke-width", 2)
        .selectAll("circle")
        .data(nodes)
        .join("circle")
        .style("fill", "#fff")
        .style("fill", "url(#grump_avatar)")
        .attr("r", 20)
        .call(drag(simulation))
        


Solution

  • I've used your code to assemble a small example, which you can see below.

    1. Inside svg > defs, create one pattern per node and use that pattern (with the ID of the company) to fetch the logo of that company;
    2. Reference the pattern for the node using the information you already have.

    Some pointers on your code:

    1. You already use ES6 logic, so you can also use Array.prototype.map and other functions. They're generally much more readable (and natively implemented!) than d3.map;
    2. There is no need to keep so many arrays of values, generally having fewer sources of truth for your data will make the code simpler to maintain and update in the future;
    3. Use clear variable names! LS and LT are logical when you know the context, but when you revisit this code in 6 months you might not instantly know what you were talking about when you wrote it.

    const nodes = [{
        id: "Amazon",
        image: "https://images-eu.ssl-images-amazon.com/images/G/02/gc/designs/livepreview/a_generic_10_uk_noto_email_v2016_uk-main._CB485921599_.png"
      },
      {
        id: "Aurora",
        image: "https://res-3.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/oeagjbu7wau6o16zdhb1"
      },
      {
        id: "Zoox",
        image: "https://res-4.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco/kpc7mmk886nbbipbeqau"
      }
    ];
    
    const links = [{
        src: 'Amazon',
        target: 'Aurora'
      },
      {
        src: 'Amazon',
        target: 'Zoox'
      },
      {
        src: 'Aurora',
        target: 'Zoox'
      }
    ];
    
    const width = 500,
      height = 500;
    
    function ForceGraph({
      nodes, // an iterable of node objects (typically [{id}, …])
      links // an iterable of link objects (typically [{src, target}, …])
    }, {
      nodeId = d => d.id, // given d in nodes, returns a unique identifier (string)
      nodeTitle, // given d in nodes, a title string
      nodeStroke = "teal", // node stroke color
      nodeStrokeWidth = 2, // node stroke width, in pixels
      nodeStrokeOpacity = 1, // node stroke opacity
      nodeRadius = 20, // node radius, in pixels
      nodeStrength = -750,
      linkDistance = 100,
      linkStrokeOpacity = 0.6, // link stroke opacity
      linkStrokeWidth = 7, // given d in links, returns a stroke width in pixels
      linkStrokeLinecap = "round", // link stroke linecap
      linkStrength,
      container
    } = {}) {
    
      // Compute values.
      const N = d3.map(nodes, nodeId);
    
      // HERE: Don't replace the input nodes using this complex method,
      // just Object.assign({}, n) to essentially perform a deep copy of a node.
    
      // Replace the input nodes and links with mutable objects for the simulation.
      nodes = nodes.map(n => Object.assign({}, n));
      links = links.map(l => ({
        source: l.src,
        target: l.target
      }));
    
      const svg = d3.select('body')
        .append("svg")
        .attr("preserveAspectRatio", "xMinYMin meet")
        .attr("viewBox", [-width / 2, -height / 2, width, height])
        .classed("svg-content-responsive", true)
    
      const defs = svg.append('svg:defs');
    
      // HERE: See we add one image per node
      defs.selectAll("pattern")
        .data(nodes)
        .join(
          enter => {
            // For every new <pattern>, set the constants and append an <image> tag
            const patterns = enter
              .append("pattern")
              .attr("width", 48)
              .attr("height", 48);
            patterns
              .append("image")
              .attr("width", 48)
              .attr("height", 48)
              .attr("x", 0)
              .attr("y", 0);
            return patterns;
          }
        )
        // For every <pattern>, set it to point to the correct
        // URL and have the correct (company) ID
        .attr("id", d => d.id)
        .select("image")
        .datum(d => {
          debugger;
          return d;
        })
        .attr("xlink:href", d => {
          debugger;
          return d.image;
        })
    
      const link = svg.append("g")
        .attr("stroke-opacity", linkStrokeOpacity)
        .attr("stroke-width", linkStrokeWidth)
        .attr("stroke-linecap", linkStrokeLinecap)
        .selectAll("line")
        .data(links)
        .join("line");
    
      // Construct the forces.
      const forceNode = d3.forceManyBody();
      const forceLink = d3.forceLink(links).id(({
        index: i
      }) => N[i]);
      if (nodeStrength !== undefined) forceNode.strength(nodeStrength);
      if (linkStrength !== undefined) forceLink.strength(linkStrength);
      forceLink.distance(linkDistance)
      const simulation = d3.forceSimulation(nodes)
        .force(link, forceLink)
        .force("charge", forceNode)
        .force("x", d3.forceX())
        .force("y", d3.forceY())
        .on("tick", ticked);
    
      const node = svg.append("g")
        .attr("stroke", nodeStroke)
        .attr("stroke-opacity", nodeStrokeOpacity)
        .attr("stroke-width", nodeStrokeWidth)
        .selectAll("circle")
        .data(nodes)
        .join("circle")
        .style("fill", "#fff")
        .style("fill", d => `url(#${d.id})`)
        .attr("r", nodeRadius);
    
      function ticked() {
        link
          .attr("x1", d => d.source.x)
          .attr("y1", d => d.source.y)
          .attr("x2", d => d.target.x)
          .attr("y2", d => d.target.y);
        node
          .attr("cx", d => d.x)
          .attr("cy", d => d.y);
      }
    } // ForceGraph
    
    ForceGraph({
      nodes,
      links
    });
    line {
      stroke: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.js"></script>