Public
Edited
Apr 29, 2024
Insert cell
Insert cell
Insert cell
// see https://talk.observablehq.com/t/deck-gl-seprops-circular-definition/7389/2
deckgl = {

var ini_zoom_mode = 'Y'

const instance = new deck.DeckGL({
container,
views,

// https://github.com/visgl/deck.gl/discussions/7574#discussioncomment-4656419
// this is a shorthand when using the default view
// controller: {scrollZoom: true, inertia: true, zoomAxis: ini_zoom_mode},

// both seem to work
/////////////////////////
// initialViewState: ini_view_state,
viewState: ini_view_state,

onViewStateChange: ({viewState, viewId}) => {

if (viewId === 'left_heatmap') {
viewState.left_heatmap = viewState;
viewState.right_heatmap = ini_view_state_generic
// console.log('left heatmap', viewState.right_heatmap)

} else {
// viewState.left_heatmap = { ...viewState.left_heatmap}

viewState.left_heatmap = ini_view_state_generic
viewState.right_heatmap = viewState;

// try to set the left target
viewState.left_heatmap.target = [ini_view_state_generic.target[0], viewState.target[1]]

console.log('right heatmap', viewState.left_heatmap)
}
// Update deck
instance.setProps({viewState: {...viewState}});
}

// onViewStateChange: ({viewId, viewState}) => {
// console.log(viewId, 'onviewstatechange!!')
// const nextZoomMode = viewState.zoom[0] > 0 ? 'all' : 'X'
// // if (zoomMode !== nextZoomMode) {
// // // update zoom mode
// // zoomMode = nextZoomMode

// // // update Props
// // instance.setProps({
// // controller: {zoomAxis: zoomMode}
// // })
// // }
// }
// getTooltip: ({object}) => object && {
// html: `${object}`,
// style: {
// fontSize: '0.8em',
// padding: '5px',
// }
// },
})
return instance;
}
Insert cell
ini_zoom = [-2, -2]
Insert cell
ini_target = [500, 500, 0]
Insert cell
views = [
new deck.OrthographicView({
id: 'left_heatmap',
x: '0%',
width: '50%',
// controller: true
controller: {scrollZoom: true, inertia: true, zoomAxis: 'X'},
}),

new deck.OrthographicView({
id: 'right_heatmap',
x: '50%',
width: '50%',
// controller: true
controller: {scrollZoom: true, inertia: true, zoomAxis: 'Y'},
}),
]
Insert cell
ini_view_state_generic = ({target: ini_target, zoom: ini_zoom})
Insert cell
ini_view_state = ({
'left_heatmap': ini_view_state_generic,
'right_heatmap': ini_view_state_generic
})
Insert cell
Insert cell
// num_points = 100
num_points = 0.001 * 1000 * 1000
Insert cell
num_cols = Math.floor(Math.sqrt(num_points))
Insert cell
mil_points = num_points /1e6
Insert cell
Math.pow(0.96, 0)
Insert cell
num_cols
Insert cell
data = {
const DEGREE_TO_RADIANS = Math.PI / 180;
let r = 0;
let a = 0;

// const num_cols = 100;
// const num_rows = 100;
var row_index = 0
return new Array(num_points).fill(0).map(_ => {
const cosA = Math.cos(a * DEGREE_TO_RADIANS);
const sinA = Math.sin(a * DEGREE_TO_RADIANS);
const radius = Math.pow(0.96, r);
const dist = 45

var inst_col = (a % num_cols)

if (a % num_cols === 0){
row_index += 1;
}
const p = {
size: radius * radius * 20,
position: [dist * inst_col, dist * row_index * 1],
// color: [(cosA + 1) / 2 * 255, (sinA + 1) / 2 * 255, 128]
color: [255, 0, 0]
};
a += 1;
return p;
});
}
Insert cell
Insert cell
defaultProps = {
return {
// Center of each circle, in [longitude, latitude, (z)]
getPosition: {type: 'accessor', value: x => x.position},
// Radius of each circle, in meters
getRadius: {type: 'accessor', value: 1},
// Color of each circle, in [R, G, B, (A)]
getColor: {type: 'accessor', value: [0, 0, 0, 255]},
// Amount to soften the edges
// smoothRadius: {type: 'number', min: 0, value: 0.5},
smoothRadius: {type: 'number', min: 0, value: 0.0},
}
}
Insert cell
Insert cell
Insert cell
// layer0.props.smoothRadius
Insert cell
Insert cell
// vertexShader = `
// attribute vec3 positions;
// attribute vec3 instancePositions;
// attribute vec3 instancePositions64Low;
// attribute float instanceRadius;
// attribute vec4 instanceColors;

// varying vec4 vColor;
// varying vec2 vPosition;

// void main(void) {
// vec3 offsetCommon = positions * project_size(instanceRadius);
// vec3 positionCommon = project_position(instancePositions, instancePositions64Low);
// gl_Position = project_common_position_to_clipspace(vec4(positionCommon + offsetCommon, 1.0));

// vPosition = positions.xy;
// vColor = instanceColors;
// }`

vertexShader = `
attribute vec3 positions;
attribute vec3 instancePositions;
attribute vec3 instancePositions64Low;
attribute float instanceRadius;
attribute vec4 instanceColors;

varying vec4 vColor;
varying vec2 vPosition;

void main(void) {
vec3 positionCommon = project_position(instancePositions + positions * instanceRadius, instancePositions64Low);
gl_Position = project_common_position_to_clipspace(vec4(positionCommon, 1.0));

vPosition = positions.xy;
vColor = instanceColors;
}`
Insert cell
// gl_FragColor = vec4(vColor.rgb, vColor.a);

fragmentShader = `
precision highp float;

uniform float smoothRadius;

varying vec4 vColor;
varying vec2 vPosition;

void main(void) {
gl_FragColor = vec4(vColor.rgb, 1.0);
}`;
Insert cell
Insert cell
getModel = {
const {Model, Geometry} = luma;
return gl => {
// Four corners of the quad
const positions = new Float32Array([-1, -1,
-1, 1,
1, 1,
1, -1]);
const geometry = new Geometry({

drawMode: gl.TRIANGLE_FAN,
// drawMode: gl.TRIANGLES, // default
vertexCount: 4,
attributes: {
positions: {size: 2, value: positions}
}
});
return new Model(gl, {vs: vertexShader, fs: fragmentShader, geometry, isInstanced: true});
}
}
Insert cell
Insert cell
layer2 = {
const {Layer} = deck;
class ScatterplotLayer extends Layer {
initializeState() {
const {gl} = this.context;

// Register attributes
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: gl.DOUBLE,
accessor: 'getPosition'
},
instanceRadius: {
size: 1,
accessor: 'getRadius',
defaultValue: 1
},
instanceColors: {
size: 4,
normalized: true,
type: gl.UNSIGNED_BYTE,
accessor: 'getColor',
defaultValue: [0, 0, 0, 255]
}
});
// Save the model in layer state
this.setState({
model: getModel(gl)
});
}
updateState() {
// Retrieve the model from layer state
this.state.model.setUniforms({
// smoothRadius: this.props.smoothRadius
});
}
}
ScatterplotLayer.layerName = 'ScatterplotLayer';
ScatterplotLayer.defaultProps = defaultProps;

const layer = new ScatterplotLayer({
id: `scatterplot-${Date.now()}`,
data,
getPosition: d => d.position,
getRadius: d => d.size,
getColor: d => d.color
});
deckgl.setProps({
layers: [layer],
getTooltip: ({object}) => object && object.message
});
return layer;
}
Insert cell
Insert cell
layer2.getAttributeManager().getAttributes();
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