Skip to content

Instantly share code, notes, and snippets.

@JHawk
Last active March 23, 2020 17:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JHawk/552a312774bfef493687a8e077e63e73 to your computer and use it in GitHub Desktop.
Save JHawk/552a312774bfef493687a8e077e63e73 to your computer and use it in GitHub Desktop.
Perspective / `open-ncovid-19` Dashboard
license: apache-2.0
height: 800
<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/data.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: "high",
Deaths: "high"
},
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