Public
Edited
May 12
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
characterListView
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
bibleBooks = FileAttachment("bible_books@2.csv").csv()
Insert cell
biblePerson = FileAttachment("BibleData-Person.csv").csv()
Insert cell
biblePersonVerse = FileAttachment("BibleData-PersonVerse.csv").csv()
Insert cell
biblePersonVerse
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
data = {
let tempArr = new Array();
for (const person of biblePersonVerse) {
if (person.person_id.includes("Abram")) {
tempArr.push(person);
}
}
return tempArr;
}
Insert cell
testmap = new Map([["Yes", true], ["No", false]])
Insert cell
// See: https://observablehq.com/@nyuvis/javascript-basics
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

// Bible book map
books = {
let bb = new Map();
for (let i = 0; i < bibleBooks.length-1; ++i) {
bb.set(bibleBooks[i].Book, {name : bibleBooks[i].Book, chapters : bibleBooks[i].Chapters, verses: bibleBooks[i].Verses})
}
return Object.fromEntries(bb);
}
Insert cell
booksMapObj = ({
'Genesis': {name : bibleBooks[0].Book, chapters : bibleBooks[0].Chapters, verses: bibleBooks[0].Verses},
'Exodus': {name : bibleBooks[1].Book, chapters : bibleBooks[1].Chapters, verses: bibleBooks[1].Verses}
})
Insert cell
booksMapObj.Exodus
Insert cell
stories = [
{name: "The Fall", book: books.Genesis, startChap: 1, endChap: 4},
{name: "Noah's Ark", book: books.Genesis, startChap: 5, endChap: 11},
{name: "Calling of Abraham", book: books.Genesis, startChap: 12, endChap: 23},
{name: "Time of Issac", book: books.Genesis, startChap: 21, endChap: 26},
{name: "Time of Jacob", book: books.Genesis, startChap: 26, endChap: 36},
{name: "Joseph Goes to Egypt", book: books.Genesis, startChap: 37, endChap: 50},
{name: "Moses Beginings", book: books.Exodus, startChap: 1, endChap: 3},
{name: "The Plagues of Egypt", book: books.Exodus, startChap: 4, endChap: 15},
{name: "In the Desert", book: books.Exodus, startChap: 16, endChap: 40}
]
Insert cell
biblePerson[0]
Insert cell
characters = {
if (useTestData) {
return [
{ name: "Adam", ageDied: 930, father: "God", uniqueAttribute: "First man", personID: "Adam_1"},
{ name: "Noah", ageDied: 950, father: "Lamech", uniqueAttribute: "Survived the flood", personID: "Noah_1"},
{ name: "Abraham", ageDied: 175, father: "Terah", uniqueAttribute: "Called to be a great nation", personID: "Abraham_1"},
{ name: "Moses", ageDied: 120, father: "Amram", uniqueAttribute: "Survived the river", personID: "Moses_1"},
{ name: "Joshua", ageDied: null, father: "Nun", uniqueAttribute: "First man", personID: "Joshua_1"},
{ name: "Jesus", ageDied: 33, father: "Joseph", uniqueAttribute: "LORD, King", personID: "Jesus_1"}
];
} else {
let tempArr = new Array();
for (const person of biblePerson) {
let tempUA = "\n - ";
let sliceUA = person.unique_attribute;
let sliceCnt = 0;

// Split text for the person's unique attribute
sliceUA = sliceUA.split(" ");
for (const w of sliceUA) {
if (sliceCnt < 4) {
tempUA += w + " ";
} else {
tempUA += w + " \n";
sliceCnt = 0;
}
sliceCnt++;
}
tempArr.push({
name: person.person_name,
ageDied: null,
father: null,
uniqueAttribute: tempUA,
personID: person.person_id
});
}
return tempArr;
}
}
Insert cell
Insert cell
{
let tempChar = characters.filter(e => e.name == "Abram");
return {charID: characters.indexOf(tempChar[0]), charData: tempChar[0]}
}
Insert cell
storiesCharacters = {
const isDebug = false;
if (useTestData) {
return [
{ storyID: 0, charID: 0},
{ storyID: 1, charID: 1},
{ storyID: 2, charID: 2},
{ storyID: 6, charID: 3},
{ storyID: 7, charID: 3},
{ storyID: 8, charID: 3},
{ storyID: 8, charID: 4},
{ storyID: 0, charID: 5},
{ storyID: 7, charID: 5}
];
} else {
let tempSCs = new Array();
for (const pv of biblePersonVerse) {
let person = characters.find(p => p.personID == pv.person_id);
let tempCharID = characters.indexOf(person);
let tempStoryID = 0;
let tempBookID = 0;
let newSC = null;
if (person != null){
for (const story of stories) {
tempBookID = bibleBooks.indexOf(bibleBooks.find(e => e.Book == story.book.name));
if(isDebug)
console.log(`Search the person verse reference (${pv.reference_id}) table includes
the book ${story.book.name} with id ${tempBookID}`);

if (pv.reference_id.includes(bibleBookRef[tempBookID])) {
let tempVerse = parseInt(pv.reference_id.split(" ")[1].split(":")[0]);

if(isDebug)
console.log(`Is the verse referenced in "${pv.reference_id}" between ${story.startChap}
and ${story.endChap} in the book of ${story.book.name}`);
if (tempVerse >= story.startChap && tempVerse <= story.endChap){
newSC = { storyID: tempStoryID, charID: tempCharID}

if(isDebug)
console.log(`Does the story and character relation exist for ${story.book.name}
and ${person.name}`);
if(!tempSCs.includes(e => e == newSC)) {
tempSCs.push(newSC);
}
}
}
tempStoryID++;
}
// if(e => pv.reference_id.includes(e.abbr)) {
// console.log(pv.reference_id)
// }
// newSC = { storyID: tempStoryID, charID: tempCharID}
// if(!tempSCs.includes(e => e == newSC)) {
// tempSCs.push()
// }
// return pv
}
}
return tempSCs;
// return [
// { storyID: 66, charID: 66}
// ];
}
}
Insert cell
storiesCharacters
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
bibleBookRef = ["GEN", "EXO"]
Insert cell
originG = ({name: "God"})
Insert cell
// Calculate node max radious
nodes = {
let n = new Array(numLevels);
for (let i = 0; i < numLevels; ++i) {
if(i == 0){
n[i]=r0;
} else{
n[i]=n[i-1]/2
}
}
return n;
}
Insert cell
// Calculate level radious
levels = {
let l = new Array(numLevels);
for (let i = 0; i < numLevels; ++i) {
if(i == 0){
l[i] = 2*r0;
} else{
l[i] = l[i-1] + nodes[i-1] + nodes[i];
}
}
return l;
}
Insert cell
// Code base from: https://observablehq.com/@proquestionasker/playing-with-concentric-circles/2
myCreateNodes = function pointsOnCircle(nodes, nodeData, leveID, prevLevelCount){
var angle = (2 * Math.PI)/nodes;
var startAngleInLevel = (Math.PI/180)*30*leveID;
var points = [];
for(var i = 0; i < nodes; i++){
points.push({
x:Math.cos(i*angle+startAngleInLevel),
y:Math.sin(i*angle+startAngleInLevel),
rotation:i*angle,
label:'point' + i,
color: (i+prevLevelCount),
nodeData: nodeData[i]
})
}
return points;
}

