Built with blockbuilder.org
xxxxxxxxxx
<div>
<button value=1>1day</button>
<button value=3>3days</button>
<button value=7>7days</button>
<button value=30>30days</button>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<link rel="stylesheet" href="cStyle.css">
<script>
/************************/
// config from sencha
var cfg = [
{
title: 'Temperature',
unit: 'oC',
cls: 'tmp',
height: 80,
displayRange: [34,42],
type: 'line',
columnName: 'TEMPERATURE'
},
{
title: 'Pulse',
unit: 'BPM',
cls: 'hr',
height: 90,
displayRange: [40,180],
type: 'line',
columnName: 'HEART_RATE'
},
{
title: 'Lying Blood Pressure',
cls: 'bp',
unit: '',
height: 180,
displayRange: [20,260],
type: 'BP',
columnName: 'LYING_SBP',
columnName2: 'LYING_DBP'
}
];
var d3c = d3c || {
parseTime: d3.utcParse("%Y-%m-%d %H:%M:%S.%L"),
formatTime: d3.utcFormat("%e %B"),
plot: [],
x:{}, // shared x axis function
xAxis:{}, //
timeExtent: [],
//plotG: [], //
// y:[], // Array of y axises
// tip: []
};
// Setup size & margins
var margin = {top: 30, right: 20, bottom: 20, left: 30, gapBetweenCharts: 40},
width = 1000 - margin.left - margin.right,
height = margin.top + margin.bottom;
for(var i=0; i<cfg.length; i++)
{
height += cfg[i].height + margin.gapBetweenCharts;
}
height = height - margin.gapBetweenCharts;
d3c.setup = function(cfg, height, width, margin){
d3c.x = d3.scaleTime().rangeRound([0, width]);
/************************/
// Add the canvases
for(var i=0; i<cfg.length; i++)
{
d3c.plot.push({
g: null, //plotG
line: null, // line series
area: null, // line shading
circle: null, // hover marker
mline: null, // not working
y: null, // plot y axis
yAxis:{},// render y axis
tip:null,
data: [] // datetime/value data required for the plot
});
var s = d3.select("body").append("svg")
.attr("class", cfg[i].cls)
.attr("width", width + margin.left + margin.right)
.attr("height", cfg[i].height + margin.top + margin.bottom);
d3c.plot[i].g = s.append("g")
.attr("class", "g-cls")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
s.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", cfg[i].height);
d3c.plot[i].y= d3.scaleLinear().rangeRound([cfg[i].height, 0]);
d3c.plot[i].tip = d3.select("body").append("div")
.attr("class", "tooltip tip_" + i)
.style("opacity", 0);
}
for(var i=0; i<cfg.length; i++)
{
d3c.plot[i].line = d3.line()
.defined(function (d) { return d.value !== 0; }) // exclude 0 (NULL)
.x(function(d) { return d3c.x(d.date); })
.y(function(d) { return d3c.plot[d.idx].y(d.value); });
d3c.plot[i].area = d3.area()
.defined(d3c.plot[i].line.defined())
.x(d3c.plot[i].line.x())
.y1(d3c.plot[i].line.y())
.y0(d3c.plot[i].y(0));
}
// Load the data
d3.tsv("data.tsv", function(d) {
d.date = d3c.parseTime(d.OBS_TAKEN_DTTM);
for(var i=0; i<cfg.length; i++)
{
// convert column values to int and replace null as 0
d[cfg[i].columnName] = +(d[cfg[i].columnName]=='NULL'?0:d[cfg[i].columnName]);
d3c.plot[i].data.push({
idx: i,
date:d.date,
value:d[cfg[i].columnName],
value2:d[cfg[i].columnName2]
});
}
return d;
}, function(error, data) {
if (error) throw error;
// Get max/min Extent of date data
d3c.timeExtent = d3.extent(data, function(d) { return d.date; });
// x-axis min/max Set min extent 3 days before last data item
var startExtent = new Date(d3c.timeExtent[1].getTime() + -5*24*3600000 /*days*/);
var jj = d3c.getTimeExtent(3);
console.log(jj);
d3c.x.domain(d3c.getTimeExtent(3));//
d3c.xAxis = d3.axisBottom(d3c.x).ticks(6);
// y-axis min/max static range from config
for(var i=0; i<cfg.length; i++)
{
d3c.plot[i].y.domain(cfg[i].displayRange);
d3c.plot[i].yAxis = d3.axisLeft(d3c.plot[i].y)
.ticks(5)
.tickSize(-width);
}
/************************/
// Draw each chart
for(var i=0; i<cfg.length; i++)
{
// Draw x-axis time
var g = d3c.plot[i].g;
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + cfg[i].height + ")")
.call(d3c.xAxis);
var pan = g.append("g").attr("class", "y pan");
// Draw y-axis
g.append("g").attr("class", "axis axis--y")
.call(d3c.plot[i].yAxis)
.append("text") //axis label
.attr("class", "title " + cfg[i].cls)
.attr("y", -7)
.text(cfg[i].title);
// Draw Type = Line
if(cfg[i].type=='line'){
pan.append("path") .attr("class", "ll line " + cfg[i].cls)
.datum(d3c.plot[i].data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", d3c.plot[i].line);
pan.append("path").attr("class", "ll area " + cfg[i].cls)
.datum(d3c.plot[i].data)
.attr("d", d3c.plot[i].area[i])
}
// Draw Type = BP
if(cfg[i].type=='BP'){
var dataBar = d3c.plot[i].data.filter(function(d){
return d.value !== 0 && d.value2 !== 0;
});
var b = pan.selectAll("BP")
.data(dataBar)
.enter().append("g")
.attr("class", "BP bar " + cfg[i].cls)
.attr("transform", function(d) {
return "translate(" + d3c.x(d.date)+ ",0)"});
b.append("rect") //middle
.attr("width", 1)
.attr("y", function(d) { return d3c.plot[i].y(d.value)})
.attr("height", function(d) {
return d3c.plot[i].y(d.value2) - d3c.plot[i].y(d.value); })
b.append("rect") //head cap
.attr("x", -3)
.attr('y', function(d) { return d3c.plot[i].y(d.value)})
.attr("width", 7.3)
.attr("height", 1);
b.append("rect") // foot cap
.attr("x", -3 )
.attr('y', function(d) { return d3c.plot[i].y(d.value2)})
.attr("width", 7)
.attr("height", 1);
}
/************************/
// Draw marker circle and line
d3c.plot[i].circle = pan.append("circle")
.attr("r",3)
.style("opacity", 0)
.attr("class", cfg[i].cls);
d3c.plot[i].mline = pan.append("line")
.attr("x1",0).attr("x2",0)
.attr("y1",0).attr("y2",cfg[i].height)
.attr("class", "mline");
var et = [
[ d3c.x(d3c.timeExtent[0]), 0 ],
[ d3c.x(d3c.timeExtent[1]), cfg[i].height ]
];
// x-pan only
var zoom = d3.zoom()
.scaleExtent([1,1]) //Dont scale
.translateExtent(et) //Dont pan-y
.on("zoom", d3c.zoomed);
d3.selectAll("svg")
.call(zoom)
// hover panel for tooltip
g.append("rect").attr("class", "hoverPanel " + cfg[i].cls)
.attr("width", width)
.attr("height", cfg[i].height)
.style('fill', 'transparent')
.on('mousemove', d3c.identifyMouse);
//tip.push(g.append("div")
// .attr("class", "tooltip tip_" + i)
//.style("opacity", 0));
}
});
}
d3c.zoomed = function() {
var t = d3.event.transform;
t.y = 0;
xt = t.rescaleX(d3c.x);
// if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
d3.selectAll("svg").selectAll("bar").attr("transform", t);
//x.domain(getTimeExtent(days));
for(var i=0; i<cfg.length; i++)
{
var plot = d3c.plot[i];
plot.g.select(".line").attr("d", plot.line.x(function(d) { return xt(d.date); }));
plot.g.select(".area").attr("d", plot.area.x(function(d) { return xt(d.date); }));
plot.g.select(".axis--x").call(d3c.xAxis.scale(xt));
}
}
d3c.getTimeExtent = function (days){
var start = new Date(d3c.timeExtent[1].getTime() + -days*24*3600000 /*days*/);
return [start, d3c.timeExtent[1]];
}
// scale x-axis
d3.selectAll("button").on("click", function(){
var days = d3.select(this).attr('value');
d3c.x.domain(d3c.getTimeExtent(days));
for(var i=0; i<cfg.length; i++)
{
var plot = d3c.plot[i];
plot.g.select(".axis--x").transition()
.duration(2500)
.call(d3c.xAxis);
plot.g.select(".line").transition()
.duration(2500)
.attr("d", plot.line);
plot.g.select(".area").transition()
.duration(2500)
.attr("d", plot.area);
plot.g.selectAll(".bar").attr("transform", d3.zoom().scale );
}
});
d3c.identifyMouse =function(){
var bisect = d3.bisector(function(d){ return d.date; }).left;
var coordinates = d3.mouse(this);
var xInv = d3c.x.invert( coordinates[0] );
var yOffset = -10;
for(var i=0; i<cfg.length; i++)
{
var plot = d3c.plot[i];
var dataBar = plot.data.filter(function(d){
return d.value !== 0 && d.value2 !== 0;
});
var pos = bisect(dataBar, xInv, 0, dataBar.length);
var smaller = dataBar[pos-1];
var larger = dataBar[pos];
var match = xInv - smaller.x < larger.x - xInv ? smaller : larger;
plot.tip.transition()
.duration(200)
.style("opacity", .9)
.transition().delay(1000)
.duration(400).style("opacity", 0);
plot.tip.html(d3c.formatTime(match.date) + "<br/>" + match.value)
.style("left", d3c.x(match.date) + "px")
.style("top", yOffset + plot.y(match.value) + "px");
yOffset += cfg[i].height + margin.gapBetweenCharts;
plot.circle.transition()
.duration(70)
.style("opacity", .9)
.attr('cx', d3c.x(match.date))
.attr('cy', plot.y(match.value))
.transition().delay(1000)
.duration(400).style("opacity", 0);
plot.mline
.attr("x1", d3c.x(match.date))
.attr("x2", d3c.x(match.date))
}
}
d3c.setup(cfg, height, width, margin);
</script>
https://d3js.org/d3.v4.min.js