geogen release

This commit is contained in:
Lucas 2019-04-26 14:29:25 +01:00
parent 9aa369b6df
commit edb0cb820d
7 changed files with 2056 additions and 0 deletions

30
.gitignore vendored Normal file
View file

@ -0,0 +1,30 @@
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# =========================
# Operating System Files
# =========================
# =========================
# Editor Temp Files
# =========================
.vs/

View file

@ -1078,6 +1078,8 @@
<button id="saveButton" data-tip="Select file format to save map">Save as</button>
<div id="saveDropdown">
<div id="saveMap" data-tip="Save as fully functional map in .map format. Shortcut: Ctrl + M">.map</div>
<div id="saveOSM" data-tip="Save as open street map format (osmxml). Shortcut: Shift + O')">.osm</div>
<!-- <div id="saveJSON" data-tip="Save as open source json format (geojson) [not great atm]. Shortcut: Shift + J')">.json</div> -->
<div id="saveSVG" data-tip="Download the map as .svg image (open in browser or Inkscape). Shortcut: Ctrl + S">.svg</div>
<div id="savePNG" data-tip="Download visible part of the map as image. Texture will not be shown. Shortcut: Ctrl + P">.png</div>
</div>
@ -2006,6 +2008,7 @@
<script src="libs/polylabel.min.js"></script>
<script src="libs/jquery-ui.min.js"></script>
<script src="libs/seedrandom.min.js"></script>
<script src="libs/NDSFutility.js"></script>
<script defer src="modules/ui/general.js"></script>
<script defer src="modules/ui/options.js"></script>
@ -2032,6 +2035,7 @@
<script defer src="modules/ui/legends-editor.js"></script>
<script defer src="modules/ui/editors.js"></script>
<script defer src="modules/save-and-load.js"></script>
<script defer src="modules/geogen.js"></script>
<script defer src="libs/quantize.min.js"></script>
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
</body>

1164
libs/NDSFutility.js Normal file

File diff suppressed because it is too large Load diff

812
modules/geogen.js Normal file
View file

@ -0,0 +1,812 @@
units = { //units, their name and conversion ratio to Kilometers
"mm": { name: "Millimeters", conv: 1000000 },
"cm": { name: "Centimeters", conv: 100000 },
"m": { name: "Meters", conv: 1000 },
"km": { name: "Kilometers", conv: 1 },
"in": { name: "Inches", conv: 39370.07874 },
"ft": { name: "Feet", conv: 3280.839895 },
"yd": { name: "Yards", conv: 1093.613298 },
"mi": { name: "Miles", conv: 0.621371192 },
"nmi": { name: "Nautical Miles", conv: 0.539956803 },
"lg": { name: "Leagues", conv: 0.207123730 }, //this is just one possible value, meaning of league differs majorly
"vr": { name: "Versta", conv: 0.937382827 },
}
const type_names = {
"continent": ["Acai", "Adroya", "Aehesus", "Aeplaes", "Aetiveth", "Afeutoris", "Ahera", "Ahux", "Aifin", "Aipodux", "Ames", "Areth", "Aseugeon", "Atane", "Aufokica", "Auluqoth", "Aupicul", "Auquan", "Aweapari", "Baeyis",
"Beawath", "Blateron", "Bleumebela", "Blifin", "Braiyias", "Breizoth", "Brezoqall", "Bruanosia", "Buadrea", "Ceiphizuan", "Cewriograi", "Chaqon", "Chialuin", "Ciclone", "Cleikoxoa", "Cleiyezand", "Cleobuxune", "Clipuzoya",
"Clowos", "Cluamisoa", "Clulath", "Codni", "Cowane", "Cracend", "Cuphiashoth", "Cusux", "Deblas", "Dreasuin", "Dreozai", "Edax", "Eduqith", "Eheiqeon", "Ehox", "Eifugias", "Eilolish", "Eiyuwax", "Eizunai", "Ekox", "Eleuvax",
"Eobekaes", "Eodraes", "Eolovin", "Eqoqor", "Eseizela", "Esune", "Etroa", "Eubrax", "Euclax", "Eufuxura", "Eukolaes", "Euweon", "Feusuin", "Fliutazias", "Flucone", "Fuazath", "Geiwis", "Gliuboya", "Grigeucax", "Gruacitane",
"Guashushea", "Helane", "Holira", "Hoqish", "Iapath", "Iawaqul", "Iboth", "Idia", "Ipeth", "Iqall", "Irend", "Iuqenax", "Iuzoa", "Iwor", "Iworis", "Jaepixari", "Jaukroran", "Jichul", "Jubrios", "Kaphox", "Kasuin", "Kerura",
"Klikesh", "Kliocane", "Klumane", "Kraeputul", "Krahonan", "Krainura", "Kreomixias", "Krereudas", "Krifera", "Kuadox", "Kuyiocroa", "Limux", "Lopall", "Miozegrera", "Nauphuan", "Niudrica", "Nodeayuin", "Nophea", "Nuawasoa",
"Odon", "Odrus", "Ogane", "Okaecox", "Oqai", "Osesh", "Osune", "Oyune", "Peoclosend", "Phuwuan", "Pleawos", "Pleidrand", "Prerai", "Priulone", "Qeawagaes", "Qeiqastrea", "Qephiasura", "Qiagreon", "Qiopath", "Reyath", "Riaploa",
"Sainacas", "Sateron", "Serihoya", "Shonios", "Shuaditish", "Shuyun", "Slaedreth", "Slaeqabios", "Slicen", "Sliperon", "Sluraetuan", "Slutax", "Suchos", "Tegreinane", "Teophiduin", "Tibrura", "Tiotavoth", "Tizeudia",
"Trepeocai", "Treutoya", "Triqos", "Uabroris", "Uahakia", "Uawiwios", "Ubenios", "Uhios", "Upiozan", "Urai", "Uwesux", "Uwush", "Vledath", "Vleowiyush", "Vlifica", "Vragrux", "Vraikequth", "Vuyiagren", "Waiqesh", "Wetrune",
"Witrari", "Wreikucix", "Wrugith", "Xearutuin", "Xidren", "Xohix", "Xualuvax", "Yaqox", "Yegos", "Yeustror", "Yucrari", "Zaeshela", "Zaiplecaes", "Zeamuin", "Zephush"],
"isle": ["Amtara Reef", "Balcaster Island", "Belleby Enclave", "Berksea Island", "Beversby Isle", "Birmingtague Ait", "Birtown Key", "Bolnach Haven", "Brightside Ait", "Brismark Islet", "Briswater Islet",
"Brommer Chain", "Calenigan Isle", "Campbo Skerry", "Camptague Island", "Canterliers Enclave", "Cartwater Peninsula", "Casneau Holm", "Cauborough Islands", "Chiboubron Haven", "Chide Islands", "Clarenside Chain",
"Cliffsomin Ait", "Cumberchester Chain", "Derwin Isles", "Digwood Cay", "Eastgue Isles", "Eatowe Atoll", "Elcola Key", "Emgough Key", "Fairdown Enclave", "Gamsons Isle", "Ganmond Haven", "Gatileche Isle",
"Gilminster Holm", "Glenheim Archipelago", "Gracewaki Reef", "Granhead Isles", "Hadway Peninsula", "Hamliers Reef", "Haspids Enclave", "Hillsline Cay", "Hingchester Isle", "Irochill Islet", "Irridown Ait",
"Killinggamau Cay", "Killingside Cay", "Kirlet Island", "Langbiens Islet", "Lashcana Island", "Leamingshaw Cay", "Limingnear Chain", "Menbour Atoll", "Milburns Refuge", "Miniris Cay", "Morintane Isle", "Petasack Holm",
"Portgeo Archipelago", "Portterel Island", "Reidby Isles", "Reidfail Isles", "Repenmark Island", "Robmont Island", "Saglodge Holm", "Salisgeo Reef", "Sedgeree Skerry", "Shawris Archipelago", "Stafcouche Cay",
"Staflams Skerry", "Stokemiota Refuge", "Susduff Ait", "Susleche Refuge", "Taunnet Islet", "The Arching Isle", "The Arid Islands", "The Burning Isles", "The Castaway Islet", "The Colossal Key", "The Deep Skerry",
"The Defeated Haven", "The Diamond Isle", "The Distant Ait", "The Dread Atoll", "The Ethereal Enclave", "The Faraway Enclave", "The Fiery Holm", "The Fiery Island", "The Flowing Isles", "The Frozen Peninsula",
"The Glowing Islet", "The Grim Reef", "The Heartless Haven", "The Hollow Islet", "The Jellyfish Holm", "The Laughing Ait", "The Light Chain", "The Lost Enclave", "The Mysterious Archipelago", "The Mysterious Key",
"The Pain Key", "The Peaceful Haven", "The Pearl Chain", "The Penguin Refuge", "The Raging Archipelago", "The Relentless Chain", "The Sad Peninsula", "The Sanctum Chain", "The Shadowed Ait", "The Shaking Isles",
"The Shark Enclave", "The Shrine Cay", "The Silver Peninsula", "The Skeleton Ait", "The Skeleton Cay", "The Skeleton Refuge", "The Starfish Haven", "The Sunny Isles", "The Torpedo Island", "The Virgin Skerry",
"The Wasting Ait", "The Waterless Skerry", "The Waveless Isle", "The Windy Island", "The Yelling Ait", "Tisliers Island", "Turgonie Chain", "Ventmiota Isles", "Virworth Peninsula", "Wallsevain Refuge", "Wilwin Reef",
"Windminster Islet", "Wynrial Chain"],
"island": ["Lighthill", "Strongmill", "Whitebridge", "Woodwall", "Deepsnow", "Irondell", "Eriwynne", "Fairwinter", "Deepwater", "Waterton", "Fallmage", "Rosedell", "Westerdragon", "Oldfield", "Morton", "Ironshade", "Merridale",
"Westcliff", "Flowercliff", "Vertkeep", "Clearmont", "Witchlyn", "Deerdell", "Hollowcastle", "Janwynne", "Clearden", "Oldburn", "Flowermeadow", "Linmoor", "Westerbeach", "Deepwolf", "Oakgate", "Southgrass", "Moormeadow",
"Qauar", "Jipon", "Venela", "Martique", "Ugada", "Maurania", "Turnistan", "Russip", "Conada", "Svalbard", "Jan Mayen", "Mari Island", "Angua", "Baruda", "Monolia", "Ameri", "Samoa"],
"freshwater": ["Shaded Lake", "Vast Expanse", "Neglected Gorge", "Glistening Loch", "Blythedows Lake", "Chamterre Lake", "Winterlan Gorge", "Wadelem Loch", "Ridgelis Expanse", "Bradbron Pond", "Calm Lake", "Mirrored Waters",
"Unstable Basin", "Barren Reservoir", "Buchstino Lagoon", "Ferquet Expanse", "Cardpids Reservoir", "Gatilin Cove", "Bloomslet Expanse", "Antitos Cove", "Western Waters", "Pleasant Depths", "Vast Reservoir",
"Peaceful Reservoir", "Harvern Basin", "Limingmeda Depths", "Wellingronto Lagoon", "Causahead Gorge", "Warming Reservoir", "Limingside Reservoir", "New Basin", "Cursed Basin", "Crystal Waters", "Ugly Lagoon",
"Croyville Pond", "Landare Lake", "Franram Loch", "Hillsval Domain", "Smithrial Basin", "Suntrie Gorge", "Serene Lagoon", "Cobalt Loch", "Arrowhead Lake", "Iris Lagoon", "Manirood Shallows", "Appleware Shallows",
"Buckinglam Basin", "Amespids Waters", "Gaulden Pond", "Margar Lake", "Coral Depths", "Boiling Gorge", "Peaceful Waters", "Flowing Shallows", "Walhurst Domain", "Wolffail Basin", "Stratsevain Lagoon", "Davellin Lake",
"Morinneau Cove", "Huntbalt Expanse"],
"salt": ["Gray Domain", "Wasor Basin", "Orocastle Lake", "Cowantrie Shallows", "Belldare Waters", "Bruderley Gorge", "Eckpond Cove", "Eastern Cove", "Coral Pond", "Shaded Waters", "Crocodile Depths", "Cottlecam Lake",
"Midalants Lake", "Onofolk Reservoir", "Herewin Depths", "Grimrial Waters", "Sherden Lagoon", "Wasteful Shallows", "Cursed Reservoir", "Uncanny Pond", "Wrinkled Cove", "Rossoll Lagoon", "Huntingpond Basin",
"Grandburn Waters", "Barnrey Domain", "Stokeleche Pond", "Kapusgus Cove", "Furthest Gorge", "Hungry Expanse", "Cursed Domain", "Narrow Depths", "Corngan Domain", "Estou Pond", "Bridgediac Loch", "Liverbiens Gorge",
"Burstry Waters", "Caubiens Waters"]
}
const natural_types =
{
land: {
area: [
"scrub", "heath", "moor", "wetland", "grassland", "fell", "bare_rock", "scree", "shingle", "sand", "mud", "cave_entrance", "sinkhole", "rock", "hill", "valley", "peninsula"
], features: [
"wood", "tree_row", "tree"
], points: [
"volcano", "cape", "peak", "spring", "hot_spring", "geyser", "stone ", "saddle", "tree", "cave_entrance", "sinkhole", "rock"
], lines: [
"river_terrace", "ridge", "arete", "cliff"
]
}, water: {
area: [
"water", "glacier", "bay", "strait", "beach", "reef"
], lines: [
"coastline"
]
}
}
burgs_groups = [
{ type: "city", pop: 100000 },
{ type: "town", pop: 2000 },
{ type: "village", pop: 100 },
{ type: "hamlet", pop: 10 },
{ type: "isolated_dwelling", pop: -1 }
]
numRegExp = new RegExp("[0-9]+");
const ln = "\r\n";
const cen_lat = 51.50265085;
const cen_lon = -3.16268235;
const rotation_angle_degs = 1;
// using the data on
// https://en.wikipedia.org/wiki/List_of_United_States_cities_by_area
// the average of the square meter per person in city cant to 46.4906806
avg_sqm_pp = 46.5;
function gen_geodata(type) {
let data = "";
let ext = "";
let dset = {};
//work out the scale in the units chosen by using the conversion ratio times by 1000 for meters
const scale = (distanceScale.value * unit_to_km(distanceUnit.value)) * 1000;
console.log(scale + " meters per pixel");
dset.settlements = dataget_settlements();
dset.points = dataget_points();
dset.states = dataget_states();
dset.coastlines = dataget_bodies("#coastline > *");
dset.lakes = dataget_bodies("#freshwater > *, #salt > *");
if (type === "osm") {
data = gen_osm(dset, scale);
ext = ".osm";
} else if (type === "json") {
data = gen_json(dset, scale);
ext = ".geo.json";
}
return { data: data, ext: ext }
}
function dataget_settlements() {
let settlements = [];
for (let i = 0, c = 0; i < pack.burgs.length; i++) {
let burg = pack.burgs[i];
let type = "";
if ("i" in burg) {
settlements[c] = burg;
//replace population with actual value, scale * 1000
pop = settlements[c].population * populationRate.value * 100;
settlements[c].population = pop;
for (let t = 0; t < burgs_groups.length; t++) {
if (pop > burgs_groups[t].pop) {
type = burgs_groups[t].type;
break;
}
}
settlements[c].type = type;
burgs_groups
c++;
}
}
return settlements;
}
function dataget_points() {
let points = [];
for (let i = 0, c = 0; i < grid.points.length; i++) {
let point = grid.points[i];
if (point.length > 0) {
points[c] = point;
c++;
}
}
return points;
}
function dataget_states() {
statesCollectStatistics();
const node_states = document.getElementById("statesHalo").childNodes;
let states = [];
for (var i = 0; i < node_states.length; i++) {
states[i] = pack.states[i];
if (states[i].center) {
states[i].x = pack.cells.p[pack.states[i].center][0];
states[i].y = pack.cells.p[pack.states[i].center][1];
}
states[i].path = node_states[i];
}
return states;
function statesCollectStatistics() {
const cells = pack.cells, states = pack.states;
states.forEach(s => s.cells = s.area = s.burgs = s.rural = s.urban = 0);
for (const i of cells.i) {
if (cells.h[i] < 20) continue;
const s = cells.state[i];
states[s].cells += 1;
states[s].area += cells.area[i];
states[s].rural += cells.pop[i];
if (cells.burg[i]) {
states[s].urban += pack.burgs[cells.burg[i]].population;
states[s].burgs++;
}
}
}
}
function dataget_bodies(query) {
let nodes = document.querySelectorAll(query);
let bodies = [];
for (var i = 0; i < nodes.length; i++) {
let match = nodes[i].id.match(numRegExp);
let f_id = Number(match[0]);
if (f_id in pack.features) {
bodies[i] = pack.features[f_id];
if (type_names[bodies[i].group] === undefined) {
console.log(bodies[i]);
}
bodies["name"] = type_names[bodies[i].group][i];
bodies[i].path = nodes[i];
} else {
console.log("no feature by with id: " + f_id);
}
}
return bodies;
}
function gen_osm(data, scale) {
let cur_id = 1;
let mpoints = osm_settlements_to_features(data.settlements, cur_id, scale);
cur_id = mpoints.last_id;
let spoints = osm_states_to_features(data.states, cur_id, scale);
cur_id = spoints.last_id;
let cpoints = osm_coastlines_to_features(data.coastlines, cur_id, scale);
cur_id = cpoints.last_id;
let lpoints = osm_lakes_to_features(data.lakes, cur_id, scale);
cur_id = lpoints.last_id;
let osm = "<?xml version='1.0' encoding='UTF-8'?>" + ln;
osm += "<osm version='0.6' generator='JOSM'>" + ln;
osm += mpoints.nodes;
osm += spoints.nodes;
osm += cpoints.nodes;
osm += lpoints.nodes;
osm += mpoints.ways
osm += spoints.ways;
osm += cpoints.ways;
osm += lpoints.ways;
osm += mpoints.relations
osm += spoints.relations;
osm += cpoints.relations;
osm += lpoints.relations;
osm += "</osm>";
return osm;
}
function gen_json(data, scale) {
let cur_id = 1;
let features = [];
//fpoints = json_points_to_features(data.points, cur_id, scale);
//cur_id = fpoints.last_id;
let mpoints = json_settlements_to_features(data.settlements, cur_id, scale);
cur_id = mpoints.last_id;
let spoints = json_states_to_features(data.states, cur_id, scale);
cur_id = spoints.last_id;
let cpoints = json_coastlines_to_features(data.coastlines, cur_id, scale);
cur_id = cpoints.last_id;
let lpoints = json_lakes_to_features(data.lakes, cur_id, scale);
cur_id = lpoints.last_id;
//features = features.concat(fpoints.json);
features = features.concat(mpoints.json);
features = features.concat(spoints.json);
features = features.concat(cpoints.json);
features = features.concat(lpoints.json);
const json_data = { "type": "FeatureCollection", "features": features };
return JSON.stringify(json_data, null, 2);
}
//--------------osm functions-------------------
function osm_settlements_to_features(data, id, scale) {
let nodes = "";
let ways = "";
let relations = "";
let tagcats = {};
for (let i = 0; i < data.length; i++) {
let members = [];
id++;
boundry = xy_to_boundry(data[i].x, data[i].y, data[i].population, scale);
start_id = id;
nodes += latlongs_to_nodes(boundry, id);
id += boundry.length + 1;
tagcats = {
type: "boundry", boundary: "administrative", admin_level: "6",
name: data[i].name
};
tags = gen_tags(tagcats);
ways += gen_way(id, start_id, boundry.length, tags);
members.push({ id: id, type: "way", role: "outer" });
id += boundry.length + 1;
start_id = id;
tagcats = {
type: "place", place: data[i].type, name: data[i].name,
cell: data[i].cell, region: data[i].region, area: (Math.PI * data.population ^ 2), culture: data[i].culture,
population: data[i].population, feature: data[i].feature,
capital: data[i].capital, port: data[i].port, settlement_id: data[i].i, x: data[i].x, y: data[i].y
};
tags = gen_tags(tagcats);
nodes += latlong_to_node(xy_to_coords(data[i].x * scale, data[i].y * scale), id, tags);
members.push({ id: id, type: "node", role: "admin_centre" });
id++;
tagcats = {
type: "boundry", boundary: "administrative", border_type: "city", admin_level: "6", designation: "principal_area",
name: data[i].name, long_name: data[i].long_name, "is_in:country": data[i].country, "is_in:country_code": data[i].country_code,
cell: data[i].cell, region: data[i].region, area: (Math.PI * data.population ^ 2), culture: data[i].culture,
population: data[i].population, feature: data[i].feature,
capital: data[i].capital, port: data[i].port, settlement_id: data[i].i, x: data[i].x, y: data[i].y
};
tags = gen_tags(tagcats);
relations += gen_relation(id++, tags, members);
}
return { nodes: nodes, ways: ways, relations: relations, last_id: id };
}
function osm_states_to_features(data, id, scale) {
let nodes = "";
let ways = "";
let relations = "";
let tagcats = {};
let tags = "";
for (let i = 0; i < data.length; i++) {
let boundries = flatten_multi_svg(data[i].path, 1000);
let members = [];
for (let b = 0; b < boundries.length; b++) {
if (boundries[b].length > 0) {
id++;
start_id = id;
tagcats = {
type: "place", place: "region",
region_id: b
};
let region_tags = gen_tags(tagcats);
nodes += latlongs_to_nodes(xypath_to_latlong(boundries[b], scale), id);
id += boundries[b].length + 1;
members.push({ id: id, type: "way", role: "outer" });
ways += gen_way(id, start_id, boundries[b].length, region_tags);
}
}
if (data[i].center) {
id++;
tagcats = {
type: "place", place: "country", name: data[i].name,
cell: data[i].cell, region: data[i].region, area: (Math.PI * data.population ^ 2), culture: data[i].culture,
x: data[i].x, y: data[i].y, center: data[i].center, state_id: data[i].i
};
tags = gen_tags(tagcats);
nodes += latlong_to_node(xy_to_coords(data[i].x * scale, data[i].y * scale), id, tags);
members.push({ id: id, type: "node", role: "admin_centre" });
id++;
}
tagcats = {
type: "multipolygon", boundary: "administrative", border_type: "city", admin_level: "6", designation: "principal_area",
name: data[i].name, long_name: data[i].long_name, "country": data[i].name, "country_code": data[i].i,
state_id: data[i].i, color: data[i].color, expansionism: data[i].expansionism, capital: data[i].capital,
state_type: data[i].type, center: data[i].center, culture: data[i].culture, cells: data[i].cells, area: data[i].area,
rural: data[i].rural, urban: data[i].urban, cities: data[i].burgs, x: data[i].x, y: data[i].y
};
tags = gen_tags(tagcats);
relations += gen_relation(id++, tags, members)
}
return { nodes: nodes, ways: ways, relations: relations, last_id: id };
}
function osm_coastlines_to_features(data, id, scale) {
let nodes = "";
let ways = "";
let relations = "";
let tagcats = {};
for (let i = 0; i < data.length; i++) {
let members = [];
id++;
boundry = xypath_to_latlong(flatten_svg(data[i].path, 1000), scale);
start_id = id;
nodes += latlongs_to_nodes(boundry, id);
id += boundry.length + 1;
tagcats = {
type: "natural", natural: "coastline", feature_id: data[i].i
};
tags = gen_tags(tagcats);
ways += gen_way(id, start_id, boundry.length, tags);
members.push({ id: id, type: "way", role: "outer" });
id++
let type = natural_types.land.area[Math.floor((Math.random() * natural_types.land.area.length))];
tagcats = {
type: "natural", natural: type, feature_id: data[i].i
};
tags = gen_tags(tagcats);
ways += gen_way(id, start_id, boundry.length, tags);
members.push({ id: id, type: "way", role: "outer" });
let name = type_names[data[i].group][i];
tagcats = {
type: "place", place: data[i].group,
feature_id: data[i].i, name: name, body: data[i].body, border: data[i].border,
cells: data[i].cells, land: data[i].land, mtype: data[i].type
};
tags = gen_tags(tagcats);
relations += gen_relation(id++, tags, members);
}
return { nodes: nodes, ways: ways, relations: relations, last_id: id };
}
function osm_lakes_to_features(data, id, scale) {
let nodes = "";
let ways = "";
let relations = "";
let tagcats = {};
for (let i = 0; i < data.length; i++) {
id++;
boundry = xypath_to_latlong(flatten_svg(data[i].path, 1000), scale);
start_id = id;
nodes += latlongs_to_nodes(boundry, id);
id += boundry.length + 1;
let name = type_names[data[i].group][i];
tagcats = {
type: "natural", natural: "water", water: "lake",
name: name, body: data[i].body, border: data[i].border,
cells: data[i].cells, land: data[i].land, mtype: data[i].mtype,
feature_id: data[i].i
};
if (data.group === "salt") {
tagcats.salt = "yes";
}
tags = gen_tags(tagcats);
ways += gen_way(id, start_id, boundry.length, tags);
id++;
}
return { nodes: nodes, ways: ways, relations: relations, last_id: id };
}
//--------------osm functions-------------------
function gen_way(way_id, start_id, num, tags) {
way = "\t<way id='-" + way_id.toString() + "' action='modify' visible='true'>" + ln;
way += gen_nd(start_id, num);
way += tags;
way += "\t</way >" + ln;
return way;
}
function gen_relation(id, tags, members, tagcats) {
relation = "";
relation += "\t<relation id='-" + (id).toString() + "' action='modify' visible='true'>" + ln;
relation += gen_members(members);
relation += tags;
relation += "\t</relation>" + ln;
return relation;
}
function gen_members(ids) {
members = "";
for (i = 0; i < ids.length; ++i) {
members += "\t\t<member type='" + ids[i].type + "' ref='-" + (ids[i].id).toString() + "' role='" + ids[i].role + "' />" + ln;
}
return members;
}
function gen_nd(id, num) {
last_nd = "\t\t<nd ref='-" + (id).toString() + "' />" + ln;
nd = "";
for (i = 0; i < num; ++i) {
nd += "\t\t<nd ref='-" + (id++).toString() + "' />" + ln;
}
nd += last_nd;
return nd;
}
function latlongs_to_nodes(latlongs, id) {
nodes = "";
for (k in latlongs) {
ll = latlongs[k];
nodes += "\t<node id='-" + (id++).toString() + "' action='modify' visible='true' lat='" + (ll.slat).toString() + "' lon='" + (ll.slon).toString() + "' />" + ln;
}
return nodes;
}
function latlong_to_node(ll, id, tags) {
node = "";
node += "\t<node id='-" + (id++).toString() + "' action='modify' visible='true' lat='" + (ll.slat).toString() + "' lon='" + (ll.slon).toString() + "'>" + ln;
node += tags;
node += "\t</node>" + ln;
return node;
}
function gen_tags(tagcats) {
let tags = "";
const blk = "Unimplemented";
for (k in tagcats) {
tags += "\t\t<tag k='" + k + "' v='" + tagcats[k] + "' />" + ln;
}
//tags += "\t\t<tag k='wikipedia' v='" + blk + "' />" + ln;
//tags += "\t\t<tag k='url' v='" + blk + "' />" + ln;
return tags;
}
//--------------json functions-------------------
function json_settlements_to_features(data, id, scale) {
allPoints = [];
for (let i = 0; i < data.length; i++) {
id++;
pop = data[i].population;
let bound = xy_to_boundry(data[i].x, data[i].y, pop, scale);
let polygon = latlongs_to_polygon(bound);
allPoints[i] = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [polygon]
},
"properties": {
"name": data[i].name, "settlement_id": data[i].i, "feature": data[i].feature, "capital": data[i].capital, "port": data[i].port, "AREA_CODE": data[i].cell, "POLYGON_ID": id, "UNIT_ID": data[i].region, "HECTARES": (Math.PI * pop ^ 2), "DESCRIPT0": "CIVIL ADMINISTRATION AREA", "CULTURE": data[i].culture, "POPULATION": pop
}
}
}
return { json: allPoints, last_id: id };
}
function json_states_to_features(data, id, scale) {
allPoints = [];
for (let i = 0; i < data.length; i++) {
id++;
pop = data[i].population;
let boundries = flatten_multi_svg(data[i].path, 1000);
let multiPolygon = []
for (let b = 1; b < boundries.length; b++) {
multiPolygon[b] = latlongs_to_polygon(xypath_to_latlong(boundries[b], scale));
}
allPoints[i] = {
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [multiPolygon]
},
"properties": {
"name": data[i].name, "state_id": data[i].i, "feature": data[i].feature, "capital": data[i].capital, "port": data[i].port, "AREA_CODE": data[i].cell, "POLYGON_ID": id, "UNIT_ID": data[i].region, "HECTARES": (Math.PI * pop ^ 2), "DESCRIPT0": "CIVIL ADMINISTRATION AREA", "CULTURE": data[i].culture, "POPULATION": pop
}
}
}
return { json: allPoints, last_id: id };
}
function json_coastlines_to_features(data, id, scale) {
allPoints = [];
for (let i = 0; i < data.length; i++) {
id++;
pop = data[i].population;
let bound = flatten_svg(data[i].path, 1000);
let polygon = latlongs_to_polygon(xypath_to_latlong(bound, scale));
allPoints[i] = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [polygon]
},
"properties": {
"name": data[i].name, "feature_id": data[i].i, "feature": data[i].feature, "capital": data[i].capital, "port": data[i].port, "AREA_CODE": data[i].cell, "POLYGON_ID": id, "UNIT_ID": data[i].region, "HECTARES": (Math.PI * pop ^ 2), "DESCRIPT0": "CIVIL ADMINISTRATION AREA", "CULTURE": data[i].culture, "POPULATION": pop
}
}
}
return { json: allPoints, last_id: id };
}
function json_points_to_features(data, id, scale) {
allPoints = [];
for (let i = 0; i < data.length; i++) {
id++;
[x, y] = data[i];
ll = xy_to_coords(x * scale, y * scale);
allPoints[c] = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [ll.slon, ll.slat] //geojson is [lon,lat]
},
"properties": {
"name": "Point: " + (c + 1),
"POINT_ID": id
}
}
}
return { json: allPoints, last_id: id };
}
//--------------json functions-------------------
function gen_settlements_props(data, it) {
let props = {
"name": data[i].name,
"settlement_id": data[i].i,
"feature": data[i].feature,
"capital": data[i].capital,
"port": data[i].port,
"AREA_CODE": data[i].cell,
"POLYGON_ID": it,
"UNIT_ID": data[i].region,
"HECTARES": (Math.PI * data.population ^ 2),
"DESCRIPT0": "CIVIL ADMINISTRATION AREA",
"CULTURE": data[i].culture,
"POPULATION": data.population
}
return props;
}
function gen_state_props(data, it) {
let props = {
"name": data.name,
"state_id": data.i,
"color": data.color,
"capital": data.capital,
"expansionism": data.expansionism,
"type": data.type,
"expansionism": data.expansionism,
"center": data.center,
"culture": data.culture,
"cells": data.cells,
"area": data.area,
"rural": data.rural,
"urban": data.urban,
"cities": data.burgs,
"AREA_CODE": data.cell,
"POLYGON_ID": it,
"HECTARES": (Math.PI * data.population ^ 2),
"DESCRIPT0": "CIVIL ADMINISTRATION AREA",
"POPULATION": data.population
}
return props;
}
function gen_coastline_props(data) {
let props = {
"name": data.name,
"state_id": data.i,
}
return props;
}
//--------------generic functions-------------------
function flatten_multi_svg(path, num) {
const len = path.getTotalLength();
const elem = path.pathSegList.numberOfItems;
var paths = [];
var nums = 0;
var poly = 0;
paths[poly] = [];
for (var i = 0; i < elem; i++) {
item = path.pathSegList.getItem(i);
if (item.pathSegType === 2) {
if (poly > 0 && i > 0) {
let fp = paths[poly][0];
paths[poly][nums] = { x: fp.x, y: fp.y };
}
poly++;
paths[poly] = [];
nums = 0;
}
paths[poly][nums] = { x: item.x, y: item.y };
nums++;
}
return paths;
}
function flatten_multi_svg2(path, num) {
var len = path.getTotalLength();
var d = [];
var c = 0;
var poly = 0;
poly++;
c = 0;
d[poly] = [];
p = path.getPointAtLength(0);
d[poly][c] = { x: p.x, y: p.y };
incr = (len / num);
for (var i = incr; i <= len; i += incr) {
item = path.getPathSegAtLength(i);
switch (item.pathSegType) {
case 2:
poly++;
c = 0;
d[poly] = [];
d[poly][c] = { x: item.x, y: item.y };
case 4:
var skip = "";
}
p = path.getPointAtLength(i);
d[poly][c] = { x: p.x, y: p.y };
c++;
}
return d;
}
function flatten_svg(path, num) {
var len = path.getTotalLength();
var d = [];
var c = 0;
c = 0;
d = [];
p = path.getPointAtLength(0);
d[c] = { x: p.x, y: p.y };
//d[poly] = [];
//var p = path.getPointAtLength(0);
//d[poly][c] = {x:p.x, y:p.y};
incr = (len / num);
for (var i = incr; i <= len; i += incr) {
p = path.getPointAtLength(i);
d[c] = { x: p.x, y: p.y };
c++;
}
d[c] = { x: d[0].x, y: d[0].y };
return d;
}
function unit_to_km(unit) {
conv = 1;
name = "Unknown";
if (unit in units) { //ignoring custom units atm
name = units[unit].name;
conv = units[unit].conv;
}
return conv;
}
function xy_to_coords(x, y) {
porg = {};
porg.x = x;
porg.y = -y;
porg.rotation_angle_degs = rotation_angle_degs;
porg.xoffset_mtrs = 0;
porg.yoffset_mtrs = 0;
porg.olon = 0.1;
porg.olat = 0.1;
return translate_coordinates(1, porg);
}
function dict_to_array(dict) {
return Object.keys(dict).map(function (key) {
return dict[key];
})
}
function xy_to_boundry(x, y, pop, scale) {
let n = 20;
// The radius of circle, which area is the places's area
// which is worked out by place's population * average m^2 per person
let r = Math.sqrt((avg_sqm_pp * pop) / Math.PI);
if (r < 20) {
r = 20;
}
let boundry = [];
x *= scale;
y *= scale;
for (let i = 0; i < n; i++) {
x_new = x + Math.cos((2 * Math.PI / n) * i) * r;
y_new = y + Math.sin((2 * Math.PI / n) * i) * r;
boundry[i] = xy_to_coords(x_new, y_new);
}
boundry[boundry.length] = boundry[0];
return boundry;
}
function xypath_to_latlong(xys, scale) {
latlongs = []
for (let i = 0; i < xys.length; i++) {
let xy = xys[i];
x = xy.x * scale;
y = xy.y * scale;
latlongs[i] = xy_to_coords(x, y);
}
return latlongs;
}
function latlongs_to_polygon(latlongs) {
polygon = [];
for (k in latlongs) {
ll = latlongs[k];
polygon[k] = [ll.slon, ll.slat]; //geojson is [lon,lat]
}
return polygon;
}

