mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-06 02:21:25 +01:00
fixed projection problems in export
added cell -> geojson export added a QGIS example style
This commit is contained in:
parent
2df9f02440
commit
a0df54bb21
8 changed files with 623 additions and 151 deletions
|
|
@ -1,6 +1,83 @@
|
|||
// Functions to save and load the map
|
||||
"use strict";
|
||||
|
||||
// download map data as GeoJSON
|
||||
function saveGeoJSON() {
|
||||
|
||||
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
||||
|
||||
|
||||
const cells = pack.cells;
|
||||
const v = pack.vertices;
|
||||
|
||||
/*
|
||||
my guesses on the cells structure:
|
||||
|
||||
cells.h = height
|
||||
cells.p = coordinates of center point (of the voronoi cell)
|
||||
cells.pop = population
|
||||
|
||||
// from voronoi.js:
|
||||
const cells = {v: [], c: [], b: []}; // voronoi cells: v = cell vertices, c = adjacent cells, b = near-border cell
|
||||
const vertices = {p: [], v: [], c: []}; // cells vertices: p = vertex coordinates, v = neighboring vertices, c = adjacent cells
|
||||
|
||||
|
||||
*/
|
||||
|
||||
cells.i.forEach(i => {
|
||||
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Polygon\", \"coordinates\": [[";
|
||||
cells.v[i].forEach(n => {
|
||||
let x = mapCoordinates.lonW + (v.p[n][0] / graphWidth) * mapCoordinates.lonT;
|
||||
let y = mapCoordinates.latN - (v.p[n][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
||||
data += "["+x+","+y+"],";
|
||||
});
|
||||
// close the ring
|
||||
let x = mapCoordinates.lonW + (v.p[cells.v[i][0]][0] / graphWidth) * mapCoordinates.lonT;
|
||||
let y = mapCoordinates.latN - (v.p[cells.v[i][0]][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
||||
data += "["+x+","+y+"]";
|
||||
|
||||
data += "]] },\n \"properties\": {\n";
|
||||
|
||||
let height = parseInt(getFriendlyHeight(cells.h[i]));
|
||||
|
||||
data += " \"id\": \""+i+"\",\n";
|
||||
data += " \"height\": \""+height+"\",\n";
|
||||
data += " \"biome\": \""+cells.biome[i]+"\",\n";
|
||||
data += " \"population\": \""+cells.pop[i]+"\",\n";
|
||||
data += " \"state\": \""+cells.state[i]+"\",\n";
|
||||
data += " \"province\": \""+cells.province[i]+"\",\n";
|
||||
data += " \"culture\": \""+cells.culture[i]+"\",\n";
|
||||
data += " \"religion\": \""+cells.religion[i]+"\"\n";
|
||||
data +=" }\n},\n";
|
||||
});
|
||||
|
||||
/*
|
||||
cells.i.forEach(i => {
|
||||
let x = (cells.p[i][0] / graphWidth) * mapCoordinates.lonT + mapCoordinates.lonW;
|
||||
let y = mapCoordinates.latN - (cells.p[i][1] / graphHeight) * mapCoordinates.lonT; // inverted in QGIS otherwise
|
||||
let height = parseInt(getFriendlyHeight(cells.h[i]));
|
||||
|
||||
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Point\", \"coordinates\": ["+x+", "+y+", "+height+"] },\n \"properties\": {\n";
|
||||
data += " \"id\": \""+i+"\",\n";
|
||||
data += " \"biome\": \""+cells.biome[i]+"\",\n";
|
||||
data += " \"height\": \""+cells.h[i]+"\"\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 = "fantasy_map_" + Date.now() + ".geojson";
|
||||
link.href = url;
|
||||
link.click();
|
||||
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
||||
}
|
||||
|
||||
// download map as SVG or PNG file
|
||||
function saveAsImage(type) {
|
||||
console.time("saveAsImage");
|
||||
|
|
@ -160,8 +237,8 @@ function saveMap() {
|
|||
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
||||
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
||||
const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|");
|
||||
const options = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value, heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
||||
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value, barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
||||
const options = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value, heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
||||
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value, barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
||||
mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value, temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds)].join("|");
|
||||
const coords = JSON.stringify(mapCoordinates);
|
||||
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|");
|
||||
|
|
@ -182,11 +259,11 @@ function saveMap() {
|
|||
const provinces = JSON.stringify(pack.provinces);
|
||||
|
||||
// data format as below
|
||||
const data = [params, options, coords, biomes, notesData, svg_xml,
|
||||
const data = [params, options, coords, biomes, notesData, svg_xml,
|
||||
gridGeneral, grid.cells.h, grid.cells.prec, grid.cells.f, grid.cells.t, grid.cells.temp,
|
||||
features, cultures, states, burgs,
|
||||
pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl,
|
||||
pack.cells.pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state,
|
||||
pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl,
|
||||
pack.cells.pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state,
|
||||
pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces].join("\r\n");
|
||||
const dataBlob = new Blob([data], {type: "text/plain"});
|
||||
const dataURL = window.URL.createObjectURL(dataBlob);
|
||||
|
|
@ -229,7 +306,7 @@ function uploadFile(file, callback) {
|
|||
<br>Please keep using an ${archive}`;
|
||||
} else {
|
||||
load = true;
|
||||
message = `The map version (${mapVersion}) does not match the Generator version (${version}).
|
||||
message = `The map version (${mapVersion}) does not match the Generator version (${version}).
|
||||
<br>The map will be auto-updated. In case of issues please keep using an ${archive} of the Generator`;
|
||||
}
|
||||
alertMessage.innerHTML = message;
|
||||
|
|
|
|||
|
|
@ -265,8 +265,8 @@ function editBurgs() {
|
|||
data += b.port ? "port," : ",";
|
||||
|
||||
// add geography data
|
||||
data += (b.x / graphWidth) * mapCoordinates.lonT + mapCoordinates.lonW + ",";
|
||||
data += (b.y / graphHeight) * mapCoordinates.latT + mapCoordinates.latS + ",";
|
||||
data += mapCoordinates.lonW + (b.x / graphWidth) * mapCoordinates.lonT + ",";
|
||||
data += mapCoordinates.latN - (b.y / graphHeight) * mapCoordinates.latT + ","; // this is inverted in QGIS otherwise
|
||||
data += parseInt(getFriendlyHeight(pack.cells.h[b.cell])) + "\n";
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
$("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"});
|
||||
$("#mapLayers").disableSelection();
|
||||
|
||||
// show control elements and remove loading screen on map load
|
||||
// show control elements and remove loading screen on map load
|
||||
d3.select("#loading").transition().duration(5000).style("opacity", 0).remove();
|
||||
d3.select("#initial").transition().duration(5000).attr("opacity", 0).remove();
|
||||
d3.select("#optionsContainer").transition().duration(5000).style("opacity", 1);
|
||||
|
|
@ -27,7 +27,7 @@ function showOptions(event) {
|
|||
regenerate.style.display = "none";
|
||||
options.style.display = "block";
|
||||
optionsTrigger.style.display = "none";
|
||||
|
||||
|
||||
if (event) event.stopPropagation();
|
||||
}
|
||||
|
||||
|
|
@ -55,19 +55,19 @@ collapsible.addEventListener("mouseleave", function() {
|
|||
});
|
||||
|
||||
// Activate options tab on click
|
||||
options.querySelector("div.tab").addEventListener("click", function(event) {
|
||||
options.querySelector("div.tab").addEventListener("click", function(event) {
|
||||
if (event.target.tagName !== "BUTTON") return;
|
||||
const id = event.target.id;
|
||||
const active = options.querySelector(".tab > button.active");
|
||||
if (active && id === active.id) return; // already active tab is clicked
|
||||
|
||||
if (active) active.classList.remove("active");
|
||||
document.getElementById(id).classList.add("active");
|
||||
document.getElementById(id).classList.add("active");
|
||||
options.querySelectorAll(".tabcontent").forEach(e => e.style.display = "none");
|
||||
|
||||
if (id === "styleTab") styleContent.style.display = "block"; else
|
||||
if (id === "optionsTab") optionsContent.style.display = "block"; else
|
||||
if (id === "toolsTab" && (!customization || customization === 10)) toolsContent.style.display = "block"; else
|
||||
if (id === "styleTab") styleContent.style.display = "block"; else
|
||||
if (id === "optionsTab") optionsContent.style.display = "block"; else
|
||||
if (id === "toolsTab" && (!customization || customization === 10)) toolsContent.style.display = "block"; else
|
||||
if (id === "toolsTab" && customization && customization !== 10) customizationMenu.style.display = "block"; else
|
||||
if (id === "aboutTab") aboutContent.style.display = "block";
|
||||
});
|
||||
|
|
@ -92,7 +92,7 @@ function selectStyleElement() {
|
|||
const sel = styleElementSelect.value;
|
||||
let el = d3.select("#"+sel);
|
||||
|
||||
styleElements.querySelectorAll("tbody").forEach(e => e.style.display = "none"); // hide all sections
|
||||
styleElements.querySelectorAll("tbody").forEach(e => e.style.display = "none"); // hide all sections
|
||||
const off = el.style("display") === "none" || !el.selectAll("*").size(); // check if layer is off
|
||||
if (off) {
|
||||
styleIsOff.style.display = "block";
|
||||
|
|
@ -104,7 +104,7 @@ function selectStyleElement() {
|
|||
if (sel == "ocean") el = oceanLayers.select("rect");
|
||||
else if (sel == "routes" || sel == "labels" || sel == "lakes" || sel == "anchors" || sel == "burgIcons" || sel == "borders") {
|
||||
el = d3.select("#"+sel).select("g#"+group).size()
|
||||
? d3.select("#"+sel).select("g#"+group)
|
||||
? d3.select("#"+sel).select("g#"+group)
|
||||
: d3.select("#"+sel).select("g");
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ function selectStyleElement() {
|
|||
styleCompassShiftY.value = tr[1];
|
||||
styleCompassSizeInput.value = styleCompassSizeOutput.value = tr[2];
|
||||
}
|
||||
|
||||
|
||||
// show specific sections
|
||||
if (sel === "terrs") styleHeightmap.style.display = "block";
|
||||
if (sel === "gridOverlay") styleGrid.style.display = "block";
|
||||
|
|
@ -175,7 +175,7 @@ function selectStyleElement() {
|
|||
if (sel === "texture") styleTexture.style.display = "block";
|
||||
if (sel === "routes" || sel === "labels" || sel == "anchors" || sel == "burgIcons" || sel === "lakes" || sel === "borders") styleGroup.style.display = "block";
|
||||
if (sel === "markers") styleMarkers.style.display = "block";
|
||||
|
||||
|
||||
if (sel === "population") {
|
||||
stylePopulation.style.display = "block";
|
||||
stylePopulationRuralStrokeInput.value = stylePopulationRuralStrokeOutput.value = population.select("#rural").attr("stroke");
|
||||
|
|
@ -262,7 +262,7 @@ function selectStyleElement() {
|
|||
if (sel === "temperature") {
|
||||
styleStrokeWidth.style.display = "block";
|
||||
styleTemperature.style.display = "block";
|
||||
styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || "";
|
||||
styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || "";
|
||||
styleTemperatureFillOpacityInput.value = styleTemperatureFillOpacityOutput.value = el.attr("fill-opacity") || .1;
|
||||
styleTemperatureFillInput.value = styleTemperatureFillOutput.value = el.attr("fill") || "#000";
|
||||
styleTemperatureFontSizeInput.value = styleTemperatureFontSizeOutput.value = el.attr("font-size") || "8px";;
|
||||
|
|
@ -369,7 +369,7 @@ styleShiftY.addEventListener("input", shiftElement);
|
|||
function shiftElement() {
|
||||
const x = styleShiftX.value || 0;
|
||||
const y = styleShiftY.value || 0;
|
||||
getEl().attr("transform", `translate(${x},${y})`);
|
||||
getEl().attr("transform", `translate(${x},${y})`);
|
||||
}
|
||||
|
||||
styleOceanBack.addEventListener("input", function() {
|
||||
|
|
@ -392,7 +392,7 @@ outlineLayersInput.addEventListener("change", function() {
|
|||
});
|
||||
|
||||
styleReliefSet.addEventListener("change", function() {
|
||||
ReliefIcons();
|
||||
ReliefIcons();
|
||||
if (!layerIsOn("toggleRelief")) toggleRelief();
|
||||
});
|
||||
|
||||
|
|
@ -453,7 +453,7 @@ styleCompassShiftY.addEventListener("input", shiftCompass);
|
|||
|
||||
function shiftCompass() {
|
||||
const tr = `translate(${styleCompassShiftX.value} ${styleCompassShiftY.value}) scale(${styleCompassSizeInput.value})`;
|
||||
d3.select("#rose").attr("transform", tr);
|
||||
d3.select("#rose").attr("transform", tr);
|
||||
}
|
||||
|
||||
styleLegendColItems.addEventListener("input", function() {
|
||||
|
|
@ -642,7 +642,7 @@ function setBase64Texture(url) {
|
|||
};
|
||||
|
||||
function fetchTextureURL(url) {
|
||||
console.log("Provided URL is", url);
|
||||
console.log("Provided URL is", url);
|
||||
const img = new Image();
|
||||
img.onload = function () {
|
||||
const canvas = document.getElementById("preview");
|
||||
|
|
@ -749,7 +749,7 @@ function toggleFullscreen() {
|
|||
|
||||
function generateMapWithSeed() {
|
||||
if (optionsSeed.value == seed) {
|
||||
tip("The current map already has this seed", false, "error");
|
||||
tip("The current map already has this seed", false, "error");
|
||||
return;
|
||||
}
|
||||
regeneratePrompt();
|
||||
|
|
@ -771,7 +771,7 @@ function showSeedHistoryDialog() {
|
|||
// generate map with historycal seed
|
||||
function restoreSeed(id) {
|
||||
if (mapHistory[id].seed == seed) {
|
||||
tip("The current map is already generated with this seed", null, "error");
|
||||
tip("The current map is already generated with this seed", null, "error");
|
||||
return;
|
||||
}
|
||||
optionsSeed.value = mapHistory[id].seed;
|
||||
|
|
@ -808,7 +808,7 @@ function changeBurgsNumberSlider(value) {
|
|||
function changeUIsize(value) {
|
||||
uiSizeInput.value = uiSizeOutput.value = value;
|
||||
document.getElementsByTagName("body")[0].style.fontSize = value * 11 + "px";
|
||||
document.getElementById("options").style.width = (value - 1) * 300 / 2 + 300 + "px";
|
||||
document.getElementById("options").style.width = (value - 1) * 300 / 2 + 300 + "px";
|
||||
}
|
||||
|
||||
function changeTooltipSize(value) {
|
||||
|
|
@ -996,11 +996,12 @@ document.getElementById("sticked").addEventListener("click", function(event) {
|
|||
else if (id === "saveMap") saveMap();
|
||||
else if (id === "saveSVG") saveAsImage("svg");
|
||||
else if (id === "savePNG") saveAsImage("png");
|
||||
if (id === "saveMap" || id === "saveSVG" || id === "savePNG") toggleSavePane();
|
||||
else if (id === "saveGeo") saveGeoJSON();
|
||||
if (id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo") toggleSavePane();
|
||||
});
|
||||
|
||||
function regeneratePrompt() {
|
||||
if (customization) {tip("Please exit the customization mode first", false, "warning"); return;}
|
||||
if (customization) {tip("Please exit the customization mode first", false, "warning"); return;}
|
||||
const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes
|
||||
if (workingTime < 15) {regenerateMap(); return;}
|
||||
|
||||
|
|
@ -1020,7 +1021,7 @@ function toggleSavePane() {
|
|||
|
||||
// ask users to allow popups
|
||||
if (!localStorage.getItem("dns_allow_popup_message")) {
|
||||
alertMessage.innerHTML = `Generator uses pop-up window to download files.
|
||||
alertMessage.innerHTML = `Generator uses pop-up window to download files.
|
||||
<br>Please ensure your browser does not block popups.
|
||||
<br>Please check browser settings and turn off adBlocker if it is enabled`;
|
||||
|
||||
|
|
@ -1042,4 +1043,4 @@ document.getElementById("mapToLoad").addEventListener("change", function() {
|
|||
const fileToLoad = this.files[0];
|
||||
this.value = "";
|
||||
uploadFile(fileToLoad);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue