Created
March 24, 2020 13:39
-
-
Save zemeolotu/b202756404dfc8cfb22d43ad646448cc to your computer and use it in GitHub Desktop.
Perspective / `open-ncovid-19` Dashboard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: apache-2.0 | |
height: 800 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> | |
<link rel='stylesheet' href="https://unpkg.com/@finos/perspective-workspace/dist/umd/material.css"> | |
<script src="https://unpkg.com/@finos/perspective-workspace"></script> | |
<script src="https://unpkg.com/@finos/perspective-viewer-datagrid"></script> | |
<script src="https://unpkg.com/@finos/perspective-viewer-d3fc"></script> | |
<script src="https://unpkg.com/@finos/perspective"></script> | |
<style> | |
body { | |
display: flex; | |
flex-direction: column; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
margin: 0; | |
padding: 0; | |
overflow: hidden; | |
} | |
perspective-viewer.workspace-master-widget table { | |
color: #cfd8dc !important; | |
} | |
perspective-viewer.workspace-master-widget tbody:hover tr.pd-selected:hover td:not(.pd-row-header-icon) span { | |
background-color: #EA7319 !important; | |
} | |
</style> | |
</head> | |
<body> | |
<perspective-workspace id="workspace"></perspective-workspace> | |
<script> | |
const SCHEMA = { | |
"Date": "datetime", | |
"CountryCode": "string", | |
"CountryName": "string", | |
"Confirmed": "float", | |
"Deaths": "float", | |
"Latitude": "float", | |
"Longitude": "float", | |
"Region": "string", | |
"SubRegion": "string" | |
}; | |
const worker = perspective.shared_worker(); | |
const FLAG_URLS = new Map(); | |
async function get_country_meta(country, map) { | |
const req = await fetch(`https://restcountries.eu/rest/v2/alpha/${country.toLowerCase()}`); | |
const json = await req.json(); | |
FLAG_URLS[country] = json.flag; | |
map.set(country, {Region: json.region, SubRegion: json.subregion}); | |
} | |
const datasource = async () => { | |
const req = fetch("https://raw.githubusercontent.com/open-covid-19/data/master/output/world.csv"); | |
const resp = await req; | |
const text = await resp.text(); | |
const table = worker.table(text); | |
const schema = await table.schema(); | |
const view = table.view({row_pivots: ["CountryCode"], columns: []}); | |
const column = await view.to_columns({start_row: 1}); | |
const country_map = new Map(); | |
const table2 = worker.table({...schema, Region: "string", SubRegion: "string"}); | |
await Promise.all(column.__ROW_PATH__.map((x) => get_country_meta(x[0], country_map))); | |
const view2 = table.view(); | |
const records = await view2.to_json(); | |
for (const row of records) { | |
table2.update([{...row, ...country_map.get(row.CountryCode)}]); | |
} | |
view.delete(); | |
view2.delete(); | |
return table2; | |
}; | |
// const unindexed = async ds => { | |
// const table = worker.table(SCHEMA); | |
// table.update(await ds); | |
// return table; | |
// } | |
// const indexed = async ds => { | |
// const table = worker.table(SCHEMA, {index: "CountryCode"}); | |
// table.update(await ds); | |
// return table; | |
// } | |
window.addEventListener("load", async () => { | |
const ds = datasource(); | |
window.workspace.tables.set("unindexed", ds); | |
window.workspace.tables.set("indexed", ds); | |
const scatter_config = window.getPlugin("d3_xy_scatter"); | |
scatter_config.max_columns = 500; | |
const area_config = window.getPlugin("d3_y_area"); | |
area_config.max_columns = 500; | |
area_config.max_cells = 100000; | |
const line_config = window.getPlugin("d3_y_line"); | |
line_config.max_columns = 500; | |
line_config.max_cells = 100000; | |
await window.workspace.restore({ | |
sizes: [0.4027196652719665, 0.5972803347280334], | |
detail: { | |
main: { | |
type: "split-area", | |
orientation: "vertical", | |
children: [ | |
{ | |
type: "tab-area", | |
widgets: ["PERSPECTIVE_GENERATED_ID_2"], | |
currentIndex: 0 | |
}, | |
{ | |
type: "tab-area", | |
widgets: ["PERSPECTIVE_GENERATED_ID_1", "PERSPECTIVE_GENERATED_ID_0"], | |
currentIndex: 0 | |
} | |
], | |
sizes: [0.5, 0.5] | |
} | |
}, | |
mode: "globalFilters", | |
master: { | |
widgets: ["One"] | |
}, | |
viewers: { | |
One: { | |
plugin: "datagrid", | |
"row-pivots": ["Region", "SubRegion", "CountryName"], | |
aggregates: { | |
CountryCode: "unique", | |
Confirmed: "sum", | |
Deaths: "sum" | |
}, | |
sort: [["Confirmed", "desc"]], | |
columns: ["CountryCode", "Confirmed", "Deaths"], | |
name: "open-covid-19", | |
table: "indexed" | |
}, | |
PERSPECTIVE_GENERATED_ID_2: { | |
plugin: "d3_y_bar", | |
"row-pivots": ["Date"], | |
aggregates: { | |
CountryCode: "unique" | |
}, | |
sort: [["Confirmed", "desc"]], | |
columns: ["Confirmed", "Deaths"], | |
plugin_config: { | |
realValues: ["Confirmed", "Deaths"], | |
splitMainValues: ["Deaths"], | |
legend: { | |
left: "80px", | |
top: "15px" | |
} | |
}, | |
name: "open-ncovid-19 Confirmed, Deaths vs Date Bar", | |
table: "unindexed" | |
}, | |
PERSPECTIVE_GENERATED_ID_1: { | |
plugin: "d3_y_line", | |
"row-pivots": ["Date"], | |
"column-pivots": ["CountryName"], | |
aggregates: { | |
CountryCode: "unique", | |
Confirmed: "high", | |
Deaths: "high", | |
"(Deaths %% Confirmed)": "high" | |
}, | |
"computed-columns": [ | |
{ | |
name: "(Deaths %% Confirmed)", | |
inputs: ["Deaths", "Confirmed"], | |
func: "percent_a_of_b" | |
}, | |
{ | |
name: "(Deaths / Confirmed)", | |
inputs: ["Deaths", "Confirmed"], | |
func: "divide" | |
} | |
], | |
columns: ["(Deaths %% Confirmed)"], | |
plugin_config: { | |
realValues: ["(Deaths %% Confirmed)"], | |
legend: { | |
left: "73px", | |
top: "10px", | |
height: "100px", | |
width: "" | |
} | |
}, | |
name: "open-ncovid-19 Deaths% vs Date", | |
table: "unindexed" | |
}, | |
PERSPECTIVE_GENERATED_ID_0: { | |
plugin: "d3_xy_scatter", | |
"row-pivots": ["Date"], | |
"column-pivots": ["CountryName"], | |
aggregates: { | |
CountryCode: "unique", | |
Confirmed: "high", | |
Deaths: "high" | |
}, | |
sort: [["Confirmed", "desc"]], | |
columns: ["Confirmed", "Deaths"], | |
plugin_config: { | |
realValues: ["Confirmed", "Deaths"], | |
legend: { | |
left: "89px", | |
top: "10px", | |
height: "100px", | |
width: "" | |
} | |
}, | |
name: "open-ncovid-19 Confirmed vs Deaths", | |
table: "unindexed" | |
} | |
} | |
}); | |
const CACHE = {}; | |
function clone_img_cache() { | |
return Object.keys(CACHE).reduce((obj, key) => { | |
obj[key] = CACHE[key].slice(); | |
return obj; | |
}, {}); | |
} | |
async function get_flag(country, x) { | |
const req2 = await fetch(FLAG_URLS[country.toUpperCase()]); | |
const svg = await req2.text(); | |
const utf8Bytes = encodeURIComponent(svg).replace(/%([0-9A-F]{2})/g, function (match, p1) { | |
return String.fromCharCode("0x" + p1); | |
}); | |
return window.btoa(utf8Bytes); | |
//return CACHE[country]; | |
} | |
function new_cache_flag(country, x) { | |
const img = document.createElement("img"); | |
img.setAttribute("height", "19"); | |
img.setAttribute("width", "24"); | |
(async () => { | |
const flag = await get_flag(country, x); | |
img.setAttribute("src", `data:image/svg+xml;base64,${flag}`); | |
CACHE[country] = CACHE[country] || []; | |
CACHE[country].push(img); | |
})(); | |
return img; | |
} | |
for (const x of window.workspace.querySelectorAll("perspective-viewer")) { | |
x.addEventListener("perspective-datagrid-after-update", event => { | |
const cache = clone_img_cache(); | |
const datagrid = event.detail; | |
for (const td of datagrid.get_tds()) { | |
const metadata = datagrid.get_meta(td); | |
if (metadata.column === "CountryCode" && metadata.value !== "-" && typeof metadata.value === "string") { | |
const country = metadata.value.toLowerCase(); | |
const span = document.createElement("span"); | |
let img; | |
if (cache[country] && cache[country].length > 0) { | |
img = cache[country].pop(); | |
} else { | |
img = new_cache_flag(country, x) | |
} | |
td.textContent = ""; | |
td.style.display = "flex"; | |
span.appendChild(img); | |
let span2 = document.createElement("span"); | |
span2.textContent = metadata.value; | |
span2.style.marginLeft = "12px"; | |
td.appendChild(span); | |
td.appendChild(span2); | |
} else { | |
td.style.display = "table-cell"; | |
} | |
} | |
}); | |
} | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment