Published
Edited
Apr 30, 2021
Insert cell
Insert cell
percentPlayed = {
let naColor = "#C33C54"
let euColor = "#4D8EA7"
let svg = d3.create('svg').style("background-color", 'white').attr("preserveAspectRatio", "none").style("background-color", 'white').attr("viewBox", "0 0 1150 650");

let viperColor = "#1ab963"
svg.append("text")
.attr("class", "signature")
.attr("text-anchor", "middle")
.attr("y", 2)
.attr("x", 963/2)
.attr("dy", ".75em")
.attr('style', "font-size: 20px; font-family: 'Bebas Neue';")
.text("Author: Sam Rosenberg").call(addWebFont, 'Bebas Neue', 'https://fonts.gstatic.com/s/bebasneue/v2/JTUSjIg69CK48gW7PXoo9WlhyyTh89Y.woff2').attr('style', d => "font-size: 20px; font-family: 'Bebas Neue';");

svg.append("text")
.attr("class", "signature")
.attr("text-anchor", "middle")
.attr("y", 480)
.attr("x", 1008)
.attr("dy", ".75em")
.attr('style', "font-size: 30px; font-family: 'Bebas Neue';")
.text("Image from valorant.fandom.com").attr('style', d => "font-size: 19px; font-family: 'Bebas Neue';");

svg.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', 140)
.attr('height', 40)
.attr('stroke', 'black')
.attr('fill', 'black');
svg.append("image")
.attr("xlink:href", "https://runitback.gg/static/media/logo.dcfad9c9.png")
.attr("x", 10)
.attr("y", -10)
.attr("width", 120)
.attr("height", 60);

svg.append("image")
.attr("xlink:href", "https://greblytics.com/wp-content/uploads/2021/04/viper.png")
.attr("x", 810)
.attr("y", 0)
.attr("width", 400)
.attr("height", 480);

var text2 = svg.append("svg:text").attr("class", "title")
.attr("xlink:href", "greblytics.com")
.attr("text-anchor", "middle")
.attr("y", 60)
.attr("x", 963/2)
.attr('style', "font-size: 45px; font-family: 'Bebas Neue';");
;
text2.append("svg:tspan").style("fill", euColor).text("Europe");
text2.append("svg:tspan").style("fill", "black").text(" vs. ");
text2.append("svg:tspan").style("fill", naColor).text("North America ");
text2.append("svg:tspan").style("fill", "black").text("Viper")
text2.append("svg:tspan").style("fill", "black").text(" Pickrates");

let viperX = d3.scaleOrdinal()
.domain(["", "S1C1", "S1C2", "S1C3", "S1M", "S2C1", "S2C2"])
.range([50, 100, 250, 400, 550, 700, 850].map(d=> d*0.95 + 30))

let viperXAxis = d3.scaleOrdinal()
.domain(["", "Stage 1: Challengers 1", "Stage 1: Challengers 2", "Stage 1: Challengers 3", "Stage 1: Masters", "Stage 2: Challengers 1", "Stage 2: Challengers 2"])
.range([21, 100, 250, 400, 550, 700, 850].map(d=> d*0.95 + 30))

let viperY = d3.scaleLinear()
.domain([-2,30])
.range([450, 80])

const line = d3.line()
.x(function(d) {return d[0];})
.y(function(d) {return d[1];})

let data = []
for (let i = 0; i < Object.keys(euVipers).length; i++)
{
/*let toAdd = {}
toAdd["x"] = Object.keys(euVipers)[i]
toAdd["y"] = euVipers[Object.keys(euVipers)[i]]
data.push(toAdd)
*/
data.push([viperX(Object.keys(euVipers)[i]), viperY(euVipers[Object.keys(euVipers)[i]])])
}
console.log(data)
d3.select('svg')
.append('path') // add a path to the existing svg
.datum(data)
.attr('d', line)
.attr('stroke', 'black')

/*
.on('mouseover', function(event, d) {
d3.select('#nameText').text(d['Name']);
let loc = d3.pointer(event);
d3.select('#nameText').attr('x', loc[0]);
d3.select('#nameText').attr('y', loc[1]-13);
})
*/
for (let i = 0; i < Object.keys(euVipers).length-1; i++)
{
let xOld = viperX(Object.keys(euVipers)[i])
let yOld = viperY(euVipers[Object.keys(euVipers)[i]])
let xNew = viperX(Object.keys(euVipers)[i+1])
let yNew = viperY(euVipers[Object.keys(euVipers)[i+1]])
svg.append("line").attr("x1", xOld).attr("y1", yOld).attr("x2", xNew).attr("y2", yNew).attr("stroke", euColor).attr("stroke-width", 3)
}

for (let i = 0; i < Object.keys(naVipers).length-1; i++)
{
let xOld = viperX(Object.keys(naVipers)[i])
let yOld = viperY(naVipers[Object.keys(naVipers)[i]])
let xNew = viperX(Object.keys(naVipers)[i+1])
let yNew = viperY(naVipers[Object.keys(naVipers)[i+1]])
svg.append("line").attr("x1", xOld).attr("y1", yOld).attr("x2", xNew).attr("y2", yNew).attr("stroke", naColor).attr("stroke-width", 3)
}

for (let i = 0; i < Object.keys(euVipers).length; i++)
{
svg.append("circle")
.attr("cx", viperX(Object.keys(euVipers)[i]))
.attr("cy", viperY(euVipers[Object.keys(euVipers)[i]]))
.attr("r", 5).attr("fill", euColor)
}

for (let i = 0; i < Object.keys(naVipers).length; i++)
{
svg.append("circle")
.attr("cx", viperX(Object.keys(naVipers)[i]))
.attr("cy", viperY(naVipers[Object.keys(naVipers)[i]]))
.attr("r", 5).attr("fill", naColor)
}


for (let i = 0; i < Object.keys(naVipers).length; i++)
{
svg.append("rect")
.attr("x", viperX(Object.keys(naVipers)[i])-80)
.attr("y", 80)
.attr("width", 160)
.attr("height", 370)
.attr("fill", 'transparent')
.on('mouseover', function(event, d) {
d3.select(this).attr('fill', "grey").attr('opacity', 0.2);
let selectedEventVipers = NAPlayers[Object.keys(NAPlayers)[i]].filter(d => d.Agent === "Viper")
drawTeamLogosNA(selectedEventVipers)
let selectedEventVipersEU = EUPlayers[Object.keys(EUPlayers)[i]].filter(d => d.Agent === "Viper")
drawTeamLogosEU(selectedEventVipersEU)
})
.on('mouseout', function(event, d) {
d3.select(this).attr('fill', "transparent");
console.log(event)
})
}
//svg.append("line").attr("x1", xOld).attr("y1", yOld).attr("x2", xNew).attr("y2", yNew).attr("stroke", naColor).attr("stroke-width", 3)

let boxWidth = 100;
function drawTeamLogosEU(selectedEventVipers)
{
for (let i = 0; i < selectedEventVipers.length; i++)
{
console.log(selectedEventVipers[i])
svg.append("rect")
.attr("x", 1090 - i*boxWidth - boxWidth/2)
.attr("y", 505)
.attr("width", boxWidth)
.attr("height", boxWidth+35)
.attr("fill", euColor).attr("opacity", 0.4)
.attr("stroke-thickness", 3).attr("stroke", "black")

svg.append("text")
.attr("class", "signature")
.attr("text-anchor", "middle")
.attr("y", 530)
.attr("x", 1090 - i*boxWidth)
.attr('style', "font-size: 50px; font-family: 'Bebas Neue';")
.text(selectedEventVipers[i]["Ign"]).attr('style', d => "font-size: 30px; font-family: 'Bebas Neue';");
svg.append("image")
.attr("xlink:href", teamLogos[selectedEventVipers[i]["Team"]])
.attr("x", 1090 - i*boxWidth - boxWidth/2)
.attr("y", 540)
.attr("width", boxWidth)
.attr("height", boxWidth);
}
}




function drawTeamLogosNA(selectedEventVipers)
{
svg.append("rect")
.attr("x", 0)
.attr("y", 500)
.attr("width", 1150)
.attr("height", 300)
.attr("fill", 'white')
for (let i = 0; i < selectedEventVipers.length; i++)
{
console.log(selectedEventVipers[i])
svg.append("rect")
.attr("x", 60 + i*boxWidth - boxWidth/2)
.attr("y", 505)
.attr("width", boxWidth)
.attr("height", boxWidth+35)
.attr("fill", naColor).attr("opacity", 0.4)
.attr("stroke-thickness", 3).attr("stroke", "black")

svg.append("text")
.attr("class", "signature")
.attr("text-anchor", "middle")
.attr("y", 530)
.attr("x", 60 + i*boxWidth)
.attr('style', "font-size: 50px; font-family: 'Bebas Neue';")
.text(selectedEventVipers[i]["Ign"]).attr('style', d => "font-size: 30px; font-family: 'Bebas Neue';");
svg.append("image")
.attr("xlink:href", teamLogos[selectedEventVipers[i]["Team"]])
.attr("x", 60 + i*boxWidth - boxWidth/2)
.attr("y", 540)
.attr("width", boxWidth)
.attr("height", boxWidth);
}
}
svg.append('g')
.attr('transform', `translate(${50}, 0)`)
.call(d3.axisLeft(viperY).tickFormat(function(d) { return d + "%"})).attr('style', d => "font-size: 20px; font-family: 'Bebas Neue';");
svg.append('g')
.attr('transform', `translate(0, ${450})`)
.call(d3.axisBottom(viperXAxis)).attr('style', d => "font-size: 15px; font-family: 'Bebas Neue';");

return svg.node();
}
Insert cell
teamLogos = {
return {"Sentinels" : "https://owcdn.net/img/5f25fa763f7ad.png", "Envy" : "https://owcdn.net/img/5f3ca822464a3.png", "TSM": "https://owcdn.net/img/5f3ca6f653a9c.png", "Immortals" : "https://owcdn.net/img/602e1b45e079f.png", "FaZe": "https://owcdn.net/img/5ef51451b0601.png", "100 Thieves" : "https://owcdn.net/img/603c00d5c5a08.png", "LG": "https://owcdn.net/img/5f36ec1f4ab21.png", "Andbox (ex)":"https://owcdn.net/img/5f64213a6402f.png", "Cloud9 Blue": "https://owcdn.net/img/60135aa861fc5.png", "XSET": "https://owcdn.net/img/5f9725636681d.png", "GEN": "https://owcdn.net/img/5f308c5756db6.png", "NRG": "https://owcdn.net/img/5f7b9ff821d6d.png", "T1":"https://owcdn.net/img/5ea85d21e625f.png", "Built By Gamers":"https://owcdn.net/img/5f88bd58c0630.png", "Andbox":"https://owcdn.net/img/5f64213a6402f.png", "Immortals (si)" : "https://owcdn.net/img/602e1b45e079f.png", "Team Liquid": "https://owcdn.net/img/5f11fac202eeb.png", "Acend":"https://owcdn.net/img/604a83800fa73.png", "Team Vitality":"https://owcdn.net/img/5efe491dd8181.png", "Fnatic":"https://owcdn.net/img/603c0453b0cd2.png", "FunPlus Phoenix":"https://owcdn.net/img/5feab33100c1d.png", "Ninjas in Pyjamas":"https://owcdn.net/img/600f8bbf4603f.png", "Guild Esports":"https://owcdn.net/img/5f901f417a773.png", "Team Heretics":"https://owcdn.net/img/5f91e71607511.png", "Ballista Esports":"https://owcdn.net/img/602974130c5c5.png", "G2 Esports":"https://owcdn.net/img/5f3cbb213e5de.png", "Monkey Business (ex)":"https://owcdn.net/img/601dad9359d35.png", "Wave Esports":"https://owcdn.net/img/60305cb6c16c7.png", "Alliance": "https://owcdn.net/img/6012ec3dde855.png", "Raise Your Edge Gaming (ex)":"https://owcdn.net/img/601568bab77e9.png"}
}
Insert cell
EU = { return {"S2C2": await FileAttachment("EU S2 C2.csv").csv(), "S2C1": await FileAttachment("EU S2 C1.csv").csv(), "S1M": await FileAttachment("EU S1 M.csv").csv(), "S1C3": await FileAttachment("EU S1 C3.csv").csv(), "S1C2": await FileAttachment("EU S1 C2.csv").csv(), "S1C1": await FileAttachment("EU S1 C1.csv").csv()}}
Insert cell
EUPlayers = { return {"S2C2": await FileAttachment("EU S2 C2 Players.csv").csv(), "S2C1": await FileAttachment("EU S2 C1 Players.csv").csv(), "S1M": await FileAttachment("EU S1 M Players.csv").csv(), "S1C3": await FileAttachment("EU S1 C3 Players.csv").csv(), "S1C2": await FileAttachment("EU S1 C2 Players.csv").csv(), "S1C1": await FileAttachment("EU S1 C1 Players.csv").csv()}}
Insert cell
NA = { return {"S2C2": await FileAttachment("NA S2 C2.csv").csv(), "S2C1": await FileAttachment("NA S2 C1.csv").csv(), "S1M": await FileAttachment("NA S1 M.csv").csv(), "S1C3": await FileAttachment("NA S1 C3.csv").csv(), "S1C2": await FileAttachment("NA S1 C2.csv").csv(), "S1C1": await FileAttachment("NA S1 C1.csv").csv()}}
Insert cell
NAPlayers = { return {"S2C2": await FileAttachment("NA S2 C2 Players.csv").csv(), "S2C1": await FileAttachment("NA S2 C1 Players.csv").csv(), "S1M": await FileAttachment("NA S1 M Players.csv").csv(), "S1C3": await FileAttachment("NA S1 C3 Players.csv").csv(), "S1C2": await FileAttachment("NA S1 C2 Players.csv").csv(), "S1C1": await FileAttachment("NA S1 C1 Players.csv").csv()}}
Insert cell
Object.keys(EU)[1]
Insert cell
euVipers = getVipersEU()
Insert cell
function getVipersEU(){
let toReturn = {}
for (let i = 0; i < Object.keys(EU).length; i++)
{
toReturn[Object.keys(EU)[i]] = EU[Object.keys(EU)[i]].filter(d => d["Agent"] === "Viper")[0]["Percentage"]
}
return toReturn
}
Insert cell
Insert cell
function getVipersNA(){
let toReturn = {}
for (let i = 0; i < Object.keys(NA).length; i++)
{
if (NA[Object.keys(NA)[i]].filter(d => d["Agent"] === "Viper")[0] !== undefined)
{
toReturn[Object.keys(NA)[i]] = NA[Object.keys(NA)[i]].filter(d => d["Agent"] === "Viper")[0]["Percentage"]
}
else
{
toReturn[Object.keys(NA)[i]] = 0
}
}
return toReturn
}
Insert cell
d3 = require('d3@v6')
Insert cell
async function toDataURL(url) {
return new Promise(async(resolve, reject) => {
const res = await fetch(url);
if(!res.ok) return reject(`Error: ${res.status} ${res.statusText}`);
const blob = await res.blob();
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => resolve(reader.result);
});
}
Insert cell
async function addWebFont(selection, fontName, fontURL, fontType = 'woff2') {
const fontData = await toDataURL(fontURL);
return selection.append('style').text(`
@font-face {
font-family: '${fontName}';
src: url(${fontData}) format('${fontType}');
}
`);
};
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more