Compare commits

...

41 Commits

Author SHA1 Message Date
Andreas Hocevar
e4be8309f7 Merge pull request #10246 from openlayers/release-v6.1.1
Release v6.1.1
2019-11-04 12:25:24 +01:00
Andreas Hocevar
39a5511073 Update package version to 6.1.1 2019-11-04 12:16:04 +01:00
Andreas Hocevar
396f07bea1 Changelog for v6.1.1 2019-11-04 12:14:09 +01:00
Olivier Guyot
33980d0ba8 Merge pull request #10235 from jahow/webgl-optimizations
Various optimizations and fixes for the WebGL points renderer
2019-11-04 10:10:20 +01:00
Olivier Guyot
7da86ae71f Webgl points renderer / slight improvements following review
Also fixes a lint error.
2019-11-04 09:55:54 +01:00
Olivier Guyot
af15cfb815 Icon webgl example / avoid doing hit detection when view is moving 2019-11-04 09:31:38 +01:00
Olivier Guyot
600e1a4647 Webgl points renderer / use a smaller canvas for hit detection render
The hit detection render is now done against a canvas with half the
width/height of the main render. This still provides sufficient precision
while requiring a much smaller memory allocation (especially for
retina devices).
2019-11-04 09:31:38 +01:00
Olivier Guyot
43010c8934 Webgl / return 0 if doing renderTarget.read outside of data width/height
This would happen when WebGLPointsLayerRenderer.forEachFeatureAtCoordinate
is called on "warped" worlds at -360/+360 degrees, and may produce false
positives.
2019-11-04 09:31:38 +01:00
jahow
f7b0f6750b Resolve memory leak when deleting a webgl layer
Various references were kept, preventing the layer and underlying
renderer and webgl context to be garbage collected.

Also, the Helper was simplified because it turns out deleting manually
all Webgl objects is useless: these objects will be released when
the context is garbage collected anyway.

Note: this touches the Layer and BaseLayer classes, as the following were
preventing the layer from being garbage collected:
* layer reference in the `state_` object in BaseLayer
* dangling listener for source change in Layer
2019-11-04 09:31:38 +01:00
Olivier Guyot
e5e03d46a0 Webgl points renderer / more optimizations
Simplify calls in the attributes callback, also less stress
on garbage collection.
2019-11-04 09:31:38 +01:00
Olivier Guyot
e78c14c061 Webgl points renderer / add a cache for features in the source
This allows quicker access to features as well as their geometries
and properties, reducing the time taken by a rebuildBuffers call.
2019-11-04 09:31:38 +01:00
Andreas Hocevar
21f99e01c3 Merge pull request #10244 from ahocevar/no-multi-assign
Update to new eslint config with no-multi-assign
2019-11-02 16:48:46 +01:00
Andreas Hocevar
8098572346 Update package-lock.json 2019-11-02 15:20:35 +01:00
Andreas Hocevar
ac50cc3460 New eslint config with no-multi-assign 2019-11-02 15:20:21 +01:00
Andreas Hocevar
06ae419db6 Merge pull request #10243 from ahocevar/fix-font-measure
Store correct font value
2019-11-02 14:08:03 +01:00
Andreas Hocevar
43d6247671 Add rendering test case 2019-11-01 22:23:34 +01:00
Andreas Hocevar
442213f85b Store correct font value 2019-11-01 22:06:00 +01:00
Andreas Hocevar
2a96b6a8e3 Merge pull request #10239 from ahocevar/simpler-container-reuse
Simpler container reuse
2019-11-01 19:14:05 +01:00
Frédéric Junod
89f92a53b4 Merge pull request #10237 from fredj/zIndex_falsy_value
Fix layer zIndex test with falsy values
2019-11-01 08:25:56 +01:00
Andreas Hocevar
9c26d9d7dd Merge pull request #10221 from Kai-W/source-undefined-bug
Fix for undefined Source
2019-10-31 22:21:45 +01:00
Andreas Hocevar
3de05c249b Fix container transform comparison 2019-10-31 20:29:20 +01:00
Andreas Hocevar
bb2bdb17aa Render vector tile layers to a single canvas 2019-10-31 20:29:19 +01:00
Frederic Junod
b8c70bcbe7 Fix layer zIndex test with falsy values 2019-10-31 15:58:36 +01:00
Frederic Junod
c23d59e3a8 Remove extra argument passed to RenderEvent constructor 2019-10-31 15:47:27 +01:00
Andreas Hocevar
5dec336f94 Merge pull request #10228 from ahocevar/vectortile-hitdetect-rendertile
Use render tiles instead of source tiles for hit detection
2019-10-31 10:44:52 +01:00
Andreas Hocevar
e3f83f3601 Merge pull request #10226 from ahocevar/reproj-tile-abort
ABORT reproj tiles properly
2019-10-30 16:55:35 +01:00
Andreas Hocevar
3d0f7e4af8 Update the vector-tile-selection example 2019-10-30 14:39:54 +01:00
Andreas Hocevar
4b13c6dae0 Speed up rendering by not using alpha 2019-10-30 14:36:23 +01:00
Andreas Hocevar
7097a4c6ba Use render tiles instead of source tiles for hit detection 2019-10-30 11:44:12 +01:00
Andreas Hocevar
79ea5bf9cd Set ABORT state in base class 2019-10-30 10:29:58 +01:00
Andreas Hocevar
97d0d277fb Merge pull request #10224 from ahocevar/hitdetect-vectortiles-fixes
Vector tile hit detection fixes
2019-10-29 22:33:53 +01:00
Andreas Hocevar
025b27bdec Use correct resolution for tile hit canvas 2019-10-29 22:11:46 +01:00
Andreas Hocevar
00af5a87be Only use source tile when it is available 2019-10-29 18:29:42 +01:00
kai.westerkamp
53f6359f8f Fix for undefined Source 2019-10-29 15:38:14 +01:00
Frédéric Junod
bac3a8e9d8 Merge pull request #10218 from fredj/doc_fixes_expression
Documentation fixes in ol/style/expressions
2019-10-29 11:17:27 +01:00
Andreas Hocevar
66eedbfed2 Merge pull request #10216 from ahocevar/changelog-dependabot
Update changelog script to recognize dependabot instead of greenkeeper
2019-10-29 10:56:16 +01:00
Frédéric Junod
7d7228d45c Update src/ol/style/expressions.js
Co-Authored-By: Olivier Guyot <olivier.guyot@camptocamp.com>
2019-10-29 10:50:14 +01:00
Andreas Hocevar
459efede60 Update package-lock.json 2019-10-29 10:47:58 +01:00
Frederic Junod
b0ed775bc6 Documentation fixes 2019-10-29 08:42:11 +01:00
Andreas Hocevar
43ebfc4653 Merge pull request #10215 from openlayers/release-v6.1.0
Release v6.1.0
2019-10-28 22:00:33 +01:00
Andreas Hocevar
49c1486e06 Update changelog script to recognize dependabot instead of greenkeeper 2019-10-28 21:49:18 +01:00
65 changed files with 649 additions and 395 deletions

17
changelog/v6.1.1.md Normal file
View File

