mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-21 19:41:23 +01:00
commit
5638e06e87
8 changed files with 149 additions and 18 deletions
|
|
@ -205,6 +205,7 @@ i.icon-lock {
|
||||||
}
|
}
|
||||||
|
|
||||||
#religionHierarchy text,
|
#religionHierarchy text,
|
||||||
|
#statesTree text,
|
||||||
#provincesTree text {
|
#provincesTree text {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
@ -212,11 +213,13 @@ i.icon-lock {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#provincesTree .selected {
|
#statesTree circle {
|
||||||
stroke: #c13119;
|
filter: url(#dropShadow05);
|
||||||
stroke-width: 2;
|
stroke: #666666;
|
||||||
|
stroke-width: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#statesTree circle.selected,
|
||||||
#provincesTree .selected {
|
#provincesTree .selected {
|
||||||
stroke: #c13119;
|
stroke: #c13119;
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
|
|
|
||||||
|
|
@ -2503,6 +2503,7 @@
|
||||||
<button id="statesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
<button id="statesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
||||||
<button id="statesLegend" data-tip="Toggle Legend box" class="icon-list-bullet"></button>
|
<button id="statesLegend" data-tip="Toggle Legend box" class="icon-list-bullet"></button>
|
||||||
<button id="statesPercentage" data-tip="Toggle percentage / absolute values views" class="icon-percent"></button>
|
<button id="statesPercentage" data-tip="Toggle percentage / absolute values views" class="icon-percent"></button>
|
||||||
|
<button id="statesChart" data-tip="Show states bubble chart" class="icon-chart-area"></button>
|
||||||
|
|
||||||
<button id="statesRegenerate" data-tip="Show the regeneration menu and more data" class="icon-cog-alt"></button>
|
<button id="statesRegenerate" data-tip="Show the regeneration menu and more data" class="icon-cog-alt"></button>
|
||||||
<div id="statesRegenerateButtons" style="display: none">
|
<div id="statesRegenerateButtons" style="display: none">
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@
|
||||||
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population
|
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population
|
||||||
|
|
||||||
if (port) {
|
if (port) {
|
||||||
b.population *= b.population * 1.3; // increase port population
|
b.population = b.population * 1.3; // increase port population
|
||||||
const e = cells.v[i].filter(v => vertices.c[v].some(c => c === cells.haven[i])); // vertices of common edge
|
const e = cells.v[i].filter(v => vertices.c[v].some(c => c === cells.haven[i])); // vertices of common edge
|
||||||
b.x = rn((vertices.p[e[0]][0] + vertices.p[e[1]][0]) / 2, 2);
|
b.x = rn((vertices.p[e[0]][0] + vertices.p[e[1]][0]) / 2, 2);
|
||||||
b.y = rn((vertices.p[e[0]][1] + vertices.p[e[1]][1]) / 2, 2);
|
b.y = rn((vertices.p[e[0]][1] + vertices.p[e[1]][1]) / 2, 2);
|
||||||
|
|
|
||||||
|
|
@ -162,11 +162,11 @@ function getMapData() {
|
||||||
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
||||||
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
||||||
const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|");
|
const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|");
|
||||||
const options = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value,
|
const options = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value,
|
||||||
heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
||||||
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value,
|
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value,
|
||||||
barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
||||||
mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value,
|
mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value,
|
||||||
temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds),
|
temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds),
|
||||||
mapName.value].join("|");
|
mapName.value].join("|");
|
||||||
const coords = JSON.stringify(mapCoordinates);
|
const coords = JSON.stringify(mapCoordinates);
|
||||||
|
|
@ -240,6 +240,7 @@ function saveGeoJSON() {
|
||||||
Cells: saveGeoJSON_Cells,
|
Cells: saveGeoJSON_Cells,
|
||||||
Routes: saveGeoJSON_Roads,
|
Routes: saveGeoJSON_Roads,
|
||||||
Rivers: saveGeoJSON_Rivers,
|
Rivers: saveGeoJSON_Rivers,
|
||||||
|
Markers: saveGeoJSON_Markers,
|
||||||
Close: function() {$(this).dialog("close");}
|
Close: function() {$(this).dialog("close");}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -377,7 +378,7 @@ const saveReminder = function() {
|
||||||
"Safety is number one priority. Please save the map",
|
"Safety is number one priority. Please save the map",
|
||||||
"Don't forget to save your map on a regular basis!",
|
"Don't forget to save your map on a regular basis!",
|
||||||
"Just a gentle reminder for you to save the map",
|
"Just a gentle reminder for you to save the map",
|
||||||
"Please forget to save your progress (saving as .map is the best option)",
|
"Please don't forget to save your progress (saving as .map is the best option)",
|
||||||
"Don't want to be reminded about need to save? Press CTRL+Q"];
|
"Don't want to be reminded about need to save? Press CTRL+Q"];
|
||||||
|
|
||||||
saveReminder.reminder = setInterval(() => {
|
saveReminder.reminder = setInterval(() => {
|
||||||
|
|
@ -430,12 +431,13 @@ function saveGeoJSON_Cells() {
|
||||||
data += "["+x+","+y+"]";
|
data += "["+x+","+y+"]";
|
||||||
data += "]] },\n \"properties\": {\n";
|
data += "]] },\n \"properties\": {\n";
|
||||||
|
|
||||||
let height = parseInt(getFriendlyHeight(cells.h[i]));
|
let height = parseInt(getFriendlyHeight([cells.p[i][0],cells.p[i][1]]));
|
||||||
|
|
||||||
data += " \"id\": \""+i+"\",\n";
|
data += " \"id\": \""+i+"\",\n";
|
||||||
data += " \"height\": \""+height+"\",\n";
|
data += " \"height\": \""+height+"\",\n";
|
||||||
data += " \"biome\": \""+cells.biome[i]+"\",\n";
|
data += " \"biome\": \""+cells.biome[i]+"\",\n";
|
||||||
data += " \"population\": \""+cells.pop[i]+"\",\n";
|
data += " \"type\": \""+pack.features[cells.f[i]].type+"\",\n";
|
||||||
|
data += " \"population\": \""+getFriendlyPopulation(i)+"\",\n";
|
||||||
data += " \"state\": \""+cells.state[i]+"\",\n";
|
data += " \"state\": \""+cells.state[i]+"\",\n";
|
||||||
data += " \"province\": \""+cells.province[i]+"\",\n";
|
data += " \"province\": \""+cells.province[i]+"\",\n";
|
||||||
data += " \"culture\": \""+cells.culture[i]+"\",\n";
|
data += " \"culture\": \""+cells.culture[i]+"\",\n";
|
||||||
|
|
@ -456,6 +458,34 @@ function saveGeoJSON_Cells() {
|
||||||
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveGeoJSON_Markers() {
|
||||||
|
|
||||||
|
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
||||||
|
|
||||||
|
markers._groups[0][0].childNodes.forEach(n => {
|
||||||
|
let x = mapCoordinates.lonW + (n.dataset.x / graphWidth) * mapCoordinates.lonT;
|
||||||
|
let y = mapCoordinates.latN - (n.dataset.y / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
||||||
|
|
||||||
|
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Point\", \"coordinates\": ["+x+", "+y+"]";
|
||||||
|
data += " },\n \"properties\": {\n";
|
||||||
|
data += " \"id\": \""+n.id+"\",\n";
|
||||||
|
data += " \"type\": \""+n.dataset.id.substring(8)+"\"\n";
|
||||||
|
data +=" }\n},\n";
|
||||||
|
|
||||||
|
});
|
||||||
|
data = data.substring(0, data.length - 2)+"\n"; // remove trailing comma
|
||||||
|
data += "]}";
|
||||||
|
|
||||||
|
const dataBlob = new Blob([data], {type: "application/json"});
|
||||||
|
const url = window.URL.createObjectURL(dataBlob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.download = getFileName("Markers") + ".geojson";
|
||||||
|
link.href = url;
|
||||||
|
link.click();
|
||||||
|
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
function uploadFile(file, callback) {
|
function uploadFile(file, callback) {
|
||||||
uploadFile.timeStart = performance.now();
|
uploadFile.timeStart = performance.now();
|
||||||
|
|
||||||
|
|
@ -822,4 +852,4 @@ function parseLoadedData(data) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,7 @@ function editBurgs() {
|
||||||
data += b.name + ",";
|
data += b.name + ",";
|
||||||
const province = pack.cells.province[b.cell];
|
const province = pack.cells.province[b.cell];
|
||||||
data += province ? pack.provinces[province].fullName + "," : ",";
|
data += province ? pack.provinces[province].fullName + "," : ",";
|
||||||
data += b.state ? pack.states[b.state].fullName : pack.states[b.state].name + ",";
|
data += b.state ? pack.states[b.state].fullName +"," : pack.states[b.state].name + ",";
|
||||||
data += pack.cultures[b.culture].name + ",";
|
data += pack.cultures[b.culture].name + ",";
|
||||||
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
|
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
|
||||||
data += rn(b.population * populationRate.value * urbanization.value) + ",";
|
data += rn(b.population * populationRate.value * urbanization.value) + ",";
|
||||||
|
|
@ -267,11 +267,11 @@ function editBurgs() {
|
||||||
// add geography data
|
// add geography data
|
||||||
data += mapCoordinates.lonW + (b.x / graphWidth) * mapCoordinates.lonT + ",";
|
data += mapCoordinates.lonW + (b.x / graphWidth) * mapCoordinates.lonT + ",";
|
||||||
data += mapCoordinates.latN - (b.y / graphHeight) * mapCoordinates.latT + ","; // this is inverted in QGIS otherwise
|
data += mapCoordinates.latN - (b.y / graphHeight) * mapCoordinates.latT + ","; // this is inverted in QGIS otherwise
|
||||||
data += parseInt(getHeight(pack.cells.h[b.cell])) + ",";
|
data += parseInt(getFriendlyHeight([b.x,b.y])) + ",";
|
||||||
|
|
||||||
// add status data
|
// add status data
|
||||||
data += b.capital ? "capital," : ",";
|
data += b.capital ? "true," : "false,";
|
||||||
data += b.port ? "port\n" : "\n";
|
data += b.port ? "true\n" : "false\n";
|
||||||
});
|
});
|
||||||
|
|
||||||
const dataBlob = new Blob([data], {type: "text/plain"});
|
const dataBlob = new Blob([data], {type: "text/plain"});
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ function editProvinces() {
|
||||||
document.getElementById("provincesManuallyApply").addEventListener("click", applyProvincesManualAssignent);
|
document.getElementById("provincesManuallyApply").addEventListener("click", applyProvincesManualAssignent);
|
||||||
document.getElementById("provincesManuallyCancel").addEventListener("click", () => exitProvincesManualAssignment());
|
document.getElementById("provincesManuallyCancel").addEventListener("click", () => exitProvincesManualAssignment());
|
||||||
document.getElementById("provincesAdd").addEventListener("click", enterAddProvinceMode);
|
document.getElementById("provincesAdd").addEventListener("click", enterAddProvinceMode);
|
||||||
|
|
||||||
body.addEventListener("click", function(ev) {
|
body.addEventListener("click", function(ev) {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
const el = ev.target, cl = el.classList, line = el.parentNode, p = +line.dataset.id;
|
const el = ev.target, cl = el.classList, line = el.parentNode, p = +line.dataset.id;
|
||||||
|
|
@ -373,7 +373,7 @@ function editProvinces() {
|
||||||
: d => d.rural + d.urban;
|
: d => d.rural + d.urban;
|
||||||
|
|
||||||
const newRoot = d3.stratify().parentId(d => d.state)(data).sum(value);
|
const newRoot = d3.stratify().parentId(d => d.state)(data).sum(value);
|
||||||
node.data(treeLayout(newRoot).leaves())
|
node.data(treeLayout(newRoot).leaves());
|
||||||
|
|
||||||
node.select("rect").transition().duration(1500)
|
node.select("rect").transition().duration(1500)
|
||||||
.attr("x", d => d.x0).attr("y", d => d.y0)
|
.attr("x", d => d.x0).attr("y", d => d.y0)
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ function editReligions() {
|
||||||
const urban = r.urban * populationRate.value * urbanization.value;
|
const urban = r.urban * populationRate.value * urbanization.value;
|
||||||
const population = rural + urban > 0 ? ". " + si(rn(rural + urban)) + " believers" : ". Extinct";
|
const population = rural + urban > 0 ? ". " + si(rn(rural + urban)) + " believers" : ". Extinct";
|
||||||
info.innerHTML = `${r.name}${type}${form}${population}`;
|
info.innerHTML = `${r.name}${type}${form}${population}`;
|
||||||
tip("Drag to change parent. Hold CTRL and click to change abbrebiation");
|
tip("Drag to change parent. Hold CTRL and click to change abbreviation");
|
||||||
}
|
}
|
||||||
|
|
||||||
const el = body.querySelector(`div[data-id='${religion}']`);
|
const el = body.querySelector(`div[data-id='${religion}']`);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ function editStates() {
|
||||||
document.getElementById("statesEditorRefresh").addEventListener("click", refreshStatesEditor);
|
document.getElementById("statesEditorRefresh").addEventListener("click", refreshStatesEditor);
|
||||||
document.getElementById("statesLegend").addEventListener("click", toggleLegend);
|
document.getElementById("statesLegend").addEventListener("click", toggleLegend);
|
||||||
document.getElementById("statesPercentage").addEventListener("click", togglePercentageMode);
|
document.getElementById("statesPercentage").addEventListener("click", togglePercentageMode);
|
||||||
|
document.getElementById("statesChart").addEventListener("click", showStatesChart);
|
||||||
document.getElementById("statesRegenerate").addEventListener("click", openRegenerationMenu);
|
document.getElementById("statesRegenerate").addEventListener("click", openRegenerationMenu);
|
||||||
document.getElementById("statesRegenerateBack").addEventListener("click", exitRegenerationMenu);
|
document.getElementById("statesRegenerateBack").addEventListener("click", exitRegenerationMenu);
|
||||||
document.getElementById("statesRecalculate").addEventListener("click", () => recalculateStates(true));
|
document.getElementById("statesRecalculate").addEventListener("click", () => recalculateStates(true));
|
||||||
|
|
@ -387,6 +388,102 @@ function editStates() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showStatesChart() {
|
||||||
|
// build hierarchy tree
|
||||||
|
const data = pack.states.filter(s => !s.removed);
|
||||||
|
const root = d3.stratify().id(d => d.i).parentId(d => d.i ? 0 : null)(data)
|
||||||
|
.sum(d => d.area).sort((a, b) => b.value - a.value);
|
||||||
|
|
||||||
|
const width = 150 + 200 * uiSizeOutput.value, height = 150 + 200 * uiSizeOutput.value;
|
||||||
|
const margin = {top: 0, right: -50, bottom: 0, left: -50};
|
||||||
|
const w = width - margin.left - margin.right;
|
||||||
|
const h = height - margin.top - margin.bottom;
|
||||||
|
const treeLayout = d3.pack().size([w, h]).padding(3);
|
||||||
|
|
||||||
|
// prepare svg
|
||||||
|
alertMessage.innerHTML = `<select id="statesTreeType" style="display:block; margin-left:13px; font-size:11px">
|
||||||
|
<option value="area" selected>Area</option>
|
||||||
|
<option value="population">Total population</option>
|
||||||
|
<option value="rural">Rural population</option>
|
||||||
|
<option value="urban">Urban population</option>
|
||||||
|
<option value="burgs">Burgs number</option>
|
||||||
|
</select>`;
|
||||||
|
alertMessage.innerHTML += `<div id='statesInfo' class='chartInfo'>‍</div>`;
|
||||||
|
const svg = d3.select("#alertMessage").insert("svg", "#statesInfo").attr("id", "statesTree")
|
||||||
|
.attr("width", width).attr("height", height).style("font-family", "Almendra SC")
|
||||||
|
.attr("text-anchor", "middle").attr("dominant-baseline", "central");
|
||||||
|
const graph = svg.append("g").attr("transform", `translate(-50, 0)`);
|
||||||
|
document.getElementById("statesTreeType").addEventListener("change", updateChart);
|
||||||
|
|
||||||
|
treeLayout(root);
|
||||||
|
|
||||||
|
const node = graph.selectAll("g").data(root.leaves()).enter()
|
||||||
|
.append("g").attr("transform", d => `translate(${d.x},${d.y})`)
|
||||||
|
.attr("data-id", d => d.data.id)
|
||||||
|
.on("mouseenter", d => showInfo(event, d))
|
||||||
|
.on("mouseleave", d => hideInfo(event, d));
|
||||||
|
|
||||||
|
node.append("circle").attr("fill", d => d.data.color).attr("r", d => d.r);
|
||||||
|
|
||||||
|
const exp = /(?=[A-Z][^A-Z])/g;
|
||||||
|
const lp = n => d3.max(n.split(exp).map(p => p.length)) + 1; // longest name part + 1
|
||||||
|
|
||||||
|
node.append("text")
|
||||||
|
.style("font-size", d => rn(d.r ** .97 * 4 / lp(d.data.name), 2) + "px")
|
||||||
|
.selectAll("tspan").data(d => d.data.name.split(exp))
|
||||||
|
.join("tspan").attr("x", 0).text(d => d)
|
||||||
|
.attr("dy", (d, i, n) => `${i ? 1 : (n.length-1) / -2}em`);
|
||||||
|
|
||||||
|
function showInfo(ev, d) {
|
||||||
|
d3.select(ev.target).select("circle").classed("selected", 1);
|
||||||
|
const state = d.data.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 option = statesTreeType.value;
|
||||||
|
const value = option === "area" ? "Area: " + area
|
||||||
|
: option === "rural" ? "Rural population: " + si(rural)
|
||||||
|
: option === "urban" ? "Urban population: " + si(urban)
|
||||||
|
: option === "burgs" ? "Burgs number: " + d.data.burgs
|
||||||
|
: "Population: " + si(rural + urban);
|
||||||
|
|
||||||
|
statesInfo.innerHTML = `${state}. ${value}`;
|
||||||
|
stateHighlightOn(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideInfo(ev) {
|
||||||
|
stateHighlightOff(ev);
|
||||||
|
if (!document.getElementById("statesInfo")) return;
|
||||||
|
statesInfo.innerHTML = "‍";
|
||||||
|
d3.select(ev.target).select("circle").classed("selected", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateChart() {
|
||||||
|
const value = this.value === "area" ? d => d.area
|
||||||
|
: this.value === "rural" ? d => d.rural
|
||||||
|
: this.value === "urban" ? d => d.urban
|
||||||
|
: this.value === "burgs" ? d => d.burgs
|
||||||
|
: d => d.rural + d.urban;
|
||||||
|
|
||||||
|
root.sum(value);
|
||||||
|
node.data(treeLayout(root).leaves());
|
||||||
|
|
||||||
|
node.transition().duration(1500).attr("transform", d => `translate(${d.x},${d.y})`)
|
||||||
|
node.select("circle").transition().duration(1500).attr("r", d => d.r);
|
||||||
|
node.select("text").transition().duration(1500)
|
||||||
|
.style("font-size", d => rn(d.r ** .97 * 4 / lp(d.data.name), 2) + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#alert").dialog({
|
||||||
|
title: "States bubble chart", width: fitContent(),
|
||||||
|
position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, buttons: {},
|
||||||
|
close: () => {alertMessage.innerHTML = "";}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function openRegenerationMenu() {
|
function openRegenerationMenu() {
|
||||||
statesBottom.querySelectorAll(":scope > button").forEach(el => el.style.display = "none");
|
statesBottom.querySelectorAll(":scope > button").forEach(el => el.style.display = "none");
|
||||||
statesRegenerateButtons.style.display = "block";
|
statesRegenerateButtons.style.display = "block";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue