0

I'm trying to have the chart tickets in a D3 bullet chart follow the data itself, as per the 2nd example here: Bullet chart ticks & labels in D3.js

The issue is that the source of this (http://boothead.github.io/d3/ex/bullet.html) no longer exists on the internet, the only thing out there is the gif in this post that I've linked.

enter image description here Does anyone have the original copy of this project or have any advice?

I'm using the first example by mbostock and trying to replicate the bottom one.

Many thanks

Ewan
  • 5
  • 2

1 Answers1

0

In the original bullet.js from the bostock example https://bl.ocks.org/mbostock/4061961

Instead of getting the ticks from the scale you get the values from the range, measure and mark

change around line 109

  // var tickVals = x1.ticks(8);
  var tickVals = rangez.concat(measurez).concat(markerz);
  // Update the tick groups.
  var tick = g.selectAll("g.tick")
      .data(tickVals, function(d) {
        return this.textContent || format(d);
      });


Edit

There is a problem if you update the data based on a new fetch from the server. some of the ticks end up on the wrong location. If the number of markers,measures,ranges also change they also end up at the wrong location.

It depends on the selection you supply to the bullet call.

The confusion is caused by the poor naming of the main program.

  var svg = d3.select("body").selectAll("svg")
      .data(data)
    .enter().append("svg")
      .attr("class", "bullet")
      .attr("width", svgWidth)
      .attr("height", svgHeight)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
      .call(chart);

The name suggests that svg is a selection of svg elements. This is incorrect, it is a selection of g elements.

The update() function should reflect this

function updateData() {
    d3.json("mailboxes.json", function (error, data) {
        d3.select("body")
          .selectAll("svg")
          .select('g')
          .data(data)
          .call(chart.duration(500));
    });
}

If the number of bullet graphs changes on the update there is the problem that they are not created or deleted if needed. So we need to make a function that can be used for initial and update calls.

function drawCharts() {
    d3.json("mailboxes.json", function (error, data) {
        var svg = d3.select("body").selectAll("svg").data(data);
        svg.exit().remove();
        svg.enter().append("svg")
            .attr("class", "bullet")
            .attr("width", svgWidth)
            .attr("height", svgHeight)
          .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        d3.select("body")
          .selectAll("svg")
          .select('g')
          .data(data)
          .call(chart.duration(500));
    });
}

A better change in bullet.js [109] would be:

  // var tickVals = x1.ticks(8);
  var tickVals = [];
  rangez.concat(measurez).concat(markerz).forEach(e => {
      if (tickVals.indexOf(e) == -1) tickVals.push(e);
  });
  // Update the tick groups.
  var tick = g.selectAll("g.tick").data(tickVals);

That is do not use the value of the tick to match, in case we have multiple values in the ticks, and remove the duplicates.

We also need to change the update of the ticks, about 30 lines down

  tickUpdate.select("text")
      .attr("y", height * 7 / 6);

to

  tickUpdate.select("text")
      .text(format)
      .attr("y", height * 7 / 6);
rioV8
  • 24,506
  • 3
  • 32
  • 49
  • Legend, this is perfect! It works exactly as expected, however I have a new problem with the axes relocating to x=0 when they change. Thank you for your help man! – Ewan Oct 11 '18 at 13:30
  • @Ewan I don't get your axes problem, there is no axes in the bullet chart – rioV8 Oct 11 '18 at 13:33
  • I guess I mean the ticks, they sometimes skew off when the data refreshes (I have it on an interval timer from a JSON file created by python - working perfectly).For example here; The number of emails has gone from 8 at a maximum to 9. The chart has redrawn the 0-8 in the correct spacing, but the 9 has been drawn a few pixels higher on the y direction and with the start of the bullet being x=0. It's probably easier to see on the top example, where the number in JSON has gone from 0 to 1, thereby the entire scale has been added, and the positioning is off. – Ewan Oct 11 '18 at 13:41
  • Helpful maybe, this is my refresh part, taken from an example online and amended to replicate the update button refresh in the mbostock example – Ewan Oct 11 '18 at 13:45
  • setInterval(function () { updateData(); }, 500); function updateData() { d3.json("mailboxes.json", function (error, data) { d3.select("body") .selectAll("svg") .datum(function (d, i) { d.ranges = data[i].ranges; d.measures = data[i].measures; d.markers = data[i].markers; return d; }) .call(chart.duration(500)); }); } – Ewan Oct 11 '18 at 13:45
  • @Ewan at line 27 `.domain([0, Math.max(rangez[0], markerz[0], measurez[0])])` the domain is changed to the max of all the values. `function updateData() { d3.json("mailboxes.json", function (error, data) { d3.select("body").selectAll("svg").data(data).call(chart.duration(500)); }); }` should also work, no need to copy the fields because it uses access functions. – rioV8 Oct 11 '18 at 14:23
  • strange that the `9` is positioned different, it has something to do with the data-key-function that uses the formatted value (I think) – rioV8 Oct 11 '18 at 14:35
  • the line `var format = tickFormat || x1.tickFormat(8);` is causing the error, `8` is not an allowed format for v3 (??). define your own `tickFormat` for the chart `var chart = d3.bullet() .width(width) .height(height) .tickFormat(d3.format(",.3f"));` – rioV8 Oct 11 '18 at 14:52
  • Hmm I've changed it now and it's showing the numbers OK which is good, however the lines still reset to x=0 for the ticks display, rather than being transposed over a hundred pixels. Can see here: https://i.imgur.com/g8zoxGn.png – Ewan Oct 12 '18 at 06:47
  • It's when the chart refreshes, its not drawing the ticks in the correct location. Refresh the page and it displays fine. I've even gone stock HTML and JS from the mbostock example and it's doing the same thing :( – Ewan Oct 12 '18 at 06:55
  • @Ewan: found the cause of the update problem, see the edit – rioV8 Oct 12 '18 at 09:41
  • Wow, man you are insane. I've spent 4 days fiddling with this, watching videos online trying to work it out. Thank you so so so much for your help! – Ewan Oct 12 '18 at 12:18
  • Can confirm it's now working as expected, with these changes explained here – Ewan Oct 12 '18 at 12:19
  • Hello @rioV8, Thanks man for the update, it works really well. But my title is being created twice when there's an update, it causes an overlap how can I fix that... And also I want to add another x-axis but on top which is ordinal on each tick that is created down. Any help is reall apprecieated. Thank you. – Bryan Nahabwe Jan 30 '20 at 12:37