0

I would like to use a timeline that just shows years. The special about my data is that I just have years. But these years are ordered. So I know the exact order but not any more detail like day or month.

So first of all I would like to have something like this:

enter image description here

This is when the years just have one event. But having many events should look like this one:

enter image description here

So what would be a good way to get this. I already had a look on the timeline of d3, but I have never seen just years where just the order is known.

Btw: later on I will need month and days because for some years I actually have them. But for 99,9% I do not. So the years are more important for now =)

Btw2: I also need to zoom in, the range is 0 until year 2500 round about.

Some links:

Small jsfiddle scetch

jsfiddle.net/kwoxer/z614xkgu/

Nearly what I'm looking for

http://visjs.org/examples/timeline/29_hiding_times.html

Just that the items shall also be zoomed in.

Community
  • 1
  • 1
kwoxer
  • 3,734
  • 4
  • 40
  • 70

1 Answers1

1

Here is an augmentation of your JS fiddle, Demo: http://jsfiddle.net/robschmuecker/c8txLxo9/

It takes the data you have and then parses it to get a collection of years so that we only insert one dom element per year rather than several. Then we can conditionally add events for years which have more than one. It has an axis based on the dates too and is zoomable.

var dataset = [
    ["2006", 1],
    ["2009", 1],
    ["2004", 1],
    ["2004", 2],
    ["2004", 3],
    ["2012", 1],
    ["2008", 1],
    ["2004", 2],
    ["2000", 1],
    ["2006", 2],
    ["2007", 1],
    ["2001", 1]
];

//console.log(dataset, dataset.length);

var yearEvents = [];

// Firstly get all the events together for each year in the dataset
dataset.forEach(function (value) {
    var yearString = value[0] + "";
    if (typeof yearEvents[yearString] == 'undefined') yearEvents[yearString] = [];
    yearEvents[yearString].push(value[1]);
});

var newDataset = [];
yearEvents.forEach(function (year, key) {
    newDataset.push([key + "", year]);
});
//console.log('yearEvents', newDataset);

var w = 500;
var h = 300;
var padding = 20;
var circleRadius = 10;

var parseDate = d3.time.format("%Y").parse;
var mindate = parseDate("2000"),
    maxdate = parseDate("2015");

var xScale = d3.time.scale()
    .domain([mindate, maxdate])
    .range([padding, w - padding * 2]);

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function (d) {
    return d[1];
})])
    .range([5, 5]);

// Define the axis
var xAxis = d3.svg.axis().scale(xScale).tickSize(-h).tickSubdivide(true);


// Define the zoom function for the zoomable tree

function zoom() {
    svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
// define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);



var svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .call(zoomListener);


// Append a group which holds all nodes and which the zoom Listener can act upon.
var svgGroup = svg.append("g");
svgGroup.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + (h - padding) + ")")
    .call(xAxis);

var circleGroups = svgGroup.selectAll('g.circle')
    .data(newDataset)
    .enter()
    .append('g')
    .attr("transform", function (d) {
    return "translate(" + xScale(parseDate(d[0])) + "," + (h - padding - (circleRadius * 3)) + ")";
});

var circles = circleGroups.append("circle").attr("r", circleRadius);


// Append the year value to the circles.  change `display` property to `block` in CSS to show them.
var circleTexts = circleGroups.append('text')
    .attr("class", "circle-year")
//make their horizontal position offest by half of their font-size.
.attr("dy", function () {
    return "0.25em"
})
    .attr("text-anchor", "middle")
    .text(function (d) {
    return d[0];
});

// Now add text to the ones with more than one event
circleGroups.each(function (d, i) {
    //console.log(this, d, i);
    var me = this;
    //see if it has more than one event and if so loop through them all and add the new text elements with their height separation based on their index
    if (d[1].length > 1) {
        d[1].forEach(function (event, index) {
            d3.select(me).append('text')
                .attr("dy", function () {
                return -((circleRadius * index) * 2 + padding);
            })
                .attr("text-anchor", "middle")
                .text(function (d) {
                return event;
            });
        });
    }
});
Rob Schmuecker
  • 8,934
  • 2
  • 18
  • 34
  • Wow that looks pretty awesome. Now I gonna try to let it start with 0 somehow and adding a slider and then it should be exactly what I was looking for. You can delete you duplicate ;) – kwoxer Mar 04 '15 at 13:32
  • Puh I really studied your code but I'm not really able to edit it to all my pleasures. I did not find out how to only zoom horizontal. I also want to when I zoom that circles are loaded and not all on the very start. I mean it's a good start though. But I have just knodlegde about the force of d3. This one is kind of very new to me =/ – kwoxer Mar 04 '15 at 14:36
  • Well I think I gonna try this one: http://timeglider.com/widget/index.php looks awesome and easy. If not working I come back. Thanks anyways. – kwoxer Mar 04 '15 at 16:10