Scale bar styling (#1025)

* feat: style scale bar

* feat: style scale bar - style presets

---------

Co-authored-by: Azgaar <azgaar.fmg@yandex.com>
This commit is contained in:
Azgaar 2023-12-09 20:01:52 +04:00 committed by GitHub
parent 52e3088763
commit 83dff665c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 543 additions and 246 deletions

View file

@ -736,4 +736,38 @@ export function resolveVersionConflicts(version) {
.style("display", "none");
vignette.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
}
if (version < 1.96) {
// v1.96.00 moved scaleBar options from units editor to style
d3.select("#scaleBar").remove();
scaleBar = svg
.insert("g", "#viewbox + *")
.attr("id", "scaleBar")
.attr("opacity", 1)
.attr("fill", "#353540")
.attr("font-size", 10)
.attr("data-size", 2)
.attr("data-x", 99)
.attr("data-y", 99)
.attr("data-label", "");
scaleBar
.append("rect")
.attr("id", "scaleBarBack")
.attr("opacity", 0.2)
.attr("fill", "#ffffff")
.attr("stroke", "#000000")
.attr("stroke-width", 1)
.attr("filter", "url(#blur5)")
.attr("data-top", 20)
.attr("data-right", 15)
.attr("data-bottom", 15)
.attr("data-left", 10);
drawScaleBar(scaleBar, scale);
fitScaleBar(scaleBar, svgWidth, svgHeight);
if (!layerIsOn("toggleScaleBar")) scaleBar.style("display", "none");
}
}

View file

