Published
Edited
Aug 21, 2020
Insert cell
md`# K-root instances used from RIPE Atlas probes ${regionName}`
Insert cell
regionName = region.charAt(0).toUpperCase() + region.slice(1);
Insert cell
regionCountries = ['KZ','KG','TJ','TM','UZ']
Insert cell
excludeProbes=[];
Insert cell
center = turf.envelope(prbGeoJson)
Insert cell
md`### stats`
Insert cell
// [d3.min(filteredPaths, d => d.properties.pathLength), d3.max(filteredPaths, d => d.properties.pathLength), d3.median(filteredPaths, d => d.properties.pathLength)]
Insert cell
// maxFarthestCount = d3.max(ffLocs, d => d.properties.aggFarthestCount);
Insert cell
// minFarthestCount = d3.min(ffLocs, d => d.properties.aggFarthestCount);
Insert cell
// Specify either inFilter or outFilter here, so you'll see only the farthest Locations and transit Locations inside the SEE region or outside of it.
// ooc = lineLocs.locations.filter(inFilter);
Insert cell
outFilter = c => { const cc = c.properties.countryCodeAlpha2; return !regionCountries.some(ccc => ccc === cc)}
Insert cell
inFilter = c => { const cc = c.properties.countryCodeAlpha2; return regionCountries.some(ccc => ccc === cc)}
Insert cell
asns = probes.reduce((asnArr, p) => {
let asn_v4 = asnArr.asn_v4.find(ac => ac.asn===p.asn_v4);
if (asn_v4) {
asn_v4.count++
} else {
asnArr.asn_v4.push({ asn: p.asn_v4, count: 1});
}
let asn_v6 = asnArr.asn_v6.find(ac => ac.asn===p.asn_v6);
if (asn_v6) {
asn_v6.count++
} else {
asnArr.asn_v6.push({ asn: p.asn_v6, count: 1});
}
return asnArr;
}, {asn_v4:[], asn_v6:[]});
Insert cell
region = "central-asia"
Insert cell
mapScale= 1450
Insert cell
latMove = -65 // [-65,-52]
Insert cell
lonMove = -52;
Insert cell
probeCenter = turf.center(prbGeoJson);
Insert cell
html`<svg><g>${krootLocs.map((l, i) => `<rect x=0 y=${i*18 + 4} width=40 height=10 style="fill: ${stringColor[krootLocs.indexOf(l)]}"/><text x=60 y=${i*18 + 15}>${l}</text>`)}</g></svg>`;
Insert cell
circleLegend = [
{cityName: "London", minRtt: 6, realCoords: [20,10]},
{cityName: "London", minRtt: 20, realCoords: [20,40]},
{cityName: "London", minRtt: 30, realCoords: [20,70]},
{cityName: "London", minRtt: 40, realCoords: [20,100]},
{cityName: "London", minRtt: 60, realCoords: [20,130]},
];
Insert cell
html`<svg><g>${circleLegend.map(krootProbesMap)}</g>
<g>
${circleLegend.map((ci,i) => `<text x=${ci.realCoords[0]+20} y=${ci.realCoords[1] + 5}>${ci.minRtt} ms ${i===0 && "or lower" || i === circleLegend.length - 1 && "or higher" || ""}</text>`)}
</g>
</svg>`
Insert cell
map = html`<h1>${regionName}</h1><svg viewBox="0 0 ${width} ${height}" style="display: block;">
<style>
.see-region { stroke: #eeeee; stroke-width: 0.5; fill-opacity: 0.3; fill: white; }
.world-bg { stroke: white; fill: #eeeeee; fill-opacity: 1.0; }
.water {fill: lightBlue; }
.hop-loc { fill: none; stroke-width: 1.5; stroke: black; }
.probe-loc { stroke-width: 2; stroke: none; fill: white; fill-opacity: 0.8 }
.l-path { fill: none; stroke-width: 2; stroke: lightBlue; }
.kreis { fill-opacity: 0.8; fill: white; stroke: black; stroke-width: 1; stroke-opacity: 1; }
.bunch-of-lines { stroke: black; stroke-width: 0.2; fill: none; stroke-opacity: 1; }
.f-locs { fill: white; fill-opacity: 0.6; stroke: black; stroke-width: 1; }
.pt-locs { fill: none; stroke: #a2cade; }
.e-line { fill: none; stroke: #ff8e19; stroke-opacity: 0.7; }
.probe-center { fill: #64d46a; stroke: none; }
.country-labels { font: 14px Arial }
</style>
<defs>
<path id="outline" d="${path(outline)}" />
<clipPath id="clip"><use xlink:href="${new URL("#outline", location)}" /></clipPath>
</defs>
<g class="world-bg">
<rect class="water" x=0 x=0 width=954 height=800></rect>
${land.features.map(c => `<path d="${path(c.geometry)}"></path>`)}
</g>
<g class="see-region" clip-path="url(${new URL("#clip", location)})">
<use xlink:href="${new URL("#outline", location)}" fill="#fff" />
${countriesGeom.map(c => `<path class="kreis" d="${path(c.geometry)}" style="fill: ${popKreisColor(c.properties.pop_density)}"></path>`)}
</g>
<g class="country-labels">
${countriesGeom.map(cc => {
const center = projection(turf.centroid(cc).geometry.coordinates);
return `<text x=${center[0]} y=${center[1]}>${cc.properties.countryName}</text>`
})}
</g>
<g>
${krootProbes.map(krootProbesMap)}
</g>

</svg>`
Insert cell
krootProbesMap = p => {
return `<circle class="probe ${p.cityName}" style="stroke: ${stringColor[krootLocs.indexOf(p.cityName)]}; fill: none;" cx=${p.coords && projection(p.coords)[0] || p.realCoords[0]} cy=${p.coords && projection(p.coords)[1] || p.realCoords[1]} r="${(p.minRtt <= 6 && 1) || (p.minRtt < 60 && p.minRtt / 6) || 10}"/>`
}
Insert cell
countriesGeom = land.features.filter(
c => {
const cc = c.properties.countryCode
return regionCountries.some(sc => sc === cc)
// return cc==='KZ' || cc==='RS' || cc==='XK' || cc==='AL' || cc==='ME' || cc==='MK' || cc==='SI' || cc=='BA' || c.properties.id === 'KOS'}
});
Insert cell
// radical researcher
// jediIxp="#ff8e19"

