block by saraquigley b926071f6187335c5d2f233c8fe66892

the enrollment cycle

Full Screen

Built with blockbuilder.org

Forked from : d3 spiral calendar block which is based on the spiral created by syntagmatic for John Hunter.

This is still using d3 v3 since I can’t quite get the new d3.radialLine() to work using the latest release of d3-shape v1.2.0.

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <link href='//fonts.googleapis.com/css?family=Lato:300,400,700' rel='stylesheet' type='text/css'>
  <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
  <link href='//fonts.googleapis.com/css?family=PT+Sans:200,300,400,700' rel='stylesheet' type='text/css'>
    <title>Calendar spiral in d3.js</title>
    <script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
    <style>
      body {
        font-family: 'PT Sans', sans-serif;
/*         margin: auto; */
      }
      .axis path {
        fill: none;
        stroke: #999;
        stroke-width: 1px;
        stroke-dasharray: 2 3;
      }
      text.months {
        font-size: 13px;
        font-weight: 100;
        fill: lightslategray;
      }
      text.title {
        font-size: 24px;
      }
      circle.tick { 
        /*fill: rgba(153, 153, 153, 0.2);*/
        stroke: #999;
        stroke-dasharray: 2 3;
      }
      path.spiral {
        fill: none;
        stroke: white;
        stroke-width: 1px;
      }
       path.spiral0, 
       path.spiral201 {
        fill: none;
        stroke: #abd9e9;
        stroke-width: 14px;
      }   
      path.spiral1, 
      path.spiral211 {
        fill: none;
        stroke: #74add1;
        stroke-width: 14px;
      }      
      path.spiral2, 
      path.spiral221 {
        fill: none;
        stroke: #4575b4;
        stroke-width: 14px;
      }
      path.spiral3, 
      path.spiral231 {
        fill: none;
        stroke: #313695;
        stroke-width: 14px;
      }
      path.spiral4, 
      path.spiral24 {
        fill: none;
        stroke: #66bd63;
        stroke-width: 14px;
      }
      circle.dot {
	      fill: white;
	      stroke-width: 1px;
      }
      
      circle.dot0 {
        stroke: #419FF7;
      }
      circle.dot1 {
        stroke: #E94685;
      }
      circle.dot2 {
        stroke: #79F347;
      }
      circle.dot3 {
        stroke: #7941F7;
      }
      
      text.year {
	      fill: #525252;
/*	      opacity: 0.5;*/
	      font-size: 32px;
	      font-weight: 800;
      }   

      text.labelText2, text.labelText3,
      text.labelText22, text.labelText23,
      text.labelText4, text.labelText24
       {
        fill: #eee;
        font-size: 13px;
      }

      text.labelText0, text.labelText1,
      text.labelText20, text.labelText21 {
        fill: #eee;
        font-size: 13px;
      }  

      .termText {
        fill: #888;
        font-size: 16px;
      }

      #chart {
        margin-top: 300px;
        margin-left: -10px;
      }
     
    </style>
  </head>
  <body>

    <div id="chart"></div>
    <script type="text/javascript" src="spiral6.js"></script>
  </body>
</html>

spiral6.js

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))
	
  });
}