Published
Edited
Sep 19, 2022
3 stars
Insert cell
Insert cell
{
const context = DOM.context2d(width, height);
const curve = d3.curveBasis(context);
const canvas = context.canvas;
const path = d3.geoPath(projection, context);

// Initialize colors
const colors = [];
const colorRange = d3.interpolateCool;
const n = 5;
for (let i = 0; i < n; i++) {
colors.push(d3.rgb(colorRange(i / (n - 1))));
}

function drawWardsCities(offsetX, offsetY, strokeColor) {
context.beginPath();
path(tokyoOutline);
context.strokeStyle = strokeColor;
context.lineWidth = 0.5;
context.stroke();
}

function drawArea(strokeColor, fillColor, feature) {
context.beginPath();
path(feature);
context.strokeStyle = strokeColor;
context.fillStyle = fillColor;
context.lineWidth = 0.2;
context.fill();
context.stroke();
}

function draw(drawAreas) {
drawWardsCities(0, 0, colors[0], 0);

if (drawAreas) {
const areaStrokeColor = colors[1];
const areaFillColor = d3.color(colors[2]).copy({ opacity: 0.5 });
//const random = d3.randomUniform(0.5, 0.8);
for (let feature of tokyoMainlandAreas.features) {
//const areaFillColor = d3.color(colors[2]).copy({ opacity: random() });
drawArea(areaStrokeColor, areaFillColor, feature);
}
}
}

// Draw caching
let cachedImage = null;

function cacheCanvas() {
cachedImage = context.getImageData(
0,
0,
context.canvas.width,
context.canvas.height
);
}
function drawFromCache() {
if (cachedImage) {
context.putImageData(cachedImage, 0, 0);
}
}

function render(transform) {
context.save();
context.clearRect(0, 0, width, height);

if (transform) {
context.translate(transform.x, transform.y);
context.scale(transform.k, transform.k);
}
draw(true);

// Render focus point
if (context.canvas.coordinates) {
context.lineWidth = 1;
context.strokeStyle = "#d00";
context.beginPath(),
path({ type: "Point", coordinates: context.canvas.coordinates }),
context.stroke();
}
context.restore();
}

// Zooming
let zoomTransform = d3.zoomIdentity;
let zoomer = d3.zoom().scaleExtent([1, 8]);

d3.select(context.canvas).call(
zoomer.on("zoom", function (evt) {
zoomTransform = evt.transform;
render(zoomTransform);
})
);

// Mouse Handling
// let mousedown = false;
// context.canvas.onmousedown = (event) => {
// mousedown = true;
// context.canvas.onmousemove(event);
// };

// context.canvas.onmousemove = ({ layerX, layerY }) => {
// if (!mousedown) return;
// let latlng = projection.invert([layerX, layerY]);
// context.canvas.coordinates = latlng;
// render(zoomTransform);
// context.canvas.dispatchEvent(new CustomEvent("input"));
// };

// context.canvas.onmouseup = (event) => {
// mousedown = false;
// };

draw(true);
cacheCanvas();
render(zoomTransform);
return context.canvas;
}
Insert cell
Insert cell
Insert cell
Insert cell
projection = d3.geoMercator().fitSize([width, height], tokyoOutline)
Insert cell
Insert cell
Insert cell
Insert cell
tokyoIslands = [
"",
"小笠原村",
"大島町",
"神津島村",
"青ヶ島村",
"八丈町",
"新島村",
"御蔵島村",
"三宅村",
"利島村"
]
Insert cell
tokyoMainlandAreas = {
let features = tokyoAreas.features.filter(
(f) => !tokyoIslands.includes(f.properties.CITY_NAME)
);
return {
type: "FeatureCollection",
features: features
};
}
Insert cell
outliers = tokyoMainlandAreas.features.slice(5600)
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