From 388b26959048b60015280d792ecd8ccf61c521b9 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Mon, 10 Jun 2019 23:24:28 +0100
Subject: [PATCH] Get a basic voronoi diagram showing. yay!

---
 client_src/js/Helpers/SVGWriter.mjs      |  21 +--
 client_src/js/MapManager.mjs             |  10 +-
 client_src/js/Overlay/VoronoiCell.mjs    |   2 +-
 client_src/js/Overlay/VoronoiOverlay.mjs |  96 ++++++++----
 package-lock.json                        | 186 ++++++++++++++---------
 package.json                             |  17 ++-
 6 files changed, 207 insertions(+), 125 deletions(-)

diff --git a/client_src/js/Helpers/SVGWriter.mjs b/client_src/js/Helpers/SVGWriter.mjs
index 68f608f..90cd67c 100644
--- a/client_src/js/Helpers/SVGWriter.mjs
+++ b/client_src/js/Helpers/SVGWriter.mjs
@@ -17,24 +17,22 @@ class SvgWriter {
 	 * @param	{String}	[heightspec="100%"]
 	 * @param	{Retangle}	[viewBox=null]
 	 */
-	constructor(widthspec = "100%", heightspec = "100%", viewBox = null) {
+	constructor(widthspec = "100%", heightspec = "100%", viewBox = null, pretty_print = false) {
 		this.unitSuffix = "";
 		
 		// ----------------------------------
 		
-		this.xml = new XMLWriter();
+		this.xml = new XMLWriter(pretty_print);
 	
 		this.xml.startDocument();
 		this.xml.writeDocType("svg", "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", null);
-		this.xml.writeComment("Generated by SVGWriter which was written by Starbeamrainbowlabs");
+		this.xml.writeComment("Generated by SVGWriter.js, which was written by Starbeamrainbowlabs & ported from SVGWriter.cs");
 		this.xml.startElement("svg", "http://www.w3.org/2000/svg");
 		this.xml.writeAttribute("version", "1.1");
-		this.xml.writeAttribute("x", "0");
-		this.xml.writeAttribute("y", "0");
 		this.xml.writeAttribute("width", widthspec);
 		this.xml.writeAttribute("height", heightspec);
 
-		if (rawViewBox != null) {
+		if (viewBox != null) {
 			this.xml.writeAttribute(
 				"viewBox",
 				`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`
@@ -165,13 +163,12 @@ class SvgWriter {
 	 * @param	{Vector2[]}	points		The co-ordinates that make up the polygon.
 	 * @return	{this}
 	 */
-	addPolygon(fillStyle, points)
-	{
+	addPolygon(fillStyle, points) {
 		this.xml.startElement("polygon");
 		this.xml.writeAttribute("fill", fillStyle);
 		this.xml.writeAttribute(
 			"points",
-			points.map((point) => `${point.x}${point.y}`).join(" ")
+			points.map((point) => `${point.x},${point.y}`).join(" ")
 		);
 		this.xml.endElement();
 		return this;
@@ -199,4 +196,10 @@ class SvgWriter {
 	}
 }
 
+SvgWriter.string2element = (svg_string) => {
+	let temp = document.createElement("div");
+	temp.innerHTML = svg_string;
+	return temp.querySelector("svg");
+}
+
 export default SvgWriter;
diff --git a/client_src/js/MapManager.mjs b/client_src/js/MapManager.mjs
index 5d73327..b1aeb64 100644
--- a/client_src/js/MapManager.mjs
+++ b/client_src/js/MapManager.mjs
@@ -72,12 +72,12 @@ class MapManager {
 	setup_overlay() {
 		this.overlay = new VoronoiOverlay();
 		this.overlay.addCells(
-			new VoronoiCell(new Vector2(100, 100)),
-			new VoronoiCell(new Vector2(100, 200)),
-			new VoronoiCell(new Vector2(75, 100)),
-			new VoronoiCell(new Vector2(50, 50))
+			new VoronoiCell(new Vector2(53.739429, -0.445607)),
+			new VoronoiCell(new Vector2(53.775879, -0.413569)),
+			new VoronoiCell(new Vector2(53.745144, -0.279373)),
+			new VoronoiCell(new Vector2(53.738222, -0.335983))
 		);
-		let svg = this.overlay.render();
+		this.overlay.add_to(this.map);
 	}
 	
 	setup_time_dimension() {
diff --git a/client_src/js/Overlay/VoronoiCell.mjs b/client_src/js/Overlay/VoronoiCell.mjs
index ca2d3dc..6dad771 100644
--- a/client_src/js/Overlay/VoronoiCell.mjs
+++ b/client_src/js/Overlay/VoronoiCell.mjs
@@ -9,7 +9,7 @@ import Vector2 from '../Helpers/Vector2.mjs';
 class VoronoiCell {
 	constructor(in_point) {
 		this.point = in_point;
-		this.def = null;
+		this.polygon = null;
 	}
 }
 
diff --git a/client_src/js/Overlay/VoronoiOverlay.mjs b/client_src/js/Overlay/VoronoiOverlay.mjs
index 90ec315..91ab4f4 100644
--- a/client_src/js/Overlay/VoronoiOverlay.mjs
+++ b/client_src/js/Overlay/VoronoiOverlay.mjs
@@ -1,6 +1,7 @@
 "use strict";
 
-import Voronoi from 'voronoi';
+import L from 'leaflet';
+import { Delaunay } from 'd3-delaunay';
 
 import Vector2 from '../Helpers/Vector2.mjs';
 import Rectangle from '../Helpers/Rectangle.mjs';
@@ -28,48 +29,89 @@ class VoronoiOverlay {
 	 * @return {Rectangle} The bounding box of the currently registered points.
 	 */
 	computeBoundingBox() {
-		let result = new Rectangle(Infinity, Infinity, -Infinity, -Infinity);
-		
-		for(let cell of this.cells) {
-			if(cell.point.x < result.x) result.x = cell.point.x;
-			if(cell.point.y < result.y) result.y = cell.point.y;
-			if(cell.point.x > result.Right) result.Right = cell.point.x;
-			if(cell.point.y > result.Bottom) result.Bottom = cell.point.y;
+		let result = {
+			x_min: Infinity,
+			x_max: -Infinity,
+			y_min: Infinity,
+			y_max: -Infinity
 		}
 		
-		result.Left -= this.border.x;
-		result.Right += this.border.x;
-		result.Top -= this.border.y;
-		result.Bottom += this.border.y;
+		for(let cell of this.cells) {
+			if(!isNaN(cell.point.x)) {
+				if(cell.point.x < result.x_min) result.x_min = cell.point.x;
+				if(cell.point.x > result.x_max) result.x_max = cell.point.x;
+			}
+			if(!isNaN(cell.point.y)) {
+				if(cell.point.y < result.y_min) result.y_min = cell.point.y;
+				if(cell.point.y > result.y_max) result.y_max = cell.point.y;
+			}
+		}
 		
-		return result;
+		result.x_min -= this.border.x;
+		result.y_min -= this.border.y;
+		result.x_max += this.border.x;
+		result.y_max += this.border.y;
+		
+		return new Rectangle(
+			result.x_min,
+			result.y_min,
+			result.x_max - result.x_min,
+			result.y_max - result.y_min
+		);
 	}
 	
 	render() {
 		let bounding_box = this.computeBoundingBox();
 		
-		// Recycle the diagram object if possible
-		if(typeof VoronoiOverlay.diagram !== "undefined")
-			VoronoiOverlay.voronoi.recycle(VoronoiOverlay.diagram);
+		let voronoi = Delaunay.from(this.cells.map(
+			(cell) => [cell.point.x, cell.point.y])
+		).voronoi([
+			bounding_box.x, bounding_box.y,
+			bounding_box.Right, bounding_box.Bottom
+		]);
 		
-		VoronoiOverlay.diagram = VoronoiOverlay.voronoi.compute(
-			this.cells.map((cell) => cell.point),
-			bounding_box
-		);
-		
-		console.log(VoronoiOverlay.diagram);
+		console.log(voronoi);
 		
 		// TODO: Map the generated polygons back onto this.cells
-		for(let their_cell of VoronoiOverlay.diagram.cells) {
-			let our_cell = this.cells.find((el) => el.point.x == their_cell.site.x && el.point.y == their_cell.site.y);
-			our_cell.def = their_cell;
+		let i = 0;
+		for(let polygon of voronoi.cellPolygons()) {
+			let our_cell = this.cells[i];
+			our_cell.polygon = polygon.map((point) => new Vector2(...point));
+			
+			i++;
 		}
 		
 		console.log(this.cells);
 		
-		this.svg = new SvgWriter();
+		this.svg = new SvgWriter(
+			"100%", "100%",
+			bounding_box,
+			true
+		);
 		
 		// TODO: Render the SVG here
+		for(let cell of this.cells) {
+			this.svg.addPolygon(
+				`hsla(${(Math.random()*360).toFixed(2)}, 50%, 50%, 0.6)`,
+				cell.polygon
+			);
+			this.svg.addCircle(cell.point, 0.005, "red");
+		}
+		
+		this.svg.complete();
+		return this.svg.toString();
+	}
+	
+	add_to(map) {
+		let bounds = this.computeBoundingBox();
+		this.layer = L.svgOverlay(
+			SvgWriter.string2element(this.render()),
+			L.latLngBounds(
+				L.latLng(bounds.TopLeft.x, bounds.TopLeft.y),
+				L.latLng(bounds.BottomRight.x, bounds.BottomRight.y)
+			)
+		);
+		this.layer.addTo(map);
 	}
 	
 	generate_overlay() {
@@ -77,7 +119,5 @@ class VoronoiOverlay {
 	}
 }
 
-VoronoiOverlay.voronoi = new Voronoi();
-
 
 export default VoronoiOverlay;
diff --git a/package-lock.json b/package-lock.json
index 31bb7a6..b42862c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,9 +25,15 @@
       }
     },
     "@types/chart.js": {
-      "version": "2.7.51",
-      "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.51.tgz",
-      "integrity": "sha512-bGIgvM8SzTSkKLWa6+uF5hNyH2K9Zdz6F8HUfsdP0j2744vuxXyY+hkHVvPeDuaWocltf4DRwInRN3+bqXW3ow==",
+      "version": "2.7.53",
+      "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.53.tgz",
+      "integrity": "sha512-z4j5PWjQff3qGF+5nJIegI5b7drtGA6+XAxoAOINZYkkUuwlmBvjbGtruUAugbaxQkLcCklKZ88dZaPSnFNggg==",
+      "dev": true
+    },
+    "@types/d3-delaunay": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-4.1.0.tgz",
+      "integrity": "sha512-KWZn+XNDLTtoR3zMpAI2go4PbRcazlOsOmlrmZAwYOgV7lYRx469ii78LLswRDI9J6jJmrlCl6Zo9ccoWFfrIw==",
       "dev": true
     },
     "@types/estree": {
@@ -61,9 +67,9 @@
       }
     },
     "@types/node": {
-      "version": "11.11.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.1.tgz",
-      "integrity": "sha512-2azXFP9n4aA2QNLkKm/F9pzKxgYj1SMawZ5Eh9iC21RH3XNcFsivLVU2NhpMgQm7YobSByvIol4c42ZFusXFHQ==",
+      "version": "12.0.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.7.tgz",
+      "integrity": "sha512-1YKeT4JitGgE4SOzyB9eMwO0nGVNkNEsm9qlIt1Lqm/tG2QEiSMTD4kS3aO6L+w5SClLVxALmIBESK6Mk5wX0A==",
       "dev": true
     },
     "@types/q": {
@@ -486,9 +492,9 @@
       }
     },
     "commander": {
-      "version": "2.19.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
-      "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
+      "version": "2.20.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
       "dev": true
     },
     "component-emitter": {
@@ -893,6 +899,14 @@
         }
       }
     },
+    "d3-delaunay": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-4.1.5.tgz",
+      "integrity": "sha512-rBKVohmXT9+BrDicH8umAVUwtkfLIydVlWnpIEDUZ4l2e1vXCaKbypByF8tkN8TUUKnzJY0s8ldQEroeBRMO9Q==",
+      "requires": {
+        "delaunator": "^2.0.0"
+      }
+    },
     "debug": {
       "version": "2.6.9",
       "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -970,6 +984,11 @@
         }
       }
     },
+    "delaunator": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-2.0.5.tgz",
+      "integrity": "sha512-GSYmkITO56erpQzv5Pw+8Vg769kurM16IVUq/AcMb5ZCJCtV7Z2agx9lJ7EbbLno8L099iH2d+hvAK34ZnsvIQ=="
+    },
     "dom-create-element-query-selector": {
       "version": "github:hekigan/dom-create-element-query-selector#39afc3fb24cc06d280ef81e167acf3d21513e3a2",
       "from": "github:hekigan/dom-create-element-query-selector"
@@ -1574,6 +1593,15 @@
         }
       }
     },
