Published
Edited
Oct 6, 2019
Importers
Insert cell
md`# Flights globe chart function`
Insert cell
chart = function(routes) {
const context = DOM.context2d(width, height);
const path = d3.geoPath(projection, context);

let colour_scale = d3
.scaleSequential(d3.interpolateOrRd)
.domain(d3.extent(routes, d => d["distance"]));

function render() {
context.clearRect(0, 0, width, height);

context.beginPath(),
path(sphere),
(context.fillStyle = "#fff"),
context.fill();

context.beginPath(),
path(land),
(context.fillStyle = "#000"),
context.fill();

context.beginPath(), path(sphere), context.stroke();

routes.forEach(function(d) {
let coordinates = [
[d["lng_from"], d["lat_from"]],
[d["lng_to"], d["lat_to"]]
];

let resampled = resample(coordinates);

let mid_point_with_drift = [
resampled["after"][1][0] + (d["lat_from"] + d["lng_to"]) / 100,
resampled["after"][1][1] + (d["lat_from"] + d["lng_to"]) / 100
];

resampled["after"][1] = mid_point_with_drift;

let route = {
type: "LineString",
coordinates: resampled["after"]
};
context.lineWidth = 1.5;
context.lineO;
context.beginPath(),
path(route),
(context.strokeStyle = colour_scale(d["distance"])),
context.stroke();
});

context.lineWidth = 1;
context.strokeStyle = "#000000";
}

return d3
.select(context.canvas)
.call(drag(projection).on("drag.render", render))
.call(render)
.node();
}
Insert cell
// routes = [
// {
// from: "Manja Airport",
// to: "London Gatwick Airport",
// id_from: 5629,
// name_from: "Manja Airport",
// city_from: "Manja",
// country_from: "Madagascar",
// iata_from: "MJA",
// icao_from: "FMSJ",
// lat_from: -21.426105,
// lng_from: 44.316509,
// id_to: 502,
// name_to: "London Gatwick Airport",
// city_to: "London",
// country_to: "United Kingdom",
// iata_to: "LGW",
// icao_to: "EGKK",
// lat_to: 51.148102,
// lng_to: -0.190278,
// distance: 5694.420782538171
// },
// {
// from: "London Gatwick Airport",
// to: "Manja Airport",
// id_from: 502,
// name_from: "London Gatwick Airport",
// city_from: "London",
// country_from: "United Kingdom",
// iata_from: "LGW",
// icao_from: "EGKK",
// lat_from: 51.148102,
// lng_from: -0.190278,
// id_to: 5629,
// name_to: "Manja Airport",
// city_to: "Manja",
// country_to: "Madagascar",
// iata_to: "MJA",
// icao_to: "FMSJ",
// lat_to: -21.426105,
// lng_to: 44.316509,
// distance: 5694.420782538171
// }
// ]
Insert cell
md`Imports`
Insert cell
d3 = require("d3@5")
Insert cell
topojson = require("topojson-client@3")
Insert cell
world = d3.json("https://unpkg.com/world-atlas@1/world/110m.json")
Insert cell
land = topojson.feature(world, world.objects.land)
Insert cell
function drag(projection) {
let v0, q0, r0;

function dragstarted() {
v0 = versor.cartesian(projection.invert(d3.mouse(this)));
q0 = versor((r0 = projection.rotate()));
}

function dragged() {
const v1 = versor.cartesian(projection.rotate(r0).invert(d3.mouse(this)));
const q1 = versor.multiply(q0, versor.delta(v0, v1));
projection.rotate(versor.rotation(q1));
}

return d3
.drag()
.on("start", dragstarted)
.on("drag", dragged);
}
Insert cell
height = width * 0.6
Insert cell
projection = d3
.geoEqualEarth()
.fitExtent([[1, 1], [width - 1, height - 1]], sphere)
.translate([width / 2, height / 2])
.rotate([0, 0])
.precision(0.1)
Insert cell
sphere = ({ type: "Sphere" })
Insert cell
function resample(coordinates) {
var i = 0,
n = coordinates.length,
before = [],
after = [];
while (++i < n) {
var c0 = coordinates[i - 1].slice(),
c1 = coordinates[i].slice(),
p0 = projection(c0),
p1 = projection(c1),
x10 = p1[0] - p0[0],
y10 = p1[1] - p0[1],
d1 = x10 * x10 + y10 * y10;
before.push(c0);
after.push(c0);
if (d1 > 4 * .1) {
// linear distance check
var c2 = d3.geoInterpolate(c0, c1)(.5),
p2 = projection(c2),
x20 = p2[0] - p0[0],
y20 = p2[1] - p0[1],
dz = y10 * x20 - x10 * y20;
if ((dz * dz) / d1 > .1) {
// perpendicular distance check
var t = (x20 * x10 + y20 * y10) / d1;
before.push(
projection.invert((c2.resampled = [p0[0] + t * x10, p0[1] + t * y10]))
);
after.push(c2);
}
}
}
if (n) before.push(c1), after.push(c1);
return { before: before, after: after };
}
Insert cell
versor = require("versor@0.0.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