Public
Edited
Dec 8, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
deckgl = {
const container = html`<div style="width:${size.w}px;height:${size.h}px;">`;
yield container;

let activeObject = undefined;
const minRadiusPx = 5;
const maxRadiusPx = 50;
const _scales = {}
mobx.autorun(() => {
_scales.x = model.markers.bubble.encoding.x.scale.d3Scale.range([0, size.w]);
_scales.y = model.markers.bubble.encoding.y.scale.d3Scale.range([0, size.h]);
_scales.size = model.markers.bubble.encoding.size.scale.d3Scale.range(getSizeRange());
_scales.color = model.markers.bubble.encoding.color.scale.d3Scale;
});
const props = {
getPosition: (d) => {
const z = d[KEY] == activeObject?.[KEY] ? 2: 1;
return ([_scales.x(d.x), _scales.y(d.y)])
},
getFillColor: (d) => {
const c = d3.color(_scales.color(d.color)).formatRgb().slice(4, -1).split(",").map(v=>+v);
const alpha = activeObject ? d[KEY] == activeObject?.[KEY] ? 250 : 20 : 180;
c[3] = alpha;
return c;
},
getLineColor: (d) => {
const c = [0, 0, 0, 127]
const alpha = activeObject ? d[KEY] == activeObject?.[KEY] ? 250 : 20 : 127;
c[3] = alpha;
return c;
},
getRadius: (d) => {
return Math.sqrt(_scales.size(d.size))
},
onHover: (d) => {
//console.log("onhover", d, activeObject);
const invalidate = d?.[KEY] !== activeObject?.[KEY]
activeObject = d;
if (invalidate) {
//setTimeout(() => {
//console.log("invalidate", d, activeObject);
deckInstance.setProps({layers: getLayers()})
//}, 0);
}
}
};
const getLayers = function(data) {
return [
new deck.ScatterplotLayer({
id: "scatterPlotLayer",
data: mutable data,
stroked: true,
getPosition: d => props.getPosition(d),
getRadius: d => props.getRadius(d),
radiusUnits: 'pixels',
getFillColor: d => props.getFillColor(d),
getLineColor: d => props.getLineColor(d),
getLineWidth: 1,
lineWidthUnits: 'pixels',
billboard: true,
pickable: true,
onHover: ({object}) => props.onHover(object),
updateTriggers: {
getFillColor: [activeObject],
getLineColor: [activeObject],
getPosition: [activeObject]
},
transitions: {
getPosition: {
duration: 600
},
getFillColor: {
duration: 60
}
}
})
]
}
const deckInstance = new deck.DeckGL({
// The HTML container to render into
container,

// Mapbox settings
// set `map: false` to turn off Mapbox base map
map: false,
views: new deck.OrthographicView({
flipY: false
}),
initialViewState: {
target: [size.w * 0.5, size.h * 0.5, 0],
zoom: 0
},
controller: true,
getTooltip: ({object}) => object && ({
text: `${object.country}: ${(new Date(object.time)).getUTCFullYear()}`,
style: {
"background-color": "#ffffff",
"border": "1px black solid",
"border-radius": "5px",
"height": "10px",
"line-height": "10px"
}
})
});

let _data;
mobx.autorun(() => {
if (model.markers.bubble.state !== "fulfilled") return;

_data = model.markers.bubble.dataArray;
deckInstance.setProps({layers: getLayers(_data)});
})

function getSizeRange() {
const extent = [0, 1];
let minArea = VizabiSharedComponents.LegacyUtils.radiusToArea(Math.max(maxRadiusPx * extent[0], minRadiusPx));
let maxArea = VizabiSharedComponents.LegacyUtils.radiusToArea(Math.max(maxRadiusPx * extent[1], minRadiusPx));
return [minArea, maxArea];
}
}
Insert cell
model = {
const model = Vizabi(data_config(url, sheet, {
markers: {
bubble: {
encoding: {
y: {data: {concept: "LEX"}},
x: {data: {concept: "GDP"}, scale: {type: "log"}},
size: {data: {concept: "POP"}},
color: {data: {concept: "world_region"}}
}
}
}
}));
const fullMarker = model.markers.bubble;
Vizabi.utils.applyDefaults(model.markers.bubble.config, DEFAULT_CORE("bubble"));
const frameType = Vizabi.stores.encodings.modelTypes.frame;
const { marker, splashMarker } = frameType.splashMarker(fullMarker);
model.markers.bubble = marker;

return model;
}
Insert cell
KEY = Symbol.for("key")
Insert cell
size = ({
w: 700,
h: 500
})
Insert cell
mutable data = []
Insert cell
mutable stepCount = null
Insert cell
mobx.autorun(() => {
console.log(step);
model.markers.bubble.state == "fulfilled" && (mutable stepCount = model.markers.bubble.encoding.frame.stepCount - 1);
})
Insert cell
mobx.autorun(() => {
if (model.markers.bubble.state !== "fulfilled") return;

mutable data = model.markers.bubble.dataArray;
})
Insert cell
mobx.autorun(() => {
model.markers.bubble.state == "fulfilled" && model.markers.bubble.encoding.frame.setStep(step);
})
Insert cell
Insert cell
Insert cell
data_config = (url, sheet, custom = {}) => deepmerge({
markers: {
bubble: {
data: { source: "data1" }
}
},
dataSources: {
data1: {
path: url,
sheet: sheet,
timeInColumns: "",
hasNameColumn: "",
modelType: "google_csv",
locale: "en"
}
}
}, custom)

Insert cell
Insert cell
{
document.head.appendChild(html`<link rel='stylesheet'
href='https://www.unpkg.com/@vizabi/shared-components@1.40.0/build/VizabiSharedComponents.css' />`);
return "css imports"
}
Insert cell
Insert cell
Vizabi = require("@vizabi/core@1.32.2")
Insert cell
VizabiSharedComponents = require("@vizabi/shared-components@1.43.2")
Insert cell
deepmerge = (await require("lodash@4")).merge
Insert cell
mobx = require('mobx@5.15.7/lib/mobx.umd.min.js')
Insert cell
d3 = require("d3@6.7.0")
Insert cell
deck = require("https://unpkg.com/deck.gl@latest/dist.min.js")
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