+    "is-reference": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.2.tgz",
+      "integrity": "sha512-Kn5g8c7XHKejFOpTf2QN9YjiHHKl5xRj+2uAZf9iM2//nkBNi/NNeB5JMoun28nEaUVHyPUzqzhfRlfAirEjXg==",
+      "dev": true,
+      "requires": {
+        "@types/estree": "0.0.39"
+      }
+    },
     "is-regex": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
@@ -1631,12 +1659,11 @@
       "dev": true
     },
     "jest-worker": {
-      "version": "24.4.0",
-      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.4.0.tgz",
-      "integrity": "sha512-BH9X/klG9vxwoO99ZBUbZFfV8qO0XNZ5SIiCyYK2zOuJBl6YJVAeNIQjcoOVNu4HGEHeYEKsUWws8kSlSbZ9YQ==",
+      "version": "24.6.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz",
+      "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==",
       "dev": true,
       "requires": {
-        "@types/node": "*",
         "merge-stream": "^1.0.1",
         "supports-color": "^6.1.0"
       },
@@ -1696,9 +1723,9 @@
       }
     },
     "leaflet": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.4.0.tgz",
-      "integrity": "sha512-x9j9tGY1+PDLN9pcWTx9/y6C5nezoTMB8BLK5jTakx+H7bPlnbCHfi9Hjg+Qt36sgDz/cb9lrSpNQXmk45Tvhw=="
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.5.1.tgz",
+      "integrity": "sha512-ekM9KAeG99tYisNBg0IzEywAlp0hYI5XRipsqRXyRTeuU8jcuntilpp+eFf5gaE0xubc9RuSNIVtByEKwqFV0w=="
     },
     "leaflet-fullscreen": {
       "version": "1.0.2",
@@ -1912,9 +1939,9 @@
       "integrity": "sha1-qiEiUSr49vpr3G8y1nqm3whvfK0="
     },
     "nightdocs": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/nightdocs/-/nightdocs-1.0.4.tgz",
