Published
Edited
Apr 24, 2019
Insert cell
md`# MOCA Spike 150 Relay Prototype`
Insert cell
build(track, mile())
Insert cell
map(us, data)
Insert cell
map = (us, data) => {
const width = 960;
const height = 600;
const path = d3.geoPath();
const spike_location = [-112.5475, 41.618611]
const svg = d3.select(DOM.svg(width, height))
.style("width", "100%")
.style("height", "auto")
.style("font-family", "Verlag-Light")
.style("font-size", "16px")
.attr("stroke-width", "1")
svg.append("path")
.datum(topojson.merge(us, us.objects.lower48.geometries))
.attr("fill", "#FFF7ED")
.attr("stroke", "#6aa8ca")
.attr("d", path);
svg.append("path")
.datum(topojson.mesh(us, us.objects.lower48, (a, b) => a !== b))
.attr("fill", "none")
.attr("stroke", "#6aa8ca")
.attr("stroke-linejoin", "round")
.attr("d", path);

let g = svg.append('g')
.attr("transform", `translate(${projection(spike_location)})`)
let spike = g.append('g');
spike.attr("transform", 'scale(0.2)').html(`${spike_svg.svg}`)
.attr("opacity", 0);
spike.transition()
.delay(1000)
.duration(2000)
.attr("opacity", 1)
for (const key of Object.keys(data)) {
for (const d of data[key]) {
let g = svg.append('g')
.attr("transform", `translate(${projection(spike_location)})`)
.attr("opacity", 0)
g.append("image")
.attr("width", 16)
.attr("height", 16)
.attr("xlink:href", avatar[key].src)
g.transition()
.delay(1000)
.duration(2000)
.attr("opacity", 1)
.attr("transform", `translate(${projection(d)})`);
}
}
return svg.node();
}
Insert cell
build = (track, mile) => {
let t = Math.floor(mile * 42 / 1912)
const svg = DOM.svg(1280, 183)
svg.style = "width: 100%;"
const container = d3.select(svg)
let g = container.selectAll('g').data(track).enter().append('g');
g.attr('fill', 'none')
g.append('path')
.attr('d', (d, i) => { return d.path[0] })
.attr('fill', (d, i) => { return (i < t) ? '#64C188' : '#ccc'})
g.append('path')
.attr('d', (d, i) => { return d.path[1] })
.attr('fill', (d, i) => { return (i < t) ? '#64C188' : '#ccc'})
g.append('polygon')
.attr('points', (d, i) => { return d.points })
.attr('fill', (d, i) => { return (i < t) ? '#64C188' : '#ccc'})
return svg;
}
Insert cell
mile = () => {
let dt = now - t0
let T = 3000
return parseInt(mile_data.mile * (dt < T ? (dt / T) : 1) )
}
Insert cell
mile_data = ({ mile: 1200 })
Insert cell
t0 = Date.now()
Insert cell
spike_svg = d3.json('https://www.mocaspike150.org/api/spike.json')
Insert cell
track = d3.json('https://www.mocaspike150.org/json/track.json')
Insert cell
avatar = d3.json('https://www.mocaspike150.org/api/club/avatar.json')
Insert cell
data = d3.json(`https://www.mocaspike150.org/api/map/club/geo.json`)
Insert cell
us = {
const us = await d3.json("https://www.mocaspike150.org/api/map/us.json");
us.objects.lower48 = {
type: "GeometryCollection",
geometries: us.objects.states.geometries.filter(d => d.id !== "02" && d.id !== "15")
};
return us;
}
Insert cell
projection = d3.geoAlbersUsa().scale(1280).translate([480, 300]);
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require('d3@5')
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