View file

@ -183,6 +183,48 @@ function saveMap() {
console.timeEnd("saveMap");
}
// Save in a geo-format, an lan long global coordinate format
function saveGeo(type) {
if (customization) {tip("Map cannot be saved when is in edit mode, please exit the mode and re-try", false, "error"); return;}
console.time("saveOSM");
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].join("|");
const options = [distanceUnit.value, distanceScale.value, areaUnit.value, heightUnit.value, heightExponent.value, temperatureScale.value,
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value, barPosX.value, barPosY.value, populationRate.value, urbanization.value,
equatorOutput.value, equidistanceOutput.value, temperatureEquatorOutput.value, temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds, null, 2)].join("|");
const coords = JSON.stringify(mapCoordinates, null, 2);
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|");
const notesData = JSON.stringify(notes, null, 2);
// 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());
let geodata = gen_geodata(type);
let data = geodata.data;
let ext = geodata.ext;
const dataBlob = new Blob([data], {type: "text/plain"});
const dataURL = window.URL.createObjectURL(dataBlob);
const link = document.createElement("a");
link.download = "fantasy_map_" + Date.now() + ext;
link.href = dataURL;
document.body.appendChild(link);
link.click();
// restore initial values
svg.attr("width", svgWidth).attr("height", svgHeight);
zoom.transform(svg, transform);
window.setTimeout(function() {window.URL.revokeObjectURL(dataURL);}, 2000);
console.timeEnd("saveOSM");
}
function uploadFile(file, callback) {
console.time("loadMap");
const fileReader = new FileReader();

View file

@ -211,6 +211,8 @@ document.addEventListener("keydown", function(event) {
if (key === 118) regenerateMap(); // "F7" for new map
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
else if (key === 9) {toggleOptions(event); event.preventDefault();} // Tab to toggle options
else if (shift && key === 79) saveGeo("osm"); // Shift + "O" to save as OSM
else if (shift && key === 74) saveGeo("json"); // Shift + "J" to save as JSON
else if (ctrl && key === 80) saveAsImage("png"); // Ctrl + "P" to save as PNG
else if (ctrl && key === 83) saveAsImage("svg"); // Ctrl + "S" to save as SVG
else if (ctrl && key === 77) saveMap(); // Ctrl + "M" to save MAP file

View file

@ -903,6 +903,8 @@ document.getElementById("sticked").addEventListener("click", function(event) {
else if (id === "loadMap") mapToLoad.click();
else if (id === "zoomReset") resetZoom(1000);
else if (id === "saveMap") saveMap();
else if (id === "saveOSM") saveGeo("osm");
else if (id === "saveJSON") saveGeo("json");
else if (id === "saveSVG") saveAsImage("svg");
else if (id === "savePNG") saveAsImage("png");
if (id === "saveMap" || id === "saveSVG" || id === "savePNG") toggleSavePane();