Public
Edited
Mar 31, 2020
Insert cell
md`# "pixelated" world map`
Insert cell
map = {
const s = 300;
const p = s / 50;
const svg = d3.create("svg")
.attr("height", s)
.attr("width", s)
.attr("viewBox", [0, 0, s, s]);
const clipPath = svg.append("defs").append("clipPath")
.attr("id", "globe");
clipPath.append("circle")
.attr("cx", s/2)
.attr("cy", s/2)
.attr("r", s/2);
const mapImage = svg.append("image")
.attr("xlink:href", "https://upload.wikimedia.org/wikipedia/commons/5/51/BlankMap-Equirectangular.svg")
.attr("width", s*2)
.attr("height", s)
.attr("x", s/10 - offset*p)
.attr("clip-path", "url(#globe)");
for(let lineX = 0; lineX < s; lineX += p) {
svg.append("line")
.attr("x1", lineX + (offset % p))
.attr("x2", lineX + (offset % p))
.attr("y1", 0)
.attr("y2", s)
.attr("stroke", "white")
.attr("stroke-width", p/4)
.attr("clip-path", "url(#globe)");
}
for(let lineY = 0; lineY < s; lineY += p) {
svg.append("line")
.attr("x1", 0)
.attr("x2", s*2)
.attr("y1", lineY)
.attr("y2", lineY)
.attr("stroke", "white")
.attr("stroke-width", p/4)
.attr("clip-path", "url(#globe)");
}
svg.append("circle")
.attr("cx", s/2)
.attr("cy", s/2)
.attr("r", s/2 - 1)
.attr("stroke", "#000")
.attr("stroke-width", p/4)
.attr("fill", "transparent");
const poiCircle = svg.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", p/2)
.attr("opacity", 0)
.attr("fill", "red");
function convertPoi(d) {
return {
...d,
coord: [ (d.coord[0]-offset+0.5)*p, (d.coord[1]+0.5)*p ]
};
}
const convertedPoi = poi.map(convertPoi);
const filteredPoi = convertedPoi.filter(d => inCircle(d.coord[0], d.coord[1], s));
const points = filteredPoi
.map(d => d.coord)
.filter(([x, y]) => inCircle(x, y, s));
const delaunay = d3D.Delaunay.from(points);
function hidePoiCircle() {
poiCircle
.attr("opacity", 0);
}
svg.on("mousemove", () => {
const [mouseX, mouseY] = d3.mouse(svg.node());
//const [x, y] = [Math.floor(mouseX/p), Math.floor(mouseY/p)];
if(!inCircle(mouseX, mouseY, s)) {
hidePoiCircle();
return;
}
const i = delaunay.find(mouseX, mouseY);
if(i === undefined || i === null || i < 0) {
hidePoiCircle();
return;
}
const place = filteredPoi[i];
if(!place) {
hidePoiCircle();
return;
}
poiCircle
.attr("cx", place.coord[0])
.attr("cy", place.coord[1]);
poiCircle
.attr("opacity", 1);
});

const wrapper = html`<div class="wrapper"></div>`;
wrapper.append(svg.node());
return wrapper;
}
Insert cell
Insert cell
inCircle = (x0, y0, size) => {
const r = size/2 - 2;
const x1 = size/2;
const y1 = size/2;
return Math.sqrt(Math.pow((x1-x0), 2) + Math.pow((y1-y0), 2)) < r;
}
Insert cell
offset = 22
Insert cell
Insert cell
d3 = require("d3@5")
Insert cell
d3D = require("d3-delaunay@5.2.1")
Insert cell
import { serialize } from "@cocisuts/saving-svg";
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