Search code examples
javascriptd3.jsscalereact-d3

Scale in different units


How to use D3 to convert and display the right information from different units

E.g.

enter image description here

All data is in mm..

[ 
    { label: 'sample1', x: 300 }, 
    { label: 'sample2', x: 1200 }, 
    { label: 'sample3', x: 4150 } 
]

So, the question is, how can I create a scale that understand the sample3 should be point in same place after the 4 and before 5.

Consider

  1. 10000, its just a sample, can be 102301 or any value
  2. I want to use D3 scale if possible to do this conversion

Attempt

let scaleX = d3.scale.linear().domain([-10, 10]).range([0, 500]) // Missing the mm information...

Solution

  • You have a conceptual problem here:

    • Mapping an input (domain) to an output (range): that's the task of the scale.
    • Formatting the number and the unit (if any) in the axis: that's the task of the axis generator

    Thus, in your scale, you'll have to set the domain to accept the raw, actual data (that is, the data the way it is) you have:

    var scale = d3.scaleLinear()
        .domain([-10000, 10000])//the extent of your actual data
        .range([min, max]);
    

    Then, in the axis generator, you change the value in the display. Here, I'm simply dividing it by 1000 and adding "mm":

    var axis = d3.axisBottom(scale)
        .tickFormat(d => d / 1000 + "mm");
    

    Note that I'm using D3 v4 in these snippets.

    Here is a demo using these values: -7500, 500 and 4250. You can see that the circles are in the adequate position, but the axis shows the values in mm.

    var data = [-7500, 500, 4250];
    
    var svg = d3.select("body")
      .append("svg")
      .attr("width", 500)
      .attr("height", 200);
    
    var scale = d3.scaleLinear()
      .domain([-10000, 10000])
      .range([20, 480]);
    
    var axis = d3.axisBottom(scale)
      .tickFormat(d => d / 1000 + "mm");
    
    var circles = svg.selectAll("foo")
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 4)
      .attr("fill", "teal")
      .attr("cy", 40)
      .attr("cx", d => scale(d));
    
    var g = svg.append("g")
      .attr("transform", "translate(0,60)")
      .call(axis);
    <script src="https://d3js.org/d3.v4.js"></script>