From 0bafcc33ca13f7e3a798cfca347ff95d62bf599c Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 14 Sep 2019 01:20:59 +0300 Subject: [PATCH] v1.0.32 --- index.css | 20 +++++- index.html | 1 + modules/ui/provinces-editor.js | 126 ++++++++++++++++++++++++++++++++- modules/ui/religions-editor.js | 15 ++-- 4 files changed, 152 insertions(+), 10 deletions(-) diff --git a/index.css b/index.css index 24783919..b5b95ea5 100644 --- a/index.css +++ b/index.css @@ -191,7 +191,7 @@ i.icon-lock { cursor: pointer; } -#religionInfo { +.chartInfo { text-align: center; font-family: sans-serif; font-style: italic; @@ -204,13 +204,24 @@ i.icon-lock { cursor: move; } -#religionHierarchy text { +#religionHierarchy text, +#provincesTree text { pointer-events: none; user-select: none; stroke: none; font-size: 11px; } +#provincesTree .selected { + stroke: #c13119; + stroke-width: 2; +} + +#provincesTree .selected { + stroke: #c13119; + stroke-width: 2; +} + .dragLine { marker-end: url(#end-arrow); stroke: #333333; @@ -1293,6 +1304,11 @@ div.states.selected { background-image: linear-gradient(to right, #f2f2f2 0%, #ebe7e7 50%, #E5DADB 100%); } +div.states.active { + border: 1px solid #c4c4c4; + background-image: linear-gradient(to right, #dedede 100%, #f2f2f2 50%, #fcfcfc 0%); +} + div.states.Self { border-color: #858b8e; background-image: linear-gradient(to right, #f2f2f2 0%, #b0c6d9 100%); diff --git a/index.html b/index.html index 85ac40e8..6e404474 100644 --- a/index.html +++ b/index.html @@ -2557,6 +2557,7 @@
+ diff --git a/modules/ui/provinces-editor.js b/modules/ui/provinces-editor.js index 61b1fc0f..1b0844da 100644 --- a/modules/ui/provinces-editor.js +++ b/modules/ui/provinces-editor.js @@ -22,6 +22,7 @@ function editProvinces() { document.getElementById("provincesEditorRefresh").addEventListener("click", refreshProvincesEditor); document.getElementById("provincesFilterState").addEventListener("change", provincesEditorAddLines); document.getElementById("provincesPercentage").addEventListener("click", togglePercentageMode); + document.getElementById("provincesChart").addEventListener("click", showChart); document.getElementById("provincesToggleLabels").addEventListener("click", toggleLabels); document.getElementById("provincesExport").addEventListener("click", downloadProvincesData); document.getElementById("provincesRemoveAll").addEventListener("click", removeAllProvinces); @@ -151,17 +152,25 @@ function editProvinces() { function provinceHighlightOn(event) { if (!customization) event.target.querySelectorAll(".hoverButton").forEach(el => el.classList.remove("placeholder")); + + const province = +event.target.dataset.id; + const el = body.querySelector(`div[data-id='${province}']`); + if (el) el.classList.add("active"); + if (!layerIsOn("toggleProvinces")) return; if (customization) return; - const province = +event.target.dataset.id; const animate = d3.transition().duration(2000).ease(d3.easeSinIn); provs.select("#province"+province).raise().transition(animate).attr("stroke-width", 2.5).attr("stroke", "#d0240f"); } function provinceHighlightOff(event) { if (!customization) event.target.querySelectorAll(".hoverButton").forEach(el => el.classList.add("placeholder")); - if (!layerIsOn("toggleProvinces")) return; + const province = +event.target.dataset.id; + const el = body.querySelector(`div[data-id='${province}']`); + if (el) el.classList.remove("active"); + + if (!layerIsOn("toggleProvinces")) return; provs.select("#province"+province).transition().attr("stroke-width", null).attr("stroke", null); } @@ -272,6 +281,119 @@ function editProvinces() { } } + function showChart() { + // build hierarchy tree + const states = pack.states.map(s => { + return {id:s.i, state: s.i?0:null, color: s.i && s.color[0] === "#" ? d3.color(s.color).darker() : "#666"} + }); + const provinces = pack.provinces.filter(p => p.i && !p.removed); + provinces.forEach(p => p.id = p.i + states.length - 1); + const data = states.concat(provinces); + const root = d3.stratify().parentId(d => d.state)(data).sum(d => d.area); + + const width = 800, height = 300; + const margin = {top: 10, right: 10, bottom: 0, left: 10}; + const w = width - margin.left - margin.right; + const h = height - margin.top - margin.bottom; + const treeLayout = d3.treemap().size([w, h]).padding(2); + + // prepare svg + alertMessage.innerHTML = ``; + alertMessage.innerHTML += `
`; + const svg = d3.select("#alertMessage").insert("svg", "#provinceInfo").attr("id", "provincesTree") + .attr("width", width).attr("height", height).attr("font-size", "10px"); + const graph = svg.append("g").attr("transform", `translate(10, 0)`); + document.getElementById("provincesTreeType").addEventListener("change", updateChart); + + treeLayout(root); + + const node = graph.selectAll("g").data(root.leaves()).enter() + .append("g").attr("data-id", d => d.data.i) + .on("mouseenter", d => showInfo(event, d)) + .on("mouseleave", d => hideInfo(event, d)); + + function showInfo(ev, d) { + d3.select(ev.target).select("rect").classed("selected", 1); + const name = d.data.fullName; + const state = pack.states[d.data.state].fullName; + + const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; + const area = d.data.area * (distanceScaleInput.value ** 2) + unit; + const rural = rn(d.data.rural * populationRate.value); + const urban = rn(d.data.urban * populationRate.value * urbanization.value); + + const value = provincesTreeType.value === "area" ? "Area: " + area + : provincesTreeType.value === "rural" ? "Rural population: " + si(rural) + : provincesTreeType.value === "urban" ? "Urban population: " + si(urban) + : "Population: " + si(rural + urban); + + provinceInfo.innerHTML = `${name}. ${state}. ${value}`; + provinceHighlightOn(ev); + } + + function hideInfo(ev) { + provinceHighlightOff(ev); + if (!document.getElementById("provinceInfo")) return; + provinceInfo.innerHTML = "‍"; + d3.select(ev.target).select("rect").classed("selected", 0); + } + + node.append("rect").attr("stroke", d => d.parent.data.color) + .attr("stroke-width", 1).attr("fill", d => d.data.color) + .attr("x", d => d.x0).attr("y", d => d.y0) + .attr("width", d => d.x1 - d.x0).attr("height", d => d.y1 - d.y0); + + node.append("text").attr("dx", ".2em").attr("dy", "1em") + .attr("x", d => d.x0).attr("y", d => d.y0); + + function hideNonfittingLabels() { + node.select("text").each(function(d) { + this.innerHTML = d.data.name; + let b = this.getBBox(); + if (b.y + b.height > d.y1 + 1) this.innerHTML = ""; + + while(b.width > 0 && b.x + b.width > d.x1) { + if (this.innerHTML.length < 3) {this.innerHTML = ""; break;} + this.innerHTML = this.innerHTML.slice(0, -2) + "…"; + b = this.getBBox(); + } + }) + + } + + function updateChart() { + const value = this.value === "area" ? d => d.area + : this.value === "rural" ? d => d.rural + : this.value === "urban" ? d => d.urban + : d => d.rural + d.urban; + + const newRoot = d3.stratify().parentId(d => d.state)(data).sum(value); + node.data(treeLayout(newRoot).leaves()) + + node.select("rect").transition().duration(1500) + .attr("x", d => d.x0).attr("y", d => d.y0) + .attr("width", d => d.x1 - d.x0).attr("height", d => d.y1 - d.y0); + + node.select("text").attr("opacity", 1).transition().duration(1500) + .attr("x", d => d.x0).attr("y", d => d.y0); + + setTimeout(hideNonfittingLabels, 2000); + } + + $("#alert").dialog({ + title: "Provinces chart", width: fitContent(), resizable: false, + position: {my: "left center", at: "left+10 center", of: "svg"}, buttons: {}, + close: () => {alertMessage.innerHTML = "";} + }); + + hideNonfittingLabels(); + } + function toggleLabels() { const hidden = provs.select("#provinceLabels").style("display") === "none"; provs.select("#provinceLabels").style("display", `${hidden ? "block" : "none"}`); diff --git a/modules/ui/religions-editor.js b/modules/ui/religions-editor.js index a51fb6e8..7037c462 100644 --- a/modules/ui/religions-editor.js +++ b/modules/ui/religions-editor.js @@ -154,6 +154,9 @@ function editReligions() { info.innerHTML = `${r.name}${type}${form}${population}${hint}`; } + const el = body.querySelector(`div[data-id='${religion}']`); + if (el) el.classList.add("active"); + if (!layerIsOn("toggleReligions")) return; if (customization) return; relig.select("#religion"+religion).raise().transition(animate).attr("stroke-width", 2.5).attr("stroke", "#c13119"); @@ -167,6 +170,10 @@ function editReligions() { d3.select("#religionHierarchy").select("g[data-id='"+religion+"'] > path").classed("selected", 0); info.innerHTML = "‍"; } + + const el = body.querySelector(`div[data-id='${religion}']`) + if (el) el.classList.remove("active"); + relig.select("#religion"+religion).transition().attr("stroke-width", null).attr("stroke", null); debug.select("#religionsCenter"+religion).transition().attr("r", 4).attr("stroke-width", 1.2).attr("stroke", null); } @@ -245,13 +252,9 @@ function editReligions() { .attr("cx", d => pack.cells.p[d.center][0]).attr("cy", d => pack.cells.p[d.center][1]) .on("mouseenter", d => { tip(d.name+ ". Drag to move the religion center", true); - const el = body.querySelector(`div[data-id='${d.i}']`); - if (el) el.classList.add("selected"); religionHighlightOn(event); }).on("mouseleave", d => { tip('', true); - const el = body.querySelector(`div[data-id='${d.i}']`) - if (el) el.classList.remove("selected"); religionHighlightOff(event); }).call(d3.drag().on("start", religionCenterDrag)); } @@ -293,7 +296,7 @@ function editReligions() { // build hierarchy tree pack.religions[0].origin = null; const religions = pack.religions.filter(r => !r.removed); - let root = d3.stratify().id(d => d.i).parentId(d => d.origin)(religions); + const root = d3.stratify().id(d => d.i).parentId(d => d.origin)(religions); const treeWidth = root.leaves().length; const treeHeight = root.height; const width = treeWidth * 40, height = treeHeight * 60; @@ -304,7 +307,7 @@ function editReligions() { const treeLayout = d3.tree().size([w, h]); // prepare svg - alertMessage.innerHTML = "
"; + alertMessage.innerHTML = "
"; const svg = d3.select("#alertMessage").insert("svg", "#religionInfo").attr("id", "religionHierarchy") .attr("width", width).attr("height", height).style("text-anchor", "middle"); const graph = svg.append("g").attr("transform", `translate(10, -45)`);