<script>
import { area, curveLinear, Delaunay, range, scaleLinear, scaleUtc } from 'd3';
import data from './area-data';
const marginTop = $ChartDocs[0].value;
const marginRight = $ChartDocs[1].value;
const marginBottom = $ChartDocs[2].value;
const marginLeft = $ChartDocs[3].value;
const inset = $ChartDocs[4].value;
const width = $ChartDocs[5].value;
const height = $ChartDocs[6].value;
const xLabel = $ChartDocs[7].value;
const yLabel = $ChartDocs[8].value;
const xFormat = $ChartDocs[9].value;
const yFormat = $ChartDocs[10].value;
const horizontalGrid = $ChartDocs[11].value;
const verticalGrid = $ChartDocs[12].value;
const colors = $ChartDocs[13].value;
const showDots = $ChartDocs[14].value;
const dotsFilled = $ChartDocs[15].value;
const r = $ChartDocs[16].value;
const strokeWidth = $ChartDocs[17].value;
const fillOpacity = $ChartDocs[18].value;
const tooltipBackground = $ChartDocs[19].value;
const tooltipTextColor = $ChartDocs[20].value;
const strokeLinecap = 'round';
const strokeLinejoin = 'round';
const xScalefactor = width / 80;
const yScalefactor = height / 40;
const curve = curveLinear;
const xType = scaleUtc;
const insetTop = inset;
const insetRight = inset;
const insetBottom = inset;
const insetLeft = inset;
const xRange = [marginLeft + insetLeft, width - marginRight - insetRight];
const yType = scaleLinear;
const yRange = [height - marginBottom - insetBottom, marginTop + insetTop];
let x, y, dotInfo, areas, filteredI, xVals = [], yVals = [], points = [], subsets = [], colorVals = [];
if (!('data' in data[0])) {
x = Object.keys(data[0])[0];
y = Object.keys(data[0])[1];
xVals = data.map((el) => el[x]);
yVals = data.map((el) => el[y]);
colorVals = data.map((el) => 0);
points = data.map((el) => ({
x: el[x],
y: el[y],
color: 0
}));
}
else {
x = Object.keys(data[0]?.data[0])[0];
y = Object.keys(data[0]?.data[0])[1];
data.forEach((subset, i) => {
subset.data.forEach((coordinate) => {
xVals.push(coordinate[x]);
yVals.push(coordinate[y]);
colorVals.push(i);
points.push(
{
x: coordinate[x],
y: coordinate[y],
color: i
});
});
subsets.push(subset.id);
});
}
const I = range(xVals.length);
const gaps = (d, i) => !isNaN(xVals[i]) && !isNaN(yVals[i]);
const cleanData = points.map(gaps);
const xDomain = [xVals[0], xVals[xVals.length - 1]];
const yDomain = [0, Math.max(...yVals)];
const xScale = xType(xDomain, xRange);
const yScale = yType(yDomain, yRange);
const niceY = scaleLinear().domain([0, Math.max(...yVals)]).nice();
const chartArea = area()
.defined(i => cleanData[i])
.curve(curve)
.x(i => xScale(xVals[i]))
.y0(yScale(0))
.y1(i => yScale(yVals[i]));
$: {
areas = [];
colors.forEach((color, j) => {
filteredI = [];
filteredI = I.filter((el, i) => colorVals[i] === j);
areas.push(chartArea(filteredI));
});
}
const pointsScaled = points.map((el) => [xScale(el.x), yScale(el.y), el.color]);
const delaunayGrid = Delaunay.from(pointsScaled);
const voronoiGrid = delaunayGrid.voronoi([0, 0, width, height]);
const xTicks = xScale.ticks(xScalefactor);
const xTicksFormatted = xTicks.map((el) => el.getFullYear());
const yTicks = niceY.ticks(yScalefactor);
</script>
<div class="chart-container">
<svg {width} {height} viewBox="0 0 {width} {height}"
cursor='crosshair'
on:mouseout="{() => dotInfo = null}"
on:blur="{() => dotInfo = null}"
>
<!-- Dots (if enabled) -->
{#if showDots && !dotInfo}
{#each I as i}
<g class='dot' pointer-events='none'>
<circle
cx={xScale(xVals[i])}
cy={yScale(yVals[i])}
r={r}
stroke={colors[colorVals[i]]}
fill={dotsFilled ? colors[colorVals[i]] : 'none'}
/>
</g>
{/each}
{/if}
<!-- Chart Areas -->
{#each areas as subsetArea, i}
<g class='chartlines' pointer-events='none'>
{#if dotInfo}
<path class="line" fill={colors[i]} fill-opacity={points[dotInfo[1]].color === i ? '0.5' : '0.1'} stroke={colors[i]} d={subsetArea} />
<circle cx={xScale(points[dotInfo[1]].x)} cy={yScale(points[dotInfo[1]].y)} r=3 stroke={colors[points[dotInfo[1]].color]} fill='none' />
{:else}
<path class="line" fill={colors[i]} stroke={colors[i]} d={subsetArea}
fill-opacity={fillOpacity} stroke-width={strokeWidth} stroke-linecap={strokeLinecap} stroke-linejoin={strokeLinejoin} />
{/if}
</g>
{/each}
<!-- Y-axis and horizontal grid lines -->
<g class="y-axis" transform="translate({marginLeft}, 0)" pointer-events='none'>
<path class="domain" stroke="black" d="M{insetLeft}, {marginTop} V{height - marginBottom + 6}"/>
{#each yTicks as tick, i}
<g class="tick" transform="translate(0, {yScale(tick)})">
<line class="tick-start" x1={insetLeft - 6} x2={insetLeft}/>
{#if horizontalGrid}
<line class="tick-grid" x1={insetLeft} x2={width - marginLeft - marginRight}/>
{/if}
<text text-align="right"x="-{marginLeft}" y="5">{tick + yFormat}</text>
</g>
{/each}
<text x="-{marginLeft}" y={marginTop - 10}>{yLabel}</text>
</g>
<!-- X-axis and vertical grid lines -->
<g class="x-axis" transform="translate(0,{height - marginBottom - insetBottom})" pointer-events='none'>
<path class="domain" stroke="black" d="M{marginLeft},0.5 H{width - marginRight}"/>
{#each xTicks as tick, i}
<g class="tick" transform="translate({xScale(tick)}, 0)">
<line class="tick-start" stroke='black' y2='6' />
{#if verticalGrid}
<line class="tick-grid" y2={-height} />
{/if}
<text font-size='8px' x={-marginLeft/4} y="20">{xTicksFormatted[i] + xFormat}</text>
</g>
{/each}
<text x={width - marginLeft - marginRight - 40} y={marginBottom}>{xLabel}</text>
</g>
{#each pointsScaled as point, i}
<path
stroke="none"
fill-opacity="0"
class="voronoi-cell"
d={voronoiGrid.renderCell(i)}
on:mouseover="{(e) => dotInfo = [point, i, e] }"
on:focus="{(e) => dotInfo = [point, i, e] }"
></path>
{/each}
</svg>
</div>
<!-- Tooltip -->
{#if dotInfo}
<div class="tooltip" style='position:absolute; left:{dotInfo[2].clientX + 12}px; top:{dotInfo[2].clientY + 12}px; pointer-events:none; background-color:{tooltipBackground}; color:{tooltipTextColor}'>
{subsets ? subsets[points[dotInfo[1]].color] : ''}:
{points[dotInfo[1]].x.getFullYear()}: {points[dotInfo[1]].y.toFixed(2)}{yFormat}
<!-- {points[dotInfo[1]].x.toLocaleDateString('en-US')} {points[dotInfo[1]].y.toFixed(2)}{yFormat} -->
</div>
{/if}
<style>
.chart-container {
justify-content: center;
align-items: center;
margin-top: 50px;
margin-left: 8
0px;
}
svg {
max-width: 100%;
height: auto;
height: "intrinsic";
margin: auto;
}
path {
fill: "green"
}
.y-axis {
font-size: "10px";
font-family: sans-serif;
text-anchor: "end";
}
.x-axis {
font-size: "10px";
font-family: sans-serif;
text-anchor: "end";
}
.tick {
opacity: 1;
}
.tick-start {
stroke: black;
stroke-opacity: 1;
}
.tick-grid {
stroke: black;
stroke-opacity: 0.2;
font-size: "11px";
color: black;
}
.tick text {
fill: black;
text-anchor: start;
}
.tooltip{
border-radius: 5px;
padding: 5px;
box-shadow: rgba(0, 0, 0, 0.09) 0px 2px 1px, rgba(0, 0, 0, 0.09) 0px 4px 2px, rgba(0, 0, 0, 0.09) 0px 8px 4px, rgba(0, 0, 0, 0.09) 0px 16px 8px, rgba(0, 0, 0, 0.09) 0px 32px 16px;
}
</style>
const csvNortheast =
`Year,Population
1920,29.662053
1930,34.427091
1940,35.976777
1950,39.477986
1960,44.677819
1970,49.040703
1980,49.135283
1990,50.809229
2000,53.594378
2010,55.317240
2020,57.609148`;
const csvMidwest =
`Year,Population
1920,34.019792
1930,38.594100
1940,40.143332
1950,44.460762
1960,51.619139
1970,56.571663
1980,58.865670
1990,59.668632
2000,64.392776
2010,66.927001
2020,68.985454`;
const csvSouth =
`Year,Population
1920,33.125803
1930,37.857633
1940,41.665901
1950,47.197088
1960,54.973113
1970,62.795367
1980,75.372362
1990,85.445930
2000,100.236820
2010,114.555744
2020,126.266107`;
const csvWest =
`Year,Population
1920,9.213920
1930,12.323836
1940,14.379119
1950,20.189962
1960,28.053104
1970,34.804193
1980,43.172490
1990,52.786082
2000,63.197932
2010,71.945553
2020,78.588572`;
function csvConvert(csv) {
return csv.split('\n').slice(1).map(str => {
const [date, population] = str.split(',')
.map((el) => (el > 1900 ? new Date(el, 0) : parseFloat(el)));
return { date, population };
});
}
const northeast = csvConvert(csvNortheast);
const midwest = csvConvert(csvMidwest);
const south = csvConvert(csvSouth);
const west = csvConvert(csvWest);
export default [
{
id: 'Northeast',
data: northeast
},
{
id: 'Midwest',
data: midwest
},
{
id: 'South',
data: south
},
{
id: 'West',
data: west
}
]
const firstDataSet =
"title_X,title_Y
x-value, y-value
x-value, y-value
x-value, y-value
const secondDataSet =
"title_X,title_Y
x-value, y-value
x-value, y-value
x-value, y-value
const thirdDataSet =
"title_X,title_Y
x-value, y-value
x-value, y-value
x-value, y-value
export { firstDataSet, secondDataSet, thirdDataSet };