Public
Edited
Nov 3
Insert cell
Insert cell
container = html `<div style="height:450px;"></div>`
Insert cell
Insert cell
Insert cell
data = {
const DEGREE_TO_RADIANS = Math.PI / 180;
let r = 0;
let a = 0;
return new Array(1976).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 = (1 - radius * 6) * 150;
const p = {
size: radius * radius * 20,
position: [cosA * dist, sinA * dist],
color: [(cosA + 1) / 2 * 255, (sinA + 1) / 2 * 255, 128]
};
a += 360 / (r * 6 + 1);
if (a > 359.99) {
a = 0;
r++;
}
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},
}
}
Insert cell
layer0 = {
const {Layer} = deck;
// Our custom layer class
class ScatterplotLayer extends Layer {
initializeState() {
// TODO
}
updateState() {
// TODO
}
}
ScatterplotLayer.layerName = 'ScatterplotLayer';
ScatterplotLayer.defaultProps = defaultProps;

return new ScatterplotLayer({
id: `scatterplot-${Date.now()}`,
data,
getPosition: d => d.position,
getRadius: d => d.size,
getColor: d => d.color
});
}
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;
}`
Insert cell
fragmentShader = `
precision highp float;

uniform float smoothRadius;

varying vec4 vColor;
varying vec2 vPosition;

void main(void) {
float distToCenter = length(vPosition);


float alpha = smoothstep(1.0, 1.0 - smoothRadius, distToCenter);

gl_FragColor = vec4(vColor.rgb, vColor.a * alpha);
}`;
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,
// vertexCount: 4,
// attributes: {
// positions: {size: 2, value: positions}
// }
// });
// return new Model(gl, {vs: vertexShader, fs: fragmentShader, geometry, isInstanced: true});
// }
// }

function getModel(device) {
const {Model, Geometry} = device;

// Define the geometry
const positions = new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]);
const geometry = new Geometry({
drawMode: device.gl.TRIANGLE_FAN, // Use device.gl
vertexCount: 4,
attributes: {
positions: {size: 2, value: positions}
}
});

// Return the Model using device
return new Model(device, {
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)
// });
// }
initializeState() {
const {device} = this.context; // Use device instead of gl
// Register attributes with device
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: device.gl.FLOAT, // Use device.gl for types
accessor: 'getPosition'
},
instanceRadius: {
size: 1,
accessor: 'getRadius',
defaultValue: 1
},
instanceColors: {
size: 4,
normalized: true,
type: device.gl.UNSIGNED_BYTE,
accessor: 'getColor',
defaultValue: [0, 0, 0, 255]
}
});
// Save the model in layer state
this.setState({
model: this.getModel(device) // Pass device
});
}
// updateState() {
// // Retrieve the model from layer state
// this.state.model.setUniforms({
// smoothRadius: this.props.smoothRadius
// });
// }

updateState() {
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]});
return layer;
}
Insert cell
Insert cell
layer2.getAttributeManager().getAttributes();
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