2

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...
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
ridermansb
  • 10,779
  • 24
  • 115
  • 226

2 Answers2

1

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>
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • yep.. this was my answer. There are only one difference, I create a `scaleFormat` because I need to fix my range between -10 and 10. – ridermansb Mar 16 '17 at 00:06
  • I believe your answer is different from this... For instance, you never use `scaleFormat` in your code, don't you agree? – Gerardo Furtado Mar 16 '17 at 00:13
  • I never use because the `ticksFormat` should be use by `axis` object. I don`t put the complete code here. That`s the reason that I mark your answer. Are more completed that mine – ridermansb Mar 16 '17 at 02:39
0

I found a way to do that..

const SIZE_MM = 10000
const SIZE_PX = 500
const scaleFormat = d3.scale.linear().domain([0, SIZE_MM]).range([-10, 10])
const ticksFormat = d => Math.round(scaleFormat(d))
const ticks = SIZE_MM / SIZE_PX 

const lineScale = d3.scale.linear().domain([0, SIZE_MM ]).range([0, SIZE_PX])
lineScale(9500)
// 475
ridermansb
  • 10,779
  • 24
  • 115
  • 226