Public
Edited
Oct 31, 2023
Fork of Untitled
Insert cell
Insert cell
function go() {
const data = [
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC23173204",
"_score" : null,
"_source" : {
"mls_status" : "ACT",
"mls_sold_date" : 1697608800000,
"mls_list_price" : "995000",
"mls_sold_price" : null,
"mls_list_date" : 1697608800000
},
"sort" : [
1697608800000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC23173109",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1697695200000,
"mls_list_price" : "450000",
"mls_sold_price" : "520000",
"mls_list_date" : 1695794400000
},
"sort" : [
1695794400000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC23107468",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1693461600000,
"mls_list_price" : 775000,
"mls_sold_price" : "851000",
"mls_list_date" : 1689919200000
},
"sort" : [
1689919200000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC23041606",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1681797600000,
"mls_list_price" : 995000,
"mls_sold_price" : "1000000",
"mls_list_date" : 1679551200000
},
"sort" : [
1679551200000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC23002857",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1677222000000,
"mls_list_price" : 749000,
"mls_sold_price" : "749000",
"mls_list_date" : 1673247600000
},
"sort" : [
1673247600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC22261304",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1676358000000,
"mls_list_price" : 1275000,
"mls_sold_price" : "1300000",
"mls_list_date" : 1673247600000
},
"sort" : [
1673247600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC22151061",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1659247200000,
"mls_list_price" : 985000,
"mls_sold_price" : "975000",
"mls_list_date" : 1658988000000
},
"sort" : [
1658988000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC22151522",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1659506400000,
"mls_list_price" : 1150000,
"mls_sold_price" : "1140000",
"mls_list_date" : 1657605600000
},
"sort" : [
1657605600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC22123681",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1657000800000,
"mls_list_price" : 1995000,
"mls_sold_price" : "2000000",
"mls_list_date" : 1655445600000
},
"sort" : [
1655445600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC21241191",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1639897200000,
"mls_list_price" : 230000,
"mls_sold_price" : "225000",
"mls_list_date" : 1640156400000
},
"sort" : [
1640156400000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC21075928",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1620540000000,
"mls_list_price" : 919000,
"mls_sold_price" : "933400",
"mls_list_date" : 1618812000000
},
"sort" : [
1618812000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC20108860",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1594879200000,
"mls_list_price" : 650000,
"mls_sold_price" : "635000",
"mls_list_date" : 1591941600000
},
"sort" : [
1591941600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC20091110",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1592719200000,
"mls_list_price" : 350000,
"mls_sold_price" : "350000",
"mls_list_date" : 1589695200000
},
"sort" : [
1589695200000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC20079724",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1592114400000,
"mls_list_price" : 875000,
"mls_sold_price" : "900000",
"mls_list_date" : 1589263200000
},
"sort" : [
1589263200000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC20060435",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1588658400000,
"mls_list_price" : 650000,
"mls_sold_price" : "646720",
"mls_list_date" : 1585893600000
},
"sort" : [
1585893600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC19079283",
"_score" : null,
"_source" : {
"mls_status" : "WDN",
"mls_sold_date" : 1697608800000,
"mls_list_price" : 349000,
"mls_sold_price" : null,
"mls_list_date" : 1556863200000
},
"sort" : [
1556863200000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC19075083",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1563084000000,
"mls_list_price" : 759000,
"mls_sold_price" : "750000",
"mls_list_date" : 1554444000000
},
"sort" : [
1554444000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC19073915",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1558504800000,
"mls_list_price" : 1688000,
"mls_sold_price" : "1700000",
"mls_list_date" : 1554357600000
},
"sort" : [
1554357600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC19049000",
"_score" : null,
"_source" : {
"mls_status" : "ACT",
"mls_sold_date" : 1557813600000,
"mls_list_price" : 949000,
"mls_sold_price" : "930000",
"mls_list_date" : 1551942000000
},
"sort" : [
1551942000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC18233234",
"_score" : null,
"_source" : {
"mls_status" : "WDN",
"mls_sold_date" : 1697608800000,
"mls_list_price" : 1715000,
"mls_sold_price" : null,
"mls_list_date" : 1540879200000
},
"sort" : [
1540879200000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC18046929",
"_score" : null,
"_source" : {
"mls_status" : "ACT",
"mls_sold_date" : 1523944800000,
"mls_list_price" : 949000,
"mls_sold_price" : "955000",
"mls_list_date" : 1520920800000
},
"sort" : [
1520920800000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC17267094",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1514444400000,
"mls_list_price" : 659000,
"mls_sold_price" : "660000",
"mls_list_date" : 1512111600000
},
"sort" : [
1512111600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC17230343",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1512889200000,
"mls_list_price" : 728000,
"mls_sold_price" : "710000",
"mls_list_date" : 1507356000000
},
"sort" : [
1507356000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC17080557",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1493532000000,
"mls_list_price" : 1457000,
"mls_sold_price" : "1457000",
"mls_list_date" : 1492236000000
},
"sort" : [
1492236000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC17031485",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1491890400000,
"mls_list_price" : 349000,
"mls_sold_price" : "312333",
"mls_list_date" : 1487142000000
},
"sort" : [
1487142000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC17031840",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1491890400000,
"mls_list_price" : 349000,
"mls_sold_price" : "312334",
"mls_list_date" : 1487142000000
},
"sort" : [
1487142000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC17031560",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1491890400000,
"mls_list_price" : 349000,
"mls_sold_price" : "312333",
"mls_list_date" : 1487142000000
},
"sort" : [
1487142000000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC17001339",
"_score" : null,
"_source" : {
"mls_status" : "SLD",
"mls_sold_date" : 1490508000000,
"mls_list_price" : 649000,
"mls_sold_price" : "644500",
"mls_list_date" : 1483599600000
},
"sort" : [
1483599600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC16098285",
"_score" : null,
"_source" : {
"mls_status" : "ACT",
"mls_sold_date" : 1470808800000,
"mls_list_price" : 475000,
"mls_sold_price" : "465000",
"mls_list_date" : 1462773600000
},
"sort" : [
1462773600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC16089913",
"_score" : null,
"_source" : {
"mls_status" : "ACT",
"mls_sold_date" : 1466056800000,
"mls_list_price" : 1029000,
"mls_sold_price" : "1034000",
"mls_list_date" : 1461909600000
},
"sort" : [
1461909600000
]
},
{
"_index" : "bk_vre_full11",
"_type" : "_doc",
"_id" : "10_CAMRMLS-Q||OC16088187",
"_score" : null,
"_source" : {
"mls_status" : "ACT",
"mls_sold_date" : 1466575200000,
"mls_list_price" : 689000,
"mls_sold_price" : "689000",
"mls_list_date" : 1461736800000
},
"sort" : [
1461736800000
]
}
]

const width = 500;
const height = 300;

const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height);
const parsedData = data.map(d => ({
date: new Date(d._source.mls_list_date),
price: d._source.mls_list_price,
status: d._source.mls_status
}));

// sort data by date
parsedData.sort((a, b) => a.date - b.date);


const simulation = d3.forceSimulation(parsedData)
.force("x", d3.forceX(width / 2))
.force("y", d3.forceY(height / 2))
.force("collide", d3.forceCollide(d => Math.sqrt(d.price / 1000)))
.on("tick", ticked);

function ticked() {
const circles = svg.selectAll("circle")
.data(parsedData, d => d.date);

circles.enter().append("circle")
.attr("r", d => Math.sqrt(d.price / 1000))
.attr("fill", d => d.status === "SLD" ? "green" : "red")
.merge(circles)
.attr("cx", d => d.x)
.attr("cy", d => d.y);

circles.exit().remove();
}


return svg.node()
}
Insert cell
go()
Insert cell
function appendSmallerCircles(d, radius, svg) {
const mainX = d.x;
const mainY = d.y;
const mainR = radius;
const maxArea = 9; // 80% of the area of the main circle
const numSmallCircles = Math.floor(Math.random() * 4) + 2;
const r = maxArea / numSmallCircles
// Generate data for smaller circles with initial radius
const smallCirclesData = Array.from({ length: numSmallCircles }, (_, i) => ({
id: i,
parentX: mainX,
parentY: mainY,
radius: r // Adjust this as needed
}));

// Run force simulation for smaller circles
const smallCircleSimulation = d3.forceSimulation(smallCirclesData)
.force("collide", d3.forceCollide(d => d.radius + 0.25).strength(0.05))
.force("boundary", forceCircularBoundary(mainX, mainY, mainR - 2).strength(1));

for (let i = 0; i < 600; i++) smallCircleSimulation.tick(); // Run the simulation
// Draw smaller circles
smallCirclesData.forEach(sc => {
svg.append("circle")
.attr("cx", sc.x)
.attr("cy", sc.y)
.attr("r", sc.radius)
.style("fill", "red");
});
}


Insert cell
forceBoundary2 = require("d3-force-boundary")
Insert cell
function constant(x) {
return function() {
return x;
};
}
Insert cell
function forceBoundary(x0, y0, x1, y1, pyramidShape) {
var strength = constant(0.1),
hardBoundary = true,
border = constant( Math.min((x1 - x0)/2, (y1 - y0)/2) ),
nodes,
strengthsX,
strengthsY,
x0z, x1z,
y0z, y1z,
borderz,
halfX, halfY;

if (typeof x0 !== "function") x0 = constant(x0 == null ? -100 : +x0);
if (typeof x1 !== "function") x1 = constant(x1 == null ? 100 : +x1);
if (typeof y0 !== "function") y0 = constant(y0 == null ? -100 : +y0);
if (typeof y1 !== "function") y1 = constant(y1 == null ? 100 : +y1);

function getVx(halfX, x, strengthX, border, alpha) {
return (halfX - x) * Math.min(2, Math.abs( halfX - x) / halfX) * strengthX * alpha;
}

function force(alpha) {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i];

// If pyramidShape is true, adjust the x boundaries based on y position
if (pyramidShape) {
var heightFactor = (node.y - y0z[i]) / (y1z[i] - y0z[i]);
var widthAdjustment = (1 - heightFactor) * (x1z[i] - x0z[i]) * 0.5;
x0z[i] += widthAdjustment;
x1z[i] -= widthAdjustment;
}

// debugger;
if ((node.x < (x0z[i] + borderz[i]) || node.x > (x1z[i] - borderz[i])) ||
(node.y < (y0z[i] + borderz[i]) || node.y > (y1z[i] - borderz[i]))) {
node.vx += getVx(halfX[i], node.x, strengthsX[i], borderz[i], alpha);
node.vy += getVx(halfY[i], node.y, strengthsY[i], borderz[i], alpha);
} else if (node.y < (y0z[i] + borderz[i]) || node.y > (y1z[i] - borderz[i])) {

// node.vx = 0;
// node.vy = 0;
}

if (hardBoundary) {
if (node.x >= x1z[i]) node.vx += x1z[i] - node.x;
if (node.x <= x0z[i]) node.vx += x0z[i] - node.x;
if (node.y >= y1z[i]) node.vy += y1z[i] - node.y;
if (node.y <= y0z[i]) node.vy += y0z[i] - node.y;
}
}
}

function initialize() {
if (!nodes) return;
var i, n = nodes.length;
strengthsX = new Array(n);
strengthsY = new Array(n);
x0z = new Array(n);
y0z = new Array(n);
x1z = new Array(n);
y1z = new Array(n);
halfY = new Array(n);
halfX = new Array(n);
borderz = new Array(n);

for (i = 0; i < n; ++i) {
strengthsX[i] = (isNaN(x0z[i] = +x0(nodes[i], i, nodes)) ||
isNaN(x1z[i] = +x1(nodes[i], i, nodes))) ? 0 : +strength(nodes[i], i, nodes);
strengthsY[i] = (isNaN(y0z[i] = +y0(nodes[i], i, nodes)) ||
isNaN(y1z[i] = +y1(nodes[i], i, nodes))) ? 0 : +strength(nodes[i], i, nodes);
halfX[i] = x0z[i] + (x1z[i] - x0z[i])/2,
halfY[i] = y0z[i] + (y1z[i] - y0z[i])/2;
borderz[i] = +border(nodes[i], i, nodes)
}
}

force.initialize = function(_) {
nodes = _;
initialize();
};

force.x0 = function(_) {
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x0;
};

force.x1 = function(_) {
return arguments.length ? (x1 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x1;
};

force.y0 = function(_) {
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y0;
};

force.y1 = function(_) {
return arguments.length ? (y1 = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y1;
};

force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
};

force.border = function(_) {
return arguments.length ? (border = typeof _ === "function" ? _ : constant(+_), initialize(), force) : border;
};

force.hardBoundary = function(_) {
return arguments.length ? (hardBoundary = _, force) : hardBoundary;
};

return force;
}
Insert cell
function forceCircularBoundary(centerX, centerY, radius) {
var strength = constant(0.1),
hardBoundary = true,
nodes;

if (typeof centerX !== "function") centerX = constant(centerX == null ? 0 : +centerX);
if (typeof centerY !== "function") centerY = constant(centerY == null ? 0 : +centerY);
if (typeof radius !== "function") radius = constant(radius == null ? 100 : +radius);

function force(alpha) {
for (var i = 0, n = nodes.length, node, dx, dy, distance; i < n; ++i) {
node = nodes[i];
dx = node.x - centerX(nodes[i], i, nodes);
dy = node.y - centerY(nodes[i], i, nodes);
distance = Math.sqrt(dx * dx + dy * dy);
// If node is outside the boundary
if (distance > radius(nodes[i], i, nodes)) {
node.vx -= dx * strength(nodes[i], i, nodes) * alpha;
node.vy -= dy * strength(nodes[i], i, nodes) * alpha;
}
if (hardBoundary && distance > radius(nodes[i], i, nodes)) {
const ratio = radius(nodes[i], i, nodes) / distance;
node.x = centerX(nodes[i], i, nodes) + ratio * dx;
node.y = centerY(nodes[i], i, nodes) + ratio * dy;
}
}
}

function initialize() {
if (!nodes) return;
}

force.initialize = function(_) {
nodes = _;
initialize();
};

force.centerX = function(_) {
return arguments.length ? (centerX = typeof _ === "function" ? _ : constant(+_), initialize(), force) : centerX;
};

force.centerY = function(_) {
return arguments.length ? (centerY = typeof _ === "function" ? _ : constant(+_), initialize(), force) : centerY;
};

force.radius = function(_) {
return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius;
};

force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
};

force.hardBoundary = function(_) {
return arguments.length ? (hardBoundary = _, force) : hardBoundary;
};

return force;
}
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