@@ -0,0 +1,17 @@
# 6.1.1
Hot on the heels of OpenLayers 6.x, this patch release includes a few fixes for existing functionality. There should be nothing special needed to upgrade an application from 6.x to 6.1.1. See the 6.0.0 release notes for details on upgrading from an older version.
## Changes
* [#10235](https://github.com/openlayers/openlayers/pull/10235) - Various optimizations and fixes for the WebGL points renderer ([@jahow](https://github.com/jahow))
* [#10244](https://github.com/openlayers/openlayers/pull/10244) - Update to new eslint config with no-multi-assign ([@ahocevar](https://github.com/ahocevar))
* [#10243](https://github.com/openlayers/openlayers/pull/10243) - Store correct font value ([@ahocevar](https://github.com/ahocevar))
* [#10239](https://github.com/openlayers/openlayers/pull/10239) - Simpler container reuse ([@ahocevar](https://github.com/ahocevar))
* [#10237](https://github.com/openlayers/openlayers/pull/10237) - Fix layer zIndex test with falsy values ([@fredj](https://github.com/fredj))
* [#10221](https://github.com/openlayers/openlayers/pull/10221) - Fix for undefined Source ([@Kai-W](https://github.com/Kai-W))
* [#10228](https://github.com/openlayers/openlayers/pull/10228) - Use render tiles instead of source tiles for hit detection ([@ahocevar](https://github.com/ahocevar))
* [#10226](https://github.com/openlayers/openlayers/pull/10226) - ABORT reproj tiles properly ([@ahocevar](https://github.com/ahocevar))
* [#10224](https://github.com/openlayers/openlayers/pull/10224) - Vector tile hit detection fixes ([@ahocevar](https://github.com/ahocevar))
* [#10218](https://github.com/openlayers/openlayers/pull/10218) - Documentation fixes in ol/style/expressions ([@fredj](https://github.com/fredj))
* [#10216](https://github.com/openlayers/openlayers/pull/10216) - Update changelog script to recognize dependabot instead of greenkeeper ([@ahocevar](https://github.com/ahocevar))

View File

@@ -59,7 +59,7 @@ const style = {
rotateWithView: false,
offset: [
0,
9
0
],
textureCoord: [
'match',
@@ -144,7 +144,7 @@ map.addLayer(
const info = document.getElementById('info');
map.on('pointermove', function(evt) {
if (map.getView().getInteracting()) {
if (map.getView().getInteracting() || map.getView().getAnimating()) {
return;
}
const pixel = evt.pixel;

View File

@@ -7,18 +7,19 @@ import {Fill, Stroke, Style} from '../src/ol/style.js';
// lookup for selection objects
let selection = {};
// feature property to act as identifier
const idProp = 'iso_a3';
const vtLayer = new VectorTileLayer({
declutter: true,
source: new VectorTileSource({
format: new MVT(),
maxZoom: 15,
format: new MVT({
idProperty: 'iso_a3'
}),
url: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' +
'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf'
}),
style: function(feature) {
const selected = !!selection[feature.get(idProp)];
const selected = !!selection[feature.getId()];
return new Style({
stroke: new Stroke({
color: selected ? 'rgba(200,20,20,0.8)' : 'gray',
@@ -56,7 +57,7 @@ map.on('click', function(event) {
if (!feature) {
return;
}
const fid = feature.get(idProp);
const fid = feature.getId();
if (selectElement.value === 'singleselect') {
selection = {};

View File

@@ -124,6 +124,7 @@ function refreshLayer(newStyle) {
if (previousLayer) {
map.removeLayer(previousLayer);
previousLayer.dispose();
}
literalStyle = newStyle;
}

48
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "ol",
"version": "6.0.1",
"version": "6.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -3636,12 +3636,12 @@
}
},
"eslint-config-openlayers": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-openlayers/-/eslint-config-openlayers-12.0.0.tgz",
"integrity": "sha512-cln+A0qoBvf9ZTVFt9UJ1pTb+TfKOZ5B55jPtYrIcREKXsRycTtWNQjtHQJRbfTPp8CGoU6tjJ6yLUsseVgv0Q==",
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-openlayers/-/eslint-config-openlayers-13.0.0.tgz",
"integrity": "sha512-FlhPbUhrgh9nyIrcf6jX8cZHLOxl2Z4rmLnMrhwBhE+KQK2n3hywXpkNvUROWV9TQpxavzaA7punYHL4ggUpig==",
"dev": true,
"requires": {
"eslint-plugin-import": "^2.17.3"
"eslint-plugin-import": "^2.18.2"
}
},
"eslint-import-resolver-node": {
@@ -3672,9 +3672,9 @@
}
},
"eslint-module-utils": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz",
"integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==",
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
"integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
"dev": true,
"requires": {
"debug": "^2.6.8",
@@ -3751,9 +3751,9 @@
}
},
"eslint-plugin-import": {
"version": "2.17.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz",
"integrity": "sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==",
"version": "2.18.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz",
"integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==",
"dev": true,
"requires": {
"array-includes": "^3.0.3",
@@ -3763,8 +3763,8 @@
"eslint-import-resolver-node": "^0.3.2",
"eslint-module-utils": "^2.4.0",
"has": "^1.0.3",
"lodash": "^4.17.11",
"minimatch": "^3.0.4",
"object.values": "^1.1.0",
"read-pkg-up": "^2.0.0",
"resolve": "^1.11.0"
},
@@ -3795,9 +3795,9 @@
"dev": true
},
"resolve": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
"integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
"integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
@@ -5767,9 +5767,9 @@
"dev": true
},
"https-proxy-agent": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz",
"integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==",
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dev": true,
"requires": {
"agent-base": "^4.3.0",
@@ -8013,6 +8013,18 @@
"isobject": "^3.0.1"
}
},
"object.values": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
"integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.12.0",
"function-bind": "^1.1.1",
"has": "^1.0.3"
}
},
"obuf": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "ol",
"version": "6.1.0",
"version": "6.1.1",
"description": "OpenLayers mapping library",
"keywords": [
"map",
@@ -57,7 +57,7 @@
"copy-webpack-plugin": "^5.0.4",
"coveralls": "3.0.7",
"eslint": "^6.0.0",
"eslint-config-openlayers": "^12.0.0",
"eslint-config-openlayers": "^13.0.0",
"expect.js": "0.3.1",
"front-matter": "^3.0.2",
"fs-extra": "^8.0.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -65,6 +65,22 @@ feature.setStyle(new Style({
}));
vectorSource.addFeature(feature);
// align left
feature = new Feature({
geometry: new Point([50, 50])
});
feature.setStyle(new Style({
text: new Text({
font: 'Ubuntu',
text: 'align left',
textAlign: 'left',
stroke: new Stroke({
color: [10, 10, 10, 0.5]
})
})
}));
vectorSource.addFeature(feature);
// background and padding
feature = new Feature({
geometry: new Point([-10, 0])

View File

@@ -69,8 +69,6 @@ class ImageTile extends Tile {
if (this.interimTile) {
this.interimTile.dispose();
}
this.state = TileState.ABORT;
this.changed();
super.disposeInternal();
}

View File

@@ -512,15 +512,18 @@ class Overlay extends BaseObject {
positioning == OverlayPositioning.CENTER_RIGHT ||
positioning == OverlayPositioning.TOP_RIGHT) {
if (this.rendered.left_ !== '') {
this.rendered.left_ = style.left = '';
this.rendered.left_ = '';
style.left = '';
}
const right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px';
if (this.rendered.right_ != right) {
this.rendered.right_ = style.right = right;
this.rendered.right_ = right;
style.right = right;
}
} else {
if (this.rendered.right_ !== '') {
this.rendered.right_ = style.right = '';
this.rendered.right_ = '';
style.right = '';
}
if (positioning == OverlayPositioning.BOTTOM_CENTER ||
positioning == OverlayPositioning.CENTER_CENTER ||
@@ -529,22 +532,26 @@ class Overlay extends BaseObject {
}
const left = Math.round(pixel[0] + offsetX) + 'px';
if (this.rendered.left_ != left) {
this.rendered.left_ = style.left = left;
this.rendered.left_ = left;
style.left = left;
}
}
if (positioning == OverlayPositioning.BOTTOM_LEFT ||
positioning == OverlayPositioning.BOTTOM_CENTER ||
positioning == OverlayPositioning.BOTTOM_RIGHT) {
if (this.rendered.top_ !== '') {
this.rendered.top_ = style.top = '';
this.rendered.top_ = '';
style.top = '';
}
const bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px';
if (this.rendered.bottom_ != bottom) {
this.rendered.bottom_ = style.bottom = bottom;
this.rendered.bottom_ = bottom;
style.bottom = bottom;
}
} else {
if (this.rendered.bottom_ !== '') {
this.rendered.bottom_ = style.bottom = '';
this.rendered.bottom_ = '';
style.bottom = '';
}
if (positioning == OverlayPositioning.CENTER_LEFT ||
positioning == OverlayPositioning.CENTER_CENTER ||
@@ -553,7 +560,8 @@ class Overlay extends BaseObject {
}
const top = Math.round(pixel[1] + offsetY) + 'px';
if (this.rendered.top_ != top) {
this.rendered.top_ = style.top = top;
this.rendered.top_ = 'top';
style.top = top;
}
}
}

View File

@@ -87,11 +87,6 @@ class Tile extends EventTarget {
const options = opt_options ? opt_options : {};
/**
* @type {ImageData}
*/
this.hitDetectionImageData = null;
/**
* @type {import("./tilecoord.js").TileCoord}
*/
@@ -149,6 +144,13 @@ class Tile extends EventTarget {
this.dispatchEvent(EventType.CHANGE);
}
/**
* @inheritDoc
*/
disposeInternal() {
this.setState(TileState.ABORT);
}
/**
* @return {string} Key.
*/

View File

@@ -3,7 +3,6 @@
*/
import {getUid} from './util.js';
import Tile from './Tile.js';
import TileState from './TileState.js';
import {createCanvasContext2D} from './dom.js';
import {unlistenByKey} from './events.js';
@@ -25,7 +24,7 @@ class VectorRenderTile extends Tile {
/**
* @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @param {TileState} state State.
* @param {import("./TileState.js").default} state State.
* @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls.
* @param {import("./tilegrid/TileGrid.js").default} sourceTileGrid Tile grid of the source.
* @param {function(VectorRenderTile):Array<import("./VectorTile").default>} getSourceTiles Function
@@ -61,6 +60,11 @@ class VectorRenderTile extends Tile {
*/
this.errorSourceTileKeys = {};
/**
* @type {ImageData}
*/
this.hitDetectionImageData = null;
/**
* @private
* @type {!Object<string, ReplayState>}
@@ -73,9 +77,9 @@ class VectorRenderTile extends Tile {
this.wantedResolution;
/**
* @type {!function(import("./VectorRenderTile.js").default):Array<import("./VectorTile.js").default>}
* @type {!function():Array<import("./VectorTile.js").default>}
*/
this.getSourceTiles_ = getSourceTiles;
this.getSourceTiles = getSourceTiles.bind(this, this);
/**
* @type {!function(import("./VectorRenderTile.js").default):void}
@@ -120,7 +124,8 @@ class VectorRenderTile extends Tile {
this.removeSourceTiles_(this);
for (const key in this.context_) {
const canvas = this.context_[key].canvas;
canvas.width = canvas.height = 0;
canvas.width = 0;
canvas.height = 0;
}
for (const key in this.executorGroups) {
const executorGroups = this.executorGroups[key];
@@ -128,7 +133,6 @@ class VectorRenderTile extends Tile {
executorGroups[i].disposeInternal();
}
}
this.setState(TileState.ABORT);
super.disposeInternal();
}
@@ -186,7 +190,7 @@ class VectorRenderTile extends Tile {
* @inheritDoc
*/
load() {
this.getSourceTiles_(this);
this.getSourceTiles();
}
}

View File

@@ -73,14 +73,6 @@ class VectorTile extends Tile {
}
/**
* @inheritDoc
*/
disposeInternal() {
this.setState(TileState.ABORT);
super.disposeInternal();
}
/**
* Get the feature format assigned for reading this tile's features.
* @return {import("./format/Feature.js").default} Feature format.

View File

@@ -37,10 +37,12 @@ export function createExtent(extent, onlyCenter, smooth) {
// note: when zooming out of bounds, min and max values for x and y may
// end up inverted (min > max); this has to be accounted for
if (minX > maxX) {
minX = maxX = (maxX + minX) / 2;
minX = (maxX + minX) / 2;
maxX = minX;
}
if (minY > maxY) {
minY = maxY = (maxY + minY) / 2;
minY = (maxY + minY) / 2;
maxY = minY;
}
let x = clamp(center[0], minX, maxX);

View File

@@ -72,7 +72,8 @@ class Target extends Disposable {
}
let listeners = this.listeners_[type];
if (!listeners) {
listeners = this.listeners_[type] = [];
listeners = [];
this.listeners_[type] = listeners;
}
if (listeners.indexOf(listener) === -1) {
listeners.push(listener);

View File

@@ -1777,7 +1777,9 @@ function setCommonGeometryProperties(multiGeometry, geometries) {
const tessellates = new Array(geometries.length);
const altitudeModes = new Array(geometries.length);
let hasExtrude, hasTessellate, hasAltitudeMode;
hasExtrude = hasTessellate = hasAltitudeMode = false;
hasExtrude = false;
hasTessellate = false;
hasAltitudeMode = false;
for (let i = 0; i < ii; ++i) {
const geometry = geometries[i];
extrudes[i] = geometry.get('extrude');

View File

@@ -654,7 +654,8 @@ class Modify extends PointerInteraction {
};
const featureSegments = [centerSegmentData, circumferenceSegmentData];
centerSegmentData.featureSegments = circumferenceSegmentData.featureSegments = featureSegments;
centerSegmentData.featureSegments = featureSegments;
circumferenceSegmentData.featureSegments = featureSegments;
this.rBush_.insert(createExtent(coordinates), centerSegmentData);
this.rBush_.insert(geometry.getExtent(), circumferenceSegmentData);
}
@@ -746,12 +747,14 @@ class Modify extends PointerInteraction {
switch (geometry.getType()) {
case GeometryType.POINT:
coordinates = vertex;
segment[0] = segment[1] = vertex;
segment[0] = vertex;
segment[1] = vertex;
break;
case GeometryType.MULTI_POINT:
coordinates = geometry.getCoordinates();
coordinates[segmentData.index] = vertex;
segment[0] = segment[1] = vertex;
segment[0] = vertex;
segment[1] = vertex;
break;
case GeometryType.LINE_STRING:
coordinates = geometry.getCoordinates();
@@ -774,7 +777,8 @@ class Modify extends PointerInteraction {
segment[index] = vertex;
break;
case GeometryType.CIRCLE:
segment[0] = segment[1] = vertex;
segment[0] = vertex;
segment[1] = vertex;
if (segmentData.index === CIRCLE_CENTER_INDEX) {
this.changingFeature_ = true;
geometry.setCenter(vertex);
@@ -889,8 +893,10 @@ class Modify extends PointerInteraction {
const coordinates = geometry.getCenter();
const centerSegmentData = segmentData.featureSegments[0];
const circumferenceSegmentData = segmentData.featureSegments[1];
centerSegmentData.segment[0] = centerSegmentData.segment[1] = coordinates;
circumferenceSegmentData.segment[0] = circumferenceSegmentData.segment[1] = coordinates;
centerSegmentData.segment[0] = coordinates;
centerSegmentData.segment[1] = coordinates;
circumferenceSegmentData.segment[0] = coordinates;
circumferenceSegmentData.segment[1] = coordinates;
this.rBush_.update(createExtent(coordinates), centerSegmentData);
this.rBush_.update(geometry.getExtent(), circumferenceSegmentData);
} else {

View File

@@ -198,7 +198,7 @@ class Translate extends PointerInteraction {
handleDownEvent(event) {
this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
if (!this.lastCoordinate_ && this.lastFeature_) {
this.startCoordinate_ =
this.startCoordinate_ = event.coordinate;
this.lastCoordinate_ = event.coordinate;
this.handleMoveEvent(event);

View File

@@ -105,14 +105,14 @@ class BaseLayer extends BaseObject {
/** @type {import("./Layer.js").State} */
const state = this.state_ || /** @type {?} */ ({
layer: this,
managed: opt_managed === undefined ? true : opt_managed,
hasOverlay: false
managed: opt_managed === undefined ? true : opt_managed
});
const zIndex = this.getZIndex();
state.opacity = clamp(Math.round(this.getOpacity() * 100) / 100, 0, 1);
state.sourceState = this.getSourceState();
state.visible = this.getVisible();
state.extent = this.getExtent();
state.zIndex = this.getZIndex() || (state.managed === false ? Infinity : 0);
state.zIndex = zIndex !== undefined ? zIndex : (state.managed === false ? Infinity : 0);
state.maxResolution = this.getMaxResolution();
state.minResolution = Math.max(this.getMinResolution(), 0);
state.minZoom = this.getMinZoom();
@@ -320,6 +320,17 @@ class BaseLayer extends BaseObject {
setZIndex(zindex) {
this.set(LayerProperty.Z_INDEX, zindex);
}
/**
* @inheritDoc
*/
disposeInternal() {
if (this.state_) {
this.state_.layer = null;
this.state_ = null;
}
super.disposeInternal();
}
}

View File

@@ -581,7 +581,8 @@ class Graticule extends VectorLayer {
createGraticule_(extent, center, resolution, squaredTolerance) {
const interval = this.getInterval_(resolution);
if (interval == -1) {
this.meridians_.length = this.parallels_.length = 0;
this.meridians_.length = 0;
this.parallels_.length = 0;
if (this.meridiansLabels_) {
this.meridiansLabels_.length = 0;
}
@@ -708,7 +709,8 @@ class Graticule extends VectorLayer {
const flatCoordinates = meridian(lon, minLat, maxLat, this.projection_, squaredTolerance);
let lineString = this.meridians_[index];
if (!lineString) {
lineString = this.meridians_[index] = new LineString(flatCoordinates, GeometryLayout.XY);
lineString = new LineString(flatCoordinates, GeometryLayout.XY);
this.meridians_[index] = lineString;
} else {
lineString.setFlatCoordinates(GeometryLayout.XY, flatCoordinates);
lineString.changed();

View File

@@ -46,7 +46,6 @@ import SourceState from '../source/State.js';
* @property {SourceState} sourceState
* @property {boolean} visible
* @property {boolean} managed
* @property {boolean} hasOverlay Set by the renderer when an overlay for points and text is used.
* @property {import("../extent.js").Extent} [extent]
* @property {number} zIndex
* @property {number} maxResolution
@@ -288,6 +287,13 @@ class Layer extends BaseLayer {
return null;
}
/**
* @inheritDoc
*/
disposeInternal() {
this.setSource(null);
super.disposeInternal();
}
}

View File

@@ -57,6 +57,9 @@ import Layer from './Layer.js';
* }
* ```
*
* **Important: a `WebGLPoints` layer must be manually disposed when removed, otherwise the underlying WebGL context
* will not be garbage collected.**
*
* Note that any property set in the options is set as a {@link module:ol/Object~BaseObject}
* property on the layer object; for example, setting `title: 'My Title'` in the
* options means that `title` is observable, and has get/set accessors.
@@ -100,6 +103,15 @@ class WebGLPointsLayer extends Layer {
attributes: this.parseResult_.attributes
});
}
/**
*
* @inheritDoc
*/
disposeInternal() {
this.renderer_.dispose();
super.disposeInternal();
}
}
export default WebGLPointsLayer;

View File

@@ -74,7 +74,10 @@ class RenderBox extends Disposable {
if (this.map_) {
this.map_.getOverlayContainer().removeChild(this.element_);
const style = this.element_.style;
style.left = style.top = style.width = style.height = 'inherit';
style.left = 'inherit';
style.top = 'inherit';
style.width = 'inherit';
style.height = 'inherit';
}
this.map_ = map;
if (this.map_) {

View File

@@ -268,10 +268,10 @@ class RenderFeature {
/**
* @return {Array<number>|Array<Array<number>>} Ends or endss.
*/
RenderFeature.prototype.getEnds =
RenderFeature.prototype.getEndss = function() {
RenderFeature.prototype.getEnds = function() {
return this.ends_;
};
RenderFeature.prototype.getEndss = RenderFeature.prototype.getEnds;
/**

View File

@@ -311,13 +311,15 @@ export const measureTextHeight = (function() {
if (!div) {
div = document.createElement('div');
div.innerHTML = 'M';
div.style.margin = div.style.padding = '0 !important';
div.style.margin = '0 !important';
div.style.padding = '0 !important';
div.style.position = 'absolute !important';
div.style.left = '-99999px !important';
}
div.style.font = font;
document.body.appendChild(div);
height = heights[font] = div.offsetHeight;
height = div.offsetHeight;
heights[font] = height;
document.body.removeChild(div);
}
return height;
@@ -333,7 +335,8 @@ export const measureTextHeight = (function() {
export function measureTextWidth(font, text) {
const measureContext = getMeasureContext();
if (font != measureFont) {
measureContext.font = measureFont = font;
measureContext.font = font;
measureFont = measureContext.font;
}
return measureContext.measureText(text).width;
}
@@ -350,7 +353,8 @@ export function measureAndCacheTextWidth(font, text, cache) {
if (text in cache) {
return cache[text];
}
const width = cache[text] = measureTextWidth(font, text);
const width = measureTextWidth(font, text);
cache[text] = width;
return width;
}

View File

@@ -87,7 +87,8 @@ class BuilderGroup {
declutter = this.declutterGroups_;
/** @type {number} */ (declutter[0][4])++;
} else {
declutter = this.declutterGroups_ = [createEmpty()];
declutter = [createEmpty()];
this.declutterGroups_ = declutter;
declutter[0].push(1);
}
}

View File

@@ -315,10 +315,14 @@ class Executor extends Disposable {
const boxY = y - padding[0];
if (fillStroke || rotation !== 0) {
p1[0] = p4[0] = boxX;
p1[1] = p2[1] = boxY;
p2[0] = p3[0] = boxX + boxW;
p3[1] = p4[1] = boxY + boxH;
p1[0] = boxX;
p4[0] = boxX;
p1[1] = boxY;
p2[1] = boxY;
p2[0] = boxX + boxW;
p3[0] = p2[0];
p3[1] = boxY + boxH;
p4[1] = p3[1];
}
let transform = null;
@@ -537,6 +541,7 @@ class Executor extends Disposable {
let lastStrokeInstruction = null;
const coordinateCache = this.coordinateCache_;
const viewRotation = this.viewRotation_;
const viewRotationFromTransform = Math.round(Math.atan2(-transform[1], transform[0]) * 1e12) / 1e12;
const state = /** @type {import("../../render.js").State} */ ({
context: context,
@@ -575,7 +580,8 @@ class Executor extends Disposable {
}
if (!pendingFill && !pendingStroke) {
context.beginPath();
prevX = prevY = NaN;
prevX = NaN;
prevY = NaN;
}
++i;
break;
@@ -643,13 +649,18 @@ class Executor extends Disposable {
strokeKey = /** @type {string} */ (instruction[20]);
fillKey = /** @type {string} */ (instruction[21]);
const labelWithAnchor = this.drawTextImageWithPointPlacement_(text, textKey, strokeKey, fillKey);
image = instruction[3] = labelWithAnchor.label;
image = labelWithAnchor.label;
instruction[3] = image;
const textOffsetX = /** @type {number} */ (instruction[22]);
anchorX = instruction[4] = (labelWithAnchor.anchorX - textOffsetX) * this.pixelRatio;
anchorX = (labelWithAnchor.anchorX - textOffsetX) * this.pixelRatio;
instruction[4] = anchorX;
const textOffsetY = /** @type {number} */ (instruction[23]);
anchorY = instruction[5] = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio;
height = instruction[7] = image.height;
width = instruction[14] = image.width;
anchorY = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio;
instruction[5] = anchorY;
height = image.height;
instruction[7] = height;
width = image.width;
instruction[14] = width;
}
let geometryWidths;
@@ -664,11 +675,16 @@ class Executor extends Disposable {
backgroundStroke = /** @type {boolean} */ (instruction[17]);
} else {
padding = defaultPadding;
backgroundFill = backgroundStroke = false;
backgroundFill = false;
backgroundStroke = false;
}
if (rotateWithView) {
if (rotateWithView && viewRotationFromTransform) {
// Canvas is expected to be rotated to reverse view rotation.
rotation += viewRotation;
} else if (!rotateWithView && !viewRotationFromTransform) {
// Canvas is not rotated, images need to be rotated back to be north-up.
rotation -= viewRotation;
}
let widthIndex = 0;
let declutterGroupIndex = 0;
@@ -725,7 +741,8 @@ class Executor extends Disposable {
if (font in this.widths_) {
cachedWidths = this.widths_[font];
} else {
cachedWidths = this.widths_[font] = {};
cachedWidths = {};
this.widths_[font] = cachedWidths;
}
const pathLength = lineStringLength(pixelCoordinates, begin, end, 2);

View File

@@ -116,7 +116,8 @@ class ExecutorGroup extends Disposable {
for (const zIndex in allInstructions) {
let executors = this.executorsByZIndex_[zIndex];
if (executors === undefined) {
this.executorsByZIndex_[zIndex] = executors = {};
executors = {};
this.executorsByZIndex_[zIndex] = executors;
}
const instructionByZindex = allInstructions[zIndex];
for (const builderType in instructionByZindex) {
@@ -139,7 +140,8 @@ class ExecutorGroup extends Disposable {
}
if (this.hitDetectionContext_) {
const canvas = this.hitDetectionContext_.canvas;
canvas.width = canvas.height = 0;
canvas.width = 0;
canvas.height = 0;
}
super.disposeInternal();

View File

@@ -719,7 +719,8 @@ class CanvasImmediateRenderer extends VectorContext {
};
} else {
if (contextFillState.fillStyle != fillState.fillStyle) {
contextFillState.fillStyle = context.fillStyle = fillState.fillStyle;
contextFillState.fillStyle = fillState.fillStyle;
context.fillStyle = fillState.fillStyle;
}
}
}
@@ -752,30 +753,33 @@ class CanvasImmediateRenderer extends VectorContext {
};
} else {
if (contextStrokeState.lineCap != strokeState.lineCap) {
contextStrokeState.lineCap = context.lineCap = strokeState.lineCap;
contextStrokeState.lineCap = strokeState.lineCap;
context.lineCap = strokeState.lineCap;
}
if (context.setLineDash) {
if (!equals(contextStrokeState.lineDash, strokeState.lineDash)) {
context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash);
}
if (contextStrokeState.lineDashOffset != strokeState.lineDashOffset) {
contextStrokeState.lineDashOffset = context.lineDashOffset =
strokeState.lineDashOffset;
contextStrokeState.lineDashOffset = strokeState.lineDashOffset;
context.lineDashOffset = strokeState.lineDashOffset;
}
}
if (contextStrokeState.lineJoin != strokeState.lineJoin) {
contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin;
contextStrokeState.lineJoin = strokeState.lineJoin;
context.lineJoin = strokeState.lineJoin;
}
if (contextStrokeState.lineWidth != strokeState.lineWidth) {
contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth;
contextStrokeState.lineWidth = strokeState.lineWidth;
context.lineWidth = strokeState.lineWidth;
}
if (contextStrokeState.miterLimit != strokeState.miterLimit) {
contextStrokeState.miterLimit = context.miterLimit =
strokeState.miterLimit;
contextStrokeState.miterLimit = strokeState.miterLimit;
context.miterLimit = strokeState.miterLimit;
}
if (contextStrokeState.strokeStyle != strokeState.strokeStyle) {
contextStrokeState.strokeStyle = context.strokeStyle =
strokeState.strokeStyle;
contextStrokeState.strokeStyle = strokeState.strokeStyle;
context.strokeStyle = strokeState.strokeStyle;
}
}
}
@@ -800,14 +804,16 @@ class CanvasImmediateRenderer extends VectorContext {
};
} else {
if (contextTextState.font != textState.font) {
contextTextState.font = context.font = textState.font;
contextTextState.font = textState.font;
context.font = textState.font;
}
if (contextTextState.textAlign != textAlign) {
contextTextState.textAlign = context.textAlign = /** @type {CanvasTextAlign} */ (textAlign);
contextTextState.textAlign = /** @type {CanvasTextAlign} */ (textAlign);
context.textAlign = /** @type {CanvasTextAlign} */ (textAlign);
}
if (contextTextState.textBaseline != textState.textBaseline) {
contextTextState.textBaseline = context.textBaseline =
/** @type {CanvasTextBaseline} */ (textState.textBaseline);
contextTextState.textBaseline = /** @type {CanvasTextBaseline} */ (textState.textBaseline);
context.textBaseline = /** @type {CanvasTextBaseline} */ (textState.textBaseline);
}
}
}

View File

@@ -50,7 +50,8 @@ class LabelCache extends LRUCache {
}
}
const canvas = this.pop();
canvas.width = canvas.height = 0;
canvas.width = 0;
canvas.height = 0;
for (const consumerId in this.consumers) {
delete this.consumers[consumerId][key];
}

View File

@@ -392,11 +392,13 @@ class CanvasTextBuilder extends CanvasBuilder {
const textFillStyle = textStyle.getFill();
if (!textFillStyle) {
fillState = this.textFillState_ = null;
fillState = null;
this.textFillState_ = fillState;
} else {
fillState = this.textFillState_;
if (!fillState) {
fillState = this.textFillState_ = /** @type {import("../canvas.js").FillState} */ ({});
fillState = /** @type {import("../canvas.js").FillState} */ ({});
this.textFillState_ = fillState;
}
fillState.fillStyle = asColorLike(
textFillStyle.getColor() || defaultFillStyle);
@@ -404,11 +406,13 @@ class CanvasTextBuilder extends CanvasBuilder {
const textStrokeStyle = textStyle.getStroke();
if (!textStrokeStyle) {
strokeState = this.textStrokeState_ = null;
strokeState = null;
this.textStrokeState_ = strokeState;
} else {
strokeState = this.textStrokeState_;
if (!strokeState) {
strokeState = this.textStrokeState_ = /** @type {import("../canvas.js").StrokeState} */ ({});
strokeState = /** @type {import("../canvas.js").StrokeState} */ ({});
this.textStrokeState_ = strokeState;
}
const lineDash = textStrokeStyle.getLineDash();
const lineDashOffset = textStrokeStyle.getLineDashOffset();

View File

@@ -62,7 +62,10 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
const image = originalStyle.getImage();
if (image) {
const imgSize = image.getImageSize();
const imgContext = createCanvasContext2D(imgSize[0], imgSize[1]);
const canvas = document.createElement('canvas');
canvas.width = imgSize[0];
canvas.height = imgSize[1];
const imgContext = canvas.getContext('2d', {alpha: false});
imgContext.fillStyle = color;
const img = imgContext.canvas;
imgContext.fillRect(0, 0, img.width, img.height);
@@ -87,7 +90,8 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
const zIndex = Number(style.getZIndex());
let byGeometryType = featuresByZIndex[zIndex];
if (!byGeometryType) {
byGeometryType = featuresByZIndex[zIndex] = {};
byGeometryType = {};
featuresByZIndex[zIndex] = byGeometryType;
byGeometryType[GeometryType.POLYGON] = [];
byGeometryType[GeometryType.CIRCLE] = [];
byGeometryType[GeometryType.LINE_STRING] = [];
@@ -133,13 +137,10 @@ export function hitDetect(pixel, features, imageData) {
const r = imageData.data[index];
const g = imageData.data[index + 1];
const b = imageData.data[index + 2];
const a = imageData.data[index + 3];
if (a === 255) {
const i = b + (256 * (g + (256 * r)));
const indexFactor = Math.ceil((256 * 256 * 256) / features.length);
if (i % indexFactor === 0) {
resultFeatures.push(features[i / indexFactor]);
}
const i = b + (256 * (g + (256 * r)));
const indexFactor = Math.ceil((256 * 256 * 256) / features.length);
if (i % indexFactor === 0) {
resultFeatures.push(features[i / indexFactor]);
}
}
return resultFeatures;

View File

@@ -98,11 +98,9 @@ class CompositeMapRenderer extends MapRenderer {
const viewState = frameState.viewState;
this.children_.length = 0;
let hasOverlay = false;
let previousElement = null;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
hasOverlay = hasOverlay || layerState.hasOverlay;
frameState.layerIndex = i;
if (!inView(layerState, viewState) ||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
@@ -114,13 +112,8 @@ class CompositeMapRenderer extends MapRenderer {
if (!element) {
continue;
}
const childElementCount = element.childElementCount;
if ((element !== previousElement || i == ii - 1) && childElementCount === 2 && !hasOverlay) {
element.removeChild(element.lastElementChild);
}
if (element !== previousElement) {
this.children_.push(element);
hasOverlay = childElementCount === 2;
previousElement = element;
}
}

View File

@@ -134,8 +134,8 @@ class MapRenderer extends Disposable {
if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter.call(thisArg2, layer)) {
const layerRenderer = layer.getRenderer();
const source = layer.getSource();
const coordinates = source.getWrapX() ? translatedCoordinate : coordinate;
if (layerRenderer && source) {
const coordinates = source.getWrapX() ? translatedCoordinate : coordinate;
const callback = forEachFeatureAtCoordinate.bind(null, layerState.managed);
tmpCoord[0] = coordinates[0] + offsets[i][0];
tmpCoord[1] = coordinates[1] + offsets[i][1];

View File

@@ -7,7 +7,7 @@ import {containsExtent, intersects} from '../../extent.js';
import {fromUserExtent} from '../../proj.js';
import {getIntersection, isEmpty} from '../../extent.js';
import CanvasLayerRenderer from './Layer.js';
import {compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
import {compose as composeTransform, makeInverse} from '../../transform.js';
/**
* @classdesc
@@ -92,7 +92,8 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
const rotation = viewState.rotation;
if (rotation) {
const size = Math.round(Math.sqrt(width * width + height * height));
width = height = size;
width = size;
height = size;
}
// set forward and inverse pixel transforms
@@ -104,7 +105,9 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
);
makeInverse(this.inversePixelTransform, this.pixelTransform);
this.useContainer(target, this.pixelTransform, layerState.opacity);
const canvasTransform = this.createTransformString(this.pixelTransform);
this.useContainer(target, canvasTransform, layerState.opacity);
const context = this.context;
const canvas = context.canvas;
@@ -162,7 +165,6 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
context.restore();
}
const canvasTransform = transformToString(this.pixelTransform);
if (canvasTransform !== canvas.style.transform) {
canvas.style.transform = canvasTransform;
}

View File

@@ -7,7 +7,7 @@ import RenderEvent from '../../render/Event.js';
import RenderEventType from '../../render/EventType.js';
import {rotateAtOffset} from '../../render/canvas.js';
import LayerRenderer from '../Layer.js';
import {create as createTransform, apply as applyTransform, compose as composeTransform, toString as transformToString} from '../../transform.js';
import {create as createTransform, apply as applyTransform, compose as composeTransform, toString} from '../../transform.js';
/**
* @abstract
@@ -69,12 +69,18 @@ class CanvasLayerRenderer extends LayerRenderer {
*/
this.containerReused = false;
/**
* @type {HTMLCanvasElement}
* @private
*/
this.createTransformStringCanvas_ = createCanvasContext2D(1, 1).canvas;
}
/**
* Get a rendering container from an existing target, if compatible.
* @param {HTMLElement} target Potential render target.
* @param {import("../../transform").Transform} transform Transform.
* @param {string} transform CSS Transform.
* @param {number} opacity Opacity.
*/
useContainer(target, transform, opacity) {
@@ -86,7 +92,7 @@ class CanvasLayerRenderer extends LayerRenderer {
context = canvas.getContext('2d');
}
}
if (context && context.canvas.style.transform === transformToString(transform)) {
if (context && context.canvas.style.transform === transform) {
// Container of the previous layer renderer can be used.
this.container = target;
this.context = context;
@@ -263,6 +269,15 @@ class CanvasLayerRenderer extends LayerRenderer {
return data;
}
/**
* @param {import("../../transform.js").Transform} transform Transform.
* @return {string} CSS transform.
*/
createTransformString(transform) {
this.createTransformStringCanvas_.style.transform = toString(transform);
return this.createTransformStringCanvas_.style.transform;
}
}
export default CanvasLayerRenderer;

View File

@@ -7,7 +7,7 @@ import TileRange from '../../TileRange.js';
import TileState from '../../TileState.js';
import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js';
import CanvasLayerRenderer from './Layer.js';
import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
import {apply as applyTransform, compose as composeTransform, makeInverse} from '../../transform.js';
import {numberSafeCompareFunction} from '../../array.js';
/**
@@ -175,7 +175,8 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
if (rotation) {
const size = Math.round(Math.sqrt(width * width + height * height));
width = height = size;
width = size;
height = size;
}
const dx = tileResolution * width / 2 / tilePixelRatio;
@@ -242,7 +243,9 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
-width / 2, -height / 2
);
this.useContainer(target, this.pixelTransform, layerState.opacity);
const canvasTransform = this.createTransformString(this.pixelTransform);
this.useContainer(target, canvasTransform, layerState.opacity);
const context = this.context;
const canvas = context.canvas;
@@ -368,7 +371,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
context.restore();
}
const canvasTransform = transformToString(this.pixelTransform);
if (canvasTransform !== canvas.style.transform) {
canvas.style.transform = canvasTransform;
}

View File

@@ -127,7 +127,9 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
makeScale(this.pixelTransform, 1 / pixelRatio, 1 / pixelRatio);
makeInverse(this.inversePixelTransform, this.pixelTransform);
this.useContainer(target, this.pixelTransform, layerState.opacity);
const canvasTransform = transformToString(this.pixelTransform);
this.useContainer(target, canvasTransform, layerState.opacity);
const context = this.context;
const canvas = context.canvas;
@@ -145,7 +147,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
if (canvas.width != width || canvas.height != height) {
canvas.width = width;
canvas.height = height;
const canvasTransform = transformToString(this.pixelTransform);
if (canvas.style.transform !== canvasTransform) {
canvas.style.transform = canvasTransform;
}

View File

@@ -2,7 +2,6 @@
* @module ol/renderer/canvas/VectorTileLayer
*/
import {getUid} from '../../util.js';
import {createCanvasContext2D} from '../../dom.js';
import TileState from '../../TileState.js';
import ViewHint from '../../ViewHint.js';
import {listen, unlistenByKey} from '../../events.js';
@@ -20,9 +19,8 @@ import {
reset as resetTransform,
scale as scaleTransform,
translate as translateTransform,
toString as transformToString,
makeScale,
makeInverse
multiply,
scale
} from '../../transform.js';
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import {clear} from '../../obj.js';
@@ -64,31 +62,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
/** @private */
this.boundHandleStyleImageChange_ = this.handleStyleImageChange_.bind(this);
/**
* @private
* @type {CanvasRenderingContext2D}
*/
this.overlayContext_ = null;
/**
* @type {string}
*/
this.overlayContextUid_;
/**
* The transform for rendered pixels to viewport CSS pixels for the overlay canvas.
* @private
* @type {import("../../transform.js").Transform}
*/
this.overlayPixelTransform_ = createTransform();
/**
* The transform for viewport CSS pixels to rendered pixels for the overlay canvas.
* @private
* @type {import("../../transform.js").Transform}
*/
this.inverseOverlayPixelTransform_ = createTransform();
/**
* @private
* @type {boolean}
@@ -131,37 +104,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
this.tmpTransform_ = createTransform();
}
/**
* @inheritDoc
*/
useContainer(target, transform, opacity) {
let overlayContext;
if (target && target.childElementCount === 2) {
overlayContext = target.lastElementChild.getContext('2d');
if (!overlayContext) {
target = null;
}
}
const containerReused = this.containerReused;
super.useContainer(target, transform, opacity);
if (containerReused) {
this.overlayContext_ = overlayContext || null;
this.overlayContextUid_ = overlayContext ? getUid(overlayContext) : undefined;
}
if (!this.overlayContext_) {
const overlayContext = createCanvasContext2D();
const style = overlayContext.canvas.style;
style.position = 'absolute';
style.left = '0';
style.transformOrigin = 'top left';
this.overlayContext_ = overlayContext;
this.overlayContextUid_ = getUid(overlayContext);
}
if (this.container.childElementCount === 1) {
this.container.appendChild(this.overlayContext_.canvas);
}
}
/**
* @param {import("../../VectorRenderTile.js").default} tile Tile.
* @param {number} pixelRatio Pixel ratio.
@@ -239,8 +181,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc
*/
prepareFrame(frameState) {
const layerState = frameState.layerStatesArray[frameState.layerIndex];
layerState.hasOverlay = true;
const layerRevision = this.getLayer().getRevision();
if (this.renderedLayerRevision_ != layerRevision) {
this.renderedTiles.length = 0;
@@ -281,6 +221,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
executorGroups[i].dispose();
}
}
tile.hitDetectionImageData = null;
tile.executorGroups[layerUid] = [];
for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
const sourceTile = sourceTiles[t];
@@ -318,7 +259,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (renderOrder && renderOrder !== builderState.renderedRenderOrder) {
features.sort(renderOrder);
}
sourceTile.hitDetectionImageData = null;
for (let i = 0, ii = features.length; i < ii; ++i) {
const feature = features[i];
if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) {
@@ -405,59 +345,55 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const projection = this.renderedProjection;
const projectionExtent = projection.getExtent();
const resolution = this.renderedResolution;
const pixelRatio = this.renderedPixelRatio;
const tileGrid = source.getTileGridForProjection(projection);
const sourceTileGrid = source.getTileGrid();
const coordinate = applyTransform(this.renderedPixelToCoordinateTransform_, pixel.slice());
const tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, resolution);
let sourceTile;
let tile;
for (let i = 0, ii = this.renderedTiles.length; i < ii; ++i) {
if (tileCoord.toString() === this.renderedTiles[i].tileCoord.toString()) {
const tile = this.renderedTiles[i];
tile = this.renderedTiles[i];
if (tile.getState() === TileState.LOADED && tile.hifi) {
const extent = tileGrid.getTileCoordExtent(tileCoord);
const extent = tileGrid.getTileCoordExtent(tile.tileCoord);
if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
const worldWidth = getWidth(projectionExtent);
const worldsAway = Math.floor((coordinate[0] - projectionExtent[0]) / worldWidth);
coordinate[0] -= (worldsAway * worldWidth);
}
const sourceTiles = source.getSourceTiles(pixelRatio, projection, tile);
const sourceTileCoord = sourceTileGrid.getTileCoordForCoordAndResolution(coordinate, resolution);
for (let j = 0, jj = sourceTiles.length; j < jj; ++j) {
if (sourceTileCoord.toString() === sourceTiles[j].tileCoord.toString()) {
sourceTile = sourceTiles[j];
break;
}
}
break;
}
break;
tile = undefined;
}
}
if (!sourceTile) {
if (!tile) {
resolve([]);
return;
}
const corner = getTopLeft(tileGrid.getTileCoordExtent(sourceTile.tileCoord));
const extent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
const corner = getTopLeft(extent);
const tilePixel = [
(coordinate[0] - corner[0]) / resolution,
(corner[1] - coordinate[1]) / resolution
];
if (!sourceTile.hitDetectionImageData) {
const tileSize = toSize(sourceTileGrid.getTileSize(sourceTileGrid.getZForResolution(resolution)));
const features = tile.getSourceTiles().reduce(function(accumulator, sourceTile) {
return accumulator.concat(sourceTile.getFeatures());
}, []);
if (!tile.hitDetectionImageData) {
const tileSize = toSize(tileGrid.getTileSize(tileGrid.getZForResolution(resolution)));
const size = [tileSize[0] / 2, tileSize[1] / 2];
const rotation = this.renderedRotation_;
const transforms = [
this.getRenderTransform(tileGrid.getTileCoordCenter(sourceTile.tileCoord),
this.getRenderTransform(tileGrid.getTileCoordCenter(tile.wrappedTileCoord),
resolution, 0, 0.5, size[0], size[1], 0)
];
requestAnimationFrame(function() {
sourceTile.hitDetectionImageData = createHitDetectionImageData(tileSize, transforms,
sourceTile.getFeatures(), layer.getStyleFunction(),
tileGrid.getTileCoordExtent(sourceTile.tileCoord), resolution, rotation);
resolve(hitDetect(tilePixel, sourceTile.getFeatures(), sourceTile.hitDetectionImageData));
tile.hitDetectionImageData = createHitDetectionImageData(tileSize, transforms,
features, layer.getStyleFunction(),
tileGrid.getTileCoordExtent(tile.wrappedTileCoord),
tile.getReplayState(layer).renderedResolution, rotation);
resolve(hitDetect(tilePixel, features, tile.hitDetectionImageData));
});
} else {
resolve(hitDetect(tilePixel, sourceTile.getFeatures(), sourceTile.hitDetectionImageData));
resolve(hitDetect(tilePixel, features, tile.hitDetectionImageData));
}
}.bind(this));
}
@@ -510,7 +446,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
}
const context = this.overlayContext_;
const context = this.context;
const declutterReplays = layer.getDeclutter() ? {} : null;
const replayTypes = VECTOR_REPLAYS[renderMode];
const pixelRatio = frameState.pixelRatio;
@@ -520,24 +456,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const rotation = viewState.rotation;
const size = frameState.size;
// set forward and inverse pixel transforms
makeScale(this.overlayPixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
makeInverse(this.inverseOverlayPixelTransform_, this.overlayPixelTransform_);
// resize and clear
const canvas = context.canvas;
const width = Math.round(size[0] * pixelRatio);
const height = Math.round(size[1] * pixelRatio);
if (canvas.width != width || canvas.height != height) {
canvas.width = width;
canvas.height = height;
const canvasTransform = transformToString(this.overlayPixelTransform_);
if (canvas.style.transform !== canvasTransform) {
canvas.style.transform = canvasTransform;
}
} else if (getUid(context) === this.overlayContextUid_) {
context.clearRect(0, 0, width, height);
}
const tiles = this.renderedTiles;
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
@@ -551,7 +471,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const tileCoord = tile.tileCoord;
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tileExtent[0];
const transform = this.getRenderTransform(center, resolution, rotation, pixelRatio, width, height, worldOffset);
const transform = multiply(
scale(this.inversePixelTransform.slice(), 1 / pixelRatio, 1 / pixelRatio),
this.getRenderTransform(center, resolution, rotation, pixelRatio, width, height, worldOffset)
);
const executorGroups = tile.executorGroups[getUid(layer)];
let clipped = false;
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
@@ -711,34 +634,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
replayState.renderedTileResolution = tile.wantedResolution;
}
/**
* @inheritDoc
*/
getDataAtPixel(pixel, frameState, hitTolerance) {
let data = super.getDataAtPixel(pixel, frameState, hitTolerance);
if (data) {
return data;
}
const renderPixel = applyTransform(this.inverseOverlayPixelTransform_, pixel.slice());
const context = this.overlayContext_;
try {
data = context.getImageData(Math.round(renderPixel[0]), Math.round(renderPixel[1]), 1, 1).data;
} catch (err) {
if (err.name === 'SecurityError') {
// tainted canvas, we assume there is data at the given pixel (although there might not be)
return new Uint8Array();
}
return data;
}
if (data[3] === 0) {
return null;
}
return data;
}
}

View File

@@ -71,7 +71,7 @@ class WebGLLayerRenderer extends LayerRenderer {
* @inheritDoc
*/
disposeInternal() {
this.helper.disposeInternal();
this.helper.dispose();
super.disposeInternal();
}
@@ -169,7 +169,10 @@ export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuf
export function getBlankImageData() {
const canvas = document.createElement('canvas');
const image = canvas.getContext('2d').createImageData(1, 1);
image.data[0] = image.data[1] = image.data[2] = image.data[3] = 255;
image.data[0] = 255;
image.data[1] = 255;
image.data[2] = 255;
image.data[3] = 255;
return image;
}

View File

@@ -19,13 +19,23 @@ import {getUid} from '../../util.js';
import WebGLRenderTarget from '../../webgl/RenderTarget.js';
import {assert} from '../../asserts.js';
import BaseVector from '../../layer/BaseVector.js';
import {listen, unlistenByKey} from '../../events.js';
import VectorEventType from '../../source/VectorEventType.js';
/**
* @typedef {Object} CustomAttribute A description of a custom attribute to be passed on to the GPU, with a value different
* for each feature.
* @property {string} name Attribute name.
* @property {function(import("../../Feature").default):number} callback This callback computes the numerical value of the
* attribute for a given feature.
* @property {function(import("../../Feature").default, Object<string, *>):number} callback This callback computes the numerical value of the
* attribute for a given feature (properties are available as 2nd arg for quicker access).
*/
/**
* @typedef {Object} FeatureCacheItem Object that holds a reference to a feature, its geometry and properties. Used to optimize
* rebuildBuffers by accessing these objects quicker.
* @property {import("../../Feature").default} feature Feature
* @property {Object<string, *>} properties Feature properties
* @property {import("../../geom").Geometry} geometry Feature geometry
*/
/**
@@ -269,6 +279,72 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.getLayer().changed();
}
}.bind(this));
/**
* This object will be updated when the source changes. Key is uid.
* @type {Object<string, FeatureCacheItem>}
* @private
*/
this.featureCache_ = {};
/**
* Amount of features in the cache.
* @type {number}
* @private
*/
this.featureCount_ = 0;
const source = this.getLayer().getSource();
this.sourceListenKeys_ = [
listen(source, VectorEventType.ADDFEATURE, this.handleSourceFeatureAdded_, this),
listen(source, VectorEventType.CHANGEFEATURE, this.handleSourceFeatureChanged_, this),
listen(source, VectorEventType.REMOVEFEATURE, this.handleSourceFeatureDelete_, this)
];
source.forEachFeature(function(feature) {
this.featureCache_[getUid(feature)] = {
feature: feature,
properties: feature.getProperties(),
geometry: feature.getGeometry()
};
this.featureCount_++;
}.bind(this));
}
/**
* @param {import("../../source/Vector.js").VectorSourceEvent} event Event.
* @private
*/
handleSourceFeatureAdded_(event) {
const feature = event.feature;
this.featureCache_[getUid(feature)] = {
feature: feature,
properties: feature.getProperties(),
geometry: feature.getGeometry()
};
this.featureCount_++;
}
/**
* @param {import("../../source/Vector.js").VectorSourceEvent} event Event.
* @private
*/
handleSourceFeatureChanged_(event) {
const feature = event.feature;
this.featureCache_[getUid(feature)] = {
feature: feature,
properties: feature.getProperties(),
geometry: feature.getGeometry()
};
}
/**
* @param {import("../../source/Vector.js").VectorSourceEvent} event Event.
* @private
*/
handleSourceFeatureDelete_(event) {
const feature = event.feature;
delete this.featureCache_[getUid(feature)];
this.featureCount_--;
}
/**
@@ -343,45 +419,41 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
* @private
*/
rebuildBuffers_(frameState) {
const layer = this.getLayer();
const vectorSource = layer.getSource();
// saves the projection transform for the current frame state
const projectionTransform = createTransform();
this.helper.makeProjectionTransform(frameState, projectionTransform);
const features = vectorSource.getFeatures();
// here we anticipate the amount of render instructions that we well generate
// this can be done since we know that for normal render we only have x, y as base instructions,
// and x, y, r, g, b, a and featureUid for hit render instructions
// and we also know the amount of custom attributes to append to these
const totalInstructionsCount = (2 + this.customAttributes.length) * features.length;
const totalInstructionsCount = (2 + this.customAttributes.length) * this.featureCount_;
if (!this.renderInstructions_ || this.renderInstructions_.length !== totalInstructionsCount) {
this.renderInstructions_ = new Float32Array(totalInstructionsCount);
}
if (this.hitDetectionEnabled_) {
const totalHitInstructionsCount = (7 + this.customAttributes.length) * features.length;
const totalHitInstructionsCount = (7 + this.customAttributes.length) * this.featureCount_;
if (!this.hitRenderInstructions_ || this.hitRenderInstructions_.length !== totalHitInstructionsCount) {
this.hitRenderInstructions_ = new Float32Array(totalHitInstructionsCount);
}
}
// loop on features to fill the buffer
let feature;
let featureCache, geometry;
const tmpCoords = [];
const tmpColor = [];
let renderIndex = 0;
let hitIndex = 0;
let hitColor;
for (let i = 0; i < features.length; i++) {
feature = features[i];
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
for (const featureUid in this.featureCache_) {
featureCache = this.featureCache_[featureUid];
geometry = /** @type {import("../../geom").Point} */(featureCache.geometry);
if (!geometry || geometry.getType() !== GeometryType.POINT) {
continue;
}
tmpCoords[0] = feature.getGeometry().getFlatCoordinates()[0];
tmpCoords[1] = feature.getGeometry().getFlatCoordinates()[1];
tmpCoords[0] = geometry.getFlatCoordinates()[0];
tmpCoords[1] = geometry.getFlatCoordinates()[1];
applyTransform(projectionTransform, tmpCoords);
hitColor = colorEncodeId(hitIndex + 6, tmpColor);
@@ -398,13 +470,13 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.hitRenderInstructions_[hitIndex++] = hitColor[1];
this.hitRenderInstructions_[hitIndex++] = hitColor[2];
this.hitRenderInstructions_[hitIndex++] = hitColor[3];
this.hitRenderInstructions_[hitIndex++] = Number(getUid(feature));
this.hitRenderInstructions_[hitIndex++] = Number(featureUid);
}
// pushing custom attributes
let value;
for (let j = 0; j < this.customAttributes.length; j++) {
value = this.customAttributes[j].callback(feature);
value = this.customAttributes[j].callback(featureCache.feature, featureCache.properties);
this.renderInstructions_[renderIndex++] = value;
if (this.hitDetectionEnabled_) {
this.hitRenderInstructions_[hitIndex++] = value;
@@ -448,7 +520,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const pixel = applyTransform(frameState.coordinateToPixelTransform, coordinate.slice());
const data = this.hitRenderTarget_.readPixel(pixel[0], pixel[1]);
const data = this.hitRenderTarget_.readPixel(pixel[0] / 2, pixel[1] / 2);
const color = [
data[0] / 255,
data[1] / 255,
@@ -476,7 +548,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
return;
}
this.hitRenderTarget_.setSize(frameState.size);
this.hitRenderTarget_.setSize([
Math.floor(frameState.size[0] / 2),
Math.floor(frameState.size[1] / 2)
]);
this.helper.useProgram(this.hitProgram_);
this.helper.prepareDrawToRenderTarget(frameState, this.hitRenderTarget_, true);
@@ -495,6 +570,11 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
*/
disposeInternal() {
this.worker_.terminate();
this.layer_ = null;
this.sourceListenKeys_.forEach(function(key) {
unlistenByKey(key);
});
this.sourceListenKeys_ = null;
super.disposeInternal();
}
}

View File

@@ -70,7 +70,8 @@ export function toSize(size, opt_size) {
if (opt_size === undefined) {
opt_size = [size, size];
} else {
opt_size[0] = opt_size[1] = size;
opt_size[0] = size;
opt_size[1] = size;
}
return opt_size;
}

View File

@@ -180,7 +180,8 @@ class UrlTile extends TileSource {
* @api
*/
setUrl(url) {
const urls = this.urls = expandUrl(url);
const urls = expandUrl(url);
this.urls = urls;
this.setUrls(urls);
}

View File

@@ -380,8 +380,9 @@ class VectorTile extends UrlTile {
// A tile grid that matches the tile size of the source tile grid is more
// likely to have 1:1 relationships between source tiles and rendered tiles.
const sourceTileGrid = this.tileGrid;
tileGrid = this.tileGrids_[code] = createForProjection(projection, undefined,
tileGrid = createForProjection(projection, undefined,
sourceTileGrid ? sourceTileGrid.getTileSize(sourceTileGrid.getMinZoom()) : undefined);
this.tileGrids_[code] = tileGrid;
}
return tileGrid;
}

View File

@@ -18,13 +18,13 @@ import {asArray, isStringColor} from '../color.js';
* * `['time']` returns the time in seconds since the creation of the layer
*
* * Math operators:
* * `['*', value1, value1]` multiplies `value1` by `value2`
* * `['/', value1, value1]` divides `value1` by `value2`
* * `['+', value1, value1]` adds `value1` and `value2`
* * `['-', value1, value1]` subtracts `value2` from `value1`
* * `['*', value1, value2]` multiplies `value1` by `value2`
* * `['/', value1, value2]` divides `value1` by `value2`
* * `['+', value1, value2]` adds `value1` and `value2`
* * `['-', value1, value2]` subtracts `value2` from `value1`
* * `['clamp', value, low, high]` clamps `value` between `low` and `high`
* * `['%', value1, value1]` returns the result of `value1 % value2` (modulo)
* * `['^', value1, value1]` returns the value of `value1` raised to the `value2` power
* * `['%', value1, value2]` returns the result of `value1 % value2` (modulo)
* * `['^', value1, value2]` returns the value of `value1` raised to the `value2` power
*
* * Transform operators:
* * `['case', condition1, output1, ...conditionN, outputN, fallback]` selects the first output whose corresponding
@@ -44,12 +44,12 @@ import {asArray, isStringColor} from '../color.js';
* between `output1` and `outputN`.
*
* * Logical operators:
* * `['<', value1, value2]` returns `true` if `value1` is strictly lower than value 2, or `false` otherwise.
* * `['<=', value1, value2]` returns `true` if `value1` is lower than or equals value 2, or `false` otherwise.
* * `['>', value1, value2]` returns `true` if `value1` is strictly greater than value 2, or `false` otherwise.
* * `['>=', value1, value2]` returns `true` if `value1` is greater than or equals value 2, or `false` otherwise.
* * `['==', value1, value2]` returns `true` if `value1` equals value 2, or `false` otherwise.
* * `['!=', value1, value2]` returns `true` if `value1` equals value 2, or `false` otherwise.
* * `['<', value1, value2]` returns `true` if `value1` is strictly lower than `value2`, or `false` otherwise.
* * `['<=', value1, value2]` returns `true` if `value1` is lower than or equals `value2`, or `false` otherwise.
* * `['>', value1, value2]` returns `true` if `value1` is strictly greater than `value2`, or `false` otherwise.
* * `['>=', value1, value2]` returns `true` if `value1` is greater than or equals `value2`, or `false` otherwise.
* * `['==', value1, value2]` returns `true` if `value1` equals `value2`, or `false` otherwise.
* * `['!=', value1, value2]` returns `true` if `value1` does not equal `value2`, or `false` otherwise.
* * `['!', value1]` returns `false` if `value1` is `true` or greater than `0`, or `true` otherwise.
* * `['between', value1, value2, value3]` returns `true` if `value1` is contained between `value2` and `value3`
* (inclusively), or `false` otherwise.
@@ -203,6 +203,19 @@ export function colorToGlsl(color) {
);
}
/**
* Returns a stable equivalent number for the string literal.
* @param {ParsingContext} context Parsing context
* @param {string} string String literal value
* @returns {number} Number equivalent
*/
export function getStringNumberEquivalent(context, string) {
if (context.stringLiteralsMap[string] === undefined) {
context.stringLiteralsMap[string] = Object.keys(context.stringLiteralsMap).length;
}
return context.stringLiteralsMap[string];
}
/**
* Returns a stable equivalent number for the string literal, for use in shaders. This number is then
* converted to be a GLSL-compatible string.
@@ -211,10 +224,7 @@ export function colorToGlsl(color) {
* @returns {string} GLSL-compatible string containing a number
*/
export function stringToGlsl(context, string) {
if (context.stringLiteralsMap[string] === undefined) {
context.stringLiteralsMap[string] = Object.keys(context.stringLiteralsMap).length;
}
return numberToGlsl(context.stringLiteralsMap[string]);
return numberToGlsl(getStringNumberEquivalent(context, string));
}
/**

View File

@@ -114,7 +114,7 @@ export function expandUrl(url) {
}
return urls;
}
match = match = /\{(\d+)-(\d+)\}/.exec(url);
match = /\{(\d+)-(\d+)\}/.exec(url);
if (match) {
// number range
const stop = parseInt(match[2], 10);

View File

@@ -266,18 +266,6 @@ class WebGLHelper extends Disposable {
*/
this.bufferCache_ = {};
/**
* @private
* @type {!Array<WebGLShader>}
*/
this.shaderCache_ = [];
/**
* @private
* @type {!Array<WebGLProgram>}
*/
this.programCache_ = [];
/**
* @private
* @type {WebGLProgram}
@@ -379,10 +367,11 @@ class WebGLHelper extends Disposable {
let bufferCache = this.bufferCache_[bufferKey];
if (!bufferCache) {
const webGlBuffer = gl.createBuffer();
bufferCache = this.bufferCache_[bufferKey] = {
bufferCache = {
buffer: buffer,
webGlBuffer: webGlBuffer
};
this.bufferCache_[bufferKey] = bufferCache;
}
gl.bindBuffer(buffer.getType(), bufferCache.webGlBuffer);
}
@@ -418,18 +407,6 @@ class WebGLHelper extends Disposable {
disposeInternal() {
this.canvas_.removeEventListener(ContextEventType.LOST, this.boundHandleWebGLContextLost_);
this.canvas_.removeEventListener(ContextEventType.RESTORED, this.boundHandleWebGLContextRestored_);
const gl = this.getGL();
if (!gl.isContextLost()) {
for (const key in this.bufferCache_) {
gl.deleteBuffer(this.bufferCache_[key].buffer);
}
for (const key in this.programCache_) {
gl.deleteProgram(this.programCache_[key]);
}
for (const key in this.shaderCache_) {
gl.deleteShader(this.shaderCache_[key]);
}
}
}
/**
@@ -479,9 +456,10 @@ class WebGLHelper extends Disposable {
*/
prepareDrawToRenderTarget(frameState, renderTarget, opt_disableAlphaBlend) {
const gl = this.getGL();
const size = renderTarget.getSize();
gl.bindFramebuffer(gl.FRAMEBUFFER, renderTarget.getFramebuffer());
gl.viewport(0, 0, frameState.size[0], frameState.size[1]);
gl.viewport(0, 0, size[0], size[1]);
gl.bindTexture(gl.TEXTURE_2D, renderTarget.getTexture());
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -651,7 +629,6 @@ class WebGLHelper extends Disposable {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
this.shaderCache_.push(shader);
return shader;
}
@@ -683,7 +660,6 @@ class WebGLHelper extends Disposable {
gl.attachShader(program, fragmentShader);
gl.attachShader(program, vertexShader);
gl.linkProgram(program);
this.programCache_.push(program);
return program;
}
@@ -810,8 +786,6 @@ class WebGLHelper extends Disposable {
*/
handleWebGLContextLost() {
clear(this.bufferCache_);
clear(this.shaderCache_);
clear(this.programCache_);
this.currentProgram_ = null;
}
@@ -822,8 +796,6 @@ class WebGLHelper extends Disposable {
handleWebGLContextRestored() {
}
// TODO: shutdown program
/**
* Will create or reuse a given webgl texture and apply the given size. If no image data
* specified, the texture will be empty, otherwise image data will be used and the `size`

View File

@@ -115,12 +115,21 @@ class WebGLRenderTarget {
/**
* Reads one pixel of the frame buffer as an array of r, g, b, a components
* in the 0-255 range (unsigned byte).
* If x and/or y are outside of existing data, an array filled with 0 is returned.
* @param {number} x Pixel coordinate
* @param {number} y Pixel coordinate
* @returns {Uint8Array} Integer array with one color value (4 components)
* @api
*/
readPixel(x, y) {
if (x < 0 || y < 0 || x > this.size_[0] || y >= this.size_[1]) {
tmpArray4[0] = 0;
tmpArray4[1] = 0;
tmpArray4[2] = 0;
tmpArray4[3] = 0;
return tmpArray4;
}
this.readAll();
const index = Math.floor(x) + (this.size_[1] - Math.floor(y) - 1) * this.size_[0];
tmpArray4[0] = this.data_[index * 4];

View File

@@ -3,7 +3,7 @@
* @module ol/webgl/ShaderBuilder
*/
import {expressionToGlsl, stringToGlsl, ValueTypes} from '../style/expressions.js';
import {expressionToGlsl, getStringNumberEquivalent, ValueTypes} from '../style/expressions.js';
/**
* @typedef {Object} VaryingDescription
@@ -450,7 +450,7 @@ export function parseLiteralStyle(style) {
}
let value = style.variables[varName];
if (typeof value === 'string') {
value = parseFloat(stringToGlsl(vertContext, value));
value = getStringNumberEquivalent(vertContext, value);
}
return value !== undefined ? value : -9999999; // to avoid matching with the first string literal
};
@@ -484,10 +484,10 @@ export function parseLiteralStyle(style) {
attributes: vertContext.attributes.map(function(attributeName) {
return {
name: attributeName,
callback: function(feature) {
let value = feature.get(attributeName);
callback: function(feature, props) {
let value = props[attributeName];
if (typeof value === 'string') {
value = parseFloat(stringToGlsl(vertContext, value));
value = getStringNumberEquivalent(vertContext, value);
}
return value !== undefined ? value : -9999999; // to avoid matching with the first string literal
}

View File

@@ -215,7 +215,8 @@ export function makeObjectPropertyPusher(valueReader, opt_property, opt_this) {
if (property in object) {
array = object[property];
} else {
array = object[property] = [];
array = [];
object[property] = array;
}
array.push(value);
}

View File

@@ -44,7 +44,7 @@ main() {
git log --first-parent --format='%aN|%s %b' ${1} |
{
while read l; do
output="`[[ ${l} =~ "openlayers/greenkeeper" ]] && echo greenkeeper || echo main`_output"
output="`[[ ${l} =~ "openlayers/dependabot" ]] && echo dependabot || echo main`_output"
if [[ ${l} =~ ${MERGE_RE} ]] ; then
number="${BASH_REMATCH[1]}"
author="${BASH_REMATCH[2]}"
@@ -60,12 +60,12 @@ main() {
echo -e "$main_output"
if [ -n "$greenkeeper_output" ]; then
if [ -n "$dependabot_output" ]; then
echo
echo "<details>"
echo " <summary>Dependency Updates</summary>"
echo
echo -e "$greenkeeper_output"
echo -e "$dependabot_output"
echo
echo "</details>"
fi

View File

@@ -23,7 +23,8 @@ describe('ol.control.Attribution', function() {
beforeEach(function() {
const target = document.createElement('div');
target.style.width = target.style.height = '100px';
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,

View File

@@ -39,7 +39,6 @@ describe('ol.layer.Group', function() {
opacity: 1,
visible: true,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
@@ -165,7 +164,6 @@ describe('ol.layer.Group', function() {
opacity: 0.5,
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 10,
@@ -209,7 +207,6 @@ describe('ol.layer.Group', function() {
opacity: 0.5,
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: groupExtent,
zIndex: 0,
@@ -254,7 +251,6 @@ describe('ol.layer.Group', function() {
opacity: 0.3,
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: groupExtent,
zIndex: 10,
@@ -273,7 +269,6 @@ describe('ol.layer.Group', function() {
opacity: 0,
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
@@ -290,7 +285,6 @@ describe('ol.layer.Group', function() {
opacity: 1,
visible: true,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
@@ -465,7 +459,6 @@ describe('ol.layer.Group', function() {
opacity: 0.25,
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 0,

View File

@@ -57,7 +57,6 @@ describe('ol.layer.Layer', function() {
opacity: 1,
visible: true,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
@@ -99,7 +98,6 @@ describe('ol.layer.Layer', function() {
opacity: 0.5,
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 10,
@@ -373,7 +371,6 @@ describe('ol.layer.Layer', function() {
opacity: 0.33,
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 10,
@@ -592,7 +589,7 @@ describe('ol.layer.Layer', function() {
const frameState = {
layerStatesArray: []
};
map.dispatchEvent(new RenderEvent('precompose', null, frameState, null, null));
map.dispatchEvent(new RenderEvent('precompose', null, frameState, null));
expect(frameState.layerStatesArray.length).to.be(1);
const layerState = frameState.layerStatesArray[0];
expect(layerState.layer).to.equal(layer);
@@ -644,18 +641,18 @@ describe('ol.layer.Layer', function() {
});
it('has Infinity as zIndex when not configured otherwise', function() {
map.dispatchEvent(new RenderEvent('precompose', null,
frameState, null, null));
map.dispatchEvent(new RenderEvent('precompose', null, frameState, null));
const layerState = frameState.layerStatesArray[0];
expect(layerState.zIndex).to.be(Infinity);
});
it('respects the configured zIndex', function() {
layer.setZIndex(42);
map.dispatchEvent(new RenderEvent('precompose', null,
frameState, null, null));
const layerState = frameState.layerStatesArray[0];
expect(layerState.zIndex).to.be(42);
[-5, 0, 42].forEach(index => {
layer.setZIndex(index);
map.dispatchEvent(new RenderEvent('precompose', null, frameState, null));
const layerState = frameState.layerStatesArray[0];
expect(layerState.zIndex).to.be(index);
});
});
});

View File

@@ -197,7 +197,8 @@ describe('ol.Map', function() {
let map;
beforeEach(function() {
const target = document.createElement('div');
target.style.width = target.style.height = '100px';
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,
@@ -259,7 +260,8 @@ describe('ol.Map', function() {
let target, map, layer;
beforeEach(function() {
target = document.createElement('div');
target.style.width = target.style.height = '100px';
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
layer = new VectorLayer({
source: new VectorSource({
@@ -360,7 +362,8 @@ describe('ol.Map', function() {
useGeographic();
target = document.createElement('div');
target.style.width = target.style.height = size + 'px';
target.style.width = size + 'px';
target.style.height = size + 'px';
document.body.appendChild(target);
map = new Map({
@@ -413,7 +416,8 @@ describe('ol.Map', function() {
useGeographic();
target = document.createElement('div');
target.style.width = target.style.height = size + 'px';
target.style.width = size + 'px';
target.style.height = size + 'px';
document.body.appendChild(target);
map = new Map({

View File

@@ -81,7 +81,8 @@ describe('ol.renderer.canvas.ImageLayer', function() {
});
div = document.createElement('div');
div.style.width = div.style.height = '100px';
div.style.width = '100px';
div.style.height = '100px';
document.body.appendChild(div);
map = new Map({
target: div,
@@ -131,7 +132,8 @@ describe('ol.renderer.canvas.ImageLayer', function() {
});
div = document.createElement('div');
div.style.width = div.style.height = '100px';
div.style.width = '100px';
div.style.height = '100px';
document.body.appendChild(div);
map = new Map({
target: div,

View File

@@ -215,12 +215,9 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
url: 'rendering/ol/data/tiles/osm/{z}/{x}/{y}.png'
})
}));
map.once('postcompose', function(e) {
expect(e.frameState.layerStatesArray[1].hasOverlay).to.be(true);
});
map.once('rendercomplete', function() {
expect(document.querySelector('.ol-layers').childElementCount).to.be(1);
expect(document.querySelector('.ol-layer').childElementCount).to.be(2);
expect(document.querySelector('.ol-layer').childElementCount).to.be(1);
map.removeLayer(map.getLayers().item(1));
map.renderSync();
expect(document.querySelector('.ol-layer').childElementCount).to.be(1);

View File

@@ -7,6 +7,7 @@ import {get as getProjection} from '../../../../../src/ol/proj.js';
import ViewHint from '../../../../../src/ol/ViewHint.js';
import {WebGLWorkerMessageType} from '../../../../../src/ol/renderer/webgl/Layer.js';
import {compose as composeTransform, create as createTransform} from '../../../../../src/ol/transform.js';
import {getUid} from '../../../../../src/ol/util.js';
const baseFrameState = {
viewHints: [],
@@ -388,4 +389,120 @@ describe('ol.renderer.webgl.PointsLayer', function() {
});
});
describe('featureCache_', function() {
let source, layer, features;
function getCache(feature, renderer) {
return renderer.featureCache_[getUid(feature)];
}
beforeEach(function() {
source = new VectorSource();
layer = new VectorLayer({
source
});
features = [
new Feature({
id: 'A',
test: 'abcd',
geometry: new Point([0, 1])
}),
new Feature({
id: 'D',
test: 'efgh',
geometry: new Point([2, 3])
}),
new Feature({
id: 'C',
test: 'ijkl',
geometry: new Point([4, 5])
})
];
});
it('contains no features initially', function() {
const renderer = new WebGLPointsLayerRenderer(layer, {
vertexShader: simpleVertexShader,
fragmentShader: simpleFragmentShader
});
expect(renderer.featureCount_).to.be(0);
});
it('contains the features initially present in the source', function() {
source.addFeatures(features);
const renderer = new WebGLPointsLayerRenderer(layer, {
vertexShader: simpleVertexShader,
fragmentShader: simpleFragmentShader
});
expect(renderer.featureCount_).to.be(3);
expect(getCache(features[0], renderer).feature).to.be(features[0]);
expect(getCache(features[0], renderer).geometry).to.be(features[0].getGeometry());
expect(getCache(features[0], renderer).properties['test']).to.be(features[0].get('test'));
expect(getCache(features[1], renderer).feature).to.be(features[1]);
expect(getCache(features[1], renderer).geometry).to.be(features[1].getGeometry());
expect(getCache(features[1], renderer).properties['test']).to.be(features[1].get('test'));
expect(getCache(features[2], renderer).feature).to.be(features[2]);
expect(getCache(features[2], renderer).geometry).to.be(features[2].getGeometry());
expect(getCache(features[2], renderer).properties['test']).to.be(features[2].get('test'));
});
it('contains the features added to the source', function() {
const renderer = new WebGLPointsLayerRenderer(layer, {
vertexShader: simpleVertexShader,
fragmentShader: simpleFragmentShader
});
source.addFeature(features[0]);
expect(renderer.featureCount_).to.be(1);
source.addFeature(features[1]);
expect(renderer.featureCount_).to.be(2);
expect(getCache(features[0], renderer).feature).to.be(features[0]);
expect(getCache(features[0], renderer).geometry).to.be(features[0].getGeometry());
expect(getCache(features[0], renderer).properties['test']).to.be(features[0].get('test'));
expect(getCache(features[1], renderer).feature).to.be(features[1]);
expect(getCache(features[1], renderer).geometry).to.be(features[1].getGeometry());
expect(getCache(features[1], renderer).properties['test']).to.be(features[1].get('test'));
});
it('does not contain the features removed to the source', function() {
const renderer = new WebGLPointsLayerRenderer(layer, {
vertexShader: simpleVertexShader,
fragmentShader: simpleFragmentShader
});
source.addFeatures(features);
expect(renderer.featureCount_).to.be(3);
source.removeFeature(features[1]);
expect(renderer.featureCount_).to.be(2);
expect(getCache(features[0], renderer).feature).to.be(features[0]);
expect(getCache(features[0], renderer).geometry).to.be(features[0].getGeometry());
expect(getCache(features[0], renderer).properties['test']).to.be(features[0].get('test'));
expect(getCache(features[2], renderer).feature).to.be(features[2]);
expect(getCache(features[2], renderer).geometry).to.be(features[2].getGeometry());
expect(getCache(features[2], renderer).properties['test']).to.be(features[2].get('test'));
});
it('contains up to date properties and geometry', function() {
const renderer = new WebGLPointsLayerRenderer(layer, {
vertexShader: simpleVertexShader,
fragmentShader: simpleFragmentShader
});
source.addFeatures(features);
features[0].set('test', 'updated');
features[0].set('added', true);
features[0].getGeometry().setCoordinates([10, 20]);
expect(renderer.featureCount_).to.be(3);
expect(getCache(features[0], renderer).feature).to.be(features[0]);
expect(getCache(features[0], renderer).geometry.getCoordinates()).to.eql([10, 20]);
expect(getCache(features[0], renderer).properties['test']).to.be(features[0].get('test'));
expect(getCache(features[0], renderer).properties['added']).to.be(features[0].get('added'));
});
});
});

View File

@@ -408,7 +408,8 @@ describe('ol.source.ImageWMS', function() {
source.loading = false;
});
const target = document.createElement('div');
target.style.width = target.style.height = '100px';
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,

View File

@@ -158,7 +158,8 @@ describe('ol.source.Vector', function() {
url: 'spec/ol/source/vectorsource/single-feature.json'
});
const target = document.createElement('div');
target.style.width = target.style.height = '100px';
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,
@@ -592,7 +593,8 @@ describe('ol.source.Vector', function() {
}
});
const div = document.createElement('div');
div.style.width = div.style.height = '100px';
div.style.width = '100px';
div.style.height = '100px';
document.body.appendChild(div);
const map = new Map({
target: div,

View File

@@ -198,7 +198,8 @@ describe('ol.source.VectorTile', function() {
});
target = document.createElement('div');
target.style.width = target.style.height = '100px';
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({

View File

@@ -206,7 +206,8 @@ describe('ol.source.XYZ', function() {
}
});
const target = document.createElement('div');
target.style.width = target.style.height = '100px';
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,

View File

@@ -123,6 +123,26 @@ describe('ol.webgl.RenderTarget', function() {
expect(spy.callCount).to.eql(2);
});
it('returns an array filled with 0 if outside of range', function() {
const rt = new WebGLRenderTarget(helper, [4, 4]);
helper.createTexture([4, 4], testImage_4x4, rt.getTexture());
let data = rt.readPixel(-1, 0);
expect(data).to.eql([0, 0, 0, 0]);
data = rt.readPixel(3, -1);
expect(data).to.eql([0, 0, 0, 0]);
data = rt.readPixel(6, 2);
expect(data).to.eql([0, 0, 0, 0]);
data = rt.readPixel(2, 7);
expect(data).to.eql([0, 0, 0, 0]);
data = rt.readPixel(2, 3);
expect(data).not.to.eql([0, 0, 0, 0]);
});
});
});