Public
Edited
Jun 9, 2024
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
geoRoskaPlonkaRaw = {
// imports
const azimuthalEqualAreaRaw = d3.geoAzimuthalEqualAreaRaw,
abs = Math.abs,
sqrt = Math.sqrt,
sign = Math.sign,
atan2 = Math.atan2,
sin = Math.sin,
cos = Math.cos,
pi = Math.PI;

return function () {
var beta = sqrt(pi / 6),
SQRT_2 = sqrt(2),
gamma = sqrt(SQRT_2),
r = beta / SQRT_2,
R = 1 / r;

var roskaplonka = function(lambda, phi) {
var t = azimuthalEqualAreaRaw(lambda, phi),
X = t[0], Y = t[1],
xy = false;
if (abs(Y) > abs(X)) xy = X, X = Y, Y = xy, xy = true;

var A = sqrt(2 * X * X + Y * Y),
B = sqrt(A) * sqrt(abs(X) + A),
x = r * B * sign(X),
y = R * B * (atan2(Y,abs(X)) - atan2(Y, A));

if (xy) xy = x, x = y, y = xy;
return [x,y];
}

roskaplonka.invert = function(x, y) {
var xy = false;
if (abs(y) > abs(x)) xy = x, x = y, y = xy, xy = true;

if (x === 0) return [0,0];

var cd = cos(y / x * pi / 12),
sd = sin(y / x * pi / 12),
xp = gamma / beta * x / sqrt(SQRT_2 - cd),
X = (SQRT_2 * cd - 1) * xp,
Y = SQRT_2 * sd * xp;

if (xy) xy = X, X = Y, Y = xy;
return azimuthalEqualAreaRaw.invert(X,Y);
};

return roskaplonka;
}
}
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
function map(projection) {
const context = DOM.context2d(width, width / 2);

projection.fitExtent(
[
[2, 2],
[width - 2, width / 2 - 2]
],
{ type: "Sphere" }
);

var path = d3.geoPath(projection, context);
context.clearRect(0, 0, width, width / 2);
if (show_sphere)
context.beginPath(),
path({ type: "Sphere" }),
(context.fillStyle = "#fefef6"),
context.fill();
if (show_sphere)
context.beginPath(),
path(d3.geoGraticule()()),
(context.strokeStyle = "#ccc"),
context.stroke();
if (show_equator)
context.beginPath(),
path(
d3
.geoGraticule()
.step([0, 100])
.extent([
[-179.99, -25],
[179.99, 25]
])()
),
(context.strokeStyle = "brown"),
context.stroke();
if (show_land)
context.beginPath(),
path(land),
(context.fillStyle = "black"),
context.fill();
if (show_sphere || show_structure || !show_land)
context.beginPath(),
path({ type: "Sphere" }),
(context.strokeStyle = "#000"),
context.stroke();

// Polyhedral projections expose their structure as projection.tree()
// To draw them we need to cancel the rotate
if (show_structure && projection.tree) {
var rotate = projection.rotate();
projection.rotate([0, 0, 0]);

// run the tree of faces to get all sites and folds
var sites = [],
folds = [],
i = 0;
function recurse(face) {
var site = d3.geoCentroid({ type: "MultiPoint", coordinates: face.face });
site.id = face.id || i++;
sites.push(site);
if (face.children) {
face.children.forEach(function (child) {
folds.push({
type: "LineString",
coordinates: child.shared.map((e) =>
d3.geoInterpolate(e, face.centroid)(1e-5)
)
});
recurse(child);
});
}
}
recurse(projection.tree());

// sites & numbers
context.beginPath(),
path.pointRadius(10)({ type: "MultiPoint", coordinates: sites }),
(context.fillStyle = "white"),
(context.strokeStyle = "black"),
context.fill(),
context.stroke();
sites.forEach((site) => {
(context.textAlign = "center"),
(context.fillStyle = "black"),
(context.font = "16px Georgia"),
(context.textBaseline = "middle"),
context.fillText(site.id, projection(site)[0], projection(site)[1] - 1);
});

// folding lines
folds.forEach((fold) => {
context.beginPath(),
(context.lineWidth = 0.5),
context.setLineDash([3, 4]),
(context.strokeStyle = "#888"),
path(fold),
context.stroke(),
context.setLineDash([]);
});

// restore the projection’s rotate
projection.rotate(rotate);
}

d3.select(context.canvas).on("pointermove", function (event) {
const gni = projection.invert(d3.pointer(event));

context.beginPath(),
path.pointRadius(2)({ type: "Point", coordinates: gni }),
(context.fillStyle = "green"),
(context.strokeStyle = "black"),
context.fill(),
context.stroke();
});

return context.canvas;
}
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