!function () { function o(t, e, s, i) { this.max_objects = e || 10, this.max_levels = s || 4, this.level = i || 0, this.bounds = t, this.objects = [], this.nodes = [] } o.prototype.split = function () { var t = this.level + 1, e = this.bounds.width / 2, s = this.bounds.height / 2, i = this.bounds.x, h = this.bounds.y; this.nodes[0] = new o({ x: i + e, y: h, width: e, height: s }, this.max_objects, this.max_levels, t), this.nodes[1] = new o({ x: i, y: h, width: e, height: s }, this.max_objects, this.max_levels, t), this.nodes[2] = new o({ x: i, y: h + s, width: e, height: s }, this.max_objects, this.max_levels, t), this.nodes[3] = new o({ x: i + e, y: h + s, width: e, height: s }, this.max_objects, this.max_levels, t) }, o.prototype.getIndex = function (t) { var e = [], s = this.bounds.x + this.bounds.width / 2, i = this.bounds.y + this.bounds.height / 2, h = t.y < i, o = t.x < s, n = t.x + t.width > s, d = t.y + t.height > i; return h && n && e.push(0), o && h && e.push(1), o && d && e.push(2), n && d && e.push(3), e }, o.prototype.insert = function (t) { var e, s = 0; if (this.nodes.length) for (e = this.getIndex(t), s = 0; s < e.length; s++)this.nodes[e[s]].insert(t); else if (this.objects.push(t), this.objects.length > this.max_objects && this.level < this.max_levels) { for (this.nodes.length || this.split(), s = 0; s < this.objects.length; s++) { e = this.getIndex(this.objects[s]); for (var i = 0; i < e.length; i++)this.nodes[e[i]].insert(this.objects[s]) } this.objects = [] } }, o.prototype.retrieve = function (t) { var e = this.getIndex(t), s = this.objects; if (this.nodes.length) for (var i = 0; i < e.length; i++)s = s.concat(this.nodes[e[i]].retrieve(t)); return s = s.filter(function (t, e) { return s.indexOf(t) >= e }) }, o.prototype.clear = function () { this.objects = []; for (var t = 0; t < this.nodes.length; t++)this.nodes.length && this.nodes[t].clear(); this.nodes = [] }, "undefined" != typeof module && void 0 !== module.exports ? module.exports = o : window.Quadtree = o }();
function plotMap() {
let dots = [];
const mapCanvas = $("#map-canvas");
const params = {
height: mapCanvas.getBoundingClientRect().height,
width: mapCanvas.getBoundingClientRect().width,
dia: Math.min(12, 0.01667 * mapCanvas.getBoundingClientRect().width),
strength: 0.1
};
let tree = new Quadtree({
x: 0,
y: 0,
width: params.width,
height: params.height
});
function tooClose(a, b, tolerance) {
px = Math.abs(a.x - b.x);
py = Math.abs(a.y - b.y);
pd = params.dia * tolerance;
return px < pd && py < pd;
}
function clamp(lowest, input, highest) {
return max(min(input, highest), lowest);
}
function isBetween(lowest, input, highest) {
return input > lowest && input < highest;
}
function equalEarth(x, y) {
var A1 = 1.340264,
A2 = -0.081106,
A3 = 0.000893,
A4 = 0.003796,
M = sqrt(3) / 2;
wrapX = x < -168.75 ? 180 + 168.75 - x : x - 11.25;
x_ = (wrapX * π) / 180;
y_ = (y * π) / 180;
var l = asin(M * sin(y_)),
l2 = l * l,
l6 = l2 * l2 * l2;
return [
0.5 +
(x_ * (0.5 / 2.7066299836960748) * cos(l)) /
(M * (A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2))),
0.5 -
l *
(0.5 / 1.3173627591574133) *
(A1 + A2 * l2 + l6 * (A3 + A4 * l2))
];
}
function ukpc(x, y) {
return [((x + 6) * (332 / 1802)) / 8.5, 0.5 + ((y - 57) * 0.5) / -7.5];
}
for (const tr of $$("tbody tr")) {
if (tr != $(`tr[data-artist="${tr.dataset.artist}"]`)) {
continue;
}
const album = JSON.parse(tr.dataset.info);
proj = equalEarth(album.lon, album.lat);
if (
isBetween(-6, album.lon, 2.5) &&
isBetween(49.5, album.lat, 57) &&
album.country != "fr" &&
album.country != "gb/nir"
) {
proj = ukpc(album.lon, album.lat);
}
tx = params.width * proj[0];
ty = params.height * proj[1];
dots.push({
sprite: album.sprite,
artist: album.artist,
alt: `${album.artist} - ${album.title} (${album.city})`,
x: tx,
y: ty,
vx: 0,
vy: 0,
height: params.dia,
width: params.dia
});
}
function loop() {
tree.clear();
for (let d of dots) {
d.collides = false;
d.x = clamp(0, d.x + d.vx, params.width);
d.y = clamp(0, d.y + d.vy, params.height);
d.vx = 0;
d.vy = 0;
tree.insert(d);
}
newMap = "";
for (let a of dots) {
let candidates = tree.retrieve(a);
const aIndex = dots.indexOf(a);
for (let b of candidates) {
if (aIndex >= dots.indexOf(b)) continue;
const tct = a.artist == b.artist ? 0.95 : 1.17;
if (tooClose(a, b, tct)) {
a.collides = true;
b.collides = true;
const px = a.x - b.x || random() * 1e-6;
const py = a.y - b.y || random() * 1e-6;
const δ =
(params.strength * params.dia) /
sqrt(px * px + py * py);
a.vx += δ * px;
b.vx -= δ * px;
a.vy += δ * py;
b.vy -= δ * py;
}
}
newMap += `<div class="dot album-cover" alt="${a.alt}" title="${a.alt}" style="--x: ${a.sprite.x}; --y: ${a.sprite.y}; top: ${a.y}px; left: ${a.x}px;"></div>`;
}
mapCanvas.innerHTML = newMap;
}
for (i = 0; i < 25 || (i > 0 && dots.some(d => d.collides == true)); i++) {
loop();
}
}
document.on("DOMContentLoaded", plotMap);