diff --git a/HighwayDataExaminer/hdxav-qh.js b/HighwayDataExaminer/hdxav-qh.js new file mode 100644 index 0000000..1ede449 --- /dev/null +++ b/HighwayDataExaminer/hdxav-qh.js @@ -0,0 +1,577 @@ +// +// HDX Algorithm Visualization Template File +// +// METAL Project +// +// Primary Authors: Gregory Drapeau +// + +// This global variable refers to the object containaing all the +// necessary fields, functions, and states for a given AV. This +// variable must be pushed to the this.avList in the hdxav.js file, +// and the file of this AV must be included in the index.php file +const hdxQHAV = { + // short name for list of avs, will be used for the av= QS parameter value + value: 'qhull', + name: "Quickhull", + description: "The divide and conquer algorithm for finding the convex hull.", + + // vertices, no edges + useV: true, + useE: false, + + currentSet: [], + vOne: null, + vTwo: null, + mostNorth: -90, + mostSouth: 90, + // array of vertices that makes up the hull + hullVertices: [], + // array of the line segments that makes up the hull + hullSegments: [], + // dividing line slope + slope: 0, + // dividing line y-intercept + yIntercept: 0, + // sets + sZero: [], + sOne: [], + sTwo: [], + // max [distance, index, object] + max: [], + // used for splitLoop due to that being used in multiple places + futureAction: "", + // temporary vertices for swaping + vOneTmp: null, + vTwoTmp: null, + // polyline object + compLine: [], + + // elements index 0 is v1, index 1 is v2, index 2 is lower set + recursionStack: [], + + // loop variable that tracks which point is currently being operated upon + nextToCheck: -1, + + // obscure irrelevant regions of the map + obscureRegion: null, + obscure: { + color: "black", + scale: 4, + weight: 0.5, + fillOpacity: 0.1, + name: "obscure", + value: 0 + }, + + // The avActions array defines all of the actions of the AV + avActions : [ + { + label: "START", + comment: "Initializes fields", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + thisAV.mostNorth = -90; + thisAV.mostSouth = 90; + for(let i = 0; i < waypoints.length; i++){ + if(waypoints[i].lat > thisAV.mostNorth){ + thisAV.mostNorth = waypoints[i].lat; + } + if(waypoints[i].lat < thisAV.mostSouth){ + thisAV.mostSouth = waypoints[i].lat; + } + } + thisAV.mostNorth += (thisAV.mostNorth-thisAV.mostSouth)/10; + thisAV.mostSouth += -(thisAV.mostNorth-thisAV.mostSouth)/11; + const sorter = new HDXWaypointsSorter(); + thisAV.currentSet = sorter.sortWaypoints(); + thisAV.vOne = thisAV.currentSet[0]; + thisAV.vTwo = thisAV.currentSet[thisAV.currentSet.length-1]; + thisAV.currentSet.splice(0,1); + thisAV.currentSet.splice(thisAV.currentSet.length-1, 1); + // dividing line + thisAV.slope = (thisAV.vTwo.lat-thisAV.vOne.lat)/(thisAV.vTwo.lon-thisAV.vOne.lon); + thisAV.yIntercept = (thisAV.slope*(thisAV.vOne.lon*(-1)))+thisAV.vOne.lat; + let comLine = []; + comLine[0] = [thisAV.vOne.lat, thisAV.vOne.lon]; + comLine[1] = [thisAV.vTwo.lat, thisAV.vTwo.lon]; + thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting)); + thisAV.compLine[thisAV.compLine.length-1].addTo(map); + thisAV.hullVertices = []; + thisAV.hullSegments = []; + thisAV.hullVertices.push(thisAV.vOne); + // AVCP updates + document.getElementById("hVertsNum").innerText = thisAV.hullVertices.length; + let newTableRow = document.createElement("tr"); + newTableRow.innerHTML = ``+thisAV.hullVertices[thisAV.hullVertices.length-1].label+` + (`+thisAV.hullVertices[thisAV.hullVertices.length-1].lat+`, `+thisAV.hullVertices[thisAV.hullVertices.length-1].lon+`)`; + document.getElementById("hullVertexTable").appendChild(newTableRow); + thisAV.nextToCheck = -1; + thisAV.futureAction = "callOne"; + hdxAVCP.update("hullv1", "v1: "+thisAV.vOne.label); + hdxAVCP.update("hullv2", "v2: "+thisAV.vTwo.label); + hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+"
"+thisAV.vOne.label+"<-->"+thisAV.vTwo.label); + updateMarkerAndTable(waypoints.indexOf(thisAV.vOne), + visualSettings.averageCoord, 40, false); + updateMarkerAndTable(waypoints.indexOf(thisAV.vTwo), + visualSettings.averageCoord, 40, false); + + hdxAV.nextAction = "splitLoop"; + }, + logMessage: function(thisAV) { + return "Doing some setup stuff"; + } + }, + { + label: "splitLoop", + comment: "Loop for splitting up of vertices", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + thisAV.nextToCheck++; + if(thisAV.nextToCheck < thisAV.currentSet.length){ + updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]), + visualSettings.visiting, 40, false); + hdxAV.nextAction = "split"; + }else{ + hdxAV.nextAction = thisAV.futureAction; + } + }, + logMessage: function(thisAV) { + return "Loop iteration: "+thisAV.nextToCheck; + } + }, + { + label: "split", + comment: "Splitting up vertices between set one and two", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + if(thisAV.vOne.lon < thisAV.vTwo.lon){ + // above hull + if(thisAV.currentSet[thisAV.nextToCheck].lat > (thisAV.slope*thisAV.currentSet[thisAV.nextToCheck].lon+thisAV.yIntercept)){ + thisAV.sOne.push(thisAV.currentSet[thisAV.nextToCheck]); + updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]), + visualSettings.discovered, 40, false); + }else{ + thisAV.sTwo.push(thisAV.currentSet[thisAV.nextToCheck]); + updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]), + visualSettings.discarded, 40, false); + } + }else{ + // below hull + if(thisAV.currentSet[thisAV.nextToCheck].lat > (thisAV.slope*thisAV.currentSet[thisAV.nextToCheck].lon+thisAV.yIntercept)){ + thisAV.sTwo.push(thisAV.currentSet[thisAV.nextToCheck]); + updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]), + visualSettings.discarded, 40, false); + }else{ + thisAV.sOne.push(thisAV.currentSet[thisAV.nextToCheck]); + updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]), + visualSettings.discovered, 40, false); + } + } + + hdxAV.nextAction = "splitLoop"; + }, + logMessage: function(thisAV) { + return "Size of set one: "+thisAV.sOne.length+"
Size of set two: "+thisAV.sTwo.length; + } + }, + { + label: "callOne", + comment: "Adding to the recursion stack and setting up variables for first call", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + hdxAV.nextAction = "fnTop"; + + if(thisAV.sOne.length > 0){ + let snapShot + thisAV.currentSet = thisAV.sOne; + if(thisAV.sTwo.length > 0){ + snapShot = [thisAV.vTwo, thisAV.vOne, thisAV.sTwo]; + }else{ + snapShot = [thisAV.vOne]; + } + thisAV.recursionStack.push(snapShot); + }else{ + thisAV.hullVertices.push(thisAV.vTwo); + hdxQHAV.updateHullVertexTable(); + if(thisAV.sTwo.length > 0){ + let vZ = thisAV.vOne; + thisAV.vOne = thisAV.vTwo; + thisAV.vTwo = vZ; + thisAV.currentSet = thisAV.sTwo; + }else{ + hdxAV.nextAction = "cleanup"; + } + } + for(let i = 0; i < thisAV.sTwo.length; i++){ + updateMarkerAndTable(waypoints.indexOf(thisAV.sTwo[i]), + visualSettings.discovered, 40, false); + } + + hdxAV.iterationDone = true; + }, + logMessage: function(thisAV) { + return "Starting at "+thisAV.vOne.label; + } + }, + { + label: "fnTop", + comment: "Setting up UI for new call and confirming vars have been reset", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + thisAV.nextToCheck = -1; + thisAV.max = [0,-1,null]; + thisAV.sOne = []; + thisAV.sTwo = []; + // dividing line + thisAV.slope = (thisAV.vTwo.lat-thisAV.vOne.lat)/(thisAV.vTwo.lon-thisAV.vOne.lon); + thisAV.yIntercept = (thisAV.slope*(thisAV.vOne.lon*(-1)))+thisAV.vOne.lat; + let comLine = []; + comLine[0] = [thisAV.vOne.lat, thisAV.vOne.lon]; + comLine[1] = [thisAV.vTwo.lat, thisAV.vTwo.lon]; + thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting)); + thisAV.compLine[thisAV.compLine.length-1].addTo(map); + + // AVCP updates + hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+"
"+thisAV.vOne.label+"<-->"+thisAV.vTwo.label); + hdxAVCP.update("hullv1", "v1: "+thisAV.vOne.label); + hdxAVCP.update("hullv2", "v2: "+thisAV.vTwo.label); + // AVCP recursionStack + let recursionTable = ""; + let trSize = Math.min(4, thisAV.recursionStack.length); + for(let i = 0; i < trSize; i++){ + let recursionIndex; + if(i == 3){ + recursionIndex = 0; + recursionTable += "..."; + }else{ + recursionIndex = thisAV.recursionStack.length-i-1; + } + if(thisAV.recursionStack[recursionIndex].length == 1){ + recursionTable += "#"+recursionIndex+"
"+shortLabel(thisAV.recursionStack[recursionIndex][0].label, 8)+""; + }else{ + recursionTable += "#"+recursionIndex+"
"+shortLabel(thisAV.recursionStack[recursionIndex][0].label, 8)+"
"+shortLabel(thisAV.recursionStack[recursionIndex][1].label, 8)+"
Size: "+thisAV.recursionStack[recursionIndex][2].length+""; + } + } + recursionTable += ""; + document.getElementById("recTable").innerHTML = recursionTable; + + hdxQHAV.updateShading(); + + hdxAV.nextAction = "findMaxLoop"; + }, + logMessage: function(thisAV) { + return "VOne: "+thisAV.vOne.label+" vTwo: "+thisAV.vTwo.label; + } + }, + { + label: "findMaxLoop", + comment: "Loop for finding the max", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + thisAV.nextToCheck++; + if(thisAV.nextToCheck thisAV.max[0]){ + if(thisAV.max[1] > -1){ + updateMarkerAndTable(waypoints.indexOf(thisAV.max[2]), + visualSettings.discovered, 40, false); + } + thisAV.max = [distance, thisAV.nextToCheck, thisAV.currentSet[thisAV.nextToCheck]]; + updateMarkerAndTable(waypoints.indexOf(thisAV.max[2]), + visualSettings.averageCoord, 40, false); + }else{ + updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]), + visualSettings.discovered, 40, false); + } + + hdxAV.nextAction = "findMaxLoop"; + }, + logMessage: function(thisAV) { + return "max distance is "+thisAV.max[0]; + } + }, + { + label: "firstSplit", + comment: "Prepares the variables for the first split of the set", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + if(thisAV.max[2] != null){ + thisAV.vOneTmp = thisAV.vOne; + thisAV.vTwoTmp = thisAV.max[2]; + // temporary line + thisAV.slope = (thisAV.vTwoTmp.lat-thisAV.vOneTmp.lat)/(thisAV.vTwoTmp.lon-thisAV.vOneTmp.lon); + thisAV.yIntercept = (thisAV.slope*(thisAV.vOneTmp.lon*(-1)))+thisAV.vOneTmp.lat; + let comLine = []; + comLine[0] = [thisAV.vOneTmp.lat, thisAV.vOneTmp.lon]; + comLine[1] = [thisAV.vTwoTmp.lat, thisAV.vTwoTmp.lon]; + thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting)); + thisAV.compLine[thisAV.compLine.length-1].addTo(map); + thisAV.nextToCheck = -1; + thisAV.currentSet.splice(thisAV.max[1],1); + thisAV.futureAction = "secondSplit"; + hdxAV.nextAction = "splitLoop"; + + hdxAVCP.update("hullMax", "vmax: "+thisAV.max[2].label); + hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+"
"+thisAV.vOneTmp.label+"<-->"+thisAV.vTwoTmp.label); + }else{ + hdxAV.nextAction = "postSplits"; + } + }, + logMessage: function(thisAV) { + return "the line is y="+thisAV.slope+"x+"+thisAV.yIntercept; + } + }, + { + label: "secondSplit", + comment: "Prepares the variables for the second split of the set", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + thisAV.vOneTmp = thisAV.max[2]; + thisAV.vTwoTmp = thisAV.vTwo; + // temporary line + thisAV.slope = (thisAV.vTwoTmp.lat-thisAV.vOneTmp.lat)/(thisAV.vTwoTmp.lon-thisAV.vOneTmp.lon); + thisAV.yIntercept = (thisAV.slope*(thisAV.vOneTmp.lon*(-1)))+thisAV.vOneTmp.lat; + let comLine = []; + comLine[0] = [thisAV.vOneTmp.lat, thisAV.vOneTmp.lon]; + comLine[1] = [thisAV.vTwoTmp.lat, thisAV.vTwoTmp.lon]; + thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting)); + thisAV.compLine[thisAV.compLine.length-1].addTo(map); + thisAV.nextToCheck = -1; + thisAV.sZero = thisAV.sOne; + thisAV.currentSet = thisAV.sTwo; + thisAV.sOne = []; + thisAV.sTwo = []; + thisAV.futureAction = "postSplits"; + hdxAV.nextAction = "splitLoop"; + + hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+"
"+thisAV.vOneTmp.label+"<-->"+thisAV.vTwoTmp.label); + }, + logMessage: function(thisAV) { + return "the line is y="+thisAV.slope+"x+"+thisAV.yIntercept; + } + }, + { + label: "postSplits", + comment: "Sets variables for the next iteration", + code: function(thisAV) { + highlightPseudocode(this.label, visualSettings.visiting); + + thisAV.sTwo = thisAV.sOne; + thisAV.sOne = thisAV.sZero; + hdxAV.nextAction = "fnTop"; + + // checking if set one is empty + if(thisAV.sOne.length > 0){ + let snapShot; + // checking if set two is empty + if(thisAV.sTwo.length > 0){ + snapShot = [thisAV.max[2], thisAV.vTwo, thisAV.sTwo]; + }else{ + snapShot = [thisAV.vTwo]; + } + thisAV.currentSet = thisAV.sOne; + thisAV.vTwo = thisAV.max[2]; + thisAV.recursionStack.push(snapShot); + // checking if set two is empty + }else if(thisAV.sTwo.length > 0){ + thisAV.currentSet = thisAV.sTwo; + thisAV.vOne = thisAV.max[2]; + thisAV.hullVertices.push(thisAV.max[2]); + hdxQHAV.updateHullVertexTable(); + // checking if recursion stack is empty + }else if(thisAV.recursionStack.length > 0){ + thisAV.hullVertices.push(thisAV.max[2]); + hdxQHAV.updateHullVertexTable(); + thisAV.hullVertices.push(thisAV.vTwo); + hdxQHAV.updateHullVertexTable(); + let snapShot = thisAV.recursionStack.pop(); + // collect any of the single vertices in stack + while(snapShot?.length==1){ + thisAV.hullVertices.push(snapShot[0]); + hdxQHAV.updateHullVertexTable(); + snapShot = thisAV.recursionStack.pop(); + } + // if stack is empty + if(snapShot==null){ + hdxAV.nextAction = "cleanup"; + }else{ + thisAV.vOne = snapShot[0]; + thisAV.vTwo = snapShot[1]; + thisAV.currentSet = snapShot[2]; + } + // if stack is empty + }else{ + hdxAV.nextAction = "cleanup"; + if(thisAV.max[2] != null){ + thisAV.hullVertices.push(thisAV.max[2]) + hdxQHAV.updateHullVertexTable(); + } + thisAV.hullVertices.push(thisAV.vTwo); + hdxQHAV.updateHullVertexTable(); + } + thisAV.sZero = []; + thisAV.sOne = []; + thisAV.sTwo = []; + + hdxAV.iterationDone = true; + + hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+"
"+thisAV.vOne.label+"<-->"+thisAV.vTwo.label); + }, + logMessage: function(thisAV) { + return "Preparing for the next iteration"; + } + }, + { + label: "cleanup", + comment: "cleanup and updates at the end of the visualization", + code: function(thisAV) { + + hdxAVCP.update("hullv1", ""); + hdxAVCP.update("hullv2", ""); + hdxAVCP.update("hullMax", ""); + hdxAVCP.update("checkingLine", ""); + hdxAVCP.update("recursionStack", ""); + + while(thisAV.compLine.length > 0){ + thisAV.compLine[thisAV.compLine.length-1].remove(); + thisAV.compLine.pop(); + } + thisAV.obscureRegion.remove(); + thisAV.obscureRegion = null; + + hdxAV.nextAction = "DONE"; + hdxAV.iterationDone = true; + + + }, + logMessage: function(thisAV) { + return "Cleanup and finalize visualization"; + } + } + ], + + // prepToStart is a required function which is called when you hit + // visualize but before you hit start + // sets up pseudocode + prepToStart() { + + // Build HTML for the pseudocode, which is an HTML table, with + // each state being a different row. + this.code = ''; + this.code += pcEntry(0,["qHull(s1, westMost, eastMost)","qHull(s2, eastMost, westMost)"],"callOne"); + this.code += pcEntry(0, ["qHull(currentSet, v1, v2)", "  if(currentSet.len==0)","     return", "  slope ← (v2.y - v1.y)/(v2.x - v1.x)", "  y-intercept ← (slope*v1.x*(-1))+y-intercept", "max ← [0, point]"], "fnTop"); + this.code += pcEntry(1, "for (i ← 0 to |currentSet-1|","findMaxLoop"); + this.code += pcEntry(2, ["if max[0] < distance(vi)", "  max ← [distance(vi)], vi"], "findMax") + this.code += pcEntry(1, ["slope1 ← (max[1].y - v1.y)/(max[1].x - v1.x)", "y-intercept1 ← (slope*v1.x*(-1))+v1.y","s1 ← split(currentSet, slope1, y-intercept1)"], "firstSplit"); + this.code += pcEntry(1, ["slope2 ← (v2.y - max[1].y)/(v2.x - max[1].x)", "y-intercept2 ← (slope*max[1].x*(-1))+max[1].y","s2 ← split(currentSet, slope2, y-intercept2)"], "secondSplit"); + this.code += pcEntry(1, ["qHull(s1, v1, max[1])", "qHull(s2, max[1]) , v2"], "postSplits"); + this.code += pcEntry(0, ["split(set, slope, y-intercept)", "  saves ← []", "  for (i ← 0 to |set-1|)"], "splitLoop"); + this.code += pcEntry(2, ["if vi.y > slope*vi.x+y-intercept", "  saves+=vi", " return saves"], "split"); + + const hullSeg =`Hull vertices found: 0 +
'; + this.code += 'sortedPoints[] ← sort(waypoints)
westMost ← sortedPoints[0]
eastMost ← sortedPoints[this.length-1]
slope ← (v2.y - v1.y)/(v2.x - v1.x)
y-intercept ← (slope*v1.x*(-1))+y-intercept
s1 ← split(sortedPoints, slope, y-intercept, +)
s2 ← currentSet-s1
LabelCoordinates
`; + hdxAVCP.update("hullSegments", hullSeg); + const recursionTable = 'Recursive Calls
'; + hdxAVCP.update("recursionStack", recursionTable); + + + }, + // setupUI for quickhull av + setupUI() { + + let newAO=""; + + hdxAV.algOptions.innerHTML = newAO; + + // Setting up the AVCP with the elements that will be used + hdxAVCP.add("hullv1", visualSettings.v1); + hdxAVCP.add("hullv2", visualSettings.v2); + hdxAVCP.add("hullMax", visualSettings.averageCoord); + hdxAVCP.add("checkingLine", visualSettings.visiting); + hdxAVCP.add("recursionStack", visualSettings.hoverV); + hdxAVCP.add("hullSegments", visualSettings.discovered); + }, + // remove any changes made + cleanupUI() { + for(let i = 0; i < this.hullSegments.length; i++){ + this.hullSegments[i].remove(); + } + }, + idOfAction(action) { + return action.label; + }, + + // Calculates the distance from the current vertex to the line to which it is being compared + distance(){ + // finding line that is perpendicular to dividing line + let distSlope = ((this.vTwo.lon-this.vOne.lon)/(this.vTwo.lat-this.vOne.lat))*(-1); + let distYIntercept = this.currentSet[this.nextToCheck].lon*distSlope*(-1)+this.currentSet[this.nextToCheck].lat; + // finding coordinates of point on line + let lonValue = (distYIntercept-this.yIntercept)/(this.slope-distSlope); + let latValue = distSlope*lonValue+distYIntercept; + return exactDistanceInMiles(latValue, lonValue, this.currentSet[this.nextToCheck].lat, this.currentSet[this.nextToCheck].lon) + }, + + // Adds vertices to the AVCP table and adds line segments to the hull when they are discovered + updateHullVertexTable(){ + // adding to table + if(this.hullVertices[0] != this.hullVertices[this.hullVertices.length-1]){ + document.getElementById("hVertsNum").innerText = this.hullVertices.length; + let newTableRow = document.createElement("tr"); + newTableRow.innerHTML = ``+this.hullVertices[this.hullVertices.length-1].label+` + (`+this.hullVertices[this.hullVertices.length-1].lat+`, `+this.hullVertices[this.hullVertices.length-1].lon+`)`; + document.getElementById("hullVertexTable").appendChild(newTableRow); + } + // adding to map hull segments + let hullLine = []; + hullLine[0] = [this.hullVertices[this.hullVertices.length-2].lat, this.hullVertices[this.hullVertices.length-2].lon]; + hullLine[1] = [this.hullVertices[this.hullVertices.length-1].lat, this.hullVertices[this.hullVertices.length-1].lon]; + this.hullSegments.push(L.polyline(hullLine, visualSettings.discovered)); + this.hullSegments[this.hullSegments.length-1].addTo(map); + }, + + // Updates shading on map + updateShading(){ + let pollygonObscure; + if(this.obscureRegion != null){ + this.obscureRegion.remove(); + } + thisobscureRegion = [null, null]; + if(this.vOne.lon < this.vTwo.lon){ + // Standard + pollygonObscure = [[this.vOne.lat, this.vOne.lon], [this.vTwo.lat, this.vTwo.lon], [89, this.vTwo.lon], [89, 179], [-89, 179], [-89, -179], [89, -179], [89, this.vTwo.lon], [this.mostNorth, this.vTwo.lon], [this.mostNorth, this.vOne.lon]]; + }else{ + // Underside + pollygonObscure = [[this.vOne.lat, this.vOne.lon], [this.vTwo.lat, this.vTwo.lon], [-89, this.vTwo.lon], [-89, -179], [89, -179], [89, 179], [-89, 179], [-89, this.vTwo.lon], [this.mostSouth, this.vTwo.lon], [this.mostSouth, this.vOne.lon]]; + } + this.obscureRegion = L.polygon(pollygonObscure, this.obscure); + this.obscureRegion.addTo(map); + } +} \ No newline at end of file diff --git a/HighwayDataExaminer/hdxav.js b/HighwayDataExaminer/hdxav.js index e4a6ed7..5eb778d 100644 --- a/HighwayDataExaminer/hdxav.js +++ b/HighwayDataExaminer/hdxav.js @@ -150,6 +150,7 @@ const hdxAV = { this.avList.push(hdxVertexExtremesSearchAV); this.avList.push(hdxVertexPairsAV); this.avList.push(hdxBFConvexHullAV); + this.avList.push(hdxQHAV); this.avList.push(hdxAPClosestPointAV); this.avList.push(hdxClosestPairsRecAV); this.avList.push(hdxClickDisAV) diff --git a/HighwayDataExaminer/index.php b/HighwayDataExaminer/index.php index e910b0e..efc6426 100644 --- a/HighwayDataExaminer/index.php +++ b/HighwayDataExaminer/index.php @@ -93,6 +93,7 @@ +