async function getSvgIncremental(worldMapImage, colorTable, removeSelector) {
let cancellationToken = getCancellationToken();
invalidation.then(() => cancellationToken.cancel = true);
const tmpWidth = 480;
const tmpHeight = tmpWidth * worldMapImage.heightRatio;
const svgMap = svg`
<svg viewBox="0 0 ${tmpWidth} ${tmpHeight}" xmlns="http://www.w3.org/2000/svg">
<style>
.spinner { animation: kf .8s linear infinite; animation-delay:-.8s }
.spinner.dot2 { animation-delay: -.65s }
.spinner.dot3 { animation-delay: -.5s }
@keyframes kf {
93.75%, 100% { opacity:.2 }
}
</style>
<g id="loading" transform="translate(${tmpWidth/2 - 12} ${tmpHeight/2 - 12})">
<circle class="spinner dot1" cx="4" cy="12" r="3"/>
<circle class="spinner dot2" cx="12" cy="12" r="3"/>
<circle class="spinner dot3" cx="20" cy="12" r="3"/>
</g>
</svg>`;
const slicer = new TileSlicer(worldMapImage);
const minScale = getMinScale(slicer);
const maxScale = getMaxScale(slicer);
getLowres().then(getHires);
return svgMap;
async function getLowres() {
let newMap = await getSvgInner(minScale);
svgMap.setAttribute("viewBox", newMap.getAttribute("viewBox"));
svgMap.replaceChildren(...newMap.children);
await pauseIfNeeded();
}
async function getHires() {
if (maxScale == minScale || cancellationToken.cancel) return;
const newMap = await getSvgInner(maxScale);
const currentPaths = extractSvgPaths(svgMap);
const newPaths = extractSvgPaths(newMap);
mergeSvgPaths(currentPaths, newPaths);
await new Promise(r => requestAnimationFrame(r));
}
async function getSvgInner(scale) {
let newMap = await getSvg(worldMapImage, colorTable, scale, cancellationToken);
if (removeSelector) [...newMap.querySelectorAll(removeSelector)].forEach(e => e.remove());
await pauseIfNeeded();
return newMap;
}
function extractSvgPaths(svgMap) {
const paths = {};
for (let g of svgMap.querySelectorAll("g.layer")) {
let layerName = g.getAttribute("class").split(" ").filter(c => c != "layer").join(" ");
let prop = mapSpec.layers[layerName].prop;
let layerPaths = paths[layerName] = new Map();
for (let path of g.querySelectorAll("path")) {
let value = path.dataset[prop];
layerPaths.set(value, path);
}
}
return paths;
}
function mergeSvgPaths(currentPaths, newPaths) {
for (let [layerName, newLayerPaths] of Object.entries(newPaths)) {
const currentLayerPaths = currentPaths[layerName];
let currentGroup = svgMap.querySelector(`g.layer.${layerName}`);
for (let [key, newPath] of newLayerPaths) {
const currentPath = currentLayerPaths.get(key);
if (!currentPath) {
currentGroup.prepend(newPath);
}
else {
const newPathString = newPath.getAttribute("d");
currentPath.setAttribute("d", newPathString)
}
}
}
}
}