Skip to content

Commit 497681d

Browse files
committed
Edition XVIII - D3.js update
1 parent 6b3a96b commit 497681d

24 files changed

Lines changed: 3491 additions & 0 deletions

File tree

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
# Map Background Scale
2+
3+
After the Euro 2024 final, our boss asked us to implement a map that colours each european country depending on their final position in the above mentioned competition.
4+
5+
![Final standings Euro 2025](./content/chart.png)
6+
7+
## Steps
8+
9+
- We will take as starting point our previous example: _00-render-map-hover_ let's copy the content and execute _npm start_
10+
11+
```bash
12+
npm start
13+
```
14+
15+
We have the following information
16+
17+
| Country | Result |
18+
| --------------------- | :------------: |
19+
| Spain | champion |
20+
| England | runner-up |
21+
| France | semifinals |
22+
| Netherlands | semifinals |
23+
| Germany | quarterfinals |
24+
| Portugal | quarterfinals |
25+
| Switzerland | quarterfinals |
26+
| Turkey | quarterfinals |
27+
| Italy | knockout-stage |
28+
| Belgium | knockout-stage |
29+
| Austria | knockout-stage |
30+
| Romania | knockout-stage |
31+
| Slovakia | knockout-stage |
32+
| Slovenia | knockout-stage |
33+
| Denmark | knockout-stage |
34+
| Georgia | knockout-stage |
35+
| Croatia | group-stage |
36+
| CzechRepublic | group-stage |
37+
| Hungary | group-stage |
38+
| Serbia | group-stage |
39+
| Poland | group-stage |
40+
| Scotland | group-stage |
41+
| Albania | group-stage |
42+
| Ukraine | group-stage |
43+
| Russia | DSQ |
44+
| Belarus | DSQ |
45+
46+
- Let's port it to json (respecting the name of the countries that we are using in our eurobasket map)
47+
48+
_./src/stats.ts_:
49+
50+
```typescript
51+
export const finalStandingsEuro2024 = [
52+
{ country: "Spain", result: "champion" },
53+
{ country: "England", result: "runner-up" },
54+
{ country: "France", result: "semifinals" },
55+
{ country: "Netherlands", result: "semifinals" },
56+
{ country: "Germany", result: "quarterfinals" },
57+
{ country: "Portugal", result: "quarterfinals" },
58+
{ country: "Switzerland", result: "quarterfinals" },
59+
{ country: "Turkey", result: "quarterfinals" },
60+
{ country: "Italy", result: "knockout-stage" },
61+
{ country: "Belgium", result: "knockout-stage" },
62+
{ country: "Austria", result: "knockout-stage" },
63+
{ country: "Romania", result: "knockout-stage" },
64+
{ country: "Slovakia", result: "knockout-stage" },
65+
{ country: "Slovenia", result: "knockout-stage" },
66+
{ country: "Denmark", result: "knockout-stage" },
67+
{ country: "Georgia", result: "knockout-stage" },
68+
{ country: "Croatia", result: "group-stage" },
69+
{ country: "CzechRepublic", result: "group-stage" },
70+
{ country: "Hungary", result: "group-stage" },
71+
{ country: "Serbia", result: "group-stage" },
72+
{ country: "Poland", result: "group-stage" },
73+
{ country: "Scotland", result: "group-stage" },
74+
{ country: "Albania", result: "group-stage" },
75+
{ country: "Ukraine", result: "group-stage" },
76+
{ country: "Russia", result: "DSQ" },
77+
{ country: "Belarus", result: "DSQ" },
78+
];
79+
80+
```
81+
82+
- Now we are going to assign a range of colores for our domain:
83+
84+
_./src/index.ts_:
85+
86+
```typescript
87+
import { finalStandingsEuro2024 } from "./stats";
88+
89+
// DIFF
90+
// set the affected color scale
91+
const color = d3
92+
.scaleOrdinal([
93+
"gold",
94+
"silver",
95+
"#006400",
96+
"#6D9062",
97+
"#DFF8D5",
98+
"#74B2DF",
99+
"black",
100+
])
101+
.domain([
102+
"champion",
103+
"runner-up",
104+
"semifinals",
105+
"quarterfinals",
106+
"knockout-stage",
107+
"group-stage",
108+
"DSQ",
109+
]);
110+
// DIFF
111+
```
112+
113+
We are using a color palette generated by [mycolor.space](https://mycolor.space/?hex=%23006400&sub=1):
114+
115+
![Color palette used](content/palette.png)
116+
117+
- Let's create a help function to map from country to color: we have to take into account that some European countries didn't qualify to the Euro 2024 (they don't exist on our list).
118+
119+
```diff
120+
const color = d3
121+
.scaleOrdinal([
122+
"gold",
123+
"silver",
124+
"#006400",
125+
"#6D9062",
126+
"#DFF8D5",
127+
"#74B2DF",
128+
"black",
129+
])
130+
.domain([
131+
"champion",
132+
"runner-up",
133+
"bronze",
134+
"semifinals",
135+
"quarterfinals",
136+
"knockout-stage",
137+
"group-stage",
138+
"DSQ",
139+
]);
140+
141+
+ const assignCountryBackgroundColor = (countryName: string) => {
142+
+ const item = finalStandingsEuro2024.find(
143+
+ item => item.country === countryName
144+
+ );
145+
+ return item ? color(item.result) : "white";
146+
+ };
147+
```
148+
149+
- Before modifying our svg, the css file _map.css_ has to be updated:
150+
151+
```diff
152+
.country {
153+
stroke-width: 1;
154+
stroke: #2f4858;
155+
- fill: #008c86;
156+
}
157+
158+
.selected-country {
159+
stroke-width: 1;
160+
stroke: #bc5b40;
161+
- fill: #f88f70;
162+
}
163+
```
164+
165+
- Let's add a fill style to match country name with corresponding background color, using the function defined previously:
166+
167+
```diff
168+
svg
169+
.selectAll("path")
170+
.data(geojson["features"])
171+
.enter()
172+
.append("path")
173+
.attr("class", "country")
174+
+ .style("fill", function(d: any) {
175+
+ return assignCountryBackgroundColor(d.properties.geounit);
176+
+ })
177+
// data loaded from json file
178+
.attr("d", geoPath as any)
179+
```
180+
181+
- Now it's time to modify mouseout and mouseover render features. In this case we will add a tooltip when mouse is over a country, which is going to display the name and final position of that country:
182+
183+
```diff
184+
+ // Define the div for the tooltip
185+
+ const div = d3
186+
+ .select("body")
187+
+ .append("div")
188+
+ .attr("class", "tooltip")
189+
+ .style("opacity", 0);
190+
191+
svg
192+
.selectAll("path")
193+
.data(geojson["features"])
194+
.enter()
195+
.append("path")
196+
.attr("class", "country")
197+
.style("fill", function (d: any) {
198+
return assignCountryBackgroundColor(d.properties.geounit);
199+
}
200+
// data loaded from json file
201+
.attr("d", geoPath as any)
202+
- .on("mouseover", function(d, i) {
203+
+ .on("mouseover", function (mouseEvent: MouseEvent, datum: any) {
204+
d3.select(this).attr("class", "selected-country");
205+
+ const country = datum.properties.geounit;
206+
+ const item = finalStandingsEuro2024.find(
207+
+ (item) => item.country === country
208+
+ );
209+
+ const countryResult = item ? item.result : "DNQ";
210+
+
211+
+ const coords = { x: mouseEvent.pageX, y: mouseEvent.pageY };
212+
+ div.transition().duration(200).style("opacity", 0.9);
213+
+ div
214+
+ .html(`<span>${country}: ${countryResult}</span>`)
215+
+ .style("left", `${coords.x}px`)
216+
+ .style("top", `${coords.y - 28}px`);
217+
})
218+
.on("mouseout", function(d, i) {
219+
d3.select(this).attr("class", "country");
220+
+ div.transition()
221+
+ .duration(500)
222+
+ .style("opacity", 0);
223+
});
224+
```
225+
226+
- This new div element requires some styling. Let's create a new css file, _./src/styles.css_ with the content below:
227+
228+
```css
229+
div.tooltip {
230+
position: absolute;
231+
text-align: center;
232+
width: 70px;
233+
height: 28px;
234+
padding: 2px;
235+
font: 12px sans-serif;
236+
background: #f7f2cb;
237+
border: 0px;
238+
border-radius: 8px;
239+
pointer-events: none;
240+
}
241+
```
242+
243+
- And import this new css file in our html file _index.html_:
244+
245+
```diff
246+
<link rel="stylesheet" type="text/css" href="./map.css" />
247+
+ <link rel="stylesheet" type="text/css" href="./styles.css" />
248+
<link rel="stylesheet" type="text/css" href="./base.css" />
249+
```
250+
251+
- Let's give a try
252+
253+
```bash
254+
npm start
255+
```
256+
257+
## About Basefactor + Lemoncode
258+
259+
We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.
260+
261+
[Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
262+
263+
[Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
264+
265+
For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: <http://lemoncode.net/master-frontend>
126 KB
Loading

04-frameworks/05-d3js/02-maps/01A-scale-background-eurobasket/content/palette.png renamed to 04-frameworks/05-d3js/02-maps/01A-scale-background-euro2024/content/palette.png

File renamed without changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "00-boilerplate",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "run-p -l type-check:watch start:dev",
8+
"type-check": "tsc --noEmit",
9+
"type-check:watch": "npm run type-check -- --watch",
10+
"start:dev": "rimraf dist && parcel ./src/index.html"
11+
},
12+
"keywords": [],
13+
"author": "",
14+
"license": "ISC",
15+
"devDependencies": {
16+
"@types/d3": "^7.1.0",
17+
"@types/node": "^17.0.18",
18+
"@types/topojson-client": "^3.1.0",
19+
"npm-run-all": "^4.1.5",
20+
"parcel": "2.3.1",
21+
"rimraf": "^6.0.1",
22+
"typescript": "^5.3.3"
23+
},
24+
"dependencies": {
25+
"d3": "^7.3.0",
26+
"d3-svg-legend": "^2.25.6",
27+
"topojson-client": "^3.1.0"
28+
}
29+
}

04-frameworks/05-d3js/02-maps/01A-scale-background-eurobasket/src/base.css renamed to 04-frameworks/05-d3js/02-maps/01A-scale-background-euro2024/src/base.css

File renamed without changes.

0 commit comments

Comments
 (0)