Published
Edited
Nov 11, 2021
Insert cell
Insert cell
chart(data)
Insert cell
chart = (dataset) => {
const hXBrush = 40
, wYBrush = 40
, mYBrush = {t: 10, b: 15, l: 40, r: 10}
, mXBrush = {t: 10, b: 20, l: 0, r: 10}
const wSVG = width + margin.left + margin.right + wYBrush + mYBrush.l + mYBrush.r
, hSVG = height + margin.top + margin.bottom + hXBrush + mXBrush.t + mXBrush.b
const svg = d3.select(DOM.svg(wSVG,hSVG))

const scatterplot = svg.append("g")
.attr('transform', `translate(${margin.left + mYBrush.l + mYBrush.r + wYBrush}, ${margin.top})`);

const clip = DOM.uid("clip")

scatterplot.append("defs")
.append("clipPath")
.attr("id", clip.id)
.append("rect")
.attr("width", width)
.attr("height", height - hXBrush);

const xScale = d3.scalePoint()
.domain(Object.values(known_ips_map))
.range([50, width]);

const xAxis = scatterplot.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${height - hXBrush})`)
.call(d3.axisBottom(xScale)
.ticks(Object.values(known_ips_map).length))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");

const portRange = [0, 65530];
const yScale = d3.scaleLinear()
.range([height - hXBrush, 0])
.domain(portRange);

const yAxis = scatterplot.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));

const circles = scatterplot.selectAll("circle")
.data(dataset)
.join("circle")
.attr("clip-path", clip)
.attr("cx", d => xScale(ipaddress_map[d["SrcAddr"]]))
.attr("cy", d => yScale(d["Sport"]))
.attr("r", 5)
.style("fill-opacity", 1)
.style("fill", d => {
return "steelblue"
});

// Brushing Sections
// X Brushing
const contextX = svg.append("g")
.attr("class", "context")
.attr("transform",
`translate(${wYBrush + margin.left + mXBrush.l + mYBrush.l + mYBrush.r},${height + margin.top + margin.bottom + mXBrush.t})`);
const xXScaleBrush =
d3.scalePoint()
.range([0, width])
.domain(Object.values(known_ips_map))

xXScaleBrush.invert = (function(){
var domain = xXScaleBrush.domain()
var range = xXScaleBrush.range()
var scale = d3.scaleQuantize().domain(range).range(domain)

return function(x){
return scale(x)
}
})()
const yXScaleBrush = d3.scaleLinear()
.range([hXBrush, 0])
.domain(d3.extent(dataset, d => d["Sport"]));
// X Brushing
function brushedX({selection}) {
console.log(selection)
let extent = selection.map(xXScaleBrush.invert, xXScaleBrush);
console.log("Extent");
console.log(extent);
xScale.domain(extent);
circles.attr("cx", d => xScale(ipaddress_map[d["SrcAddr"]]))
xAxis.call(d3.axisBottom(xScale));
}
const brushX = d3.brushX().extent([[0, 0],[width, hXBrush]]).on("brush", brushedX);

contextX.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0,${hXBrush})`)
.call(d3.axisBottom(xXScaleBrush));

contextX.selectAll("circle")
.data(dataset)
.join("circle")
.style("fill", "#555")
.attr("r", 2)
.attr("cx", d => xScale(ipaddress_map[d["SrcAddr"]]))
.attr("cy", d => yXScaleBrush(d["Sport"]))

contextX.append("g")
.attr("class", "x brush")
.call(brushX);
// Y Brushing
const contextY = svg.append("g")
.attr("class", "context")
.attr("transform", `translate(${mYBrush.l},${margin.top})`);

const yYScaleBrush = d3.scaleLinear()
.range([height - margin.top - margin.bottom, 0])
.domain(portRange);
const xYScaleBrush = d3.scalePoint()
.range([0, wYBrush])
.domain(Object.values(known_ips_map))
function brushedY({selection}) {
let extent = selection.map(yYScaleBrush.invert, yYScaleBrush);
yScale.domain([extent[1], extent[0]]);
circles.attr("cy", d => yScale(d["Sport"]))
yAxis.call(d3.axisLeft(yScale));
}
const brushY = d3.brushY().extent([[0, 0],[wYBrush, height - hXBrush]]).on("brush", brushedY);


contextY.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yYScaleBrush));

contextY.selectAll("circle")
.data(dataset)
.join("circle")
.style("fill", "#555")
.attr("r", 2)
.attr("cy", d => yScale(d["Sport"]))
.attr("cx", d => xYScaleBrush(ipaddress_map[d["SrcAddr"]]))
contextY.append("g")
.attr("class", "y brush")
.call(brushY);
return svg.node()
}
Insert cell
Insert cell
Insert cell
{
for await (const i of asyncIterableRange(data.length, 1000)) {
// yield chart(data.slice(0, i));
}
}
Insert cell
margin = [{top: 30, right: 10, bottom: 30, left:30}][0]
Insert cell
xXScaleBrush =
d3.scalePoint()
.range([0, width])
.domain(Object.values(known_ips_map))
Insert cell
xXScaleBrush.invert = (function(){
var domain = xXScaleBrush.domain()
var range = xXScaleBrush.range()
var scale = d3.scaleQuantize().domain(range).range(domain)

return function(x){
return scale(x)
}
})()
Insert cell
s = [193, 402]
Insert cell
s.map(x => x)
Insert cell
xXScaleBrush.invert(193)
Insert cell
Insert cell
height = 500
Insert cell
width = 500
Insert cell
ipaddress_text = FileAttachment("ipdict_SWaT.json").text()
Insert cell
ipaddress_map = JSON.parse(ipaddress_text)
Insert cell
known_ips_text = FileAttachment("knownips_SWaT.json").text()
Insert cell
known_ips_map = JSON.parse(known_ips_text)
Insert cell
yAxis = (g, y) => g
.call(d3.axisLeft(y))
.call(g => g.select(".domain").attr("display", "none"))
Insert cell
# Appendix
Insert cell
d3 = require("d3@6")
Insert cell
db = DatabaseClient("SWaTFirstDataset")
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