Skip to content

Instantly share code, notes, and snippets.

@ssaleh2
Last active September 22, 2016 01:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ssaleh2/6dd5562c983fbc77bc3bb59aae49c1be to your computer and use it in GitHub Desktop.
Save ssaleh2/6dd5562c983fbc77bc3bb59aae49c1be to your computer and use it in GitHub Desktop.
multi-line HR alarm FINAL
license: mit
Time Heart_Rate Alarm_LOW Alarm_HIGH Alarm_Seg
17 2234 69 60 110
18 2236 70 60 110
0 2200 74 60 110
1 2202 74 60 110
0 2200 74 60 110
1 2202 74 60 110
2 2204 73 60 110
3 2206 72 60 110
13 2226 69 60 110
14 2228 69 60 110
6 2212 72 60 110
7 2214 72 60 110
8 2216 72 60 110
9 2218 81 60 110
10 2220 80 60 110
11 2222 79 60 110
12 2224 74 60 110
13 2226 69 60 110
14 2228 69 60 110
15 2230 69 60 110
16 2232 69 60 110
17 2234 69 60 110
18 2236 70 60 110
0 2200 74 60 110
1 2202 74 60 110
2 2204 73 60 110
11 2222 79 60 110
12 2224 74 60 110
13 2226 69 60 110
14 2228 69 60 110
15 2230 69 60 110
16 2232 69 60 110
2 2204 73 60 110
3 2206 72 60 110
4 2208 72 60 110
10 2220 80 60 110
11 2222 79 60 110
12 2224 74 60 110
13 2226 69 60 110
14 2228 69 60 110
15 2230 69 60 110
16 2232 69 60 110
17 2234 69 60 110
18 2236 70 60 110
19 2238 70 60 110
20 2240 70 60 110
21 2242 70 60 110
22 2244 71 60 110
23 2246 72 60 110
24 2248 73 60 110
25 2250 73 60 110
26 2252 74 60 110
27 2254 74 60 110
29 2258 75 60 110
27 2254 74 60 110
28 2256 75 60 110
29 2258 75 60 110
28 2256 75 60 110
29 2258 75 60 110
27 2254 74 60 110
28 2256 75 60 110
29 2258 75 60 110
30 2260 74 60 110
31 2262 74 50 100
32 2264 74 50 100
33 2266 65 50 100
34 2268 53 50 100
35 2270 50 50 100 50
36 2272 48 50 100 48
37 2274 42 50 100 42
38 2276 42 50 100 42
39 2278 45 50 100 45
40 2280 45 50 100 45
41 2282 50 50 100 50
42 2284 60 50 100
43 2286 75 50 100
44 2288 75 50 100
45 2290 75 50 100
46 2292 75 50 100
47 2294 75 50 100
48 2296 75 50 100
49 2298 75 50 100
50 2300 75 50 100
51 2302 75 50 100
52 2304 75 50 100
53 2306 75 50 100
54 2308 75 50 100
55 2310 75 50 100
56 2312 75 50 100
57 2314 75 50 100
58 2316 76 50 100
59 2318 76 50 100
60 2320 76 50 100
61 2322 75 50 100
62 2324 67 50 100
63 2326 67 50 100
64 2328 67 50 100
65 2330 61 50 100
66 2332 50 50 100 50
67 2334 47 50 100 47
68 2336 47 50 100 47
69 2338 50 50 100 50
70 2340 57 50 100
71 2342 87 50 100
72 2344 87 50 100
73 2346 90 50 100
74 2348 90 50 100
75 2350 100 50 100
76 2352 84 50 100
77 2354 73 50 100
78 2356 73 50 100
79 2358 81 50 100
80 2360 78 50 100
81 2362 77 50 100
82 2364 70 50 100
83 2366 69 50 100
84 2368 70 50 100
85 2370 71 50 100
86 2372 74 50 100
87 2374 77 50 100
88 2376 74 50 100
89 2378 80 50 100
90 2380 79 50 100
91 2382 78 50 100
92 2384 78 50 100
93 2386 88 50 100
94 2388 83 50 100
95 2390 77 50 100
96 2392 87 50 100
97 2394 86 50 100
98 2396 86 50 100
99 2398 75 50 100
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.graph .axis {
stroke-width: 1;
}
.graph .axis .tick line {
stroke: black;
}
.graph .axis .tick text {
fill: black;
font-size: 0.7em;
}
.graph .axis .domain {
fill: none;
stroke: black;
}
.graph .group {
fill: none;
stroke: black;
stroke-width: 2.5;
}
.d3-tip {
line-height: 1;
font-weight: bold;
font-size: 15px;
font-family: "Courier New";
padding: 10px;
background: rgba(0, 0, 0, 0.4);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.4);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
</head>
<body>
<div class="graph"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.7/d3-tip.js"></script>
<script>
var limit = 60 * 1,
duration = 667,
now = new Date(Date.now() - duration)
var width = 800,
height = 400;
var tip = d3.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d) {return "Name: Propranolol " + "<br>" + "Dose: 30 mg" + "<br>" + "Source: PO"});
var x = d3.time.scale()
.domain([now - (limit - 2), now - duration])
.range([0, width])
var y = d3.scale.linear()
.domain([20, 180])
.range([height-3, 0+7])
var maxIndex = 60;
var minIndex = 0;
var fullData;
var subset;
var pitch = 764.81;
var index_change_thresh;
// var formatDate = d3.time.parse("%Y-%m-%d %H:%M:%S");
var circle = null;
var circleTransition = null;
var latestBeat = null;
var insideBeat = false;
var SECONDS_SAMPLE = 5;
var BEAT_TIME = 400;
var TICK_FREQUENCY = SECONDS_SAMPLE * 1000 / BEAT_TIME;
var BEAT_VALUES = [0, 0, 3, -4, 10, -7, 3, 0, 0];
var CIRCLE_FULL_RADIUS = 40;
var MAX_LATENCY = 5000;
var colorScale = d3.scale.linear()
.domain([BEAT_TIME, (MAX_LATENCY - BEAT_TIME) / 2, MAX_LATENCY])
.range(["#6D9521", "#D77900", "#CD3333"]);
var radiusScale = d3.scale.linear()
.range([5, CIRCLE_FULL_RADIUS])
.domain([MAX_LATENCY, BEAT_TIME]);
d3.csv("data.csv", function(error, data) {
if (error) throw error;
fullData = data;
data.forEach(function(d) {
d.index = +d.index;
d.Time = +d.Time - 2200;
d.Heart_Rate = +d.Heart_Rate;
d.Alarm_LOW = +d.Alarm_LOW;
d.Alarm_HIGH = +d.Alarm_HIGH;
d.Alarm_Seg = +d.Alarm_Seg;
});
subset = data.slice(minIndex, maxIndex);
var groups = {
alarm_top: {
value: 0,
color: 'grey',
data: subset.map(function(d){
return d.Alarm_HIGH
})
},
HR: {
value: 0,
color: 'green',
data: subset.map(function(d){
return d.Heart_Rate
})
},
alarm_bottom: {
value: 0,
color: 'grey',
data: subset.map(function(d){
return d.Alarm_LOW
})
},
alarm_seg: {
value: 0,
color: 'red',
data: subset.map(function(d){
return d.Alarm_Seg
}),
}
}
var svg = d3.select('.graph').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height + 50)
.append("g")
.attr("transform", "translate(50,0)")
var line = d3.svg.line()
.interpolate('basis')
.defined(function(d) { return d; })
.x(function(d, i) {
return x(now - (limit - 1 - i) * duration)
})
.y(function(d) {
return y(d)
});
var alarm = d3.svg.line()
.interpolate('step')
.defined(function(d) { return d; })
.x(function(d, i) {
return x(now - (limit - 1 - i) * duration)
})
.y(function(d) {
return y(d)
});
// circle = svg.append("circle")
// .attr("fill", "#6D9521")
// .attr("cx", width - 50)
// .attr("cy", height / 8)
// .attr("r", CIRCLE_FULL_RADIUS);
logo_image = svg.append("svg:image")
.attr("xlink:href", "https://photos-2.dropbox.com/t/2/AACPAgeamMH9Pqwm8qfgBxaOeveu2bDn1biBdysgdCQFZw/12/44674655/png/32x32/1/_/1/2/precisely_no_shadow.png/ELKvmSIYqSwgBygH/468DdTQyet8GP-yo8y3b7M9JiwEWLPCEdNtewgcPu3k?size=800x600&size_mode=3")
.attr("x", function(d) { return 0;})
.attr("y", function(d) { return 0;})
.attr("width", 85)
.attr("height", 85);
dx_image = svg.append("svg:image")
.attr("xlink:href", "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcTGdTw9a2xeKGu4ezKtjwutax0XzkBuTa2yrtDZ-wTu4g5mgcv2") //"https://cdn0.iconfinder.com/data/icons/healthcare-and-medicine-kit/512/medical_test_stethoscope-512.png")
.attr("x", function(d) { return 160;})
.attr("y", function(d) { return 25;})
.attr("width", 55)
.attr("height", 55);
dem_image = svg.append("svg:image")
.attr("xlink:href", "http://www.arlington-tx.gov/cdp/wp-content/uploads/sites/11/2014/04/Demographics.png")
.attr("x", function(d) { return 95;})
.attr("y", function(d) { return 25;})
.attr("width", 55)
.attr("height", 55);
icu_image = svg.append("svg:image")
.attr("xlink:href", "http://fcmcng.com/wp-content/uploads/2015/03/ICU_Icon-08.png")
.attr("x", function(d) { return 225;})
.attr("y", function(d) { return 25;})
.attr("width", 55)
.attr("height", 55);
var med_image = svg.append("svg:image")
.attr("xlink:href", "https://cdn3.iconfinder.com/data/icons/medical-8/512/medication-512.png")
.attr("x", function(d) { return 280;})
.attr("y", function(d) { return 25;})
.attr("width", 55)
.attr("height", 55);
var plus = null,
med_name,
med_change;
var gradient = svg.append("defs")
.append("linearGradient")
.attr("id", "gradient")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "0%")
.attr("spreadMethod", "pad");
gradient.append("stop")
.attr("offset", "0%")
.attr("stop-color", "#D14424")
.attr("stop-opacity", 0.4);
gradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", "#D14424")
.attr("stop-opacity", 0.0);
var Yaxis = svg.append('g')
.attr('class', 'y axis')
.attr("transform", "translate (" + 750 + ", 0)")
.call(y.axis = d3.svg.axis().scale(y).orient("left"))
var Xaxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(-2,' + height + ')')
.call(x.axis = d3.svg.axis().scale(x).orient('bottom'))
var paths = svg.append('g')
groups['HR'].path = paths.append('path')
.data([groups['HR'].data])
.attr('class', 'HR' + ' group')
.style('stroke', groups['HR'].color)
groups['alarm_top'].path = paths.append('path')
.data([groups['alarm_top'].data])
.attr('class', 'alarm_top' + ' group')
.style("stroke-dasharray", ("3, 3"))
.style('stroke', groups['alarm_top'].color)
groups['alarm_bottom'].path = paths.append('path')
.data([groups['alarm_bottom'].data])
.attr('class', 'alarm_bottom' + ' group')
.style('stroke', groups['alarm_bottom'].color)
.style("stroke-dasharray", ("3, 3"))
groups['alarm_seg'].path = paths.append('path')
.data([groups['alarm_seg'].data])
.attr('class', 'alarm_seg' + ' group')
.style('stroke', groups['alarm_seg'].color)
// for tool tips
svg.call(tip);
function mouseover(d) {
d3.select(d.data.city.line).classed("city--hover", true);
d.data.city.line.parentNode.appendChild(d.data.city.line);
focus.attr("transform", "translate(" + x(d.data.date) + "," + y(d.data.value) + ")");
focus.select("text").text(d.data.city.name);
}
function mouseout(d) {
d3.select(d.data.city.line).classed("city--hover", false);
focus.attr("transform", "translate(-100,-100)");
}
//audio context
var ac = this.AudioContext ? new AudioContext() : new webkitAudioContext();
ac.createGain();
//generate oscillator
function osc(pitch, waveform){
// var ac = new AudioContext(),
oscillator = ac.createOscillator(),
oscillator.type = waveform;
oscillator.frequency.value = pitch;
gainNode = ac.createGain();
oscillator.connect(gainNode);
gainNode.connect(ac.destination);
gainNode.gain.value = .2;
return {osc: oscillator, gain: gainNode};
};
function tick() {
//add new values
now = new Date()
groups['HR'].data.push(fullData[maxIndex]['Heart_Rate'])
groups['HR'].path.attr('d', line)
// when lower alarm limit changes
if (fullData[maxIndex]['Alarm_LOW'] != groups['alarm_bottom'].data[groups['alarm_bottom'].data.length-1]){
index_change_thresh = maxIndex;
med_change = svg.append("line")
.attr({
x1: index_change_thresh*800/60 - 85,
x2: index_change_thresh*800/60 - 85,
y1: 90,
y2: height
})
.attr("class", "med_change")
.style("stroke", "#F25214")
.style("stroke-width", 3)
.style("stroke-dasharray", ("3, 3"));
med_effect = svg.append("rect")
.attr({
x: index_change_thresh*800/60 - 85,
y: 90,
})
.attr("width", 800)
.attr("height", height - 90)
.style("fill", "url(#gradient)");
med_image = svg.append('g')
.append("svg:image")
.attr("xlink:href", "https://cdn3.iconfinder.com/data/icons/medical-8/512/medication-512.png")
.attr("class", "add_med")
.attr("x", function(d) { return index_change_thresh*800/60 - 85;})
.attr("y", function(d) { return 100;})
.attr("width", 40)
.attr("height", 40)
.on("mouseover", tip.show)
.on("mouseout", tip.hide);
;
// med_image.append("text")
// .attr("dy", function(d) { return -10;})
// .text("Propranolol " + "<br>" + "30 mg" + "<br>" + "PO");
plus = svg.append('g')
.append("svg:image")
.attr("xlink:href", "http://www.freeiconspng.com/uploads/plus-icon-black-2.png")
.attr("class", "add_med")
.attr("x", function(d) { return index_change_thresh*800/60 - 55;})
.attr("y", function(d) { return 108;})
.attr("width", 35)
.attr("height", 35);
// med_name = svg.append("text")
// .attr("class", "add_med")
// .attr("x", function(d) { return index_change_thresh*800/60 - 83;})
// .attr("y", function(d) { return 155;})
// .text("Propranolol" + "\n" +
// "30 mg PO");
};
//Move medication along axis
if (fullData[maxIndex]['Alarm_LOW'] == groups['alarm_bottom'].data[groups['alarm_bottom'].data.length-1] && plus != null){
index_change_thresh -= 1.15;
plus
.transition()
.duration(duration)
.ease('linear')
.attr("x", function(d) { return index_change_thresh*800/60 - 55;})
med_image
.transition()
.duration(duration)
.ease('linear')
.attr("x", function(d) { return index_change_thresh*800/60 - 85;})
// med_name.transition()
// .duration(duration)
// .ease('linear')
// .attr("x", function(d) { return index_change_thresh*800/60 - 83;})
med_change
.transition()
.duration(duration)
.ease('linear')
.attr("x1", function(d) { return index_change_thresh*800/60 -85;})
.attr("x2", function(d) { return index_change_thresh*800/60 -85;});
med_effect
.transition()
.duration(duration)
.ease('linear')
.attr("x", function(d) { return index_change_thresh*800/60 -85;})
// .attr("width", function(d) { return index_change_thresh*800/60 -85;})
};
groups['alarm_bottom'].data.push(fullData[maxIndex]['Alarm_LOW'])
groups['alarm_bottom'].path.attr('d', alarm)
groups['alarm_top'].data.push(fullData[maxIndex]['Alarm_HIGH'])
groups['alarm_top'].path.attr('d', alarm)
groups['alarm_seg'].data.push(fullData[maxIndex]['Alarm_Seg'])
groups['alarm_seg'].path.attr('d', line)
// console.log(dem_image)
// index_change_thresh -= 1.15;
// d3.select("add_med").attr('transform', 'translate(' + index_change_thresh*800/60 - 110 +')')
// .transition()
// .attr(attr("x", function(d) { return index_change_thresh*800/60 - 110;}))
// .duration(duration)
// .ease('linear')
// .attr('transform', 'translate(' + x(now - (limit - 1) * duration) + ')')
// .each('end', tick)
maxIndex += 1;
// Shift domain
x.domain([now - (limit - 2) * duration, now - duration])
// Slide x-axis left
Xaxis.transition()
.duration(duration)
.ease('linear')
.call(x.axis)
// Slide paths left
paths.attr('transform', null)
.transition()
.duration(duration)
.ease('linear')
.attr('transform', 'translate(' + x(now - (limit - 1) * duration) + ')')
.each('end', tick)
//play alarm sound
if (fullData[maxIndex]['Alarm_Seg']!=0){
var o = osc(pitch, 'sine');
o.osc.start(ac.currentTime + 3.5);
o.osc.stop(ac.currentTime + 3.75);
}
// Remove oldest data point from each group
for (var name in groups) {
var group = groups[name]
group.data.shift()
}
}
tick()
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment