|
var width = 900, |
|
height = 1000, |
|
num_axes = 12, |
|
tick_axis = 9, |
|
start = 0, |
|
firstJan = 0.25, |
|
end = 2.35, |
|
startDate = new Date(2016,10,1); |
|
|
|
var theta = function(r) { |
|
return 2*Math.PI*r; |
|
}; |
|
|
|
|
|
var months = ['Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar']; |
|
var labels1 = ['Targets','Admits','SIR/Melt Tracking','Est. YAHC from Fall Actuals/Spring Forecast','YAHC → '] |
|
var labels2 = ['Target Setting with UCOP','New Admits Notified','Estimated YAHC using Admit SIR and Melt Data', |
|
' Estimated YAHC is based on Fall Actuals and Forecasted Spring Headcounts','Actual YAHC → ']; |
|
|
|
|
|
var r = d3.min([width,height])/2-100; |
|
var r2 = r; |
|
|
|
var radius = d3.scale.linear() |
|
.domain([start, end]) |
|
.range([0, r]); |
|
|
|
var radius_axis = d3.scale.linear() |
|
.domain([start, end]) |
|
.range([0, r+34]); |
|
|
|
var angle = d3.scale.linear() |
|
.domain([0,num_axes]) |
|
.range([0,360]) |
|
|
|
|
|
|
|
var svg = d3.select("#chart").append("svg") |
|
.attr("width", width) |
|
.attr("height", height) |
|
.append("g") |
|
.attr("transform", "translate(" + ((width/2)+50) + "," + ((height/2.1)+8) +")"); |
|
|
|
svg.append("rect") |
|
.attr("width", width-108) |
|
.attr("height", height-8) |
|
// .style("stroke","#000") |
|
.style("stroke","none") // switch these two to show the bounding box for taking snapshots |
|
.style("fill","none") |
|
.attr("transform", "translate(" + (-(width/2)+50) + "," + (-(height/2.1)) +")"); |
|
|
|
|
|
// -- ----------------------------------------------------------- |
|
// add the donut chart for terms |
|
// -- ----------------------------------------------------------- |
|
|
|
var termdata = [{"name": "", "value": 3}, |
|
{"name": "Spring Term", "value": 18}, |
|
{"name": "", "value": 1}, |
|
{"name": "Summer Session", "value": 8}, |
|
{"name": "", "value": 1}, |
|
{"name": "Fall Term", "value": 18}, |
|
{"name": "", "value": 3} |
|
]; |
|
var color2 = d3.scale.ordinal() |
|
.range(["#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]); |
|
|
|
var arc = d3.svg.arc() |
|
.outerRadius(348) |
|
.innerRadius(338); |
|
|
|
var pie = d3.layout.pie() |
|
.sort(null) |
|
.value(function(d) { |
|
return d.value; |
|
}); |
|
|
|
|
|
|
|
var g = svg.selectAll(".arc") |
|
.data(pie(termdata)) |
|
.enter().append("g") |
|
.attr("class", "arc"); |
|
|
|
g.append("path") |
|
.attr("d", arc) |
|
.style("fill", "url(#whitecarbon)") |
|
// .attr("class", "lightstripe") |
|
.attr("id", function(d,i) {return "arc" + i;}) |
|
.style("fill-opacity", function(d) { return d.data.name === "" ? 0 : 0.75; }); |
|
|
|
// g.append("text") |
|
// .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; }) |
|
// .attr("dy", ".35em") |
|
// .text(function(d) { return d.data.name; }); |
|
|
|
g.selectAll(".termText") |
|
.data(pie(termdata)) |
|
.enter().append("text") |
|
// .attr("x",100) |
|
// .attr("x",function(d,i) { return (d.endAngle - d.startAngle)/2; }) |
|
// .attr("dy", function(d,i) { return (d.endAngle > 90 * Math.PI/180 ? 18 : -11); }) |
|
.attr("dy", -8) |
|
.attr("class", "termText") |
|
.append("textPath") |
|
// .attr("startOffset","50%") |
|
.style("text-anchor","start") |
|
.attr("xlink:href",function(d,i){return "#arc" + i;}) |
|
.text(function(d) { return d.data.name;} ); |
|
|
|
var pieces = d3.range(start, end+0.001, (end-start)/1000); |
|
|
|
var spiral = d3.svg.line.radial() |
|
.interpolate("cardinal") |
|
.angle(theta) |
|
.radius(radius); |
|
|
|
|
|
|
|
|
|
|
|
svg.selectAll("circle.tick") |
|
.data(d3.range(firstJan,(end+0.001),1)) |
|
.enter().append("circle") |
|
.attr("class", "tick") |
|
.attr("fill","#525252") |
|
.attr("fill-opacity",function(d,i) {return (i+1)/(12*i);}) |
|
.attr("cx", 0) |
|
.attr("cy", 0) |
|
.attr("r", function(d) { return radius(d); }); |
|
|
|
svg.selectAll(".axis") |
|
.data(d3.range(num_axes)) |
|
.enter().append("g") |
|
.attr("class", "axis") |
|
.attr("transform", function(d) { return "rotate(" + -angle(d) + ")"; }) |
|
.call(radial_tick); |
|
|
|
|
|
|
|
svg.selectAll("text.year") |
|
.data(['','Year 1','Year 2','Year 3']) |
|
.enter() |
|
.append("text") |
|
.attr("x","0") |
|
.attr("y",function(d,i){ return (i*-100)+56; }) |
|
.style("fill-opacity", function(d,i) {return 1/(i+1.5);}) |
|
.attr("class","year") |
|
.attr("text-anchor","middle") |
|
.text(function(d){ return d; }); |
|
|
|
|
|
svg.selectAll("text.months") |
|
.data(d3.range(num_axes)) |
|
.enter().append("text") |
|
.attr("class","months") |
|
.attr("y", radius(end)+13) |
|
.text(function(d,i) { return months[i]; }) |
|
.attr("text-anchor", "middle") |
|
.attr("transform", function(d,i) { |
|
var tempX = Math.round(1.15*(r * Math.cos((angle(d)/180)*Math.PI))); |
|
var tempY = Math.round(1.15*(r * Math.sin((angle(d)/180)*Math.PI))) - r - 6; |
|
return "translate(" + tempX + ", " + tempY + ")"; |
|
}); |
|
|
|
|
|
svg.selectAll(".spiral") |
|
.data([pieces]) |
|
.enter().append("path") |
|
.attr("class", "spiral") |
|
.attr("d", spiral) |
|
.attr("transform", function(d) { return "rotate(" + (90) + ")" });//starts in October |
|
|
|
|
|
var year1 = []; |
|
year1[0] = {startDay:1, startMth:10, startYear:2016, endDay:20, endMth:2, endYear:2017}; |
|
year1[1] = {startDay:21, startMth:2, startYear:2017, endDay:15, endMth:6, endYear:2017}; |
|
year1[2] = {startDay:16, startMth:6, startYear:2017, endDay:15, endMth:10, endYear:2017}; |
|
year1[3] = {startDay:16, startMth:10, startYear:2017, endDay:15, endMth:3, endYear:2018}; |
|
year1[4] = {startDay:16, startMth:3, startYear:2018, endDay:12, endMth:4, endYear:2018}; |
|
|
|
var year2 = []; |
|
year2[0] = {startDay:1, startMth:10, startYear:2017, endDay:20, endMth:2, endYear:2018}; |
|
year2[1] = {startDay:21, startMth:2, startYear:2018, endDay:15, endMth:6, endYear:2018}; |
|
year2[2] = {startDay:16, startMth:6, startYear:2018, endDay:15, endMth:10, endYear:2018}; |
|
year2[3] = {startDay:16, startMth:10, startYear:2018, endDay:15, endMth:3, endYear:2019}; |
|
year2[4] = {startDay:16, startMth:3, startYear:2019, endDay:12, endMth:4, endYear:2019}; |
|
|
|
var specialDays = []; |
|
// specialDays[0] = { day: 1, month: 11, year: 2016, ref:2 }; |
|
// specialDays[1] = { day: 2, month: 3, year: 2017, ref:2 }; |
|
// specialDays[2] = { day: 1, month: 6, year: 2017, ref:2}; |
|
// specialDays[3] = { day: 1, month: 6, year: 2016, ref:1}; |
|
// specialDays[4] = { day: 1, month: 3, year: 2017, ref:1}; |
|
// specialDays[5] = { day: 6, month: 4, year: 2017, ref:0}; |
|
// specialDays[6] = { day: 31, month: 1, year: 2017, ref:0}; |
|
// specialDays[7] = { day: 8, month: 10, year: 2017, ref:3}; |
|
// specialDays[8] = { day: 5, month: 11, year: 2017, ref:3}; |
|
|
|
|
|
for (var p=0; p<year1.length; p++) { |
|
|
|
|
|
var date1 = new Date(year1[p].startYear, year1[p].startMth, year1[p].startDay); |
|
var date2 = new Date(year1[p].endYear, year1[p].endMth, year1[p].endDay); |
|
|
|
|
|
|
|
var timeDiff1 = Math.abs(date1.getTime() - startDate.getTime()); |
|
var diffDays1 = (Math.ceil(timeDiff1 / (1000 * 3600 * 24)))/365; |
|
|
|
var timeDiff2 = Math.abs(date2.getTime() - startDate.getTime()); |
|
var diffDays2 = (Math.ceil(timeDiff2 / (1000 * 3600 * 24)))/365; |
|
|
|
var pieces2 = d3.range(diffDays1, diffDays2+0.001, (diffDays2 - diffDays1)/1000); |
|
|
|
(p==0) ? r2 = r * 0.90 : r2 = r * 1.0 ; |
|
|
|
r = r2; |
|
|
|
var theta2 = function(r2) { |
|
return 2*Math.PI*r2; |
|
}; |
|
|
|
var radius2 = d3.scale.linear() |
|
.domain([start, end]) |
|
.range([0, r2]); |
|
|
|
var spiral2 = d3.svg.line.radial() |
|
.interpolate("cardinal") |
|
.angle(theta2) |
|
.radius(radius2); |
|
|
|
svg.selectAll(".spiral"+p) |
|
.data([pieces2]) |
|
.enter().append("path") |
|
.attr("class", "spiral"+p) |
|
.attr("d", spiral2) |
|
.style("stroke", color2(p)) |
|
.style("fill", "none") |
|
.attr("id", "spiral"+p) //Give each slice a unique ID |
|
.attr("transform", function(d) { return "rotate(" + (-90) + ")" });//starts in October |
|
|
|
svg.selectAll(".labelText"+p) |
|
.data([pieces2]) |
|
.enter().append("text") |
|
.attr("x",3) |
|
.attr("dy",4) |
|
.attr("class", "labelText"+p) |
|
.append("textPath") |
|
.attr("xlink:href",function(d,i){return "#spiral"+p;}) |
|
.text(labels1[p]); |
|
|
|
|
|
for (var q=0; q<specialDays.length; q++) {//specialDays.length |
|
|
|
if (specialDays[q].ref==p) { |
|
|
|
var date1 = new Date(specialDays[q].year, specialDays[q].month, specialDays[q].day); |
|
|
|
var timeDiff1 = Math.abs(date1.getTime() - startDate.getTime()); |
|
var diffDays1 = (Math.ceil(timeDiff1 / (1000 * 3600 * 24)))/365; |
|
|
|
var rad = radius2(diffDays1); |
|
var ang = angle(specialDays[q].month + ((specialDays[q].day-1)/30))-30-90; |
|
|
|
|
|
var x = rad * Math.cos((ang/180)*Math.PI); |
|
var y = rad * Math.sin((ang/180)*Math.PI); |
|
|
|
svg.append("circle").attr("cx",x).attr("cy",y).attr("r","3").attr("class","dot dot"+p); |
|
|
|
} |
|
} |
|
} |
|
|
|
var r = d3.min([width,height])/2-60; |
|
var r2 = r; |
|
|
|
for (var p=0; p<year2.length; p++) { |
|
|
|
|
|
var date1 = new Date(year2[p].startYear, year2[p].startMth, year2[p].startDay); |
|
var date2 = new Date(year2[p].endYear, year2[p].endMth, year2[p].endDay); |
|
|
|
var color = d3.scale.category10(); |
|
|
|
var timeDiff1 = Math.abs(date1.getTime() - startDate.getTime()); |
|
var diffDays1 = (Math.ceil(timeDiff1 / (1000 * 3600 * 24)))/365; |
|
|
|
var timeDiff2 = Math.abs(date2.getTime() - startDate.getTime()); |
|
var diffDays2 = (Math.ceil(timeDiff2 / (1000 * 3600 * 24)))/365; |
|
|
|
var pieces2 = d3.range(diffDays1, diffDays2+0.001, (diffDays2 - diffDays1)/1000); |
|
|
|
(p==0) ? r2 = r * 0.90 : r2 = r * 1.0 ; |
|
|
|
r = r2; |
|
|
|
var theta2 = function(r2) { |
|
return 2*Math.PI*r2; |
|
}; |
|
|
|
var radius2 = d3.scale.linear() |
|
.domain([start, end]) |
|
.range([0, r2]); |
|
|
|
|
|
var spiral2 = d3.svg.line.radial() |
|
.interpolate("cardinal") |
|
.angle(theta2) |
|
.radius(radius2); |
|
|
|
|
|
svg.selectAll(".spiral2"+p) |
|
.data([pieces2]) |
|
.enter().append("path") |
|
.attr("class", "spiral2"+p) |
|
.style("stroke", color2(p)) |
|
.style("stroke-width",16) |
|
.style("fill","none") |
|
.attr("d", spiral2) |
|
.attr("id", "spiral2"+p) //Give each slice a unique ID |
|
.attr("transform", function(d) { return "rotate(" + (-90) + ")" });//starts in October |
|
|
|
|
|
svg.selectAll(".labelText2"+p) |
|
.data([pieces2]) |
|
.enter().append("text") |
|
.attr("x",3) |
|
.attr("dy",4) |
|
.attr("class", "labelText2"+p) |
|
.append("textPath") |
|
.attr("xlink:href",function(d,i){return "#spiral2"+p;}) |
|
.text(labels2[p]); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function radial_tick(selection) { |
|
selection.each(function(axis_num) { |
|
d3.svg.axis() |
|
.scale(radius_axis) |
|
.ticks(4) |
|
.tickValues( axis_num == tick_axis ? [] : []) |
|
.orient("top")(d3.select(this)) |
|
|
|
}); |
|
} |
|
|