Published
Edited
Mar 25, 2022
2 forks
Insert cell
Insert cell
Insert cell
Insert cell
output = html`
<div class='parent'>
<div id='qButtons' ></div><div></div>
<div id='rtext'></div><div id='rsvg_div'></div>
</div>

<style>
.parent { display: grid; grid-template-columns: 40% 60%; grid-template-rows: 1fr auto; gap: 0px 0px;
justify-content: stretch; align-content: start; justify-items: stretch; align-items: start;
font-family: "Fira Sans", sans-serif;}
.katex-display, figure, h1, h2, h3, h4, h5, h6, p, table { max-width: 100% }
</style>
`
Insert cell
Insert cell
// trackPosition: Array(5) [396.58, 420.34, 444.09999999999997, 467.86, 491.62]
// trackWidth: 23.759999999999998
// sectorCount: Array(5) [3, 2, 2, 2, 2]
// sectorsGap: Array(5) [30, 45, 45, 45, 45]

svg = {
let r = rings[0].radius
const svgRing = d3.create('svg').attr("width", svg_w).attr("height", svg_h) // + status area

for (let _index = 0; _index < 4; ++_index) {
svgRing.append('circle')
.attr('cx',0)
.attr('cy',0)
.attr('r', teRings.radius[_index])
.style('fill', '#F19A3E30')
.attr('transform',`translate(${svg_w/2} ${svg_h/2})`)
}

svgRing.append('line')
.attr('x1',0)
.attr('y1',svg_h/2)
.attr('x2',svg_w)
.attr('y2',svg_h/2)
.style('stroke','black')
.style('stroke-width', 1)
svgRing.append('line')
.attr('x1',svg_w/2)
.attr('y1',0)
.attr('x2',svg_w/2)
.attr('y2',svg_h)
.style('stroke','black')
.style('stroke-width', 1)

// Loop over one track of one ring and put a circle in every blip position.

for (let _ring = 0; _ring < 4; ++_ring) {
// let _ring=3
for (let _track = 0; _track < teRings.trackCount[_ring]; ++_track) { // 3
let r = teRings.ringStart[_ring]+teRings.trackPosition[_ring][_track]
for (let _sector = 0; _sector < teRings.sectorCount[_ring][_track]; ++_sector) { // 7
let a = (_sector * (teRings.sectorGap[_ring][_track]+teRings.sectorOffset[_ring][_track]))+(teRings.firstSectorOffset[_ring][_track])
svgRing.append('circle')
.attr('cx',xAngle(r,a)) // radius, angle
.attr('cy',yAngle(r,a))
.attr('r',blipSize/2)
.attr('fill', clr[_track])
.attr('transform',`translate(${svg_w/2} ${svg_h/2}) rotate(270)`)
.attr('class',`blip_track${_track}`)
}
}
}
return svgRing.node()
}
Insert cell
teRings = {
let ringRadius = [svg_h*0.25, // radius of each ring (outer edge) Using mutiply might is 'like' a percentage of total
svg_h*0.35,
svg_h*0.43,
svg_h*0.5]

let ringWidth = [ringRadius[0], // ring radius of inner ring = wing width
ringRadius[1]-ringRadius[0],
ringRadius[2]-ringRadius[1],
ringRadius[3]-ringRadius[2] ]

// the inner position of each ring.
let ringStart = [ringRadius[0]-ringWidth[0],
ringRadius[1]-ringWidth[1],
ringRadius[2]-ringWidth[2],
ringRadius[3]-ringWidth[3]]

// trackCount: number of tracks in a ring
let trackCount = [_.floor(ringWidth[0]/blipSize) -1, // -1 as inner track on inner ring has issues
_.floor(ringWidth[1]/blipSize),
_.floor(ringWidth[2]/blipSize),
_.floor(ringWidth[3]/blipSize) ]
// trackSpacing: the space each track uses
let trackSpacing = [ringWidth[0]/trackCount[0], // divide the space (width) on a ring evenly by the number of tacks
ringWidth[1]/trackCount[1],
ringWidth[2]/trackCount[2],
ringWidth[3]/trackCount[3] ]
// loop over each rings tracks to find the center of each track as a radius value.
let trackPosition = []
for (let _ring = 0; _ring < 4 ; ++_ring) {
let tempTrackPos = []
for (let _trackNumber = 0; _trackNumber < trackCount[_ring]; ++_trackNumber) {
tempTrackPos.push((trackSpacing[_ring]*_trackNumber)+(trackSpacing[_ring]/2))
}
trackPosition.push(tempTrackPos) // maybe needs to be reversed??
}
// -------------------------------------------------------------------------------------------------------------------
// the size of 1 degree of difference at tracks radius. For positioning. size in pixels)
// 90/(blipSize/ sectorSize(22) ) 22 radius with a blipSize of 22 means 1.5 blips can fit into 90 degrees (rounded down)

let sectorGap = []
for (let _ring = 0; _ring < 4 ; ++_ring) {
let tempSectorGap = []
for (let _trackNumber = 0; _trackNumber < trackCount[_ring]; ++_trackNumber) {

tempSectorGap.push(blipSize/(sectorSize(ringStart[_ring]+trackPosition[_ring][_trackNumber])))
}
sectorGap.push(tempSectorGap)
}

//---------------
// sectors positions don't start from zero but from half blip size from zero, in degress
let firstSectorOffset = []
for (let _ring = 0; _ring < 4 ; ++_ring) {
let tempFirstSectorOffset = []
for (let _trackNumber = 0; _trackNumber < trackCount[_ring]; ++_trackNumber) {

tempFirstSectorOffset.push(sectorGap[_ring][_trackNumber]*0.5) // degrees to 1st blip
}
firstSectorOffset.push(tempFirstSectorOffset)
}
//----------------------------------------------------------------------------------------------------------------------------
// how many sector fit on a track
// Doesn't count the offset padding values.
// sectorGap[_ring][_trackNumber] // 1 degree movement around track in pixels
let sectorCount = []
for (let _ring = 0; _ring < 4 ; ++_ring) {
let tempTrackSectorCount = []
for (let _trackNumber = 0; _trackNumber < trackCount[_ring]; ++_trackNumber) {

tempTrackSectorCount.push( _.floor((90-firstSectorOffset[_ring][_trackNumber])/sectorGap[_ring][_trackNumber])) // number of blips on the track
}
sectorCount.push(tempTrackSectorCount)
}

// ------------------------------------------------------------------------------
// i need to get the floored remainder and divide that by the number of blips on a track and add that to the offset.
let sectorOffset = []
for (let _ring = 0; _ring < 4 ; ++_ring) {
let tempSectorOffset = []
for (let _trackNumber = 0; _trackNumber < trackCount[_ring]; ++_trackNumber) {
let so = 90-(sectorGap[_ring][_trackNumber]*sectorCount[_ring][_trackNumber])
tempSectorOffset.push(so/sectorCount[_ring][_trackNumber])

}
sectorOffset.push(tempSectorOffset)
}
// sectorGap[]
// ------------------------------------------------------------------------------
let quadrantName = _.uniq(blips.map(d => d.quadrant))
let quadrantRotation = [ 0, 90, 180, 270 ] // rotatation value for quadrent (I could have more quadrants by defing quadrant sizes.)
return {name:ringNames,
radius:ringRadius,
ringWidth:ringWidth,
ringStart:ringStart,
trackCount:trackCount, // number of tracks in a ring
trackSpacing:trackSpacing, // the space each track uses
trackPosition:trackPosition, // radius position []
sectorGap:sectorGap, // the size of 1 degree of difference at tracks in degrees (? radius [])
sectorCount:sectorCount, // number of blips that can fit onto a rings track []
firstSectorOffset:firstSectorOffset, // in degrees []
sectorOffset:sectorOffset,
quadrantName:quadrantName,
quadrantRotation:quadrantRotation
}
}
Insert cell
clr = ['#e35a2c','#63d9ee','#f3f836','#1caeae','#89DAFF','#FEEA00','#3F612D','#9D8189' ]
Insert cell
---
# X Y position based of radius and angle
These are worked out as if the circle center is at 0,0
Insert cell
function xAngle(radius, angle){
return radius * Math.cos(angle*DR) // need to convert degrees to radians? degrees * (PI/180)
}
Insert cell
function yAngle(radius, angle){
return radius * Math.sin(angle*DR)
}
Insert cell
Insert cell
rings=[]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
blipSize = 22 // the size of datapoint on the radar
Insert cell
ringNames = ['adopt', 'trial', 'assess','hold']
Insert cell
colour = ['#89DAFF','#FEEA00','#3F612D','#9D8189']
Insert cell
Insert cell
90/(22/sectorSize(22))
Insert cell
function sectorSize (radius) {

let radians1 = 1 * (PI/180)
let radians2 = 2 * (PI/180)
let Y0 = Math.sin(radians1)*radius
let X0 = Math.cos(radians1)*radius
let Y1 = Math.sin(radians2)*radius
let X1 = Math.cos(radians2)*radius
let a = X0 - X1
let b = Y0 - Y1
return Math.sqrt( a*a + b*b )
}

