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

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