diff --git a/public/index.css b/public/index.css
index d2061efd9a..85f9087f61 100644
--- a/public/index.css
+++ b/public/index.css
@@ -122,10 +122,9 @@ a {
user-select: none;
}
-#population,
-#cells,
-#compass {
- fill: none;
+#cells,
+#compass {
+ fill: none;
}
#landmass {
@@ -175,12 +174,13 @@ a {
}
t,
-#regions,
-#cults,
-#relig,
-#biomes,
-#provincesBody,
-#terrs,
+#regions,
+#cults,
+#relig,
+#biomes,
+#population,
+#provincesBody,
+#terrs,
#tooltip,
#temperature,
#texture,
@@ -213,21 +213,28 @@ t,
stroke-linejoin: round;
}
-#statesBody,
-#provincesBody,
-#relig,
-#biomes,
-#cults {
- stroke-linejoin: round;
- fill-rule: evenodd;
-}
-
-#statesBody,
-#provincesBody,
-#relig,
-#cults {
- mask: url(#land);
-}
+#statesBody,
+#provincesBody,
+#relig,
+#biomes,
+#population,
+#cults {
+ stroke-linejoin: round;
+ fill-rule: evenodd;
+}
+
+#population,
+#population path {
+ stroke-dasharray: none !important;
+}
+
+#statesBody,
+#provincesBody,
+#relig,
+#population,
+#cults {
+ mask: url(#land);
+}
#borders {
stroke-linejoin: round;
diff --git a/public/main.js b/public/main.js
index ff5a68ab5d..7a6ca3816c 100644
--- a/public/main.js
+++ b/public/main.js
@@ -45,6 +45,7 @@ let texture = viewbox.append("g").attr("id", "texture");
let terrs = viewbox.append("g").attr("id", "terrs");
let lakes = viewbox.append("g").attr("id", "lakes");
let biomes = viewbox.append("g").attr("id", "biomes");
+let population = viewbox.append("g").attr("id", "population");
let cells = viewbox.append("g").attr("id", "cells");
let gridOverlay = viewbox.append("g").attr("id", "gridOverlay");
let coordinates = viewbox.append("g").attr("id", "coordinates");
@@ -72,7 +73,6 @@ let goods = viewbox.append("g").attr("id", "goods").style("display", "none");
let markets = viewbox.append("g").attr("id", "markets");
let tradeAnimation = viewbox.append("g").attr("id", "tradeAnimation");
let prec = viewbox.append("g").attr("id", "prec").style("display", "none");
-let population = viewbox.append("g").attr("id", "population");
let emblems = viewbox.append("g").attr("id", "emblems").style("display", "none");
let icons = viewbox.append("g").attr("id", "icons");
let labels = viewbox.append("g").attr("id", "labels");
diff --git a/public/modules/io/load.js b/public/modules/io/load.js
index 80c0697d8c..d7e9fa0179 100644
--- a/public/modules/io/load.js
+++ b/public/modules/io/load.js
@@ -464,7 +464,10 @@ async function parseLoadedData(data, mapVersion) {
if (isVisible(borders) && hasChild(borders, "path")) turnOn("toggleBorders");
if (isVisible(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
if (hasChildren(temperature)) turnOn("toggleTemperature");
- if (hasChild(population, "line")) turnOn("togglePopulation");
+ if (hasChild(population, "path, line")) {
+ turnOn("togglePopulation");
+ drawPopulation();
+ }
if (isVisible(ice)) turnOn("toggleIce");
if (hasChild(prec, "circle")) turnOn("togglePrecipitation");
if (isVisible(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
diff --git a/public/modules/ui/layers.js b/public/modules/ui/layers.js
index e4071ea1ef..54747763f1 100644
--- a/public/modules/ui/layers.js
+++ b/public/modules/ui/layers.js
@@ -359,76 +359,110 @@ function drawPrecipitation() {
}
function togglePopulation(event) {
- if (!population.selectAll("line").size()) {
+ if (!population.selectAll("path, line").size()) {
turnButtonOn("togglePopulation");
drawPopulation();
if (event && isCtrlClick(event)) editStyle("population");
} else {
if (event && isCtrlClick(event)) return editStyle("population");
turnButtonOff("togglePopulation");
-
- const isD3data = population.select("line").datum();
- if (!isD3data) {
- // just remove
- population.selectAll("line").remove();
- } else {
- // remove with animation
- const hide = d3.transition().duration(1000).ease(d3.easeSinIn);
- population
- .select("#rural")
- .selectAll("line")
- .transition(hide)
- .attr("y2", d => d[1])
- .remove();
- population
- .select("#urban")
- .selectAll("line")
- .transition(hide)
- .delay(1000)
- .attr("y2", d => d[1])
- .remove();
- }
+ population.selectAll("*").remove();
}
}
function drawPopulation() {
- population.selectAll("line").remove();
+ TIME && console.time("drawPopulation");
+
+ biomes.node().after(population.node());
+
+ population
+ .attr("mask", "url(#land)")
+ .attr("stroke", null)
+ .attr("stroke-width", null)
+ .attr("stroke-dasharray", null)
+ .attr("stroke-linecap", null)
+ .style("stroke", null)
+ .style("stroke-width", null)
+ .style("stroke-dasharray", null);
const { cells, burgs } = pack;
- const show = d3.transition().duration(2000).ease(d3.easeSinIn);
+ const urban = new Float64Array(cells.i.length);
+ const total = new Float64Array(cells.i.length);
+ let maxPopulation = 0;
+
+ for (const cellId of cells.i) {
+ if (cells.h[cellId] < 20) continue;
+
+ const burg = burgs[cells.burg[cellId]];
+ const ruralPopulation = cells.pop[cellId] * populationRate;
+ urban[cellId] =
+ burg && !burg.removed ? (burg.population || 0) * populationRate * urbanization : 0;
+ total[cellId] = ruralPopulation + urban[cellId];
+ if (total[cellId] > maxPopulation) maxPopulation = total[cellId];
+ }
- const rural = Array.from(
- cells.i.filter(i => cells.pop[i] > 0),
- i => [...cells.p[i], cells.p[i][1] - cells.pop[i] / 5]
- );
+ const colors = new Array(cells.i.length);
+ for (const cellId of cells.i) {
+ if (cells.h[cellId] < 20) continue;
+ colors[cellId] = getPopulationColor(urban[cellId], total[cellId], maxPopulation);
+ }
- population
- .select("#rural")
- .selectAll("line")
- .data(rural)
- .enter()
- .append("line")
- .attr("x1", d => d[0])
- .attr("y1", d => d[1])
- .attr("x2", d => d[0])
- .attr("y2", d => d[1])
- .transition(show)
- .attr("y2", d => d[2]);
+ const isolines = getIsolines(pack, cellId => colors[cellId] || null, { fill: true, waterGap: true });
+ const populationShapes = Object.entries(isolines).map(([color, { fill, waterGap }], index) => {
+ const closedFill = closeSvgPathRings(fill);
+ const coastalClipId = `population-coast-clip${index}`;
+ const outsideFillClip = /* html */ `