Public
Edited
Jan 30, 2023
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
overlayLayer = () => {
return new deck.TextLayer({
id: 'overlay-layer',
data: [
{
text: 'Filecoin Published Deals',
position: [ -200, -120 ],
size: 24,
color: [255, 255, 255, 255]
},
{
text: formatInTimeZone(viewof elapsed.value, 'yyyy MMM dd HH:mm', 'utc'),
position: [ -70, 115 ],
size: 32,
color: [255, 255, 255, 255]
},
{
text: 'Provider.Quest',
position: [ 160, -135 ],
size: 14,
color: [255, 255, 255, 255]
},
{
text: 'Regular Deal',
position: [ -200, 105 ],
size: 16,
color: colorBlue
},
{
text: 'Filecoin+ Deal',
position: [ -200, 118 ],
size: 16,
color: colorOrange
}
],
getPosition: d => d.position,
getText: d => d.text,
getSize: d => d.size,
getColor: d => d.color,
getTextAnchor: 'start'
})
}
Insert cell
deckgl.update(elapsed)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = minerLocationsReport.minerLocations
.filter(({ miner }) => minerPowerDailyAverageReport.miners[miner])
.map(({ miner, long, lat, numLocations }) => {
const power = minerPowerDailyAverageReport.miners[miner].qualityAdjPower
return {
latitude: Number(lat),
longitude: Number(long),
qualityAdjPower: power / numLocations
}
})
Insert cell
Insert cell
deck = require.alias({
// optional dependencies
h3: {}
})('deck.gl@latest/dist.min.js')
Insert cell
Insert cell
Insert cell
initialViewState = ({
latitude: 30,
longitude: 25,
zoom: 1.05,
bearing: 0,
pitch: 0,
minZoom: 1,
maxZoom: 20
})
Insert cell
deckgl = {
const myDeck = new deck.DeckGL({
container,
map: mapboxgl,
mapboxAccessToken: '',
// This token is for demo-purpose only and rotated regularly. Get your token at https://www.mapbox.com
mapboxApiAccessToken: 'pk.eyJ1IjoidWJlcmRhdGEiLCJhIjoiY2pudzRtaWloMDAzcTN2bzN1aXdxZHB5bSJ9.2bkj3IiRC8wj3jLThvDGdA',
mapStyle: 'mapbox://styles/mapbox/dark-v9',
initialViewState,
controller: true,
views: [
new deck.MapView({
id: 'map-view'
}),
new deck.OrthographicView({
id: 'ortho-view'
})
],
layers: [
heatmapLayer(),
tripsLayer(),
overlayLayer()
],
layerFilter: ({ layer, viewport }) => {
if (viewport.id === 'map-view' && layer.id === 'heatmap-layer') return true
if (viewport.id === 'map-view' && layer.id === 'trips-layer') return true
if (viewport.id === 'ortho-view' && layer.id === 'overlay-layer') return true
return false
}
})
return Object.assign(myDeck, {
update(sliderValue) {
// console.log('Jim update', sliderValue)
myDeck.setProps({ layers: [
heatmapLayer(),
tripsLayer(),
overlayLayer()
] })
}
})
}
Insert cell
hexagonLayer = new deck.HexagonLayer({
id: 'miners',
extruded: true,
data: data,
radius: 80000,
getPosition: d => [d.longitude, d.latitude],
getElevationWeight: d => d.qualityAdjPower,
elevationAggregation: 'SUM',
elevationScale: 400,
opacity: 0.6,
colorRange: [
[0, 170, 0, 255],
[0, 190, 0, 255],
[0, 255, 0, 255]
]
})
Insert cell
tripsLayer = () => new deck.TripsLayer({
id: 'trips-layer',
data: dealTripsData,
currentTime: viewof elapsed.value - startTime,
// fadeTrail: true,
getTimestamps: d => d.waypoints.map(p => p.timestamp - startTime),
trailLength: 600000,
/* props inherited from PathLayer class */
// billboard: false,
capRounded: true,
// getColor: [253, 128, 93],
getColor: d => d.verified ? colorOrange : colorBlue,
getPath: d => d.waypoints.map(p => p.coordinates),
getWidth: d => d.width,
// getWidth: 1,
jointRounded: true,
// miterLimit: 4,
// rounded: true,
// widthMaxPixels: Number.MAX_SAFE_INTEGER,
widthMinPixels: 1,
// widthScale: 1,
// widthUnits: 'meters',
/* props inherited from Layer class */
// autoHighlight: false,
// coordinateOrigin: [0, 0, 0],
// coordinateSystem: COORDINATE_SYSTEM.LNGLAT,
// highlightColor: [0, 0, 128, 128],
// modelMatrix: null,
opacity: 0.8,
// pickable: false,
// visible: true,
// wrapLongitude: false,
/*
getPath: d => d.waypoints.map(p => p.coordinates),
// deduct start timestamp from each data point to avoid overflow
getTimestamps: d => d.waypoints.map(p => p.timestamp - 1554772579000),
getColor: [253, 128, 93],
opacity: 0.8,
widthMinPixels: 5,
rounded: true,
fadeTrail: true,
trailLength: 200,
currentTime: 100
*/
})
Insert cell
heatmapLayer = () => {
const binIndex = Math.floor(heatmapTimeScale(viewof elapsed.value))
return new deck.HeatmapLayer({
id: 'heatmap-layer',
data: heatmapData,
getPosition: d => d[0],
getWeight: d => {
return d[1][binIndex]
},
weightsTextureSize: 512,
radiusPixels: 40,
aggregation: 'SUM',
updateTriggers: {
getWeight: binIndex
}
})
}
Insert cell
heatmapLayer()
Insert cell
Insert cell
Insert cell
pairsData = (await fetch(dealPairsUrl)).json()
Insert cell
providerLocations = {
const providerLocations = new Map()
for (const { miner, long, lat } of minerLocationsReport.minerLocations) {
const locations = providerLocations.get(miner) || []
locations.push([long, lat])
providerLocations.set(miner, locations)
}
return providerLocations
}
Insert cell
filteredPairs = {
const tripsData = []
for (const { provider, start, ...rest } of pairsData) {
const providerLocationList = providerLocations.get(provider)
if (providerLocationList) {
const publishTime = d3.isoParse(start)
tripsData.push({
publishTime,
provider,
...rest
})
}
}
return tripsData
}
Insert cell
clients = new Set(filteredPairs.map(({ client }) => client))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
clientLocations = {
const clientLocations = new Map()
for (const client of clients) {
const encoder = new TextEncoder()
const data = encoder.encode(client)
const hash = await crypto.subtle.digest('SHA-256', data)
const hash2Bytes = new Uint8Array(hash.slice(0,2))
const index = hash2Bytes[0] * 256 + hash2Bytes[1]
const [latitude, longitude] = lookupGeo(cityIndex, index)
clientLocations.set(client, [longitude, latitude])
}
return clientLocations
}
Insert cell
dealTripsData = {
const tripsData = []
const rng = new seedrandom('hello.')
const bandwidth = 0.2 * 1024**3 // bytes per minute - FIXME: set per pair
for (const { publishTime, provider, client, verified, count, size } of filteredPairs) {
const providerLocationList = providerLocations.get(provider)
for (let i = 0; i < count; i++) {
const providerLocation = providerLocationList[Math.floor(rng.quick() * providerLocationList.length)]
const clientLocation = clientLocations.get(client)
const startTime = dateFns.sub(publishTime, { seconds: 24 * 60 * 60 * rng.quick() })
const duration = size / bandwidth
const endTime = dateFns.add(startTime, { minutes: duration })
/*
tripsData.push({
publishTime,
startTime,
duration,
endTime,
i,
count,
// random: rng.quick(),
// providerLocations: providerLocationList,
clientLocation,
providerLocation,
provider,
client,
size
})
*/
tripsData.push({
verified,
width: 200 * 1000 // 200km
* size / (32 * 1024**3),
waypoints: [
{
coordinates: clientLocation,
timestamp: startTime.getTime()
},
{
coordinates: providerLocation,
timestamp: endTime.getTime()
}
]
})
}
}
return tripsData
}
Insert cell
numHeatmapBins = dateFns.differenceInHours(endTime, startTime) * 4
Insert cell
heatmapTimeScale = d3.scaleTime()
.domain([new Date(startTime), new Date(endTime)])
.range([0, numHeatmapBins])
Insert cell
heatmapData = Array.from(d3.rollup(
dealTripsData,
values => {
const bins = []
for (const value of values) {
const timestamp = value.waypoints[1].timestamp
const binIndex = Math.floor(heatmapTimeScale(timestamp))
for (let i = binIndex + 1; i <= binIndex + 3 * 4; i++) { // heat up for 3 hours
bins[i] = (bins[i] || 0) + value.width
}
}
// return { length: values.length, bins, v: values }
return bins
},
d => d.waypoints[1].coordinates
))
Insert cell
startTime = dealTripsData.reduce((min, { waypoints }) => {
const timestamp = waypoints[0].timestamp
if (!min) return timestamp
return min > timestamp ? timestamp : min
}, null)
Insert cell
endTime = dealTripsData.reduce((max, { waypoints }) => {
const timestamp = waypoints[1].timestamp
if (!max) return timestamp
return timestamp > max ? timestamp : max
}, null)
Insert cell
colorBlue = {
const c = d3.color(d3.schemeTableau10[0])
return [c.r, c.g, c.b]
}
Insert cell
colorOrange = {
const c = d3.color(d3.schemeTableau10[1])
return [c.r, c.g, c.b]
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dateFnsTz = import('https://cdn.skypack.dev/date-fns-tz@1.1.6?min')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
formatInTimeZone = (date, fmt, tz) =>
dateFnsTz.format(dateFnsTz.utcToZonedTime(date, tz),
fmt,
{ timeZone: tz })
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