-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmapscript.js
More file actions
217 lines (184 loc) · 8.81 KB
/
Copy pathmapscript.js
File metadata and controls
217 lines (184 loc) · 8.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Load data and initialize the map
Promise.all([
d3.csv("healthProcessedData.csv"),
d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json") // US states TopoJSON
]).then(([data, us]) => {
console.log("Processed Data:", data); // Debug: Check processed data
console.log("US TopoJSON:", us); // Debug: Check TopoJSON data
// Map state abbreviations to full state names
const stateAbbrToName = {
"AL": "Alabama", "AK": "Alaska", "AZ": "Arizona", "AR": "Arkansas", "CA": "California",
"CO": "Colorado", "CT": "Connecticut", "DE": "Delaware", "FL": "Florida", "GA": "Georgia",
"HI": "Hawaii", "ID": "Idaho", "IL": "Illinois", "IN": "Indiana", "IA": "Iowa",
"KS": "Kansas", "KY": "Kentucky", "LA": "Louisiana", "ME": "Maine", "MD": "Maryland",
"MA": "Massachusetts", "MI": "Michigan", "MN": "Minnesota", "MS": "Mississippi", "MO": "Missouri",
"MT": "Montana", "NE": "Nebraska", "NV": "Nevada", "NH": "New Hampshire", "NJ": "New Jersey",
"NM": "New Mexico", "NY": "New York", "NC": "North Carolina", "ND": "North Dakota", "OH": "Ohio",
"OK": "Oklahoma", "OR": "Oregon", "PA": "Pennsylvania", "RI": "Rhode Island", "SC": "South Carolina",
"SD": "South Dakota", "TN": "Tennessee", "TX": "Texas", "UT": "Utah", "VT": "Vermont",
"VA": "Virginia", "WA": "Washington", "WV": "West Virginia", "WI": "Wisconsin", "WY": "Wyoming"
};
// Parse year and search_count as numbers
data.forEach(d => {
d.year = +d.year; // Convert year to a number
d.search_count = +d.search_count; // Convert search_count to a number
});
// Get unique conditions
const conditions = [...new Set(data.map(d => d.condition))];
conditions.sort(); // Sort conditions alphabetically
// Populate the condition dropdown
const conditionSelect = d3.select("#condition-select");
conditionSelect.selectAll("option")
.data(conditions)
.enter()
.append("option")
.attr("value", d => d)
.text(d => d);
// Group data by year, state, and condition
const groupedData = d3.rollup(
data,
v => d3.sum(v, d => d.search_count), // Sum search counts
d => d.year, // Group by year
d => d.state_abb, // Group by state abbreviation
d => d.condition // Group by condition
);
// Group data for "All Years" (sum across all years)
const allYearsData = d3.rollup(
data,
v => d3.sum(v, d => d.search_count), // Sum search counts
d => d.state_abb, // Group by state abbreviation
d => d.condition // Group by condition
);
console.log("Grouped Data:", groupedData); // Debug: Check grouped data
console.log("All Years Data:", allYearsData); // Debug: Check all years data
// Get unique years
const years = Array.from(groupedData.keys()).sort();
// Update radio buttons dynamically
const yearSelector = d3.select("#year-selector");
yearSelector.selectAll("label")
.data(["All Years", ...years]) // Add "All Years" option
.enter()
.append("label")
.html(d => `<input type="radio" name="year" value="${d}"> ${d}`);
// Create the bubble map
const { updateBubbleMap } = createBubbleMap(us, groupedData, allYearsData, stateAbbrToName, years);
// Add event listener for year selection
yearSelector.selectAll("input")
.on("change", function () {
const selectedYear = this.value; // Get selected year or "All Years"
const selectedCondition = conditionSelect.node().value; // Get selected condition
updateBubbleMap(selectedYear, selectedCondition);
});
// Add event listener for condition selection
conditionSelect.on("change", function () {
const selectedYear = yearSelector.select("input:checked").node().value; // Get selected year or "All Years"
const selectedCondition = this.value; // Get selected condition
updateBubbleMap(selectedYear, selectedCondition);
});
});
// Function to create the bubble map
function createBubbleMap(us, groupedData, allYearsData, stateAbbrToName, years) {
const width = 975;
const height = 610;
// Create the SVG container.
const svg = d3.select("#map-container")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "width: 100%; height: auto; height: intrinsic;");
// Create a projection for the US map.
const projection = d3.geoAlbersUsa()
.translate([width / 2, height / 2])
.scale(width);
// Create a path generator.
const path = d3.geoPath().projection(projection);
// Create the cartographic background layers.
svg.append("path")
.datum(topojson.feature(us, us.objects.states))
.attr("fill", "#ddd")
.attr("d", path);
svg.append("path")
.datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b))
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("d", path);
// Create a group for the bubbles.
const bubbles = svg.append("g");
// Function to update the bubble map for a selected year and condition.
function updateBubbleMap(selectedYear, selectedCondition) {
let yearData;
if (selectedYear === "All Years") {
// Use data for all years
yearData = allYearsData;
} else {
// Use data for the selected year
yearData = groupedData.get(+selectedYear);
}
if (!yearData) {
console.error("No data found for year:", selectedYear);
return;
}
// Filter data based on the selected condition
const formattedYearData = Array.from(yearData, ([stateAbbr, conditions]) => {
const searchCount = selectedCondition === "All"
? d3.sum(Array.from(conditions.values())) // Sum all conditions if "All" is selected
: conditions.get(selectedCondition) || 0; // Get specific condition count
return {
stateAbbr,
stateName: stateAbbrToName[stateAbbr], // Map abbreviation to full name
searchCount
};
}).filter(d => d.stateName && d.searchCount > 0); // Filter out states without a matching name or zero count
console.log("Year Data:", formattedYearData); // Debug: Check year data
// Construct the radius scale.
const radius = d3.scaleSqrt()
.domain([0, d3.max(formattedYearData, d => d.searchCount)])
.range([0, 40]);
// Join the data and update the bubbles.
const bubble = bubbles.selectAll("circle")
.data(formattedYearData, d => d.stateAbbr);
bubble.enter()
.append("circle")
.attr("fill", "brown")
.attr("fill-opacity", 0.5)
.attr("stroke", "#fff")
.attr("stroke-width", 0.5)
.on("mouseover", function (event, d) {
// Highlight the bubble
d3.select(this)
.attr("fill-opacity", 1)
.attr("stroke", "#333")
.attr("stroke-width", 2);
// Show tooltip
tooltip.style("opacity", 1)
.html(`State: ${d.stateName}<br>Abbreviation: ${d.stateAbbr}<br>Search Count: ${d3.format(",.0f")(d.searchCount)}`)
.style("left", `${event.pageX + 5}px`)
.style("top", `${event.pageY - 28}px`);
})
.on("mouseout", function () {
// Restore the bubble's appearance
d3.select(this)
.attr("fill-opacity", 0.5)
.attr("stroke", "#fff")
.attr("stroke-width", 0.5);
// Hide tooltip
tooltip.style("opacity", 0);
})
.merge(bubble)
.attr("transform", d => {
const centroid = path.centroid(topojson.feature(us, us.objects.states).features.find(f => f.properties.name === d.stateName));
return `translate(${centroid})`;
})
.attr("r", d => radius(d.searchCount));
bubble.exit().remove();
}
// Create a tooltip
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Initialize the map with "All Years" and "All" condition
updateBubbleMap("All Years", "All");
return { updateBubbleMap };
}