diff --git a/rendering/cases/layer-vector-decluttering/expected.png b/rendering/cases/layer-vector-decluttering/expected.png new file mode 100644 index 0000000000..9e862e38d8 Binary files /dev/null and b/rendering/cases/layer-vector-decluttering/expected.png differ diff --git a/rendering/cases/layer-vector-decluttering/main.js b/rendering/cases/layer-vector-decluttering/main.js new file mode 100644 index 0000000000..a709c5d207 --- /dev/null +++ b/rendering/cases/layer-vector-decluttering/main.js @@ -0,0 +1,161 @@ +import Map from '../../../src/ol/Map.js'; +import View from '../../../src/ol/View.js'; +import VectorSource from '../../../src/ol/source/Vector.js'; +import VectorLayer from '../../../src/ol/layer/Vector.js'; +import Feature from '../../../src/ol/Feature.js'; +import Point from '../../../src/ol/geom/Point.js'; +import Style from '../../../src/ol/style/Style.js'; +import Text from '../../../src/ol/style/Text.js'; +import CircleStyle from '../../../src/ol/style/Circle.js'; +import Stroke from '../../../src/ol/style/Stroke.js'; +import LineString from '../../../src/ol/geom/LineString.js'; + +let center = [1825927.7316762917, 6143091.089223046]; +const map = new Map({ + pixelRatio: 1, + target: 'map', + view: new View({ + center: center, + zoom: 13 + }) +}); + +const source1 = new VectorSource(); +const layer1 = new VectorLayer({ + declutter: true, + source: source1 +}); + +const source2 = new VectorSource(); +const layer2 = new VectorLayer({ + declutter: true, + source: source2 +}); + +const source3 = new VectorSource(); +const layer3 = new VectorLayer({ + declutter: true, + source: source3 +}); + +const source4 = new VectorSource(); +const layer4 = new VectorLayer({ + declutter: true, + source: source4 +}); + +const feature1 = new Feature({ + geometry: new Point(center), + zIndex: 2 +}); +source1.addFeature(feature1); +source1.addFeature(new Feature({ + geometry: new Point([center[0] - 540, center[1]]), + zIndex: 3 +})); +source1.addFeature(new Feature({ + geometry: new Point([center[0] + 540, center[1]]), + zIndex: 1 +})); +layer1.setStyle(function(feature) { + return new Style({ + image: new CircleStyle({ + radius: 15, + stroke: new Stroke({ + color: 'blue' + }) + }) + }); +}); +map.addLayer(layer1); + +center = [center[0] + 500, center[1] + 500]; +const feature2 = new Feature({ + geometry: new Point(center), + text: 'center', + zIndex: 2 +}); +source2.addFeature(feature2); +source2.addFeature(new Feature({ + geometry: new Point([center[0] - 540, center[1]]), + text: 'west', + zIndex: 3 +})); +source2.addFeature(new Feature({ + geometry: new Point([center[0] + 540, center[1]]), + text: 'east', + zIndex: 1 +})); +layer2.setStyle(function(feature) { + return new Style({ + text: new Text({ + text: feature.get('text'), + font: '12px sans-serif' + }) + }); +}); +map.addLayer(layer2); + +center = [center[0] + 500, center[1] + 500]; +source3.addFeature(new Feature({ + geometry: new Point(center), + text: 'center' +})); +source3.addFeature(new Feature({ + geometry: new Point([center[0] - 540, center[1]]), + text: 'west' +})); +source3.addFeature(new Feature({ + geometry: new Point([center[0] + 540, center[1]]), + text: 'east' +})); +layer3.setStyle(function(feature) { + return new Style({ + image: new CircleStyle({ + radius: 5, + stroke: new Stroke({ + color: 'red' + }) + }), + text: new Text({ + text: feature.get('text'), + font: '12px sans-serif', + textBaseline: 'bottom', + offsetY: -5 + }) + }); +}); +map.addLayer(layer3); + +center = [center[0] - 2000, center[1] - 2000]; +const point = new Feature(new Point(center)); +point.setStyle(new Style({ + zIndex: 2, + image: new CircleStyle({ + radius: 8, + stroke: new Stroke({ + color: 'blue' + }) + }) +})); +const line = new Feature(new LineString([ + [center[0] - 650, center[1] - 200], + [center[0] + 650, center[1] - 200] +])); +line.setStyle(new Style({ + zIndex: 1, + stroke: new Stroke({ + color: '#CCC', + width: 12 + }), + text: new Text({ + placement: 'line', + text: 'east-west', + font: '12px sans-serif' + }) +})); +source4.addFeature(point); +source4.addFeature(line); +map.addLayer(layer4); + +render({tolerance: 0.02}); diff --git a/rendering/cases/layer-vector-polygon-partial/expected.png b/rendering/cases/layer-vector-polygon-partial/expected.png new file mode 100644 index 0000000000..56e2fa461b Binary files /dev/null and b/rendering/cases/layer-vector-polygon-partial/expected.png differ diff --git a/rendering/cases/layer-vector-polygon-partial/main.js b/rendering/cases/layer-vector-polygon-partial/main.js new file mode 100644 index 0000000000..8370649101 --- /dev/null +++ b/rendering/cases/layer-vector-polygon-partial/main.js @@ -0,0 +1,54 @@ +import Map from '../../../src/ol/Map.js'; +import View from '../../../src/ol/View.js'; +import VectorLayer from '../../../src/ol/layer/Vector.js'; +import VectorSource from '../../../src/ol/source/Vector.js'; +import Feature from '../../../src/ol/Feature.js'; +import Polygon from '../../../src/ol/geom/Polygon.js'; +import Style from '../../../src/ol/style/Style.js'; +import Stroke from '../../../src/ol/style/Stroke.js'; +import Fill from '../../../src/ol/style/Fill.js'; + +const src = new VectorSource({ + features: [ + new Feature(new Polygon([[ + [-22, 18], + [-22, 78], + [-9, 78], + [-9, 18], + [-22, 18] + ]])), + new Feature(new Polygon([[ + [-9, 18], + [-9, 78], + [4, 78], + [4, 18], + [-9, 18] + ]])) + ] +}); +const layer = new VectorLayer({ + renderBuffer: 0, + source: src, + style: new Style({ + stroke: new Stroke({ + color: [0, 0, 0, 1], + width: 2 + }), + fill: new Fill({ + color: [255, 0, 0, 1] + }) + }) +}); +const view = new View({ + center: [-9.5, 78], + zoom: 2, + projection: 'EPSG:4326' +}); +new Map({ + pixelRatio: 1, + layers: [layer], + target: 'map', + view: view +}); + +render(); diff --git a/rendering/cases/layer-vector-polygon/expected.png b/rendering/cases/layer-vector-polygon/expected.png new file mode 100644 index 0000000000..f205b88c89 Binary files /dev/null and b/rendering/cases/layer-vector-polygon/expected.png differ diff --git a/rendering/cases/layer-vector-polygon/main.js b/rendering/cases/layer-vector-polygon/main.js new file mode 100644 index 0000000000..811c6d7673 --- /dev/null +++ b/rendering/cases/layer-vector-polygon/main.js @@ -0,0 +1,43 @@ +import Map from '../../../src/ol/Map.js'; +import View from '../../../src/ol/View.js'; +import VectorLayer from '../../../src/ol/layer/Vector.js'; +import VectorSource from '../../../src/ol/source/Vector.js'; +import Feature from '../../../src/ol/Feature.js'; +import Polygon from '../../../src/ol/geom/Polygon.js'; +import Style from '../../../src/ol/style/Style.js'; +import Fill from '../../../src/ol/style/Fill.js'; + +const feature = new Feature({ + geometry: new Polygon([ + [[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]], + [[0, 60], [-17.6336, 24.2705], [-57.0634, 18.5410], [-28.5317, -9.2705], [-35.2671, -48.5410], [0, -30], [35.2671, -48.5410], [28.5317, -9.2705], [57.0634, 18.5410], [17.6336, 24.2705], [0, 60]] + ]) +}); + +const src = new VectorSource({ + features: [ + feature + ] +}); +const layer = new VectorLayer({ + renderBuffer: 0, + source: src, + style: new Style({ + fill: new Fill({ + color: 'blue' + }) + }) +}); +const view = new View({ + center: [0, 0], + zoom: 1, + projection: 'EPSG:4326' +}); +new Map({ + pixelRatio: 1, + layers: [layer], + target: 'map', + view: view +}); + +render(); diff --git a/rendering/cases/layer-vector/expected.png b/rendering/cases/layer-vector/expected.png new file mode 100644 index 0000000000..e1a9deecf7 Binary files /dev/null and b/rendering/cases/layer-vector/expected.png differ diff --git a/rendering/cases/layer-vector/main.js b/rendering/cases/layer-vector/main.js new file mode 100644 index 0000000000..2b178d5351 --- /dev/null +++ b/rendering/cases/layer-vector/main.js @@ -0,0 +1,110 @@ +import Feature from '../../../src/ol/Feature.js'; +import Map from '../../../src/ol/Map.js'; +import View from '../../../src/ol/View.js'; +import VectorLayer from '../../../src/ol/layer/Vector.js'; +import VectorSource from '../../../src/ol/source/Vector.js'; +import Style from '../../../src/ol/style/Style.js'; +import Stroke from '../../../src/ol/style/Stroke.js'; +import Polygon from '../../../src/ol/geom/Polygon.js'; +import Circle from '../../../src/ol/geom/Circle.js'; +import LineString from '../../../src/ol/geom/LineString.js'; + +const center = [1825927.7316762917, 6143091.089223046]; + +const source1 = new VectorSource(); +const source2 = new VectorSource(); +const vectorLayer1 = new VectorLayer({ + source: source1, + style: new Style({ + stroke: new Stroke({ + color: '#3399CC', + width: 1.25 + }) + }) +}); +const vectorLayer2 = new VectorLayer({ + source: source2, + opacity: 0.6 +}); + +function addCircle(r, source) { + source.addFeature(new Feature(new Circle(center, r))); +} + +function addPolygon(r, source) { + source.addFeature(new Feature(new Polygon([ + [ + [center[0] - r, center[1] - r], + [center[0] + r, center[1] - r], + [center[0] + r, center[1] + r], + [center[0] - r, center[1] + r], + [center[0] - r, center[1] - r] + ] + ]))); +} + +const smallLine = new Feature(new LineString([ + [center[0], center[1] - 1], + [center[0], center[1] + 1] +])); +smallLine.setStyle(new Style({ + zIndex: -99, + stroke: new Stroke({width: 75, color: 'red'}) +})); +smallLine.getGeometry().translate(-1000, 1000); +source1.addFeature(smallLine); +addPolygon(100, source1); +addCircle(200, source1); +addPolygon(250, source1); +addCircle(500, source1); +addPolygon(600, source1); +addPolygon(720, source1); + +const smallLine2 = new Feature(new LineString([ + [center[0], center[1] - 1000], + [center[0], center[1] + 1000] +])); +smallLine2.setStyle([ + new Style({ + stroke: new Stroke({width: 35, color: 'blue'}) + }), + new Style({ + stroke: new Stroke({width: 15, color: 'green'}) + }) +]); +smallLine2.getGeometry().translate(1000, 1000); +source1.addFeature(smallLine2); + +const smallLine3 = new Feature(new LineString([ + [center[0], center[1] - 1], + [center[0], center[1] + 1] +])); +smallLine3.setStyle([ + new Style({ + stroke: new Stroke({width: 75, color: 'red'}) + }), + new Style({ + stroke: new Stroke({width: 45, color: 'white'}) + }) +]); +smallLine3.getGeometry().translate(-1000, -1000); + +addPolygon(400, source2); +addCircle(400, source2); +source2.addFeature(smallLine3); + +const map = new Map({ + layers: [ + vectorLayer1, + vectorLayer2 + ], + target: 'map', + view: new View({ + center: center, + zoom: 13 + }) +}); + +map.getView().setRotation(Math.PI + Math.PI / 4); + +render(); diff --git a/test/rendering/ol/layer/vector.test.js b/test/rendering/ol/layer/vector.test.js deleted file mode 100644 index 4b38fd8e98..0000000000 --- a/test/rendering/ol/layer/vector.test.js +++ /dev/null @@ -1,762 +0,0 @@ -import Feature from '../../../../src/ol/Feature.js'; -import Map from '../../../../src/ol/Map.js'; -import View from '../../../../src/ol/View.js'; -import GeoJSON from '../../../../src/ol/format/GeoJSON.js'; -import Circle from '../../../../src/ol/geom/Circle.js'; -import LineString from '../../../../src/ol/geom/LineString.js'; -import Point from '../../../../src/ol/geom/Point.js'; -import Polygon from '../../../../src/ol/geom/Polygon.js'; -import VectorLayer from '../../../../src/ol/layer/Vector.js'; -import VectorSource from '../../../../src/ol/source/Vector.js'; -import CircleStyle from '../../../../src/ol/style/Circle.js'; -import Fill from '../../../../src/ol/style/Fill.js'; -import Stroke from '../../../../src/ol/style/Stroke.js'; -import Style from '../../../../src/ol/style/Style.js'; -import Text from '../../../../src/ol/style/Text.js'; - - -describe('ol.rendering.layer.Vector', function() { - - const center = [1825927.7316762917, 6143091.089223046]; - - let map; - function createMap() { - map = new Map({ - pixelRatio: 1, - target: createMapDiv(80, 80), - view: new View({ - center: center, - zoom: 13 - }) - }); - } - - afterEach(function() { - if (map) { - disposeMap(map); - } - map = null; - }); - - let source; - - function addCircle(r) { - source.addFeature(new Feature(new Circle(center, r))); - } - - function addPolygon(r) { - source.addFeature(new Feature(new Polygon([ - [ - [center[0] - r, center[1] - r], - [center[0] + r, center[1] - r], - [center[0] + r, center[1] + r], - [center[0] - r, center[1] + r], - [center[0] - r, center[1] - r] - ] - ]))); - } - - function addLineString(r) { - source.addFeature(new Feature(new LineString([ - [center[0] - r, center[1] - r], - [center[0] + r, center[1] - r], - [center[0] + r, center[1] + r], - [center[0] - r, center[1] + r], - [center[0] - r, center[1] - r] - ]))); - } - - describe('vector layer', function() { - - beforeEach(function() { - source = new VectorSource(); - }); - - it('renders opacity correctly with the canvas renderer', function(done) { - createMap(); - const smallLine = new Feature(new LineString([ - [center[0], center[1] - 1], - [center[0], center[1] + 1] - ])); - smallLine.setStyle(new Style({ - zIndex: -99, - stroke: new Stroke({width: 75, color: 'red'}) - })); - source.addFeature(smallLine); - addPolygon(100); - addCircle(200); - addPolygon(250); - addCircle(500); - addPolygon(600); - addPolygon(720); - map.addLayer(new VectorLayer({ - source: source - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas.png', - 17, done); - }); - }); - - it('renders transparent layers correctly with the canvas renderer', function(done) { - createMap(); - const smallLine = new Feature(new LineString([ - [center[0], center[1] - 1], - [center[0], center[1] + 1] - ])); - smallLine.setStyle([ - new Style({ - stroke: new Stroke({width: 75, color: 'red'}) - }), - new Style({ - stroke: new Stroke({width: 45, color: 'white'}) - }) - ]); - source.addFeature(smallLine); - const smallLine2 = new Feature(new LineString([ - [center[0], center[1] - 1000], - [center[0], center[1] + 1000] - ])); - smallLine2.setStyle([ - new Style({ - stroke: new Stroke({width: 35, color: 'blue'}) - }), - new Style({ - stroke: new Stroke({width: 15, color: 'green'}) - }) - ]); - source.addFeature(smallLine2); - - map.addLayer(new VectorLayer({ - source: source, - opacity: 0.5 - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-transparent.png', - 7, done); - }); - }); - - it('renders rotation correctly with the canvas renderer', function(done) { - createMap(); - map.getView().setRotation(Math.PI + Math.PI / 4); - addPolygon(300); - addCircle(500); - map.addLayer(new VectorLayer({ - source: source, - style: new Style({ - stroke: new Stroke({ - width: 2, - color: 'black' - }) - }) - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-rotated.png', - 1.7, done); - }); - }); - - it('renders fill/stroke batches correctly with the canvas renderer', function(done) { - createMap(); - source = new VectorSource({ - overlaps: false - }); - addPolygon(100); - addCircle(200); - addPolygon(250); - addCircle(500); - addPolygon(600); - addPolygon(720); - map.addLayer(new VectorLayer({ - source: source, - style: new Style({ - stroke: new Stroke({ - color: '#3399CC', - width: 1.25 - }) - }) - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-opaque.png', - 24.34, done); - }); - }); - - it('renders stroke batches correctly with the canvas renderer', function(done) { - createMap(); - source = new VectorSource({ - overlaps: false - }); - addLineString(100); - addLineString(250); - addLineString(600); - addLineString(720); - map.addLayer(new VectorLayer({ - source: source, - style: new Style({ - stroke: new Stroke({ - color: '#3399CC', - width: 1.25 - }) - }) - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-stroke.png', - 7, done); - }); - }); - - it('interrupts fill/stroke batches correctly with the canvas renderer', function(done) { - createMap(); - let color; - function createSource(overlaps) { - color = '#3399CC'; - source = new VectorSource({ - overlaps: overlaps - }); - addPolygon(720); - addPolygon(600); - addCircle(500); - addPolygon(250); - addCircle(200); - addPolygon(100); - return source; - } - function alternateColor() { - if (color == '#3399CC') { - color = '#CC9933'; - } else { - color = '#3399CC'; - } - return color; - } - const layer = new VectorLayer({ - source: createSource(true), - style: function(feature) { - alternateColor(); - return new Style({ - stroke: new Stroke({ - color: alternateColor(), - width: 1.25 - }), - fill: new Fill({ - color: alternateColor() - }) - }); - } - }); - map.addLayer(layer); - map.once('postrender', function() { - const canvas = map.getRenderer().canvas_; - // take a snapshot of this `overlaps: true` image - const referenceImage = canvas.toDataURL('image/png'); - // now render the same with `overlaps: false` - layer.setSource(createSource(false)); - // result should be the same as with `overlaps: true` - map.once('postrender', function(e) { - expectResemble(map, referenceImage, 1e-9, done); - }); - }); - }); - - it('interrupts stroke batches correctly with the canvas renderer', function(done) { - createMap(); - let color; - function createSource(overlaps) { - color = '#3399CC'; - source = new VectorSource({ - overlaps: overlaps - }); - addLineString(720); - addLineString(600); - addLineString(250); - addLineString(100); - return source; - } - function alternateColor() { - if (color == '#3399CC') { - color = '#CC9933'; - } else { - color = '#3399CC'; - } - return color; - } - const layer = new VectorLayer({ - source: createSource(true), - style: function(feature) { - alternateColor(); - return new Style({ - stroke: new Stroke({ - color: alternateColor(), - width: 1.25 - }), - fill: new Fill({ - color: alternateColor() - }) - }); - } - }); - map.addLayer(layer); - map.once('postrender', function() { - const canvas = map.getRenderer().canvas_; - // take a snapshot of this `overlaps: true` image - const referenceImage = canvas.toDataURL('image/png'); - // now render the same with `overlaps: false` - layer.setSource(createSource(false)); - // result should be exactly the same as with `overlaps: true` - map.once('postrender', function() { - expectResemble(map, referenceImage, 1e-9, done); - }); - }); - }); - }); - - describe('polygon rendering', function() { - - let map2; - beforeEach(function() { - map2 = new Map({ - pixelRatio: 1, - target: createMapDiv(128, 128), - view: new View({ - center: [0, 0], - zoom: 0 - }) - }); - }); - - afterEach(function() { - disposeMap(map2); - map2 = null; - }); - - it('renders a feature that spans the world', function(done) { - const json = { - type: 'Feature', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90] - ], - [ - [0, 60], [-17.6336, 24.2705], [-57.0634, 18.5410], [-28.5317, -9.2705], [-35.2671, -48.5410], [0, -30], [35.2671, -48.5410], [28.5317, -9.2705], [57.0634, 18.5410], [17.6336, 24.2705], [0, 60] - ] - ] - }, - properties: {} - }; - - const format = new GeoJSON({featureProjection: 'EPSG:3857'}); - const feature = format.readFeature(json); - - const layer = new VectorLayer({ - source: new VectorSource({ - features: [feature] - }), - style: new Style({ - fill: new Fill({ - color: 'blue' - }) - }) - }); - - map2.addLayer(layer); - - map2.once('postrender', function() { - expectResemble(map2, 'rendering/ol/layer/expected/inverted-star.png', 1, done); - }); - - }); - - }); - - describe('Polygon simplification', function() { - - let layer, map3; - - beforeEach(function() { - const src = new VectorSource({ - features: [ - new Feature(new Polygon([[ - [-22, 58], - [-22, 78], - [-9, 78], - [-9, 58], - [-22, 58] - ]])), - new Feature(new Polygon([[ - [-9, 58], - [-9, 78], - [4, 78], - [4, 58], - [-9, 58] - ]])) - ] - }); - layer = new VectorLayer({ - renderBuffer: 0, - source: src - }); - const view = new View({ - center: [-9.5, 78], - zoom: 2, - projection: 'EPSG:4326' - }); - - map3 = new Map({ - pixelRatio: 1, - layers: [layer], - target: createMapDiv(100, 100), - view: view - }); - }); - - afterEach(function() { - disposeMap(map3); - map3 = null; - }); - - it('renders partially out-of-view polygons with a fill and stroke', function(done) { - layer.setStyle(new Style({ - stroke: new Stroke({ - color: [0, 0, 0, 1], - width: 2 - }), - fill: new Fill({ - color: [255, 0, 0, 1] - }) - })); - map3.once('postrender', function() { - expectResemble(map3, 'rendering/ol/layer/expected/vector-canvas-simplified.png', - IMAGE_TOLERANCE, done); - }); - }); - - it('renders partially out-of-view polygons with a fill', function(done) { - layer.setStyle(new Style({ - fill: new Fill({ - color: [0, 0, 0, 1] - }) - })); - map3.once('postrender', function() { - expectResemble(map3, 'rendering/ol/layer/expected/vector-canvas-simplified-fill.png', - IMAGE_TOLERANCE, done); - }); - }); - - it('renders partially out-of-view polygons with a stroke', function(done) { - layer.setStyle(new Style({ - stroke: new Stroke({ - color: [0, 0, 0, 1], - width: 2 - }) - })); - map3.once('postrender', function() { - expectResemble(map3, 'rendering/ol/layer/expected/vector-canvas-simplified-stroke.png', - IMAGE_TOLERANCE, done); - }); - }); - - }); - - describe('decluttering', function() { - - beforeEach(function() { - source = new VectorSource(); - }); - - it('declutters text', function(done) { - createMap(); - const layer = new VectorLayer({ - declutter: true, - source: source - }); - map.addLayer(layer); - - const centerFeature = new Feature({ - geometry: new Point(center), - text: 'center' - }); - source.addFeature(centerFeature); - source.addFeature(new Feature({ - geometry: new Point([center[0] - 540, center[1]]), - text: 'west' - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] + 540, center[1]]), - text: 'east' - })); - - layer.setStyle(function(feature) { - return new Style({ - text: new Text({ - text: feature.get('text'), - font: '12px sans-serif' - }) - }); - }); - - map.once('postrender', function() { - const hitDetected = map.getFeaturesAtPixel([42, 42]); - expect(hitDetected).to.have.length(1); - expect(hitDetected[0]).to.equal(centerFeature); - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter.png', - 2.2, done); - }); - }); - - it('declutters text and respects z-index', function(done) { - createMap(); - const layer = new VectorLayer({ - declutter: true, - source: source - }); - map.addLayer(layer); - - source.addFeature(new Feature({ - geometry: new Point(center), - text: 'center', - zIndex: 2 - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] - 540, center[1]]), - text: 'west', - zIndex: 3 - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] + 540, center[1]]), - text: 'east', - zIndex: 1 - })); - - layer.setStyle(function(feature) { - return new Style({ - zIndex: feature.get('zIndex'), - text: new Text({ - text: feature.get('text'), - font: '12px sans-serif' - }) - }); - }); - - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-zindex.png', - 3.9, done); - }); - }); - - it('declutters images', function(done) { - createMap(); - const layer = new VectorLayer({ - declutter: true, - source: source - }); - map.addLayer(layer); - - const centerFeature = new Feature({ - geometry: new Point(center) - }); - source.addFeature(centerFeature); - source.addFeature(new Feature({ - geometry: new Point([center[0] - 540, center[1]]) - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] + 540, center[1]]) - })); - - layer.setStyle(function(feature) { - return new Style({ - image: new CircleStyle({ - radius: 15, - stroke: new Stroke({ - color: 'blue' - }) - }) - }); - }); - - map.once('postrender', function() { - const hitDetected = map.getFeaturesAtPixel([40, 40]); - expect(hitDetected).to.have.length(1); - expect(hitDetected[0]).to.equal(centerFeature); - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-image.png', - IMAGE_TOLERANCE, done); - }); - }); - - it('declutters images and respects z-index', function(done) { - createMap(); - const layer = new VectorLayer({ - declutter: true, - source: source - }); - map.addLayer(layer); - - source.addFeature(new Feature({ - geometry: new Point(center), - zIndex: 2 - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] - 540, center[1]]), - zIndex: 3 - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] + 540, center[1]]), - zIndex: 1 - })); - - layer.setStyle(function(feature) { - return new Style({ - zIndex: feature.get('zIndex'), - image: new CircleStyle({ - radius: 15, - stroke: new Stroke({ - color: 'blue' - }) - }) - }); - }); - - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-image-zindex.png', - IMAGE_TOLERANCE, done); - }); - }); - - it('declutters image & text groups', function(done) { - createMap(); - const layer = new VectorLayer({ - declutter: true, - source: source - }); - map.addLayer(layer); - - source.addFeature(new Feature({ - geometry: new Point(center), - text: 'center' - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] - 540, center[1]]), - text: 'west' - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] + 540, center[1]]), - text: 'east' - })); - - layer.setStyle(function(feature) { - return new Style({ - image: new CircleStyle({ - radius: 5, - stroke: new Stroke({ - color: 'blue' - }) - }), - text: new Text({ - text: feature.get('text'), - font: '12px sans-serif', - textBaseline: 'bottom', - offsetY: -5 - }) - }); - }); - - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-group.png', - 2.2, done); - }); - }); - - it('declutters text along lines and images', function(done) { - createMap(); - const layer = new VectorLayer({ - declutter: true, - source: source - }); - map.addLayer(layer); - - const point = new Feature(new Point(center)); - point.setStyle(new Style({ - image: new CircleStyle({ - radius: 8, - stroke: new Stroke({ - color: 'blue' - }) - }) - })); - const line = new Feature(new LineString([ - [center[0] - 650, center[1] - 200], - [center[0] + 650, center[1] - 200] - ])); - line.setStyle(new Style({ - stroke: new Stroke({ - color: '#CCC', - width: 12 - }), - text: new Text({ - placement: 'line', - text: 'east-west', - font: '12px sans-serif' - }) - })); - - source.addFeature(point); - source.addFeature(line); - - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-line.png', - IMAGE_TOLERANCE, done); - }); - }); - - it('declutters text along lines and images with z-index', function(done) { - createMap(); - const layer = new VectorLayer({ - declutter: true, - source: source - }); - map.addLayer(layer); - - const point = new Feature(new Point(center)); - point.setStyle(new Style({ - zIndex: 2, - image: new CircleStyle({ - radius: 8, - stroke: new Stroke({ - color: 'blue' - }) - }) - })); - const line = new Feature(new LineString([ - [center[0] - 650, center[1] - 200], - [center[0] + 650, center[1] - 200] - ])); - line.setStyle(new Style({ - zIndex: 1, - stroke: new Stroke({ - color: '#CCC', - width: 12 - }), - text: new Text({ - placement: 'line', - text: 'east-west', - font: '12px sans-serif' - }) - })); - - source.addFeature(point); - source.addFeature(line); - - map.once('postrender', function() { - const hitDetected = map.getFeaturesAtPixel([35, 46]); - expect(hitDetected).to.have.length(1); - expect(hitDetected[0]).to.equal(line); - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-line-zindex.png', - 4.1, done); - }); - }); - }); - -});