Files
cup26/public/data/viz/final-2022.json
T
NilsBriggen dd8156376d Phase 3: data-story viz — StatsBomb shot map, xG race, pass networks
- buildStatsbombViz.ts: processes the 2022 WC Final (Argentina 3-3 France) from
  StatsBomb open data → shots+xG, cumulative xG race, starting-XI pass networks,
  totals, shootout result. Output committed (11KB) to avoid build-time fetch
- Custom SVG viz (no chart lib): reusable Pitch (StatsBomb 120x80 coords),
  ShotMap (xG-sized dots, teams attacking opposite goals), XgRace (stepped
  cumulative xG with goal markers, d3-scale), PassNetwork (nodes ∝ involvement,
  links ∝ pass volume)
- StoryPage: narrative with hero scoreline, by-the-numbers bars, and the three
  visualizations with explanatory copy
- Verified in browser: all three render correctly, no console errors

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 13:34:19 +02:00

1 line
9.8 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"matchId":3869685,"competition":"FIFA World Cup","stage":"Final","date":"2022-12-18","home":"Argentina","away":"France","homeScore":3,"awayScore":3,"result":"Argentina won 42 on penalties","pitch":{"length":120,"width":80},"shots":[{"team":"Argentina","player":"Alexis MacAllister","minute":4,"x":92.4,"y":30,"xg":0.025,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Rodrigo De Paul","minute":7,"x":99.2,"y":47.9,"xg":0.063,"outcome":"Blocked","goal":false},{"team":"Argentina","player":"Ángel Di María","minute":16,"x":103.1,"y":34.6,"xg":0.107,"outcome":"Off T","goal":false},{"team":"Argentina","player":"Lionel Messi","minute":22,"x":108,"y":40,"xg":0.784,"outcome":"Goal","goal":true},{"team":"Argentina","player":"Alexis MacAllister","minute":31,"x":94.3,"y":23.5,"xg":0.011,"outcome":"Wayward","goal":false},{"team":"Argentina","player":"Ángel Di María","minute":35,"x":111.8,"y":32.1,"xg":0.303,"outcome":"Goal","goal":true},{"team":"Argentina","player":"Rodrigo De Paul","minute":48,"x":103.3,"y":52.3,"xg":0.04,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Julián Álvarez","minute":58,"x":110.5,"y":24.9,"xg":0.06,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Lionel Messi","minute":59,"x":109.7,"y":46,"xg":0.116,"outcome":"Off T","goal":false},{"team":"France","player":"Randal Kolo","minute":67,"x":113.4,"y":34.6,"xg":0.096,"outcome":"Off T","goal":false},{"team":"France","player":"Kylian Mbappé","minute":70,"x":102.5,"y":27.9,"xg":0.051,"outcome":"Off T","goal":false},{"team":"Argentina","player":"Enzo Fernandez","minute":71,"x":98.9,"y":32.2,"xg":0.042,"outcome":"Saved","goal":false},{"team":"France","player":"Kylian Mbappé","minute":79,"x":108,"y":40,"xg":0.784,"outcome":"Goal","goal":true},{"team":"France","player":"Kylian Mbappé","minute":80,"x":104.8,"y":30.1,"xg":0.102,"outcome":"Goal","goal":true},{"team":"Argentina","player":"Enzo Fernandez","minute":83,"x":92.2,"y":35.9,"xg":0.03,"outcome":"Blocked","goal":false},{"team":"France","player":"Kylian Mbappé","minute":92,"x":95.9,"y":32.2,"xg":0.032,"outcome":"Blocked","goal":false},{"team":"France","player":"Adrien Rabiot","minute":93,"x":108.9,"y":28.5,"xg":0.093,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Lionel Messi","minute":96,"x":96.2,"y":40.9,"xg":0.043,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Alexis MacAllister","minute":92,"x":98.2,"y":23.8,"xg":0.019,"outcome":"Off T","goal":false},{"team":"France","player":"Youssouf Fofana","minute":99,"x":89.8,"y":39.9,"xg":0.023,"outcome":"Blocked","goal":false},{"team":"Argentina","player":"Lautaro Martínez","minute":104,"x":108.8,"y":45.3,"xg":0.233,"outcome":"Blocked","goal":false},{"team":"Argentina","player":"Gonzalo Montiel","minute":104,"x":96.3,"y":49.4,"xg":0.021,"outcome":"Blocked","goal":false},{"team":"Argentina","player":"Lautaro Martínez","minute":105,"x":105.1,"y":33.5,"xg":0.14,"outcome":"Off T","goal":false},{"team":"Argentina","player":"Lionel Messi","minute":106,"x":103.6,"y":55.8,"xg":0.025,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Lautaro Martínez","minute":107,"x":114.4,"y":52,"xg":0.104,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Lionel Messi","minute":107,"x":116.6,"y":43,"xg":0.488,"outcome":"Goal","goal":true},{"team":"France","player":"Kylian Mbappé","minute":115,"x":101.4,"y":26.6,"xg":0.031,"outcome":"Blocked","goal":false},{"team":"France","player":"Kylian Mbappé","minute":117,"x":108,"y":40,"xg":0.784,"outcome":"Goal","goal":true},{"team":"France","player":"Randal Kolo","minute":122,"x":103.7,"y":45.1,"xg":0.278,"outcome":"Saved","goal":false},{"team":"Argentina","player":"Lautaro Martínez","minute":122,"x":110.8,"y":41.6,"xg":0.107,"outcome":"Off T","goal":false}],"goals":[{"team":"Argentina","player":"Lionel Messi","minute":22,"xg":0.784},{"team":"Argentina","player":"Ángel Di María","minute":35,"xg":0.303},{"team":"France","player":"Kylian Mbappé","minute":79,"xg":0.784},{"team":"France","player":"Kylian Mbappé","minute":80,"xg":0.102},{"team":"Argentina","player":"Lionel Messi","minute":107,"xg":0.488},{"team":"France","player":"Kylian Mbappé","minute":117,"xg":0.784}],"xgRace":{"home":[{"minute":0,"cum":0},{"minute":4,"cum":0.025},{"minute":7,"cum":0.088},{"minute":16,"cum":0.195},{"minute":22,"cum":0.979},{"minute":31,"cum":0.99},{"minute":35,"cum":1.293},{"minute":48,"cum":1.333},{"minute":58,"cum":1.393},{"minute":59,"cum":1.509},{"minute":71,"cum":1.551},{"minute":83,"cum":1.581},{"minute":92,"cum":1.6},{"minute":96,"cum":1.643},{"minute":104,"cum":1.876},{"minute":104,"cum":1.897},{"minute":105,"cum":2.037},{"minute":106,"cum":2.062},{"minute":107,"cum":2.166},{"minute":107,"cum":2.654},{"minute":122,"cum":2.761}],"away":[{"minute":0,"cum":0},{"minute":67,"cum":0.096},{"minute":70,"cum":0.147},{"minute":79,"cum":0.931},{"minute":80,"cum":1.033},{"minute":92,"cum":1.065},{"minute":93,"cum":1.158},{"minute":99,"cum":1.181},{"minute":115,"cum":1.212},{"minute":117,"cum":1.996},{"minute":122,"cum":2.274}]},"passNetwork":{"home":{"players":[{"player":"Nahuel Molina","x":58.1375,"y":72.27749999999999,"passes":40},{"player":"Rodrigo De Paul","x":61.53720930232559,"y":59.93720930232558,"passes":43},{"player":"Cristian Romero","x":34.3595238095238,"y":52.030952380952385,"passes":42},{"player":"Nicolás Otamendi","x":38.56304347826087,"y":23.382608695652173,"passes":46},{"player":"Nicolás Tagliafico","x":57.916666666666664,"y":7.897222222222222,"passes":36},{"player":"Emiliano Martínez","x":13.500000000000004,"y":45.136842105263156,"passes":19},{"player":"Alexis MacAllister","x":68.54285714285713,"y":23.157142857142855,"passes":28},{"player":"Lionel Messi","x":77.84242424242423,"y":54.233333333333334,"passes":33},{"player":"Ángel Di María","x":81.76896551724136,"y":11.313793103448276,"passes":29},{"player":"Enzo Fernandez","x":55.37058823529413,"y":36.672549019607835,"passes":51},{"player":"Julián Álvarez","x":80.0764705882353,"y":41.49411764705882,"passes":17}],"edges":[{"a":"Nahuel Molina","b":"Rodrigo De Paul","count":18},{"a":"Cristian Romero","b":"Rodrigo De Paul","count":15},{"a":"Cristian Romero","b":"Nicolás Otamendi","count":21},{"a":"Nicolás Otamendi","b":"Nicolás Tagliafico","count":18},{"a":"Alexis MacAllister","b":"Nicolás Tagliafico","count":12},{"a":"Emiliano Martínez","b":"Nicolás Otamendi","count":9},{"a":"Alexis MacAllister","b":"Lionel Messi","count":7},{"a":"Lionel Messi","b":"Ángel Di María","count":9},{"a":"Lionel Messi","b":"Rodrigo De Paul","count":16},{"a":"Nicolás Otamendi","b":"Rodrigo De Paul","count":9},{"a":"Enzo Fernandez","b":"Nicolás Otamendi","count":17},{"a":"Cristian Romero","b":"Enzo Fernandez","count":13},{"a":"Cristian Romero","b":"Nahuel Molina","count":6},{"a":"Cristian Romero","b":"Lionel Messi","count":3},{"a":"Cristian Romero","b":"Ángel Di María","count":3},{"a":"Nicolás Tagliafico","b":"Ángel Di María","count":12},{"a":"Alexis MacAllister","b":"Ángel Di María","count":11},{"a":"Alexis MacAllister","b":"Julián Álvarez","count":5},{"a":"Enzo Fernandez","b":"Lionel Messi","count":7},{"a":"Alexis MacAllister","b":"Enzo Fernandez","count":9},{"a":"Julián Álvarez","b":"Ángel Di María","count":4},{"a":"Rodrigo De Paul","b":"Ángel Di María","count":3},{"a":"Cristian Romero","b":"Emiliano Martínez","count":7},{"a":"Enzo Fernandez","b":"Nahuel Molina","count":7},{"a":"Enzo Fernandez","b":"Rodrigo De Paul","count":11},{"a":"Julián Álvarez","b":"Lionel Messi","count":6},{"a":"Julián Álvarez","b":"Rodrigo De Paul","count":3},{"a":"Nicolás Otamendi","b":"Ángel Di María","count":7},{"a":"Enzo Fernandez","b":"Nicolás Tagliafico","count":8},{"a":"Lionel Messi","b":"Nahuel Molina","count":9},{"a":"Julián Álvarez","b":"Nahuel Molina","count":3},{"a":"Enzo Fernandez","b":"Julián Álvarez","count":5},{"a":"Emiliano Martínez","b":"Nahuel Molina","count":4},{"a":"Enzo Fernandez","b":"Ángel Di María","count":4}]},"away":{"players":[{"player":"Antoine Griezmann","x":70.58999999999999,"y":52.65,"passes":10},{"player":"Aurélien Tchouaméni","x":53.07142857142856,"y":39.42857142857143,"passes":14},{"player":"Theo Hernández","x":54.33157894736842,"y":9.1,"passes":19},{"player":"Adrien Rabiot","x":57.92,"y":28.87333333333333,"passes":15},{"player":"Raphaël Varane","x":43.61000000000001,"y":52.345000000000006,"passes":20},{"player":"Jules Koundé","x":47.27647058823529,"y":68.81764705882354,"passes":17},{"player":"Hugo Lloris","x":10.95,"y":36.46,"passes":10},{"player":"Olivier Giroud","x":64.32222222222222,"y":46.21111111111111,"passes":9},{"player":"Kylian Mbappé","x":81.43333333333334,"y":13.077777777777776,"passes":9},{"player":"Ousmane Dembélé","x":67.12307692307692,"y":69.4076923076923,"passes":13},{"player":"Dayotchanculle Upamecano","x":41.39375,"y":25.162500000000005,"passes":16}],"edges":[{"a":"Adrien Rabiot","b":"Theo Hernández","count":5},{"a":"Raphaël Varane","b":"Theo Hernández","count":3},{"a":"Jules Koundé","b":"Raphaël Varane","count":9},{"a":"Jules Koundé","b":"Ousmane Dembélé","count":10},{"a":"Hugo Lloris","b":"Olivier Giroud","count":3},{"a":"Antoine Griezmann","b":"Olivier Giroud","count":3},{"a":"Adrien Rabiot","b":"Kylian Mbappé","count":5},{"a":"Antoine Griezmann","b":"Jules Koundé","count":4},{"a":"Adrien Rabiot","b":"Dayotchanculle Upamecano","count":3},{"a":"Dayotchanculle Upamecano","b":"Theo Hernández","count":6},{"a":"Kylian Mbappé","b":"Theo Hernández","count":8},{"a":"Aurélien Tchouaméni","b":"Dayotchanculle Upamecano","count":3},{"a":"Hugo Lloris","b":"Raphaël Varane","count":3},{"a":"Dayotchanculle Upamecano","b":"Raphaël Varane","count":12},{"a":"Adrien Rabiot","b":"Aurélien Tchouaméni","count":6},{"a":"Aurélien Tchouaméni","b":"Raphaël Varane","count":3},{"a":"Olivier Giroud","b":"Ousmane Dembélé","count":3}]}},"totals":{"home":{"xg":2.76,"shots":20,"goals":3,"passes":693,"passAccuracy":81},"away":{"xg":2.27,"shots":10,"goals":3,"passes":570,"passAccuracy":76}}}