-      "integrity": "sha512-zlZTyxXJ4MkZgt2Gkc8xeT/9fex5TF+jiScngGTNMNxkwhtL6Yi7CQJyHUHP/H/vdSXqfWEAuVrITYETS2+ChQ==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nightdocs/-/nightdocs-1.0.5.tgz",
+      "integrity": "sha512-z8e1ffeWF67QYu7I6bU5/4WreDFjKJVvfRRVvnoWcyjkWUz4Nn2COVOSWr7ypAdyNbDwMb6Cbf8ZZsTfXX2Tlg==",
       "dev": true,
       "requires": {
         "marked": "^0.6.1",
@@ -1923,9 +1950,9 @@
       }
     },
     "nightink": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/nightink/-/nightink-0.1.2.tgz",
-      "integrity": "sha512-nAyyf1EvghaFtmwD4ox2rFiY0eWqAsW6rulIPaPC5IMv3YaNKYdQ4F7zUZ6E7eONEVQGtiCGTv6YksY9wHHs1g==",
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/nightink/-/nightink-0.1.3.tgz",
+      "integrity": "sha512-eGlInN79j0g/QdTf8GFwuUvGbIsgWBfcNop219q9Nbf8WUlplDu7AriHoyKY+B0IeAB9zWg59NlBlJUIqmpEiQ==",
       "dev": true,
       "requires": {
         "await-fs": "^1.0.0",
@@ -3609,77 +3636,93 @@
       "dev": true
     },
     "rollup": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.10.1.tgz",
