Closed tpitale closed 8 years ago
This is similar to #34 and I haven't figured out a clean way to make this work. It's tricky because the width
value for the chart depends on how it's laid out so it's difficult to know ahead of time. Some possibilities:
interval
for the chart and calculate the width from thatI think an interval solution will work and I've been investigating how best to add it, but I think the best solution is to create an ordinal scale from the given linear/time scale:
// Use linear/time scale to define ticks for ordinal
var xScale = d3.scale.linear().domain([0, 50]);
var xScaleOrdinal = d3.scale.ordinal().domain(xScale.ticks(10));
// => pass ordinal to bars
Closing this issue in favor of #34
I tried:
xScale = d3c.helpers.createScale({type: 'time', data: options.data, key: 'x', adjacent: true})
xScaleOrdinal = d3.scale.ordinal().domain(xScale.ticks(10));
scales = {
x: xScaleOrdinal,
y: {domain: [-5, 5]}
}
And got some very strange results on the axis. And no width.
I'm probably missing something.
I also tried passing the domain
from time xscale to the ordinal domain
.
apologies for the coffeescript, not my project
Can I just set the bar width myself? I see there's a function, but unclear how it's used. I tried call barWidth
on a chart and passing a function. That just errors. Again, probably doing it wrong.
Well, I'm getting somewhere. Somewhere weird.
height = 350
width = 960
formatDate = d3.time.format('%H:%M')
parseDate = d3.time.format("%Y-%m-%d %H:%M").parse
chart = ->
d3.select('#response_container .d3-graph').chart('Compose', (options) ->
# Options function:
# Gets evaluated on each draw and takes in data and returns options
# xScale = d3c.helpers.createScale({type: 'time', data: options.data, key: 'x', adjacent: true})
# xScaleOrdinal = d3.scale.ordinal().domain(xScale.domain());
domain = d3.extent(options.data, (d) -> d.x)
# timeScale = d3c.helpers.createScale({type: 'time', domain: domain, range: [0, width], adjacent: true})
scales = {
# x: d3.scale.ordinal().domain(domain).range([0, width])
x: {type: 'ordinal', domain: domain, range: [0, width], adjacent: true},
y: {domain: [-5, 5], range: [height, 0]}
}
charts = [
# Create results chart:
# d3c.lines -> type: 'Lines' -> d3.chart('Lines')
# 'results' -> chart key for transitions and legend
# {data: data} -> pass given data to Lines chart
d3c.bars('results', {data: options.data, xScale: scales.x, yScale: scales.y})
]
title = d3c.title('Responses')
legend = d3c.legend({charts: ['results']}) # match key to charts key/name
x_axis = d3c.axis('xAxis', {scale: scales.x, tickFormat: formatDate})
y_axis = d3c.axis('yAxis', {scale: scales.y, ticks: 5})
[
# Add charts to chart layer (d3c.layered)
title,
[y_axis, d3c.layered(charts), legend],
x_axis
]
)
# .margins({
# top: 35, right: 20, bottom: 20, left: 40
# }).height(350).responsive(true)
bar_chart = chart()
bar_chart.height(height)
bar_chart.width(width)
data = [
{x: parseDate('2016-02-11 11:01'), y: -5},
{x: parseDate('2016-02-11 11:02'), y: -2},
{x: parseDate('2016-02-11 11:03'), y: 1},
{x: parseDate('2016-02-11 11:04'), y: -5},
{x: parseDate('2016-02-11 11:05'), y: -2},
{x: parseDate('2016-02-11 11:06'), y: 1},
{x: parseDate('2016-02-11 11:07'), y: -5},
{x: parseDate('2016-02-11 11:08'), y: -2},
{x: parseDate('2016-02-11 11:09'), y: 1}
]
bar_chart.draw({
data: data
})
Get's me:
I can get the xAxis … or I can get bars with width. But not both.
This is the BEST!
height = 350
width = 960
formatDate = d3.time.format('%H:%M')
parseDate = d3.time.format("%Y-%m-%d %H:%M").parse
chart = ->
d3.select('#response_container .d3-graph').chart('Compose', (options) ->
# Options function:
# Gets evaluated on each draw and takes in data and returns options
# xScale = d3c.helpers.createScale({type: 'time', data: options.data, key: 'x', adjacent: true})
# xScaleOrdinal = d3.scale.ordinal().domain(xScale.domain());
domain = d3.extent(options.data, (d) -> d.x)
# timeScale = d3c.helpers.createScale({type: 'time', domain: domain, range: [0, width], adjacent: true})
scales = {
# x: d3.scale.ordinal().domain(domain).range([0, width])
x: d3c.helpers.createScale({type: 'ordinal', domain: domain, range: [0, width], adjacent: true}),
y: {domain: [-5, 5], range: [height, 0]}
}
charts = [
# Create results chart:
# d3c.lines -> type: 'Lines' -> d3.chart('Lines')
# 'results' -> chart key for transitions and legend
# {data: data} -> pass given data to Lines chart
d3c.bars('results', {data: options.data, xScale: scales.x, yScale: scales.y})
]
console.log(scales.x(options.data[0].x))
console.log(scales.x(options.data[1].x))
console.log(scales.x(options.data[2].x))
console.log(scales.x(options.data[3].x))
console.log(scales.x(options.data[4].x))
console.log(scales.x(options.data[5].x))
console.log(scales.x(options.data[6].x))
console.log(scales.x(options.data[7].x))
console.log(scales.x(options.data[8].x))
title = d3c.title('Responses')
legend = d3c.legend({charts: ['results']}) # match key to charts key/name
x_axis = d3c.axis('xAxis', {scale: scales.x, tickFormat: formatDate})
y_axis = d3c.axis('yAxis', {scale: scales.y, ticks: 5})
[
# Add charts to chart layer (d3c.layered)
title,
[y_axis, d3c.layered(charts), legend],
x_axis
]
)
# .margins({
# top: 35, right: 20, bottom: 20, left: 40
# }).height(350).responsive(true)
bar_chart = chart()
bar_chart.height(height)
bar_chart.width(width)
data = [
{x: parseDate('2016-02-11 11:01'), y: -5},
{x: parseDate('2016-02-11 11:02'), y: -2},
{x: parseDate('2016-02-11 11:03'), y: 1},
{x: parseDate('2016-02-11 11:04'), y: -5},
{x: parseDate('2016-02-11 11:05'), y: -2},
{x: parseDate('2016-02-11 11:06'), y: 1},
{x: parseDate('2016-02-11 11:07'), y: -5},
{x: parseDate('2016-02-11 11:08'), y: -2},
{x: parseDate('2016-02-11 11:09'), y: 1}
]
bar_chart.draw({
data: data
})
Yields the correct graph! If I remove all the console.log
bits, it no longer works!
Obviously, nothing to do with the console.log
itself. Just the usage of the x scale.
Turned it into options.data.forEach((value, key) -> scales.x(value.x))
.
The scale has to be time to get the right output from calling the scale.
Thu Feb 11 2016 11:01:00 GMT-0600 (CST) 0
Thu Feb 11 2016 11:02:00 GMT-0600 (CST) 120
Thu Feb 11 2016 11:03:00 GMT-0600 (CST) 240
Thu Feb 11 2016 11:04:00 GMT-0600 (CST) 360
Thu Feb 11 2016 11:05:00 GMT-0600 (CST) 480
Thu Feb 11 2016 11:06:00 GMT-0600 (CST) 600
Thu Feb 11 2016 11:07:00 GMT-0600 (CST) 720
Thu Feb 11 2016 11:08:00 GMT-0600 (CST) 840
Thu Feb 11 2016 11:09:00 GMT-0600 (CST) 960
This output is using a time
type rather than ordinal
. Ordinal gives the right bars (sometimes out of order, though). Time gives the right scale, order/position. But no width, obviously.
Ordinal gives this output:
Thu Feb 11 2016 11:01:00 GMT-0600 (CST) 240
Thu Feb 11 2016 11:02:00 GMT-0600 (CST) 240
Thu Feb 11 2016 11:03:00 GMT-0600 (CST) 720
Thu Feb 11 2016 11:04:00 GMT-0600 (CST) 240
Thu Feb 11 2016 11:05:00 GMT-0600 (CST) 720
Thu Feb 11 2016 11:06:00 GMT-0600 (CST) 240
Thu Feb 11 2016 11:07:00 GMT-0600 (CST) 720
Thu Feb 11 2016 11:08:00 GMT-0600 (CST) 240
Thu Feb 11 2016 11:09:00 GMT-0600 (CST) 720
Based on: https://github.com/mbostock/d3/wiki/Ordinal-Scales, ordinal is not what I want. I want linear (or it's extension, time). But linear gives me an error about n.getHour
. Not sure what that is yet.
I think the following line is your issue:
domain = d3.extent(options.data, (d) -> d.x)
This sets the domain to [Feb 11 11:01, Feb 11 11:09]
and ignores all of the times in between. For ordinal every x value needs to be in the domain so the domain should be something like the following:
domain = data.map(d => d.x)
// domain = [Feb 11 11:01, Feb 11 11:02, Feb 1111:03, ...]
scales = {
x: {type: 'ordinal', domain: domain, adjacent: true},
y: {domain: [-5, 5]}
}
Let me know if that works for you.
The
itemWidth
function returns either a scale'swidth
or uses therangeBand
to calculate one. It appears thatd3.scale.time
has neither function.I've not come up with a workaround for this, as most suggestions are to simply calculate
width/data.length
which seems unlikely to be what's intended.Open to suggestions!