class Stat { constructor(year, incidents, charges, adult, youth) { this.year = year; this.incidents = incidents; this.charges = charges; this.youth = youth; this.adult = adult; statArray.push(this) } } const statArray = []; const travellerDataRaw = ` [ { "Date": "11/1/2017", "Travellers": "31725" }, { "Date": "12/1/2017", "Travellers": "39722" }, { "Date": "1/1/2018", "Travellers": "41652" }, { "Date": "2/1/2018", "Travellers": "56086" }, { "Date": "3/1/2018", "Travellers": "33296" }, { "Date": "4/1/2018", "Travellers": "40565" }, { "Date": "5/1/2018", "Travellers": "63095" }, { "Date": "6/1/2018", "Travellers": "76199" }, { "Date": "7/1/2018", "Travellers": "103091" }, { "Date": "8/1/2018", "Travellers": "102247" }, { "Date": "9/1/2018", "Travellers": "97179" }, { "Date": "10/1/2018", "Travellers": "66313" }, { "Date": "11/1/2018", "Travellers": "34157" }, { "Date": "12/1/2018", "Travellers": "43325" }, { "Date": "1/1/2019", "Travellers": "56652" }, { "Date": "2/1/2019", "Travellers": "45960" }, { "Date": "3/1/2019", "Travellers": "34209" }, { "Date": "4/1/2019", "Travellers": "42645" }, { "Date": "5/1/2019", "Travellers": "62156" }, { "Date": "6/1/2019", "Travellers": "77410" }, { "Date": "7/1/2019", "Travellers": "104441" }, { "Date": "8/1/2019", "Travellers": "106527" }, { "Date": "9/1/2019", "Travellers": "91968" }, { "Date": "10/1/2019", "Travellers": "60821" }, { "Date": "11/1/2019", "Travellers": "29899" } ]` let travellerData =JSON.parse(travellerDataRaw); console.log(travellerData) const chartSize = { width: 350, height: 350 } const margin = { top: 0, right: 20, left: 60, bottom: 60 } const width = chartSize.width - margin.left - margin.right; const height = chartSize.height - margin.top - margin.bottom; function makeChart(container, data, [x, date], y, title, subtitle) { const headline = d3.select(container).append('p') .attr('class', 'chart-title') .text(title) const subHeadline = d3.select(container).append('p') .attr('class', 'chart-subtitle') .text(subtitle) const chart = d3.select(container).append('svg') .attr('class', 'svg-chart') .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // find max and min of data function getMin(N) { return data.reduce((min, b) => Math.min(min, b[N]), data[0][N]); } function getMax(N) { return data.reduce((max, b) => Math.max(max, b[N]), data[0][N]); } const limits = { x: { min: 0, max: 0 }, y: { min: 0, max: 0 } }; function capFirst(string) { return string.split(' ').map((s) => s.charAt(0).toUpperCase() + s.substring(1)).join(' '); } function numberWithCommas(x) { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } y.forEach(plot => { limits.y.min = getMin(plot) < limits.y.min ? getMin(plot) : limits.y.min; limits.y.max = getMax(plot) > limits.y.max ? getMax(plot) : limits.y.max; }) const legend = chart.append('g') .attr('class', 'chart-legend') if (date) { data.forEach(point => { point['rawDate'] = point[x]; const year = point[x].split('/')[2]; const month = point[x].split('/')[0] - 1; const day = point[x].split('/')[1]; point[x] = new Date(year, month, day) }) } const xScale = d3.scaleLinear() .domain([getMin(x), getMax(x)]) // input .range([0, width]); // output /* const xScale = d3.scaleLinear() .domain([new Date(2018, 0, 1), new Date(2019, 10, 1)]) // input .range([0, width]); // output */ const yScale = d3.scaleLinear() .domain([limits.y.min, limits.y.max*1.2]) // input .range([height, 0]); // output let xAxis = chart.append('g') .attr('class', 'xAxis chart-axis') .attr('transform', `translate(0, ${height})`) .call(d3.axisBottom() .scale(xScale) .ticks(data.length/2) .tickFormat(d3.timeFormat("%m/%Y")) ) .selectAll("text") .style("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", ".15em") .attr("transform", "rotate(-65)"); let yAxis = chart.append('g') .attr('class', 'yAxis chart-axis') .call(d3.axisLeft() .scale(yScale) .ticks(5) ) y.forEach((plot, i) => { let layer = chart.append('g') const lineZero = d3.line() .x(function(d, i) { return xScale(d[x]); }) .y(function(d) { return yScale(0); }) .curve(d3.curveMonotoneX) const line = d3.line() .x(function(d, i) { return xScale(d[x]); }) .y(function(d) { return yScale(d[plot]); }) .curve(d3.curveMonotoneX) layer.append('path') .datum(data) .attr('class', `layer-${i+1}-line chart-line`) .attr('fill', 'none') .attr('d', lineZero) .transition().duration(750) .attr('d', line) layer.selectAll('circle') .data(data) .enter() .append('circle') .attr('class', `layer-${i+1}-dots chart-dots`) .attr('cx', d => xScale(d[x])) .attr('cy', d => yScale(0)) .attr('r', 5) .transition().duration(750) .attr('cy', d => yScale(d[plot])) let hoverLayer = chart.append('g') var tool_tip = d3.tip() .attr("class", "chart-tooltip") .offset([-4, 0]) .html(d => { let content = `
${capFirst(y[i])} (${d[x].getMonth() + 1}/${d[x].getFullYear()})
${numberWithCommas(d[plot])}
` return content }); hoverLayer.call(tool_tip); hoverLayer.selectAll('circle') .data(data) .enter() .append('circle') .attr('class', `hover-dots`) .attr('cx', d => xScale(d[x])) .attr('cy', d => yScale(d[plot])) .attr('r', 12) .attr('fill', '#bbbbbb00') .on('mouseover', tool_tip.show) .on('mouseout', tool_tip.hide); let legendLayer = legend.append('g') legendLayer.append('path') .attr('class', `legend-line layer-${i+1}-line chart-line`) .attr('fill', 'none') .attr('d', `M${20} ${10 + i*20} H55`) legendLayer.append('circle') .attr('class', `legend-dots layer-${i+1}-dots chart-dots`) .attr('cx', 37.5) .attr('cy', 10 + i*20) .attr('r', 5) legendLayer.append('text') .attr('x', 65) .attr('y', 13 + i*20) .text(capFirst(y[i])) }) let chartSource = d3.select(container).append('div') .attr('class', 'chart-source') .html(`Source: Statistics Canada`) }