-      "integrity": "sha512-pW353tmBE7QP622ITkGxtqF0d5gSRCVPD9xqM+fcPjudeZfoXMFW2sCzsTe2TU/zU1xamIjiS9xuFCPVT9fESw==",
+      "version": "1.14.6",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.14.6.tgz",
+      "integrity": "sha512-A8f54Fms9PAG2VBLBg/XOBgN6tRQKgroltD86f+gF5+6eRmAAlFBJAFV0K7MHl2aHOCcrATWO1B8SAjVD2Ehsw==",
       "dev": true,
       "requires": {
         "@types/estree": "0.0.39",
-        "@types/node": "^11.13.5",
+        "@types/node": "^12.0.7",
         "acorn": "^6.1.1"
-      },
-      "dependencies": {
-        "@types/node": {
-          "version": "11.13.6",
-          "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.6.tgz",
-          "integrity": "sha512-Xoo/EBzEe8HxTSwaZNLZjaW6M6tA/+GmD3/DZ6uo8qSaolE/9Oarko0oV1fVfrLqOz0tx0nXJB4rdD5c+vixLw==",
-          "dev": true
-        }
       }
     },
     "rollup-plugin-commonjs": {
-      "version": "9.3.4",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.3.4.tgz",
-      "integrity": "sha512-DTZOvRoiVIHHLFBCL4pFxOaJt8pagxsVldEXBOn6wl3/V21wVaj17HFfyzTsQUuou3sZL3lEJZVWKPFblJfI6w==",
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.0.0.tgz",
+      "integrity": "sha512-B8MoX5GRpj3kW4+YaFO/di2JsZkBxNjVmZ9LWjUoTAjq8N9wc7HObMXPsrvolVV9JXVtYSscflXM14A19dXPNQ==",
       "dev": true,
       "requires": {
         "estree-walker": "^0.6.0",
+        "is-reference": "^1.1.2",
         "magic-string": "^0.25.2",
-        "resolve": "^1.10.0",
-        "rollup-pluginutils": "^2.6.0"
+        "resolve": "^1.10.1",
+        "rollup-pluginutils": "^2.7.0"
       },
       "dependencies": {
         "resolve": {
-          "version": "1.10.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
-          "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+          "version": "1.11.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
+          "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
           "dev": true,
           "requires": {
             "path-parse": "^1.0.6"
           }
         },
         "rollup-pluginutils": {
-          "version": "2.6.0",
-          "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz",
-          "integrity": "sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ==",
+          "version": "2.8.1",
+          "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz",
+          "integrity": "sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==",
           "dev": true,
           "requires": {
-            "estree-walker": "^0.6.0",
-            "micromatch": "^3.1.10"
+            "estree-walker": "^0.6.1"
+          },
+          "dependencies": {
+            "estree-walker": {
+              "version": "0.6.1",
+              "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
+              "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
+              "dev": true
+            }
           }
         }
       }
     },
     "rollup-plugin-node-resolve": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.2.3.tgz",
