From d3b492d17544dbd1eb199343cd581cab961bce6c Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Sun, 1 Dec 2019 12:16:39 +0100 Subject: [PATCH] Interactive SVG layer example --- examples/data/world.svg | 5188 +++++++++++++++++++++++++++++++++++++++ examples/svg-layer.css | 7 + examples/svg-layer.html | 9 + examples/svg-layer.js | 48 + src/ol/ol.css | 6 + src/ol/transform.js | 18 + 6 files changed, 5276 insertions(+) create mode 100644 examples/data/world.svg create mode 100644 examples/svg-layer.css create mode 100644 examples/svg-layer.html create mode 100644 examples/svg-layer.js diff --git a/examples/data/world.svg b/examples/data/world.svg new file mode 100644 index 0000000000..7c193a2e51 --- /dev/null +++ b/examples/data/world.svg @@ -0,0 +1,5188 @@ + + + diff --git a/examples/svg-layer.css b/examples/svg-layer.css new file mode 100644 index 0000000000..f1624f129a --- /dev/null +++ b/examples/svg-layer.css @@ -0,0 +1,7 @@ +#map { + background: black; +} + +.svg-layer path:hover { + opacity: 0.4; +} diff --git a/examples/svg-layer.html b/examples/svg-layer.html new file mode 100644 index 0000000000..2d5d0355d2 --- /dev/null +++ b/examples/svg-layer.html @@ -0,0 +1,9 @@ +--- +layout: example.html +title: SVG Layer +shortdesc: Example of a map with an interactive svg layer. +docs: > + With a plain `ol/Layer` and a `render` function, we can use an interactive svg as layer. Hover over countries to see the interactivity that is defined purely with a css `:hover` pseudo-class. +tags: "svg, layer, render, transform" +--- +
diff --git a/examples/svg-layer.js b/examples/svg-layer.js new file mode 100644 index 0000000000..6c56a53706 --- /dev/null +++ b/examples/svg-layer.js @@ -0,0 +1,48 @@ +import Map from '../src/ol/Map.js'; +import View from '../src/ol/View.js'; +import Layer from '../src/ol/layer/Layer.js'; +import {composeCssTransform} from '../src/ol/transform.js'; + +const map = new Map({ + target: 'map', + view: new View({ + center: [0, 0], + extent: [-180, -90, 180, 90], + projection: 'EPSG:4326', + zoom: 2 + }) +}); + +const svgContainer = document.createElement('div'); +const xhr = new XMLHttpRequest(); +xhr.open('GET', 'data/world.svg'); +xhr.addEventListener('load', function() { + const svg = xhr.responseXML.documentElement; + svgContainer.ownerDocument.importNode(svg); + svgContainer.appendChild(svg); +}); +xhr.send(); + +const width = 2560; +const height = 1280; +const svgResolution = 360 / width; +svgContainer.style.width = width + 'px'; +svgContainer.style.height = height + 'px'; +svgContainer.style.transformOrigin = 'top left'; +svgContainer.className = 'svg-layer'; + +map.addLayer(new Layer({ + render: function(frameState) { + const scale = svgResolution / frameState.viewState.resolution; + const center = frameState.viewState.center; + const size = frameState.size; + const cssTransform = composeCssTransform( + size[0] / 2, size[1] / 2, + scale, scale, + frameState.viewState.rotation, + -center[0] / svgResolution - width / 2, center[1] / svgResolution - height / 2); + svgContainer.style.transform = cssTransform; + svgContainer.style.opacity = this.getOpacity(); + return svgContainer; + } +})); diff --git a/src/ol/ol.css b/src/ol/ol.css index 36845ba789..0a3ee06e0e 100644 --- a/src/ol/ol.css +++ b/src/ol/ol.css @@ -74,6 +74,12 @@ user-select: none; -webkit-tap-highlight-color: rgba(0,0,0,0); } +.ol-overlaycontainer, .ol-overlaycontainer-stopevent { + pointer-events: none; +} +.ol-overlaycontainer > *, .ol-overlaycontainer-stopevent > * { + pointer-events: auto; +} .ol-selectable { -webkit-touch-callout: default; -webkit-user-select: text; diff --git a/src/ol/transform.js b/src/ol/transform.js index c7511480fb..3eb9ecd6ac 100644 --- a/src/ol/transform.js +++ b/src/ol/transform.js @@ -210,6 +210,24 @@ export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) { return transform; } +/** + * Creates a composite transform given an initial translation, scale, rotation, and + * final translation (in that order only, not commutative). The resulting transform + * string can be applied as `transform` porperty of an HTMLElement's style. + * @param {number} dx1 Initial translation x. + * @param {number} dy1 Initial translation y. + * @param {number} sx Scale factor x. + * @param {number} sy Scale factor y. + * @param {number} angle Rotation (in counter-clockwise radians). + * @param {number} dx2 Final translation x. + * @param {number} dy2 Final translation y. + * @return {string} The composite css transform. + * @api + */ +export function composeCssTransform(dx1, dy1, sx, sy, angle, dx2, dy2) { + return toString(compose(create(), dx1, dy1, sx, sy, angle, dx2, dy2)); +} + /** * Invert the given transform.