Insert cell
function toRadians (angle) {
return angle * (Math.PI / 180);
}
Insert cell
ringsize = (svg_h/2)/4 // the average size of the ring before scaling
Insert cell
scaleRings = [0.35, 0.25, 0.1, 0, 0] // the size of the rings (area) gets small further from the center
Insert cell
ringText_poz = {
let poz = []
for (let index = 1; index <= 4; ++index) {
poz.push(ringsize*index+(svg_w/2)) // radius of circle or distance from the center
}

for (let index = 1; index <= 4; ++index) {
poz.push(svg_w/2-(ringsize*index))
}
return poz
}
Insert cell
PI = Math.PI // I like this as a constant
Insert cell
DR = PI/180 // DEGREES TO RADIUNS
Insert cell
svg_w = ~~(width *0.7)
Insert cell
svg_h = ~~(svg_w*0.7)
Insert cell
Insert cell
GoogleSheet[0] // example element from the loaded CSV file.
Insert cell
GoogleSheet = await d3.csv("https://docs.google.com/spreadsheets/d/18A7oDuavlh89rAmqcaXpqle8QLqIvlAkoEUxcObzuUM/gviz/tq?tqx=out:csv&sheet=1985253373")
Insert cell
Insert cell
GoogleSheet.columns
Insert cell
Insert cell
blips = _.map(GoogleSheet, new InputSanitizer().sanitize)
Insert cell
Inputs.table(blips)
Insert cell
quadrants = _.uniq(blips.map(d => d.quadrant))
Insert cell
Insert cell
Insert cell
BS = {
let m = []
quadrants.forEach( q => { // makean array for each quadrant
m.push([])
// need to add 4 arrays to this for ADOPT TRIAL ASSESS HOLD
})
blips.forEach( b => {
m[quadrants.indexOf(b.quadrant)].push({name:b.name, ring:b.ring, isNew:b.isNew, description:b.description, quadrant:b.quadrant})
})
return m
}
Insert cell
BlipsGrouped = {
let m = {}
quadrants.forEach( (q,i) => { // makean array for each quadrant
m[q]={Adopt:[],Trial:[],Assess:[],Hold:[],zone:i*90}
})
blips.forEach( (b,i) => {
m[b.quadrant][b.ring][i]={name:b.name, isNew:b.isNew, description:b.description}

m[b.quadrant][b.ring] = _.compact(m[b.quadrant][b.ring]) // filter empty elements
})

return m
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md_colour = '#e0f0a0'
Insert cell
Insert cell
function calculateArea(myRadius) {
return (myRadius * myRadius * Math.PI);
}
Insert cell
Insert cell
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