-      "integrity": "sha512-r+WaesPzdGEynpLZLALFEDugA4ACa5zn7bc/+LVX4vAXQQ8IgDHv0xfsSvJ8tDXUtprfBtrDtRFg27ifKjcJTg==",
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.0.1.tgz",
+      "integrity": "sha512-9s3dTu44SKQZM/Pwll42GpqXgT+WdvO0Ga01lF8cwZqJGqRUATtD+GrP3uIzZdpnbPonEJbVasfFt80VGPQqKw==",
       "dev": true,
       "requires": {
         "@types/resolve": "0.0.8",
         "builtin-modules": "^3.1.0",
         "is-module": "^1.0.0",
-        "resolve": "^1.10.0"
+        "resolve": "^1.11.0",
+        "rollup-pluginutils": "^2.8.0"
       },
       "dependencies": {
+        "estree-walker": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
+          "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
+          "dev": true
+        },
         "resolve": {
-          "version": "1.10.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
-          "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+          "version": "1.11.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
+          "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
           "dev": true,
           "requires": {
             "path-parse": "^1.0.6"
           }
+        },
+        "rollup-pluginutils": {
+          "version": "2.8.1",
+          "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz",
+          "integrity": "sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==",
+          "dev": true,
+          "requires": {
+            "estree-walker": "^0.6.1"
+          }
         }
       }
     },
@@ -3750,15 +3793,15 @@
       }
     },
     "rollup-plugin-terser": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-4.0.4.tgz",
