-
+
@@ -2949,7 +2950,7 @@
-
+
@@ -2964,7 +2965,7 @@
-
+
diff --git a/main.js b/main.js
index 89b3fb15..62127e0e 100644
--- a/main.js
+++ b/main.js
@@ -173,7 +173,7 @@ void function checkLoadParameters() {
function loadMapFromURL(maplink, random) {
const URL = decodeURIComponent(maplink);
-
+
fetch(URL, {method: 'GET', mode: 'cors'})
.then(response => {
if(response.ok) return response.blob();
@@ -188,7 +188,7 @@ function loadMapFromURL(maplink, random) {
function showUploadErrorMessage(error, URL, random) {
console.error(error);
alertMessage.innerHTML = `
- Cannot load map from the
link provided .
+ Cannot load map from the
link provided .
${random?`A new random map is generated. `:''}
Please ensure the linked file is reachable and CORS is allowed on server side`;
$("#alert").dialog({title: "Loading error", width: 320, buttons: {OK: function() {$(this).dialog("close");}}});
@@ -350,7 +350,7 @@ function applyDefaultNamesData() {
["Abim","Adjumani","Alebtong","Amolatar","Amuria","Amuru","Apac","Arua","Arusha","Babati","Baragoi","Bombo","Budaka","Bugembe","Bugiri","Buikwe","Bukedea","Bukoba","Bukomansimbi","Bukungu","Buliisa","Bundibugyo","Bungoma","Busembatya","Bushenyi","Busia","Busia","Busolwe","Butaleja","Butambala","Butere","Buwenge","Buyende","Dadaab","Dodoma","Dokolo","Eldoret","Elegu","Emali","Embu","Entebbe","Garissa","Gede","Gulu","Handeni","Hima","Hoima","Hola","Ibanda","Iganga","Iringa","Isingiro","Isiolo","Jinja","Kaabong","Kabale","Kaberamaido","Kabuyanda","Kabwohe","Kagadi","Kahama","Kajiado","Kakamega","Kakinga","Kakira","Kakiri","Kakuma","Kalangala","Kaliro","Kalisizo","Kalongo","Kalungu","Kampala","Kamuli","Kamwenge","Kanoni","Kanungu","Kapchorwa","Kapenguria","Kasese","Kasulu","Katakwi","Kayunga","Kericho","Keroka","Kiambu","Kibaale","Kibaha","Kibingo","Kiboga","Kibwezi","Kigoma","Kihiihi","Kilifi","Kira","Kiruhura","Kiryandongo","Kisii","Kisoro","Kisumu","Kitale","Kitgum","Kitui","Koboko","Korogwe","Kotido","Kumi","Kyazanga","Kyegegwa","Kyenjojo","Kyotera","Lamu","Langata","Lindi","Lodwar","Lokichoggio","Londiani","Loyangalani","Lugazi","Lukaya","Luweero","Lwakhakha","Lwengo","Lyantonde","Machakos","Mafinga","Makambako","Makindu","Malaba","Malindi","Manafwa","Mandera","Maralal","Marsabit","Masaka","Masindi","MasindiPort","Masulita","Matugga","Mayuge","Mbale","Mbarara","Mbeya","Meru","Mitooma","Mityana","Mombasa","Morogoro","Moroto","Moshi","Moyale","Moyo","Mpanda","Mpigi","Mpondwe","Mtwara","Mubende","Mukono","Mumias","Muranga","Musoma","Mutomo","Mutukula","Mwanza","Nagongera","Nairobi","Naivasha","Nakapiripirit","Nakaseke","Nakasongola","Nakuru","Namanga","Namayingo","Namutumba","Nansana","Nanyuki","Narok","Naromoru","Nebbi","Ngora","Njeru","Njombe","Nkokonjeru","Ntungamo","Nyahururu","Nyeri","Oyam","Pader","Paidha","Pakwach","Pallisa","Rakai","Ruiru","Rukungiri","Rwimi","Sanga","Sembabule","Shimoni","Shinyanga","Singida","Sironko","Songea","Soroti","Ssabagabo","Sumbawanga","Tabora","Takaungu","Tanga","Thika","Tororo","Tunduma","Vihiga","Voi","Wajir","Wakiso","Watamu","Webuye","Wobulenzi","Wote","Wundanyi","Yumbe","Zanzibar"],
["An Khe","An Nhon","Ayun Pa","Ba Don","Ba Ria","Bac Giang","Bac Kan","Bac Lieu","Bac Ninh","Bao Loc","Ben Cat","Ben Tre","Bien Hoa","Bim Son","Binh Long","Binh Minh","Buon Ho","Buon Ma Thuot","Ca Mau","Cai Lay","Cam Pha","Cam Ranh","Can Tho","Cao Bang","Cao Lanh","Chau Doc","Chi Linh","Cua Lo","Da Lat","Da Nang","Di An","Dien Ban","Dien Bien Phu","Dong Ha","Dong Hoi","Dong Trieu","Duyen Hai","Gia Nghia","Gia Rai","Go Cong","Ha Giang","Ha Long","Ha Noi","Ha Tinh","Hai Duong","Hai Phong","Hoa Binh","Hoang Mai","Hoi An","Hong Linh","Hong Ngu","Hue","Hung Yen","Huong Thuy","Huong Tra","Kien Tuong","Kon Tum","Ky Anh","La Gi","Lai Chau","Lang Son","Lao Cai","Long Khanh","Long My","Long Xuyen","Mong Cai","Muong Lay","My Hao","My Tho","Nam Dinh","Nga Bay","Nga Nam","Nghia Lo","Nha Trang","Ninh Binh","Ninh Hoa","Phan Rang Thap Cham","Phan Thiet","Pho Yen","Phu Ly","Phu My","Phu Tho","Phuoc Long","Pleiku","Quang Ngai","Quang Tri","Quang Yen","Quy Nhon","Rach Gia","Sa Dec","Sam Son","Soc Trang","Son La","Son Tay","Song Cau","Song Cong","Tam Diep","Tam Ky","Tan An","Tan Chau","Tan Uyen","Tay Ninh","Thai Binh","Thai Hoa","Thai Nguyen","Thanh Hoa","Thu Dau Mot","Thuan An","Tra Vinh","Tu Son","Tuy Hoa","Tuyen Quang","Uong Bi","Vi Thanh","Viet Tri","Vinh","Vinh Chau","Vinh Long","Vinh Yen","Vung Tau","Yen Bai"],
["Chaiwan", "Chekham", "Cheungshawan", "Chingchung", "Chinghoi", "Chingsen", "Chingshing", "Chiunam", "Chiuon", "Chiuyeung", "Chiyuen", "Choihung", "Chuehoi", "Chuiman", "Chungfa", "Chungfu", "Chungsan", "Chunguktsuen", "Dakhing", "Daopo", "Daumun", "Dingwu", "Dinpak", "Donggun", "Dongyuen", "Duenchau", "Fachau", "Fado", "Fanling", "Fatgong", "Fatshan", "Fotan", "Fuktien", "Fumun", "Funggong", "Funghoi", "Fungshun", "Fungtei", "Gamtin", "Gochau", "Goming", "Gonghoi", "Gongshing", "Goyiu", "Hanghau", "Hangmei", "Hashan", "Hengfachuen", "Hengon", "Heungchau", "Heunggong", "Heungkiu", "Hingning", "Hohfuktong", "Hoichue", "Hoifung", "Hoiping", "Hokong", "Hokshan", "Homantin", "Hotin", "Hoyuen", "Hunghom", "Hungshuikiu", "Jiuling", "Kamping", "Kamsheung", "Kamwan", "Kaulongtong", "Keilun", "Kinon", "Kinsang", "Kityeung", "Kongmun", "Kukgong", "Kwaifong", "Kwaihing", "Kwongchau", "Kwongling", "Kwongming", "Kwuntong", "Laichikok", "Laiking", "Laiwan", "Lamtei", "Lamtin", "Leitung", "Leungking", "Limkong", "Linchau", "Linnam", "Linping", "Linshan", "Loding", "Lokcheong", "Lokfu", "Lokmachau", "Longchuen", "Longgong", "Longmun", "Longping", "Longwa", "Longwu", "Lowu", "Luichau", "Lukfung", "Lukho", "Lungmun", "Macheung", "Maliushui", "Maonshan", "Mauming", "Maunam", "Meifoo", "Mingkum", "Mogong", "Mongkok", "Muichau", "Muigong", "Muiyuen", "Naiwai", "Namcheong", "Namhoi", "Namhong", "Namo", "Namsha", "Namshan", "Nganwai", "Ngchuen", "Ngoumun", "Ngwa", "Nngautaukok", "Onting", "Pakwun", "Paotoishan", "Pingshan", "Pingyuen", "Poklo", "Polam", "Pongon", "Poning", "Potau", "Puito", "Punyue", "Saiwanho", "Saiyingpun", "Samshing", "Samshui", "Samtsen", "Samyuenlei", "Sanfung", "Sanhing", "Sanhui", "Sanwai", "Sanwui", "Seiwui", "Shamshuipo", "Shanmei", "Shantau", "Shatin", "Shatinwai", "Shaukeiwan", "Shauking", "Shekkipmei", "Shekmun", "Shekpai", "Sheungshui", "Shingkui", "Shiuhing", "Shundak", "Shunyi", "Shupinwai", "Simshing", "Siuhei", "Siuhong", "Siukwan", "Siulun", "Suikai", "Taihing", "Taikoo", "Taipo", "Taishuihang", "Taiwai", "Taiwo", "Taiwohau", "Tinhau", "Tinho", "Tinking", "Tinshuiwai", "Tiukengleng", "Toishan", "Tongfong", "Tonglowan", "Tsakyoochung", "Tsamgong", "Tsangshing", "Tseungkwano", "Tsihing", "Tsimshatsui", "Tsinggong", "Tsingshantsuen", "Tsingwun", "Tsingyi", "Tsingyuen", "Tsiuchau", "Tsuenshekshan", "Tsuenwan", "Tuenmun", "Tungchung", "Waichap", "Waichau", "Waidong", "Wailoi", "Waishing", "Waiyeung", "Wanchai", "Wanfau", "Wanon", "Wanshing", "Wingon", "Wongchukhang", "Wongpo", "Wongtaisin", "Woping", "Wukaisha", "Yano", "Yaumatei", "Yauoi", "Yautong", "Yenfa", "Yeungchun", "Yeungdong", "Yeunggong", "Yeungsai", "Yeungshan", "Yimtin", "Yingdak", "Yiuping", "Yongshing", "Yongyuen", "Yuenlong", "Yuenshing", "Yuetsau", "Yuknam", "Yunping", "Yuyuen"],
- ["Adaatsag", "Airag", "Alag Erdene", "Altai", "Altanshiree", "Altantsogts", "Arbulag", "Baatsagaan", "Batnorov", "Batshireet", "Battsengel", "Bayan Adarga", "Bayan Agt", "Bayanbulag", "Bayandalai", "Bayandun", "Bayangovi", "Bayanjargalan", "Bayankhongor", "Bayankhutag", "Bayanlig", "Bayanmonkh", "Bayannuur", "Bayan Ondor", "Bayan Ovoo", "Bayantal", "Bayantsagaan", "Bayantumen", "Bayan Uul", "Bayanzurkh", "Berkh", "Biger", "Binder", "Bogd", "Bombogor", "Bor Ondor", "Bugat", "Bulgan", "Buregkhangai", "Burentogtokh", "Buutsagaan", "Buyant", "Chandmani", "Chandmani Ondor", "Choibalsan", "Chuluunkhoroot", "Chuluut", "Dadal", "Dalanjargalan", "Dalanzadgad", "Darkhan", "Darvi", "Dashbalbar", "Dashinchilen", "Delger", "Delgerekh", "Delgerkhaan", "Delgerkhangai", "Delgertsogt", "Deluun", "Deren", "Dorgon", "Duut", "Erdene", "Erdenebulgan", "Erdeneburen", "Erdenedalai", "Erdenemandal", "Erdenetsogt", "Galshar", "Galt", "Galuut", "Govi Ugtaal", "Gurvan", "Gurvanbulag", "Gurvansaikhan", "Gurvanzagal", "Ikhkhet", "Ikh Tamir", "Ikh Uul", "Jargalan", "Jargalant", "Jargaltkhaan", "Jinst", "Khairkhan", "Khalhgol", "Khaliun", "Khanbogd", "Khangai", "Khangal", "Khankh", "Khankhongor", "Khashaat", "Khatanbulag", "Khatgal", "Kherlen", "Khishig Ondor", "Khokh", "Kholonbuir", "Khongor", "Khotont", "Khovd", "Khovsgol", "Khuld", "Khureemaral", "Khurmen", "Khutag Ondor", "Luus", "Mandakh", "Mandal Ovoo", "Mankhan", "Manlai", "Matad", "Mogod", "Monkhkhairkhan", "Moron", "Most", "Myangad", "Nogoonnuur", "Nomgon", "Norovlin", "Noyon", "Ogii", "Olgii", "Olziit", "Omnodelger", "Ondorkhaan", "Ondorshil", "Ondor Ulaan", "Orgon", "Orkhon", "Rashaant", "Renchinlkhumbe", "Sagsai", "Saikhan", "Saikhandulaan", "Saikhan Ovoo", "Sainshand", "Saintsagaan", "Selenge", "Sergelen", "Sevrei", "Sharga", "Sharyngol", "Shine Ider", "Shinejinst", "Shiveegovi", "Sumber", "Taishir", "Tarialan", "Tariat", "Teshig", "Togrog", "Tolbo", "Tomorbulag", "Tonkhil", "Tosontsengel", "Tsagaandelger", "Tsagaannuur", "Tsagaan Ovoo", "Tsagaan Uur", "Tsakhir", "Tseel", "Tsengel", "Tsenkher", "Tsenkhermandal", "Tsetseg", "Tsetserleg", "Tsogt", "Tsogt Ovoo", "Tsogttsetsii", "Tunel", "Tuvshruulekh", "Ulaanbadrakh", "Ulaankhus", "Ulaan Uul", "Uyench", "Yesonbulag", "Zag", "Zamyn Uud", "Zereg"]
+ ["Adaatsag", "Airag", "Alag Erdene", "Altai", "Altanshiree", "Altantsogts", "Arbulag", "Baatsagaan", "Batnorov", "Batshireet", "Battsengel", "Bayan Adarga", "Bayan Agt", "Bayanbulag", "Bayandalai", "Bayandun", "Bayangovi", "Bayanjargalan", "Bayankhongor", "Bayankhutag", "Bayanlig", "Bayanmonkh", "Bayannuur", "Bayan Ondor", "Bayan Ovoo", "Bayantal", "Bayantsagaan", "Bayantumen", "Bayan Uul", "Bayanzurkh", "Berkh", "Biger", "Binder", "Bogd", "Bombogor", "Bor Ondor", "Bugat", "Bulgan", "Buregkhangai", "Burentogtokh", "Buutsagaan", "Buyant", "Chandmani", "Chandmani Ondor", "Choibalsan", "Chuluunkhoroot", "Chuluut", "Dadal", "Dalanjargalan", "Dalanzadgad", "Darkhan", "Darvi", "Dashbalbar", "Dashinchilen", "Delger", "Delgerekh", "Delgerkhaan", "Delgerkhangai", "Delgertsogt", "Deluun", "Deren", "Dorgon", "Duut", "Erdene", "Erdenebulgan", "Erdeneburen", "Erdenedalai", "Erdenemandal", "Erdenetsogt", "Galshar", "Galt", "Galuut", "Govi Ugtaal", "Gurvan", "Gurvanbulag", "Gurvansaikhan", "Gurvanzagal", "Ikhkhet", "Ikh Tamir", "Ikh Uul", "Jargalan", "Jargalant", "Jargaltkhaan", "Jinst", "Khairkhan", "Khalhgol", "Khaliun", "Khanbogd", "Khangai", "Khangal", "Khankh", "Khankhongor", "Khashaat", "Khatanbulag", "Khatgal", "Kherlen", "Khishig Ondor", "Khokh", "Kholonbuir", "Khongor", "Khotont", "Khovd", "Khovsgol", "Khuld", "Khureemaral", "Khurmen", "Khutag Ondor", "Luus", "Mandakh", "Mandal Ovoo", "Mankhan", "Manlai", "Matad", "Mogod", "Monkhkhairkhan", "Moron", "Most", "Myangad", "Nogoonnuur", "Nomgon", "Norovlin", "Noyon", "Ogii", "Olgii", "Olziit", "Omnodelger", "Ondorkhaan", "Ondorshil", "Ondor Ulaan", "Orgon", "Orkhon", "Rashaant", "Renchinlkhumbe", "Sagsai", "Saikhan", "Saikhandulaan", "Saikhan Ovoo", "Sainshand", "Saintsagaan", "Selenge", "Sergelen", "Sevrei", "Sharga", "Sharyngol", "Shine Ider", "Shinejinst", "Shiveegovi", "Sumber", "Taishir", "Tarialan", "Tariat", "Teshig", "Togrog", "Tolbo", "Tomorbulag", "Tonkhil", "Tosontsengel", "Tsagaandelger", "Tsagaannuur", "Tsagaan Ovoo", "Tsagaan Uur", "Tsakhir", "Tseel", "Tsengel", "Tsenkher", "Tsenkhermandal", "Tsetseg", "Tsetserleg", "Tsogt", "Tsogt Ovoo", "Tsogttsetsii", "Tunel", "Tuvshruulekh", "Ulaanbadrakh", "Ulaankhus", "Ulaan Uul", "Uyench", "Yesonbulag", "Zag", "Zamyn Uud", "Zereg"]
];
}
@@ -441,10 +441,10 @@ function applyDefaultStyle() {
// heightmap style
terrs.attr("opacity", null).attr("filter", null).attr("mask", "url(#land)").attr("stroke", "none");
- const changed = styleHeightmapSchemeInput.value !== "bright" ||
- styleHeightmapTerracingInput.value != 0 ||
- styleHeightmapSkipInput.value != 5 ||
- styleHeightmapSimplificationInput.value != 0 ||
+ const changed = styleHeightmapSchemeInput.value !== "bright" ||
+ styleHeightmapTerracingInput.value != 0 ||
+ styleHeightmapSkipInput.value != 5 ||
+ styleHeightmapSimplificationInput.value != 0 ||
styleHeightmapCurveInput.value != 0;
styleHeightmapSchemeInput.value = "bright";
styleHeightmapTerracingInput.value = styleHeightmapTerracingOutput.value = 0;
@@ -473,7 +473,7 @@ function applyDefaultStyle() {
labels.select("#states").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", stateLabelSize).attr("data-size", stateLabelSize).attr("filter", null);
labels.select("#addedLabels").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 18).attr("data-size", 18).attr("filter", null);
invokeActiveZooming();
-
+
fogging.attr("opacity", .8).attr("fill", "#000000").attr("stroke-width", 5);
}
@@ -481,7 +481,7 @@ function showWelcomeMessage() {
const link = 'https://www.reddit.com/r/FantasyMapGenerator/comments/cxu1c5/update_new_version_is_published_v_10'; // announcement on Reddit
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version
${version} .
- This version is compatible with versions 0.8b and 0.9b, but not with older .map files.
+ This version is compatible with versions 0.8b and 0.9b, but not with older .map files.
Please use an
archived version to open old files.
Main changes:
@@ -504,8 +504,8 @@ function showWelcomeMessage() {
Desktop version (see here)
-
Join our Reddit community and
- Discord server
+
Join our Reddit community and
+ Discord server
to ask questions, share maps, discuss the Generator, report bugs and propose new features.
Thanks for all supporters on Patreon !
`;
@@ -663,17 +663,17 @@ function generate() {
generatePrecipitation();
reGraph();
drawCoastline();
-
+
elevateLakes();
Rivers.generate();
defineBiomes();
-
+
rankCells();
Cultures.generate();
Cultures.expand();
BurgsAndStates.generate();
Religions.generate();
-
+
drawStates();
drawBorders();
BurgsAndStates.drawStateLabels();
@@ -702,7 +702,7 @@ function generate() {
}, position: {my: "center", at: "center", of: "svg"}
});
}
-
+
}
// generate map seed (string!) or get it from URL searchParams
@@ -738,7 +738,7 @@ function calculateVoronoi(graph, points) {
const allPoints = points.concat(grid.boundary);
const delaunay = Delaunator.from(allPoints);
console.timeEnd("calculateDelaunay");
-
+
console.time("calculateVoronoi");
const voronoi = Voronoi(delaunay, allPoints, n);
graph.cells = voronoi.cells;
@@ -892,7 +892,7 @@ function generatePrecipitation() {
// x1 = 70-90 latitude: dry all year (sinking zone)
}
const lalitudeModifier = [4,2,2,2,1,1,2,2,2,2,3,3,2,2,1,1,1,0.5]; // by 5d step
-
+
// difine wind directions based on cells latitude and prevailing winds there
d3.range(0, cells.i.length, cellsX).forEach(function(c, i) {
const lat = mapCoordinates.latN - i / cellsY * mapCoordinates.latT;
@@ -904,7 +904,7 @@ function generatePrecipitation() {
if (winds[tier] > 100 && winds[tier] < 260) northerly++;
else if (winds[tier] > 280 || winds[tier] < 80) southerly++;
});
-
+
// distribute winds by direction
if (westerly.length) passWind(westerly, 120 * modifier, 1, cellsX);
if (easterly.length) passWind(easterly, 120 * modifier, -1, cellsX);
@@ -929,7 +929,7 @@ function generatePrecipitation() {
let humidity = maxPrec - cells.h[first]; // initial water amount
if (humidity <= 0) continue; // if first cell in row is too elevated cosdired wind dry
for (let s = 0, current = first; s < steps; s++, current += next) {
- // no flux on permafrost
+ // no flux on permafrost
if (cells.temp[current] < -5) continue;
// water cell
if (cells.h[current] < 20) {
@@ -1145,7 +1145,7 @@ function reMarkFeatures() {
cellNumber++;
}
if (land && !eLand) {
- cells.t[q] = 1;
+ cells.t[q] = 1;
cells.t[e] = -1;
cells.harbor[q]++;
if (!cells.haven[q]) cells.haven[q] = e;
@@ -1259,7 +1259,7 @@ function addZone() {
const neib = state.neighbors.values().next().value;
const data = cells.i.filter(i => cells.state[i] === state.i && cells.c[i].some(c => cells.state[c] === neib));
-
+
const rebels = rw({Rebels:5, Insurgents:2, Recusants:1, Mutineers:1, Rioters:1, Dissenters:1, Secessionists:1, Insurrection:2, Rebellion:1, Conspiracy:2});
const name = getAdjective(states[neib].name) + " " + rebels;
@@ -1317,7 +1317,7 @@ function addMarkers() {
void function addMines() {
let hills = Array.from(cells.i).filter(i => cells.h[i] > 47 && cells.burg[i]);
let count = !hills.length ? 0 : Math.ceil(hills.length / 7);
- if (!count) return;
+ if (!count) return;
addMarker("mine", "⚒", 50, 50, 20);
const resources = {"salt":5, "gold":2, "silver":4, "copper":2, "iron":3, "lead":1, "tin":1};
@@ -1373,11 +1373,11 @@ function addMarkers() {
let taverns = Array.from(cells.i).filter(i => cells.crossroad[i] && cells.h[i] >= 20 && cells.road[i] > maxRoad);
if (!taverns.length) return;
addMarker("inn", "🍻", 50, 50, 17.5);
-
+
const color = ["Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"];
const animal = ["Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Crane", "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Wolf", "Wolverine", "Camel", "Falcon", "Hound", "Ox"];
const adj = ["New", "Good", "High", "Old", "Great", "Big", "Major", "Happy", "Main", "Huge", "Far", "Beautiful", "Fair", "Prime", "Ancient", "Golden", "Proud", "Lucky", "Fat", "Honest", "Giant", "Distant", "Friendly", "Loud", "Hungry", "Magical", "Superior", "Peaceful", "Frozen", "Divine", "Favorable", "Brave", "Sunny", "Flying"];
-
+
for (let i=0; i < taverns.length && i < 4; i++) {
const cell = taverns.splice(Math.floor(Math.random() * taverns.length), 1);
diff --git a/maps/template_continent.txt b/maps/template_continent.txt
new file mode 100644
index 00000000..b0a610cc
--- /dev/null
+++ b/maps/template_continent.txt
@@ -0,0 +1,11 @@
+Hill 6-12 10-20 10-90 10-90
+Hill 2-5 20-30 25-75 20-80
+Smooth 1 0 0 0
+Range 0-2 30-60 25-75 25-75
+Smooth 2 0 0 0
+Range 1-2 40-50 40-60 40-60
+Multiply 1.2 land 0 0
+Pit 1-3 10-30 30-70 20-80
+Trough 0-2 20-30 15-85 20-80
+Hill 2-4 10-25 15-85 20-80
+Smooth 3 0 0 0
diff --git a/maps/template_squarish.txt b/maps/template_squarish.txt
new file mode 100644
index 00000000..75da0a6e
--- /dev/null
+++ b/maps/template_squarish.txt
@@ -0,0 +1,14 @@
+Hill 2-3 10-25 20-30 20-30
+Hill 1-2 10-25 20-30 70-80
+Hill 2-3 10-25 70-80 20-30
+Hill 1-2 10-25 70-80 70-80
+Hill 6-12 10-20 30-70 30-70
+Smooth 1 0 0 0
+Hill 4-6 10-30 30-70 30-70
+Range 1-2 40-50 20-80 20-40
+Trough 1-2 10-30 15-85 40-80
+Pit 2-4 10-20 15-85 20-80
+Pit 1-2 20-30 25-75 60-80
+Smooth 3 0 0 0
+Range 0-2 40-50 15-85 20-80
+Trough 0-2 40-50 15-85 20-80
diff --git a/maps/template_vertical.txt b/maps/template_vertical.txt
new file mode 100644
index 00000000..f86c7056
--- /dev/null
+++ b/maps/template_vertical.txt
@@ -0,0 +1,13 @@
+Hill 1 90-100 45-55 25-30
+Hill 1 60-80 45-55 70-75
+Hill 1 60-80 30-50 40-50
+Hill 1 40-50 50-70 40-60
+Multiply 0.6 20-100 0 0
+Smooth 1 0 0 0
+Range 0-2 40-50 30-60 30-60
+Trough 1-3 20-40 25-75 20-80
+Smooth 2 0 0 0
+Pit 3-5 10-30 15-85 20-80
+Hill 3-5 10-30 20-80 20-80
+Range 1-2 20-50 30-70 20-65
+Trough 1-2 20-50 15-85 20-80
diff --git a/modules/save-and-load.js b/modules/save-and-load.js
index 682a6c25..54784123 100644
--- a/modules/save-and-load.js
+++ b/modules/save-and-load.js
@@ -1,6 +1,176 @@
// Functions to save and load the map
"use strict";
+// download map data as GeoJSON
+function saveGeoJSON() {
+ saveGeoJSON_Cells();
+ saveGeoJSON_Roads();
+ saveGeoJSON_Rivers();
+}
+
+function saveGeoJSON_Roads() {
+ // this is work-in-progress
+ roads = routes.select("#roads");
+ trails = routes.select("#trails");
+ searoutes = routes.select("#searoutes");
+
+ let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
+
+ routes._groups[0][0].childNodes.forEach(n => {
+ //console.log(n.id);
+ n.childNodes.forEach(r => {
+ data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"LineString\", \"coordinates\": ";
+ data += JSON.stringify(getRoadPoints(r));
+ data += " },\n \"properties\": {\n";
+ data += " \"id\": \""+r.id+"\",\n";
+ data += " \"type\": \""+n.id+"\"\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 = "fmg_routes_" + Date.now() + ".geojson";
+ link.href = url;
+ link.click();
+ window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
+}
+
+function saveGeoJSON_Rivers() {
+ let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
+
+ rivers._groups[0][0].childNodes.forEach(n => {
+ data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"LineString\", \"coordinates\": ";
+ data += JSON.stringify(getRiverPoints(n));
+ data += " },\n \"properties\": {\n";
+ data += " \"id\": \""+n.id+"\",\n";
+ data += " \"width\": \""+n.dataset.width+"\",\n";
+ data += " \"increment\": \""+n.dataset.increment+"\"\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 = "fmg_rivers_" + Date.now() + ".geojson";
+ link.href = url;
+ link.click();
+ window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
+}
+
+function getRoadPoints(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);
+
+ let x = mapCoordinates.lonW + (p.x / graphWidth) * mapCoordinates.lonT;
+ let y = mapCoordinates.latN - (p.y / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
+
+ points.push([x,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);
+
+ let x = mapCoordinates.lonW + (((p1.x+p2.x)/2) / graphWidth) * mapCoordinates.lonT;
+ let y = mapCoordinates.latN - (((p1.y+p2.y)/2) / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
+
+ points.push([x,y]);
+ }
+ return points;
+}
+
+
+function saveGeoJSON_Cells() {
+ 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 = "fmg_cells_" + 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");
@@ -161,23 +331,23 @@ function getMapData() {
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("|");
const notesData = JSON.stringify(notes);
-
+
// set transform values to default
svg.attr("width", graphWidth).attr("height", graphHeight);
const transform = d3.zoomTransform(svg.node());
viewbox.attr("transform", null);
const svg_xml = (new XMLSerializer()).serializeToString(svg.node());
-
+
// restore initial values
svg.attr("width", svgWidth).attr("height", svgHeight);
zoom.transform(svg, transform);
-
+
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);
@@ -185,13 +355,13 @@ function getMapData() {
const burgs = JSON.stringify(pack.burgs);
const religions = JSON.stringify(pack.religions);
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 blob = new Blob([data], {type: "text/plain"});
@@ -239,7 +409,7 @@ function uploadFile(file, callback) {
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}).
The map will be auto-updated. In case of issues please keep using an ${archive} of the Generator`;
}
alertMessage.innerHTML = message;
@@ -256,16 +426,16 @@ function parseLoadedData(data) {
try {
const reliefIcons = document.getElementById("defs-relief").innerHTML; // save relief icons
const hatching = document.getElementById("hatching").cloneNode(true); // save hatching
-
+
void function parseParameters() {
const params = data[0].split("|");
if (params[3]) {seed = params[3]; optionsSeed.value = seed;}
if (params[4]) graphWidth = +params[4];
if (params[5]) graphHeight = +params[5];
}()
-
+
console.group("Loaded Map " + seed);
-
+
void function parseOptions() {
const options = data[1].split("|");
if (options[0]) applyOption(distanceUnitInput, options[0]);
@@ -289,17 +459,17 @@ function parseLoadedData(data) {
if (options[18]) precInput.value = precOutput.value = options[18];
if (options[19]) winds = JSON.parse(options[19]);
}()
-
+
void function parseConfiguration() {
if (data[2]) mapCoordinates = JSON.parse(data[2]);
if (data[4]) notes = JSON.parse(data[4]);
-
+
const biomes = data[3].split("|");
biomesData = applyDefaultBiomesSystem();
biomesData.color = biomes[0].split(",");
biomesData.habitability = biomes[1].split(",").map(h => +h);
biomesData.name = biomes[2].split(",");
-
+
// push custom biomes if any
for (let i=biomesData.i.length; i < biomesData.name.length; i++) {
biomesData.i.push(biomesData.i.length);
@@ -308,12 +478,12 @@ function parseLoadedData(data) {
biomesData.cost.push(50);
}
}()
-
+
void function replaceSVG() {
svg.remove();
document.body.insertAdjacentHTML("afterbegin", data[5]);
}()
-
+
void function redefineElements() {
svg = d3.select("#map");
defs = svg.select("#deftemp");
@@ -364,7 +534,7 @@ function parseLoadedData(data) {
salt = lakes.select("#salt");
burgLabels = labels.select("#burgLabels");
}()
-
+
void function parseGridData() {
grid = JSON.parse(data[6]);
calculateVoronoi(grid, grid.points);
@@ -374,7 +544,7 @@ function parseLoadedData(data) {
grid.cells.t = Int8Array.from(data[10].split(","));
grid.cells.temp = Int8Array.from(data[11].split(","));
}()
-
+
void function parsePackData() {
pack = {};
reGraph();
@@ -385,7 +555,7 @@ function parseLoadedData(data) {
pack.burgs = JSON.parse(data[15]);
pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: "No religion"}];
pack.provinces = data[30] ? JSON.parse(data[30]) : [0];
-
+
const cells = pack.cells;
cells.biome = Uint8Array.from(data[16].split(","));
cells.burg = Uint16Array.from(data[17].split(","));
@@ -401,7 +571,7 @@ function parseLoadedData(data) {
cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(cells.i.length);
cells.crossroad = data[28] ? Uint16Array.from(data[28].split(",")) : new Uint16Array(cells.i.length);
}()
-
+
void function restoreLayersState() {
if (texture.style("display") !== "none" && texture.select("image").size()) turnButtonOn("toggleTexture"); else turnButtonOff("toggleTexture");
if (terrs.selectAll("*").size()) turnButtonOn("toggleHeight"); else turnButtonOff("toggleHeight");
@@ -426,15 +596,15 @@ function parseLoadedData(data) {
if (markers.selectAll("*").size() && markers.style("display") !== "none") turnButtonOn("toggleMarkers"); else turnButtonOff("toggleMarkers");
if (ruler.style("display") !== "none") turnButtonOn("toggleRulers"); else turnButtonOff("toggleRulers");
if (scaleBar.style("display") !== "none") turnButtonOn("toggleScaleBar"); else turnButtonOff("toggleScaleBar");
-
+
// special case for population bars
const populationIsOn = population.selectAll("line").size();
if (populationIsOn) drawPopulation();
if (populationIsOn) turnButtonOn("togglePopulation"); else turnButtonOff("togglePopulation");
-
+
getCurrentPreset();
}()
-
+
void function restoreEvents() {
ruler.selectAll("g").call(d3.drag().on("start", dragRuler));
ruler.selectAll("text").on("click", removeParent);
@@ -443,36 +613,36 @@ function parseLoadedData(data) {
ruler.selectAll("g.ruler rect").call(d3.drag().on("start", rulerCenterDrag));
ruler.selectAll("g.opisometer circle").call(d3.drag().on("start", dragOpisometerEnd));
ruler.selectAll("g.opisometer circle").call(d3.drag().on("start", dragOpisometerEnd));
-
+
scaleBar.on("mousemove", () => tip("Click to open Units Editor"));
legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend());
}()
-
+
void function resolveVersionConflicts() {
const version = parseFloat(data[0].split("|")[0]);
if (version == 0.8) {
// 0.9 has additional relief icons to be included into older maps
document.getElementById("defs-relief").innerHTML = reliefIcons;
}
-
+
if (version < 1) {
// 1.0 adds a new religions layer
relig = viewbox.insert("g", "#terrain").attr("id", "relig");
Religions.generate();
-
+
// 1.0 adds a legend box
legend = svg.append("g").attr("id", "legend");
legend.attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC")
.attr("font-size", 13).attr("data-size", 13).attr("data-x", 99).attr("data-y", 93)
.attr("stroke-width", 2.5).attr("stroke", "#812929").attr("stroke-dasharray", "0 4 10 4").attr("stroke-linecap", "round");
-
+
// 1.0 separated drawBorders fron drawStates()
stateBorders = borders.append("g").attr("id", "stateBorders");
provinceBorders = borders.append("g").attr("id", "provinceBorders");
borders.attr("opacity", null).attr("stroke", null).attr("stroke-width", null).attr("stroke-dasharray", null).attr("stroke-linecap", null).attr("filter", null);
stateBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt");
provinceBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", .5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt");
-
+
// 1.0 adds state relations, provinces, forms and full names
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", .6);
BurgsAndStates.collectStatistics();
@@ -483,57 +653,57 @@ function parseLoadedData(data) {
drawBorders();
if (!layerIsOn("toggleBorders")) $('#borders').fadeOut();
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
-
+
// 1.0 adds hatching
document.getElementsByTagName("defs")[0].appendChild(hatching);
-
+
// 1.0 adds zones layer
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
zones.attr("opacity", .6).attr("stroke", null).attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt");
addZone();
if (!markers.selectAll("*").size()) {addMarkers(); turnButtonOn("toggleMarkers");}
-
+
// 1.0 add fogging layer (state focus)
let fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)")
.append("g").attr("id", "fogging").attr("display", "none");
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%")
.attr("height", "100%").attr("fill", "white");
-
+
// 1.0 changes states opacity bask to regions level
if (statesBody.attr("opacity")) {
regions.attr("opacity", statesBody.attr("opacity"));
statesBody.attr("opacity", null);
}
-
+
// 1.0 changed labels to multi-lined
labels.selectAll("textPath").each(function() {
const text = this.textContent;
const shift = this.getComputedTextLength() / -1.5;
this.innerHTML = `
${text} `;
});
-
+
// 1.0 added new biome - Wetland
biomesData.name.push("Wetland");
biomesData.color.push("#0b9131");
biomesData.habitability.push(12);
}
-
+
if (version == 1) {
// v 1.0 initial code had a bug with religion layer id
if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig");
-
+
// v 1.0 initially has Sympathy status then relaced with Friendly
for (const s of pack.states) {
s.diplomacy = s.diplomacy.map(r => r === "Sympathy" ? "Friendly" : r);
}
}
}()
-
+
changeMapSize();
if (window.restoreDefaultEvents) restoreDefaultEvents();
invokeActiveZooming();
-
+
console.warn(`TOTAL: ${rn((performance.now()-uploadFile.timeStart)/1000,2)}s`);
showStatistics();
console.groupEnd("Loaded Map " + seed);
@@ -635,4 +805,4 @@ function toggleSaveReminder() {
localStorage.removeItem("noReminder");
saveReminder();
}
-}
\ No newline at end of file
+}
diff --git a/modules/ui/burgs-editor.js b/modules/ui/burgs-editor.js
index 30217d11..7358a8db 100644
--- a/modules/ui/burgs-editor.js
+++ b/modules/ui/burgs-editor.js
@@ -252,7 +252,7 @@ function editBurgs() {
}
function downloadBurgsData() {
- let data = "Id,Burg,State,Culture,Population,Capital,Port\n"; // headers
+ let data = "Id,Burg,State,Culture,Population,Capital,Port,Longitude,Latitude,Elevation\n"; // headers
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
valid.forEach(b => {
@@ -262,7 +262,12 @@ function editBurgs() {
data += pack.cultures[b.culture].name + ",";
data += rn(b.population * populationRate.value * urbanization.value) + ",";
data += b.capital ? "capital," : ",";
- data += b.port ? "port\n" : "\n";
+ data += b.port ? "port," : ",";
+
+ // add geography data
+ 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";
});
const dataBlob = new Blob([data], {type: "text/plain"});
@@ -315,7 +320,7 @@ function editBurgs() {
}
});
}
-
+
fileReader.readAsText(fileToLoad, "UTF-8");
}
@@ -339,4 +344,3 @@ function editBurgs() {
}
}
-
diff --git a/modules/ui/options.js b/modules/ui/options.js
index 9dc280ab..cf10282c 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -21,7 +21,7 @@ function showOptions(event) {
regenerate.style.display = "none";
options.style.display = "block";
optionsTrigger.style.display = "none";
-
+
if (event) event.stopPropagation();
}
@@ -49,19 +49,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";
});
@@ -86,7 +86,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";
@@ -98,7 +98,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");
}
@@ -161,7 +161,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";
@@ -169,7 +169,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");
@@ -256,7 +256,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";;
@@ -363,7 +363,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() {
@@ -386,7 +386,7 @@ outlineLayersInput.addEventListener("change", function() {
});
styleReliefSet.addEventListener("change", function() {
- ReliefIcons();
+ ReliefIcons();
if (!layerIsOn("toggleRelief")) toggleRelief();
});
@@ -447,7 +447,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() {
@@ -636,7 +636,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");
@@ -744,7 +744,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();
@@ -766,7 +766,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;
@@ -803,7 +803,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) {
@@ -992,8 +992,9 @@ document.getElementById("sticked").addEventListener("click", function(event) {
else if (id === "saveMap") saveMap();
else if (id === "saveSVG") saveAsImage("svg");
else if (id === "savePNG") saveAsImage("png");
+ else if (id === "saveGeo") saveGeoJSON();
else if (id === "saveDropbox") saveDropbox();
- if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveDropbox") toggleSavePane();
+ if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo" || id === "saveDropbox") toggleSavePane();
if (id === "loadMap") mapToLoad.click();
else if (id === "quickLoad") quickLoad();
else if (id === "loadURL") loadURL();
@@ -1021,7 +1022,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.
Please ensure your browser does not block popups.
Please check browser settings and turn off adBlocker if it is enabled`;
@@ -1059,7 +1060,7 @@ function toggleLoadPane() {
function loadURL() {
const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
- const inner = `Provide URL to a .map file:
+ const inner = `Provide URL to a .map file:
Please note server should allow CORS for file to be loaded. If CORS is not allowed, save file to Dropbox and provide a direct link `;
alertMessage.innerHTML = inner;
@@ -1103,4 +1104,4 @@ document.getElementById("mapToLoad").addEventListener("change", function() {
this.value = "";
closeDialogs();
uploadFile(fileToLoad);
-});
\ No newline at end of file
+});