Public
Edited
Nov 1, 2022
6 stars
Insert cell
Insert cell
container = html `<div style="height:450px;"></div>`
Insert cell
Insert cell
Insert cell
locations = FileAttachment("flowmap-locations.csv").csv()
Insert cell
flows = FileAttachment("flowmap-flows.csv").csv()
Insert cell
Insert cell
defaultProps = {
return {
// node styles
getNodeColor: {type: 'accessor', value: [255, 255, 255, 255]},
nodeSizeScale: 1,
// edge styles
getEdgeColor: {type: 'accessor', value: [0, 0, 0, 255]},
edgeWidthScale: 1,
}
}
Insert cell
FlowLayerV0 = {
const {CompositeLayer} = deck;
// Our custom layer class
class FlowLayer extends CompositeLayer {
static defaultProps = defaultProps
static layerName = 'FlowLayer'
updateState() {
// TODO
}
renderLayers() {
// TODO
}
}

return FlowLayer
}
Insert cell
Insert cell
layer = new FlowLayerV2({
data: {locations, flows},
nodeSizeScale: 5,
getEdgeColor: [255, 255, 255, 220],
edgeWidthScale: 5
})
Insert cell
layer.props.getEdgeColor
Insert cell
deckgl.setProps({layers: [layer]})
Insert cell
Insert cell
FlowLayerV1 = {
const {CompositeLayer} = deck;
// Our custom layer class
class FlowLayer extends CompositeLayer {
static defaultProps = defaultProps
static layerName = 'FlowLayer' + Date.now()
updateState({changeFlags, props}) {
// TODO
}
renderLayers() {
const {locations, flows} = this.props.data
const { getNodeColor, nodeSizeScale } = this.props

return [
new deck.ScatterplotLayer(this.getSubLayerProps({
id: 'locations',
data: locations,
getPosition: d => [Number(d.lon), Number(d.lat)],
getFillColor: getNodeColor,
getRadius: 1000,
radiusScale: nodeSizeScale
}))
]
}
}

return FlowLayer
}
Insert cell
Insert cell
Insert cell
FlowLayerV2 = {
const {CompositeLayer} = deck;
// Our custom layer class
class FlowLayer extends CompositeLayer {
static defaultProps = defaultProps
static layerName = 'FlowLayer' + Date.now()
updateState({changeFlags, props}) {
if (changeFlags.dataChanged) {
// index locations
const locationById = {}
for (const loc of props.data.locations) {
locationById[loc.id] = {...loc, count: 0}
}
// aggregate flows
for (const flow of props.data.flows) {
const origin = locationById[flow.origin]
const dest = locationById[flow.dest]
origin.count += Number(flow.count)
dest.count += Number(flow.count)
}

this.setState({locationById})
}
}
renderLayers() {
const {locations, flows} = this.props.data
const {getNodeColor, getEdgeColor, nodeSizeScale, edgeWidthScale} = this.props
const {locationById} = this.state

return [
new deck.ScatterplotLayer(this.getSubLayerProps({
id: 'locations',
data: locations,
getPosition: d => [Number(d.lon), Number(d.lat)],
getFillColor: getNodeColor,
getRadius: d => {
const loc = locationById[d.id]
return Math.sqrt(loc.count)
},
radiusScale: nodeSizeScale
})),
new deck.ArcLayer(this.getSubLayerProps({
id: 'flows',
data: flows,
getSourcePosition: d => {
const loc = locationById[d.origin]
return [Number(loc.lon), Number(loc.lat)]
},
getTargetPosition: d => {
const loc = locationById[d.dest]
return [Number(loc.lon), Number(loc.lat)]
},
getWidth: d => Number(d.count),
getTilt: 15,
widthUnits: 'meters',
widthScale: edgeWidthScale,
getSourceColor: getEdgeColor,
getTargetColor: getEdgeColor
}))
]
}
}

return FlowLayer
}
Insert cell
Insert cell
Insert cell
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