Published
Edited
Aug 3, 2021
Insert cell
md`# Google Input Calibration`
Insert cell
xlsx = require('https://bundle.run/xlsx@0.14.1')
Insert cell
Insert cell
Insert cell
// width = 1200
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
svg.append("g")
.call(yAxis_ticks);

const g = svg.selectAll(".vars")
.data(counts)
.join("g")
.attr("class", "vars")
.attr("transform", d => `translate(0,${y(d.variable) + y.bandwidth()/2})`);
g.each(function(d, i) {
const x = d3.scaleLinear()
// .domain([0, 3]).nice()
.domain([0, 2]).nice()
.range([margin.left, width - margin.right]);
const xAxis = g => g
// .call(d3.axisBottom(x).tickValues([0, 1, 2, 3]).tickFormat(dd => x_lookup[d.variable] [dd]).tickPadding(12))
.call(d3.axisBottom(x).tickValues([0, 1, 2]).tickFormat(dd => x_lookup[d.variable][dd]).tickPadding(12))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").remove())
.call(g => g.selectAll("text").attr("font-weight", "bold"))
.call(g => g.selectAll("text").attr("color", (dd, i) => highlight(d, i)))
.call(g => g.selectAll("text").style("mix-blend-mode", "multiply"));
d3.select(this).selectAll(".dots")
// .data([0, 1, 2, 3])
.data([0, 1, 2])
.join("circle")
.attr("class", "dots")
.style("mix-blend-mode", "multiply")
.attr("stroke", "#00695C")
.attr("stroke-width", 1)
.attr("fill", "#80CBC4")
// .attr("fill", chroma("#00a1b0").brighten(1.5).desaturate())
.attr("fill-opacity", .5)
.attr("cy", 0)
.attr("cx", (d, i) => x(i))
.attr("r", dd => scale(d[dd.toString(16)]))
d3.select(this).selectAll(".points")
// .data([0, 1, 2, 3])
.data([0, 1, 2])
.join("circle")
.attr("class", "points")
.style("mix-blend-mode", "multiply")
// .attr("stroke", "#00a1b0")
// .attr("stroke-width", 2)
.attr("fill", (dd, i) => highlight(d, i))
.attr("cy", 0)
.attr("cx", (d, i) => x(i))
.attr("r", 4)
d3.select(this).append("g").call(xAxis);

})

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
svg.selectAll("text").attr("font-size", 16).attr("font-family", "Open Sans")

return svg.node();
}
Insert cell
filename = binary.name.split(".")[0]
Insert cell
input = new Uint8Array(await Files.buffer(binary))
Insert cell
wb = xlsx.read(input, {type: 'array'})
Insert cell
// data must be structured with typical input fields as column headers, with one column calling out building name
// data = FileAttachment("UT Baseline Inputs@1.csv").csv({typed: true})

