|
| 1 | +const margin = { top: 40, bottom: 10, left: 120, right: 20 }; |
| 2 | +const width = 800 - margin.left - margin.right; |
| 3 | +const height = 600 - margin.top - margin.bottom; |
| 4 | + |
| 5 | +// Creates sources <svg> element |
| 6 | +const svg = d3 |
| 7 | + .select("body") |
| 8 | + .append("svg") |
| 9 | + .attr("width", width + margin.left + margin.right) |
| 10 | + .attr("height", height + margin.top + margin.bottom); |
| 11 | + |
| 12 | +// Group used to enforce margin |
| 13 | +const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`); |
| 14 | +g.append("g").attr("class", "x axis"); |
| 15 | +g.append("g").attr("class", "y axis"); |
| 16 | + |
| 17 | +// delcare the Data Element Type for proper typing |
| 18 | +interface IElem { |
| 19 | + temperature: number; |
| 20 | + location: { |
| 21 | + country: string; |
| 22 | + city: string; |
| 23 | + }; |
| 24 | +} |
| 25 | + |
| 26 | +// Global variable for all data |
| 27 | +let data: IElem[]; |
| 28 | + |
| 29 | +// Scales setup |
| 30 | +const xscale = d3.scaleLinear().range([0, width]); |
| 31 | +const yscale = d3 |
| 32 | + .scaleBand() |
| 33 | + .rangeRound([0, height]) |
| 34 | + .paddingInner(0.1); |
| 35 | + |
| 36 | +// Axis setup |
| 37 | +const xaxis = d3.axisTop(xscale); |
| 38 | +const yaxis = d3.axisLeft(yscale); |
| 39 | + |
| 40 | +///////////////////////// |
| 41 | + |
| 42 | +d3.json("weather.json").then((json: IElem[]) => { |
| 43 | + data = json; |
| 44 | + |
| 45 | + update(data); |
| 46 | +}); |
| 47 | + |
| 48 | +function update(new_data: IElem[]) { |
| 49 | + //update the scales |
| 50 | + // need to use the ! to tell TypeScript that it will always return a number |
| 51 | + xscale.domain([0, d3.max(new_data, d => d.temperature)!]); |
| 52 | + yscale.domain(new_data.map(d => d.location.city)); |
| 53 | + //render the axis |
| 54 | + // specify the generic argument to enforce being a SVGGEelement |
| 55 | + g.select<SVGGElement>(".x.axis") |
| 56 | + .transition() |
| 57 | + .call(xaxis); |
| 58 | + g.select<SVGGElement>(".y.axis") |
| 59 | + .transition() |
| 60 | + .call(yaxis); |
| 61 | + |
| 62 | + // Render the chart with new data |
| 63 | + |
| 64 | + // DATA JOIN use the key argument for ensurign that the same DOM element is bound to the same data-item |
| 65 | + const rect = g |
| 66 | + .selectAll("rect") |
| 67 | + .data(new_data, d => (d as IElem).location.city) // key argument cannot be properly typed |
| 68 | + .join( |
| 69 | + // ENTER |
| 70 | + // new elements |
| 71 | + enter => { |
| 72 | + const rect_enter = enter.append("rect").attr("x", 0); |
| 73 | + rect_enter.append("title"); |
| 74 | + return rect_enter; |
| 75 | + }, |
| 76 | + // UPDATE |
| 77 | + // update existing elements |
| 78 | + update => update, |
| 79 | + // EXIT |
| 80 | + // elements that aren't associated with data |
| 81 | + exit => exit.remove() |
| 82 | + ); |
| 83 | + |
| 84 | + // ENTER + UPDATE |
| 85 | + // both old and new elements |
| 86 | + rect |
| 87 | + .transition() |
| 88 | + .attr("height", yscale.bandwidth()) |
| 89 | + .attr("width", d => xscale(d.temperature)!) |
| 90 | + .attr("y", d => yscale(d.location.city)!); |
| 91 | + |
| 92 | + rect.select("title").text(d => d.location.city); |
| 93 | +} |
| 94 | + |
| 95 | +//interactivity |
| 96 | +d3.select<HTMLInputElement, unknown>("#filter-us-only").on("change", function() { |
| 97 | + // This will be triggered when the user selects or unselects the checkbox |
| 98 | + // since we typed the select such that this is a HTMLInputElement we can just use the this context and have proper autocompletion |
| 99 | + const checked = this.checked; |
| 100 | + if (checked) { |
| 101 | + // Checkbox was just checked |
| 102 | + |
| 103 | + // Keep only data element whose country is US |
| 104 | + const filtered_data = data.filter(d => d.location.country === "US"); |
| 105 | + |
| 106 | + update(filtered_data); // Update the chart with the filtered data |
| 107 | + } else { |
| 108 | + // Checkbox was just unchecked |
| 109 | + update(data); // Update the chart with all the data we have |
| 110 | + } |
| 111 | +}); |
0 commit comments