diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index d717792c..cde1b72c 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -989,151 +989,44 @@ function toggleStates(event) {
function drawStates() {
TIME && console.time("drawStates");
- regions.selectAll("path").remove();
+ const {cells, states} = pack;
- const {cells, vertices, features} = pack;
- const states = pack.states;
- const n = cells.i.length;
-
- const used = new Uint8Array(cells.i.length);
- const vArray = new Array(states.length); // store vertices array
- const body = new Array(states.length).fill(""); // path around each state
- const gap = new Array(states.length).fill(""); // path along water for each state to fill the gaps
- const halo = new Array(states.length).fill(""); // path around states, but not lakes
-
- const getStringPoint = v => vertices.p[v[0]].join(",");
-
- // define inner-state lakes to omit on border render
- const innerLakes = features.map(feature => {
- if (feature.type !== "lake") return false;
- if (!feature.shoreline) Lakes.getShoreline(feature);
-
- const states = feature.shoreline.map(i => cells.state[i]);
- return new Set(states).size > 1 ? false : true;
+ const renderHalo = shapeRendering.value === "geometricPrecision";
+ const paths = getVertexPaths({
+ getType: cellId => cells.state[cellId],
+ options: {fill: true, waterGap: true, halo: renderHalo}
});
- for (const i of cells.i) {
- if (!cells.state[i] || used[i]) continue;
- const state = cells.state[i];
+ const maxLength = states.length - 1;
+ const bodyPaths = new Array(maxLength);
+ const clipPaths = new Array(maxLength);
+ const haloPaths = new Array(maxLength);
- const onborder = cells.c[i].some(n => cells.state[n] !== state);
- if (!onborder) continue;
+ for (const [index, {fill, waterGap, halo}] of paths) {
+ const color = states[index].color;
- const borderWith = cells.c[i].map(c => cells.state[c]).find(n => n !== state);
- const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.state[i] === borderWith));
- const chain = connectVertices(vertex, state);
+ bodyPaths.push(
+ /* html */ ``
+ );
- const noInnerLakes = chain.filter(v => v[1] !== "innerLake");
- if (noInnerLakes.length < 3) continue;
-
- // get path around the state
- if (!vArray[state]) vArray[state] = [];
- const points = noInnerLakes.map(v => vertices.p[v[0]]);
- vArray[state].push(points);
- body[state] += "M" + points.join("L");
-
- // connect path for halo
- let discontinued = true;
- halo[state] += noInnerLakes
- .map(v => {
- if (v[1] === "border") {
- discontinued = true;
- return "";
- }
-
- const operation = discontinued ? "M" : "L";
- discontinued = false;
- return `${operation}${getStringPoint(v)}`;
- })
- .join("");
-
- // connect gaps between state and water into a single path
- discontinued = true;
- gap[state] += chain
- .map(v => {
- if (v[1] === "land") {
- discontinued = true;
- return "";
- }
-
- const operation = discontinued ? "M" : "L";
- discontinued = false;
- return `${operation}${getStringPoint(v)}`;
- })
- .join("");
- }
-
- // find state visual center
- vArray.forEach((ar, i) => {
- const sorted = ar.sort((a, b) => b.length - a.length); // sort by points number
- states[i].pole = polylabel(sorted, 1.0); // pole of inaccessibility
- });
-
- const bodyData = body.map((p, s) => [p.length > 10 ? p : null, s, states[s].color]).filter(d => d[0]);
- const gapData = gap.map((p, s) => [p.length > 10 ? p : null, s, states[s].color]).filter(d => d[0]);
-
- const bodyString = bodyData.map(d => ``).join("");
- const gapString = gapData.map(d => ``).join("");
- statesBody.html(bodyString + gapString);
-
- const isOptimized = shapeRendering.value === "optimizeSpeed";
- if (!isOptimized) {
- const haloData = halo.map((p, s) => [p.length > 10 ? p : null, s, states[s].color]).filter(d => d[0]);
-
- const haloString = haloData
- .map(d => {
- const stroke = d3.color(d[2]) ? d3.color(d[2]).darker().hex() : "#666666";
- return ``;
- })
- .join("");
- statesHalo.html(haloString);
-
- const clipString = bodyData
- .map(d => ``)
- .join("");
- defs.select("#statePaths").html(clipString);
- }
-
- function connectVertices(start, state) {
- const chain = []; // vertices chain to form a path
- const getType = c => {
- const borderCell = c.find(i => cells.b[i]);
- if (borderCell) return "border";
-
- const waterCell = c.find(i => cells.h[i] < 20);
- if (!waterCell) return "land";
- if (innerLakes[cells.f[waterCell]]) return "innerLake";
- return features[cells.f[waterCell]].type;
- };
-
- for (let i = 0, current = start; i === 0 || (current !== start && i < 20000); i++) {
- const prev = chain.length ? chain[chain.length - 1][0] : -1; // previous vertex in chain
-
- const c = vertices.c[current]; // cells adjacent to vertex
- chain.push([current, getType(c)]); // add current vertex to sequence
-
- c.filter(c => cells.state[c] === state).forEach(c => (used[c] = 1));
- const c0 = c[0] >= n || cells.state[c[0]] !== state;
- const c1 = c[1] >= n || cells.state[c[1]] !== state;
- const c2 = c[2] >= n || cells.state[c[2]] !== state;
-
- const v = vertices.v[current]; // neighboring vertices
-
- if (v[0] !== prev && c0 !== c1) current = v[0];
- else if (v[1] !== prev && c1 !== c2) current = v[1];
- else if (v[2] !== prev && c0 !== c2) current = v[2];
-
- if (current === prev) {
- ERROR && console.error("Next vertex is not found");
- break;
- }
+ if (renderHalo) {
+ const haloColor = d3.color(color)?.darker().hex() || "#666666";
+ clipPaths.push(/* html */ ``);
+ haloPaths.push(
+ /* html */ ``
+ );
}
-
- if (chain.length) chain.push(chain[0]);
- return chain;
}
- invokeActiveZooming();
+ byId("statesBody").innerHTML = bodyPaths.join("");
+ byId("statePaths").innerHTML = renderHalo ? clipPaths.join("") : "";
+ byId("statesHalo").innerHTML = renderHalo ? haloPaths.join("") : "";
+
+ // vArray.forEach((ar, i) => {
+ // const sorted = ar.sort((a, b) => b.length - a.length); // sort by points number
+ // states[i].pole = polylabel(sorted, 1.0); // pole of inaccessibility
+ // });
+
TIME && console.timeEnd("drawStates");
}
diff --git a/utils/pathUtils.js b/utils/pathUtils.js
index e148270b..df617e05 100644
--- a/utils/pathUtils.js
+++ b/utils/pathUtils.js
@@ -65,7 +65,7 @@ function getVertexPaths({getType, options}) {
function isLandVertex(vertex) {
const adjacentCells = vertices.c[vertex];
- return adjacentCells.every(i => cells.h[i] >= MIN_LAND_HEIGHT);
+ return adjacentCells.every(i => cells.h[i] >= 20);
}
function addPath(index, vertexChain) {