@ -88,12 +88,6 @@ function getSettings() {
heightUnit: heightUnit.value,
heightExponent: heightExponentInput.value,
temperatureScale: temperatureScale.value,
barSize: barSizeInput.value,
barLabel: barLabel.value,
barBackOpacity: barBackOpacity.value,
barBackColor: barBackColor.value,
barPosX: barPosX.value,
barPosY: barPosY.value,
populationRate: populationRate,
urbanization: urbanization,
mapSize: mapSizeOutput.value,

View file

@ -218,6 +218,7 @@ async function parseLoadedData(data) {
INFO && console.group("Loaded Map " + seed);
// TODO: move all to options object
void (function parseSettings() {
const settings = data[1].split("|");
if (settings[0]) applyOption(distanceUnitInput, settings[0]);
@ -226,23 +227,16 @@ async function parseLoadedData(data) {
if (settings[3]) applyOption(heightUnit, settings[3]);
if (settings[4]) heightExponentInput.value = heightExponentOutput.value = settings[4];
if (settings[5]) temperatureScale.value = settings[5];
if (settings[6]) barSizeInput.value = barSizeOutput.value = settings[6];
if (settings[7] !== undefined) barLabel.value = settings[7];
if (settings[8] !== undefined) barBackOpacity.value = settings[8];
if (settings[9]) barBackColor.value = settings[9];
if (settings[10]) barPosX.value = settings[10];
if (settings[11]) barPosY.value = settings[11];
// setting 6-11 (scaleBar) are part of style now, kept as "" in newer versions for compatibility
if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12];
if (settings[13]) urbanization = urbanizationInput.value = urbanizationOutput.value = settings[13];
if (settings[14]) mapSizeInput.value = mapSizeOutput.value = minmax(settings[14], 1, 100);
if (settings[15]) latitudeInput.value = latitudeOutput.value = minmax(settings[15], 0, 100);
if (settings[18]) precInput.value = precOutput.value = settings[18];
if (settings[19]) options = JSON.parse(settings[19]);
// setting 16 and 17 (temperature) are part of options now, kept as "" in newer versions for compatibility
if (settings[16]) options.temperatureEquator = +settings[16];
if (settings[17]) options.temperatureNorthPole = options.temperatureSouthPole = +settings[17];
if (settings[20]) mapName.value = settings[20];
if (settings[21]) hideLabels.checked = +settings[21];
if (settings[22]) stylePreset.value = settings[22];
@ -453,7 +447,6 @@ async function parseLoadedData(data) {
})();
void (function restoreEvents() {
scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", () => editUnits());
legend
.on("mousemove", () => tip("Drag to change the position. Click to hide the legend"))
.on("click", () => clearLegend());

View file

@ -49,12 +49,12 @@ function prepareMapData() {
heightUnit.value,
heightExponentInput.value,
temperatureScale.value,
barSizeInput.value,
barLabel.value,
barBackOpacity.value,
barBackColor.value,
barPosX.value,
barPosY.value,
"", // previously used for barSize.value
"", // previously used for barLabel.value
"", // previously used for barBackColor.value
"", // previously used for barBackColor.value
"", // previously used for barPosX.value
"", // previously used for barPosY.value
populationRate,
urbanization,
mapSizeOutput.value,

View file

@ -1670,10 +1670,7 @@ function toggleLabels(event) {
invokeActiveZooming();
if (event && isCtrlClick(event)) editStyle("labels");
} else {
if (event && isCtrlClick(event)) {
editStyle("labels");
return;
}
if (event && isCtrlClick(event)) return editStyle("labels");
turnButtonOff("toggleLabels");
labels.style("display", "none");
}
@ -1685,10 +1682,7 @@ function toggleIcons(event) {
$("#icons").fadeIn();
if (event && isCtrlClick(event)) editStyle("burgIcons");
} else {
if (event && isCtrlClick(event)) {
editStyle("burgIcons");
return;
}
if (event && isCtrlClick(event)) return editStyle("burgIcons");
turnButtonOff("toggleIcons");
$("#icons").fadeOut();
}
@ -1701,10 +1695,7 @@ function toggleRulers(event) {
rulers.draw();
ruler.style("display", null);
} else {
if (event && isCtrlClick(event)) {
editStyle("ruler");
return;
}
if (event && isCtrlClick(event)) return editStyle("ruler");
turnButtonOff("toggleRulers");
ruler.selectAll("*").remove();
ruler.style("display", "none");
@ -1715,17 +1706,113 @@ function toggleScaleBar(event) {
if (!layerIsOn("toggleScaleBar")) {
turnButtonOn("toggleScaleBar");
$("#scaleBar").fadeIn();
if (event && isCtrlClick(event)) editUnits();
if (event && isCtrlClick(event)) editStyle("scaleBar");
} else {
if (event && isCtrlClick(event)) {
editUnits();
return;
}
if (event && isCtrlClick(event)) return editStyle("scaleBar");
$("#scaleBar").fadeOut();
turnButtonOff("toggleScaleBar");
}
}
function drawScaleBar(scaleBar, scaleLevel) {
if (!scaleBar.size() || scaleBar.style("display") === "none") return;
const distanceScale = +distanceScaleInput.value;
const unit = distanceUnitInput.value;
const size = +scaleBar.attr("data-bar-size");
const length = (function () {
const init = 100;
let val = (init * size * distanceScale) / scaleLevel; // bar length in distance unit
if (val > 900) val = rn(val, -3); // round to 1000
else if (val > 90) val = rn(val, -2); // round to 100
else if (val > 9) val = rn(val, -1); // round to 10
else val = rn(val); // round to 1
const length = (val * scaleLevel) / distanceScale; // actual length in pixels on this scale
return length;
})();
scaleBar.select("#scaleBarContent").remove(); // redraw content every time
const content = scaleBar.append("g").attr("id", "scaleBarContent");
const lines = content.append("g");
lines
.append("line")
.attr("x1", 0.5)
.attr("y1", 0)
.attr("x2", length + size - 0.5)
.attr("y2", 0)
.attr("stroke-width", size)
.attr("stroke", "white");
lines
.append("line")
.attr("x1", 0)
.attr("y1", size)
.attr("x2", length + size)
.attr("y2", size)
.attr("stroke-width", size)
.attr("stroke", "#3d3d3d");
lines
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", length + size)
.attr("y2", 0)
.attr("stroke-width", rn(size * 3, 2))
.attr("stroke-dasharray", size + " " + rn(length / 5 - size, 2))
.attr("stroke", "#3d3d3d");
const texts = content.append("g").attr("text-anchor", "middle").attr("font-family", "var(--serif)");
texts
.selectAll("text")
.data(d3.range(0, 6))
.enter()
.append("text")
.attr("x", d => rn((d * length) / 5, 2))
.attr("y", 0)
.attr("dy", "-.6em")
.text(d => rn((((d * length) / 5) * distanceScale) / scaleLevel) + (d < 5 ? "" : " " + unit));
const label = scaleBar.attr("data-label");
if (label) {
texts
.append("text")
.attr("x", (length + 1) / 2)
.attr("dy", ".6em")
.attr("dominant-baseline", "text-before-edge")
.text(label);
}
const scaleBarBack = scaleBar.select("#scaleBarBack");
if (scaleBarBack.size()) {
const bbox = content.node().getBBox();
const paddingTop = +scaleBarBack.attr("data-top") || 0;
const paddingLeft = +scaleBarBack.attr("data-left") || 0;
const paddingRight = +scaleBarBack.attr("data-right") || 0;
const paddingBottom = +scaleBarBack.attr("data-bottom") || 0;
scaleBar
.select("#scaleBarBack")
.attr("x", -paddingLeft)
.attr("y", -paddingTop)
.attr("width", bbox.width + paddingRight)
.attr("height", bbox.height + paddingBottom);
}
}
// fit ScaleBar to screen size
function fitScaleBar(scaleBar, fullWidth, fullHeight) {
if (!scaleBar.select("rect").size() || scaleBar.style("display") === "none") return;
const posX = +scaleBar.attr("data-x") || 99;
const posY = +scaleBar.attr("data-y") || 99;
const bbox = scaleBar.select("rect").node().getBBox();
const x = rn((fullWidth * posX) / 100 - bbox.width + 10);
const y = rn((fullHeight * posY) / 100 - bbox.height + 20);
scaleBar.attr("transform", `translate(${x},${y})`);
}
function toggleZones(event) {
if (!layerIsOn("toggleZones")) {
turnButtonOn("toggleZones");
@ -1748,10 +1835,7 @@ function toggleEmblems(event) {
$("#emblems").fadeIn();
if (event && isCtrlClick(event)) editStyle("emblems");
} else {
if (event && isCtrlClick(event)) {
editStyle("emblems");
return;
}
if (event && isCtrlClick(event)) return editStyle("emblems");
$("#emblems").fadeOut();
turnButtonOff("toggleEmblems");
}

View file

@ -532,101 +532,3 @@ class Planimeter extends Measurer {
this.el.select("text").attr("x", c[0]).attr("y", c[1]).text(area);
}
}
// Scale bar
function drawScaleBar(scaleBar, scaleLevel) {
if (!scaleBar.size() || scaleBar.style("display") === "none") return;
scaleBar.selectAll("*").remove(); // fully redraw every time
const distanceScale = +distanceScaleInput.value;
const unit = distanceUnitInput.value;
const size = +barSizeInput.value;
// calculate size
const init = 100;
let val = (init * size * distanceScale) / scaleLevel; // bar length in distance unit
if (val > 900) val = rn(val, -3);
// round to 1000
else if (val > 90) val = rn(val, -2);
// round to 100
else if (val > 9) val = rn(val, -1);
// round to 10
else val = rn(val); // round to 1
const length = (val * scaleLevel) / distanceScale; // actual length in pixels on this scale
scaleBar
.append("line")
.attr("x1", 0.5)
.attr("y1", 0)
.attr("x2", length + size - 0.5)
.attr("y2", 0)
.attr("stroke-width", size)
.attr("stroke", "white");
scaleBar
.append("line")
.attr("x1", 0)
.attr("y1", size)
.attr("x2", length + size)
.attr("y2", size)
.attr("stroke-width", size)
.attr("stroke", "#3d3d3d");
const dash = size + " " + rn(length / 5 - size, 2);
scaleBar
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", length + size)
.attr("y2", 0)
.attr("stroke-width", rn(size * 3, 2))
.attr("stroke-dasharray", dash)
.attr("stroke", "#3d3d3d");
const fontSize = rn(5 * size, 1);
scaleBar
.selectAll("text")
.data(d3.range(0, 6))
.enter()
.append("text")
.attr("x", d => rn((d * length) / 5, 2))
.attr("y", 0)
.attr("dy", "-.6em")
.attr("font-size", fontSize)
.text(d => rn((((d * length) / 5) * distanceScale) / scaleLevel) + (d < 5 ? "" : " " + unit));
if (barLabel.value !== "") {
scaleBar
.append("text")
.attr("x", (length + 1) / 2)
.attr("y", 2 * size)
.attr("dominant-baseline", "text-before-edge")
.attr("font-size", fontSize)
.text(barLabel.value);
}
const bbox = scaleBar.node().getBBox();
// append backbround rectangle
scaleBar
.insert("rect", ":first-child")
.attr("x", -10)
.attr("y", -20)
.attr("width", bbox.width + 10)
.attr("height", bbox.height + 15)
.attr("stroke-width", size)
.attr("stroke", "none")
.attr("filter", "url(#blur5)")
.attr("fill", barBackColor.value)
.attr("opacity", +barBackOpacity.value);
}
// fit ScaleBar to screen size
function fitScaleBar(scaleBar, fullWidth, fullHeight) {
if (!scaleBar.select("rect").size() || scaleBar.style("display") === "none") return;
const px = isNaN(+barPosX.value) ? 0.99 : barPosX.value / 100;
const py = isNaN(+barPosY.value) ? 0.99 : barPosY.value / 100;
const bbox = scaleBar.select("rect").node().getBBox();
const x = rn(fullWidth * px - bbox.width + 10);
const y = rn(fullHeight * py - bbox.height + 20);
scaleBar.attr("transform", `translate(${x},${y})`);
}

View file

@ -774,7 +774,7 @@ function showExportPane() {
}
async function exportToJson(type) {
const {exportToJson} = await import("../dynamic/export-json.js?v=1.93.03");
const {exportToJson} = await import("../dynamic/export-json.js?v=1.96.00");
exportToJson(type);
}

View file

@ -14,6 +14,7 @@
byId("styleFilterInput").innerHTML = allOptions;
byId("styleStatesBodyFilter").innerHTML = allOptions;
byId("styleScaleBarBackgroundFilter").innerHTML = allOptions;
}
// store some style inputs as options
@ -94,13 +95,13 @@ function selectStyleElement() {
}
// filter
if (!["landmass", "legend", "regions"].includes(styleElement)) {
if (!["landmass", "legend", "regions", "scaleBar"].includes(styleElement)) {
styleFilter.style.display = "block";
styleFilterInput.value = el.attr("filter") || "";
}
// fill
if (["rivers", "lakes", "landmass", "prec", "ice", "fogging", "vignette"].includes(styleElement)) {
if (["rivers", "lakes", "landmass", "prec", "ice", "fogging", "scaleBar", "vignette"].includes(styleElement)) {
styleFill.style.display = "block";
styleFillInput.value = styleFillOutput.value = el.attr("fill");
}
@ -356,6 +357,31 @@ function selectStyleElement() {
if (auto) styleFilter.style.display = "none";
}
if (styleElement === "scaleBar") {
styleScaleBar.style.display = "block";
styleScaleBarSize.value = el.attr("data-bar-size");
styleScaleBarFontSize.value = el.attr("font-size");
styleScaleBarPositionX.value = el.attr("data-x") || "99";
styleScaleBarPositionY.value = el.attr("data-y") || "99";
styleScaleBarLabel.value = el.attr("data-label") || "";
const scaleBarBack = el.select("#scaleBarBack");
if (scaleBarBack.size()) {
styleScaleBarBackgroundOpacityInput.value = styleScaleBarBackgroundOpacityOutput.value =
scaleBarBack.attr("opacity");
styleScaleBarBackgroundFillInput.value = styleScaleBarBackgroundFillOutput.value = scaleBarBack.attr("fill");
styleScaleBarBackgroundStrokeInput.value = styleScaleBarBackgroundStrokeOutput.value =
scaleBarBack.attr("stroke");
styleScaleBarBackgroundStrokeWidth.value = scaleBarBack.attr("stroke-width");
styleScaleBarBackgroundFilter.value = scaleBarBack.attr("filter");
styleScaleBarBackgroundPaddingTop.value = scaleBarBack.attr("data-top");
styleScaleBarBackgroundPaddingRight.value = scaleBarBack.attr("data-right");
styleScaleBarBackgroundPaddingBottom.value = scaleBarBack.attr("data-bottom");
styleScaleBarBackgroundPaddingLeft.value = scaleBarBack.attr("data-left");
}
}
if (styleElement === "vignette") {
styleVignette.style.display = "block";
@ -1043,6 +1069,44 @@ styleVignetteBlur.addEventListener("input", function () {
byId("vignette-rect")?.setAttribute("filter", `blur(${this.value}px)`);
});
styleScaleBar.addEventListener("input", function (event) {
const scaleBarBack = scaleBar.select("#scaleBarBack");
if (!scaleBarBack.size()) return;
const {id, value} = event.target;
if (id === "styleScaleBarSize") scaleBar.attr("data-bar-size", value);
else if (id === "styleScaleBarFontSize") scaleBar.attr("font-size", value);
else if (id === "styleScaleBarPositionX") scaleBar.attr("data-x", value);
else if (id === "styleScaleBarPositionY") scaleBar.attr("data-y", value);
else if (id === "styleScaleBarLabel") scaleBar.attr("data-label", value);
else if (id === "styleScaleBarBackgroundOpacityInput") scaleBarBack.attr("opacity", value);
else if (id === "styleScaleBarBackgroundFillInput") scaleBarBack.attr("fill", value);
else if (id === "styleScaleBarBackgroundStrokeInput") scaleBarBack.attr("stroke", value);
else if (id === "styleScaleBarBackgroundStrokeWidth") scaleBarBack.attr("stroke-width", value);
else if (id === "styleScaleBarBackgroundFilter") scaleBarBack.attr("filter", value);
else if (id === "styleScaleBarBackgroundPaddingTop") scaleBarBack.attr("data-top", value);
else if (id === "styleScaleBarBackgroundPaddingRight") scaleBarBack.attr("data-right", value);
else if (id === "styleScaleBarBackgroundPaddingBottom") scaleBarBack.attr("data-bottom", value);
else if (id === "styleScaleBarBackgroundPaddingLeft") scaleBarBack.attr("data-left", value);
if (
[
"styleScaleBarSize",
"styleScaleBarPositionX",
"styleScaleBarPositionY",
"styleScaleBarLabel",
"styleScaleBarBackgroundPaddingLeft",
"styleScaleBarBackgroundPaddingTop",
"styleScaleBarBackgroundPaddingRight",
"styleScaleBarBackgroundPaddingBottom"
].includes(id)
) {
drawScaleBar(scaleBar, scale);
fitScaleBar(scaleBar, svgWidth, svgHeight);
}
});
function updateElements() {
// burgIcons to desired size
burgIcons.selectAll("g").each(function () {

View file

@ -140,6 +140,9 @@ function applyStyleWithUiRefresh(style) {
invokeActiveZooming();
setPresetRemoveButtonVisibiliy();
drawScaleBar(scaleBar, scale);
fitScaleBar(scaleBar, svgWidth, svgHeight);
}
function addStylePreset() {
@ -301,7 +304,19 @@ function addStylePreset() {
],
"#fogging": ["opacity", "fill", "filter"],
"#vignette": ["opacity", "fill", "filter"],
"#vignette-rect": ["x", "y", "width", "height", "rx", "ry", "filter"]
"#vignette-rect": ["x", "y", "width", "height", "rx", "ry", "filter"],
"#scaleBar": ["opacity", "fill", "font-size", "data-bar-size", "data-x", "data-y", "data-label"],
"#scaleBarBack": [
"opacity",
"fill",
"stroke",
"stroke-width",
"filter",
"data-top",
"data-right",
"data-bottom",
"data-left"
]
};
for (const selector in attributes) {

View file

@ -24,13 +24,6 @@ function editUnits() {
byId("heightExponentInput").addEventListener("input", changeHeightExponent);
byId("heightExponentOutput").addEventListener("input", changeHeightExponent);
byId("temperatureScale").addEventListener("change", changeTemperatureScale);
byId("barSizeOutput").addEventListener("input", renderScaleBar);
byId("barSizeInput").addEventListener("input", renderScaleBar);
byId("barLabel").addEventListener("input", renderScaleBar);
byId("barPosX").addEventListener("input", fitScaleBar);
byId("barPosY").addEventListener("input", fitScaleBar);
byId("barBackOpacity").addEventListener("input", changeScaleBarOpacity);
byId("barBackColor").addEventListener("input", changeScaleBarColor);
byId("populationRateOutput").addEventListener("input", changePopulationRate);
byId("populationRateInput").addEventListener("change", changePopulationRate);
@ -84,14 +77,6 @@ function editUnits() {
if (layerIsOn("toggleTemp")) drawTemp();
}
function changeScaleBarOpacity() {
scaleBar.select("rect").attr("opacity", this.value);
}
function changeScaleBarColor() {
scaleBar.select("rect").attr("fill", this.value);
}
function changePopulationRate() {
populationRate = +this.value;
}
@ -129,19 +114,6 @@ function editUnits() {
localStorage.removeItem("heightExponent");
calculateTemperatures();
// scale bar
barSizeOutput.value = barSizeInput.value = 2;
barLabel.value = "";
barBackOpacity.value = 0.2;
barBackColor.value = "#ffffff";
barPosX.value = barPosY.value = 99;
localStorage.removeItem("barSize");
localStorage.removeItem("barLabel");
localStorage.removeItem("barBackOpacity");
localStorage.removeItem("barBackColor");
localStorage.removeItem("barPosX");
localStorage.removeItem("barPosY");
renderScaleBar();
// population