Published
Edited
6 forks
14 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Globe = require("globe.gl")
Insert cell
d3 = require('d3')
Insert cell
THREE = {
const THREE = window.THREE = await require("three@0.99.0/build/three.min.js");
await require("three@0.99.0/examples/js/controls/OrbitControls.js").catch(() => {});
return window.THREE;
}
Insert cell
indexBy = require('index-array-by')
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
// Gen random data
const N = 300;
const gData = [...Array(N).keys()].map(() => ({
lat: (Math.random() - 0.5) * 180,
lng: (Math.random() - 0.5) * 360,
size: Math.random() / 3,
color: ['red', 'white', 'blue', 'green'][Math.round(Math.random() * 3)]
}));
Globe()
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg')
.pointsData(gData)
.pointAltitude('size')
.pointColor('color')
.width(containerWidth)
.height(containerHeight)
(container)
yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
// Gen random data
const N = 20;
const arcsData = [...Array(N).keys()].map(() => ({
startLat: (Math.random() - 0.5) * 180,
startLng: (Math.random() - 0.5) * 360,
endLat: (Math.random() - 0.5) * 180,
endLng: (Math.random() - 0.5) * 360,
color: [['red', 'white', 'blue', 'green'][Math.round(Math.random() * 3)], ['red', 'white', 'blue', 'green'][Math.round(Math.random() * 3)]]
}));
Globe()
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg')
.arcsData(arcsData)
.arcColor('color')
.arcDashLength(() => Math.random())
.arcDashGap(() => Math.random())
.arcDashAnimateTime(() => Math.random() * 4000 + 500)
.width(containerWidth)
.height(containerHeight)
(container)
yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
const COUNTRY = 'Portugal';
const MAP_CENTER = { lat: 37.6, lng: -16.6, altitude: 0.4 };
const OPACITY = 0.1;
const myGlobe = Globe()
.width(containerWidth)
.height(containerHeight)
(container)
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg')
.arcLabel(d => `${d.airline}: ${d.srcIata} &#8594; ${d.dstIata}`)
.arcStartLat(d => d.srcAirport.lat)
.arcStartLng(d => d.srcAirport.lng)
.arcEndLat(d => d.dstAirport.lat)
.arcEndLng(d => d.dstAirport.lng)
.arcColor(d => [`rgba(0, 255, 0, ${OPACITY})`, `rgba(255, 0, 0, ${OPACITY})`])
.arcDashLength(0.4)
.arcDashGap(0.2)
.arcDashAnimateTime(1500)
.onArcHover(hoverArc => myGlobe
.arcColor(d => {
const op = !hoverArc ? OPACITY : d === hoverArc ? 0.9 : OPACITY / 4;
return [`rgba(0, 255, 0, ${op})`, `rgba(255, 0, 0, ${op})`];
})
)
.pointColor(() => 'orange')
.pointAltitude(0)
.pointRadius(0.04)
.pointsMerge(true);
// load data
const airportParse = ([airportId, name, city, country, iata, icao, lat, lng, alt, timezone, dst, tz, type, source]) => ({ airportId, name, city, country, iata, icao, lat, lng, alt, timezone, dst, tz, type, source });
const routeParse = ([airline, airlineId, srcIata, srcAirportId, dstIata, dstAirportId, codeshare, stops, equipment]) => ({ airline, airlineId, srcIata, srcAirportId, dstIata, dstAirportId, codeshare, stops, equipment});
Promise.all([
fetch('https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat').then(res => res.text())
.then(d => d3.csvParseRows(d, airportParse)),
fetch('https://raw.githubusercontent.com/jpatokal/openflights/master/data/routes.dat').then(res => res.text())
.then(d => d3.csvParseRows(d, routeParse))
]).then(([airports, routes]) => {
const filteredAirports = airports.filter(d => d.country === COUNTRY);
const byIata = indexBy(filteredAirports, 'iata', false);
const filteredRoutes = routes
.filter(d => byIata.hasOwnProperty(d.srcIata) && byIata.hasOwnProperty(d.dstIata)) // exclude unknown airports
.filter(d => d.stops === '0') // non-stop flights only
.map(d => Object.assign(d, {
srcAirport: byIata[d.srcIata],
dstAirport: byIata[d.dstIata]
}))
// .filter(d => d.srcAirport.country !== d.dstAirport.country); // international routes
// .filter(d => d.srcAirport.country === d.dstAirport.country); // domestic routes
.filter(d => d.srcAirport.country === COUNTRY && d.dstAirport.country === COUNTRY); // domestic routes within country
myGlobe
.pointsData(filteredAirports)
.arcsData(filteredRoutes)
.pointOfView(MAP_CENTER, 4000);

});
yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
const colorScale = d3.scaleSequentialSqrt(d3.interpolateYlOrRd);
// GDP per capita (avoiding countries with small pop)
const getVal = feat => feat.properties.GDP_MD_EST / Math.max(1e5, feat.properties.POP_EST);
fetch('https://raw.githubusercontent.com/vasturiano/globe.gl/master/example/datasets/ne_110m_admin_0_countries.geojson').then(res => res.json()).then(countries =>
{
const maxVal = Math.max(...countries.features.map(getVal));
colorScale.domain([0, maxVal]);
const world = Globe()
.width(containerWidth)
.height(containerHeight)
(container)
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg')
.polygonsData(countries.features)
.polygonAltitude(0.06)
.polygonCapColor(feat => colorScale(getVal(feat)))
.polygonSideColor(() => 'rgba(0, 100, 0, 0.15)')
.polygonStrokeColor(() => '#111');
world.polygonLabel(({ properties: d }) => `
<b>${d.ADMIN} (${d.ISO_A2}):</b> <br />
GDP: <i>${d.GDP_MD_EST}</i> M$<br/>
Population: <i>${d.POP_EST}</i>
`)
.onPolygonHover(hoverD => world
.polygonAltitude(d => d === hoverD ? 0.12 : 0.06)
.polygonCapColor(d => d === hoverD ? 'steelblue' : colorScale(getVal(d)))
)
.polygonsTransitionDuration(300)
(document.getElementById('globeViz'))
});
yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
const world = Globe()
.width(containerWidth)
.height(containerHeight)
(container)
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-dark.jpg')
.pointOfView({ altitude: 4 }, 5000)
.polygonCapColor(feat => 'rgba(200, 0, 0, 0.6)')
.polygonSideColor(() => 'rgba(0, 100, 0, 0.05)')
.polygonLabel(({ properties: d }) => `
<b>${d.ADMIN} (${d.ISO_A2})</b> <br />
Population: <i>${Math.round(+d.POP_EST / 1e4) / 1e2}M</i>
`);
// Auto-rotate
world.controls().autoRotate = true;
world.controls().autoRotateSpeed = 0.3;
fetch('https://raw.githubusercontent.com/vasturiano/globe.gl/master/example/datasets/ne_110m_admin_0_countries.geojson').then(res => res.json()).then(countries => {
world.polygonsData(countries.features);
setTimeout(() => world
.polygonsTransitionDuration(4000)
.polygonAltitude(feat => Math.max(0.1, Math.sqrt(+feat.properties.POP_EST) * 7e-5))
, 3000);
});

yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
fetch('https://raw.githubusercontent.com/vasturiano/globe.gl/master/example/datasets/ne_110m_populated_places_simple.geojson').then(res => res.json()).then(places => {
Globe()
.width(containerWidth)
.height(containerHeight)
(container)
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg')
.labelsData(places.features)
.labelLat(d => d.properties.latitude)
.labelLng(d => d.properties.longitude)
.labelText(d => d.properties.name)
.labelSize(d => Math.sqrt(d.properties.pop_max) * 4e-4)
.labelDotRadius(d => Math.sqrt(d.properties.pop_max) * 4e-4)
.labelColor(() => 'rgba(255, 165, 0, 0.75)')
.labelResolution(2)
(document.getElementById('globeViz'))
});
yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
// Gen random data
const N = 300;
const gData = [...Array(N).keys()].map(() => ({
lat: (Math.random() - 0.5) * 180,
lng: (Math.random() - 0.5) * 360,
alt: Math.random() * 0.8 + 0.1,
radius: Math.random() * 5,
color: ['red', 'white', 'blue', 'green'][Math.round(Math.random() * 3)]
}));
const world = Globe()
.width(containerWidth)
.height(containerHeight)
(container)
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-blue-marble.jpg')
.bumpImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-topology.png')
.pointOfView({ altitude: 3.5 })
.customLayerData(gData)
.customThreeObject(d => new THREE.Mesh(
new THREE.SphereBufferGeometry(d.radius),
new THREE.MeshLambertMaterial({ color: d.color })
))
.customThreeObjectUpdate((obj, d) => {
Object.assign(obj.position, world.getCoords(d.lat, d.lng, d.alt));
});
(function moveSpheres() {
gData.forEach(d => d.lat += 0.2);
world.customLayerData(world.customLayerData());
requestAnimationFrame(moveSpheres);
})();

yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
const weightColor = d3.scaleSequentialSqrt(d3.interpolateYlOrRd)
.domain([0, 1e7]);
const world = Globe()
.width(containerWidth)
.height(containerHeight)
(container)
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg')
.bumpImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-topology.png')
.hexBinPointWeight('pop')
.hexAltitude(d => d.sumWeight * 6e-8)
.hexBinResolution(4)
.hexTopColor(d => weightColor(d.sumWeight))
.hexSideColor(d => weightColor(d.sumWeight))
.hexBinMerge(true)
.enablePointerInteraction(false); // performance improvement
fetch('https://raw.githubusercontent.com/vasturiano/globe.gl/master/example/datasets/world_population.csv').then(res => res.text())
.then(csv => d3.csvParse(csv, ({ lat, lng, pop }) => ({ lat: +lat, lng: +lng, pop: +pop })))
.then(data => world.hexBinPointsData(data));
// Add auto-rotation
world.controls().autoRotate = true;
world.controls().autoRotateSpeed = 0.1;

yield container;
}
Insert cell
Insert cell
{
const containerWidth = width;
const containerHeight = 500;
const container = html`<div style="height:500px"></div>`;
yield container;
// Gen random data
const COUNTRY = 'United States';
const OPACITY = 0.1;
const myGlobe = Globe()
.width(containerWidth)
.height(containerHeight)
(container)
.globeImageUrl('//cdn.jsdelivr.net/npm/three-globe/example/img/earth-night.jpg')
.pointOfView({ lat: 39.6, lng: -98.5, altitude: 2 }) // aim at continental US centroid
.arcLabel(d => `${d.airline}: ${d.srcIata} &#8594; ${d.dstIata}`)
.arcStartLat(d => +d.srcAirport.lat)
.arcStartLng(d => +d.srcAirport.lng)
.arcEndLat(d => +d.dstAirport.lat)
.arcEndLng(d => +d.dstAirport.lng)
.arcDashLength(0.25)
.arcDashGap(1)
.arcDashInitialGap(() => Math.random())
.arcDashAnimateTime(4000)
.arcColor(d => [`rgba(0, 255, 0, ${OPACITY})`, `rgba(255, 0, 0, ${OPACITY})`])
.arcsTransitionDuration(0)
.pointColor(() => 'orange')
.pointAltitude(0)
.pointRadius(0.02)
.pointsMerge(true);
// load data
const airportParse = ([airportId, name, city, country, iata, icao, lat, lng, alt, timezone, dst, tz, type, source]) => ({ airportId, name, city, country, iata, icao, lat, lng, alt, timezone, dst, tz, type, source });
const routeParse = ([airline, airlineId, srcIata, srcAirportId, dstIata, dstAirportId, codeshare, stops, equipment]) => ({ airline, airlineId, srcIata, srcAirportId, dstIata, dstAirportId, codeshare, stops, equipment});
Promise.all([
fetch('https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat').then(res => res.text())
.then(d => d3.csvParseRows(d, airportParse)),
fetch('https://raw.githubusercontent.com/jpatokal/openflights/master/data/routes.dat').then(res => res.text())
.then(d => d3.csvParseRows(d, routeParse))
]).then(([airports, routes]) => {
const byIata = indexBy(airports, 'iata', false);
const filteredRoutes = routes
.filter(d => byIata.hasOwnProperty(d.srcIata) && byIata.hasOwnProperty(d.dstIata)) // exclude unknown airports
.filter(d => d.stops === '0') // non-stop flights only
.map(d => Object.assign(d, {
srcAirport: byIata[d.srcIata],
dstAirport: byIata[d.dstIata]
}))
.filter(d => d.srcAirport.country === COUNTRY && d.dstAirport.country !== COUNTRY); // international routes from country
myGlobe
.pointsData(airports)
.arcsData(filteredRoutes);
});

yield container;
}
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