diff --git a/generic-pin.svg b/generic-pin.svg
new file mode 100644
index 0000000..88872f5
--- /dev/null
+++ b/generic-pin.svg
@@ -0,0 +1,13 @@
+
+
diff --git a/main.js b/main.js
index 510e50d..e1ce3a6 100644
--- a/main.js
+++ b/main.js
@@ -11,6 +11,14 @@ import ToggleMenuControl from './ui/controls.js';
import layerCategories from './layers/index.js';
+import VectorLayer from 'ol/layer/Vector';
+import {Vector as VectorSource} from 'ol/source.js';
+import GeoJSON from 'ol/format/GeoJSON.js';
+import {Style} from 'ol/style.js';
+import Icon from 'ol/style/Icon.js';
+
+import pin from './generic-pin.svg?url';
+
// from https://openlayers.org/en/latest/examples/popup.html
const container = document.getElementById('popup');
const content = document.getElementById('popup-content');
@@ -140,6 +148,71 @@ for (let category of layerCategories) {
}
}
+const customLayerDiv = document.createElement("div");
+customLayerDiv.innerHTML = `
+
+ Custom
+
+
+
+
+ (must be in GeoJSON format)
+
+ `;
+const labelInput = customLayerDiv.querySelector('input[name=label]');
+const sourceInput = customLayerDiv.querySelector('input[name=source]');
+const colorInput = customLayerDiv.querySelector('input[name=color]');
+
+customLayerDiv.querySelector("button").addEventListener("click", function(){
+ newCustomLayer(labelInput.value, sourceInput.value, colorInput.value);
+});
+document.querySelector("aside").appendChild(customLayerDiv);
+
+// https://stackoverflow.com/a/64090995
+let hslToRgb = (h,s,l, a=s*Math.min(l,1-l), f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1)) => [f(0),f(8),f(4)];
+
+function newCustomLayer(name, sourceURL, colorString) {
+ let color;
+ if (colorString) {
+ color = colorString.split(' ').map(Number);
+ if (color.length != 3 || color.some(Number.isNaN)) {
+ alert("Invalid color provided; using random color instead.");
+ color = null;
+ }
+ }
+ if (!color) {
+ color = hslToRgb(Math.random()*360, 1, 0.5).map(c => c * 255);
+ }
+ const li = document.createElement("li");
+ const layer = new VectorLayer({
+ source: new VectorSource({
+ // In case people put in layers that don't serve proper CORS headers, we
+ // wrap them in this proxy so they Just Work.
+ url: `https://corsproxy.io/?${encodeURIComponent(sourceURL)}`,
+ format: new GeoJSON,
+ }),
+ style: new Style({
+ image: new Icon({
+ anchor: [0.5, 1],
+ src: pin,
+ color: color,
+ }),
+ }),
+ });
+ li.innerHTML = `
+
+ `;
+ li.querySelector("input").addEventListener("change", function(e){
+ if (e.target.checked) {
+ map.getLayers().push(layer);
+ } else {
+ map.getLayers().remove(layer);
+ }
+ });
+ map.getLayers().push(layer);
+ customLayerDiv.querySelector("ul").appendChild(li);
+}
+
let location_set = false;
if (urlLayers.length > 0) {