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