From 8a05492eb4c3f33d2aa320a7f3071f617231e721 Mon Sep 17 00:00:00 2001 From: Ronit Jadhav Date: Fri, 27 Oct 2023 10:47:06 +0200 Subject: [PATCH 1/3] added complex polygon and point features --- cases/point-rendering/index.html | 16 ++ .../main.js | 150 +++++----- cases/polygon-rendering/index.html | 16 ++ cases/polygon-rendering/main.js | 257 ++++++++++++++++++ cases/vector-rendering/index.html | 31 --- index.html | 13 +- package-lock.json | 7 + package.json | 1 + 8 files changed, 391 insertions(+), 100 deletions(-) create mode 100644 cases/point-rendering/index.html rename cases/{vector-rendering => point-rendering}/main.js (57%) create mode 100644 cases/polygon-rendering/index.html create mode 100644 cases/polygon-rendering/main.js delete mode 100644 cases/vector-rendering/index.html diff --git a/cases/point-rendering/index.html b/cases/point-rendering/index.html new file mode 100644 index 0000000..59754d9 --- /dev/null +++ b/cases/point-rendering/index.html @@ -0,0 +1,16 @@ + + + + + + + Point Rendering + + + + +
+ + + + diff --git a/cases/vector-rendering/main.js b/cases/point-rendering/main.js similarity index 57% rename from cases/vector-rendering/main.js rename to cases/point-rendering/main.js index 70c1ff2..2964391 100644 --- a/cases/vector-rendering/main.js +++ b/cases/point-rendering/main.js @@ -1,4 +1,5 @@ /* eslint-disable no-console */ +import GUI from 'lil-gui'; import GeoJSON from 'ol/format/GeoJSON.js'; import Layer from 'ol/layer/Layer.js'; import Link from 'ol/interaction/Link.js'; @@ -24,40 +25,29 @@ const source = new VectorSource(); const colors = ['#6ff05b', '#00AAFF', '#faa91e']; -const style = { - 'fill-color': ['get', 'color'], - 'stroke-color': 'gray', - 'stroke-width': 0.5, -}; +const gui = new GUI(); -class WebGLLayer extends Layer { - /** - * @return {WebGLVectorLayerRenderer} The renderer. - */ - createRenderer() { - return new WebGLVectorLayerRenderer(this, { - style, - }); - } -} +const link = new Link(); -function useWebGL() { - map.getLayers().clear(); - map.addLayer(new WebGLLayer({source})); -} +const style = { + 'circle-radius': ['get', 'radius'], + 'circle-fill-color': ['get', 'color'], + 'circle-stroke-color': 'gray', + 'circle-stroke-width': 0.5, +}; -function useCanvas() { - map.getLayers().clear(); - map.addLayer(new VectorLayer({source, style})); -} +const gui_obj = { + 'Feature count': 200000, + 'Use WebGL': false, + 'Radius': 4, +}; -const link = new Link(); +const useWebGLCheckbox = gui.add(gui_obj, 'Use WebGL'); +const featureCountSlider = gui.add(gui_obj, 'Feature count', 100000, 500000); +const radiusMeasure = gui.add(gui_obj, 'Radius', 4, 40, 1); -const webglToggle = /** @type {HTMLInputElement} */ ( - document.getElementById('renderer') -); -webglToggle.addEventListener('change', function () { - if (webglToggle.checked) { +useWebGLCheckbox.onChange((/** @type {boolean} */ value) => { + if (value) { link.update('renderer', 'webgl'); useWebGL(); } else { @@ -66,63 +56,82 @@ webglToggle.addEventListener('change', function () { } }); +featureCountSlider.onFinishChange(() => { + link.update('count', featureCountSlider.getValue()); + resetData(parseInt(featureCountSlider.getValue()), gui_obj.Radius); +}); + +radiusMeasure.onFinishChange(() => { + link.update('radius', radiusMeasure.getValue()); + resetData(gui_obj['Feature count'], gui_obj.Radius); +}); + const initialRenderer = link.track('renderer', (newRenderer) => { if (newRenderer === 'webgl') { - webglToggle.checked = true; + gui_obj['Use WebGL'] = true; + useWebGLCheckbox.listen(); useWebGL(); } else { - webglToggle.checked = false; useCanvas(); + gui_obj['Use WebGL'] = false; + useWebGLCheckbox.listen(); } }); -webglToggle.checked = initialRenderer === 'webgl'; - -const countSelect = /** @type {HTMLSelectElement} */ ( - document.getElementById('count') -); -countSelect.addEventListener('change', function () { - link.update('count', countSelect.value); - resetData(parseInt(countSelect.value)); -}); const initialCount = link.track('count', (newCount) => { - resetData(parseInt(newCount)); + resetData(parseInt(newCount), parseInt(radiusMeasure.getValue())); +}); + +const initialRadius = link.track('radius', (newRadius) => { + resetData(parseInt(featureCountSlider.getValue()), parseInt(newRadius)); }); -if (initialCount) { - countSelect.value = initialCount; -} map.addInteraction(link); +class WebGLLayer extends Layer { + /** + * @return {WebGLVectorLayerRenderer} The renderer. + */ + createRenderer() { + return new WebGLVectorLayerRenderer(this, { + style, + }); + } +} + +function useWebGL() { + map.getLayers().clear(); + map.addLayer(new WebGLLayer({source})); +} + +function useCanvas() { + map.getLayers().clear(); + map.addLayer(new VectorLayer({source, style})); +} + /** - * @param {number} count The number of features to create. - * @return {import('geojson').FeatureCollection} The features. + * @param {number} count + * @param {number} radius */ -function makeData(count) { - const size = 180 / Math.floor(Math.sqrt(count / 2)); + +function makeData(count, radius) { + const size = 400 / Math.floor(Math.sqrt(count / 2)); /** * @type {Array} */ const features = []; for (let lon = -180; lon < 180 - size / 4; lon += size) { for (let lat = -90; lat < 90 - size / 4; lat += size) { - const buffer = (0.1 + Math.random() * 0.1) * size; + const buffer = (0.3 + Math.random() * 0.2) * size * (radius / 5); // Increase the buffer for larger points features.push({ type: 'Feature', properties: { color: colors[Math.floor(Math.random() * colors.length)], + radius, }, geometry: { - type: 'Polygon', - coordinates: [ - [ - [lon + buffer, lat + buffer], - [lon + size - buffer, lat + buffer], - [lon + size - buffer, lat + size - buffer], - [lon + buffer, lat + size - buffer], - [lon + buffer, lat + buffer], - ], - ], + type: 'Point', + coordinates: [lon + buffer, lat + buffer], }, }); } @@ -156,10 +165,11 @@ async function addFeatures(features) { /** * @param {number} count The number of features to create. + * @param {number} numVertices */ -function resetData(count) { +function resetData(count, numVertices) { source.clear(); - const data = makeData(count); + const data = makeData(count, numVertices); const features = parseFeatures(data); addFeatures(features); console.time('first render'); @@ -168,10 +178,22 @@ function resetData(count) { function main() { const count = initialCount ? parseInt(initialCount) - : parseInt(countSelect.value); - resetData(count); + : parseInt(featureCountSlider.getValue()); + const radius = initialRadius + ? parseInt(initialRadius) + : parseInt(radiusMeasure.getValue()); + + resetData(count, radius); + + gui_obj['Feature count'] = count; + gui_obj.Radius = radius; + featureCountSlider.listen(); + radiusMeasure.listen(); + + gui_obj['Use WebGL'] = initialRenderer === 'webgl'; + useWebGLCheckbox.listen(); - if (initialRenderer === 'webgl') { + if (gui_obj['Use WebGL']) { useWebGL(); } else { useCanvas(); diff --git a/cases/polygon-rendering/index.html b/cases/polygon-rendering/index.html new file mode 100644 index 0000000..4114d41 --- /dev/null +++ b/cases/polygon-rendering/index.html @@ -0,0 +1,16 @@ + + + + + + + Polygon Rendering + + + + +
+ + + + diff --git a/cases/polygon-rendering/main.js b/cases/polygon-rendering/main.js new file mode 100644 index 0000000..c54789e --- /dev/null +++ b/cases/polygon-rendering/main.js @@ -0,0 +1,257 @@ +/* eslint-disable no-console */ +import GUI from 'lil-gui'; +import GeoJSON from 'ol/format/GeoJSON.js'; +import Layer from 'ol/layer/Layer.js'; +import Link from 'ol/interaction/Link.js'; +import Map from 'ol/Map.js'; +import VectorLayer from 'ol/layer/Vector.js'; +import VectorSource from 'ol/source/Vector.js'; +import View from 'ol/View.js'; +import WebGLVectorLayerRenderer from 'ol/renderer/webgl/VectorLayer.js'; +import {useGeographic} from 'ol/proj.js'; + +useGeographic(); + +const map = new Map({ + layers: [], + target: 'map', + view: new View({ + center: [0, 0], + zoom: 4, + }), +}); + +const source = new VectorSource(); + +const colors = ['#6ff05b', '#00AAFF', '#faa91e']; + +const gui = new GUI(); + +const link = new Link(); + +const style = { + 'fill-color': ['get', 'color'], + 'stroke-color': 'gray', + 'stroke-width': 0.5, +}; + +const gui_obj = { + 'Feature count': 200000, + 'Use WebGL': false, + 'Vertices': 4, + 'Outline': true, +}; + +const useWebGLCheckbox = gui.add(gui_obj, 'Use WebGL'); +const featureCountSlider = gui.add(gui_obj, 'Feature count', 100000, 500000); +const verticesCount = gui.add(gui_obj, 'Vertices', 3, 20, 1); +const outlineCheckbox = gui.add(gui_obj, 'Outline'); + +useWebGLCheckbox.onChange((/** @type {boolean} */ value) => { + if (value) { + link.update('renderer', 'webgl'); + useWebGL(); + } else { + link.update('renderer', 'canvas'); + useCanvas(); + } +}); + +featureCountSlider.onFinishChange(() => { + link.update('count', featureCountSlider.getValue()); + resetData( + parseInt(featureCountSlider.getValue()), + parseInt(verticesCount.getValue()) + ); +}); + +verticesCount.onFinishChange(() => { + link.update('vertices', verticesCount.getValue()); + resetData( + parseInt(featureCountSlider.getValue()), + parseInt(verticesCount.getValue()) + ); +}); + +outlineCheckbox.onChange((/** @type {boolean} */ value) => { + link.update('outline', value ? 'yes' : 'no'); + if (value) { + style['stroke-width'] = 0.5; + } else { + delete style['stroke-width']; + } + + resetData(featureCountSlider.getValue(), verticesCount.getValue()) + + if (gui_obj['Use WebGL']) { + useWebGL(); + } else { + useCanvas(); + } +}); + +const initialRenderer = link.track('renderer', (newRenderer) => { + if (newRenderer === 'webgl') { + gui_obj['Use WebGL'] = true; + useWebGLCheckbox.listen(); + useWebGL(); + } else { + useCanvas(); + gui_obj['Use WebGL'] = false; + useWebGLCheckbox.listen(); + } +}); + +const initialCount = link.track('count', (newCount) => { + resetData(parseInt(newCount), parseInt(verticesCount.getValue())); +}); + +const initialVertices = link.track('vertices', (newVertices) => { + resetData(parseInt(featureCountSlider.getValue()), parseInt(newVertices)); +}); + +const initialOutline = link.track('outline', (newOutline) => { + if (newOutline === 'yes') { + style['stroke-width'] = 0.5; + } else { + delete style['stroke-width']; + } +}); + +map.addInteraction(link); + +class WebGLLayer extends Layer { + /** + * @return {WebGLVectorLayerRenderer} The renderer. + */ + createRenderer() { + return new WebGLVectorLayerRenderer(this, { + style, + }); + } +} + +function useWebGL() { + map.getLayers().clear(); + map.addLayer(new WebGLLayer({source})); +} + +function useCanvas() { + map.getLayers().clear(); + map.addLayer(new VectorLayer({source, style})); +} + +/** + * @param {number} count + * @param {number} numVertices + */ + +function makeData(count, numVertices) { + const size = 400 / Math.floor(Math.sqrt(count / 2)); // Increase the size for larger polygons + /** + * @type {Array} + */ + const features = []; + for (let lon = -180; lon < 180 - size / 4; lon += size) { + for (let lat = -90; lat < 90 - size / 4; lat += size) { + const buffer = (0.3 + Math.random() * 0.2) * size; // Increase the buffer for larger polygons + + // Calculate the angle between vertices + const angleStep = (2 * Math.PI) / numVertices; + + // Generate the vertices of the polygon + const polygonCoordinates = []; + for (let i = 0; i < numVertices; i++) { + const angle = i * angleStep; + const x = lon + size / 2 + buffer * Math.cos(angle); + const y = lat + size / 2 + buffer * Math.sin(angle); + polygonCoordinates.push([x, y]); + } + // Close the polygon by adding the first vertex at the end + polygonCoordinates.push(polygonCoordinates[0]); + + features.push({ + type: 'Feature', + properties: { + color: colors[Math.floor(Math.random() * colors.length)], + }, + geometry: { + type: 'Polygon', + coordinates: [polygonCoordinates], + }, + }); + } + } + return { + type: 'FeatureCollection', + features, + }; +} + +const format = new GeoJSON(); +/** + * @param {import('geojson').FeatureCollection} data The GeoJSON data. + * @return {Array} The features. + */ +function parseFeatures(data) { + console.time('parse features'); + const features = format.readFeatures(data); + console.timeEnd('parse features'); + return features; +} + +/** + * @param {Array} features The features. + */ +async function addFeatures(features) { + console.time('add features'); + source.addFeatures(features); + console.timeEnd('add features'); +} + +/** + * @param {number} count The number of features to create. + * @param {number} numVertices + */ +function resetData(count, numVertices) { + source.clear(); + const data = makeData(count, numVertices); + const features = parseFeatures(data); + addFeatures(features); + console.time('first render'); +} + +function main() { + + const count = initialCount ? parseInt(initialCount) : parseInt(featureCountSlider.getValue()); + const vertices = initialVertices + ? parseInt(initialVertices) + : parseInt(verticesCount.getValue()); + + gui_obj.Outline = initialOutline === 'yes'; + outlineCheckbox.listen(); + + if (gui_obj.Outline) { + style['stroke-width'] = 0.5; + } else { + delete style['stroke-width']; + } + + resetData(count, vertices); + + gui_obj['Feature count'] = count; + gui_obj.Vertices = vertices; + featureCountSlider.listen(); + verticesCount.listen(); + + gui_obj['Use WebGL'] = initialRenderer === 'webgl'; + useWebGLCheckbox.listen(); + + if (gui_obj['Use WebGL']) { + useWebGL(); + } else { + useCanvas(); + } +} + +main(); diff --git a/cases/vector-rendering/index.html b/cases/vector-rendering/index.html deleted file mode 100644 index dce99c3..0000000 --- a/cases/vector-rendering/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - Vector Rendering - - - - -
-
- -
- -
- - - - - diff --git a/index.html b/index.html index e81e504..87f1a9d 100644 --- a/index.html +++ b/index.html @@ -9,11 +9,14 @@ - + diff --git a/package-lock.json b/package-lock.json index 47c8fea..63e7859 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@types/geojson": "^7946.0.10", "eslint": "^8.49.0", "eslint-config-openlayers": "^18.0.0", + "lil-gui": "^0.18.2", "typescript": "^5.2.2", "vite": "^4.4.9" } @@ -2097,6 +2098,12 @@ "node": ">= 0.8.0" } }, + "node_modules/lil-gui": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.18.2.tgz", + "integrity": "sha512-DgdrLy3/KGC0PiQLKgOcJMPItP4xY4iWgJ9+91Zaxfr8GCTmMps05QS9w9jW7yspILlbscbquwjOwxmWnSx5Uw==", + "dev": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", diff --git a/package.json b/package.json index 7fa1c8e..3ec945c 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@types/geojson": "^7946.0.10", "eslint": "^8.49.0", "eslint-config-openlayers": "^18.0.0", + "lil-gui": "^0.18.2", "typescript": "^5.2.2", "vite": "^4.4.9" }, From d8d5784ca4341396702fc5791c546711d3920568 Mon Sep 17 00:00:00 2001 From: Ronit Jadhav Date: Sun, 29 Oct 2023 13:31:10 +0100 Subject: [PATCH 2/3] delete/add stoke-color for outline --- cases/point-rendering/main.js | 3 ++- cases/polygon-rendering/main.js | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/cases/point-rendering/main.js b/cases/point-rendering/main.js index 2964391..543cc6b 100644 --- a/cases/point-rendering/main.js +++ b/cases/point-rendering/main.js @@ -144,7 +144,7 @@ function makeData(count, radius) { const format = new GeoJSON(); /** - * @param {import('geojson').FeatureCollection} data The GeoJSON data. + * @param {{features: Array, type: string}} data The GeoJSON data. * @return {Array} The features. */ function parseFeatures(data) { @@ -167,6 +167,7 @@ async function addFeatures(features) { * @param {number} count The number of features to create. * @param {number} numVertices */ + function resetData(count, numVertices) { source.clear(); const data = makeData(count, numVertices); diff --git a/cases/polygon-rendering/main.js b/cases/polygon-rendering/main.js index c54789e..dde6933 100644 --- a/cases/polygon-rendering/main.js +++ b/cases/polygon-rendering/main.js @@ -77,11 +77,13 @@ outlineCheckbox.onChange((/** @type {boolean} */ value) => { link.update('outline', value ? 'yes' : 'no'); if (value) { style['stroke-width'] = 0.5; + style['stroke-color'] = 'gray'; } else { delete style['stroke-width']; + delete style['stroke-color']; } - resetData(featureCountSlider.getValue(), verticesCount.getValue()) + resetData(featureCountSlider.getValue(), verticesCount.getValue()); if (gui_obj['Use WebGL']) { useWebGL(); @@ -113,8 +115,10 @@ const initialVertices = link.track('vertices', (newVertices) => { const initialOutline = link.track('outline', (newOutline) => { if (newOutline === 'yes') { style['stroke-width'] = 0.5; + style['stroke-color'] = 'gray'; } else { delete style['stroke-width']; + delete style['stroke-color']; } }); @@ -190,7 +194,7 @@ function makeData(count, numVertices) { const format = new GeoJSON(); /** - * @param {import('geojson').FeatureCollection} data The GeoJSON data. + * @param {{features: Array, type: string}} data The GeoJSON data. * @return {Array} The features. */ function parseFeatures(data) { @@ -213,6 +217,7 @@ async function addFeatures(features) { * @param {number} count The number of features to create. * @param {number} numVertices */ + function resetData(count, numVertices) { source.clear(); const data = makeData(count, numVertices); @@ -222,8 +227,9 @@ function resetData(count, numVertices) { } function main() { - - const count = initialCount ? parseInt(initialCount) : parseInt(featureCountSlider.getValue()); + const count = initialCount + ? parseInt(initialCount) + : parseInt(featureCountSlider.getValue()); const vertices = initialVertices ? parseInt(initialVertices) : parseInt(verticesCount.getValue()); @@ -233,8 +239,10 @@ function main() { if (gui_obj.Outline) { style['stroke-width'] = 0.5; + style['stroke-color'] = 'gray'; } else { delete style['stroke-width']; + delete style['stroke-color']; } resetData(count, vertices); From 5653bcd7ad23514dfe4ebe1878745ae92bc74f41 Mon Sep 17 00:00:00 2001 From: Ronit Jadhav Date: Thu, 2 Nov 2023 14:37:58 +0100 Subject: [PATCH 3/3] Resolved type type check errors --- cases/point-rendering/main.js | 5 +++++ cases/polygon-rendering/main.js | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cases/point-rendering/main.js b/cases/point-rendering/main.js index 543cc6b..6ba9345 100644 --- a/cases/point-rendering/main.js +++ b/cases/point-rendering/main.js @@ -29,7 +29,12 @@ const gui = new GUI(); const link = new Link(); +/** + * @type {import('ol/style/flat.js').FlatStyle & import('ol/style/literal.js').LiteralStyle} + */ const style = { + // This has to be fixed upstream + // @ts-ignore 'circle-radius': ['get', 'radius'], 'circle-fill-color': ['get', 'color'], 'circle-stroke-color': 'gray', diff --git a/cases/polygon-rendering/main.js b/cases/polygon-rendering/main.js index dde6933..c0b0b52 100644 --- a/cases/polygon-rendering/main.js +++ b/cases/polygon-rendering/main.js @@ -29,6 +29,10 @@ const gui = new GUI(); const link = new Link(); +/** + * + * @type {import('ol/style/flat.js').FlatStyle & import('ol/style/literal.js').LiteralStyle} + */ const style = { 'fill-color': ['get', 'color'], 'stroke-color': 'gray', @@ -241,8 +245,12 @@ function main() { style['stroke-width'] = 0.5; style['stroke-color'] = 'gray'; } else { - delete style['stroke-width']; - delete style['stroke-color']; + if (style['stroke-width'] !== undefined) { + delete style['stroke-width']; + } + if (style['stroke-color'] !== undefined) { + delete style['stroke-color']; + } } resetData(count, vertices);