-
Notifications
You must be signed in to change notification settings - Fork 0
/
map.js
130 lines (119 loc) · 4.03 KB
/
map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
'use strict';
const minZoom = 2;
const maxZoom = 19;
const maxMomentsPerLevel = 25;
const momentsUrl = "zoom.json";
const map = L.map("map").setView([0, 0], 2);
const seed = (Math.random()*2**32)>>>0;
let moments = null;
let visibleMoments = new Map();
function sfc32(a, b, c, d) {
return function() {
a |= 0; b |= 0; c |= 0; d |= 0;
let t = (a + b | 0) + d | 0;
d = d + 1 | 0;
a = b ^ b >>> 9;
b = c + (c << 3) | 0;
c = c << 21 | c >>> 11;
c = c + t | 0;
return (t >>> 0) / 4294967296;
}
}
const getRandomMoment = function(rand, i, j, z) {
const key = `${i},${j},${z}`;
const value = moments[key];
if (value.hasOwnProperty("moments")) {
const choice = Math.floor(rand() * value["moments"].length);
return value["moments"][choice];
}
const total = value["counts"].reduce((a, b) => a + b, 0);
const r = rand() * total;
let sum = 0;
let choice = 0;
for (; choice < 4; choice++) {
sum += value["counts"][choice];
if (sum > r) {
break;
}
}
return getRandomMoment(rand, 2 * i + choice % 2, 2 * j + (choice >> 1), z + 1);
}
const getMoments = function(maxMoments, i, j, z) {
const key = `${i},${j},${z}`;
// no moments in chosen region
if (!moments.hasOwnProperty(key)) {
return [];
}
// fewer than maxMomentsPerLevel in region, return it
if (moments[key].hasOwnProperty("moments")) {
return moments[key]["moments"];
}
// chose maxMoments moments from subregions at random
const rand = sfc32(seed, i, j, z);
const chosenMoments = [];
for (let _k = 0; _k < maxMoments; _k++) {
chosenMoments.push(getRandomMoment(rand, i, j, z));
}
return chosenMoments;
}
const refresh = function (newMoments) {
for (const momentID of visibleMoments.keys()) {
if (!newMoments.has(momentID)) {
map.removeLayer(visibleMoments.get(momentID));
visibleMoments.delete(momentID);
}
}
for (const momentID of newMoments.keys()) {
if (!visibleMoments.has(momentID)) {
const moment = newMoments.get(momentID);
const newMarker = L.marker([moment.latitude, moment.longitude]).addTo(map);
newMarker._icon.classList.add("pinkmarker");
newMarker.bindPopup(moment["description"].replaceAll("\n", "<br>"));
visibleMoments.set(momentID, newMarker);
}
}
}
const reload = function () {
if (!moments) {
return;
}
const cz = map.getZoom();
const bounds = map.getBounds();
const minLon = Math.max(bounds.getWest(), -180);
const minLat = Math.max(bounds.getSouth(), -90);
const maxLon = Math.min(bounds.getEast(), 180);
const maxLat = Math.min(bounds.getNorth(), 90);
const newMoments = new Map();
for (let z = minZoom; z <= cz; z++) {
const minBoxLon = Math.floor((minLon + 180) / 360 * 2 ** z);
const minBoxLat = Math.floor((minLat + 90) / 360 * 2 ** z);
const maxBoxLon = Math.floor((maxLon + 180) / 360 * 2 ** z);
const maxBoxLat = Math.floor((maxLat + 90) / 360 * 2 ** z);
for (let i = minBoxLon; i <= maxBoxLon; i++) {
for (let j = minBoxLat; j <= maxBoxLat; j++) {
const chosenMoments = getMoments(maxMomentsPerLevel, i, j, z);
// console.log(z, i, j, chosenMoments.length);
for (const moment of chosenMoments) {
newMoments.set(moment["id"], moment);
}
}
}
}
refresh(newMoments);
}
const loadJson = async function (url) {
const response = await fetch(url);
if (response.ok) {
moments = await response.json();
reload();
}
}
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>",
maxZoom: 19,
minZoom: minZoom
}).addTo(map);
loadJson(momentsUrl);
map.on("zoomend", reload);
map.on("resize", reload);
map.on("moveend", reload);