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) {