Public
Edited
Feb 17, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
custom_sim = function( graph ) {
const r = 5
const height = 600

return d3.forceSimulation( self, graph['nodes'] )
.force("x", d3.forceX(width/2))
.force("y", d3.forceY(height/2))
.force("collide", d3.forceCollide(1.5*r))
.force("charge", d3.forceManyBody().strength(-30))
//.force("ring", ringForce() )
.force("link", d3.forceLink()
.id(function (d) { return d.id; })
.distance(d => (d.source.type == 'peer' || d.target.type == 'peer') ? 10 : 100 )
.strength( 1 )
) /* .on('tick', g => function ticked(self, graph) {
const link = self.selectAll("line")
.data(graph['links'])
.join("line");

const node = self.selectAll("circle")
.data(graph['nodes'])
.join("circle");
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
})
*/
}
Insert cell
Insert cell
c2site_map = {
var j = await FileAttachment("k-root-communities.json").json()
return j.reduce( (agg, d) => {
agg[ d.community ] = d.name
return agg
}, {})
}
Insert cell
/* canvas_html = html`
<canvas id="bgpviz" width="800" height="800">
text for unsupported browsers
</canvas>` */
Insert cell
/* { // custom force directed graph in observable
canvas_html;
var canvas = document.getElementById(canvas_html.id);
var ctx = canvas.getContext('2d')
var r = 5
var width = 800 // why does canvas.width not work here?
var height = 800
var nodes = graph.nodes
var links = graph.links
nodes.forEach( d => { // set initial positions
var angle = Math.random() * Math.PI*2
d.x = width/2 + Math.cos( angle ) * 40 * ( d.min_distance ? d.min_distance : 7 )
d.y = height/2 + Math.sin( angle ) * 40 * ( d.min_distance ? d.min_distance : 7 )
})

var simulation = d3.forceSimulation()
.force("x", d3.forceX(width/2))
.force("y", d3.forceY(height/2))
.force("collide", d3.forceCollide(1.5*r))
.force("charge", d3.forceManyBody().strength(-30))
//.force("ring", ringForce() )
.force("link", d3.forceLink()
.id(function (d) { return d.id; })
.distance(d => (d.source.type == 'peer' || d.target.type == 'peer') ? 10 : 100 )
.strength( 1 )
);


simulation.nodes( nodes )
simulation.force("link")
.links( links );
simulation.on("tick",update)


function update() {
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.globalAlpha = 0.2;
ctx.strokeStyle = "#aaa";
graph.links.forEach(drawLink);
ctx.stroke();
ctx.globalAlpha = 1.0;
graph.nodes.forEach(drawNode);
}

function drawNode(d) {
ctx.beginPath();
if (d.type == 'peer') {
ctx.fillStyle = '#000'
} else {
ctx.fillStyle = '#aaa'
}
ctx.moveTo(d.x, d.y);
ctx.arc(d.x, d.y, r, 0, Math.PI*2);
ctx.fill();
}

function drawLink(l) {
ctx.moveTo(l.source.x, l.source.y);
ctx.lineTo(l.target.x, l.target.y);
}

function ringForce() {
nodes.forEach( node => {
node.vx += 1000 - Math.random() * 2000;
node.vy += 1000 - Math.random() * 2000;
} )
}
update()
} */
Insert cell
function node_setTitle( d ) {
if ( d.type == 'peer' ) {
return "peer id: " + d.name + "\n community: " + d.community + "\n path: " + d.path.map( d => d.toString() ).join(' ')
} else {
return d.name
}
}
Insert cell
Insert cell
Insert cell
routes = {
if ( date == null ) {
return routes_lg
} else {
return routes_bgplay
}
}
Insert cell
Insert cell
Insert cell
com_scale = d3.scaleOrdinal(d3.schemeCategory10).domain( communities.map( d => d.community ).filter( d => d !== "(not set)" ) ).unknown('rgba(224,224,224,16')

Insert cell
origins = {
}
Insert cell
communities = {
var c = {} // counter
routes.forEach( d => {
if (! c.hasOwnProperty( d.community ) ) {
c[ d.community ] = {'community': d.community, 'count': 0, 'peers': []}
}
c[ d.community ]['count'] += 1
c[ d.community ]['peers'].push( d.peer_id )
})
for (var community in c) {
if ( community in c2site_map ) {
c[ community ]['site_name'] = c2site_map[ community ]
}
}
return Object.keys( c ).sort( (a,b) => c[b].count - c[a].count ).map( v => c[v] )
//return cmt_sort.map( v => c[v] )
}
Insert cell
graph = { // adhere to https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/data_network.json
var n_desc = {} // node descriptions
var l_desc = {} // unique links
/*
const pfx_key = pfx
n_desc[ pfx_key ] = {
'id': pfx,
'name': pfx,
'type': 'pfx'
}
*/
routes.forEach( entry => {
const peer_key = entry.peer_id
n_desc[ peer_key ] = {
'id': peer_key,
'name': peer_key,
'path': entry.path,
'community': entry.community,
'type': 'peer'
}
var path_len = entry.path.length
entry.path.forEach( (asn,idx) => {
const asn_key = asn.toString()
if ( ! n_desc.hasOwnProperty( asn_key ) ) {
n_desc[ asn_key ] = {
'id': asn_key,
'name': "AS" + asn_key,
'type': 'asn',
'min_distance': path_len - idx,
'communities': [entry.community] // plural!
}
} else { // update the 'complex' fields
// still check if the min_distance is correct one
n_desc[ asn_key ]['min_distance'] = d3.min([
n_desc[ asn_key ]['min_distance'],
path_len - idx
])
// update the communities
if (! n_desc[ asn_key ]['communities'].includes( entry.community ) ) {
n_desc[ asn_key ]['communities'].push( entry.community )
}
// console.log( "POST", asn_key, entry.community, n_desc[ asn_key ]['communities'] )

}
if ( idx < entry.path.length - 1 ) {
var link_key = asn + "-" + entry.path[ idx + 1 ] // there is directionality so no need to distinguish between A->B and B->A
link_key += '-'
if ( entry.community !== undefined ) {
link_key += entry.community
}
l_desc[ link_key ] = {
'id': link_key,
'source': asn.toString(),
'target': entry.path[ idx + 1 ].toString(),
'community': entry.community
}
}
})
// bind peer to path[0]
var link_key = peer_key + "-" + entry.path[ 0 ]
l_desc[ link_key ] = {
'id': link_key,
'source': peer_key,
'target': entry.path[ 0 ].toString(),
'community': entry.community
}
// do we need to bind pfx to path[-1]? only when we look at multi origin (like hijack or anycast with asn-per-location)
})
return {
'nodes': Object.keys( n_desc ).map( x => n_desc[ x ] ),
'links': Object.keys( l_desc ).map( x => l_desc[ x ] )
}
}
Insert cell
yesterday = {
var date = new Date();
date.setDate(date.getDate() - 1);
return date
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more