Published
Edited
Feb 9, 2020
2 forks
3 stars
Insert cell
Insert cell
flightArcs = d3.csvParse(await FileAttachment('flightData.csv').text()).map(row => ({
type: 'LineString',
destination: row.target_country,
airline: row.airline,
coordinates: [
[row.source_longitude, row.source_latitude],
[row.target_longitude, row.target_latitude]
]
}));
Insert cell
Insert cell
airlines = new Set(flightArcs.map(f => f.airline));
Insert cell
countriesVisisted = new Set(flightArcs.map(f => f.destination));
Insert cell
html`
<style>
${styles}
</style>
<svg id="myMap" viewBox="0,0,500,500">
<defs>${filter}</defs>
${globe.node()}
</svg>`
Insert cell
Insert cell
globe = {
const width = 500, height = 500;
const mapRadius = height / 3;
const projection = d3.geoOrthographic()
.scale(mapRadius)
.precision(0.1)
.translate([width / 2, height / 2]);
const path = d3.geoPath().projection(projection);
const color = d3.scaleOrdinal(d3.schemeAccent)
.domain(airlines);

const createGlobe = () => {
const countriesGroup = d3.create('svg:g');
countriesGroup
.append('circle')
.attr('id', 'atmoshpere')
.attr('filter', 'url(#atmosphere-glow)')
.attr('cx', width / 2)
.attr('cy', height / 2)
.attr('r', mapRadius);

countriesGroup
.append('circle')
.attr('id', 'ocean')
.attr('cx', width / 2)
.attr('cy', height / 2)
.attr('r', mapRadius);

countriesGroup
.selectAll('path')
.data(topojson.features)
.enter()
.append('path')
.attr('d', path)
.attr('id', d => `country ${d.properties.iso_a3}`)
.attr('filter', d => countriesVisisted.has(d.properties.name) ? 'url(#path-glow)' : undefined)
.attr('class', d => countriesVisisted.has(d.properties.name) ? 'country highlight' : 'country');

countriesGroup
.selectAll('.flightarc')
.data(flightArcs)
.enter()
.append('path')
.attr('class', 'flightarc')
.attr('filter', 'url(#path-glow)')
.style('stroke', d => color(d.airline))
.attr('d', path);
return countriesGroup;
};

const setupRotationAndAtmosphere = () => {
const atmosphereFlicker = (flickerAmount = 1) => {
const newAtmosphereRadius = 10 + Math.random() * flickerAmount;
const atmosphereSVGElement = document.getElementById('atmosphere-offset');
if (atmosphereSVGElement) {
atmosphereSVGElement.setAttribute('dx', newAtmosphereRadius);
atmosphereSVGElement.setAttribute('dy', newAtmosphereRadius);
}
};

let prevTime = 0;
const latestRotation = [0, 0];
d3.timer(time => {
const rotationX = latestRotation[0] - (((prevTime - time) * 360) / 30000);
latestRotation[0] = rotationX <= -180 ? 180 : rotationX;
latestRotation[1] = Math.sin(time / 2000) * (width / 100);
projection.rotate(latestRotation);
d3.selectAll('path').attr('d', path);
prevTime = time;
atmosphereFlicker();
});
};

const myGlobe = createGlobe();
setupRotationAndAtmosphere();
return myGlobe;
}
Insert cell
Insert cell
Insert cell
Insert cell
topojson = JSON.parse(await FileAttachment('globe.geo.json').text());
Insert cell
d3 = require('d3');
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