diff --git a/index.html b/index.html index 09f5a2c6..db3487e7 100644 --- a/index.html +++ b/index.html @@ -4124,7 +4124,8 @@ - + + diff --git a/modules/save-and-load.js b/modules/load.js similarity index 60% rename from modules/save-and-load.js rename to modules/load.js index d3353525..500c390a 100644 --- a/modules/save-and-load.js +++ b/modules/load.js @@ -1,540 +1,6 @@ // Functions to save and load the map "use strict"; -// download map as SVG -async function saveSVG() { - TIME && console.time("saveSVG"); - const url = await getMapURL("svg"); - const link = document.createElement("a"); - link.download = getFileName() + ".svg"; - link.href = url; - link.click(); - - tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, "success", 5000); - TIME && console.timeEnd("saveSVG"); -} - -// download map as PNG -async function savePNG() { - TIME && console.time("savePNG"); - const url = await getMapURL("png"); - - const link = document.createElement("a"); - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); - canvas.width = svgWidth * pngResolutionInput.value; - canvas.height = svgHeight * pngResolutionInput.value; - const img = new Image(); - img.src = url; - - img.onload = function () { - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - link.download = getFileName() + ".png"; - canvas.toBlob(function (blob) { - link.href = window.URL.createObjectURL(blob); - link.click(); - window.setTimeout(function () { - canvas.remove(); - window.URL.revokeObjectURL(link.href); - tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, "success", 5000); - }, 1000); - }); - }; - - TIME && console.timeEnd("savePNG"); -} - -// download map as JPEG -async function saveJPEG() { - TIME && console.time("saveJPEG"); - const url = await getMapURL("png"); - - const canvas = document.createElement("canvas"); - canvas.width = svgWidth * pngResolutionInput.value; - canvas.height = svgHeight * pngResolutionInput.value; - const img = new Image(); - img.src = url; - - img.onload = async function () { - canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height); - const quality = Math.min(rn(1 - pngResolutionInput.value / 20, 2), 0.92); - const URL = await canvas.toDataURL("image/jpeg", quality); - const link = document.createElement("a"); - link.download = getFileName() + ".jpeg"; - link.href = URL; - link.click(); - tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000); - window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000); - }; - - TIME && console.timeEnd("saveJPEG"); -} - -// parse map svg to object url -async function getMapURL(type, subtype) { - const cloneEl = document.getElementById("map").cloneNode(true); // clone svg - cloneEl.id = "fantasyMap"; - document.body.appendChild(cloneEl); - const clone = d3.select(cloneEl); - clone.select("#debug").remove(); - - const cloneDefs = cloneEl.getElementsByTagName("defs")[0]; - const svgDefs = document.getElementById("defElements"); - - const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1; - if (isFirefox && type === "mesh") clone.select("#oceanPattern").remove(); - if (subtype === "globe") clone.select("#scaleBar").remove(); - if (subtype === "noWater") { - clone.select("#oceanBase").attr("opacity", 0); - clone.select("#oceanPattern").attr("opacity", 0); - } - if (type !== "png") { - // reset transform to show the whole map - clone.attr("width", graphWidth).attr("height", graphHeight); - clone.select("#viewbox").attr("transform", null); - } - if (type === "svg") removeUnusedElements(clone); - if (customization && type === "mesh") updateMeshCells(clone); - inlineStyle(clone); - - // remove unused filters - const filters = cloneEl.querySelectorAll("filter"); - for (let i = 0; i < filters.length; i++) { - const id = filters[i].id; - if (cloneEl.querySelector("[filter='url(#" + id + ")']")) continue; - if (cloneEl.getAttribute("filter") === "url(#" + id + ")") continue; - filters[i].remove(); - } - - // remove unused patterns - const patterns = cloneEl.querySelectorAll("pattern"); - for (let i = 0; i < patterns.length; i++) { - const id = patterns[i].id; - if (cloneEl.querySelector("[fill='url(#" + id + ")']")) continue; - patterns[i].remove(); - } - - // remove unused symbols - const symbols = cloneEl.querySelectorAll("symbol"); - for (let i = 0; i < symbols.length; i++) { - const id = symbols[i].id; - if (cloneEl.querySelector("use[*|href='#" + id + "']")) continue; - symbols[i].remove(); - } - - // add displayed emblems - if (layerIsOn("toggleEmblems") && emblems.selectAll("use").size()) { - cloneEl - .getElementById("emblems") - ?.querySelectorAll("use") - .forEach(el => { - const href = el.getAttribute("href") || el.getAttribute("xlink:href"); - if (!href) return; - const emblem = document.getElementById(href.slice(1)); - if (emblem) cloneDefs.append(emblem.cloneNode(true)); - }); - } else { - cloneDefs.querySelector("#defs-emblems")?.remove(); - } - - // replace ocean pattern href to base64 - if (PRODUCTION && cloneEl.getElementById("oceanicPattern")) { - const el = cloneEl.getElementById("oceanicPattern"); - const url = el.getAttribute("href"); - await new Promise(resolve => { - getBase64(url, base64 => { - el.setAttribute("href", base64); - resolve(); - }); - }); - } - - // add relief icons - if (cloneEl.getElementById("terrain")) { - const uniqueElements = new Set(); - const terrainNodes = cloneEl.getElementById("terrain").childNodes; - for (let i = 0; i < terrainNodes.length; i++) { - const href = terrainNodes[i].getAttribute("href") || terrainNodes[i].getAttribute("xlink:href"); - uniqueElements.add(href); - } - - const defsRelief = svgDefs.getElementById("defs-relief"); - for (const terrain of [...uniqueElements]) { - const element = defsRelief.querySelector(terrain); - if (element) cloneDefs.appendChild(element.cloneNode(true)); - } - } - - // add wind rose - if (cloneEl.getElementById("compass")) { - const rose = svgDefs.getElementById("rose"); - if (rose) cloneDefs.appendChild(rose.cloneNode(true)); - } - - // add port icon - if (cloneEl.getElementById("anchors")) { - const anchor = svgDefs.getElementById("icon-anchor"); - if (anchor) cloneDefs.appendChild(anchor.cloneNode(true)); - } - - // add grid pattern - if (cloneEl.getElementById("gridOverlay")?.hasChildNodes()) { - const type = cloneEl.getElementById("gridOverlay").getAttribute("type"); - const pattern = svgDefs.getElementById("pattern_" + type); - if (pattern) cloneDefs.appendChild(pattern.cloneNode(true)); - } - - if (!cloneEl.getElementById("hatching").children.length) cloneEl.getElementById("hatching").remove(); //remove unused hatching group - if (!cloneEl.getElementById("fogging-cont")) cloneEl.getElementById("fog").remove(); //remove unused fog - if (!cloneEl.getElementById("regions")) cloneEl.getElementById("statePaths").remove(); // removed unused statePaths - if (!cloneEl.getElementById("labels")) cloneEl.getElementById("textPaths").remove(); // removed unused textPaths - - // add armies style - if (cloneEl.getElementById("armies")) cloneEl.insertAdjacentHTML("afterbegin", ""); - - const fontStyle = await GFontToDataURI(getFontsToLoad(clone)); // load non-standard fonts - if (fontStyle) clone.select("defs").append("style").text(fontStyle.join("\n")); // add font to style - clone.remove(); - - const serialized = `` + new XMLSerializer().serializeToString(cloneEl); - const blob = new Blob([serialized], {type: "image/svg+xml;charset=utf-8"}); - const url = window.URL.createObjectURL(blob); - window.setTimeout(() => window.URL.revokeObjectURL(url), 5000); - return url; -} - -// remove hidden g elements and g elements without children to make downloaded svg smaller in size -function removeUnusedElements(clone) { - if (!terrain.selectAll("use").size()) clone.select("#defs-relief").remove(); - if (markers.style("display") === "none") clone.select("#defs-markers").remove(); - - for (let empty = 1; empty; ) { - empty = 0; - clone.selectAll("g").each(function () { - if (!this.hasChildNodes() || this.style.display === "none" || this.classList.contains("hidden")) { - empty++; - this.remove(); - } - if (this.hasAttribute("display") && this.style.display === "inline") this.removeAttribute("display"); - }); - } -} - -function updateMeshCells(clone) { - const data = renderOcean.checked ? grid.cells.i : grid.cells.i.filter(i => grid.cells.h[i] >= 20); - const scheme = getColorScheme(); - clone.select("#heights").attr("filter", "url(#blur1)"); - clone - .select("#heights") - .selectAll("polygon") - .data(data) - .join("polygon") - .attr("points", d => getGridPolygon(d)) - .attr("id", d => "cell" + d) - .attr("stroke", d => getColor(grid.cells.h[d], scheme)); -} - -// for each g element get inline style -function inlineStyle(clone) { - const emptyG = clone.append("g").node(); - const defaultStyles = window.getComputedStyle(emptyG); - - clone.selectAll("g, #ruler *, #scaleBar > text").each(function () { - const compStyle = window.getComputedStyle(this); - let style = ""; - - for (let i = 0; i < compStyle.length; i++) { - const key = compStyle[i]; - const value = compStyle.getPropertyValue(key); - - // Firefox mask hack - if (key === "mask-image" && value !== defaultStyles.getPropertyValue(key)) { - style += "mask-image: url('#land');"; - continue; - } - - if (key === "cursor") continue; // cursor should be default - if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute - if (value === defaultStyles.getPropertyValue(key)) continue; - style += key + ":" + value + ";"; - } - - for (const key in compStyle) { - const value = compStyle.getPropertyValue(key); - - if (key === "cursor") continue; // cursor should be default - if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute - if (value === defaultStyles.getPropertyValue(key)) continue; - style += key + ":" + value + ";"; - } - - if (style != "") this.setAttribute("style", style); - }); - - emptyG.remove(); -} - -// get non-standard fonts used for labels to fetch them from web -function getFontsToLoad(clone) { - const webSafe = ["Georgia", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New", "Verdana", "Arial", "Impact"]; // fonts to not fetch - - const fontsInUse = new Set(); // to store fonts currently in use - clone.selectAll("#labels > g").each(function () { - if (!this.hasChildNodes()) return; - const font = this.dataset.font; - if (!font || webSafe.includes(font)) return; - fontsInUse.add(font); - }); - const legendFont = legend.attr("data-font"); - if (legend.node().hasChildNodes() && !webSafe.includes(legendFont)) fontsInUse.add(legendFont); - const fonts = [...fontsInUse]; - return fonts.length ? "https://fonts.googleapis.com/css?family=" + fonts.join("|") : null; -} - -// code from Kaiido's answer https://stackoverflow.com/questions/42402584/how-to-use-google-fonts-in-canvas-when-drawing-dom-objects-in-svg -function GFontToDataURI(url) { - if (!url) return Promise.resolve(); - return fetch(url) // first fecth the embed stylesheet page - .then(resp => resp.text()) // we only need the text of it - .then(text => { - let s = document.createElement("style"); - s.innerHTML = text; - document.head.appendChild(s); - const styleSheet = Array.prototype.filter.call(document.styleSheets, sS => sS.ownerNode === s)[0]; - - const FontRule = rule => { - const src = rule.style.getPropertyValue("src"); - const url = src ? src.split("url(")[1].split(")")[0] : ""; - return {rule, src, url: url.substring(url.length - 1, 1)}; - }; - const fontProms = []; - - for (const r of styleSheet.cssRules) { - let fR = FontRule(r); - if (!fR.url) continue; - - fontProms.push( - fetch(fR.url) // fetch the actual font-file (.woff) - .then(resp => resp.blob()) - .then(blob => { - return new Promise(resolve => { - let f = new FileReader(); - f.onload = e => resolve(f.result); - f.readAsDataURL(blob); - }); - }) - .then(dataURL => fR.rule.cssText.replace(fR.url, dataURL)) - ); - } - document.head.removeChild(s); // clean up - return Promise.all(fontProms); // wait for all this has been done - }); -} - -// prepare map data for saving -function getMapData() { - TIME && console.time("createMapDataBlob"); - - return new Promise(resolve => { - const date = new Date(); - 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, mapId].join("|"); - const settings = [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(options), mapName.value, +hideLabels.checked].join("|"); - const coords = JSON.stringify(mapCoordinates); - const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); - const notesData = JSON.stringify(notes); - const rulersString = rulers.toString(); - - // clone svg - const cloneEl = document.getElementById("map").cloneNode(true); - - // set transform values to default - cloneEl.setAttribute("width", graphWidth); - cloneEl.setAttribute("height", graphHeight); - cloneEl.querySelector("#viewbox").removeAttribute("transform"); - - // always remove rulers - cloneEl.querySelector("#ruler").innerHTML = ""; - - const svg_xml = new XMLSerializer().serializeToString(cloneEl); - - const gridGeneral = JSON.stringify({spacing: grid.spacing, cellsX: grid.cellsX, cellsY: grid.cellsY, boundary: grid.boundary, points: grid.points, features: grid.features}); - const features = JSON.stringify(pack.features); - const cultures = JSON.stringify(pack.cultures); - const states = JSON.stringify(pack.states); - const burgs = JSON.stringify(pack.burgs); - const religions = JSON.stringify(pack.religions); - const provinces = JSON.stringify(pack.provinces); - const rivers = JSON.stringify(pack.rivers); - - // store name array only if it is not the same as default - const defaultNB = Names.getNameBases(); - const namesData = nameBases - .map((b, i) => { - const names = defaultNB[i] && defaultNB[i].b === b.b ? "" : b.b; - return `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${names}`; - }) - .join("/"); - - // round population to save resources - const pop = Array.from(pack.cells.pop).map(p => rn(p, 4)); - - // data format as below - const data = [params, settings, 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, pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state, pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces, namesData, rivers, rulersString].join("\r\n"); - const blob = new Blob([data], {type: "text/plain"}); - - TIME && console.timeEnd("createMapDataBlob"); - resolve(blob); - }); -} - -// Download .map file -async function saveMap() { - if (customization) { - tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); - return; - } - closeDialogs("#alert"); - - const blob = await getMapData(); - const URL = window.URL.createObjectURL(blob); - const link = document.createElement("a"); - link.download = getFileName() + ".map"; - link.href = URL; - link.click(); - tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000); - window.URL.revokeObjectURL(URL); -} - -function saveGeoJSON_Cells() { - const json = {type: "FeatureCollection", features: []}; - const cells = pack.cells; - const getPopulation = i => { - const [r, u] = getCellPopulation(i); - return rn(r + u); - }; - const getHeight = i => parseInt(getFriendlyHeight([cells.p[i][0], cells.p[i][1]])); - - cells.i.forEach(i => { - const coordinates = getCellCoordinates(cells.v[i]); - const height = getHeight(i); - const biome = cells.biome[i]; - const type = pack.features[cells.f[i]].type; - const population = getPopulation(i); - const state = cells.state[i]; - const province = cells.province[i]; - const culture = cells.culture[i]; - const religion = cells.religion[i]; - const neighbors = cells.c[i]; - - const properties = {id: i, height, biome, type, population, state, province, culture, religion, neighbors}; - const feature = {type: "Feature", geometry: {type: "Polygon", coordinates}, properties}; - json.features.push(feature); - }); - - const name = getFileName("Cells") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); -} - -function saveGeoJSON_Routes() { - const json = {type: "FeatureCollection", features: []}; - - routes.selectAll("g > path").each(function () { - const coordinates = getRoutePoints(this); - const id = this.id; - const type = this.parentElement.id; - - const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, type}}; - json.features.push(feature); - }); - - const name = getFileName("Routes") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); -} - -function saveGeoJSON_Rivers() { - const json = {type: "FeatureCollection", features: []}; - - rivers.selectAll("path").each(function () { - const coordinates = getRiverPoints(this); - const id = this.id; - const width = +this.dataset.increment; - const increment = +this.dataset.increment; - const river = pack.rivers.find(r => r.i === +id.slice(5)); - const name = river ? river.name : ""; - const type = river ? river.type : ""; - const i = river ? river.i : ""; - const basin = river ? river.basin : ""; - - const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, i, basin, name, type, width, increment}}; - json.features.push(feature); - }); - - const name = getFileName("Rivers") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); -} - -function saveGeoJSON_Markers() { - const json = {type: "FeatureCollection", features: []}; - - markers.selectAll("use").each(function () { - const coordinates = getQGIScoordinates(this.dataset.x, this.dataset.y); - const id = this.id; - const type = this.dataset.id.substring(1); - const icon = document.getElementById(type).textContent; - const note = notes.length ? notes.find(note => note.id === this.id) : null; - const name = note ? note.name : ""; - const legend = note ? note.legend : ""; - - const feature = {type: "Feature", geometry: {type: "Point", coordinates}, properties: {id, type, icon, name, legend}}; - json.features.push(feature); - }); - - const name = getFileName("Markers") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); -} - -function getCellCoordinates(vertices) { - const p = pack.vertices.p; - const coordinates = vertices.map(n => getQGIScoordinates(p[n][0], p[n][1])); - return [coordinates.concat([coordinates[0]])]; -} - -function getRoutePoints(node) { - let points = []; - const l = node.getTotalLength(); - const increment = l / Math.ceil(l / 2); - for (let i = 0; i <= l; i += increment) { - const p = node.getPointAtLength(i); - points.push(getQGIScoordinates(p.x, p.y)); - } - return points; -} - -function getRiverPoints(node) { - let points = []; - const l = node.getTotalLength() / 2; // half-length - const increment = 0.25; // defines density of points - for (let i = l, c = i; i >= 0; i -= increment, c += increment) { - const p1 = node.getPointAtLength(i); - const p2 = node.getPointAtLength(c); - const [x, y] = getQGIScoordinates((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); - points.push([x, y]); - } - return points; -} - -async function quickSave() { - if (customization) { - tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); - return; - } - const blob = await getMapData(); - if (blob) ldb.set("lastMap", blob); // auto-save map - tip("Map is saved to browser memory. Please also save as .map file to secure progress", true, "success", 2000); -} - function quickLoad() { ldb.get("lastMap", blob => { if (blob) { @@ -580,32 +46,6 @@ function loadMapPrompt(blob) { } } -const saveReminder = function () { - if (localStorage.getItem("noReminder")) return; - const message = ["Please don't forget to save your work as a .map file", "Please remember to save work as a .map file", "Saving in .map format will ensure your data won't be lost in case of issues", "Safety is number one priority. Please save the map", "Don't forget to save your map on a regular basis!", "Just a gentle reminder for you to save the map", "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"]; - - saveReminder.reminder = setInterval(() => { - if (customization) return; - tip(ra(message), true, "warn", 2500); - }, 1e6); - saveReminder.status = 1; -}; - -saveReminder(); - -function toggleSaveReminder() { - if (saveReminder.status) { - tip("Save reminder is turned off. Press CTRL+Q again to re-initiate", true, "warn", 2000); - clearInterval(saveReminder.reminder); - localStorage.setItem("noReminder", true); - saveReminder.status = 0; - } else { - tip("Save reminder is turned on. Press CTRL+Q to turn off", true, "warn", 2000); - localStorage.removeItem("noReminder"); - saveReminder(); - } -} - function uploadMap(file, callback) { uploadMap.timeStart = performance.now(); @@ -1232,8 +672,7 @@ function parseLoadedData(data) { const pattern = document.getElementById("oceanic"); const filter = pattern.firstElementChild.getAttribute("filter"); const href = filter ? "./images/" + filter.replace("url(#", "").replace(")", "") + ".png" : ""; - pattern.innerHTML = ``; - document.getElementById("oceanPattern").setAttribute("opacity", 0.2); + pattern.innerHTML = ``; } })(); diff --git a/modules/save.js b/modules/save.js new file mode 100644 index 00000000..82564489 --- /dev/null +++ b/modules/save.js @@ -0,0 +1,559 @@ +// Functions to save and load the map +"use strict"; + +// download map as SVG +async function saveSVG() { + TIME && console.time("saveSVG"); + const url = await getMapURL("svg"); + const link = document.createElement("a"); + link.download = getFileName() + ".svg"; + link.href = url; + link.click(); + + tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, "success", 5000); + TIME && console.timeEnd("saveSVG"); +} + +// download map as PNG +async function savePNG() { + TIME && console.time("savePNG"); + const url = await getMapURL("png"); + + const link = document.createElement("a"); + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + canvas.width = svgWidth * pngResolutionInput.value; + canvas.height = svgHeight * pngResolutionInput.value; + const img = new Image(); + img.src = url; + + img.onload = function () { + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + link.download = getFileName() + ".png"; + canvas.toBlob(function (blob) { + link.href = window.URL.createObjectURL(blob); + link.click(); + window.setTimeout(function () { + canvas.remove(); + window.URL.revokeObjectURL(link.href); + tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, "success", 5000); + }, 1000); + }); + }; + + TIME && console.timeEnd("savePNG"); +} + +// download map as JPEG +async function saveJPEG() { + TIME && console.time("saveJPEG"); + const url = await getMapURL("png"); + + const canvas = document.createElement("canvas"); + canvas.width = svgWidth * pngResolutionInput.value; + canvas.height = svgHeight * pngResolutionInput.value; + const img = new Image(); + img.src = url; + + img.onload = async function () { + canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height); + const quality = Math.min(rn(1 - pngResolutionInput.value / 20, 2), 0.92); + const URL = await canvas.toDataURL("image/jpeg", quality); + const link = document.createElement("a"); + link.download = getFileName() + ".jpeg"; + link.href = URL; + link.click(); + tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000); + window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000); + }; + + TIME && console.timeEnd("saveJPEG"); +} + +// parse map svg to object url +async function getMapURL(type, subtype) { + const cloneEl = document.getElementById("map").cloneNode(true); // clone svg + cloneEl.id = "fantasyMap"; + document.body.appendChild(cloneEl); + const clone = d3.select(cloneEl); + clone.select("#debug").remove(); + + const cloneDefs = cloneEl.getElementsByTagName("defs")[0]; + const svgDefs = document.getElementById("defElements"); + + const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1; + if (isFirefox && type === "mesh") clone.select("#oceanPattern").remove(); + if (subtype === "globe") clone.select("#scaleBar").remove(); + if (subtype === "noWater") { + clone.select("#oceanBase").attr("opacity", 0); + clone.select("#oceanPattern").attr("opacity", 0); + } + if (type !== "png") { + // reset transform to show the whole map + clone.attr("width", graphWidth).attr("height", graphHeight); + clone.select("#viewbox").attr("transform", null); + } + if (type === "svg") removeUnusedElements(clone); + if (customization && type === "mesh") updateMeshCells(clone); + inlineStyle(clone); + + // remove unused filters + const filters = cloneEl.querySelectorAll("filter"); + for (let i = 0; i < filters.length; i++) { + const id = filters[i].id; + if (cloneEl.querySelector("[filter='url(#" + id + ")']")) continue; + if (cloneEl.getAttribute("filter") === "url(#" + id + ")") continue; + filters[i].remove(); + } + + // remove unused patterns + const patterns = cloneEl.querySelectorAll("pattern"); + for (let i = 0; i < patterns.length; i++) { + const id = patterns[i].id; + if (cloneEl.querySelector("[fill='url(#" + id + ")']")) continue; + patterns[i].remove(); + } + + // remove unused symbols + const symbols = cloneEl.querySelectorAll("symbol"); + for (let i = 0; i < symbols.length; i++) { + const id = symbols[i].id; + if (cloneEl.querySelector("use[*|href='#" + id + "']")) continue; + symbols[i].remove(); + } + + // add displayed emblems + if (layerIsOn("toggleEmblems") && emblems.selectAll("use").size()) { + cloneEl + .getElementById("emblems") + ?.querySelectorAll("use") + .forEach(el => { + const href = el.getAttribute("href") || el.getAttribute("xlink:href"); + if (!href) return; + const emblem = document.getElementById(href.slice(1)); + if (emblem) cloneDefs.append(emblem.cloneNode(true)); + }); + } else { + cloneDefs.querySelector("#defs-emblems")?.remove(); + } + + // replace ocean pattern href to base64 + if (PRODUCTION && cloneEl.getElementById("oceanicPattern")) { + const el = cloneEl.getElementById("oceanicPattern"); + const url = el.getAttribute("href"); + await new Promise(resolve => { + getBase64(url, base64 => { + el.setAttribute("href", base64); + resolve(); + }); + }); + } + + // add relief icons + if (cloneEl.getElementById("terrain")) { + const uniqueElements = new Set(); + const terrainNodes = cloneEl.getElementById("terrain").childNodes; + for (let i = 0; i < terrainNodes.length; i++) { + const href = terrainNodes[i].getAttribute("href") || terrainNodes[i].getAttribute("xlink:href"); + uniqueElements.add(href); + } + + const defsRelief = svgDefs.getElementById("defs-relief"); + for (const terrain of [...uniqueElements]) { + const element = defsRelief.querySelector(terrain); + if (element) cloneDefs.appendChild(element.cloneNode(true)); + } + } + + // add wind rose + if (cloneEl.getElementById("compass")) { + const rose = svgDefs.getElementById("rose"); + if (rose) cloneDefs.appendChild(rose.cloneNode(true)); + } + + // add port icon + if (cloneEl.getElementById("anchors")) { + const anchor = svgDefs.getElementById("icon-anchor"); + if (anchor) cloneDefs.appendChild(anchor.cloneNode(true)); + } + + // add grid pattern + if (cloneEl.getElementById("gridOverlay")?.hasChildNodes()) { + const type = cloneEl.getElementById("gridOverlay").getAttribute("type"); + const pattern = svgDefs.getElementById("pattern_" + type); + if (pattern) cloneDefs.appendChild(pattern.cloneNode(true)); + } + + if (!cloneEl.getElementById("hatching").children.length) cloneEl.getElementById("hatching").remove(); //remove unused hatching group + if (!cloneEl.getElementById("fogging-cont")) cloneEl.getElementById("fog").remove(); //remove unused fog + if (!cloneEl.getElementById("regions")) cloneEl.getElementById("statePaths").remove(); // removed unused statePaths + if (!cloneEl.getElementById("labels")) cloneEl.getElementById("textPaths").remove(); // removed unused textPaths + + // add armies style + if (cloneEl.getElementById("armies")) cloneEl.insertAdjacentHTML("afterbegin", ""); + + const fontStyle = await GFontToDataURI(getFontsToLoad(clone)); // load non-standard fonts + if (fontStyle) clone.select("defs").append("style").text(fontStyle.join("\n")); // add font to style + clone.remove(); + + const serialized = `` + new XMLSerializer().serializeToString(cloneEl); + const blob = new Blob([serialized], {type: "image/svg+xml;charset=utf-8"}); + const url = window.URL.createObjectURL(blob); + window.setTimeout(() => window.URL.revokeObjectURL(url), 5000); + return url; +} + +// remove hidden g elements and g elements without children to make downloaded svg smaller in size +function removeUnusedElements(clone) { + if (!terrain.selectAll("use").size()) clone.select("#defs-relief").remove(); + if (markers.style("display") === "none") clone.select("#defs-markers").remove(); + + for (let empty = 1; empty; ) { + empty = 0; + clone.selectAll("g").each(function () { + if (!this.hasChildNodes() || this.style.display === "none" || this.classList.contains("hidden")) { + empty++; + this.remove(); + } + if (this.hasAttribute("display") && this.style.display === "inline") this.removeAttribute("display"); + }); + } +} + +function updateMeshCells(clone) { + const data = renderOcean.checked ? grid.cells.i : grid.cells.i.filter(i => grid.cells.h[i] >= 20); + const scheme = getColorScheme(); + clone.select("#heights").attr("filter", "url(#blur1)"); + clone + .select("#heights") + .selectAll("polygon") + .data(data) + .join("polygon") + .attr("points", d => getGridPolygon(d)) + .attr("id", d => "cell" + d) + .attr("stroke", d => getColor(grid.cells.h[d], scheme)); +} + +// for each g element get inline style +function inlineStyle(clone) { + const emptyG = clone.append("g").node(); + const defaultStyles = window.getComputedStyle(emptyG); + + clone.selectAll("g, #ruler *, #scaleBar > text").each(function () { + const compStyle = window.getComputedStyle(this); + let style = ""; + + for (let i = 0; i < compStyle.length; i++) { + const key = compStyle[i]; + const value = compStyle.getPropertyValue(key); + + // Firefox mask hack + if (key === "mask-image" && value !== defaultStyles.getPropertyValue(key)) { + style += "mask-image: url('#land');"; + continue; + } + + if (key === "cursor") continue; // cursor should be default + if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute + if (value === defaultStyles.getPropertyValue(key)) continue; + style += key + ":" + value + ";"; + } + + for (const key in compStyle) { + const value = compStyle.getPropertyValue(key); + + if (key === "cursor") continue; // cursor should be default + if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute + if (value === defaultStyles.getPropertyValue(key)) continue; + style += key + ":" + value + ";"; + } + + if (style != "") this.setAttribute("style", style); + }); + + emptyG.remove(); +} + +// get non-standard fonts used for labels to fetch them from web +function getFontsToLoad(clone) { + const webSafe = ["Georgia", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New", "Verdana", "Arial", "Impact"]; // fonts to not fetch + + const fontsInUse = new Set(); // to store fonts currently in use + clone.selectAll("#labels > g").each(function () { + if (!this.hasChildNodes()) return; + const font = this.dataset.font; + if (!font || webSafe.includes(font)) return; + fontsInUse.add(font); + }); + const legendFont = legend.attr("data-font"); + if (legend.node().hasChildNodes() && !webSafe.includes(legendFont)) fontsInUse.add(legendFont); + const fonts = [...fontsInUse]; + return fonts.length ? "https://fonts.googleapis.com/css?family=" + fonts.join("|") : null; +} + +// code from Kaiido's answer https://stackoverflow.com/questions/42402584/how-to-use-google-fonts-in-canvas-when-drawing-dom-objects-in-svg +function GFontToDataURI(url) { + if (!url) return Promise.resolve(); + return fetch(url) // first fecth the embed stylesheet page + .then(resp => resp.text()) // we only need the text of it + .then(text => { + let s = document.createElement("style"); + s.innerHTML = text; + document.head.appendChild(s); + const styleSheet = Array.prototype.filter.call(document.styleSheets, sS => sS.ownerNode === s)[0]; + + const FontRule = rule => { + const src = rule.style.getPropertyValue("src"); + const url = src ? src.split("url(")[1].split(")")[0] : ""; + return {rule, src, url: url.substring(url.length - 1, 1)}; + }; + const fontProms = []; + + for (const r of styleSheet.cssRules) { + let fR = FontRule(r); + if (!fR.url) continue; + + fontProms.push( + fetch(fR.url) // fetch the actual font-file (.woff) + .then(resp => resp.blob()) + .then(blob => { + return new Promise(resolve => { + let f = new FileReader(); + f.onload = e => resolve(f.result); + f.readAsDataURL(blob); + }); + }) + .then(dataURL => fR.rule.cssText.replace(fR.url, dataURL)) + ); + } + document.head.removeChild(s); // clean up + return Promise.all(fontProms); // wait for all this has been done + }); +} + +// prepare map data for saving +function getMapData() { + TIME && console.time("createMapDataBlob"); + + return new Promise(resolve => { + const date = new Date(); + 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, mapId].join("|"); + const settings = [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(options), mapName.value, +hideLabels.checked].join("|"); + const coords = JSON.stringify(mapCoordinates); + const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); + const notesData = JSON.stringify(notes); + const rulersString = rulers.toString(); + + // clone svg + const cloneEl = document.getElementById("map").cloneNode(true); + + // set transform values to default + cloneEl.setAttribute("width", graphWidth); + cloneEl.setAttribute("height", graphHeight); + cloneEl.querySelector("#viewbox").removeAttribute("transform"); + + // always remove rulers + cloneEl.querySelector("#ruler").innerHTML = ""; + + const svg_xml = new XMLSerializer().serializeToString(cloneEl); + + const gridGeneral = JSON.stringify({spacing: grid.spacing, cellsX: grid.cellsX, cellsY: grid.cellsY, boundary: grid.boundary, points: grid.points, features: grid.features}); + const features = JSON.stringify(pack.features); + const cultures = JSON.stringify(pack.cultures); + const states = JSON.stringify(pack.states); + const burgs = JSON.stringify(pack.burgs); + const religions = JSON.stringify(pack.religions); + const provinces = JSON.stringify(pack.provinces); + const rivers = JSON.stringify(pack.rivers); + + // store name array only if it is not the same as default + const defaultNB = Names.getNameBases(); + const namesData = nameBases + .map((b, i) => { + const names = defaultNB[i] && defaultNB[i].b === b.b ? "" : b.b; + return `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${names}`; + }) + .join("/"); + + // round population to save resources + const pop = Array.from(pack.cells.pop).map(p => rn(p, 4)); + + // data format as below + const data = [params, settings, 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, pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state, pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces, namesData, rivers, rulersString].join("\r\n"); + const blob = new Blob([data], {type: "text/plain"}); + + TIME && console.timeEnd("createMapDataBlob"); + resolve(blob); + }); +} + +// Download .map file +async function saveMap() { + if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); + closeDialogs("#alert"); + + const blob = await getMapData(); + const URL = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.download = getFileName() + ".map"; + link.href = URL; + link.click(); + tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000); + window.URL.revokeObjectURL(URL); +} + +function saveGeoJSON_Cells() { + const json = {type: "FeatureCollection", features: []}; + const cells = pack.cells; + const getPopulation = i => { + const [r, u] = getCellPopulation(i); + return rn(r + u); + }; + const getHeight = i => parseInt(getFriendlyHeight([cells.p[i][0], cells.p[i][1]])); + + cells.i.forEach(i => { + const coordinates = getCellCoordinates(cells.v[i]); + const height = getHeight(i); + const biome = cells.biome[i]; + const type = pack.features[cells.f[i]].type; + const population = getPopulation(i); + const state = cells.state[i]; + const province = cells.province[i]; + const culture = cells.culture[i]; + const religion = cells.religion[i]; + const neighbors = cells.c[i]; + + const properties = {id: i, height, biome, type, population, state, province, culture, religion, neighbors}; + const feature = {type: "Feature", geometry: {type: "Polygon", coordinates}, properties}; + json.features.push(feature); + }); + + const name = getFileName("Cells") + ".geojson"; + downloadFile(JSON.stringify(json), name, "application/json"); +} + +function saveGeoJSON_Routes() { + const json = {type: "FeatureCollection", features: []}; + + routes.selectAll("g > path").each(function () { + const coordinates = getRoutePoints(this); + const id = this.id; + const type = this.parentElement.id; + + const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, type}}; + json.features.push(feature); + }); + + const name = getFileName("Routes") + ".geojson"; + downloadFile(JSON.stringify(json), name, "application/json"); +} + +function saveGeoJSON_Rivers() { + const json = {type: "FeatureCollection", features: []}; + + rivers.selectAll("path").each(function () { + const coordinates = getRiverPoints(this); + const id = this.id; + const width = +this.dataset.increment; + const increment = +this.dataset.increment; + const river = pack.rivers.find(r => r.i === +id.slice(5)); + const name = river ? river.name : ""; + const type = river ? river.type : ""; + const i = river ? river.i : ""; + const basin = river ? river.basin : ""; + + const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, i, basin, name, type, width, increment}}; + json.features.push(feature); + }); + + const name = getFileName("Rivers") + ".geojson"; + downloadFile(JSON.stringify(json), name, "application/json"); +} + +function saveGeoJSON_Markers() { + const json = {type: "FeatureCollection", features: []}; + + markers.selectAll("use").each(function () { + const coordinates = getQGIScoordinates(this.dataset.x, this.dataset.y); + const id = this.id; + const type = this.dataset.id.substring(1); + const icon = document.getElementById(type).textContent; + const note = notes.length ? notes.find(note => note.id === this.id) : null; + const name = note ? note.name : ""; + const legend = note ? note.legend : ""; + + const feature = {type: "Feature", geometry: {type: "Point", coordinates}, properties: {id, type, icon, name, legend}}; + json.features.push(feature); + }); + + const name = getFileName("Markers") + ".geojson"; + downloadFile(JSON.stringify(json), name, "application/json"); +} + +function getCellCoordinates(vertices) { + const p = pack.vertices.p; + const coordinates = vertices.map(n => getQGIScoordinates(p[n][0], p[n][1])); + return [coordinates.concat([coordinates[0]])]; +} + +function getRoutePoints(node) { + let points = []; + const l = node.getTotalLength(); + const increment = l / Math.ceil(l / 2); + for (let i = 0; i <= l; i += increment) { + const p = node.getPointAtLength(i); + points.push(getQGIScoordinates(p.x, p.y)); + } + return points; +} + +function getRiverPoints(node) { + let points = []; + const l = node.getTotalLength() / 2; // half-length + const increment = 0.25; // defines density of points + for (let i = l, c = i; i >= 0; i -= increment, c += increment) { + const p1 = node.getPointAtLength(i); + const p2 = node.getPointAtLength(c); + const [x, y] = getQGIScoordinates((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); + points.push([x, y]); + } + return points; +} + +async function quickSave() { + if (customization) { + tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); + return; + } + const blob = await getMapData(); + if (blob) ldb.set("lastMap", blob); // auto-save map + tip("Map is saved to browser memory. Please also save as .map file to secure progress", true, "success", 2000); +} + +const saveReminder = function () { + if (localStorage.getItem("noReminder")) return; + const message = ["Please don't forget to save your work as a .map file", "Please remember to save work as a .map file", "Saving in .map format will ensure your data won't be lost in case of issues", "Safety is number one priority. Please save the map", "Don't forget to save your map on a regular basis!", "Just a gentle reminder for you to save the map", "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"]; + + saveReminder.reminder = setInterval(() => { + if (customization) return; + tip(ra(message), true, "warn", 2500); + }, 1e6); + saveReminder.status = 1; +}; + +saveReminder(); + +function toggleSaveReminder() { + if (saveReminder.status) { + tip("Save reminder is turned off. Press CTRL+Q again to re-initiate", true, "warn", 2000); + clearInterval(saveReminder.reminder); + localStorage.setItem("noReminder", true); + saveReminder.status = 0; + } else { + tip("Save reminder is turned on. Press CTRL+Q to turn off", true, "warn", 2000); + localStorage.removeItem("noReminder"); + saveReminder(); + } +} diff --git a/modules/ui/style.js b/modules/ui/style.js index 2a298f27..2e8bb720 100644 --- a/modules/ui/style.js +++ b/modules/ui/style.js @@ -219,7 +219,7 @@ function selectStyleElement() { styleOcean.style.display = "block"; styleOceanFill.value = styleOceanFillOutput.value = oceanLayers.select("#oceanBase").attr("fill"); styleOceanPattern.value = document.getElementById("oceanicPattern")?.getAttribute("href"); - styleOceanPatternOpacity.value = styleOceanPatternOpacityOutput.value = document.getElementById("oceanPattern").getAttribute("opacity") || 1; + styleOceanPatternOpacity.value = styleOceanPatternOpacityOutput.value = document.getElementById("oceanicPattern").getAttribute("opacity") || 1; outlineLayers.value = oceanLayers.attr("layers"); } @@ -404,7 +404,7 @@ styleOceanPattern.addEventListener("change", function () { }); styleOceanPatternOpacity.addEventListener("input", function () { - document.getElementById("oceanPattern").setAttribute("opacity", this.value); + document.getElementById("oceanicPattern").setAttribute("opacity", this.value); styleOceanPatternOpacityOutput.value = this.value; }); @@ -719,10 +719,10 @@ function fetchTextureURL(url) { } const defaultStyles = { - styleAncient: `{"#map":{"background-color":"#000000","filter":"url(#filter-sepia)","data-filter":"sepia"},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.05,"opacity":0.8,"fill-opacity":0.8,"filter":null},"#biomes":{"opacity":null,"filter":null,"mask":null},"#stateBorders":{"opacity":0.8,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null},"#provinceBorders":{"opacity":0.8,"stroke":"#56566d","stroke-width":0.2,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#d4d4d4","stroke-width":1,"stroke-dasharray":5,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":"translate(80 80) scale(.25)"},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#eee9d7","filter":null},"#markers":{"opacity":null,"rescale":1,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000ff"},"#urban":{"stroke":"#ff0000"},"#freshwater":{"opacity":0.5,"fill":"#a6c1fd","stroke":"#5f799d","stroke-width":0.7,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.5,"stroke":"#1f3846","stroke-width":0.7,"filter":"url(#dropShadow)","auto-filter":1},"#lake_island":{"opacity":1,"stroke":"#7c8eaf","stroke-width":0.35,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":null,"fill":"#5d97bb"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.8,"stroke":"#2e1607","stroke-width":1.23,"stroke-dasharray":3,"stroke-linecap":"inherit","filter":null,"mask":null},"#trails":{"opacity":0.8,"stroke":"#331809","stroke-width":0.5,"stroke-dasharray":"1 2","stroke-linecap":"butt","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#ffffff","stroke-width":0.8,"stroke-dasharray":"1 2","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":""},"#statesHalo":{"opacity":1,"data-width":10,"stroke-width":10},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.8,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#outline)"},"#emblems":{"opacity":0.6,"stroke-width":0.8,"filter":"url(#dropShadow05)"},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{},"#zones":{"opacity":0.6,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null,"mask":null},"#ocean":{"opacity":1},"#oceanLayers":{"filter":"url(#blur5)","layers":"-6,-4,-2"},"#oceanBase":{"fill":"#a7a01f"},"#oceanPattern":{"opacity":0.2},"#oceanicPattern":{"href":"./images/pattern1.png"},"#terrs":{"opacity":null,"scheme":"light","terracing":0,"skip":0,"relax":0,"curve":0,"filter":null,"mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":8,"font-size":8,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #cities":{"opacity":1,"fill":"#fdfab9","fill-opacity":0.7,"size":1,"stroke":"#54251d","stroke-width":0.3,"stroke-dasharray":".3 .4","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#3e3e4b","stroke-width":1.2},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":4,"font-size":4,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #towns":{"opacity":1,"fill":"#fef4d8","fill-opacity":0.7,"size":0.5,"stroke":"#463124","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#30426f","filter":null}}`, - styleGloom: `{"#map":{"background-color":"#000000","filter":null,"data-filter":null},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.3,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":null,"filter":"url(#blur5)","mask":"url(#land)"},"#stateBorders":{"opacity":1,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":""},"#provinceBorders":{"opacity":1,"stroke":"#56566d","stroke-width":0.3,"stroke-dasharray":".7 1","stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":14,"font-size":14,"stroke":"#4a4a4a","stroke-width":1,"stroke-dasharray":6,"stroke-linecap":null,"filter":"","mask":""},"#compass":{"opacity":0.6,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":"translate(100 100) scale(0.3)"},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":1,"filter":null},"#cults":{"opacity":0.7,"stroke":"#777777","stroke-width":1.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#e0e0e0","filter":null},"#markers":{"opacity":0.8,"rescale":1,"filter":"url(#dropShadow05)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000aa"},"#urban":{"stroke":"#9d0000"},"#freshwater":{"opacity":0.5,"fill":"#a6c1fd","stroke":"#5f799d","stroke-width":0.7,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.6,"stroke":"#1f3846","stroke-width":0.7,"filter":"url(#dropShadow)","auto-filter":1},"#lake_island":{"opacity":1,"stroke":"#7c8eaf","stroke-width":0.35,"filter":null},"#terrain":{"opacity":0.9,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":"","fill":"#779582"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":1,"stroke":"#8b4418","stroke-width":0.9,"stroke-dasharray":"2 3","stroke-linecap":"round","filter":"","mask":null},"#trails":{"opacity":1,"stroke":"#844017","stroke-width":0.2,"stroke-dasharray":".5 1","stroke-linecap":"round","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#5e1865","stroke-width":0.6,"stroke-dasharray":"1.2 2.4","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":"url(#dropShadow)"},"#statesHalo":{"opacity":1,"data-width":10.2,"stroke-width":10.2},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":1,"font-size":"11px","fill":"#62001b","fill-opacity":0.3,"stroke":null,"stroke-width":2,"stroke-dasharray":2,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow05)"},"#emblems": {"opacity":0.6,"stroke-width":0.5,"filter":null},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{"x":0,"y":0},"#zones":{"opacity":0.5,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":"url(#dropShadow01)","mask":null},"#ocean":{"opacity":1},"#oceanLayers":{"filter":null,"layers":"-6,-4,-2"},"#oceanBase":{"fill":"#4e6964"},"#oceanPattern":{"opacity":0.2},"#oceanicPattern":{"href":"./images/pattern3.png"},"#terrs":{"opacity":1,"scheme":"bright","terracing":0,"skip":0,"relax":1,"curve":1,"filter":"url(#filter-grayscale)","mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":7,"font-size":7,"data-font":"Bitter","font-family":"Bitter"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":2,"stroke":"#444444","stroke-width":0.25,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":0.8,"fill":"#ffffff","size":4,"stroke":"#3e3e4b","stroke-width":1},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":3,"font-size":3,"data-font":"Bitter","font-family":"Bitter"},"#burgIcons > #towns":{"opacity":0.95,"fill":"#ffffff","fill-opacity":0.7,"size":0.8,"stroke":"#3e3e4b","stroke-width":0.2,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1.6,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#4e4e4e","stroke":"#b5b5b5","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":""},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#1b1423","filter":null}}`, - styleClean: `{"#map":{"background-color":"#000000","filter":null,"data-filter":null},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":0.5,"filter":"url(#blur7)","mask":"url(#land)"},"#stateBorders":{"opacity":0.8,"stroke":"#414141","stroke-width":0.7,"stroke-dasharray":0,"stroke-linecap":"butt","filter":""},"#provinceBorders":{"opacity":0.8,"stroke":"#414141","stroke-width":0.45,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.09,"filter":null,"mask":"url(#land)"},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#414141","stroke-width":0.45,"stroke-dasharray":3,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":null},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#eeedeb","filter":null},"#markers":{"opacity":null,"rescale":null,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0,"fill":"#0080ff","filter":null},"#population":{"opacity":null,"stroke-width":2.58,"stroke-dasharray":0,"stroke-linecap":"butt","filter":"url(#blur3)"},"#rural":{"stroke":"#ff0000"},"#urban":{"stroke":"#800000"},"#freshwater":{"opacity":0.5,"fill":"#aadaff","stroke":"#5f799d","stroke-width":0,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.6,"stroke":"#595959","stroke-width":0.4,"filter":"","auto-filter":0},"#lake_island":{"opacity":0,"stroke":"#7c8eaf","stroke-width":0,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":null,"fill":"#aadaff"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.9,"stroke":"#f6d068","stroke-width":0.7,"stroke-dasharray":0,"stroke-linecap":"inherit","filter":null,"mask":null},"#trails":{"opacity":1,"stroke":"#ffffff","stroke-width":0.25,"stroke-dasharray":"","stroke-linecap":"round","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#4f82c6","stroke-width":0.45,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null,"mask":"url(#water)"},"#regions":{"opacity":0.4,"filter":null},"#statesHalo":{"opacity":0,"data-width":null,"stroke-width":0},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow01)"},"#emblems":{"opacity":1,"stroke-width":1,"filter":null},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{},"#zones":{"opacity":0.7,"stroke":"#ff6262","stroke-width":0,"stroke-dasharray":"","stroke-linecap":"butt","filter":null,"mask":null},"#ocean":{"opacity":null},"#oceanLayers":{"filter":"","layers":"none"},"#oceanBase":{"fill":"#aadaff"},"#oceanPattern":{"opacity":0.2},"#oceanicPattern":{"href":""},"#terrs":{"opacity":0.5,"scheme":"bright","terracing":0,"skip":5,"relax":0,"curve":0,"filter":"","mask":"url(#land)"},"#legend":{"data-size":12.74,"font-size":12.74,"data-font":"Arial","font-family":"Arial","stroke":"#909090","stroke-width":1.13,"stroke-dasharray":0,"stroke-linecap":"round","data-x":98.39,"data-y":12.67,"data-columns":null},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#414141","data-size":7,"font-size":7,"data-font":"Arial","font-family":"Arial"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":1,"stroke":"#3e3e4b","stroke-width":0.24,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#303030","stroke-width":1.7},"#burgLabels > #towns":{"opacity":1,"fill":"#414141","data-size":3,"font-size":3,"data-font":"Arial","font-family":"Arial"},"#burgIcons > #towns":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":0.5,"stroke":"#3e3e4b","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.06},"#labels > #states":{"opacity":1,"fill":"#292929","stroke":"#303030","stroke-width":0,"data-size":10,"font-size":10,"data-font":"Arial","font-family":"Arial","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#414141","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Arial","font-family":"Arial","filter":null},"#fogging":{"opacity":1,"fill":"#ffffff","filter":null}}`, - styleMonochrome: `{"#map":{"background-color":"#000000","filter":"url(#filter-grayscale)","data-filter":"grayscale"},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.3,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":null,"filter":"url(#blur5)","mask":null},"#stateBorders":{"opacity":1,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null},"#provinceBorders":{"opacity":1,"stroke":"#56566d","stroke-width":0.4,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#d4d4d4","stroke-width":1,"stroke-dasharray":5,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":null},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#000000","filter":null},"#markers":{"opacity":null,"rescale":1,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000ff"},"#urban":{"stroke":"#ff0000"},"#freshwater":{"opacity":1,"fill":"#000000","stroke":"#515151","stroke-width":0,"filter":null},"#salt":{"opacity":1,"fill":"#000000","stroke":"#484848","stroke-width":0,"filter":null},"#sinkhole":{"opacity":1,"fill":"#000000","stroke":"#5f5f5f","stroke-width":0.5,"filter":null},"#frozen":{"opacity":1,"fill":"#000000","stroke":"#6f6f6f","stroke-width":0,"filter":null},"#lava":{"opacity":1,"fill":"#000000","stroke":"#5d5d5d","stroke-width":0,"filter":""},"#sea_island":{"opacity":1,"stroke":"#1f3846","stroke-width":0,"filter":"","auto-filter":0},"#lake_island":{"opacity":0,"stroke":"#7c8eaf","stroke-width":0,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":0.2,"filter":"url(#blur1)","fill":"#000000"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.9,"stroke":"#d06324","stroke-width":0.7,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null,"mask":null},"#trails":{"opacity":0.9,"stroke":"#d06324","stroke-width":0.25,"stroke-dasharray":".8 1.6","stroke-linecap":"butt","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#ffffff","stroke-width":0.45,"stroke-dasharray":"1 2","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":null},"#statesHalo":{"opacity":1,"data-width":10,"stroke-width":10},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow05)"},"#texture":{"opacity":1,"filter":null,"mask":"url(#land)"},"#emblems": {"opacity": 0.5,"stroke-width": 0.5,"filter": null},"#textureImage":{},"#zones":{"opacity":0.6,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null,"mask":null},"#ocean":{"opacity":0},"#oceanLayers":{"filter":null,"layers":"none"},"#oceanBase":{"fill":"#000000"},"#oceanPattern":{"opacity":0.2},"#oceanicPattern":{"href":""},"#terrs":{"opacity":1,"scheme":"monochrome","terracing":0,"skip":5,"relax":0,"curve":0,"filter":"url(#blur3)","mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":7,"font-size":7,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":1,"stroke":"#3e3e4b","stroke-width":0.24,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#3e3e4b","stroke-width":1.2},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":4,"font-size":4,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #towns":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":0.5,"stroke":"#3e3e4b","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#30426f","filter":null}}` + styleAncient: `{"#map":{"background-color":"#000000","filter":"url(#filter-sepia)","data-filter":"sepia"},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.05,"opacity":0.8,"fill-opacity":0.8,"filter":null},"#biomes":{"opacity":null,"filter":null,"mask":null},"#stateBorders":{"opacity":0.8,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null},"#provinceBorders":{"opacity":0.8,"stroke":"#56566d","stroke-width":0.2,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#d4d4d4","stroke-width":1,"stroke-dasharray":5,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":"translate(80 80) scale(.25)"},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#eee9d7","filter":null},"#markers":{"opacity":null,"rescale":1,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000ff"},"#urban":{"stroke":"#ff0000"},"#freshwater":{"opacity":0.5,"fill":"#a6c1fd","stroke":"#5f799d","stroke-width":0.7,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.5,"stroke":"#1f3846","stroke-width":0.7,"filter":"url(#dropShadow)","auto-filter":1},"#lake_island":{"opacity":1,"stroke":"#7c8eaf","stroke-width":0.35,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":null,"fill":"#5d97bb"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.8,"stroke":"#2e1607","stroke-width":1.23,"stroke-dasharray":3,"stroke-linecap":"inherit","filter":null,"mask":null},"#trails":{"opacity":0.8,"stroke":"#331809","stroke-width":0.5,"stroke-dasharray":"1 2","stroke-linecap":"butt","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#ffffff","stroke-width":0.8,"stroke-dasharray":"1 2","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":""},"#statesHalo":{"opacity":1,"data-width":10,"stroke-width":10},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.8,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#outline)"},"#emblems":{"opacity":0.6,"stroke-width":0.8,"filter":"url(#dropShadow05)"},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{},"#zones":{"opacity":0.6,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null,"mask":null},"#oceanLayers":{"filter":"url(#blur5)","layers":"-6,-4,-2"},"#oceanBase":{"fill":"#a7a01f"},,"#oceanicPattern":{"href":"./images/pattern1.png", "opacity":0.2},"#terrs":{"opacity":null,"scheme":"light","terracing":0,"skip":0,"relax":0,"curve":0,"filter":null,"mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":8,"font-size":8,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #cities":{"opacity":1,"fill":"#fdfab9","fill-opacity":0.7,"size":1,"stroke":"#54251d","stroke-width":0.3,"stroke-dasharray":".3 .4","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#3e3e4b","stroke-width":1.2},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":4,"font-size":4,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #towns":{"opacity":1,"fill":"#fef4d8","fill-opacity":0.7,"size":0.5,"stroke":"#463124","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#30426f","filter":null}}`, + styleGloom: `{"#map":{"background-color":"#000000","filter":null,"data-filter":null},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.3,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":null,"filter":"url(#blur5)","mask":"url(#land)"},"#stateBorders":{"opacity":1,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":""},"#provinceBorders":{"opacity":1,"stroke":"#56566d","stroke-width":0.3,"stroke-dasharray":".7 1","stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":14,"font-size":14,"stroke":"#4a4a4a","stroke-width":1,"stroke-dasharray":6,"stroke-linecap":null,"filter":"","mask":""},"#compass":{"opacity":0.6,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":"translate(100 100) scale(0.3)"},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":1,"filter":null},"#cults":{"opacity":0.7,"stroke":"#777777","stroke-width":1.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#e0e0e0","filter":null},"#markers":{"opacity":0.8,"rescale":1,"filter":"url(#dropShadow05)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000aa"},"#urban":{"stroke":"#9d0000"},"#freshwater":{"opacity":0.5,"fill":"#a6c1fd","stroke":"#5f799d","stroke-width":0.7,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.6,"stroke":"#1f3846","stroke-width":0.7,"filter":"url(#dropShadow)","auto-filter":1},"#lake_island":{"opacity":1,"stroke":"#7c8eaf","stroke-width":0.35,"filter":null},"#terrain":{"opacity":0.9,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":"","fill":"#779582"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":1,"stroke":"#8b4418","stroke-width":0.9,"stroke-dasharray":"2 3","stroke-linecap":"round","filter":"","mask":null},"#trails":{"opacity":1,"stroke":"#844017","stroke-width":0.2,"stroke-dasharray":".5 1","stroke-linecap":"round","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#5e1865","stroke-width":0.6,"stroke-dasharray":"1.2 2.4","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":"url(#dropShadow)"},"#statesHalo":{"opacity":1,"data-width":10.2,"stroke-width":10.2},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":1,"font-size":"11px","fill":"#62001b","fill-opacity":0.3,"stroke":null,"stroke-width":2,"stroke-dasharray":2,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow05)"},"#emblems": {"opacity":0.6,"stroke-width":0.5,"filter":null},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{"x":0,"y":0},"#zones":{"opacity":0.5,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":"url(#dropShadow01)","mask":null},#oceanLayers":{"filter":null,"layers":"-6,-4,-2"},"#oceanBase":{"fill":"#4e6964"},,"#oceanicPattern":{"href":"./images/pattern3.png", "opacity":0.2},"#terrs":{"opacity":1,"scheme":"bright","terracing":0,"skip":0,"relax":1,"curve":1,"filter":"url(#filter-grayscale)","mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":7,"font-size":7,"data-font":"Bitter","font-family":"Bitter"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":2,"stroke":"#444444","stroke-width":0.25,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":0.8,"fill":"#ffffff","size":4,"stroke":"#3e3e4b","stroke-width":1},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":3,"font-size":3,"data-font":"Bitter","font-family":"Bitter"},"#burgIcons > #towns":{"opacity":0.95,"fill":"#ffffff","fill-opacity":0.7,"size":0.8,"stroke":"#3e3e4b","stroke-width":0.2,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1.6,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#4e4e4e","stroke":"#b5b5b5","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":""},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#1b1423","filter":null}}`, + styleClean: `{"#map":{"background-color":"#000000","filter":null,"data-filter":null},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":0.5,"filter":"url(#blur7)","mask":"url(#land)"},"#stateBorders":{"opacity":0.8,"stroke":"#414141","stroke-width":0.7,"stroke-dasharray":0,"stroke-linecap":"butt","filter":""},"#provinceBorders":{"opacity":0.8,"stroke":"#414141","stroke-width":0.45,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.09,"filter":null,"mask":"url(#land)"},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#414141","stroke-width":0.45,"stroke-dasharray":3,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":null},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#eeedeb","filter":null},"#markers":{"opacity":null,"rescale":null,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0,"fill":"#0080ff","filter":null},"#population":{"opacity":null,"stroke-width":2.58,"stroke-dasharray":0,"stroke-linecap":"butt","filter":"url(#blur3)"},"#rural":{"stroke":"#ff0000"},"#urban":{"stroke":"#800000"},"#freshwater":{"opacity":0.5,"fill":"#aadaff","stroke":"#5f799d","stroke-width":0,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.6,"stroke":"#595959","stroke-width":0.4,"filter":"","auto-filter":0},"#lake_island":{"opacity":0,"stroke":"#7c8eaf","stroke-width":0,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":null,"fill":"#aadaff"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.9,"stroke":"#f6d068","stroke-width":0.7,"stroke-dasharray":0,"stroke-linecap":"inherit","filter":null,"mask":null},"#trails":{"opacity":1,"stroke":"#ffffff","stroke-width":0.25,"stroke-dasharray":"","stroke-linecap":"round","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#4f82c6","stroke-width":0.45,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null,"mask":"url(#water)"},"#regions":{"opacity":0.4,"filter":null},"#statesHalo":{"opacity":0,"data-width":null,"stroke-width":0},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow01)"},"#emblems":{"opacity":1,"stroke-width":1,"filter":null},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{},"#zones":{"opacity":0.7,"stroke":"#ff6262","stroke-width":0,"stroke-dasharray":"","stroke-linecap":"butt","filter":null,"mask":null},"#oceanLayers":{"filter":"","layers":"none"},"#oceanBase":{"fill":"#aadaff"},,"#oceanicPattern":{"href":"", "opacity":0.2},"#terrs":{"opacity":0.5,"scheme":"bright","terracing":0,"skip":5,"relax":0,"curve":0,"filter":"","mask":"url(#land)"},"#legend":{"data-size":12.74,"font-size":12.74,"data-font":"Arial","font-family":"Arial","stroke":"#909090","stroke-width":1.13,"stroke-dasharray":0,"stroke-linecap":"round","data-x":98.39,"data-y":12.67,"data-columns":null},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#414141","data-size":7,"font-size":7,"data-font":"Arial","font-family":"Arial"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":1,"stroke":"#3e3e4b","stroke-width":0.24,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#303030","stroke-width":1.7},"#burgLabels > #towns":{"opacity":1,"fill":"#414141","data-size":3,"font-size":3,"data-font":"Arial","font-family":"Arial"},"#burgIcons > #towns":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":0.5,"stroke":"#3e3e4b","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.06},"#labels > #states":{"opacity":1,"fill":"#292929","stroke":"#303030","stroke-width":0,"data-size":10,"font-size":10,"data-font":"Arial","font-family":"Arial","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#414141","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Arial","font-family":"Arial","filter":null},"#fogging":{"opacity":1,"fill":"#ffffff","filter":null}}`, + styleMonochrome: `{"#map":{"background-color":"#000000","filter":"url(#filter-grayscale)","data-filter":"grayscale"},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.3,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":null,"filter":"url(#blur5)","mask":null},"#stateBorders":{"opacity":1,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null},"#provinceBorders":{"opacity":1,"stroke":"#56566d","stroke-width":0.4,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#d4d4d4","stroke-width":1,"stroke-dasharray":5,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":null},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#000000","filter":null},"#markers":{"opacity":null,"rescale":1,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000ff"},"#urban":{"stroke":"#ff0000"},"#freshwater":{"opacity":1,"fill":"#000000","stroke":"#515151","stroke-width":0,"filter":null},"#salt":{"opacity":1,"fill":"#000000","stroke":"#484848","stroke-width":0,"filter":null},"#sinkhole":{"opacity":1,"fill":"#000000","stroke":"#5f5f5f","stroke-width":0.5,"filter":null},"#frozen":{"opacity":1,"fill":"#000000","stroke":"#6f6f6f","stroke-width":0,"filter":null},"#lava":{"opacity":1,"fill":"#000000","stroke":"#5d5d5d","stroke-width":0,"filter":""},"#sea_island":{"opacity":1,"stroke":"#1f3846","stroke-width":0,"filter":"","auto-filter":0},"#lake_island":{"opacity":0,"stroke":"#7c8eaf","stroke-width":0,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":0.2,"filter":"url(#blur1)","fill":"#000000"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.9,"stroke":"#d06324","stroke-width":0.7,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null,"mask":null},"#trails":{"opacity":0.9,"stroke":"#d06324","stroke-width":0.25,"stroke-dasharray":".8 1.6","stroke-linecap":"butt","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#ffffff","stroke-width":0.45,"stroke-dasharray":"1 2","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":null},"#statesHalo":{"opacity":1,"data-width":10,"stroke-width":10},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow05)"},"#texture":{"opacity":1,"filter":null,"mask":"url(#land)"},"#emblems": {"opacity": 0.5,"stroke-width": 0.5,"filter": null},"#textureImage":{},"#zones":{"opacity":0.6,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null,"mask":null},"#oceanLayers":{"filter":null,"layers":"none"},"#oceanBase":{"fill":"#000000"},,"#oceanicPattern":{"href":"", "opacity":0.2},"#terrs":{"opacity":1,"scheme":"monochrome","terracing":0,"skip":5,"relax":0,"curve":0,"filter":"url(#blur3)","mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":7,"font-size":7,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":1,"stroke":"#3e3e4b","stroke-width":0.24,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#3e3e4b","stroke-width":1.2},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":4,"font-size":4,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #towns":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":0.5,"stroke":"#3e3e4b","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#30426f","filter":null}}` }; // apply default or custom style settings on load @@ -800,8 +800,7 @@ function applyDefaultStyle() { svg.attr("background-color", "#000000").attr("data-filter", null).attr("filter", null); oceanLayers.select("rect").attr("fill", "#466eab"); // old color #53679f oceanLayers.attr("filter", null).attr("layers", "-6,-3,-1"); - oceanPattern.attr("opacity", 0.2); - svg.select("#oceanicPattern").attr("href", "./images/pattern1.png"); + svg.select("#oceanicPattern").attr("href", "./images/pattern1.png").attr("opacity", 0.2); // heightmap style terrs.attr("opacity", null).attr("filter", null).attr("mask", "url(#land)").attr("stroke", "none").attr("scheme", "bright").attr("terracing", 0).attr("skip", 5).attr("relax", 0).attr("curve", 0); @@ -988,8 +987,7 @@ function addStylePreset() { "#zones": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"], "#oceanLayers": ["filter", "layers"], "#oceanBase": ["fill"], - "#oceanPattern": ["opacity"], - "#oceanicPattern": ["href"], + "#oceanicPattern": ["href", "opacity"], "#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"], "#legend": ["data-size", "font-size", "data-font", "font-family", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "data-x", "data-y", "data-columns"], "#legendBox": ["fill", "fill-opacity"],