Commit 3a6cef04 authored by Almouhannad Hafez's avatar Almouhannad Hafez

Use Chart factory

parent 88f5d2aa
...@@ -9,7 +9,16 @@ ...@@ -9,7 +9,16 @@
<body> <body>
<div id="scatterplot-container"> <div id="chart-select">
<label for="chart">Select chart:</label>
<select id="charts-select-list" name="charts-select-list">
<option value="" disabled selected></option>
<option value="Scatterplot">Scatterplot</option>
<option value="LineChart">Line chart</option>
</select>
</div>
<div id="Scatterplot-container" style="display: none;">
<div id="scatterplot-tooltip"></div> <div id="scatterplot-tooltip"></div>
<svg id="scatterplot"></svg> <svg id="scatterplot"></svg>
<ul class="legend" id="scatterplot-legend"> <ul class="legend" id="scatterplot-legend">
...@@ -20,7 +29,7 @@ ...@@ -20,7 +29,7 @@
</ul> </ul>
</div> </div>
<div id="line-chart-container"> <div id="LineChart-container" style="display: none;">
<div id="line-chart-tooltip"></div> <div id="line-chart-tooltip"></div>
<div class="heading"> <div class="heading">
<p class="title">S&P 500 Index</p> <p class="title">S&P 500 Index</p>
......
...@@ -8,35 +8,72 @@ body { ...@@ -8,35 +8,72 @@ body {
background: #f7f7f7; background: #f7f7f7;
} }
#chart-select {
margin-left: 2em;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 300px;
text-align: center;
}
label {
font-size: 1.2em;
margin-bottom: 10px;
display: block;
color: #333;
}
select {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 1em;
color: #555;
background-color: #f9f9f9;
transition: border-color 0.3s;
}
select:focus {
border-color: #007BFF;
outline: none;
background-color: #fff;
}
option {
padding: 10px;
}
/* #region scatterplot*/ /* #region scatterplot*/
#scatterplot-container { #Scatterplot-container {
padding: 1em; padding: 1em;
} }
#scatterplot-container .source { #Scatterplot-container .source {
font-size: 10px; font-size: 10px;
color: #888; color: #888;
} }
#scatterplot-container .source a { #Scatterplot-container .source a {
color: #888; color: #888;
} }
/* Axes */ /* Axes */
#scatterplot-container .axis line { #Scatterplot-container .axis line {
fill: none; fill: none;
stroke: #ddd; stroke: #ddd;
shape-rendering: crispEdges; shape-rendering: crispEdges;
} }
#scatterplot-container .axis text { #Scatterplot-container .axis text {
font-size: 13px; font-size: 13px;
fill: #6b6b6b; fill: #6b6b6b;
} }
#scatterplot-container .axis-title { #Scatterplot-container .axis-title {
font-size: 13px; font-size: 13px;
fill: #888; fill: #888;
} }
...@@ -45,50 +82,50 @@ body { ...@@ -45,50 +82,50 @@ body {
stroke: #b1b1b1; stroke: #b1b1b1;
} }
#scatterplot-container .y-axis .tick:first-child text { #Scatterplot-container .y-axis .tick:first-child text {
display: none; display: none;
} }
#scatterplot-container .x-axis .tick:first-child line { #Scatterplot-container .x-axis .tick:first-child line {
display: none; display: none;
} }
#scatterplot-container .axis path { #Scatterplot-container .axis path {
display: none; display: none;
} }
/* Legend */ /* Legend */
#scatterplot-container .legend { #Scatterplot-container .legend {
margin: 20px 0; margin: 20px 0;
list-style: none; list-style: none;
padding: 0; padding: 0;
} }
#scatterplot-container .legend li { #Scatterplot-container .legend li {
display: inline-block; display: inline-block;
margin: 0 20px 0 0; margin: 0 20px 0 0;
} }
#scatterplot-container .legend-e { #Scatterplot-container .legend-e {
width: 12px; width: 12px;
height: 12px; height: 12px;
margin-right: 3px; margin-right: 3px;
display: inline-block; display: inline-block;
} }
#scatterplot-container .legend-e.easy { #Scatterplot-container .legend-e.easy {
background: #d3eecd; background: #d3eecd;
} }
#scatterplot-container .legend-e.intermediate { #Scatterplot-container .legend-e.intermediate {
background: #7bc77e; background: #7bc77e;
} }
#scatterplot-container .legend-e.difficult { #Scatterplot-container .legend-e.difficult {
background: #2a8d46; background: #2a8d46;
} }
#scatterplot-container p { #Scatterplot-container p {
font-weight: 900; font-weight: 900;
color: #2a8d46; color: #2a8d46;
} }
...@@ -117,28 +154,28 @@ body { ...@@ -117,28 +154,28 @@ body {
/* #region Area chart */ /* #region Area chart */
#line-chart-container .area { #LineChart-container .area {
fill: #e9eff5; fill: #e9eff5;
position: relative; position: relative;
} }
#line-chart-container .line { #LineChart-container .line {
fill: none; fill: none;
stroke: #537591; stroke: #537591;
stroke-width: 2.5px; stroke-width: 2.5px;
} }
#line-chart-container .title { #LineChart-container .title {
font-weight: 900; font-weight: 900;
font-size: 40px; font-size: 40px;
} }
#line-chart-container .select-text { #LineChart-container .select-text {
font-weight: 900; font-weight: 900;
margin-right: 0.5em; margin-right: 0.5em;
} }
#line-chart-container .heading { #LineChart-container .heading {
margin-left: 3em; margin-left: 3em;
} }
......
import { Chart } from "./chart";
import { ChartConfiguration } from "./chartConfiguration";
import { ChartNames } from "./ChartsNames";
import { LineChart } from "./lineChart";
import { Scatterplot } from "./scatter";
export class ChartFactory {
private static chartMap: Map<string, { instance: Chart; datasetName: string }> = new Map();
private static scatterPlot: Chart = new Scatterplot(new ChartConfiguration("#scatterplot"));
private static lineChart: Chart = new LineChart(new ChartConfiguration("#line-chart"));
static {
this.chartMap.set(ChartNames.SCATTER, { instance: this.scatterPlot, datasetName: "/data/vancouver_trails.csv" });
this.chartMap.set(ChartNames.LINE_CHART, { instance: this.lineChart, datasetName: "/data/sp_500_index.csv" });
}
static createChart(chartName: string): { instance: Chart; datasetName: string } {
const chartInstance = this.chartMap.get(chartName);
if (chartInstance) {
return chartInstance;
}
else throw new Error("No such chart");
}
}
export class ChartNames {
static readonly SCATTER = 'Scatterplot';
static readonly LINE_CHART = 'LineChart';
static readonly CHARTS = [this.SCATTER, this.LINE_CHART];
}
\ No newline at end of file
...@@ -156,6 +156,13 @@ export class LineChart extends Chart { ...@@ -156,6 +156,13 @@ export class LineChart extends Chart {
public updateVis(): void { public updateVis(): void {
const vis = this; const vis = this;
const dateParser = d3.timeParse("%Y-%m-%d");
vis.data.forEach((d: any) => {
if (typeof (d.date) == "string")
d.date = dateParser(d.date);
d.close = +d.close;
});
// Specificy accessor functions // Specificy accessor functions
vis.xValue = (d: any) => d.date; vis.xValue = (d: any) => d.date;
vis.yValue = (d: any) => d.close; vis.yValue = (d: any) => d.close;
......
import { Chart } from './chart';
import { ChartConfiguration } from './chartConfiguration'; import { ChartFactory } from './ChartFactory';
import { LineChart } from './lineChart'; import { ChartNames } from './ChartsNames';
import { Scatterplot } from './scatter';
import '/src/css/style.css'; import '/src/css/style.css';
import * as d3 from 'd3'; import * as d3 from 'd3';
const selectedDifficulties: string[] = [];
let data: any[] = []; let data: any[] = [];
const scatterplot = new Scatterplot(new ChartConfiguration("#scatterplot")); let chart: Chart;
d3.select("#charts-select-list")
.on("change", function () {
let selectedChart: string = d3.select(this).property('value');
let chartInstance = ChartFactory.createChart(selectedChart);
chart = chartInstance.instance;
for (const chartName of ChartNames.CHARTS) {
d3.select(`#${chartName}-container`).style("display", "none");
}
d3.select(`#${selectedChart}-container`).style("display", "block");
/**
* Load data from CSV file
*/
d3.csv(chartInstance.datasetName)
.then(inputData => {
data = inputData;
// Initialize chart
chart.data = inputData;
// Show chart
chart.updateVis();
})
.catch(error => console.error(error));
});
////////////////////// Events handlers //////////////////////
// #region Scatterplot
const selectedDifficulties: string[] = [];
function handleLevelSelectionEvent(element: any, difficulty: string) { function handleLevelSelectionEvent(element: any, difficulty: string) {
const index = selectedDifficulties.indexOf(difficulty); const index = selectedDifficulties.indexOf(difficulty);
if (index > -1) { if (index > -1) {
...@@ -20,8 +48,8 @@ function handleLevelSelectionEvent(element: any, difficulty: string) { ...@@ -20,8 +48,8 @@ function handleLevelSelectionEvent(element: any, difficulty: string) {
d3.select(element).style("opacity", 0.5); d3.select(element).style("opacity", 0.5);
} }
const filteredData = data.filter(item => !selectedDifficulties.includes(item.difficulty)); const filteredData = data.filter(item => !selectedDifficulties.includes(item.difficulty));
scatterplot.data = filteredData; chart.data = filteredData;
scatterplot.updateVis(); chart.updateVis();
} }
const difficulties = ["Easy", "Intermediate", "Difficult"] const difficulties = ["Easy", "Intermediate", "Difficult"]
// Add click event listeners for each difficulty level // Add click event listeners for each difficulty level
...@@ -32,52 +60,17 @@ for (const level of difficulties) { ...@@ -32,52 +60,17 @@ for (const level of difficulties) {
handleLevelSelectionEvent(this, level); handleLevelSelectionEvent(this, level);
}); });
} }
/** // #endregion
* Load data from CSV file asynchronously and render scatter plot
*/
d3.csv('/data/vancouver_trails.csv')
.then(inputData => {
// Convert strings to numbers
inputData.forEach((d: any) => {
d.time = +d.time;
d.distance = +d.distance;
});
data = inputData;
// Initialize chart
scatterplot.data = inputData;
// Show chart
scatterplot.updateVis();
})
.catch(error => console.error(error));
/**
* Load data from CSV file asynchronously and render scatter plot
*/
const lineChart = new LineChart(new ChartConfiguration("#line-chart")); // #region LineChart
let lineChartData: any[];
d3.select('#line-chart-year-input') d3.select('#line-chart-year-input')
.on('input', function () { .on('input', function () {
// Get the current value of the input // Get the current value of the input
let value = d3.select(this).property('value'); let value = d3.select(this).property('value');
value = +value; value = +value;
console.log(lineChartData[0].date); const lineChartFilteredData = data.filter(item => item.date.getFullYear() >= value);
const lineChartFilteredData = lineChartData.filter(item => item.date.getFullYear() >= value); chart.data = lineChartFilteredData;
lineChart.data = lineChartFilteredData; chart.updateVis();
lineChart.updateVis();
}); });
// #endregion
d3.csv('/data/sp_500_index.csv')
.then(inputData => {
const dateParser = d3.timeParse("%Y-%m-%d");
inputData.forEach((d: any) => {
d.date = dateParser(d.date);
d.close = +d.close;
});
lineChartData = inputData;
lineChart.data = inputData;
lineChart.updateVis();
})
.catch(error => console.error(error));
...@@ -83,6 +83,10 @@ export class Scatterplot extends Chart { ...@@ -83,6 +83,10 @@ export class Scatterplot extends Chart {
public updateVis(): void { public updateVis(): void {
let vis = this; let vis = this;
vis.data.forEach((d: any) => {
d.time = +d.time;
d.distance = +d.distance;
});
// Specificy accessor functions // Specificy accessor functions
vis.colorValue = d => d.difficulty; vis.colorValue = d => d.difficulty;
vis.xValue = d => d.time; vis.xValue = d => d.time;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment