diff --git a/css/style.css b/css/style.css index ec729db0062fed15bb9c8ea7b1ff0a79b855ff1b..95ae4c609ced05154e67314664ee35343f5be22c 100644 --- a/css/style.css +++ b/css/style.css @@ -31,7 +31,7 @@ p { .box1 { display: flex; - flex-grow: 7; + flex-grow: 8; flex-direction: row; } diff --git a/index.html b/index.html index 9a5e5be697a7d07b54a16dac45cd6ab4392452ec..64f15a1c0c3726630f8bdc985ab7fdd543058245 100644 --- a/index.html +++ b/index.html @@ -28,12 +28,13 @@ <div style="display: flex; flex-grow: 2; flex-direction: column;"> <h1 style="margin: 0; padding: 0; text-align: center; font-size: 12vh;">ProgGIS</h1> <div id="buttons1" class="box1"> - <p style="width: 7vw;"></p> + <p style="width: 1vw;"></p> <button class="button" onclick="openBox('bufferBox')">Buffer</button> <button class="button" onclick="openBox('differenceBox')">Difference</button> <button class="button" onclick="openBox('dissolveBox')">Dissolve</button> <button class="button" onclick="openBox('extractBox')">Extract</button> <button class="button" onclick="openBox('intersectionBox')">Intersection</button> + <button class="button" onclick="openBox('suitabilityBox')">Suitability</button> <button class="button" onclick="openBox('unionBox')">Union</button> </div> <div id="buttons2" class="box2" style="display: none;"> @@ -75,7 +76,7 @@ <button id="exampleData" class="onClick" onclick="loadExampleData()">Click here to load the sample datasets</button> </div> - <div id="layers" style="font-family: monospace; margin-left: 1vw; margin-top: 2vh; font-size: 18px; height: 53vh; overflow-y: scroll;"></div> + <div id="layers" style="font-family: monospace; margin-left: 1vw; margin-top: 2vh; font-size: 18px; height: 52vh; overflow-y: scroll;"></div> </div> <div id="map" style="position: relative; z-index: 1; height: 80vh;"> @@ -193,6 +194,36 @@ <button class="button" style="font-size: 25px;" onclick="makeIntersection()">Intersect</button> </div> + <div id="suitabilityBox" class="box"> + <div class="box3"> + <h1 style="font-size: 4vh; text-align: center; width: 36vw;">Suitability</h1> + + <svg style="cursor: pointer; padding-top: 15px;" onclick="closeBox('suitabilityBox')" + xmlns="http://www.w3.org/2000/svg" width="3vw" height="3vw" fill="currentColor" class="bi bi-x-circle-fill" viewBox="0 0 16 16"> + <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/> + </svg> + </div> + + <p>Choose map layers in prioritised order:</p> + + <div id="priorityDiv"> + <div style="display: flex; justify-content: center;"> + <select id="priority1"></select> + + <svg onclick="addPrioritizedLayer()" style="cursor: pointer; margin-left: 1vw;" xmlns="http://www.w3.org/2000/svg" + width="3vh" height="3vh" fill="currentColor" class="bi bi-plus-circle-fill" viewBox="0 0 16 16"> + <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3v-3z"/> + </svg> + </div> + </div> + + <p>Give this suitability analyse a cathegory name:</p> + + <input id="suitabilityName"><br> + + <button class="button" style="font-size: 25px; margin-top: 4vh;" onclick="suitability()">Calculate suitable areas</button> + </div> + <div id="unionBox" class="box"> <div class="box3"> <h1 style="font-size: 4vh; text-align: center; width: 36vw;">Union</h1> @@ -291,6 +322,7 @@ <script src="javascript/intersect.js"></script> <script src="javascript/union.js"></script> <script src="javascript/extract.js"></script> + <script src="javascript/suitability.js"></script> <!-- Map change --> diff --git a/javascript/leafletLayerControl.js b/javascript/leafletLayerControl.js index 998ffb727c26cb8384763c13fc739285d50ed22a..aed39662e70ab26dabd559148b1f5b20c8813d09 100644 --- a/javascript/leafletLayerControl.js +++ b/javascript/leafletLayerControl.js @@ -85,7 +85,7 @@ function checkExampleData() { exampleLoaded = false; document.getElementById("exampleData").style.display = "block"; - document.getElementById("layers").style.height = "53vh"; + document.getElementById("layers").style.height = "52vh"; } function handleLayer(name) { // Funksjon som viser og skjuler kartlag i kartet og endrer farge på knappene i sidebaren avhengig av lagets synlighet @@ -121,3 +121,12 @@ function doLayerExist(name) { } return false; } + +function doLayerExist2(name) { + for (key in overlayMaps) { + if (key.toString().slice(0, -1) == name) { + return true; + } + } + return false; +} diff --git a/javascript/sidebar&boxes.js b/javascript/sidebar&boxes.js index 5926cdb90d24e15ffdbb7a3d0124adb5af684bb9..0f8fb9af3c1c7d6f1800550fc820b940ad96e20b 100644 --- a/javascript/sidebar&boxes.js +++ b/javascript/sidebar&boxes.js @@ -36,6 +36,11 @@ function openBox(id) { // Åpner aktuell boks (id) fillSelect("extractSelect"); } else if (id == "intersectionBox") { fillDoubleSelect("intersectionSelect"); + } else if (id == "suitabilityBox") { + var numb = document.getElementById("priorityDiv").childElementCount; + for (var i=1; i<=numb; i++) { + fillSelect("priority" + i.toString()); + } } else if (id == "unionBox") { fillDoubleSelect("unionSelect"); } diff --git a/javascript/suitability.js b/javascript/suitability.js new file mode 100644 index 0000000000000000000000000000000000000000..8e7fa27c0bdd30640c823afe63ca2c918ff60a76 --- /dev/null +++ b/javascript/suitability.js @@ -0,0 +1,208 @@ +function addPrioritizedLayer() { + var newPriority = document.createElement("div"); + var numb = document.getElementById("priorityDiv").childElementCount + 1; + var start =`<div id="div${numb}" style="display: flex; flex-grow: 2; flex-direction: row; justify-content: center; margin-top: 1vh;">`; + var newSelect = `<select id="priority${numb.toString()}"></select>`; + var newButton = `<svg onclick="removePrioritizedLayer(${numb})" style="cursor: pointer; margin-left: 1vw;" xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16"> <path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06Zm6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528ZM8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5"/> </svg>`; + var end = '</div>' + newPriority.innerHTML = start + newSelect + newButton + end; + + document.getElementById("priorityDiv").appendChild(newPriority); + fillSelect("priority" + numb.toString()); +} + +function removePrioritizedLayer(numb) { + var count = document.getElementById("priorityDiv").childElementCount; + if (numb == count) { + document.getElementById("priorityDiv").removeChild(document.getElementById("div" + numb.toString()).parentElement); + } else { + for (var i=numb; i<count; i++) { + document.getElementById("priority" + i.toString()).value = document.getElementById("priority" + (i+1).toString()).value; + } + document.getElementById("priorityDiv").removeChild(document.getElementById("div" + count.toString()).parentElement); + } +} + +function suitability() { + // Sjekker at det er gitt inn navn: + var regex = /^[a-zA-Z_0-9]+$/; + if (!document.getElementById("suitabilityName").value) { + return alert("You need to choose a name for the new layer!"); + } else if (!document.getElementById("suitabilityName").value.match(regex)) { + return alert("The new name must consist of normal letters!"); + } else if (doLayerExist2(document.getElementById("suitabilityName").value)) { + return alert("Choose another name! There exists already a layer with that name.") + } + categoryName = document.getElementById("suitabilityName").value; + + // Henter aktuelle element: + var numb = document.getElementById("priorityDiv").childElementCount; + var priEl = []; + for (var i = 1; i < numb + 1; i++) { + priEl.push("priority" + i.toString()); + } + + // Finner alle mulige kombinasjoner av elementene: + priEl = getCombinations(priEl); + + // Beregner poengscore for hver kombinasjon: + + var scores = getScores(numb, priEl); + + // Sorterer lista basert på score: + + priEl = insertionSort(priEl, scores); + + // Lager de ulike kartlagene med intersect: + + var newLayers = []; + + for (var i = 0; i < priEl.length; i++) { + if (priEl[i].length == 1) { + var a = getMapLayer(priEl[i][0]); + if (a == null) { + return alert("You need to choose all layers!"); + } + newLayers.push(a); + } else { + var a = getMapLayer(priEl[i][0]); + if (a == null) { + return alert("You need to choose all layers!"); + } + var newLayer = featureCollectionToMultiPolygon(a); + for (var j = 1; j < priEl[i].length; j++) { + var b = getMapLayer(priEl[i][j]); + if (b == null) { + return alert("You need to choose all layers!"); + } + var layer = featureCollectionToMultiPolygon(b); + newLayer = turf.intersect(newLayer, layer); + } + newLayers.push(newLayer); + } + } + + // Bruker difference for å unngå overlappende kartlag: + + for (var i = 1; i < newLayers.length; i++) { + for (var j = 0; j < i; j++) { + + var a = newLayers[i]; + var b = newLayers[j]; + + if (a == undefined || b == undefined) { + break; + } + + if (!isMultiPolygon(a)) { + a = featureCollectionToMultiPolygon(a); + } + if (!isMultiPolygon(b)) { + b = featureCollectionToMultiPolygon(b); + } + + newLayers[i] = turf.difference(a, b); + } + } + + // Gjør om tilbake til FeatureCollection fra MultiPolygon: + for (var i = 0; i < newLayers.length; i++) { + if (newLayers[i] == undefined) { + continue; + } else if (isMultiPolygon(newLayers[i])) { + newLayers[i] = multiPolygonToFeatureCollection(newLayers[i]); + } + } + + // Legger til de nye lagene som har innhold i kartet: + + // Karakteristiske farger for lagene: + // Rødt: "rgb(255,0,0)" Grønt: "rgb(0,255,0)" + + var intervall = 255 / (newLayers.length - 1); + var r = 0; + var g = 255; + + for (var i = 0; i < newLayers.length; i++) { + if (newLayers[i] == undefined) { + continue; + } + var name = categoryName + (i+1).toString(); + + var newLayer = L.geoJSON(newLayers[i], {style: {color: "rgb(" + r.toString() + "," + g.toString() + ",0)"}}); + r += intervall; + g += -intervall; + overlayMaps[name] = newLayer; + updateSidebar(); + handleLayer(name); + } + + document.getElementById("suitabilityName").value = ""; + for (var i = 2; i <= numb; i++) { + removePrioritizedLayer(i); + } + fillSelect("priority1"); +} + +function getCombinations(valuesArray) { + var combi = []; + var temp = []; + var slent = Math.pow(2, valuesArray.length); + + for (var i = 0; i < slent; i++) { + temp = []; + for (var j = 0; j < valuesArray.length; j++) { + if ((i & Math.pow(2, j))) { + temp.push(valuesArray[j]); + } + } + if (temp.length > 0) { + combi.push(temp); + } + } + + combi.sort((a, b) => b.length - a.length); + //console.log(combi.join("\n")); + return combi; +} + +function getScores(numb, list) { + var scores = {}; + for (var i = 0; i < list.length; i++) { + var score = 0; + for (var j = 0; j < list[i].length; j++) { + score += numb + 1 - parseInt(list[i][j].slice(-1)); + } + scores[list[i]] = score; + } + return scores; +} + +function insertionSort(list, dict) { + sorted = [list[0]] + for (var i = 1; i < list.length; i++) { + if (dict[list[i]] < dict[sorted[sorted.length - 1]]) { + sorted.push(list[i]); + } else { + for (var j = 0; j < sorted.length; j++) { + if (dict[sorted[j]] <= dict[list[i]]) { + sorted.push(sorted[sorted.length - 1]); + for (var k = sorted.length - 2; k > j; k--) { + sorted[k] = sorted[k - 1]; + } + sorted[j] = list[i]; + break; + } + } + } + } + return sorted; +} + +function getMapLayer(string) { + var input = document.getElementById(string).value; + if (input == "- - -") { + return null; + } + return overlayMaps[input].toGeoJSON(); +}