Compare commits
62 Commits
v6.0.0-bet
...
v6.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5891aaadf9 | ||
|
|
3ba7ecc602 | ||
|
|
887d8e8a90 | ||
|
|
251fc79484 | ||
|
|
752b69680e | ||
|
|
84a82ea5b2 | ||
|
|
08dd5f58a2 | ||
|
|
04963c83d6 | ||
|
|
711dacf4b7 | ||
|
|
6fbd196132 | ||
|
|
5969ac31ea | ||
|
|
21c51ff784 | ||
|
|
3d10e92218 | ||
|
|
84371fe4be | ||
|
|
1995164219 | ||
|
|
4f128a1ec0 | ||
|
|
f11744da56 | ||
|
|
08434ed3d5 | ||
|
|
2c76d7531b | ||
|
|
bbec7d76d4 | ||
|
|
255c4b34ba | ||
|
|
fd220f9cce | ||
|
|
34d68aadbd | ||
|
|
3bc822e8ef | ||
|
|
7340e865a3 | ||
|
|
5b231fe990 | ||
|
|
9c55256de2 | ||
|
|
491020f027 | ||
|
|
2989c84248 | ||
|
|
db1515a9f3 | ||
|
|
a774b1a278 | ||
|
|
81865f70e4 | ||
|
|
c6db2d07bb | ||
|
|
91215b303e | ||
|
|
319a905ec0 | ||
|
|
9883b7d3d9 | ||
|
|
07e31840eb | ||
|
|
03483b5439 | ||
|
|
9a42ab73d8 | ||
|
|
746f92d597 | ||
|
|
238fbca650 | ||
|
|
3875147812 | ||
|
|
be065cdacc | ||
|
|
187f58c1c3 | ||
|
|
c20bdedcac | ||
|
|
617dd9f031 | ||
|
|
077afac90a | ||
|
|
f091d96b6c | ||
|
|
a30aa78726 | ||
|
|
66f49559ee | ||
|
|
8899c3e3c5 | ||
|
|
10a2b718f5 | ||
|
|
c72f699c90 | ||
|
|
16e132caea | ||
|
|
070c1ec029 | ||
|
|
21c26cabed | ||
|
|
0f73f16cfa | ||
|
|
dfa8506549 | ||
|
|
8fb6ed5c6f | ||
|
|
c6a859d1ed | ||
|
|
b955579a9c | ||
|
|
56f37ab347 |
@@ -4,6 +4,16 @@
|
||||
|
||||
#### Backwards incompatible changes
|
||||
|
||||
#### Removal of optional this arguments
|
||||
|
||||
The optional this (i.e. opt_this) arguments were removed from the following methods.
|
||||
Please use closures, the es6 arrow function or the bind method to achieve this effect (Bind is explained here:
|
||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
|
||||
|
||||
* `forEachCorner` in `ol/extent`
|
||||
* `LRUCache#forEach`
|
||||
* `RBush#forEach` and `RBush#forEachInExtent`
|
||||
|
||||
##### The `setCenter`, `setZoom`, `setResolution` and `setRotation` methods on `ol/View` do not bypass constraints anymore
|
||||
|
||||
Previously, these methods allowed setting values that were inconsistent with the given view constraints.
|
||||
|
||||
@@ -100,8 +100,7 @@ function xyz2rgb(x) {
|
||||
|
||||
const raster = new RasterSource({
|
||||
sources: [new Stamen({
|
||||
layer: 'watercolor',
|
||||
transition: 0
|
||||
layer: 'watercolor'
|
||||
})],
|
||||
operation: function(pixels, data) {
|
||||
const hcl = rgb2hcl(pixels[0]);
|
||||
|
||||
45717
examples/data/csv/meteorite_landings.csv
Normal file
25
examples/filter-points-webgl.html
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
layout: example.html
|
||||
title: Filtering features with WebGL
|
||||
shortdesc: Using WebGL to filter large quantities of features
|
||||
docs: >
|
||||
This example shows how to use `ol/renderer/webgl/PointsLayer` to dynamically filter a large amount
|
||||
of point geometries. The above map is based on a dataset from the NASA containing 45k recorded meteorite
|
||||
landing sites. Each meteorite is marked by a circle on the map (the bigger the circle, the heavier
|
||||
the object). A pulse effect has been added, which is slightly offset by the year of the impact.
|
||||
|
||||
Adjusting the sliders causes the objects outside of the date range to be filtered out of the map. This is done using
|
||||
a custom fragment shader on the layer renderer, and by using the `v_opacity` attribute of the rendered objects
|
||||
to store the year of impact.
|
||||
|
||||
tags: "webgl, icon, sprite, filter, feature"
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<form>
|
||||
<div id="status">Show impacts between <span class="min-year"></span> and <span class="max-year"></span></div>
|
||||
|
||||
<label>Minimum year:</label>
|
||||
<input id="min-year" type="range" min="1850" max="2015" step="1" value="1850"/>
|
||||
<label>Maximum year:</label>
|
||||
<input id="max-year" type="range" min="1850" max="2015" step="1" value="2015"/>
|
||||
</form>
|
||||
162
examples/filter-points-webgl.js
Normal file
@@ -0,0 +1,162 @@
|
||||
import Map from '../src/ol/Map.js';
|
||||
import View from '../src/ol/View.js';
|
||||
import TileLayer from '../src/ol/layer/Tile.js';
|
||||
import Feature from '../src/ol/Feature';
|
||||
import Point from '../src/ol/geom/Point';
|
||||
import VectorLayer from '../src/ol/layer/Vector';
|
||||
import {Vector} from '../src/ol/source';
|
||||
import {fromLonLat} from '../src/ol/proj';
|
||||
import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer';
|
||||
import {clamp, lerp} from '../src/ol/math';
|
||||
import Stamen from '../src/ol/source/Stamen';
|
||||
|
||||
const features = [];
|
||||
const vectorSource = new Vector({
|
||||
features: [],
|
||||
attributions: 'NASA'
|
||||
});
|
||||
|
||||
const oldColor = [180, 140, 140];
|
||||
const newColor = [255, 80, 80];
|
||||
|
||||
const startTime = Date.now() * 0.001;
|
||||
|
||||
// hanle input values & events
|
||||
const minYearInput = document.getElementById('min-year');
|
||||
const maxYearInput = document.getElementById('max-year');
|
||||
function updateStatusText() {
|
||||
const div = document.getElementById('status');
|
||||
div.querySelector('span.min-year').textContent = minYearInput.value;
|
||||
div.querySelector('span.max-year').textContent = maxYearInput.value;
|
||||
}
|
||||
minYearInput.addEventListener('input', updateStatusText);
|
||||
minYearInput.addEventListener('change', updateStatusText);
|
||||
maxYearInput.addEventListener('input', updateStatusText);
|
||||
maxYearInput.addEventListener('change', updateStatusText);
|
||||
updateStatusText();
|
||||
|
||||
class WebglPointsLayer extends VectorLayer {
|
||||
createRenderer() {
|
||||
return new WebGLPointsLayerRenderer(this, {
|
||||
colorCallback: function(feature, vertex, component) {
|
||||
// component at index 3 is alpha
|
||||
if (component === 3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// color is interpolated based on year
|
||||
const ratio = clamp((feature.get('year') - 1800) / (2013 - 1800), 0, 1);
|
||||
return lerp(oldColor[component], newColor[component], ratio) / 255;
|
||||
},
|
||||
sizeCallback: function(feature) {
|
||||
return 18 * clamp(feature.get('mass') / 200000, 0, 1) + 8;
|
||||
},
|
||||
fragmentShader: [
|
||||
'precision mediump float;',
|
||||
|
||||
'uniform float u_time;',
|
||||
'uniform float u_minYear;',
|
||||
'uniform float u_maxYear;',
|
||||
|
||||
'varying vec2 v_texCoord;',
|
||||
'varying float v_opacity;',
|
||||
'varying vec4 v_color;',
|
||||
|
||||
'void main(void) {',
|
||||
' float impactYear = v_opacity;',
|
||||
|
||||
// filter out pixels if the year is outside of the given range
|
||||
' if (impactYear < u_minYear || v_opacity > u_maxYear) {',
|
||||
' discard;',
|
||||
' }',
|
||||
|
||||
' vec2 texCoord = v_texCoord * 2.0 - vec2(1.0, 1.0);',
|
||||
' float sqRadius = texCoord.x * texCoord.x + texCoord.y * texCoord.y;',
|
||||
' float value = 2.0 * (1.0 - sqRadius);',
|
||||
' float alpha = smoothstep(0.0, 1.0, value);',
|
||||
|
||||
' vec3 color = v_color.rgb;',
|
||||
' float period = 8.0;',
|
||||
' color.g *= 2.0 * (1.0 - sqrt(mod(u_time + impactYear * 0.025, period) / period));',
|
||||
|
||||
' gl_FragColor = vec4(color, v_color.a);',
|
||||
' gl_FragColor.a *= alpha;',
|
||||
' gl_FragColor.rgb *= gl_FragColor.a;',
|
||||
'}'
|
||||
].join(' '),
|
||||
opacityCallback: function(feature) {
|
||||
// here the opacity channel of the vertices is used to store the year of impact
|
||||
return feature.get('year');
|
||||
},
|
||||
uniforms: {
|
||||
u_time: function() {
|
||||
return Date.now() * 0.001 - startTime;
|
||||
},
|
||||
u_minYear: function() {
|
||||
return parseInt(minYearInput.value);
|
||||
},
|
||||
u_maxYear: function() {
|
||||
return parseInt(maxYearInput.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadData() {
|
||||
const client = new XMLHttpRequest();
|
||||
client.open('GET', 'data/csv/meteorite_landings.csv');
|
||||
client.onload = function() {
|
||||
const csv = client.responseText;
|
||||
let curIndex;
|
||||
let prevIndex = 0;
|
||||
let line;
|
||||
while ((curIndex = csv.indexOf('\n', prevIndex)) > 0) {
|
||||
line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
|
||||
prevIndex = curIndex + 1;
|
||||
|
||||
// skip header
|
||||
if (prevIndex === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const coords = fromLonLat([parseFloat(line[4]), parseFloat(line[3])]);
|
||||
|
||||
features.push(new Feature({
|
||||
mass: parseFloat(line[1]) || 0,
|
||||
year: parseInt(line[2]) || 0,
|
||||
geometry: new Point(coords)
|
||||
}));
|
||||
}
|
||||
vectorSource.addFeatures(features);
|
||||
};
|
||||
client.send();
|
||||
}
|
||||
|
||||
loadData();
|
||||
|
||||
const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new Stamen({
|
||||
layer: 'toner'
|
||||
})
|
||||
}),
|
||||
new WebglPointsLayer({
|
||||
source: vectorSource
|
||||
})
|
||||
],
|
||||
target: document.getElementById('map'),
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
zoom: 2
|
||||
})
|
||||
});
|
||||
|
||||
// animate the map
|
||||
function animate() {
|
||||
map.render();
|
||||
window.requestAnimationFrame(animate);
|
||||
}
|
||||
animate();
|
||||
@@ -24,8 +24,7 @@ function flood(pixels, data) {
|
||||
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
|
||||
const elevation = new XYZ({
|
||||
url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key,
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
crossOrigin: 'anonymous'
|
||||
});
|
||||
|
||||
const raster = new RasterSource({
|
||||
|
||||
@@ -100,8 +100,7 @@ function shade(inputs, data) {
|
||||
|
||||
const elevation = new XYZ({
|
||||
url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png',
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
crossOrigin: 'anonymous'
|
||||
});
|
||||
|
||||
const raster = new Raster({
|
||||
|
||||
32
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ol",
|
||||
"version": "5.3.0",
|
||||
"version": "6.0.0-beta.6",
|
||||
"description": "OpenLayers mapping library",
|
||||
"keywords": [
|
||||
"map",
|
||||
@@ -41,28 +41,28 @@
|
||||
"rbush": "2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openlayers/eslint-plugin": "^4.0.0-beta.1",
|
||||
"@openlayers/eslint-plugin": "^4.0.0-beta.2",
|
||||
"@types/arcgis-rest-api": "^10.4.4",
|
||||
"@types/geojson": "^7946.0.6",
|
||||
"@types/geojson": "^7946.0.7",
|
||||
"@types/pbf": "^3.0.1",
|
||||
"@types/rbush": "^2.0.2",
|
||||
"@types/topojson-specification": "^1.0.1",
|
||||
"buble": "^0.19.7",
|
||||
"buble-loader": "^0.5.1",
|
||||
"chaikin-smooth": "^1.0.4",
|
||||
"clean-css-cli": "4.2.1",
|
||||
"copy-webpack-plugin": "^5.0.1",
|
||||
"clean-css-cli": "4.3.0",
|
||||
"copy-webpack-plugin": "^5.0.2",
|
||||
"coveralls": "3.0.3",
|
||||
"eslint": "^5.15.2",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-openlayers": "^11.0.0",
|
||||
"expect.js": "0.3.1",
|
||||
"front-matter": "^3.0.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"globby": "^9.1.0",
|
||||
"handlebars": "4.1.1",
|
||||
"globby": "^9.2.0",
|
||||
"handlebars": "4.1.2",
|
||||
"istanbul": "0.4.5",
|
||||
"jquery": "3.3.1",
|
||||
"jquery": "3.4.0",
|
||||
"jsdoc": "3.5.5",
|
||||
"jsdoc-plugin-typescript": "^1.0.7",
|
||||
"karma": "^4.0.1",
|
||||
@@ -73,24 +73,24 @@
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^4.0.0-rc.2",
|
||||
"loglevelnext": "^3.0.0",
|
||||
"marked": "0.6.1",
|
||||
"mocha": "6.0.2",
|
||||
"ol-mapbox-style": "^4.2.1",
|
||||
"marked": "0.6.2",
|
||||
"mocha": "6.1.3",
|
||||
"ol-mapbox-style": "^4.3.1",
|
||||
"pixelmatch": "^4.0.2",
|
||||
"pngjs": "^3.4.0",
|
||||
"proj4": "2.5.0",
|
||||
"puppeteer": "~1.14.0",
|
||||
"serve-static": "^1.13.2",
|
||||
"shx": "^0.3.2",
|
||||
"sinon": "^7.2.7",
|
||||
"sinon": "^7.3.1",
|
||||
"terser-webpack-plugin": "^1.2.3",
|
||||
"typescript": "^3.2.2",
|
||||
"url-polyfill": "^1.1.5",
|
||||
"walk": "^2.3.9",
|
||||
"webpack": "4.29.6",
|
||||
"webpack": "4.30.0",
|
||||
"webpack-cli": "^3.3.0",
|
||||
"webpack-dev-middleware": "^3.6.1",
|
||||
"webpack-dev-server": "^3.2.1",
|
||||
"webpack-dev-middleware": "^3.6.2",
|
||||
"webpack-dev-server": "^3.3.1",
|
||||
"yargs": "^13.2.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 935 B After Width: | Height: | Size: 1016 B |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 891 B After Width: | Height: | Size: 904 B |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
@@ -131,4 +131,4 @@ const map = new Map({
|
||||
});
|
||||
map.getView().fit(vectorSource.getExtent());
|
||||
|
||||
render({tolerance: 0.02});
|
||||
render({tolerance: 0.021});
|
||||
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -103,4 +103,4 @@ const map = new Map({
|
||||
});
|
||||
map.getView().fit(vectorSource.getExtent());
|
||||
|
||||
render({tolerance: 0.02});
|
||||
render({tolerance: 0.024});
|
||||
|
||||
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 778 B After Width: | Height: | Size: 886 B |
|
Before Width: | Height: | Size: 801 B After Width: | Height: | Size: 910 B |
@@ -170,6 +170,9 @@ class Tile extends EventTarget {
|
||||
// cleaned up by refreshInterimChain)
|
||||
do {
|
||||
if (tile.getState() == TileState.LOADED) {
|
||||
// Show tile immediately instead of fading it in after loading, because
|
||||
// the interim tile is in place already
|
||||
this.transition_ = 0;
|
||||
return tile;
|
||||
}
|
||||
tile = tile.interimTile;
|
||||
|
||||
@@ -44,7 +44,7 @@ class TileCache extends LRUCache {
|
||||
this.remove(getKey(tile.tileCoord));
|
||||
tile.dispose();
|
||||
}
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -284,8 +284,6 @@ class View extends BaseObject {
|
||||
*/
|
||||
this.updateAnimationKey_;
|
||||
|
||||
this.updateAnimations_ = this.updateAnimations_.bind(this);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const
|
||||
@@ -452,6 +450,17 @@ class View extends BaseObject {
|
||||
* @api
|
||||
*/
|
||||
animate(var_args) {
|
||||
if (this.isDef() && !this.getAnimating()) {
|
||||
this.resolveConstraints(0);
|
||||
}
|
||||
this.animate_.apply(this, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {...(AnimationOptions|function(boolean): void)} var_args Animation options.
|
||||
*/
|
||||
animate_(var_args) {
|
||||
let animationCount = arguments.length;
|
||||
let callback;
|
||||
if (animationCount > 1 && typeof arguments[animationCount - 1] === 'function') {
|
||||
@@ -641,11 +650,7 @@ class View extends BaseObject {
|
||||
// prune completed series
|
||||
this.animations_ = this.animations_.filter(Boolean);
|
||||
if (more && this.updateAnimationKey_ === undefined) {
|
||||
this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_);
|
||||
}
|
||||
|
||||
if (!this.getAnimating()) {
|
||||
setTimeout(this.resolveConstraints.bind(this), 0);
|
||||
this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -929,13 +934,9 @@ class View extends BaseObject {
|
||||
const center = /** @type {import("./coordinate.js").Coordinate} */ (this.getCenter());
|
||||
const projection = this.getProjection();
|
||||
const resolution = /** @type {number} */ (this.getResolution());
|
||||
const pixelResolution = resolution / pixelRatio;
|
||||
const rotation = this.getRotation();
|
||||
return {
|
||||
center: [
|
||||
Math.round(center[0] / pixelResolution) * pixelResolution,
|
||||
Math.round(center[1] / pixelResolution) * pixelResolution
|
||||
],
|
||||
center: center.slice(0),
|
||||
projection: projection !== undefined ? projection : null,
|
||||
resolution: resolution,
|
||||
rotation: rotation,
|
||||
@@ -1085,7 +1086,7 @@ class View extends BaseObject {
|
||||
const callback = options.callback ? options.callback : VOID;
|
||||
|
||||
if (options.duration !== undefined) {
|
||||
this.animate({
|
||||
this.animate_({
|
||||
resolution: resolution,
|
||||
center: this.getConstrainedCenter(center, resolution),
|
||||
duration: options.duration,
|
||||
@@ -1312,7 +1313,7 @@ class View extends BaseObject {
|
||||
this.cancelAnimations();
|
||||
}
|
||||
|
||||
this.animate({
|
||||
this.animate_({
|
||||
rotation: newRotation,
|
||||
center: newCenter,
|
||||
resolution: newResolution,
|
||||
@@ -1325,9 +1326,13 @@ class View extends BaseObject {
|
||||
|
||||
/**
|
||||
* Notify the View that an interaction has started.
|
||||
* The view state will be resolved to a stable one if needed
|
||||
* (depending on its constraints).
|
||||
* @api
|
||||
*/
|
||||
beginInteraction() {
|
||||
this.resolveConstraints(0);
|
||||
|
||||
this.setHint(ViewHint.INTERACTING, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -400,26 +400,25 @@ export function extendXY(extent, x, y) {
|
||||
* callback returns a truthy value the function returns that value
|
||||
* immediately. Otherwise the function returns `false`.
|
||||
* @param {Extent} extent Extent.
|
||||
* @param {function(this:T, import("./coordinate.js").Coordinate): S} callback Callback.
|
||||
* @param {T=} opt_this Value to use as `this` when executing `callback`.
|
||||
* @param {function(import("./coordinate.js").Coordinate): S} callback Callback.
|
||||
* @return {S|boolean} Value.
|
||||
* @template S, T
|
||||
* @template S
|
||||
*/
|
||||
export function forEachCorner(extent, callback, opt_this) {
|
||||
export function forEachCorner(extent, callback) {
|
||||
let val;
|
||||
val = callback.call(opt_this, getBottomLeft(extent));
|
||||
val = callback(getBottomLeft(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
val = callback.call(opt_this, getBottomRight(extent));
|
||||
val = callback(getBottomRight(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
val = callback.call(opt_this, getTopRight(extent));
|
||||
val = callback(getTopRight(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
val = callback.call(opt_this, getTopLeft(extent));
|
||||
val = callback(getTopLeft(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
export {default as Circle} from './geom/Circle.js';
|
||||
export {default as Geometry} from './geom/Geometry.js';
|
||||
export {default as GeometryCollection} from './geom/GeometryCollection.js';
|
||||
export {default as LineString} from './geom/LineString.js';
|
||||
export {default as MultiLineString} from './geom/MultiLineString.js';
|
||||
export {default as MultiPoint} from './geom/MultiPoint.js';
|
||||
|
||||
@@ -5,6 +5,8 @@ import {createOrUpdate, forEachCorner, intersects} from '../extent.js';
|
||||
import GeometryType from './GeometryType.js';
|
||||
import SimpleGeometry from './SimpleGeometry.js';
|
||||
import {deflateCoordinate} from './flat/deflate.js';
|
||||
import {rotate, translate} from './flat/transform.js';
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -143,7 +145,7 @@ class Circle extends SimpleGeometry {
|
||||
return true;
|
||||
}
|
||||
|
||||
return forEachCorner(extent, this.intersectsCoordinate, this);
|
||||
return forEachCorner(extent, this.intersectsCoordinate.bind(this));
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -212,6 +214,29 @@ class Circle extends SimpleGeometry {
|
||||
this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius;
|
||||
this.changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
rotate(angle, anchor) {
|
||||
const center = this.getCenter();
|
||||
const stride = this.getStride();
|
||||
this.setCenter(rotate(center, 0, center.length, stride, angle, anchor, center));
|
||||
this.changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
translate(deltaX, deltaY) {
|
||||
const center = this.getCenter();
|
||||
const stride = this.getStride();
|
||||
this.setCenter(translate(center, 0, center.length, stride, deltaX, deltaY, center));
|
||||
this.changed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ export function pan(view, delta, opt_duration) {
|
||||
const currentCenter = view.getCenter();
|
||||
if (currentCenter) {
|
||||
const center = [currentCenter[0] + delta[0], currentCenter[1] + delta[1]];
|
||||
view.animate({
|
||||
view.animate_({
|
||||
duration: opt_duration !== undefined ? opt_duration : 250,
|
||||
easing: linear,
|
||||
center: view.getConstrainedCenter(center)
|
||||
|
||||
@@ -8,13 +8,6 @@ import Interaction, {zoomByDelta} from './Interaction.js';
|
||||
import {clamp} from '../math.js';
|
||||
|
||||
|
||||
/**
|
||||
* Maximum mouse wheel delta.
|
||||
* @type {number}
|
||||
*/
|
||||
const MAX_DELTA = 1;
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
@@ -30,6 +23,7 @@ export const Mode = {
|
||||
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
||||
* boolean to indicate whether that event should be handled. Default is
|
||||
* {@link module:ol/events/condition~always}.
|
||||
* @property {number} [maxDelta=1] Maximum mouse wheel delta.
|
||||
* @property {number} [duration=250] Animation duration in milliseconds.
|
||||
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
|
||||
* @property {boolean} [useAnchor=true] Enable zooming using the mouse's
|
||||
@@ -65,6 +59,12 @@ class MouseWheelZoom extends Interaction {
|
||||
*/
|
||||
this.lastDelta_ = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.maxDelta_ = options.maxDelta !== undefined ? options.maxDelta : 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
@@ -235,8 +235,7 @@ class MouseWheelZoom extends Interaction {
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
const maxDelta = MAX_DELTA;
|
||||
const delta = clamp(this.totalDelta_, -maxDelta, maxDelta);
|
||||
const delta = clamp(this.totalDelta_, -this.maxDelta_, this.maxDelta_);
|
||||
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
|
||||
this.mode_ = undefined;
|
||||
this.totalDelta_ = 0;
|
||||
|
||||
@@ -27,7 +27,6 @@ import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer';
|
||||
* of the heatmap, specified as an array of CSS color strings.
|
||||
* @property {number} [radius=8] Radius size in pixels.
|
||||
* @property {number} [blur=15] Blur size in pixels.
|
||||
* @property {number} [shadow=250] Shadow size in pixels.
|
||||
* @property {string|function(import("../Feature.js").default):number} [weight='weight'] The feature
|
||||
* attribute to use for the weight or a function that returns a weight from a feature. Weight values
|
||||
* should range from 0 to 1 (and values outside will be clamped to that range).
|
||||
@@ -75,7 +74,6 @@ class Heatmap extends VectorLayer {
|
||||
delete baseOptions.gradient;
|
||||
delete baseOptions.radius;
|
||||
delete baseOptions.blur;
|
||||
delete baseOptions.shadow;
|
||||
delete baseOptions.weight;
|
||||
super(baseOptions);
|
||||
|
||||
@@ -85,24 +83,6 @@ class Heatmap extends VectorLayer {
|
||||
*/
|
||||
this.gradient_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.shadow_ = options.shadow !== undefined ? options.shadow : 250;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {string|undefined}
|
||||
*/
|
||||
this.circleImage_ = undefined;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array<Array<import("../style/Style.js").default>>}
|
||||
*/
|
||||
this.styleCache_ = null;
|
||||
|
||||
listen(this,
|
||||
getChangeEventType(Property.GRADIENT),
|
||||
this.handleGradientChanged_, this);
|
||||
@@ -206,15 +186,15 @@ class Heatmap extends VectorLayer {
|
||||
attribute float a_rotateWithView;
|
||||
attribute vec2 a_offsets;
|
||||
attribute float a_opacity;
|
||||
|
||||
|
||||
uniform mat4 u_projectionMatrix;
|
||||
uniform mat4 u_offsetScaleMatrix;
|
||||
uniform mat4 u_offsetRotateMatrix;
|
||||
uniform float u_size;
|
||||
|
||||
|
||||
varying vec2 v_texCoord;
|
||||
varying float v_opacity;
|
||||
|
||||
|
||||
void main(void) {
|
||||
mat4 offsetMatrix = u_offsetScaleMatrix;
|
||||
if (a_rotateWithView == 1.0) {
|
||||
@@ -229,16 +209,16 @@ class Heatmap extends VectorLayer {
|
||||
precision mediump float;
|
||||
uniform float u_resolution;
|
||||
uniform float u_blurSlope;
|
||||
|
||||
|
||||
varying vec2 v_texCoord;
|
||||
varying float v_opacity;
|
||||
|
||||
|
||||
void main(void) {
|
||||
vec2 texCoord = v_texCoord * 2.0 - vec2(1.0, 1.0);
|
||||
float sqRadius = texCoord.x * texCoord.x + texCoord.y * texCoord.y;
|
||||
float value = (1.0 - sqrt(sqRadius)) * u_blurSlope;
|
||||
float alpha = smoothstep(0.0, 1.0, value) * v_opacity;
|
||||
gl_FragColor = vec4(1.0, 1.0, 1.0, alpha);
|
||||
gl_FragColor = vec4(alpha, alpha, alpha, alpha);
|
||||
}`,
|
||||
uniforms: {
|
||||
u_size: function() {
|
||||
@@ -273,9 +253,7 @@ class Heatmap extends VectorLayer {
|
||||
}
|
||||
}
|
||||
],
|
||||
opacityCallback: function(feature) {
|
||||
return this.weightFunction_(feature);
|
||||
}.bind(this)
|
||||
opacityCallback: this.weightFunction_
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
* @module ol/render/webgl
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
export const DEFAULT_FONT = '10px sans-serif';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {import("../color.js").Color}
|
||||
*/
|
||||
export const DEFAULT_FILLSTYLE = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
export const DEFAULT_LINECAP = 'round';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
export const DEFAULT_LINEDASH = [];
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_LINEDASHOFFSET = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
export const DEFAULT_LINEJOIN = 'round';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_MITERLIMIT = 10;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {import("../color.js").Color}
|
||||
*/
|
||||
export const DEFAULT_STROKESTYLE = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_TEXTALIGN = 0.5;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_TEXTBASELINE = 0.5;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_LINEWIDTH = 1;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const EPSILON = Number.EPSILON || 2.220446049250313e-16;
|
||||
|
||||
/**
|
||||
* Calculates the orientation of a triangle based on the determinant method.
|
||||
* @param {number} x1 First X coordinate.
|
||||
* @param {number} y1 First Y coordinate.
|
||||
* @param {number} x2 Second X coordinate.
|
||||
* @param {number} y2 Second Y coordinate.
|
||||
* @param {number} x3 Third X coordinate.
|
||||
* @param {number} y3 Third Y coordinate.
|
||||
* @return {boolean|undefined} Triangle is clockwise.
|
||||
*/
|
||||
export const triangleIsCounterClockwise = function(x1, y1, x2, y2, x3, y3) {
|
||||
const area = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
|
||||
return (area <= EPSILON && area >= -EPSILON) ?
|
||||
undefined : area > 0;
|
||||
};
|
||||
|
||||
@@ -110,8 +110,7 @@ const FRAGMENT_SHADER = `
|
||||
*
|
||||
* The following uniform is used for the main texture: `u_texture`.
|
||||
*
|
||||
* Please note that the main shader output should have premultiplied alpha, otherwise the colors will be blended
|
||||
* additively.
|
||||
* Please note that the main shader output should have premultiplied alpha, otherwise visual anomalies may occur.
|
||||
*
|
||||
* Points are rendered as quads with the following structure:
|
||||
*
|
||||
@@ -323,6 +322,9 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
baseIndex + 1, baseIndex + 2, baseIndex + 3
|
||||
);
|
||||
});
|
||||
|
||||
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
|
||||
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||
}
|
||||
|
||||
// write new data
|
||||
|
||||
@@ -288,8 +288,7 @@ class RasterSource extends ImageSource {
|
||||
frameState.focus = center;
|
||||
frameState.size[0] = Math.round(getWidth(extent) / resolution);
|
||||
frameState.size[1] = Math.round(getHeight(extent) / resolution);
|
||||
frameState.time = Date.now();
|
||||
frameState.animate = false;
|
||||
frameState.time = Infinity;
|
||||
|
||||
const viewState = frameState.viewState;
|
||||
viewState.center = center;
|
||||
|
||||
@@ -485,7 +485,7 @@ class VectorSource extends Source {
|
||||
}
|
||||
} else {
|
||||
if (this.featuresRtree_) {
|
||||
this.featuresRtree_.forEach(this.removeFeatureInternal, this);
|
||||
this.featuresRtree_.forEach(this.removeFeatureInternal.bind(this));
|
||||
for (const id in this.nullGeometryFeatures_) {
|
||||
this.removeFeatureInternal(this.nullGeometryFeatures_[id]);
|
||||
}
|
||||
|
||||
@@ -96,17 +96,15 @@ class LRUCache extends EventTarget {
|
||||
|
||||
|
||||
/**
|
||||
* @param {function(this: S, T, string, LRUCache): ?} f The function
|
||||
* @param {function(T, string, LRUCache): ?} f The function
|
||||
* to call for every entry from the oldest to the newer. This function takes
|
||||
* 3 arguments (the entry value, the entry key and the LRUCache object).
|
||||
* The return value is ignored.
|
||||
* @param {S=} opt_this The object to use as `this` in `f`.
|
||||
* @template S
|
||||
*/
|
||||
forEach(f, opt_this) {
|
||||
forEach(f) {
|
||||
let entry = this.oldest_;
|
||||
while (entry) {
|
||||
f.call(opt_this, entry.value_, entry.key_, this);
|
||||
f(entry.value_, entry.key_, this);
|
||||
entry = entry.newer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,41 +156,35 @@ class RBush {
|
||||
* Calls a callback function with each value in the tree.
|
||||
* If the callback returns a truthy value, this value is returned without
|
||||
* checking the rest of the tree.
|
||||
* @param {function(this: S, T): *} callback Callback.
|
||||
* @param {S=} opt_this The object to use as `this` in `callback`.
|
||||
* @param {function(T): *} callback Callback.
|
||||
* @return {*} Callback return value.
|
||||
* @template S
|
||||
*/
|
||||
forEach(callback, opt_this) {
|
||||
return this.forEach_(this.getAll(), callback, opt_this);
|
||||
forEach(callback) {
|
||||
return this.forEach_(this.getAll(), callback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls a callback function with each value in the provided extent.
|
||||
* @param {import("../extent.js").Extent} extent Extent.
|
||||
* @param {function(this: S, T): *} callback Callback.
|
||||
* @param {S=} opt_this The object to use as `this` in `callback`.
|
||||
* @param {function(T): *} callback Callback.
|
||||
* @return {*} Callback return value.
|
||||
* @template S
|
||||
*/
|
||||
forEachInExtent(extent, callback, opt_this) {
|
||||
return this.forEach_(this.getInExtent(extent), callback, opt_this);
|
||||
forEachInExtent(extent, callback) {
|
||||
return this.forEach_(this.getInExtent(extent), callback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array<T>} values Values.
|
||||
* @param {function(this: S, T): *} callback Callback.
|
||||
* @param {S=} opt_this The object to use as `this` in `callback`.
|
||||
* @param {function(T): *} callback Callback.
|
||||
* @private
|
||||
* @return {*} Callback return value.
|
||||
* @template S
|
||||
*/
|
||||
forEach_(values, callback, opt_this) {
|
||||
forEach_(values, callback) {
|
||||
let result;
|
||||
for (let i = 0, l = values.length; i < l; i++) {
|
||||
result = callback.call(opt_this, values[i]);
|
||||
result = callback(values[i]);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("../color.js").Color|import("../colorlike.js").ColorLike} [color] A color, gradient or pattern.
|
||||
* @property {import("../color.js").Color|import("../colorlike.js").ColorLike} [color=null] A color, gradient or pattern.
|
||||
* See {@link module:ol/color~Color} and {@link module:ol/colorlike~ColorLike} for possible formats.
|
||||
* Default null; if null, the Canvas/renderer default black will be used.
|
||||
*/
|
||||
|
||||
@@ -16,14 +16,14 @@ import ImageStyle from './Image.js';
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {Array<number>} [anchor=[0.5, 0.5]] Anchor. Default value is the icon center.
|
||||
* @property {import("./IconOrigin.js").default} [anchorOrigin] Origin of the anchor: `bottom-left`, `bottom-right`,
|
||||
* `top-left` or `top-right`. Default is `top-left`.
|
||||
* @property {import("./IconAnchorUnits.js").default} [anchorXUnits] Units in which the anchor x value is
|
||||
* @property {import("./IconOrigin.js").default} [anchorOrigin='top-left'] Origin of the anchor: `bottom-left`, `bottom-right`,
|
||||
* `top-left` or `top-right`.
|
||||
* @property {import("./IconAnchorUnits.js").default} [anchorXUnits='fraction'] Units in which the anchor x value is
|
||||
* specified. A value of `'fraction'` indicates the x value is a fraction of the icon. A value of `'pixels'` indicates
|
||||
* the x value in pixels. Default is `'fraction'`.
|
||||
* @property {import("./IconAnchorUnits.js").default} [anchorYUnits] Units in which the anchor y value is
|
||||
* the x value in pixels.
|
||||
* @property {import("./IconAnchorUnits.js").default} [anchorYUnits='fraction'] Units in which the anchor y value is
|
||||
* specified. A value of `'fraction'` indicates the y value is a fraction of the icon. A value of `'pixels'` indicates
|
||||
* the y value in pixels. Default is `'fraction'`.
|
||||
* the y value in pixels.
|
||||
* @property {import("../color.js").Color|string} [color] Color to tint the icon. If not specified,
|
||||
* the icon will be left as is.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that you must provide a
|
||||
@@ -34,8 +34,8 @@ import ImageStyle from './Image.js';
|
||||
* to provide the size of the image, with the `imgSize` option.
|
||||
* @property {Array<number>} [offset=[0, 0]] Offset, which, together with the size and the offset origin, define the
|
||||
* sub-rectangle to use from the original icon image.
|
||||
* @property {import("./IconOrigin.js").default} [offsetOrigin] Origin of the offset: `bottom-left`, `bottom-right`,
|
||||
* `top-left` or `top-right`. Default is `top-left`.
|
||||
* @property {import("./IconOrigin.js").default} [offsetOrigin='top-left'] Origin of the offset: `bottom-left`, `bottom-right`,
|
||||
* `top-left` or `top-right`.
|
||||
* @property {number} [opacity=1] Opacity of the icon.
|
||||
* @property {number} [scale=1] Scale.
|
||||
* @property {boolean} [rotateWithView=false] Whether to rotate the icon with the view.
|
||||
|
||||
@@ -18,13 +18,13 @@ const DEFAULT_FILL_COLOR = '#333';
|
||||
* @typedef {Object} Options
|
||||
* @property {string} [font] Font style as CSS 'font' value, see:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font. Default is '10px sans-serif'
|
||||
* @property {number} [maxAngle] When `placement` is set to `'line'`, allow a maximum angle between adjacent characters.
|
||||
* @property {number} [maxAngle=Math.PI/4] When `placement` is set to `'line'`, allow a maximum angle between adjacent characters.
|
||||
* The expected value is in radians, and the default is 45° (`Math.PI / 4`).
|
||||
* @property {number} [offsetX=0] Horizontal text offset in pixels. A positive will shift the text right.
|
||||
* @property {number} [offsetY=0] Vertical text offset in pixels. A positive will shift the text down.
|
||||
* @property {boolean} [overflow=false] For polygon labels or when `placement` is set to `'line'`, allow text to exceed
|
||||
* the width of the polygon at the label position or the length of the path that it follows.
|
||||
* @property {import("./TextPlacement.js").default|string} [placement] Text placement.
|
||||
* @property {import("./TextPlacement.js").default|string} [placement='point'] Text placement.
|
||||
* @property {number} [scale] Scale.
|
||||
* @property {boolean} [rotateWithView=false] Whether to rotate the text with the view.
|
||||
* @property {number} [rotation=0] Rotation in radians (positive rotation clockwise).
|
||||
|
||||
@@ -23,8 +23,8 @@ import {getContext} from '../webgl';
|
||||
|
||||
/**
|
||||
* @typedef {Object} BufferCacheEntry
|
||||
* @property {import("./Buffer.js").default} buf
|
||||
* @property {WebGLBuffer} buffer
|
||||
* @property {import("./Buffer.js").default} buffer
|
||||
* @property {WebGLBuffer} webGlBuffer
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -153,15 +153,24 @@ export const DefaultAttrib = {
|
||||
* ### Binding WebGL buffers and flushing data into them:
|
||||
*
|
||||
* Data that must be passed to the GPU has to be transferred using `WebGLArrayBuffer` objects.
|
||||
* A buffer has to be created only once, but must be bound everytime the data it holds is changed. Using `WebGLHelper.bindBuffer`
|
||||
* will bind the buffer and flush the new data to the GPU.
|
||||
* A buffer has to be created only once, but must be bound everytime the buffer content should be used for rendering.
|
||||
* This is done using `WebGLHelper.bindBuffer`.
|
||||
* When the buffer's array content has changed, the new data has to be flushed to the GPU memory; this is done using
|
||||
* `WebGLHelper.flushBufferData`. Note: this operation is expensive and should be done as infrequently as possible.
|
||||
*
|
||||
* For now, the `WebGLHelper` class expects {@link module:ol/webgl/Buffer~WebGLArrayBuffer} objects.
|
||||
* When binding a `WebGLArrayBuffer`, a `target` parameter must be given: it should be either {@link module:ol/webgl~ARRAY_BUFFER}
|
||||
* (if the buffer contains vertices data) or {@link module:ol/webgl~ELEMENT_ARRAY_BUFFER} (if the buffer contains indices data).
|
||||
*
|
||||
* Examples below:
|
||||
* ```js
|
||||
* // at initialization phase
|
||||
* this.verticesBuffer = new WebGLArrayBuffer([], DYNAMIC_DRAW);
|
||||
* this.indicesBuffer = new WebGLArrayBuffer([], DYNAMIC_DRAW);
|
||||
*
|
||||
* // when array values have changed
|
||||
* this.context.flushBufferData(ARRAY_BUFFER, this.verticesBuffer);
|
||||
* this.context.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer);
|
||||
*
|
||||
* // at rendering phase
|
||||
* this.context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer);
|
||||
* this.context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer);
|
||||
@@ -342,24 +351,35 @@ class WebGLHelper extends Disposable {
|
||||
* Just bind the buffer if it's in the cache. Otherwise create
|
||||
* the WebGL buffer, bind it, populate it, and add an entry to
|
||||
* the cache.
|
||||
* TODO: improve this, the logic is unclear: we want A/ to bind a buffer and B/ to flush data in it
|
||||
* @param {number} target Target.
|
||||
* @param {import("./Buffer").default} buf Buffer.
|
||||
* @param {number} target Target, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.
|
||||
* @param {import("./Buffer").default} buffer Buffer.
|
||||
* @api
|
||||
*/
|
||||
bindBuffer(target, buf) {
|
||||
bindBuffer(target, buffer) {
|
||||
const gl = this.getGL();
|
||||
const arr = buf.getArray();
|
||||
const bufferKey = getUid(buf);
|
||||
const bufferKey = getUid(buffer);
|
||||
let bufferCache = this.bufferCache_[bufferKey];
|
||||
if (!bufferCache) {
|
||||
const buffer = gl.createBuffer();
|
||||
const webGlBuffer = gl.createBuffer();
|
||||
bufferCache = this.bufferCache_[bufferKey] = {
|
||||
buf: buf,
|
||||
buffer: buffer
|
||||
buffer: buffer,
|
||||
webGlBuffer: webGlBuffer
|
||||
};
|
||||
}
|
||||
gl.bindBuffer(target, bufferCache.buffer);
|
||||
gl.bindBuffer(target, bufferCache.webGlBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data contained in the buffer array; this is required for the
|
||||
* new data to be rendered
|
||||
* @param {number} target Target, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.
|
||||
* @param {import("./Buffer").default} buffer Buffer.
|
||||
* @api
|
||||
*/
|
||||
flushBufferData(target, buffer) {
|
||||
const gl = this.getGL();
|
||||
const arr = buffer.getArray();
|
||||
this.bindBuffer(target, buffer);
|
||||
let /** @type {ArrayBufferView} */ arrayBuffer;
|
||||
if (target == ARRAY_BUFFER) {
|
||||
arrayBuffer = new Float32Array(arr);
|
||||
@@ -367,7 +387,7 @@ class WebGLHelper extends Disposable {
|
||||
arrayBuffer = this.hasOESElementIndexUint ?
|
||||
new Uint32Array(arr) : new Uint16Array(arr);
|
||||
}
|
||||
gl.bufferData(target, arrayBuffer, buf.getUsage());
|
||||
gl.bufferData(target, arrayBuffer, buffer.getUsage());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,6 @@ const DEFAULT_FRAGMENT_SHADER = `
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(u_image, v_texCoord);
|
||||
gl_FragColor.rgb *= gl_FragColor.a;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -59,6 +58,9 @@ const DEFAULT_FRAGMENT_SHADER = `
|
||||
* a pixel which is 100% red with an opacity of 50% must have a color of (r=0.5, g=0, b=0, a=0.5).
|
||||
* Failing to provide pixel colors with premultiplied alpha will result in render anomalies.
|
||||
*
|
||||
* The default post-processing pass does *not* multiply color values with alpha value, it expects color values to be
|
||||
* premultiplied.
|
||||
*
|
||||
* Default shaders are shown hereafter:
|
||||
*
|
||||
* * Vertex shader:
|
||||
@@ -91,7 +93,6 @@ const DEFAULT_FRAGMENT_SHADER = `
|
||||
*
|
||||
* void main() {
|
||||
* gl_FragColor = texture2D(u_image, v_texCoord);
|
||||
* gl_FragColor.rgb *= gl_FragColor.a;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
|
||||
@@ -76,7 +76,7 @@ main() {
|
||||
npm install
|
||||
npm run build-package
|
||||
cd ${BUILT_PACKAGE}
|
||||
npm publish
|
||||
npm publish --tag beta
|
||||
}
|
||||
|
||||
if test ${#} -ne 1; then
|
||||
|
||||
@@ -239,13 +239,6 @@ describe('ol.extent', function() {
|
||||
}
|
||||
);
|
||||
|
||||
it('calls the callback with given scope', function() {
|
||||
const extent = [1, 2, 3, 4];
|
||||
const scope = {humpty: 'dumpty'};
|
||||
_ol_extent_.forEachCorner(extent, callbackTrue, scope);
|
||||
expect(callbackTrue.calledOn(scope)).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getArea', function() {
|
||||
|
||||
@@ -263,6 +263,34 @@ describe('ol.geom.Circle', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('#rotate', function() {
|
||||
|
||||
it('rotates the center around the anchor', function() {
|
||||
circle.setCenter([1, 0]);
|
||||
circle.rotate(Math.PI / 2, [2, 0]);
|
||||
expect(circle.getCenter()).to.eql([2, -1]);
|
||||
expect(circle.getExtent()).to.eql([1, -2, 3, 0]);
|
||||
});
|
||||
|
||||
it('does not change if the anchor equals the center', function() {
|
||||
const center = [1, 0];
|
||||
circle.setCenter(center);
|
||||
const extent = circle.getExtent();
|
||||
circle.rotate(Math.PI / 2, center);
|
||||
expect(circle.getCenter()).to.eql(center);
|
||||
expect(circle.getExtent()).to.eql(extent);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#translate', function() {
|
||||
|
||||
it('translates the circle', function() {
|
||||
circle.setCenter([1, 1]);
|
||||
circle.translate(5, 10);
|
||||
expect(circle.getCenter()).to.eql([6, 11]);
|
||||
expect(circle.getExtent()).to.eql([5, 10, 7, 12]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('ol.interaction.KeyboardPan', function() {
|
||||
describe('handleEvent()', function() {
|
||||
it('pans on arrow keys', function() {
|
||||
const view = map.getView();
|
||||
const spy = sinon.spy(view, 'animate');
|
||||
const spy = sinon.spy(view, 'animate_');
|
||||
const event = new MapBrowserEvent('keydown', map, {
|
||||
type: 'keydown',
|
||||
target: map.getTargetElement(),
|
||||
@@ -51,7 +51,7 @@ describe('ol.interaction.KeyboardPan', function() {
|
||||
expect(spy.getCall(3).args[0].center).to.eql([128, 0]);
|
||||
view.setCenter([0, 0]);
|
||||
|
||||
view.animate.restore();
|
||||
view.animate_.restore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -41,13 +41,18 @@ describe('ol.renderer.canvas.TileLayer', function() {
|
||||
document.body.removeChild(target);
|
||||
});
|
||||
|
||||
it('properly handles interim tiles', function() {
|
||||
it('properly handles interim tiles', function(done) {
|
||||
const layer = map.getLayers().item(0);
|
||||
source.once('tileloadend', function(e) {
|
||||
expect(e.tile.inTransition()).to.be(false);
|
||||
done();
|
||||
});
|
||||
source.updateParams({TIME: '1'});
|
||||
map.renderSync();
|
||||
const tiles = map.getRenderer().getLayerRenderer(layer).renderedTiles;
|
||||
expect(tiles.length).to.be(1);
|
||||
expect(tiles[0]).to.equal(tile);
|
||||
expect(tile.inTransition()).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ where('Uint8ClampedArray').describe('ol.source.Raster', function() {
|
||||
map2.once('moveend', function() {
|
||||
expect(tileCache.getCount()).to.equal(1);
|
||||
const state = tileCache.peekLast().getState();
|
||||
expect(state === TileState.LOADED || state === TileState.LOADED).to.be(true);
|
||||
expect(state === TileState.LOADING || state === TileState.LOADED).to.be(true);
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
@@ -531,8 +531,8 @@ describe('ol.source.Vector', function() {
|
||||
loader: function(extent) {
|
||||
setTimeout(function() {
|
||||
const lonLatExtent = transformExtent(extent, 'EPSG:3857', 'EPSG:4326');
|
||||
expect(lonLatExtent[0]).to.roughlyEqual(-99.261474609, 1e-9);
|
||||
expect(lonLatExtent[2]).to.roughlyEqual(-95.965576171, 1e-9);
|
||||
expect(lonLatExtent[0]).to.roughlyEqual(-99.259349218, 1e-9);
|
||||
expect(lonLatExtent[2]).to.roughlyEqual(-95.963450781, 1e-9);
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -1801,6 +1801,52 @@ describe('ol.View', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('does not start unexpected animations during interaction', function() {
|
||||
let map;
|
||||
beforeEach(function() {
|
||||
map = new Map({
|
||||
target: createMapDiv(512, 256)
|
||||
});
|
||||
});
|
||||
afterEach(function() {
|
||||
disposeMap(map);
|
||||
});
|
||||
|
||||
it('works when initialized with #setCenter() and #setZoom()', function(done) {
|
||||
const view = map.getView();
|
||||
let callCount = 0;
|
||||
view.on('change:resolution', function() {
|
||||
++callCount;
|
||||
});
|
||||
view.setCenter([0, 0]);
|
||||
view.setZoom(0);
|
||||
view.beginInteraction();
|
||||
view.endInteraction();
|
||||
setTimeout(function() {
|
||||
expect(callCount).to.be(1);
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
it('works when initialized with #animate()', function(done) {
|
||||
const view = map.getView();
|
||||
let callCount = 0;
|
||||
view.on('change:resolution', function() {
|
||||
++callCount;
|
||||
});
|
||||
view.animate({
|
||||
center: [0, 0],
|
||||
zoom: 0
|
||||
});
|
||||
view.beginInteraction();
|
||||
view.endInteraction();
|
||||
setTimeout(function() {
|
||||
expect(callCount).to.be(1);
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ol.View.isNoopAnimation()', function() {
|
||||
|
||||
const cases = [{
|
||||
|
||||