Published
Edited
May 19, 2021
Estimating geolocation through a contour density
Multilateration from your browserWeb latency - Radar Chart
Insert cell
Insert cell
Insert cell
Insert cell
{
// let centroid = [
// -sum_array(circles.map(c => c.geometry.coordinates[0])) / circles.length,
// -sum_array(circles.map(c => c.geometry.coordinates[1])) / circles.length
// ];

// _;

projection.rotate([-4.9173649, -52.3328799]);

const circle = d3.geoCircle(),
height = width * (9 / 16), // 16:9 aspect ratio
svg = d3
.create("svg")
.attr("width", width)
.attr("height", height);

svg
.append("path")
.datum(water)
.attr("class", "outer")
.attr("fill", "none")
.attr("stroke", "#999")
.attr("stroke-width", "0.5px")
.attr("d", path)
.attr("id", "usPath");

svg
.append("clipPath")
.attr("id", "usClipPath")
.append("use")
.attr("xlink:href", "#usPath");

const g = svg
.append("g")
.selectAll(".contour")
.data(a.contours)
.join("g");

g.append("path")
.attr("clip-path", "url(#usClipPath)")
.attr("class", d => `contour ${d.value}`)
.attr("d", d3.geoPath())
.attr("stroke-width", (d, i) => (i % 5 ? 0.25 : 1)) // every 5 lines
.style("stroke", "white")
.attr("fill", d => colorScale(d.value));

// empty points overlay, keep for reference
// svg
// .selectAll(".point")
// .data(geojson)
// .join("path")
// .attr("class", "point")
// .attr("d", path);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
offset = 6.7
Insert cell
contour_data = gridPoints.reduce((acc, climdiv) => {
// calculate the number of points based on the temp change value, down to 0.1.
const num_points = Math.floor(Math.abs(climdiv.data));
// console.log('num_points', num_points);

// create an array of that same value repeated to create stacked points tied to the data value
const array = new Array(num_points).fill(climdiv.centroid, 0, num_points);

return [...acc, ...array];
}, [])
Insert cell
contours = contour(contour_data)
Insert cell
a = {
let gridPoints = await rawPoints
.map((point, i) => {
let contained_s = circles.map(circle => {
let distance = d3.geoDistance(circle.geometry.coordinates, point);
let radius = miliseconds_to_radius(Math.min(...circle.rtts));

let geoCircle = d3
.geoCircle()
.center(circle.geometry.coordinates)
.radius(radius)();

let contained = d3.geoContains(geoCircle, point);

return { contained: contained, distance: distance };
});

return {
centroid: projection(point),
data: sum_array(
contained_s.map(
// if contained, then return 1/x else 0
contained => (contained.contained ? 1.0 / contained.distance : 0)
)
)
};
})
.filter(d => d.centroid !== null && d.data !== null);

console.log('grid', gridPoints);
let contour_data = await gridPoints.reduce((acc, climdiv) => {
// calculate the number of points based on the temp change value, down to 0.1.
const num_points = Math.floor(Math.abs(climdiv.data));
// console.log('num_points', num_points);

// create an array of that same value repeated to create stacked points tied to the data value
const array = new Array(num_points).fill(climdiv.centroid, 0, num_points);

return [...acc, ...array];
}, []);

console.log('contour_data', contour_data);

let contour = d3
.contourDensity()
.x(d => d[0])
.y(d => d[1])
.size([width, width * (9 / 16)]) // w,h
.cellSize(2);

let contours = await contour(contour_data);

let colors = [
"#023858",
"#045a8d",
"#0570b0",
"#3690c0",
"#74a9cf",
"#a6bddb",
"#d0d1e6",
"#fff",
"#fed976",
"#feb24c",
"#fd8d3c",
"#fc4e2a",
"#e31a1c",
"#bd0026",
"#800026"
];

let density_thresholds = await contours.map(
d => Math.floor(d.value * 100000) / 100000
);

let linearColorScale = d3
.scaleLinear()
.domain(d3.range(0, 1, 1 / colors.length))
.range(colors)
.interpolate(d3.interpolateLab);

let quantz = d3.quantize(linearColorScale, density_thresholds.length * 2);

return {
quantz: quantz,
density_thresholds: density_thresholds,
contours: contours,
contour_data: contour_data,
gridPoints: gridPoints
};
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
contour = d3
.contourDensity()
.x(d => d[0])
.y(d => d[1])
.size([width, height]) // w,h
.cellSize(2)
Insert cell
Insert cell
Insert cell
{
button;
measure();
}
Insert cell
circles = {
test_count;
return points.filter(p => p.rtts);
}
Insert cell
import {
points,
mutable selected_points,
mutable current_point,
test_count,
measure
} from "@aguformoso/web-latency-headless"
Insert cell
// mutable circles = JSON.parse(
// `[{"geometry":{"type":"Point","coordinates":[16.6176942,49.1971726]},"rtts":[35,36]},{"geometry":{"type":"Point","coordinates":[16.5989768,49.2099358]},"rtts":[47]},{"geometry":{"type":"Point","coordinates":[6.1119969,49.5041843]},"rtts":[25,26]},{"geometry":{"type":"Point","coordinates":[4.42069023,51.18446055]},"rtts":[27,27]},{"geometry":{"type":"Point","coordinates":[9.8971509,57.022937]},"rtts":[36,34,36]},{"geometry":{"type":"Point","coordinates":[10.1405088,56.1075405]},"rtts":[25,25]},{"geometry":{"type":"Point","coordinates":[12.377482,55.728092]},"rtts":[36]},{"geometry":{"type":"Point","coordinates":[19.0029788,50.25361764]},"rtts":[46]},{"geometry":{"type":"Point","coordinates":[9.6053289,47.2703929]},"rtts":[36,36,36]},{"geometry":{"type":"Point","coordinates":[14.2652247,46.6160291]},"rtts":[43,46]},{"geometry":{"type":"Point","coordinates":[14.3182153,46.61584621]},"rtts":[56]},{"geometry":{"type":"Point","coordinates":[7.4397554,46.9544044]},"rtts":[36,38,39]},{"geometry":{"type":"Point","coordinates":[7.5889323,47.557092]},"rtts":[35,36]},{"geometry":{"type":"Point","coordinates":[8.6177789,47.4016718]},"rtts":[36,36]},{"geometry":{"type":"Point","coordinates":[8.5573111,47.4326816]},"rtts":[35]},{"geometry":{"type":"Point","coordinates":[6.6403302,46.5172206]},"rtts":[38]},{"geometry":{"type":"Point","coordinates":[4.8951679,52.3702157]},"rtts":[16,16,16]},{"geometry":{"type":"Point","coordinates":[4.9173649,52.3328799]},"rtts":[18,16,17]},{"geometry":{"type":"Point","coordinates":[4.832429,52.345433]},"rtts":[18,17,16]},{"geometry":{"type":"Point","coordinates":[4.6652669,52.3911648]},"rtts":[16,17]},{"geometry":{"type":"Point","coordinates":[4.9318931,52.3365239]},"rtts":[16,16]},{"geometry":{"type":"Point","coordinates":[4.8292414,52.3449285]},"rtts":[16,17]},{"geometry":{"type":"Point","coordinates":[4.6647595,52.3908925]},"rtts":[18]},{"geometry":{"type":"Point","coordinates":[4.8951679,52.3702157]},"rtts":[16]},{"geometry":{"type":"Point","coordinates":[4.9450801,52.2934123]},"rtts":[16]},{"geometry":{"type":"Point","coordinates":[4.88648057,52.3361664]},"rtts":[15]},{"geometry":{"type":"Point","coordinates":[10.07094,53.74242]},"rtts":[30,26,30]},{"geometry":{"type":"Point","coordinates":[13.404954,52.5200066]},"rtts":[35,35,35]},{"geometry":{"type":"Point","coordinates":[13.36945,52.50192]},"rtts":[31,27,30]},{"geometry":{"type":"Point","coordinates":[13.4285599,52.4529565]},"rtts":[29,27,25]},{"geometry":{"type":"Point","coordinates":[13.3224154,52.523444]},"rtts":[28,28,31]},{"geometry":{"type":"Point","coordinates":[8.76863,53.08495]},"rtts":[34,35,34]},{"geometry":{"type":"Point","coordinates":[10.5417063,52.2764787]},"rtts":[30,30,30]},{"geometry":{"type":"Point","coordinates":[8.7472439,48.7122107]},"rtts":[30,36,35]},{"geometry":{"type":"Point","coordinates":[7.058659,50.9123781]},"rtts":[18,22]},{"geometry":{"type":"Point","coordinates":[13.7347646,51.0377067]},"rtts":[36,35]},{"geometry":{"type":"Point","coordinates":[7.52804,51.50437]},"rtts":[25,26]},{"geometry":{"type":"Point","coordinates":[7.4879214,51.5209565]},"rtts":[35,36]},{"geometry":{"type":"Point","coordinates":[7.40332,51.49087]},"rtts":[20,19]},{"geometry":{"type":"Point","coordinates":[6.7734556,51.2277411]},"rtts":[25,26]},{"geometry":{"type":"Point","coordinates":[6.8175201,51.2673008]},"rtts":[25,22]},{"geometry":{"type":"Point","coordinates":[6.8071504,51.2196318]},"rtts":[25,26]},{"geometry":{"type":"Point","coordinates":[6.867753,51.188264]},"rtts":[35]},{"geometry":{"type":"Point","coordinates":[6.8677694,51.1882705]},"rtts":[26]},{"geometry":{"type":"Point","coordinates":[6.8175201,51.2673008]},"rtts":[20]},{"geometry":{"type":"Point","coordinates":[6.870125,51.186031]},"rtts":[23]},{"geometry":{"type":"Point","coordinates":[11.02943756,49.57230843]},"rtts":[35]},{"geometry":{"type":"Point","coordinates":[8.62412,52.38469]},"rtts":[29]},{"geometry":{"type":"Point","coordinates":[8.3909827,48.9525729]},"rtts":[31]},{"geometry":{"type":"Point","coordinates":[8.6011068,50.1281415]},"rtts":[27]}]`
// )
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