Published
Edited
Mar 8, 2022
Importers
2 stars
Insert cell
Insert cell
baseimage = FileAttachment("2021-04-honeycomb-kafka-migration.png").image()
Insert cell
baseimage.cloneNode(true)
Insert cell
Insert cell
Insert cell
Insert cell
time2image = d3.scaleTime()
.domain(["2020-03-09T06:00Z", "2020-03-10T09:00Z"].map(Date.parse))
.range([133, 2654])
Insert cell
Insert cell
Insert cell
annotations = [
mark("2020-03-09T14:30Z", "shedding some application-side load off impacted partitions", {
w: 320, side: "left", color: "orange"
}),
mark("2020-03-09T19:30Z", "shedding more load", {w: 100, color: "orange", side: "left"}),
mark("2020-03-09T21:40Z", "shedding more load", {w: 100, color: "orange"}),
mark("2020-03-10T08:45Z", "rebalancing load post-recovery", {w: 150, color: "orange", side: "left"})
/* we'll describe the use for lane() in another notebook
lane("2020-03-09T21:40Z", "2020-03-10T01:00Z", {note:"resizing ec2s", y:70},[
["2020-03-10T01:00Z", "2020-03-10T01:45Z", {opacity:.5}]
])
*/
]
Insert cell
annotatedImage = {
const view = html`
<style>
.annotated {position: relative; height: ${annotationsHeight}px; /* units of screen pixels */ }
.annotated img {position: absolute; bottom: 0;}
.annotated svg {position: absolute; top: 0;}
${markCSS}
${laneCSS}
</style>
<div class="annotated">
${baseimage.cloneNode(true)}
<svg viewBox="0 0 ${baseimage.naturalWidth} ${screen2image(annotationsHeight)}">
${annotations}
</svg>
</div>
`
return view
}
Insert cell
Insert cell
annotationsHeight = Math.round(baseimage.height * 1.2)
Insert cell
Insert cell
markCSS = `
.annotated svg .mark p {
display: inline-block;
margin: 0;
font-size: ${screen2image(20)}px;
}
`
Insert cell
function mark(when, note, {y=0, color="red", side="right", w=null}={}) {
const x = time2image(new Date(when))
const y1 = screen2image(y)
const y2 = screen2image(annotationsHeight)
const isRight = side == "right"
const left = isRight ? "" : " translateX(-100%)"
const pstyle = `
color: ${color};
transform: translate(${x}px, ${y1}px)${left};
${isRight ? "" : "text-align: right;"}
${(w == null) ? "" : `max-width: ${screen2image(w)}px;`}
`.replace(/(\n| +)/, "")
return svg`
<g class="mark">
<line stroke=${color} stroke-width=${screen2image(2)}
x1=${x} x2=${x} y1=${y1} y2=${y2} />
<foreignObject x=0 y=0
width=${baseimage.naturalWidth}
height=${y2}>
<p style="${pstyle}">${note}</p>
</foreignObject>
</g>`
}
Insert cell
laneCSS = `
.annotated svg .lane {
display: inline-block;
position: relative;
transform: translate(var(--x), var(--y));
height: ${screen2image(28)}px;
font-size: ${screen2image(20)}px;
}
.annotated svg .lane time {
display: inline-block;
position: relative;
height: 100%;
}
.annotated svg .lane time:after {
content:'';
position: absolute; top: 0; left: 0;
width: 100%; height: 100%;
background-color: var(--color);
opacity: var(--opacity);
}
.annotated svg .lane span {
display: inline-block;
position: absolute; top:0; left: 0;
width: 100%; height: 100%;
text-align: center;
}
}`
Insert cell
function lane(first, newer, {note="", y=0, color="red", opacity=0.2}={},
[...spans]=[]) {
const foHeight = screen2image(annotationsHeight)
const x0 = time2image(new Date(first))
const x1 = time2image(new Date(newer))
const y0 = screen2image(y)
const timespans = spans.map(([older, newer, {opacity=0.5}={}]) => {
const sx0 = time2image(new Date(older))
const sx1 = time2image(new Date(newer))
return html`<time style="width: ${sx1-sx0}px;--opacity:${opacity};"></time>`
})
return svg`
<foreignObject x=0 y=0 width=${baseimage.naturalWidth} height=${foHeight}>
<div class=lane style="--x:${x0}px;--y:${y0}px;--color:${color};">
<time style="width:${x1-x0}px;--opacity: ${opacity};"></time>${timespans}
<span>${note}</span>
</div></foreignObject>`
}
Insert cell
Insert cell
screen2image = d3.scaleLinear().domain([0, width]).range([0, baseimage.naturalWidth])
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