-      "integrity": "sha512-wPANT5XKVJJ8RDUN0+wIr7UPd0lIXBo4UdJ59VmlPCtlFsE20AM+14pe+tk7YunCsWEiuzkDBY3QIkSCjtrPXg==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.0.0.tgz",
+      "integrity": "sha512-W+jJ4opYnlmNyVW0vtRufs+EGf68BIJ7bnOazgz8mgz8pA9lUyrEifAhPs5y9M16wFeAyBGaRjKip4dnFBtXaw==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
-        "jest-worker": "^24.0.0",
-        "serialize-javascript": "^1.6.1",
-        "terser": "^3.14.1"
+        "jest-worker": "^24.6.0",
+        "serialize-javascript": "^1.7.0",
+        "terser": "^4.0.0"
       }
     },
     "rollup-pluginutils": {
@@ -3807,9 +3850,9 @@
       "dev": true
     },
     "serialize-javascript": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz",
-      "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==",
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz",
+      "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==",
       "dev": true
     },
     "set-value": {
@@ -3991,9 +4034,9 @@
       }
     },
     "source-map-support": {
-      "version": "0.5.11",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.11.tgz",
-      "integrity": "sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ==",
+      "version": "0.5.12",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
+      "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
       "dev": true,
       "requires": {
         "buffer-from": "^1.0.0",
@@ -4165,9 +4208,9 @@
       "integrity": "sha1-Rw9vL6VGYGTrGpUB10wWqIPPv9o="
     },
     "terser": {
-      "version": "3.17.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz",
-      "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.0.0.tgz",
+      "integrity": "sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA==",
       "dev": true,
       "requires": {
         "commander": "^2.19.0",
@@ -4362,11 +4405,6 @@
       "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
       "dev": true
     },
-    "voronoi": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/voronoi/-/voronoi-1.0.0.tgz",
-      "integrity": "sha1-wIPK+pjENZpRgBi/6IYkyyJ6wpE="
-    },
     "xml-writer": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/xml-writer/-/xml-writer-1.7.0.tgz",
diff --git a/package.json b/package.json
index 85d57ae..ad20ef2 100644
--- a/package.json
+++ b/package.json
@@ -19,31 +19,32 @@
 	"homepage": "https://github.com/sbrl/ConnectedHumber-Air-Quality-Interface#readme",
 	"dependencies": {
 		"chart.js": "^2.8.0",
+		"d3-delaunay": "^4.1.5",
 		"dom-create-element-query-selector": "github:hekigan/dom-create-element-query-selector",
 		"iso8601-js-period": "^0.2.1",
-		"leaflet": "^1.4.0",
+		"leaflet": "^1.5.1",
 		"leaflet-fullscreen": "^1.0.2",
 		"leaflet.markercluster": "^1.4.1",
 		"moment": "^2.24.0",
 		"nanomodal": "^5.1.1",
 		"smartsettings": "^1.2.3",
 		"tabs": "^0.2.0",
-		"voronoi": "^1.0.0",
 		"xml-writer": "^1.7.0"
 	},
 	"devDependencies": {
-		"@types/chart.js": "^2.7.51",
+		"@types/chart.js": "^2.7.53",
+		"@types/d3-delaunay": "^4.1.0",
 		"@types/leaflet": "^1.4.4",
 		"@types/leaflet-fullscreen": "^1.0.4",
-		"nightdocs": "^1.0.4",
+		"nightdocs": "^1.0.5",
 		"postcss-copy": "^7.1.0",
 		"postcss-import": "^12.0.1",
-		"rollup": "^1.10.1",
-		"rollup-plugin-commonjs": "^9.3.4",
-		"rollup-plugin-node-resolve": "^4.2.3",
+		"rollup": "^1.14.6",
+		"rollup-plugin-commonjs": "^10.0.0",
+		"rollup-plugin-node-resolve": "^5.0.1",
 		"rollup-plugin-postcss": "^2.0.3",
 		"rollup-plugin-replace": "^2.2.0",
-		"rollup-plugin-terser": "^4.0.4"
+		"rollup-plugin-terser": "^5.0.0"
 	},
 	"docpress": {
 		"github": "ConnectedHumber/Air-Quality-Web",