Insert cell
excel = wb.Sheets["Sheet1"]
Insert cell
data = xlsx.utils.sheet_to_json(excel)
Insert cell
Insert cell
margin = ({top: 80, right: 100, bottom: 50, left: 350})
Insert cell
height = 1400
Insert cell
d3 = require("d3@^6.1")
Insert cell
_ = require('lodash@4.17.15/lodash.js').catch(() => window["_"])
Insert cell
colors = ({"S": "#f79320", "E": "#fed202", "C":"#00a1b0"})
Insert cell
chroma = require('chroma-js')
Insert cell
counts = {

const count = (d, i) => {
const arr = data.map(dd => dd[d]);
return arr.filter(dd => dd === i).length;
}
const keymap = Object.keys(v_lookup).map(d => ({variable: d, "0": count(d, 0), "1": count(d, 1), "2": count(d, 2)}));
const reverse = d => {
if (d.variable === "HUMIDMIN" || d.variable === "HUMIDMAX") {
let [z, o, t] = [d["0"], d["1"], d["2"]];
return {...d, "0": t, "2": z};
}
else return d;
}
return keymap.map(d => reverse(d));

}
Insert cell
energy = d => {
let r = "Low";
switch(d) {
case 0:
r= "High";
break;
case 1:
r= "Mid";
break;
default:
r="Low";
}
return r;
}
Insert cell
y = d3.scaleBand()
.domain(Object.keys(v_lookup).reverse())
.range([height - margin.bottom, margin.top])
.padding(.1)
.paddingOuter(.5)
Insert cell
x = d3.scaleLinear()
// .domain([0, 3]).nice()
.domain([0, 2]).nice()
.range([margin.left, width - margin.right])
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${margin.top})`)
// .call(d3.axisTop(x).tickValues([0, 1, 2, 3]).tickFormat((d, i) => energy(i)).tickPadding(10))
.call(d3.axisTop(x).tickValues([0, 1, 2]).tickFormat((d, i) => energy(i)).tickPadding(10))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:first-of-type text").clone()
.attr("y", -50)
.attr("x", -18)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.html("Energy Use"))
Insert cell
yAxis = g => g
.attr("transform", `translate(${5},0)`)
.call(d3.axisRight(y).tickFormat(d => v_lookup[d]))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").remove())
Insert cell
yAxis_ticks = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisRight(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll("text").remove())
.call(g => g.selectAll(".tick line")
.attr("stroke-opacity", 0.1)
.attr("x2", width - margin.left - margin.right))
Insert cell
scale = d3.scaleLinear()
.domain([0, 100])
.range([0, y.bandwidth()/1.5])
Insert cell
v_lookup = ({
// "WWR": "Window Wall Ratio (%)",
// "FORMFACT": "Form Factor",
"WINU": "Window Assembly U (BTU/h-ft2-°F)",
"WINSHGC": "Window SHGC",
"WALLR": "Effective Wall R (h-ft2-°F/BTU)",
"ROOFR": "Effective Roof R (h-ft2-°F/BTU)",
"INFIL": "Infiltration (CFM/ft2)",
"EPD": "Equipment (W/ft2)",
"LPD": "Lighting (W/ft2)",
// "ACH": "Air Changes per Hour",
"VENTSCH": "Ventilation Schedule",
"HUMIDMAX": "Humidity Control Max RH",
"SAT": "Supply Temperature Reset (°F)",
"FANPOWER": "Fan Pressure Drop (in. H2O)",
// "OASYSTEM": "Outside Air System",
"ECON": "Economizer Operation",
"HRV": "Heat Recovery Ventilation",
"COOLCOP": "Cooling COP",
"HEATCOP": "Heating COP"
})
Insert cell
x_lookup = ({
"WWR": ["80%", "60%", "40%"],
"FORMFACT": [1.4, 1.2, 0.9],
"WINU": [0.9, 0.6, 0.3],
"WINSHGC": [0.4, 0.3, 0.2],
"WALLR": [5, 10, 15],
"ROOFR": [10, 15, 20],
"INFIL": [0.10, 0.06, 0.02],
"EPD": [9, 6, 3],
"LPD": [1.2, 0.9, 0.6],
"ACH": [2.25, 1.50, 0.75],
"VENTSCH": ["Always On", "Reduced", "Minimum"],
"HUMIDMAX": ["50%", "70%", "90%"],
"SAT": ["55", "60", "NA"],
"FANPOWER": [8, 6, 4],
"OASYSTEM": ["Recirculating", "Once Through", "NA"],
"ECON": ["No Economizer", "Drybulb", "NA"],
"HRV": ["0", "40%", "70%"],
"COOLCOP": [1, 3, 6],
"HEATCOP": [0.7, 1.0, 3.5]
})

Insert cell
serialize = {
const xmlns = "http://www.w3.org/2000/xmlns/";
const xlinkns = "http://www.w3.org/1999/xlink";
const svgns = "http://www.w3.org/2000/svg";
return function serialize(svg) {
svg = svg.cloneNode(true);
const fragment = window.location.href + "#";
const walker = document.createTreeWalker(svg, NodeFilter.SHOW_ELEMENT, null, false);
while (walker.nextNode()) {
for (const attr of walker.currentNode.attributes) {
if (attr.value.includes(fragment)) {
attr.value = attr.value.replace(fragment, "#");
}
}
}
svg.setAttributeNS(xmlns, "xmlns", svgns);
svg.setAttributeNS(xmlns, "xmlns:xlink", xlinkns);
const serializer = new window.XMLSerializer;
const string = serializer.serializeToString(svg);
return new Blob([string], {type: "image/svg+xml"});
};
}
Insert cell
highlight = (d, i) => {
const objs = Object.entries(d).filter(dd => dd[0] != "variable").sort((a, b) => b[1] - a[1]);
const index = +objs[0][0];
return index === i ? "#00695C": "#dfdfdf";

}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more