From 178061ab9de04c0ec00e240ca18011d6448144c1 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Wed, 14 Nov 2018 19:26:49 +0100 Subject: [PATCH] Use ol/layer/Layer instead of ol/source/ImageCanvas in d3 example --- examples/d3.html | 2 +- examples/d3.js | 124 +++++++++++++++++++++++++---------------------- 2 files changed, 67 insertions(+), 59 deletions(-) diff --git a/examples/d3.html b/examples/d3.html index eeff216b37..6dcbfecc66 100644 --- a/examples/d3.html +++ b/examples/d3.html @@ -3,7 +3,7 @@ layout: example.html title: d3 Integration shortdesc: Example of using OpenLayers and d3 together. docs: > -

The example loads TopoJSON geometries and uses d3 (d3.geo.path) to render these geometries to a canvas element that is then used as the image of an OpenLayers image layer.

+ The example loads TopoJSON geometries and uses d3 (d3.geo.path) to render these geometries to a SVG element. tags: "d3" resources: - https://unpkg.com/d3@4.12.0/build/d3.js diff --git a/examples/d3.js b/examples/d3.js index 70a431d2b8..8294539a76 100644 --- a/examples/d3.js +++ b/examples/d3.js @@ -1,10 +1,70 @@ -import Map from '../src/ol/Map.js'; +import Map from '../src/ol/CompositeMap.js'; import View from '../src/ol/View.js'; import {getWidth, getCenter} from '../src/ol/extent.js'; -import {Image as ImageLayer, Tile as TileLayer} from '../src/ol/layer.js'; +import {Layer, Tile as TileLayer} from '../src/ol/layer.js'; +import SourceState from '../src/ol/source/State'; import {fromLonLat, toLonLat} from '../src/ol/proj.js'; -import {ImageCanvas as ImageCanvasSource, Stamen} from '../src/ol/source.js'; +import Stamen from '../src/ol/source/Stamen.js'; +class CanvasLayer extends Layer { + + constructor(options) { + super(options); + + this.features = options.features; + + this.svg = d3.select(document.createElement('div')).append('svg') + .style('position', 'absolute'); + + this.svg.append('path') + .datum(this.features) + .attr('class', 'boundary'); + } + + getSourceState() { + return SourceState.READY; + } + + render(frameState) { + const width = frameState.size[0]; + const height = frameState.size[1]; + const projection = frameState.viewState.projection; + const d3Projection = d3.geoMercator().scale(1).translate([0, 0]); + let d3Path = d3.geoPath().projection(d3Projection); + + const pixelBounds = d3Path.bounds(this.features); + const pixelBoundsWidth = pixelBounds[1][0] - pixelBounds[0][0]; + const pixelBoundsHeight = pixelBounds[1][1] - pixelBounds[0][1]; + + const geoBounds = d3.geoBounds(this.features); + const geoBoundsLeftBottom = fromLonLat(geoBounds[0], projection); + const geoBoundsRightTop = fromLonLat(geoBounds[1], projection); + let geoBoundsWidth = geoBoundsRightTop[0] - geoBoundsLeftBottom[0]; + if (geoBoundsWidth < 0) { + geoBoundsWidth += getWidth(projection.getExtent()); + } + const geoBoundsHeight = geoBoundsRightTop[1] - geoBoundsLeftBottom[1]; + + const widthResolution = geoBoundsWidth / pixelBoundsWidth; + const heightResolution = geoBoundsHeight / pixelBoundsHeight; + const r = Math.max(widthResolution, heightResolution); + const scale = r / frameState.viewState.resolution; + + const center = toLonLat(getCenter(frameState.extent), projection); + d3Projection.scale(scale).center(center).translate([width / 2, height / 2]); + + d3Path = d3Path.projection(d3Projection); + d3Path(this.features); + + this.svg.attr('width', width); + this.svg.attr('height', height); + + this.svg.select('path') + .attr('d', d3Path); + + return this.svg.node(); + } +} const map = new Map({ layers: [ @@ -26,62 +86,10 @@ const map = new Map({ * Load the topojson data and create an ol/layer/Image for that data. */ d3.json('data/topojson/us.json', function(error, us) { - const features = topojson.feature(us, us.objects.counties); - /** - * This function uses d3 to render the topojson features to a canvas. - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/size~Size} size Size. - * @param {module:ol/proj/Projection~Projection} projection Projection. - * @return {HTMLCanvasElement} A canvas element. - */ - const canvasFunction = function(extent, resolution, pixelRatio, size, projection) { - const canvasWidth = size[0]; - const canvasHeight = size[1]; - - const canvas = d3.select(document.createElement('canvas')); - canvas.attr('width', canvasWidth).attr('height', canvasHeight); - - const context = canvas.node().getContext('2d'); - - const d3Projection = d3.geoMercator().scale(1).translate([0, 0]); - let d3Path = d3.geoPath().projection(d3Projection); - - const pixelBounds = d3Path.bounds(features); - const pixelBoundsWidth = pixelBounds[1][0] - pixelBounds[0][0]; - const pixelBoundsHeight = pixelBounds[1][1] - pixelBounds[0][1]; - - const geoBounds = d3.geoBounds(features); - const geoBoundsLeftBottom = fromLonLat(geoBounds[0], projection); - const geoBoundsRightTop = fromLonLat(geoBounds[1], projection); - let geoBoundsWidth = geoBoundsRightTop[0] - geoBoundsLeftBottom[0]; - if (geoBoundsWidth < 0) { - geoBoundsWidth += getWidth(projection.getExtent()); - } - const geoBoundsHeight = geoBoundsRightTop[1] - geoBoundsLeftBottom[1]; - - const widthResolution = geoBoundsWidth / pixelBoundsWidth; - const heightResolution = geoBoundsHeight / pixelBoundsHeight; - const r = Math.max(widthResolution, heightResolution); - const scale = r / (resolution / pixelRatio); - - const center = toLonLat(getCenter(extent), projection); - d3Projection.scale(scale).center(center) - .translate([canvasWidth / 2, canvasHeight / 2]); - d3Path = d3Path.projection(d3Projection).context(context); - d3Path(features); - context.stroke(); - - return canvas.node(); - }; - - const layer = new ImageLayer({ - source: new ImageCanvasSource({ - canvasFunction: canvasFunction, - projection: 'EPSG:3857' - }) + const layer = new CanvasLayer({ + features: topojson.feature(us, us.objects.counties) }); + map.addLayer(layer); });