Published
Edited
Mar 18, 2020
1 fork
Insert cell
md`# Private Kit`
Insert cell
default_data = ({ "timelineObjects": [] })
Insert cell
mutable data = default_data
Insert cell
md`## Choose the input data here`
Insert cell
viewof data_in = html`<input type=file accept="*/*">`
Insert cell
data_text = Files.text(data_in)
Insert cell
{ mutable data = JSON.parse(data_text) }
Insert cell
data
Insert cell
Insert cell
function extractTimeS(timeStrMs) {
if (timeStrMs.length != 13)
throw `Unexpected string ${timeStrMs}`
return parseInt(timeStrMs.substr(0, timeStrMs.length - 3), 10);
}
Insert cell
extractTimeS("1583085476383") == 1583085476
Insert cell
path = {
var failures = []
var p = []
var len = data["timelineObjects"].length;
for (var i = 0; i < len; i++) {
let item = data["timelineObjects"][i];
if (item['activitySegment']) {
let o = item['activitySegment'];
p.push({
y: o["startLocation"]["latitudeE7"],
x: o["startLocation"]["longitudeE7"],
t: extractTimeS(o["duration"]["startTimestampMs"])
});
p.push({
y: o["endLocation"]["latitudeE7"],
x: o["endLocation"]["longitudeE7"],
t: extractTimeS(o["duration"]["endTimestampMs"])
});
} else if (item['placeVisit']) {
let o = item['placeVisit'];
p.push({
y: o["centerLatE7"],
x: o["centerLngE7"],
t: extractTimeS(o["duration"]["startTimestampMs"]),
meta: o["location"],
});
p.push({
y: o["centerLatE7"],
x: o["centerLngE7"],
t: extractTimeS(o["duration"]["endTimestampMs"]),
meta: o["location"],
});
} else {
failures.push(item)
}
}
return p;
}
Insert cell
md`# Rudimentary deletion support`
Insert cell
Insert cell
md`## Choose the parameters to blur the data with (distance, time) here`
Insert cell
Insert cell
Insert cell
blur_radius_m
Insert cell
Insert cell
Insert cell
blur_time_s
Insert cell
function offsetLatE7(distance_m) {
return distance_m * (1e7/111111)
}
Insert cell
function offsetLngE7(distance_m, latE7) {
return distance_m * (1e7/111111) * Math.cos((latE7/1e7) * Math.PI/180)
}
Insert cell
function offsetPoint(pt) {
let offset_angle = Math.random() * 2 * Math.PI;
let offset_y = blur_radius_m * Math.sin(offset_angle);
let offset_x = blur_radius_m * Math.cos(offset_angle);
let offset_time_s = Math.random() * blur_time_s;
return {
y: Math.round(pt.y + offsetLatE7(offset_y)),
x: Math.round(pt.x + offsetLngE7(offset_x, pt.y)),
t: Math.round(pt.t + offset_time_s)
}
}
Insert cell
offsetPoint({y: 423962436,
x: -710998953,
t: 1583084539})
Insert cell
blurred_path = {
let len = path.length;
let p = [];
if (len > 0) {
p.push(offsetPoint(path[0]));
}
for (var i = 1; i < len - 1; i+=2) {
p.push(offsetPoint(path[i]));
if (path[i+1].x == path[i].x && path[i+1].y == path[i].y &&
path[i+1].t == path[i].t) {
// Avoid offsetting the same location by two random numbers
// which will reduce the blur.
p.push(p[i]);
} else {
p.push(offsetPoint(path[i + 1]));
}
}
if (len > 1) {
p[len - 1] = offsetPoint(path[len - 1]);
}
return p
}
Insert cell
md`# Create a 3d index`
Insert cell
rb3d = require('rbush-3d@0.0.4/dist/rbush3d.min.js')
Insert cell
rtree = {
let tree = new rb3d.RBush3D();
let items = [];
let len = blurred_path.length;
for (var i = 0; i < len; i+=2) {
let a = blurred_path[i];
let b = blurred_path[i + 1];
items.push({
data: i,
minX: Math.min(a.x, b.x),
minY: Math.min(a.y, b.y),
minZ: Math.min(a.t, b.t),
maxX: Math.max(a.x, b.x ),
maxY: Math.max(a.y, b.y),
maxZ: Math.max(a.t, b.t),
});
}
tree.load(items)
return tree;
}
Insert cell
{
if (path.length > 0) {
return rtree.collides({
minX: Math.min(path[0].x, path[1].x),
minY: Math.min(path[0].y, path[1].y),
minZ: Math.min(path[0].t, path[1].t),
maxX: Math.max(path[0].x, path[1].x),
maxY: Math.max(path[0].y, path[1].y),
maxZ: Math.max(path[0].t, path[1].t)
})
}
}
Insert cell
{
if (path.length > 0) {
return rtree.collides({
minX: Math.min(path[0].x, path[1].x),
minY: Math.min(path[0].y, path[1].y),
minZ: (new Date().getTime()),
maxX: Math.max(path[0].x, path[1].x),
maxY: Math.max(path[0].y, path[1].y),
maxZ: (new Date().getTime() + 100)
})
}
}

Insert cell
Insert cell
import { pathsIntersect, overlapTime, flatEarth } from "@kunalb/intersection"
Insert cell
blurred_path
Insert cell
viewof test_radius_m = html`<input type=range>`
Insert cell
function testSegment(segment) {
let potential_intersections = rtree.search({
minX: Math.min(segment[0].x, segment[1].x),
minY: Math.min(segment[0].y, segment[1].y),
minZ: Math.min(segment[0].t, segment[1].t),
maxX: Math.max(segment[0].x, segment[1].x),
maxY: Math.max(segment[0].y, segment[1].y),
maxZ: Math.max(segment[0].t, segment[1].t)
});
for (var i in potential_intersections) {
let potential_segment = [
blurred_path[potential_intersections[i].data],
blurred_path[potential_intersections[i].data + 1]
];
let flattened = flatEarth(segment, potential_segment);
let overlapped = overlapTime(flattened[0], flattened[1]);

console.log(overlapped);
if (overlapped.length > 0 && pathsIntersect(overlapped[0], overlapped[1], test_radius_m)) {
return true;
}
}
return false;
}
Insert cell
path.length && testSegment([path[0], path[1]])
Insert cell
function testPath(path) {
}
Insert cell
md`# Visualization`
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