Published
Edited
Feb 9, 2022
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mapProjection = d3
.geoAlbersUsa()
// .fitSize([mapWidth - 10, mapHeight - 10], getState("New York"))
.scale(projScale)
.translate([projX, projY])
Insert cell
Insert cell
Insert cell
Insert cell
generateDensity = (feature) => {
if (feature === undefined || feature === null) return;

const { properties } = feature;
const bounds = d3.geoPath().bounds(feature);
// const bounds = path.bounds(feature);
// const populationData = Math.round(feature.metric); // / peoplePerPoint);
if (!feature.points) return;
const populationData = Math.round(+feature.points / peoplePerPoint);

if (!populationData) return;

// https://github.com/d3/d3-geo#path_bounds
const x_min = bounds[0][0];
const x_max = bounds[1][0];
const y_min = bounds[0][1];
const y_max = bounds[1][1];

let hits = 0;
let count = 0;

const limit = populationData * 10; // limit test to 10x the population.

let points = [];
while (hits < populationData - 1 && count < limit) {
const lat = y_min + Math.random() * (y_max - y_min);
const lng = x_min + Math.random() * (x_max - x_min);

// const randomPoint = turf.point(projection.invert([lng, lat]), {
const randomPoint = turf.point([lng, lat], {
//color: "gray" //colorScheme[ethnicity_column]
color: chance.pickone(raceScaleEntities).color
});

if (turf.booleanPointInPolygon(randomPoint, feature)) {
points.push(randomPoint);
hits++;
}

count++;
}
// console.log(hits, count);

return turf.featureCollection(points);
}
Insert cell
//makeMapWithSimulation(getState("New York"))
Insert cell
Insert cell
Insert cell
init = {
resetSim;
config.startTime = false;
config.ticks = 0;

cancelAnimationFrame(config.animReqId);

const nodesToAdd = [];
for (let i = 0; i < 1000; i++) {
// const fakePoint = JSON.parse(JSON.stringify(chance.pickone(fakePointData)));
const fakePoint = JSON.parse(JSON.stringify(fakePointData[0]));

const projectedPoint = mapProjection([
fakePoint.latlng[1],
fakePoint.latlng[0]
]);
fakePoint.id = Math.random();
fakePoint.delay = (i - 1) * 0.1 * delayBetweenNodes;
fakePoint.x =
projectedPoint[0] + chance.floating({ min: -offsetPx, max: offsetPx });
fakePoint.y =
projectedPoint[1] + chance.floating({ min: -offsetPx, max: offsetPx });

if (chance.bool())
fakePoint.targetX = fakePoint.x + chance.integer({ min: -100, max: 800 });
if (chance.bool())
fakePoint.targetY = fakePoint.y + chance.integer({ min: 100, max: 800 });

fakePoint.r = windNodeR;
nodesToAdd.push(fakePoint);
}

forceSim.nodes([]).stop();

// for (let i = 0; i < 130; i++) {
// forceSim.tick();
// }

renderNodes(forceDemographicMap.getContext("2d"));

config.animReqId = requestAnimationFrame((t) => animate(t, nodesToAdd));

return nodesToAdd;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//windNodeCount = 10
//windNodeCount = Inputs.range([10, 1000], { value: 10, step: 1 })
Insert cell
function makeMap(zoomShape) {
// let ctx = DOM.context2d(mapWidth, mapHeight);
// // ctx.canvas.width = mapWidth; // * window.devicePixelRatio;
// // ctx.canvas.height = mapHeight; // * window.devicePixelRatio;
// ctx.canvas.width = mapWidth;
// ctx.canvas.height = mapHeight;
// ctx.canvas.style.width = mapWidth + "px";
// ctx.canvas.style.height = mapHeight + "px";
const canvas = html`<canvas width=${width + "px"} height="${
mapHeight + "px"
}"></canvas>`;
const ctx = canvas.getContext("2d");
scaleCanvas(ctx.canvas, ctx);

// d3.select(ctx.canvas).call(
// d3
// .zoom()
// .scaleExtent([1, 12])
// .on("zoom", ({ transform }) => zoomed(transform))
// );

let projection = mapProjection;
/*.fitSize(
[mapWidth - 10, mapHeight - 10],
zoomShape
);*/

let path = d3.geoPath(projection).context(ctx);

function zoomed(transform) {
ctx.save();
ctx.clearRect(0, 0, mapWidth, mapHeight);
ctx.translate(transform.x, transform.y);
ctx.scale(transform.k, transform.k);

ctx.beginPath();
path(zoomShape);
ctx.stroke();

shapesWithData.forEach((d) => {
ctx.strokeStyle = "#ddd";
ctx.lineWidth = 0.25 / transform.k;
//ctx.globalAlpha = 0;
//if (d.nyt) ctx.globalAlpha = 1;

if (showCountyLines) {
ctx.beginPath();
path(d);
ctx.stroke();
}

// const fillColor = d3.color(colorScale(colorScale.domain()[1]));
// fillColor.opacity = 0.25;
// ctx.fillStyle = fillColor.toString();

if (d.points && d.points.features) {
d.points.features.forEach((feature) => {
// const {
// properties: { color }
// } = feature;

const {
geometry: {
coordinates: [x, y]
}
} = feature;

let c = projection([x, y]);
if (!c) return;
// console.log({ x, y, feature });
const dotColor = d3.color(feature.properties.color);
dotColor.opacity = dotOpacity;
ctx.fillStyle = dotColor.toString();
ctx.fillRect(
c[0],
c[1],
demographicPointSize / transform.k,
demographicPointSize / transform.k
);
// ctx.beginPath();
// ctx.arc(
// c[0],
// c[1],
// 10, //demographicPointSize / transform.k,
// 0,
// Math.PI * 2
// );
// ctx.fill();
});
}
});

ctx.fillStyle = "#111";
ctx.beginPath();
ctx.font = "24px Helvetica";
// ctx.fillText(normalDate(date), mapWidth / 2 - 40, 35);
ctx.fill();

ctx.fill();
ctx.restore();
}

zoomed(d3.zoomIdentity);

// let r = 0.5;
// let r = 1;

// ctx.strokeStyle = "#111";
// ctx.lineWidth = 1;
// ctx.globalAlpha = 1;

return ctx.canvas;
}
Insert cell
function makeMapWithSimulation(zoomShape) {
resetSim;
// let ctx = DOM.context2d(mapWidth, mapHeight);
// // ctx.canvas.width = mapWidth; // * window.devicePixelRatio;
// // ctx.canvas.height = mapHeight; // * window.devicePixelRatio;
// ctx.canvas.width = mapWidth;
// ctx.canvas.height = mapHeight;
// ctx.canvas.style.width = mapWidth + "px";
// ctx.canvas.style.height = mapHeight + "px";
// scaleCanvas(ctx.canvas, ctx);
const canvas = html`<canvas width=${width + "px"} height="${
mapHeight + "px"
}"></canvas>`;
const ctx = canvas.getContext("2d");
scaleCanvas(canvas, ctx);

// d3.select(ctx.canvas).call(
// d3
// .zoom()
// .scaleExtent([1, 12])
// .on("zoom", ({ transform }) => zoomed(transform))
// );

let projection = mapProjection;
/*
.fitSize(
[mapWidth - 10, mapHeight - 10],
zoomShape
);
*/
let path = d3.geoPath(projection).context(ctx);

function zoomed(transform) {
ctx.save();
ctx.clearRect(0, 0, mapWidth, mapHeight);
ctx.translate(transform.x, transform.y);
ctx.scale(transform.k, transform.k);

ctx.beginPath();
path(zoomShape);
ctx.stroke();

shapesWithData.forEach((d) => {
ctx.strokeStyle = "#ddd";
ctx.lineWidth = 0.25 / transform.k;
//ctx.globalAlpha = 0;
//if (d.nyt) ctx.globalAlpha = 1;

if (showCountyLines) {
ctx.beginPath();
path(d);
ctx.stroke();
}
});

ctx.fillStyle = "#111";
ctx.beginPath();
ctx.font = "24px Helvetica";
// ctx.fillText(normalDate(date), mapWidth / 2 - 40, 35);
ctx.fill();

ctx.restore();
}

zoomed(d3.zoomIdentity);

return canvas;
}
Insert cell
function animate(time, nodesToAdd) {
//console.log("animate", time);
if (!config.startTime) {
config.startTime = time;
}

const progress = time - config.startTime;
const nodes = renderNodes(forceDemographicMap.getContext("2d"));
const newNodes = [];

for (let i = 0; i < nodesToAdd.length; i++) {
const node = nodesToAdd[i];
if (node.delay < progress) {
newNodes.push(node);
nodesToAdd.splice(i, 1);
i--;
}
}

forceSim.nodes(nodes.concat(newNodes)).alpha(1).tick();

// render composite
drawCompositeCanvas(compositeCanvas.getContext("2d"));

config.ticks++;

if (config.ticks < maxTicks || nodesToAdd.length > 0) {
config.animReqId = requestAnimationFrame((t) => animate(t, nodesToAdd));
}
}
Insert cell
function renderNodes(ctx) {
//let path = d3.geoPath(mapProjection).context(ctx);
//console.log("drawing");
//ctx.fillStyle = `rgba(255, 255, 255, ${bgOpacity})`;
//ctx.fillRect(0, 0, mapWidth, mapHeight);

//ctx.clearRect(0, 0, mapWidth, mapHeight);

// ctx.globalCompositeOperation = "xor";
//ctx.globalCompositeOperation = "destination-in";

// ctx.strokeStyle = "#111";
// ctx.lineWidth = 2;
//ctx.globalAlpha = 1;

// Draw nation
// ctx.beginPath();
// path(nationShape);
// ctx.stroke();

// windPoints.forEach((d) => { // real data
forceSim.nodes().forEach((d) => {
const fillColor = d3.color("red");
fillColor.opacity = +nodeOpacity;
ctx.fillStyle = fillColor.toString();
ctx.beginPath();
ctx.arc(d.x, d.y, d.r, 0, Math.PI * 2);
//ctx.arc(d.x, d.y, 50, 0, Math.PI * 2);

ctx.fill();
});

return forceSim.nodes();
}
Insert cell
Insert cell
Insert cell
raceScaleEntities = [
{
key: "U7B003",
description: "Population of one race: White alone",
color: "red"
},
{
key: "U7B004",
description: "Population of one race: Black or African American alone",
color: "orange"
},
{
key: "U7B005",
description:
"Population of one race: American Indian and Alaska Native alone",
color: "yellow"
},
{
key: "U7B006",
description: "Population of one race: Asian alone",
color: "green"
},
{
key: "U7B007",
description:
"Population of one race: Native Hawaiian and Other Pacific Islander alone",
color: "blue"
},
{
key: "U7B008",
description: "Population of one race: Some Other Race alone",
color: "indigo"
}
]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import { slider } from "@jashkenas/inputs"
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