Published
Edited
Feb 22, 2021
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
alphabet = literals.map(d => {
return {
symbol: d[0][0],
codeWord: d[0],
spelling: d[1],
morseCode: d[2]
};
})
Insert cell
literals = [
["Alfa", "AL-FAH", ".-"], // full stop, hyphen-minus
["Bravo", "BRAH-VOH", "-..."],
["Charlie", "CHAR-LEE", "-.-."],
["Delta", "DELL-TAH", "-.."],
["Echo", "ECK-OH", "."],
["Foxtrot", "FOCKS-TROT", "..-."],
["Golf", "GOLF", "--."],
["Hotel", "HO-TELL", "...."],
["India", "IN-DEE-AH)", ".."],
["Juliett", "JEW-LEE-ETT", ".---"],
["Kilo", "KEY-LOH", "-.-"],
["Lima", "LEE-MAH", ".-.."],
["Mike", "MIKE", "--"],
["November", "NO-VEM-BER", "-."],
["Oscar", "OSS-CAH", "---"],
["Papa", "PAH-PAH", ".--."],
["Quebec", "KEH-BECK", "--.-"],
["Romeo", "ROW-ME-OH", ".-."],
["Sierra", "SEE-AIR-RAH", "..."],
["Tango", "TANG-GO", "-"],
["Uniform", "YOU-NEE-FORM", "..-"],
["Victor", "VIK-TAH", "...-"],
["Whiskey", "WISS-KEY", ".--"],
["X-ray", "ECKS-RAY", "-..-"],
["Yankee", "YANG-KEY", "-.--"],
["Zulu", "ZOO-LOO", "--.."],
]
Insert cell
Insert cell
Insert cell
chart = (alphabet, options) => {
const svg = d3.create("svg")
.attr("class", "phonetic-alphabet")
.attr("width", options.sideLength)
.attr("height", options.sideLength);

const centerPoint = options.sideLength / 2;
const plane = svg.append("g")
.attr("class", "plane")
.attr("transform", `translate(${centerPoint},${centerPoint})`);
const appendCharacter = (container, character, index) => {
const angleInDegree = 360 / alphabet.length * index;
let transform = `rotate(${angleInDegree - 90.1}) translate(${options.innerRadius},0)`;
const characterContainer = container.append("g")
.attr("class", "character")
.attr("transform", transform);
const dx = appendMorseCode(characterContainer, character.morseCode);
let text = character.codeWord;
if (options.showSpelling) {
text += ` (${character.spelling})`;
}
characterContainer.append("text")
.attr("class", "code-word")
.attr("dx", dx + 5)
.attr("dy", 6)
.text(text);
characterContainer.append("text")
.attr("class", "symbol")
.attr("dx", dx + 5)
.attr("dy", 6)
.text(character.symbol);
if (angleInDegree >= 180) {
const textWidth = measureWidth(text);
const containerWidth = dx + 5 + textWidth;
transform += ` rotate(180) translate(-${containerWidth},0)`;
characterContainer.attr("transform", transform);
}
};

alphabet.forEach((character, index) => appendCharacter(plane, character, index));
if (options.scaleToFit) {
svg.attr("viewBox", autoBox);
}
return svg.node();
}
Insert cell
d3 = require("d3-selection@2")
Insert cell
appendMorseCode = (container, morseCode) => {
const codeContainer = container.append("g")
.attr("class", "morse-code");

let x = 0;
for (const signal of morseCode) {
if (signal === '.') {
x = appendDot(codeContainer, x);
} else if (signal === '-') {
x = appendDash(codeContainer, x);
} else {
throw `Unknown signal: '${signal}'`;
}
}

return x;
}
Insert cell
appendDot = (container, x = 0) => {
const dotWidth = 4;
const margin = x ? 4 : 0;
container.append("rect")
.attr("class", "dot")
.attr("x", x + margin)
.attr("width", dotWidth)
.attr("height", 4);

return x + margin + dotWidth;
}
Insert cell
appendDash = (container, x = 0) => {
const dashWidth = 12
const margin = x ? 4 : 0;
container.append("rect")
.attr("class", "dash")
.attr("x", x + margin)
.attr("width", dashWidth)
.attr("height", 4);

return x + margin + dashWidth;
}
Insert cell
measureWidth = (text) => {
const context = document.createElement("canvas")
.getContext("2d");
context.font = "12px sans-serif";
return context.measureText(text).width;
}
Insert cell
function autoBox() {
document.body.appendChild(this);
const {x, y, width, height} = this.getBBox();
document.body.removeChild(this);
return [x, y, width, height];
}
Insert cell
html`<style>
abbr[title] {
text-underline-position: under; /* 'auto' appears broken (Chrome) */
}

.morse-code {
fill: none;
stroke: #555;
stroke-width: 0.5;
}

.symbol,
.code-word {
font-family: sans-serif;
font-size: 12px;
}

.symbol {
fill: black;
}

.code-word {
fill: gray;
}

.dot,
.dash {
fill: black;
stroke: none;
}
</style>`
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