From 359b27ae2d80f9f75004cc6fd661ca59fb9b5c96 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 8 Sep 2019 14:48:54 +0300 Subject: [PATCH] v1.0.11 --- QGIS/Style_Biomes.qml | 361 ------------------------------------- QGIS/add_random_points.php | 81 --------- index.html | 2 +- modules/save-and-load.js | 310 ++++++++++++++----------------- modules/ui/general.js | 1 + modules/ui/options.js | 10 +- 6 files changed, 146 insertions(+), 619 deletions(-) delete mode 100644 QGIS/Style_Biomes.qml delete mode 100644 QGIS/add_random_points.php diff --git a/QGIS/Style_Biomes.qml b/QGIS/Style_Biomes.qml deleted file mode 100644 index c482cfe3..00000000 --- a/QGIS/Style_Biomes.qml +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 2 - diff --git a/QGIS/add_random_points.php b/QGIS/add_random_points.php deleted file mode 100644 index 46452498..00000000 --- a/QGIS/add_random_points.php +++ /dev/null @@ -1,81 +0,0 @@ -\n" ); -} - -// FIXME: This script created a few cases of self-intersection that must be fixed manually -// in QGIS: Vector -> Geometry Tools -> Check Validity - -$cells = json_decode(file_get_contents($argv[1]), true); - - -for ($i=0; $i<$iterations; $i++) { - $lookup = array(); - - foreach ($cells['features'] as &$cell) { - $points = $cell['geometry']['coordinates'][0]; - - $prev = null; - $newpoints = array(); - - foreach ($points as $point) { - if ($prev) { - $distance = sqrt(pow($prev[0] - $point[0], 2) + pow($prev[1] - $point[1], 2)); - if ($distance == 0) continue; - - if ($distance > $min_distance) { - $id_a = $prev[0]."-".$prev[1]; - $id_b = $point[0]."-".$point[1]; - - if (isset($lookup[$id_a."--".$id_b])) { - $newpoints[] = $lookup[$id_a."--".$id_b]; - } elseif (isset($lookup[$id_b."--".$id_a])) { - $newpoints[] = $lookup[$id_b."--".$id_a]; - } else { - $x = ($prev[0]+$point[0])/2.0; - $y = ($prev[1]+$point[1])/2.0; - - $r = mt_rand() / mt_getrandmax(); // 0-1 - $r = ($r+1) / 2; // 0.5 - 1.0 - - // if we define dx=x2-x1 and dy=y2-y1, then the normals are (-dy, dx) and (dy, -dx). - $dx = $point[0] - $x; - $dy = $point[1] - $y; - - if (mt_rand() / mt_getrandmax() < 0.5) { - $x_off = -$dy; - $y_off = $dx; - } else { - $x_off = $dy; - $y_off = -$dx; - } - - $x_off *= $r * $max_deviation; - $x_off = max(min($x_off, $max_abs), $max_abs*-1); - - $y_off *= $r * $max_deviation; - $y_off = max(min($y_off, $max_abs), $max_abs*-1); - - $p = array($x + $x_off, $y + $y_off); - $lookup[$id_a."--".$id_b] = $p; - $newpoints[] = $p; - } - } - } - $newpoints[] = $point; - $prev = $point; - } - $cell['geometry']['coordinates'][0] = $newpoints; - } -} - -echo json_encode($cells, JSON_PRETTY_PRINT); - -?> diff --git a/index.html b/index.html index e54a2cd2..4def49d8 100644 --- a/index.html +++ b/index.html @@ -1838,7 +1838,7 @@
.map
.svg
.png
-
.json
+
.json
storage
diff --git a/modules/save-and-load.js b/modules/save-and-load.js index 373da683..36cf9505 100644 --- a/modules/save-and-load.js +++ b/modules/save-and-load.js @@ -1,176 +1,6 @@ // 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"); @@ -387,6 +217,146 @@ async function saveMap() { window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000); } +// download map data as GeoJSON +function saveGeoJSON() { + alertMessage.innerHTML = `You can export map data in GeoJSON format used in GIS tools such as QGIS. + Check out wiki-page for guidance`; + + $("#alert").dialog({title: "GIS data export", resizable: false, width: 320, position: {my: "center", at: "center", of: "svg"}, + buttons: { + Cells: saveGeoJSON_Cells, + Routes: saveGeoJSON_Roads, + Rivers: saveGeoJSON_Rivers, + Close: function() {$(this).dialog("close");} + } + }); +} + +function saveGeoJSON_Roads() { + let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n"; + + routes._groups[0][0].childNodes.forEach(n => { + 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, v = pack.vertices; + + 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"; + }); + + 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); +} + function uploadFile(file, callback) { uploadFile.timeStart = performance.now(); diff --git a/modules/ui/general.js b/modules/ui/general.js index 64ab81f3..929a9ece 100644 --- a/modules/ui/general.js +++ b/modules/ui/general.js @@ -242,6 +242,7 @@ document.addEventListener("keydown", function(event) { 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 + else if (ctrl && key === 71) saveGeoJSON(); // Ctrl + "G" to save as GeoJSON else if (ctrl && key === 85) mapToLoad.click(); // Ctrl + "U" to load MAP from URL else if (ctrl && key === 76) mapToLoad.click(); // Ctrl + "L" to load MAP from local file else if (ctrl && key === 81) toggleSaveReminder(); // Ctrl + "Q" to toggle save reminder diff --git a/modules/ui/options.js b/modules/ui/options.js index cf10282c..6729fc20 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -1027,12 +1027,10 @@ function toggleSavePane() {
Please check browser settings and turn off adBlocker if it is enabled`; $("#alert").dialog({title: "File saver", resizable: false, position: {my: "center", at: "center", of: "svg"}, - buttons: { - OK: function() { - localStorage.setItem("dns_allow_popup_message", true); - $(this).dialog("close"); - } - } + buttons: {OK: function() { + localStorage.setItem("dns_allow_popup_message", true); + $(this).dialog("close"); + }} }); } }