Insert cell
Insert cell
viewof r0 = Inputs.range([5, 100], {label: "Radious of Node0:", step: 5, value: 65})
Insert cell
viewof nodeScale = Inputs.range([0.5, 1], {label: "Scale of node: ", step: .05, value: .9})
Insert cell
viewof numLevels = Inputs.range([1, 6], {label: "Number of Levels: ", step: 1, value: 3})
Insert cell
dimensions = ({width: 1000, height: 600})
Insert cell
selectAll = "All"; //Constat variable
Insert cell
Insert cell
Insert cell
Insert cell
// Even points scale by level
numNodesLevel = {
let nl = new Array(numLevels);
for (let i = 0; i < numLevels; ++i) {
nl[i] = Math.pow(2,(i+1));
}
return nl;
}
Insert cell
// Even points scale by level
nodesLevelScale = {
let nl_scale = new Array(numLevels);
for (let i = 0; i < numLevels; ++i) {
nl_scale[i] = d3.scaleLinear()
.range([dimensions.width/2-levels[i], dimensions.width/2+levels[i]])
.domain([-1, 1])
}
return nl_scale;
}
Insert cell
moireTree1 = {
// Create a square
const svg = DOM.svg(dimensions.width, dimensions.height);
const color = d3.scaleSequential(d3.interpolateViridis).domain([0, 10]);

//Add concentric circles, levels
for (let i = 0; i < numLevels; ++i) {
let evenPoints = createNodes(numNodesLevel[i])
// Add concentric circles
d3.select(svg)
.append("circle")
.attr("r", levels[i])
.attr('cx', dimensions.width/2)
.attr('cy', dimensions.height/2)
// .attr('cx', scale(0))
// .attr('cy', scale(0))
.attr('class', 'outerCircle')
.attr('stroke', 'black')
.attr('fill', 'none')
.attr('stroke-width', '2px')
.attr('stroke-dasharray', '10 5')

// Add circles too levels
d3.select(svg)
.selectAll(".pointCircle")
.data(evenPoints)
.enter().append("circle")
.attr('class', 'pointCircle'+i)
.attr("r", 5)
.attr('cx', d => nodesLevelScale[i](d.x))
.attr('cy', d => nodesLevelScale[i](d.y)-(dimensions.width/2-dimensions.height/2))
.attr('fill', (d, i) => color(i))
.attr('stroke', 'white')
.attr('stroke-width', '3px')
.transition()
.duration(1000)
.delay((d, i) => i * 100)
.attr('r', nodes[i]*nodeScale)
}
return svg;
}
Insert cell
Insert cell
scale1 = d3.scaleLinear()
.range([dimensions.width/2-levels[2], dimensions.width/2+levels[2]])
.domain([-1, 1])
Insert cell
scale0 = d3.scaleLinear()
.range([dimensions.width/2-r0*2, dimensions.width/2+r0*2])
.domain([-1, 1])
Insert cell
// Code based on: https://observablehq.com/@proquestionasker/playing-with-concentric-circles/2
moireTree0 = {
// Create a square
const svg = DOM.svg(dimensions.width, dimensions.height);
const color = d3.scaleSequential(d3.interpolateViridis).domain([0, 10]);

const numNodesLevel = 4;
const evenPoints = createNodes(numNodesLevel)

//Add concentric circles, levels
for (let i = 0; i < numLevels; ++i) {
d3.select(svg)
.append("circle")
.attr("r", levels[i])
.attr('cx', dimensions.width/2)
.attr('cy', dimensions.height/2)
// .attr('cx', scale(0))
// .attr('cy', scale(0))
.attr('class', 'outerCircle')
.attr('stroke', 'black')
.attr('fill', 'none')
.attr('stroke-width', '2px')
.attr('stroke-dasharray', '10 5')
}
d3.select(svg)
.selectAll(".pointCircle")
.data(evenPoints)
.enter().append("circle")
.attr('class', 'pointCircle')
.attr("r", 10)
.attr('cx', d => scale0(d.x))
.attr('cy', d => scale0(d.y)-(dimensions.width/2-dimensions.height/2))
// .attr('cx', d => scale(d.x))
// .attr('cy', d => scale(d.y))
.attr('fill', (d, i) => color(i))
.attr('stroke', 'white')
.attr('stroke-width', '3px')
.transition()
.duration(1000)
.delay((d, i) => i * 100)
.attr('r', nodes[0]*3/numNodesLevel)
// .on("start", function repeat() {
// d3.active(this)
// .attr('r', 8)
// .transition()
// .attr("r", 12)
// .transition()
// .on("start", repeat);
// });
return svg;
}
Insert cell
// Code from: https://observablehq.com/@proquestionasker/playing-with-concentric-circles/2
createNodes = function pointsOnCircle(num){
var angle = (2 * Math.PI)/num;
var points = [];
var i=0;
for(var a = 0; a < (2 * Math.PI); a+=angle){
i++;
points.push({
x:Math.cos(a),
y:Math.sin(a),
rotation:a,
label:'point' + i
})
}
return points;
}

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