Published
Edited
Feb 8, 2019
1 fork
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
option2 = {
// Constants
const scaleEnabled = true
const minConnections = 1
const bgColor = "white" //"#F5F5F5"
const height = 650;
const minSizePx = 24
const sizeModifier = 1.1
const userSize = 2;
const siteSize = 1;
const trackerSize = 1; //0.5;
const sizeIncrement = 2;
const siteDistance = 100;
const trackerDistance = 50;
const stroke = 2;
let siteNodes = [];
let trackerNodes = [];
let maxTracking = 0;

// Builds links & nodes objects
for (var site of data) {
const trackers = site.trackers
siteNodes.push({
name: site.url,
icon: site.icon,
trackers: Object.keys(site.trackers),
group: 100,
size: siteSize,
outerSize: siteSize*1.7
})
for (var trackerGroup in trackers) {
if (!trackers.hasOwnProperty(trackerGroup)) continue;
const inx = trackerNodes.findIndex(n => n.name === trackerGroup)
if (inx !== -1) {
trackerNodes[inx].connections.push(site.url)
if (trackerNodes[inx].connections.length > maxTracking)
maxTracking = trackerNodes[inx].connections.length
if (scaleEnabled) trackerNodes[inx].outerSize *= sizeModifier
} else {
trackerNodes.push({
type: 'tracker',
name: trackerGroup,
group: 300,
connections: [site.url],
icon: `http://localhost:8080/${icons[trackerGroup]}`,
// icon: icons[trackerGroup],
size: trackerSize * 0.7,
// outerSize: trackerSize * 2
})
}
}
}
// Filter connections
trackerNodes = trackerNodes.filter(n => n.connections.length >= minVal)
const
radius = Math.min(width, height) / 3,
innerRadius = 0.2 * radius

// Main rendering SVG elements
const svg = d3.select(DOM.svg(width, height))
.attr("viewBox", [-width / 2, -height / 2, width, height]);
const pie = d3.pie()
.value(1)
.endAngle(Math.PI * 1)
.sort((a,b) => {
if (b.connections.length < a.connections.length) return -1
if (b.connections.length > a.connections.length) return 1
return 0
})
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius((d) => d.outerRadius);
const trackersPie = pie(trackerNodes)
// Calculate Gradient & Radius
const startColor = [255, 131, 131]
const endColor = [255, 196, 43]
trackersPie.forEach(tracker => {
tracker.outerRadius = (radius - innerRadius) * (tracker.data.connections.length / maxTracking) + innerRadius
tracker.innerRadius = innerRadius
tracker.fill = [0,0,0]
startColor.forEach((color, i) => {
tracker.fill[i] = color + (tracker.index / trackersPie.length) * (endColor[i] - color)
})
})
const minRadius = Math.min(...trackersPie.map(d => d.outerRadius))
// Main background
svg.append('rect')
.attr("width", width)
.attr("height", height)
.attr("x", -width/2)
.attr("y", -height/2)
.attr("fill", bgColor);

const trackers = svg.append("g")
.attr("transform", `translate(100,0)`)
.selectAll()
.data(trackersPie)
.enter().append("g")
.attr("class", "trackerChart")
.on("mouseover", (d) => {
d3.selectAll('.trackerChart')
.filter(k => k.data.name !== d.data.name)
.transition()
.duration(1)
.attr('opacity', '0.1')
d3.selectAll('.sites')
.filter(k => !k.data.trackers.includes(d.data.name))
.transition()
.duration(200)
.attr('opacity', '0.1')
d3.selectAll('.sites')
.filter(k => k.data.trackers.includes(d.data.name))
.select('circle')
.attr('stroke', "#ff0000") //C6D6E1
.attr('stroke-width', 2)
})
.on("mouseout", (d) => {
d3.selectAll('.trackerChart')
.filter(k => k.data.name !== d.data.name)
.transition()
.duration(100)
.attr('opacity', '1.0')
d3.selectAll('.sites')
.filter(k => !k.data.trackers.includes(d.data.name))
.transition()
.duration(100)
.attr('opacity', '1.0')
d3.selectAll('.sites')
.filter(k => k.data.trackers.includes(d.data.name))
.select('circle')
.attr('stroke', "#C6D6E1")
.attr('stroke-width', 1)
})
const logoOffset = 25
trackers.append("path")
.attr("fill", (d) => `rgb(${d.fill[0]}, ${d.fill[1]}, ${d.fill[2]})`)
.attr("d", arc)
trackers.append("line")
.attr("x1", d => getCenter(d)[0])
.attr("y1", d => getCenter(d)[1])
.attr("x2", d => getCenter(d, logoOffset)[0])
.attr("y2", d => getCenter(d, logoOffset)[1])
.attr("stroke", "#FFE2E2")
.attr("stroke-width", 1)
.attr("stroke-dasharray", "3,3")
trackers.append("circle")
.attr("cx", d => getCenter(d)[0])
.attr("cy", d => getCenter(d)[1])
.attr("fill", '#FE5050')
.attr("r", 3)
trackers.append("svg:image")
.attr("xlink:href", d => d.data.icon)
.attr("x", d => getCenter(d, logoOffset)[0] - d.data.size * minSizePx / 2)
.attr("y", d => getCenter(d, logoOffset)[1] - d.data.size * minSizePx / 2)
.attr("width", d => d.data.size * minSizePx)
.attr("height", d => d.data.size * minSizePx);
svg.append("circle")
.attr("transform", `translate(100,0)`)
.attr("r", innerRadius)
.attr("fill", "#FE5050")
// svg.append("circle")
// .attr("transform", `translate(100,0)`)
// .attr("r", ((minRadius-innerRadius)/2)+innerRadius)
// .attr("fill", "#FE5050")
// .attr("fill-opacity", 0.1)
// svg.append("circle")
// .attr("transform", `translate(100,0)`)
// .attr("r", minRadius)
// .attr("fill", "#FE5050")
// .attr("fill-opacity", 0.04)
const siteBlockSize = [300, 250]
const pack = d3.pack()
.size(siteBlockSize)
.padding(10);
const sitesH = d3.hierarchy({children: siteNodes})
.sum(d => d.size)
.sort((a,b) => b.size - a.size)
const sites = svg.append("g")
.attr("transform", `translate(-${siteBlockSize[0]/2+250} -${siteBlockSize[1]/2})`)
.selectAll()
.data(pack(sitesH).children)
.enter().append('svg')
.attr("class", "sites")
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("width", d => d.data.outerSize * minSizePx)
.attr("height", d => d.data.outerSize * minSizePx)
// .enter().append("svg:image")
// .attr("xlink:href", d => d.data.icon)
// .attr("x", d => d.x)
// .attr("y", d => d.y)
// .attr("width", d => d.data.size * minSizePx)
// .attr("height", d => d.data.size * minSizePx);
sites.append("circle")
.attr("cx", "50%")
.attr("cy", "50%")
.attr("r", "47%")
.attr("fill", "none")
.attr("stroke", "#C6D6E1")
.attr("stroke-width", 1)
sites.append("svg:image")
.attr("xlink:href", d => d.data.icon)
.attr("x", d => d.data.outerSize * minSizePx / 2 - d.data.size * minSizePx / 2)
.attr("y", d => d.data.outerSize * minSizePx / 2 - d.data.size * minSizePx / 2)
.attr("width", d => d.data.size * minSizePx)
.attr("height", d => d.data.size * minSizePx);


return svg.node();
}
Insert cell
Insert cell
Insert cell
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