// jediTransit="#a2cade";
// jediEyeballs="#64d46a";
// jediEyeballsNoProbes="#dcede6";
// jediAnthracite="#363738";
Insert cell
// html`<svg><g class="bunch-of-lines">${filteredPaths.map(s => `<path d="${path(s)}" style="stroke: ${stringColor[s.properties.idx % stringColor.length]};"/>`)}</g></svg>`
Insert cell
Insert cell
// farthestScale = d3.scaleLinear()
// .domain([1, maxFarthestCount])
// .range([2, 30]);
Insert cell
// farthestLine = d3.scaleLinear()
// .domain([1, maxFarthestCount])
// .range([0.2, 60])
Insert cell
pathLengthScale = d3.scaleLinear()
.domain([30, 500, 1800])
.range(["#64d46a","yellow","#ff0000"])
Insert cell
height = 800
Insert cell
popKreisColor = d3.scaleLinear()
.domain([30, 580])
.range(["#ffffff", "#cccccc"])
.interpolate(d3.interpolateLab);
Insert cell
stringColor =["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"]
Insert cell
krootLocs = Array.from(new Set(krootProbes.map(p => p.cityName)));
Insert cell
path = d3.geoPath(projection.scale(mapScale).rotate([latMove,lonMove]))
Insert cell
projection = d3.geoMercator()
Insert cell
// lineLocs = fetch(`https://sg-pub.ripe.net/jasper/${region}/${region}.linestrings.geo.json`).then(d => d.json(), e => console.log(e));
Insert cell
colsv = d3.dsvFormat(";");
Insert cell
// krootProbes = colsv.parse(await FileAttachment("k-root-central-asia.cleaned@1.csv").text(), d =>
// ({
// coords: [Number(d.Longitude), Number(d.Latitude)],
// minRtt: Number(d['Min. Rtt']),
// cityName: String(d.City)
// })
// );
Insert cell
krootbProbes2 = await FileAttachment("k-root-central-asia.cleaned@2.csv").json(); //.map(r => ({
// coords: [r.longitude, r.latitude],
// minRtt: r.size,
// cityName: r.city
// }))
Insert cell
krootProbes = krootbProbes2.map(r => ({
coords: [r.longitude, r.latitude],
minRtt: r.size,
cityName: r.city
}))
Insert cell
d3.dsvFormat("|")
Insert cell
probes = fetch(`https://sg-pub.ripe.net/jasper/${region}-cr/${region}_probes.json`).then(d => d.json())
Insert cell
apnic_file = fetch("https://sg-pub.ripe.net/jasper/germany-cr/pop_coverage_v2.json").then(d => d.json())
Insert cell
enduserAsns = apnic_file.find(d => d.country==="MK" || d.country==="SI" || d.country==="RS" || d.countr==="HR" || d.country==="BA" || d.country==="ME" || d.country==="AL" || d.country==='XK').eyeball_networks.map(en => en.as);
Insert cell
Insert cell
world = fetch("https://sg-pub.ripe.net/jasper/see-cr/openipmapCountries_50m.topo.json").then(r => r.json())
Insert cell
land = topojson.feature(world, world.objects["openipmapCountries_50m"])
Insert cell
outline = ({type: "Sphere"})
Insert cell
turf = require("https://npmcdn.com/@turf/turf@5.1.6/turf.js")
Insert cell
d3 = require("d3@5")
Insert cell
topojson = import("topojson-client@3")
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