Home > Software design >  Replacing colored circles with image circles using javascript d3 visualization
Replacing colored circles with image circles using javascript d3 visualization

Time:12-06

I am trying to replace the colored circles with image circles. The following plot is made using d3 in javascript. I cannot add image due to my less than 10 rating.

enter image description here

// https://observablehq.com/@d3/smooth-zooming@58


function _chart(width,height,d3,data,radius)
{
  let currentTransform = [width / 2, height / 2, height];

  const svg = d3.create("svg")
      .attr("viewBox", [0, 0, width, height])

  const g = svg.append("g");

  g.selectAll("circle")
    .data(data)
    .join("circle")
      .attr("cx", ([x]) => x)
      .attr("cy", ([, y]) => y)
      .attr("r", radius)
      .attr("fill", (d, i) => d3.interpolateRainbow(i/360))

  function transition() {
    const d = data[Math.floor(Math.random() * data.length)];
    const i = d3.interpolateZoom(currentTransform, [...d, radius * 2   1]);

    g.transition()
        .delay(250)
        .duration(i.duration)
        .attrTween("transform", () => t => transform(currentTransform = i(t)))
        .on("end", transition);
  }

  function transform([x, y, r]) {
    return `
      translate(${width / 2}, ${height / 2})
      scale(${height / r})
      translate(${-x}, ${-y})
    `;
  }

  return svg.call(transition).node();
}


function _height(){return(
500
)}

function _radius(){return(
  2
)}

function _step(radius){return(
radius * 2
)}

function _data(step,theta,width,height){return(
Array.from({length: 2000}, (_, i) => {
  const r = step * Math.sqrt(i  = 0.5), a = theta * i;
  return [
    width / 2   r * Math.cos(a),
    height / 2   r * Math.sin(a)
  ];
})
)}

function _theta(){return(
Math.PI * (3 - Math.sqrt(5))
)}

function _d3(require){return(
require("d3@6")
)}

export default function define(runtime, observer) {
  const main = runtime.module();
  
  main.variable(observer("chart")).define("chart", ["width","height","d3","data","radius"], _chart);
  main.variable().define("height", _height);
  main.variable().define("radius", _radius);
  main.variable().define("step", ["radius"], _step);
  main.variable().define("data", ["step","theta","width","height"], _data);
  main.variable().define("theta", _theta);
  main.variable().define("d3", ["require"], _d3);
  return main;
}

I think that we might need to alter the data function. I was not able to show it with images as I am a beginner in Javascript.

CodePudding user response:

First off, your immediate question is:

I am trying to replace the colored circles with image circles.

This has been answered a number of times on StackOverflow, for example here.

Doing so in the context of d3 is a bit different, though, since you'll probably want some code along the lines of:

let url = ...
let defs = svg.append("defs");
defs
  .append("pattern")
  ...
  .attr("id", 'image')
  .append("image")
  ...
  .attr("href", url);

There are more details on this below.

Also, the code block that you've posted looks to me like it was returned by the Observalbe embedding API, i.e.: https://api.observablehq.com/@d3/smooth-zooming.js?v=3

I've forked that notebook and made the changes necessary to include a few images. Here's the fork and here's the proper way to embed that code:

<div id="observablehq-chart-25b97cbc"></div>
<script type="module">
import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@5/dist/runtime.js";
import define from "https://api.observablehq.com/d/adfba0cfb4396df0.js?v=3";
new Runtime().module(define, name => {
  if (name === "chart") return new Inspector(document.querySelector("#observablehq-chart-25b97cbc"));
});
</script>

If you examine the forked notebook, you should notice a code block near the top that looks something like so:

image_urls = [
  "https://upload.wikimedia.org/wikipedia/commons/3/3d/Perspectiva_Corporum_Regularium_36a.jpg",
  "https://upload.wikimedia.org/wikipedia/commons/7/7f/Hexaeder_NdFeB_5041.jpg",
  "https://upload.wikimedia.org/wikipedia/commons/f/f6/Hexader_1.jpg"
]

I guess that could be just about any list of publicly accessible URLs pointing to images.

The next code block, that defines the chart, contains a few lines that look like:

let defs = svg.append("defs");
image_urls.forEach(function (url, i) {
  defs
    .append("pattern")
    .attr("id", `image${i}`)
    .attr("x", "0%")
    .attr("y", "0%")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("viewBox", "50 50 200 100")
    .append("image")
    .attr("x", "0%")
    .attr("y", "0%")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("href", url);
});

That sets up the patterns that you can use as fills. Finally, that bit is used later when the circles are joined:

g.selectAll("circle")
  .data(data)
  .join("circle")
  ...
  .attr("fill", (_, i) => `url(#image${i % image_urls.length}`);
  • Related