Public
Edited
Dec 2, 2022
Insert cell
Insert cell
view = {
// interactive selection
const origin = vl.selectPoint().fields('origin')
.on('mouseover').nearest(true);
const id = vl.selectPoint().on('mouseover').fields('origin').nearest(false); // .resolve('global')

// shared data reference for lookup transforms
const foreign = vl.data(capitals).key('CountryCode').fields('latitude', 'longitude', 'member', 'CountryCode');

// const foreign2 = vl.data(linesCapitals).key('origin').fields('origin');
// map. Just EU?
const map = vl.markGeoshape({stroke: '#fff', strokeWidth: 1.25, zindex:0})
.data(europe)
.transform( // filter to selected id only?
// vl.filter(id.empty(false)),
vl.lookup('id').from(foreign.fields('member')),
// vl.lookup('id').from(foreign2.fields('origin'))
// vl.lookup('id').from(foreign)
)
// .params(origin)
.encode(
vl.color().fieldN('member').legend(null).scale({range: ['#f0f0f3','#3d6a9a']}),
// vl.stroke().value('#fff').if(id, vl.stroke().value('#00ffff')).value('#fff')
);
// add route lines from selected origin to destination
const routes = vl.markRule({color: '#00ffff', opacity: 0.6})
.data(linesCapitals)
.transform(
vl.filter(origin.empty(false)), // filter to selected origin only
vl.lookup('origin').from(foreign.fields('latitude', 'longitude')).as('latitude', 'longitude'), // origin lat/lon
vl.lookup('destination').from(foreign.fields('latitude', 'longitude')).as('lat2', 'lon2') // dest lat/lon
)
.encode(
vl.latitude().field('latitude'),
vl.longitude().field('longitude'),
vl.latitude2().field('lat2'),
vl.longitude2().field('lon2')
);

// 1. aggregation still required? probs no
// 2. lookup location data from capitals.csv
const points = vl.markCircle()
.data(linesCapitals)
.transform(
//vl.groupby('origin').aggregate(vl.count()),
vl.lookup('origin').from(foreign.fields('Country', 'City', 'Address', 'latitude', 'longitude'))
)
.params(origin)
.encode(
vl.latitude().fieldQ('latitude'),
vl.longitude().fieldQ('longitude'),
vl.size().value(20).if(origin, vl.size().value(100)),
vl.color().value('#00ffff'), //.if(origin, vl.color().value('#00ffff')),
vl.tooltip(['City',"Country","Address"])
// vl.size().fieldQ('count').legend(null).scale({range: [0, 20]}),
// vl.order().fieldQ('routes').sort('descending') // place smaller circles on top
);
return vl.layer(map, routes, points)
.project(vl.projection('conicEquidistant') // change projection center etc here
.rotate([-20.0, 0.0])
.center([0, 53])
.parallels([35.0, 65.0])
.scale(780))
.width(800)
.height(600)
.config({view: {stroke: null}}) // viewport line
.render({renderer: 'svg'});
}
Insert cell
Insert cell
// europe = aq.from(europe1)
// .derive({member: d => "N"})
Insert cell
europe = vl.topojson(europeRaw).feature('europe')
//
//vl.topojson('https://unpkg.com/world-atlas@1/world/50m.json').feature('countries')
//vl.topojson('https://raw.githubusercontent.com/leakyMirror/map-of-europe/master/TopoJSON/europe.topojson').feature('europe')
Insert cell
europeRaw = FileAttachment("europe@1.json").json()
Insert cell
lines-capitals@5.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
capitals@26.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
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