website/static/workers/scatterplot-data-decoder.js (74 lines of code) (raw):
importScripts('./util.js');
const FLUSH_LIMIT = 100000;
const COORDINATE_PRECISION = 7;
let sequence;
let result = [];
let count = 0;
onmessage = function(e) {
const lines = e.data.text.split('\n');
lines.forEach(function(l, i) {
if (!l.length) {
return;
}
if (!sequence) {
sequence = decodeSequence(l);
return;
}
const bbox = decodeBbox(l.slice(0, 20));
const bitmap = decodeBitmap(l.slice(20));
for (var i = 0; i < bitmap.length; i++) {
if (bitmap[i] > 0) {
const point = [
bbox[0] + (bbox[2] - bbox[0]) * sequence[i * 2],
bbox[1] + (bbox[3] - bbox[1]) * sequence[i * 2 + 1],
Number(bitmap[i])
];
result.push(point);
count++;
}
}
if (result.length >= FLUSH_LIMIT) {
flush();
}
});
if (e.data.event === 'load') {
flush();
postMessage({action: 'end'});
}
};
function flush() {
postMessage({
action: 'add',
data: result,
meta: {points: count}
});
result = [];
}
function decodeSequence(str) {
const seq = [];
const tokens = str.split(/([A-Z])/).map(function(v) {
return parseInt(v, 36);
});
for (let i = 0; i < tokens.length - 1; i += 2) {
seq.push(tokens[i] / Math.pow(2, tokens[i + 1] - 10));
}
return seq;
}
function decodeBbox(str) {
const multiplyer = Math.pow(10, COORDINATE_PRECISION);
return decodeNumberArr(str, 90, 32, 5).map(function(x) {
return x / multiplyer - 180;
});
}
function decodeBitmap(str) {
const chunkSize = 4;
let match = '';
for (let i = 0; i < str.length; i++) {
let seg = (str.charCodeAt(i) - 32).toString(3);
while (seg.length < chunkSize) {
seg = `0${ seg}`;
}
match += seg;
}
return match;
}