Compare commits
103 Commits
v6.0.0-bet
...
v6.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cc1ff0f45 | ||
|
|
2df5f85018 | ||
|
|
4a0d6ce43c | ||
|
|
187969cbb3 | ||
|
|
8cdfc6e17c | ||
|
|
9cd35d67a9 | ||
|
|
4d2fa476a3 | ||
|
|
f864c05070 | ||
|
|
3557271e5a | ||
|
|
8a49e06ebd | ||
|
|
6783f6adb9 | ||
|
|
5eb27f7704 | ||
|
|
df240fe905 | ||
|
|
300522e3cc | ||
|
|
3ede6e32be | ||
|
|
ce45a10063 | ||
|
|
2438ef167b | ||
|
|
d6dc7a926d | ||
|
|
6e6a2cae06 | ||
|
|
a6ebad008f | ||
|
|
ed5d4aaf6d | ||
|
|
f507efe77d | ||
|
|
83173cd385 | ||
|
|
b6b91abc27 | ||
|
|
05c791efcd | ||
|
|
2342626a57 | ||
|
|
7c0a0a4786 | ||
|
|
a35ca03e07 | ||
|
|
c522454af1 | ||
|
|
ef10834eb3 | ||
|
|
8efad7bcff | ||
|
|
51c8886d60 | ||
|
|
34a8702202 | ||
|
|
1f00da8d83 | ||
|
|
8d793ad6ec | ||
|
|
bd8a7bff16 | ||
|
|
d059689856 | ||
|
|
4ea28333f8 | ||
|
|
edebf35135 | ||
|
|
90b645daa5 | ||
|
|
9eac927b45 | ||
|
|
ec5e8bfa50 | ||
|
|
124e984a6d | ||
|
|
76f44efbb6 | ||
|
|
ddaaf6a860 | ||
|
|
92c16ecdf5 | ||
|
|
635d7a9189 | ||
|
|
fc6ca5eaf3 | ||
|
|
803f960ded | ||
|
|
ef1b672323 | ||
|
|
002dc34455 | ||
|
|
cc8389d588 | ||
|
|
a8e0e4e960 | ||
|
|
9ae7256048 | ||
|
|
e036767746 | ||
|
|
9a49c91fdd | ||
|
|
37ff7f547b | ||
|
|
c97a728531 | ||
|
|
811bff0430 | ||
|
|
2412fe0211 | ||
|
|
32084a7fce | ||
|
|
7817cf31c6 | ||
|
|
2e5aac8dba | ||
|
|
8b08996703 | ||
|
|
1fbbc56f93 | ||
|
|
8f77a300de | ||
|
|
91d49b26b5 | ||
|
|
02ce2f4bec | ||
|
|
d4f98c8e59 | ||
|
|
ab0860a22a | ||
|
|
dd478167a0 | ||
|
|
2df911e0a8 | ||
|
|
e9939ecc38 | ||
|
|
d058439756 | ||
|
|
8c21c9196d | ||
|
|
68b7831daf | ||
|
|
153e06e4d5 | ||
|
|
9bedfb7cb0 | ||
|
|
1bd23a0c32 | ||
|
|
50343afa61 | ||
|
|
a9e1ebccd3 | ||
|
|
9672142c1e | ||
|
|
6123be726a | ||
|
|
05d8517686 | ||
|
|
87d5f4c8bc | ||
|
|
06be00bbd5 | ||
|
|
7fb113c3dc | ||
|
|
98b0c65450 | ||
|
|
698816030e | ||
|
|
03e70bd10e | ||
|
|
e0983cb1c6 | ||
|
|
a366803cdd | ||
|
|
33d007ce01 | ||
|
|
8566cfc227 | ||
|
|
65be907095 | ||
|
|
532b8194b1 | ||
|
|
eb912d95ca | ||
|
|
245ded43d4 | ||
|
|
7c215b2532 | ||
|
|
579d05a0cb | ||
|
|
b42ee8ca0f | ||
|
|
ede864c823 | ||
|
|
9860699d5f |
@@ -4,9 +4,19 @@
|
||||
|
||||
#### Backwards incompatible changes
|
||||
|
||||
#### Removal of `TOUCH` constant from `ol/has`
|
||||
|
||||
If you were previously using this constant, you can check if `'ontouchstart'` is defined in `window` instead.
|
||||
|
||||
```js
|
||||
if ('ontouchstart' in window) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Removal of `GEOLOCATION` constant from `ol/has`
|
||||
|
||||
If you were previously using this constant, you can check if `'geolocation'` is define in `navigator` instead.
|
||||
If you were previously using this constant, you can check if `'geolocation'` is defined in `navigator` instead.
|
||||
|
||||
```js
|
||||
if ('geolocation' in navigator) {
|
||||
|
||||
@@ -59,7 +59,9 @@ function includeAugments(doclet) {
|
||||
});
|
||||
}
|
||||
cls._hideConstructor = true;
|
||||
delete cls.undocumented;
|
||||
if (!cls.undocumented) {
|
||||
cls._documented = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,6 +152,9 @@ exports.handlers = {
|
||||
// Remove all other undocumented symbols
|
||||
doclet.undocumented = true;
|
||||
}
|
||||
if (doclet._documented) {
|
||||
delete doclet.undocumented;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,55 +214,39 @@ function buildNav(members) {
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
function createEntry(type, v) {
|
||||
return {
|
||||
type: type,
|
||||
longname: v.longname,
|
||||
name: v.name,
|
||||
classes: find({
|
||||
kind: 'class',
|
||||
memberof: v.longname
|
||||
}).map(createEntry.bind(this, 'class')),
|
||||
members: find({
|
||||
kind: 'member',
|
||||
memberof: v.longname
|
||||
}),
|
||||
methods: find({
|
||||
kind: 'function',
|
||||
memberof: v.longname
|
||||
}),
|
||||
typedefs: find({
|
||||
kind: 'typedef',
|
||||
memberof: v.longname
|
||||
}),
|
||||
events: find({
|
||||
kind: 'event',
|
||||
memberof: v.longname
|
||||
})
|
||||
};
|
||||
}
|
||||
_.each(merged, function(v) {
|
||||
// exclude interfaces from sidebar
|
||||
if (v.interface !== true) {
|
||||
if (v.kind == 'module') {
|
||||
nav.push({
|
||||
type: 'module',
|
||||
longname: v.longname,
|
||||
name: v.name,
|
||||
members: find({
|
||||
kind: 'member',
|
||||
memberof: v.longname
|
||||
}),
|
||||
methods: find({
|
||||
kind: 'function',
|
||||
memberof: v.longname
|
||||
}),
|
||||
typedefs: find({
|
||||
kind: 'typedef',
|
||||
memberof: v.longname
|
||||
}),
|
||||
events: find({
|
||||
kind: 'event',
|
||||
memberof: v.longname
|
||||
})
|
||||
});
|
||||
}
|
||||
if (v.kind == 'class') {
|
||||
nav.push({
|
||||
type: 'class',
|
||||
longname: v.longname,
|
||||
name: v.name,
|
||||
members: find({
|
||||
kind: 'member',
|
||||
memberof: v.longname
|
||||
}),
|
||||
methods: find({
|
||||
kind: 'function',
|
||||
memberof: v.longname
|
||||
}),
|
||||
typedefs: find({
|
||||
kind: 'typedef',
|
||||
memberof: v.longname
|
||||
}),
|
||||
fires: v.fires,
|
||||
events: find({
|
||||
kind: 'event',
|
||||
memberof: v.longname
|
||||
})
|
||||
});
|
||||
nav.push(createEntry('module', v));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,13 +12,18 @@ $(function () {
|
||||
var $item = $(v);
|
||||
|
||||
if ($item.data('name') && regexp.test($item.data('name'))) {
|
||||
const container = $item.parent().parent().parent();
|
||||
container.show();
|
||||
container.closest('.itemMembers').show();
|
||||
container.closest('.item').show();
|
||||
$item.show();
|
||||
$item.closest('.itemMembers').show();
|
||||
$item.closest('.item').show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$el.find('.item, .itemMembers').show();
|
||||
$el.find('.item, .itemMembers').hide();
|
||||
$('.navigation>ul>li').show();
|
||||
}
|
||||
|
||||
$el.find('.list').scrollTop(0);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<sup class="variation"><?js= doc.variation ?></sup>
|
||||
<?js } ?></h2>
|
||||
<br>
|
||||
<?js if (doc.stability || doc.kind == 'namespace') {
|
||||
<?js if (doc.stability || doc.kind == 'namespace' || doc.kind == 'module') {
|
||||
var ancestors = doc.ancestors.map(a => a.replace(/>\./g, '>').replace(/\.</g, '<')).join('/');
|
||||
var parts = [];
|
||||
if (ancestors) {
|
||||
@@ -26,8 +26,21 @@
|
||||
}
|
||||
var importPath = parts.join('/');
|
||||
?>
|
||||
<?js
|
||||
var nameParts = doc.name.split('/');
|
||||
var moduleName = nameParts[nameParts.length - 1];
|
||||
if(moduleName) {
|
||||
var firstChar = moduleName.charAt(0);
|
||||
moduleName = firstChar.toUpperCase() + moduleName.slice(1);
|
||||
var isClassModule = firstChar.toUpperCase() === firstChar;
|
||||
}
|
||||
?>
|
||||
<?js if (doc.kind == 'module' && !isClassModule && nameParts.length < 3) {?>
|
||||
<pre class="prettyprint source"><code>import * as ol<?js= moduleName ?> from '<?js= doc.name ?>';</code></pre>
|
||||
<?js } else if(doc.kind !== 'module') { ?>
|
||||
<pre class="prettyprint source"><code>import <?js= doc.name ?> from '<?js= importPath ?>';</code></pre>
|
||||
<?js } ?>
|
||||
<?js } ?>
|
||||
<?js if (doc.classdesc) { ?>
|
||||
<div class="class-description"><?js= doc.classdesc ?></div>
|
||||
<?js } ?>
|
||||
@@ -143,6 +156,7 @@
|
||||
<h3 class="subsection-title">Methods</h3>
|
||||
|
||||
<dl><?js methods.forEach(function(m) { ?>
|
||||
<?js m.parent = doc ?>
|
||||
<?js= self.partial('method.tmpl', m) ?>
|
||||
<?js }); ?></dl>
|
||||
<?js } ?>
|
||||
|
||||
@@ -27,6 +27,10 @@ var self = this;
|
||||
</dt>
|
||||
<dd class="<?js= (data.stability && data.stability !== 'stable') ? 'unstable' : '' ?>">
|
||||
|
||||
<?js if (data.parent && data.parent.kind == 'module' && data.parent.name.split('ol/').length < 3) { ?>
|
||||
<pre class="prettyprint source"><code>import {<?js= data.name ?>} from '<?js= data.parent.name ?>';</code></pre>
|
||||
<?js } ?>
|
||||
|
||||
<?js if (data.description) { ?>
|
||||
<div class="description">
|
||||
<?js= data.description ?>
|
||||
|
||||
@@ -10,11 +10,12 @@ function toShortName(name) {
|
||||
</div>
|
||||
<ul class="list">
|
||||
<?js
|
||||
this.nav.forEach(function (item) {
|
||||
let navbuilder;
|
||||
this.nav.forEach(navbuilder = function (item) {
|
||||
?>
|
||||
<li class="item" data-name="<?js= item.longname ?>">
|
||||
<span class="title">
|
||||
<?js= self.linkto(item.longname, item.longname.replace('module:', '')) ?>
|
||||
<?js= self.linkto(item.longname, item.type === 'module' ? item.longname.replace('module:', '') : item.name) ?>
|
||||
<?js if (item.type === 'namespace' &&
|
||||
(item.members.length + item.typedefs.length + item.methods.length +
|
||||
item.events.length > 0)) { ?>
|
||||
@@ -22,6 +23,18 @@ function toShortName(name) {
|
||||
</span>
|
||||
<ul class="members itemMembers">
|
||||
<?js
|
||||
if (item.classes.length) {
|
||||
?>
|
||||
<span class="subtitle">Classes</span>
|
||||
<?js
|
||||
item.classes.forEach(function (v) {
|
||||
navbuilder(v);
|
||||
});
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
<ul class="members itemMembers">
|
||||
<?js
|
||||
if (item.members.length) {
|
||||
?>
|
||||
<span class="subtitle">Members</span>
|
||||
|
||||
@@ -228,3 +228,11 @@ Missing or invalid `size`.
|
||||
### 61
|
||||
|
||||
Cannot determine IIIF Image API version from provided image information JSON.
|
||||
|
||||
### 62
|
||||
|
||||
A `WebGLArrayBuffer` must either be of type `ELEMENT_ARRAY_BUFFER` or `ARRAY_BUFFER`.
|
||||
|
||||
### 63
|
||||
|
||||
Support for the `OES_element_index_uint` WebGL extension is mandatory for WebGL layers.
|
||||
|
||||
@@ -129,7 +129,7 @@ const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure'
|
||||
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json'
|
||||
})
|
||||
}),
|
||||
new VectorLayer({
|
||||
|
||||
@@ -6,7 +6,7 @@ import VectorTileSource from '../src/ol/source/VectorTile.js';
|
||||
import {Tile as TileLayer, VectorTile as VectorTileLayer} from '../src/ol/layer.js';
|
||||
import Projection from '../src/ol/proj/Projection.js';
|
||||
|
||||
|
||||
// Converts geojson-vt data to GeoJSON
|
||||
const replacer = function(key, value) {
|
||||
if (value.geometry) {
|
||||
let type;
|
||||
@@ -46,11 +46,6 @@ const replacer = function(key, value) {
|
||||
}
|
||||
};
|
||||
|
||||
const tilePixels = new Projection({
|
||||
code: 'TILE_PIXELS',
|
||||
units: 'tile-pixels'
|
||||
});
|
||||
|
||||
const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
@@ -73,23 +68,22 @@ fetch(url).then(function(response) {
|
||||
debug: 1
|
||||
});
|
||||
const vectorSource = new VectorTileSource({
|
||||
format: new GeoJSON(),
|
||||
tileLoadFunction: function(tile) {
|
||||
const format = tile.getFormat();
|
||||
const tileCoord = tile.getTileCoord();
|
||||
format: new GeoJSON({
|
||||
// Data returned from geojson-vt is in tile pixel units
|
||||
dataProjection: new Projection({
|
||||
code: 'TILE_PIXELS',
|
||||
units: 'tile-pixels',
|
||||
extent: [0, 0, 4096, 4096]
|
||||
})
|
||||
}),
|
||||
tileUrlFunction: function(tileCoord) {
|
||||
const data = tileIndex.getTile(tileCoord[0], tileCoord[1], tileCoord[2]);
|
||||
|
||||
const features = format.readFeatures(
|
||||
JSON.stringify({
|
||||
const geojson = JSON.stringify({
|
||||
type: 'FeatureCollection',
|
||||
features: data ? data.features : []
|
||||
}, replacer));
|
||||
tile.setLoader(function() {
|
||||
tile.setFeatures(features);
|
||||
tile.setProjection(tilePixels);
|
||||
});
|
||||
},
|
||||
url: 'data:' // arbitrary url, we don't use it in the tileLoadFunction
|
||||
}, replacer);
|
||||
return 'data:application/json;charset=UTF-8,' + geojson;
|
||||
}
|
||||
});
|
||||
const vectorLayer = new VectorTileLayer({
|
||||
source: vectorSource
|
||||
|
||||
@@ -56,7 +56,7 @@ const vectorLayer = new VectorLayer({
|
||||
|
||||
const rasterLayer = new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
|
||||
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json',
|
||||
crossOrigin: ''
|
||||
})
|
||||
});
|
||||
|
||||
@@ -13,5 +13,8 @@ docs: >
|
||||
|
||||
The dataset contains around 80k points and can be found here: https://www.kaggle.com/NUFORC/ufo-sightings
|
||||
tags: "webgl, icon, sprite, point, ufo"
|
||||
cloak:
|
||||
- key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg
|
||||
value: Your Mapbox access token from https://mapbox.com/ here
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -10,6 +10,8 @@ import {fromLonLat} from '../src/ol/proj.js';
|
||||
import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer.js';
|
||||
import {lerp} from '../src/ol/math.js';
|
||||
|
||||
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
|
||||
|
||||
const vectorSource = new Vector({
|
||||
features: [],
|
||||
attributions: 'National UFO Reporting Center'
|
||||
@@ -105,7 +107,7 @@ new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v3/mapbox.world-dark.json?secure',
|
||||
url: 'https://api.tiles.mapbox.com/v4/mapbox.world-dark.json?access_token=' + key,
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
}),
|
||||
|
||||
@@ -37,7 +37,7 @@ const vectorLayer = new VectorLayer({
|
||||
|
||||
const rasterLayer = new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
|
||||
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json',
|
||||
crossOrigin: ''
|
||||
})
|
||||
});
|
||||
|
||||
@@ -3,13 +3,13 @@ layout: example.html
|
||||
title: Mapbox-gl Layer
|
||||
shortdesc: Example of a Mapbox-gl-js layer integration.
|
||||
docs: >
|
||||
Show how to add a mapbox-gl-js layer in an openlayers map. **Note**: Make sure to get your own Mapbox API key when using this example. No map will be visible when the API key has expired.
|
||||
tags: "simple, mapbox, vector, tiles"
|
||||
Show how to add a mapbox-gl-js layer in an openlayers map. **Note**: Make sure to get your own API key at https://www.maptiler.com/cloud/ when using this example. No map will be visible when the API key has expired.
|
||||
tags: "simple, mapbox, vector, tiles, maptiler"
|
||||
resources:
|
||||
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.js
|
||||
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.css
|
||||
cloak:
|
||||
- key: ER67WIiPdCQvhgsUjoWK
|
||||
value: Your Mapbox access token from https://mapbox.com/ here
|
||||
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -11,7 +11,7 @@ const center = [-98.8, 37.9];
|
||||
const key = 'ER67WIiPdCQvhgsUjoWK';
|
||||
|
||||
const mbMap = new mapboxgl.Map({
|
||||
style: 'https://maps.tilehosting.com/styles/bright/style.json?key=' + key,
|
||||
style: 'https://api.maptiler.com/maps/bright/style.json?key=' + key,
|
||||
attributionControl: false,
|
||||
boxZoom: false,
|
||||
center: center,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
layout: example-verbatim.html
|
||||
title: Vector tiles created from a Mapbox Style object
|
||||
shortdesc: Example of using ol-mapbox-style with tiles from tilehosting.com.
|
||||
tags: "vector tiles, mapbox style, ol-mapbox-style"
|
||||
tags: "vector tiles, mapbox style, ol-mapbox-style, maptiler"
|
||||
cloak:
|
||||
- key: lirfd6Fegsjkvs0lshxe
|
||||
value: Your API key from http://tilehosting.com/ here
|
||||
- key: ER67WIiPdCQvhgsUjoWK
|
||||
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import apply from 'ol-mapbox-style';
|
||||
|
||||
apply('map', 'https://maps.tilehosting.com/styles/topo/style.json?key=ER67WIiPdCQvhgsUjoWK');
|
||||
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=ER67WIiPdCQvhgsUjoWK');
|
||||
|
||||
@@ -7,6 +7,9 @@ docs: >
|
||||
Click on the map to get a popup. The popup is composed of a few basic elements: a container, a close button, and a place for the content. To anchor the popup to the map, an <code>ol/Overlay</code> is created with the popup container. A listener is registered for the map's <code>click</code> event to display the popup, and another listener is set as the <code>click</code> handler for the close button to hide the popup.
|
||||
</p>
|
||||
tags: "overlay, popup"
|
||||
cloak:
|
||||
- key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg
|
||||
value: Your Mapbox access token from https://mapbox.com/ here
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<div id="popup" class="ol-popup">
|
||||
|
||||
@@ -6,6 +6,7 @@ import TileLayer from '../src/ol/layer/Tile.js';
|
||||
import {toLonLat} from '../src/ol/proj.js';
|
||||
import TileJSON from '../src/ol/source/TileJSON.js';
|
||||
|
||||
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
|
||||
|
||||
/**
|
||||
* Elements that make up the popup.
|
||||
@@ -45,7 +46,7 @@ const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v3/mapbox.natural-earth-hypso-bathy.json?secure',
|
||||
url: 'https://api.tiles.mapbox.com/v4/mapbox.natural-earth-hypso-bathy.json?access_token=' + key,
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
|
||||
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json',
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ title: UTFGrid
|
||||
shortdesc: This example shows how to read data from a UTFGrid source.
|
||||
docs: >
|
||||
<p>Point to a country to see its name and flag.</p>
|
||||
Tiles made with [TileMill](http://tilemill.com). Hosting on MapBox.com or with open-source [TileServer](https://github.com/klokantech/tileserver-php/).
|
||||
Tiles made with <a href="http://tilemill.com">TileMill</a>. Hosting on <a href="mapbox.com">mapbox.com</a> or with open-source <a href="https://github.com/klokantech/tileserver-php/">TileServer</a>.
|
||||
tags: "utfgrid, tilejson"
|
||||
cloak:
|
||||
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
|
||||
|
||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ol",
|
||||
"version": "6.0.0-beta.9",
|
||||
"version": "6.0.0-beta.10",
|
||||
"description": "OpenLayers mapping library",
|
||||
"keywords": [
|
||||
"map",
|
||||
@@ -54,8 +54,8 @@
|
||||
"chaikin-smooth": "^1.0.4",
|
||||
"clean-css-cli": "4.3.0",
|
||||
"copy-webpack-plugin": "^5.0.3",
|
||||
"coveralls": "3.0.3",
|
||||
"eslint": "^5.16.0",
|
||||
"coveralls": "3.0.4",
|
||||
"eslint": "^6.0.0",
|
||||
"eslint-config-openlayers": "^12.0.0",
|
||||
"expect.js": "0.3.1",
|
||||
"front-matter": "^3.0.2",
|
||||
@@ -81,10 +81,10 @@
|
||||
"marked": "0.6.2",
|
||||
"mocha": "6.1.4",
|
||||
"ol-mapbox-style": "^5.0.0-beta.2",
|
||||
"pixelmatch": "^4.0.2",
|
||||
"pixelmatch": "^5.0.0",
|
||||
"pngjs": "^3.4.0",
|
||||
"proj4": "2.5.0",
|
||||
"puppeteer": "~1.17.0",
|
||||
"puppeteer": "~1.18.0",
|
||||
"rollup": "^1.12.0",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-commonjs": "^10.0.0",
|
||||
@@ -97,7 +97,7 @@
|
||||
"typescript": "^3.4.5",
|
||||
"url-polyfill": "^1.1.5",
|
||||
"walk": "^2.3.9",
|
||||
"webpack": "4.32.2",
|
||||
"webpack": "4.35.0",
|
||||
"webpack-cli": "^3.3.2",
|
||||
"webpack-dev-middleware": "^3.6.2",
|
||||
"webpack-dev-server": "^3.3.1",
|
||||
|
||||
@@ -22,5 +22,16 @@ module.exports = {
|
||||
context: __dirname,
|
||||
target: 'web',
|
||||
entry: entry,
|
||||
devtool: 'source-map'
|
||||
devtool: 'source-map',
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js$/,
|
||||
use: {
|
||||
loader: path.join(__dirname, '../examples/webpack/worker-loader.js')
|
||||
},
|
||||
include: [
|
||||
path.join(__dirname, '../src/ol/worker')
|
||||
]
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import ImageState from './ImageState.js';
|
||||
import {listenOnce, unlistenByKey} from './events.js';
|
||||
import EventType from './events/EventType.js';
|
||||
import {getHeight} from './extent.js';
|
||||
import {IMAGE_DECODE} from './has.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -157,7 +158,8 @@ class ImageWrapper extends ImageBase {
|
||||
*/
|
||||
export function listenImage(image, loadHandler, errorHandler) {
|
||||
const img = /** @type {HTMLImageElement} */ (image);
|
||||
if (img.decode) {
|
||||
|
||||
if (IMAGE_DECODE) {
|
||||
const promise = img.decode();
|
||||
let listening = true;
|
||||
const unlisten = function() {
|
||||
@@ -169,8 +171,14 @@ export function listenImage(image, loadHandler, errorHandler) {
|
||||
}
|
||||
}).catch(function(error) {
|
||||
if (listening) {
|
||||
// FIXME: Unconditionally call errorHandler() when this bug is fixed upstream:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=198527
|
||||
if (error.name === 'EncodingError' && error.message === 'Invalid image type.') {
|
||||
loadHandler();
|
||||
} else {
|
||||
errorHandler();
|
||||
}
|
||||
}
|
||||
});
|
||||
return unlisten;
|
||||
}
|
||||
|
||||
@@ -260,12 +260,6 @@ class MapBrowserEventHandler extends EventTarget {
|
||||
this.dragging_);
|
||||
this.dispatchEvent(newEvent);
|
||||
}
|
||||
|
||||
// Some native android browser triggers mousemove events during small period
|
||||
// of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
|
||||
// https://code.google.com/p/android/issues/detail?id=19827
|
||||
// ex: Galaxy Tab P3110 + Android 4.1.1
|
||||
pointerEvent.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ import {listen, unlistenByKey, unlisten} from './events.js';
|
||||
import EventType from './events/EventType.js';
|
||||
import {createEmpty, clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js';
|
||||
import {TRUE} from './functions.js';
|
||||
import {DEVICE_PIXEL_RATIO, TOUCH} from './has.js';
|
||||
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE} from './has.js';
|
||||
import LayerGroup from './layer/Group.js';
|
||||
import {hasArea} from './size.js';
|
||||
import {DROP} from './structs/PriorityQueue.js';
|
||||
@@ -230,7 +230,7 @@ class PluggableMap extends BaseObject {
|
||||
* @type {!HTMLElement}
|
||||
*/
|
||||
this.viewport_ = document.createElement('div');
|
||||
this.viewport_.className = 'ol-viewport' + (TOUCH ? ' ol-touch' : '');
|
||||
this.viewport_.className = 'ol-viewport' + ('ontouchstart' in window ? ' ol-touch' : '');
|
||||
this.viewport_.style.position = 'relative';
|
||||
this.viewport_.style.overflow = 'hidden';
|
||||
this.viewport_.style.width = '100%';
|
||||
@@ -301,6 +301,11 @@ class PluggableMap extends BaseObject {
|
||||
*/
|
||||
this.interactions = optionsInternal.interactions || new Collection();
|
||||
|
||||
/**
|
||||
* @type {import("./events/Target.js").default}
|
||||
*/
|
||||
this.labelCache_ = null;
|
||||
|
||||
/**
|
||||
* @type {import("./events.js").EventsKey}
|
||||
*/
|
||||
@@ -323,7 +328,7 @@ class PluggableMap extends BaseObject {
|
||||
* @type {import("./renderer/Map.js").default}
|
||||
* @private
|
||||
*/
|
||||
this.renderer_ = this.createRenderer();
|
||||
this.renderer_ = null;
|
||||
|
||||
/**
|
||||
* @type {function(Event): void|undefined}
|
||||
@@ -511,24 +516,6 @@ class PluggableMap extends BaseObject {
|
||||
overlay.setMap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a label cache for listening to font changes.
|
||||
* @param {import("./events/Target.js").default} labelCache Label cache.
|
||||
*/
|
||||
attachLabelCache(labelCache) {
|
||||
this.detachLabelCache();
|
||||
this.labelCacheListenerKey_ = listen(labelCache, EventType.CLEAR, this.redrawText.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach the label cache, i.e. no longer listen to font changes.
|
||||
*/
|
||||
detachLabelCache() {
|
||||
if (this.labelCacheListenerKey_) {
|
||||
unlistenByKey(this.labelCacheListenerKey_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @inheritDoc
|
||||
@@ -542,11 +529,6 @@ class PluggableMap extends BaseObject {
|
||||
removeEventListener(EventType.RESIZE, this.handleResize_, false);
|
||||
this.handleResize_ = undefined;
|
||||
}
|
||||
if (this.animationDelayKey_) {
|
||||
cancelAnimationFrame(this.animationDelayKey_);
|
||||
this.animationDelayKey_ = undefined;
|
||||
}
|
||||
this.detachLabelCache();
|
||||
this.setTarget(null);
|
||||
super.disposeInternal();
|
||||
}
|
||||
@@ -985,7 +967,7 @@ class PluggableMap extends BaseObject {
|
||||
if (frameState) {
|
||||
const hints = frameState.viewHints;
|
||||
if (hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]) {
|
||||
const lowOnFrameBudget = Date.now() - frameState.time > 8;
|
||||
const lowOnFrameBudget = !IMAGE_DECODE && Date.now() - frameState.time > 8;
|
||||
maxTotalLoading = lowOnFrameBudget ? 0 : 8;
|
||||
maxNewLoads = lowOnFrameBudget ? 0 : 2;
|
||||
}
|
||||
@@ -1041,6 +1023,14 @@ class PluggableMap extends BaseObject {
|
||||
}
|
||||
|
||||
if (!targetElement) {
|
||||
if (this.renderer_) {
|
||||
this.renderer_.dispose();
|
||||
this.renderer_ = null;
|
||||
}
|
||||
if (this.animationDelayKey_) {
|
||||
cancelAnimationFrame(this.animationDelayKey_);
|
||||
this.animationDelayKey_ = undefined;
|
||||
}
|
||||
removeNode(this.viewport_);
|
||||
if (this.handleResize_ !== undefined) {
|
||||
removeEventListener(EventType.RESIZE, this.handleResize_, false);
|
||||
@@ -1048,6 +1038,9 @@ class PluggableMap extends BaseObject {
|
||||
}
|
||||
} else {
|
||||
targetElement.appendChild(this.viewport_);
|
||||
if (!this.renderer_) {
|
||||
this.renderer_ = this.createRenderer();
|
||||
}
|
||||
|
||||
const keyboardEventTarget = !this.keyboardEventTarget_ ?
|
||||
targetElement : this.keyboardEventTarget_;
|
||||
@@ -1166,7 +1159,7 @@ class PluggableMap extends BaseObject {
|
||||
* @api
|
||||
*/
|
||||
render() {
|
||||
if (this.animationDelayKey_ === undefined) {
|
||||
if (this.renderer_ && this.animationDelayKey_ === undefined) {
|
||||
this.animationDelayKey_ = requestAnimationFrame(this.animationDelay_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ class ControlledMap extends PluggableMap {
|
||||
* @property {boolean} [collapsible=true] Whether the control can be collapsed or not.
|
||||
* @property {string|HTMLElement} [label='»'] Text label to use for the collapsed
|
||||
* overviewmap button. Instead of text, also an element (e.g. a `span` element) can be used.
|
||||
* @property {Array<import("../layer/Layer.js").default>|import("../Collection.js").default<import("../layer/Layer.js").default>} layers
|
||||
* Layers for the overview map (mandatory).
|
||||
* @property {Array<import("../layer/Layer.js").default>|import("../Collection.js").default<import("../layer/Layer.js").default>} [layers]
|
||||
* Layers for the overview map.
|
||||
* @property {function(import("../MapEvent.js").default)} [render] Function called when the control
|
||||
* should be re-rendered. This is called in a `requestAnimationFrame` callback.
|
||||
* @property {HTMLElement|string} [target] Specify a target if you want the control
|
||||
@@ -159,13 +159,9 @@ class OverviewMap extends Control {
|
||||
const ovmap = this.ovmap_;
|
||||
|
||||
if (options.layers) {
|
||||
/** @type {Array<import("../layer/Layer.js").default>} */ (options.layers).forEach(
|
||||
/**
|
||||
* @param {import("../layer/Layer.js").default} layer Layer.
|
||||
*/
|
||||
(function(layer) {
|
||||
options.layers.forEach(function(layer) {
|
||||
ovmap.addLayer(layer);
|
||||
}).bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
const box = document.createElement('div');
|
||||
|
||||
@@ -13,8 +13,10 @@ import {get as getProjection, equivalent as equivalentProjection, transformExten
|
||||
* the `dataProjection` of the format is assigned (where set). If the projection
|
||||
* can not be derived from the data and if no `dataProjection` is set for a format,
|
||||
* the features will not be reprojected.
|
||||
* @property {import("../extent.js").Extent} [extent] Tile extent of the tile being read. This is only used and
|
||||
* required for {@link module:ol/format/MVT}.
|
||||
* @property {import("../extent.js").Extent} [extent] Tile extent in map units of the tile being read.
|
||||
* This is only required when reading data with tile pixels as geometry units. When configured,
|
||||
* a `dataProjection` with `TILE_PIXELS` as `units` and the tile's pixel extent as `extent` needs to be
|
||||
* provided.
|
||||
* @property {import("../proj.js").ProjectionLike} [featureProjection] Projection of the feature geometries
|
||||
* created by the format reader. If not provided, features will be returned in the
|
||||
* `dataProjection`.
|
||||
@@ -86,9 +88,14 @@ class FeatureFormat {
|
||||
getReadOptions(source, opt_options) {
|
||||
let options;
|
||||
if (opt_options) {
|
||||
let dataProjection = opt_options.dataProjection ?
|
||||
opt_options.dataProjection : this.readProjection(source);
|
||||
if (opt_options.extent) {
|
||||
dataProjection = getProjection(dataProjection);
|
||||
dataProjection.setWorldExtent(opt_options.extent);
|
||||
}
|
||||
options = {
|
||||
dataProjection: opt_options.dataProjection ?
|
||||
opt_options.dataProjection : this.readProjection(source),
|
||||
dataProjection: dataProjection,
|
||||
featureProjection: opt_options.featureProjection
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,30 +39,8 @@ export const MAC = ua.indexOf('macintosh') !== -1;
|
||||
*/
|
||||
export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
|
||||
|
||||
|
||||
/**
|
||||
* True if browser supports touch events.
|
||||
* @const
|
||||
* @type {boolean}
|
||||
* @api
|
||||
*/
|
||||
export const TOUCH = 'ontouchstart' in window;
|
||||
|
||||
|
||||
/**
|
||||
* True if browser supports pointer events.
|
||||
* @const
|
||||
* Image.prototype.decode() is supported.
|
||||
* @type {boolean}
|
||||
*/
|
||||
export const POINTER = 'PointerEvent' in window;
|
||||
|
||||
|
||||
/**
|
||||
* True if browser supports ms pointer events (IE 10).
|
||||
* @const
|
||||
* @type {boolean}
|
||||
*/
|
||||
export const MSPOINTER = !!(navigator.msPointerEnabled);
|
||||
|
||||
|
||||
export {HAS as WEBGL} from './webgl.js';
|
||||
export const IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode;
|
||||
|
||||
@@ -167,7 +167,7 @@ class DragPan extends PointerInteraction {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ol.MapBrowserEvent} mapBrowserEvent Browser event.
|
||||
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Browser event.
|
||||
* @return {boolean} Combined condition result.
|
||||
*/
|
||||
function defaultCondition(mapBrowserEvent) {
|
||||
|
||||
@@ -35,6 +35,13 @@ const TranslateEventType = {
|
||||
TRANSLATEEND: 'translateend'
|
||||
};
|
||||
|
||||
/**
|
||||
* A function that takes an {@link module:ol/Feature} or
|
||||
* {@link module:ol/render/Feature} and an
|
||||
* {@link module:ol/layer/Layer} and returns `true` if the feature may be
|
||||
* translated or `false` otherwise.
|
||||
* @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default):boolean} FilterFunction
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
@@ -45,6 +52,10 @@ const TranslateEventType = {
|
||||
* function will be called for each layer in the map and should return
|
||||
* `true` for layers that you want to be translatable. If the option is
|
||||
* absent, all visible layers will be considered translatable.
|
||||
* @property {FilterFunction} [filter] A function
|
||||
* that takes an {@link module:ol/Feature} and an
|
||||
* {@link module:ol/layer/Layer} and returns `true` if the feature may be
|
||||
* translated or `false` otherwise.
|
||||
* @property {number} [hitTolerance=0] Hit-detection tolerance. Pixels inside the radius around the given position
|
||||
* will be checked for features.
|
||||
*/
|
||||
@@ -136,6 +147,12 @@ class Translate extends PointerInteraction {
|
||||
*/
|
||||
this.layerFilter_ = layerFilter;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {FilterFunction}
|
||||
*/
|
||||
this.filter_ = options.filter ? options.filter : TRUE;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
@@ -245,10 +262,12 @@ class Translate extends PointerInteraction {
|
||||
*/
|
||||
featuresAtPixel_(pixel, map) {
|
||||
return map.forEachFeatureAtPixel(pixel,
|
||||
function(feature) {
|
||||
function(feature, layer) {
|
||||
if (this.filter_(feature, layer)) {
|
||||
if (!this.features_ || includes(this.features_.getArray(), feature)) {
|
||||
return feature;
|
||||
}
|
||||
}
|
||||
}.bind(this), {
|
||||
layerFilter: this.layerFilter_,
|
||||
hitTolerance: this.hitTolerance_
|
||||
|
||||
@@ -93,7 +93,8 @@ class BaseLayer extends BaseObject {
|
||||
/** @type {import("./Layer.js").State} */
|
||||
const state = this.state_ || /** @type {?} */ ({
|
||||
layer: this,
|
||||
managed: opt_managed === undefined ? true : opt_managed
|
||||
managed: opt_managed === undefined ? true : opt_managed,
|
||||
hasOverlay: false
|
||||
});
|
||||
state.opacity = clamp(Math.round(this.getOpacity() * 100) / 100, 0, 1);
|
||||
state.sourceState = this.getSourceState();
|
||||
|
||||
@@ -72,8 +72,7 @@ class BaseVectorLayer extends Layer {
|
||||
* @param {Options=} opt_options Options.
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
const options = opt_options ?
|
||||
opt_options : /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
const baseOptions = assign({}, options);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ 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
|
||||
|
||||
@@ -5,11 +5,6 @@ import BaseTileLayer from './BaseTile.js';
|
||||
import CanvasTileLayerRenderer from '../renderer/canvas/TileLayer.js';
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {import("./BaseTile.js").Options} Options
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* For layer sources that provide pre-rendered, tiled images in grids that are
|
||||
@@ -23,7 +18,7 @@ import CanvasTileLayerRenderer from '../renderer/canvas/TileLayer.js';
|
||||
class TileLayer extends BaseTileLayer {
|
||||
|
||||
/**
|
||||
* @param {Options=} opt_options Tile layer options.
|
||||
* @param {import("./BaseTile.js").Options=} opt_options Tile layer options.
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
super(opt_options);
|
||||
|
||||
@@ -5,11 +5,6 @@ import BaseVectorLayer from './BaseVector.js';
|
||||
import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js';
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {import("./BaseVector.js").Options} Options
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Vector data that is rendered client-side.
|
||||
@@ -22,7 +17,7 @@ import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js';
|
||||
*/
|
||||
class VectorLayer extends BaseVectorLayer {
|
||||
/**
|
||||
* @param {Options=} opt_options Options.
|
||||
* @param {import("./BaseVector.js").Options=} opt_options Options.
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
super(opt_options);
|
||||
|
||||
@@ -60,7 +60,7 @@ class VectorImageLayer extends BaseVectorLayer {
|
||||
* @param {Options=} opt_options Options.
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
const options = opt_options ? opt_options : /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
const baseOptions = assign({}, options);
|
||||
delete baseOptions.imageRatio;
|
||||
|
||||
@@ -86,7 +86,7 @@ class VectorTileLayer extends BaseVectorLayer {
|
||||
delete baseOptions.preload;
|
||||
delete baseOptions.useInterimTilesOnError;
|
||||
|
||||
super(/** @type {import("./Vector.js").Options} */ (baseOptions));
|
||||
super(/** @type {import("./BaseVector.js").Options} */ (baseOptions));
|
||||
|
||||
const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
|
||||
assert(renderMode == undefined ||
|
||||
|
||||
@@ -63,10 +63,6 @@
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.ol-overlay-container {
|
||||
will-change: left,right,top,bottom;
|
||||
}
|
||||
|
||||
.ol-unsupported {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
import {listen, unlisten} from '../events.js';
|
||||
import EventTarget from '../events/Target.js';
|
||||
import {POINTER, MSPOINTER, TOUCH} from '../has.js';
|
||||
import PointerEventType from './EventType.js';
|
||||
import MouseSource, {prepareEvent as prepareMouseEvent} from './MouseSource.js';
|
||||
import MsSource from './MsSource.js';
|
||||
@@ -124,15 +123,15 @@ class PointerEventHandler extends EventTarget {
|
||||
* that generate pointer events.
|
||||
*/
|
||||
registerSources() {
|
||||
if (POINTER) {
|
||||
if ('PointerEvent' in window) {
|
||||
this.registerSource('native', new NativeSource(this));
|
||||
} else if (MSPOINTER) {
|
||||
} else if (window.navigator.msPointerEnabled) {
|
||||
this.registerSource('ms', new MsSource(this));
|
||||
} else {
|
||||
const mouseSource = new MouseSource(this);
|
||||
this.registerSource('mouse', mouseSource);
|
||||
|
||||
if (TOUCH) {
|
||||
if ('ontouchstart' in window) {
|
||||
this.registerSource('touch', new TouchSource(this, mouseSource));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ import {toEPSG4326, fromEPSG4326, PROJECTIONS as EPSG3857_PROJECTIONS} from './p
|
||||
import {PROJECTIONS as EPSG4326_PROJECTIONS} from './proj/epsg4326.js';
|
||||
import Projection from './proj/Projection.js';
|
||||
import Units, {METERS_PER_UNIT} from './proj/Units.js';
|
||||
import * as projections from './proj/projections.js';
|
||||
import {add as addTransformFunc, clear as clearTransformFuncs, get as getTransformFunc} from './proj/transforms.js';
|
||||
import {add as addProj, clear as clearProj, get as getProj} from './proj/projections.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -133,7 +133,7 @@ export function identityTransform(input, opt_output, opt_dimension) {
|
||||
* @api
|
||||
*/
|
||||
export function addProjection(projection) {
|
||||
projections.add(projection.getCode(), projection);
|
||||
addProj(projection.getCode(), projection);
|
||||
addTransformFunc(projection, projection, cloneTransform);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ export function addProjections(projections) {
|
||||
*/
|
||||
export function get(projectionLike) {
|
||||
return typeof projectionLike === 'string' ?
|
||||
projections.get(/** @type {string} */ (projectionLike)) :
|
||||
getProj(/** @type {string} */ (projectionLike)) :
|
||||
(/** @type {Projection} */ (projectionLike) || null);
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ export function addEquivalentTransforms(projections1, projections2, forwardTrans
|
||||
* Clear all cached projections and transforms.
|
||||
*/
|
||||
export function clearAllProjections() {
|
||||
projections.clear();
|
||||
clearProj();
|
||||
clearTransformFuncs();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,8 @@ class RenderEvent extends Event {
|
||||
* CSS pixels to rendered pixels.
|
||||
* @param {import("../PluggableMap.js").FrameState=} opt_frameState Frame state.
|
||||
* @param {?CanvasRenderingContext2D=} opt_context Context.
|
||||
* @param {?import("../webgl/Helper.js").default=} opt_glContext WebGL Context.
|
||||
*/
|
||||
constructor(type, opt_inversePixelTransform, opt_frameState, opt_context, opt_glContext) {
|
||||
constructor(type, opt_inversePixelTransform, opt_frameState, opt_context) {
|
||||
|
||||
super(type);
|
||||
|
||||
@@ -41,14 +40,6 @@ class RenderEvent extends Event {
|
||||
*/
|
||||
this.context = opt_context;
|
||||
|
||||
/**
|
||||
* WebGL context. Only available when a WebGL renderer is used, null
|
||||
* otherwise.
|
||||
* @type {import("../webgl/Helper.js").default|null|undefined}
|
||||
* @api
|
||||
*/
|
||||
this.glContext = opt_glContext;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import MapRenderer from './Map.js';
|
||||
import SourceState from '../source/State.js';
|
||||
import {replaceChildren} from '../dom.js';
|
||||
import {labelCache} from '../render/canvas.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import {listen, unlistenByKey} from '../events.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -23,7 +25,11 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
*/
|
||||
constructor(map) {
|
||||
super(map);
|
||||
map.attachLabelCache(labelCache);
|
||||
|
||||
/**
|
||||
* @type {import("../events.js").EventsKey}
|
||||
*/
|
||||
this.labelCacheKey_ = listen(labelCache, EventType.CLEAR, map.redrawText.bind(map));
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -66,6 +72,11 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
disposeInternal() {
|
||||
unlistenByKey(this.labelCacheKey_);
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -87,9 +98,11 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
const viewResolution = frameState.viewState.resolution;
|
||||
|
||||
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 (!visibleAtResolution(layerState, viewResolution) ||
|
||||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
|
||||
@@ -98,8 +111,15 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
|
||||
const layer = layerState.layer;
|
||||
const element = layer.render(frameState, previousElement);
|
||||
if (!element) {
|
||||
continue;
|
||||
}
|
||||
if ((element !== previousElement || i == ii - 1) && element.childElementCount === 2 && !hasOverlay) {
|
||||
element.removeChild(element.lastElementChild);
|
||||
}
|
||||
if (element !== previousElement) {
|
||||
this.children_.push(element);
|
||||
hasOverlay = false;
|
||||
previousElement = element;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
}
|
||||
|
||||
// set forward and inverse pixel transforms
|
||||
composeTransform(this.pixelTransform_,
|
||||
composeTransform(this.pixelTransform,
|
||||
frameState.size[0] / 2, frameState.size[1] / 2,
|
||||
1 / pixelRatio, 1 / pixelRatio,
|
||||
rotation,
|
||||
-width / 2, -height / 2
|
||||
);
|
||||
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||
makeInverse(this.inversePixelTransform, this.pixelTransform);
|
||||
|
||||
this.useContainer(target, this.pixelTransform_, layerState.opacity);
|
||||
this.useContainer(target, this.pixelTransform, layerState.opacity);
|
||||
|
||||
const context = this.context;
|
||||
const canvas = context.canvas;
|
||||
@@ -160,7 +160,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
context.restore();
|
||||
}
|
||||
|
||||
const canvasTransform = transformToString(this.pixelTransform_);
|
||||
const canvasTransform = transformToString(this.pixelTransform);
|
||||
if (canvasTransform !== canvas.style.transform) {
|
||||
canvas.style.transform = canvasTransform;
|
||||
}
|
||||
|
||||
@@ -44,18 +44,18 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
/**
|
||||
* The transform for rendered pixels to viewport CSS pixels. This transform must
|
||||
* be set when rendering a frame and may be used by other functions after rendering.
|
||||
* @private
|
||||
* @protected
|
||||
* @type {import("../../transform.js").Transform}
|
||||
*/
|
||||
this.pixelTransform_ = createTransform();
|
||||
this.pixelTransform = createTransform();
|
||||
|
||||
/**
|
||||
* The transform for viewport CSS pixels to rendered pixels. This transform must
|
||||
* be set when rendering a frame and may be used by other functions after rendering.
|
||||
* @private
|
||||
* @protected
|
||||
* @type {import("../../transform.js").Transform}
|
||||
*/
|
||||
this.inversePixelTransform_ = createTransform();
|
||||
this.inversePixelTransform = createTransform();
|
||||
|
||||
/**
|
||||
* @protected
|
||||
@@ -163,7 +163,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
applyTransform(frameState.coordinateToPixelTransform, bottomRight);
|
||||
applyTransform(frameState.coordinateToPixelTransform, bottomLeft);
|
||||
|
||||
const inverted = this.inversePixelTransform_;
|
||||
const inverted = this.inversePixelTransform;
|
||||
applyTransform(inverted, topLeft);
|
||||
applyTransform(inverted, topRight);
|
||||
applyTransform(inverted, bottomRight);
|
||||
@@ -187,7 +187,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
dispatchRenderEvent_(type, context, frameState) {
|
||||
const layer = this.getLayer();
|
||||
if (layer.hasListener(type)) {
|
||||
const event = new RenderEvent(type, this.inversePixelTransform_, frameState, context, null);
|
||||
const event = new RenderEvent(type, this.inversePixelTransform, frameState, context);
|
||||
layer.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
* returned, and empty array will be returned.
|
||||
*/
|
||||
getDataAtPixel(pixel, frameState, hitTolerance) {
|
||||
const renderPixel = applyTransform(this.inversePixelTransform_, pixel.slice());
|
||||
const renderPixel = applyTransform(this.inversePixelTransform, pixel.slice());
|
||||
const context = this.context;
|
||||
|
||||
let data;
|
||||
|
||||
@@ -7,6 +7,7 @@ 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 {numberSafeCompareFunction} from '../../array.js';
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -227,18 +228,18 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
const canvasScale = tileResolution / viewResolution;
|
||||
|
||||
// set forward and inverse pixel transforms
|
||||
composeTransform(this.pixelTransform_,
|
||||
composeTransform(this.pixelTransform,
|
||||
frameState.size[0] / 2, frameState.size[1] / 2,
|
||||
1 / tilePixelRatio, 1 / tilePixelRatio,
|
||||
rotation,
|
||||
-width / 2, -height / 2
|
||||
);
|
||||
|
||||
this.useContainer(target, this.pixelTransform_, layerState.opacity);
|
||||
this.useContainer(target, this.pixelTransform, layerState.opacity);
|
||||
const context = this.context;
|
||||
const canvas = context.canvas;
|
||||
|
||||
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||
makeInverse(this.inversePixelTransform, this.pixelTransform);
|
||||
|
||||
// set scale transform for calculating tile positions on the canvas
|
||||
composeTransform(this.tempTransform_,
|
||||
@@ -264,15 +265,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
this.renderedTiles.length = 0;
|
||||
/** @type {Array<number>} */
|
||||
let zs = Object.keys(tilesToDrawByZ).map(Number);
|
||||
zs.sort(function(a, b) {
|
||||
if (a === z) {
|
||||
return 1;
|
||||
} else if (b === z) {
|
||||
return -1;
|
||||
} else {
|
||||
return a > b ? 1 : a < b ? -1 : 0;
|
||||
}
|
||||
});
|
||||
zs.sort(numberSafeCompareFunction);
|
||||
|
||||
let clips, clipZs, currentClip;
|
||||
if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) {
|
||||
@@ -311,7 +304,9 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
const h = nextY - y;
|
||||
const transition = z === currentZ;
|
||||
|
||||
if (clips && (!transition || tile.getAlpha(getUid(this), frameState.time) === 1)) {
|
||||
const inTransition = transition && tile.getAlpha(getUid(this), frameState.time) !== 1;
|
||||
if (!inTransition) {
|
||||
if (clips) {
|
||||
// Clip mask for regions in this tile that already filled by a higher z tile
|
||||
context.save();
|
||||
currentClip = [x, y, x + w, y, x + w, y + h, x, y + h];
|
||||
@@ -334,9 +329,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
}
|
||||
clips.push(currentClip);
|
||||
clipZs.push(currentZ);
|
||||
} else {
|
||||
context.clearRect(x, y, w, h);
|
||||
}
|
||||
}
|
||||
this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity);
|
||||
if (clips) {
|
||||
if (clips && !inTransition) {
|
||||
context.restore();
|
||||
}
|
||||
this.renderedTiles.push(tile);
|
||||
@@ -361,7 +359,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
context.restore();
|
||||
}
|
||||
|
||||
const canvasTransform = transformToString(this.pixelTransform_);
|
||||
const canvasTransform = transformToString(this.pixelTransform);
|
||||
if (canvasTransform !== canvas.style.transform) {
|
||||
canvas.style.transform = canvasTransform;
|
||||
}
|
||||
|
||||
@@ -86,10 +86,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
const layerState = frameState.layerStatesArray[frameState.layerIndex];
|
||||
|
||||
// set forward and inverse pixel transforms
|
||||
makeScale(this.pixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
|
||||
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
|
||||
makeScale(this.pixelTransform, 1 / pixelRatio, 1 / pixelRatio);
|
||||
makeInverse(this.inversePixelTransform, this.pixelTransform);
|
||||
|
||||
this.useContainer(target, this.pixelTransform_, layerState.opacity);
|
||||
this.useContainer(target, this.pixelTransform, layerState.opacity);
|
||||
const context = this.context;
|
||||
const canvas = context.canvas;
|
||||
|
||||
@@ -107,7 +107,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
if (canvas.width != width || canvas.height != height) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const canvasTransform = transformToString(this.pixelTransform_);
|
||||
const canvasTransform = transformToString(this.pixelTransform);
|
||||
if (canvas.style.transform !== canvasTransform) {
|
||||
canvas.style.transform = canvasTransform;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
*/
|
||||
this.overlayContext_ = null;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
this.overlayContextUid_;
|
||||
|
||||
/**
|
||||
* The transform for rendered pixels to viewport CSS pixels for the overlay canvas.
|
||||
* @private
|
||||
@@ -124,11 +129,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
}
|
||||
const containerReused = this.containerReused;
|
||||
super.useContainer(target, transform, opacity);
|
||||
if (containerReused && !this.containerReused && !overlayContext) {
|
||||
this.overlayContext_ = null;
|
||||
}
|
||||
if (this.containerReused && overlayContext) {
|
||||
this.overlayContext_ = overlayContext;
|
||||
if (containerReused) {
|
||||
this.overlayContext_ = overlayContext || null;
|
||||
this.overlayContextUid_ = overlayContext ? getUid(overlayContext) : undefined;
|
||||
}
|
||||
if (!this.overlayContext_) {
|
||||
const overlayContext = createCanvasContext2D();
|
||||
@@ -136,6 +139,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
style.position = 'absolute';
|
||||
style.transformOrigin = 'top left';
|
||||
this.overlayContext_ = overlayContext;
|
||||
this.overlayContextUid_ = getUid(overlayContext);
|
||||
}
|
||||
if (this.container.childElementCount === 1) {
|
||||
this.container.appendChild(this.overlayContext_.canvas);
|
||||
@@ -219,6 +223,8 @@ 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;
|
||||
@@ -407,7 +413,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
// Unqueue tiles from the image queue when we don't need any more
|
||||
const usedTiles = frameState.usedTiles[getUid(source)];
|
||||
for (const tileUid in this.renderTileImageQueue_) {
|
||||
if (!(tileUid in usedTiles)) {
|
||||
if (!usedTiles || !(tileUid in usedTiles)) {
|
||||
delete this.renderTileImageQueue_[tileUid];
|
||||
}
|
||||
}
|
||||
@@ -434,7 +440,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
if (canvas.style.transform !== canvasTransform) {
|
||||
canvas.style.transform = canvasTransform;
|
||||
}
|
||||
} else if (!this.containerReused) {
|
||||
} else if (getUid(context) === this.overlayContextUid_) {
|
||||
context.clearRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,26 @@ import LayerRenderer from '../Layer.js';
|
||||
import WebGLHelper from '../../webgl/Helper.js';
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
export const WebGLWorkerMessageType = {
|
||||
GENERATE_BUFFERS: 'GENERATE_BUFFERS'
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} WebGLWorkerGenerateBuffersMessage
|
||||
* This message will trigger the generation of a vertex and an index buffer based on the given render instructions.
|
||||
* When the buffers are generated, the worked will send a message of the same type to the main thread, with
|
||||
* the generated buffers in it.
|
||||
* Note that any addition properties present in the message *will* be sent back to the main thread.
|
||||
* @property {WebGLWorkerMessageType} type Message type
|
||||
* @property {ArrayBuffer} renderInstructions Render instructions raw binary buffer.
|
||||
* @property {ArrayBuffer} [vertexBuffer] Vertices array raw binary buffer (sent by the worker).
|
||||
* @property {ArrayBuffer} [indexBuffer] Indices array raw binary buffer (sent by the worker).
|
||||
* @property {number} [customAttributesCount] Amount of custom attributes count in the render instructions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PostProcessesOptions
|
||||
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
|
||||
@@ -36,7 +56,11 @@ class WebGLLayerRenderer extends LayerRenderer {
|
||||
|
||||
const options = opt_options || {};
|
||||
|
||||
this.helper_ = new WebGLHelper({
|
||||
/**
|
||||
* @type {WebGLHelper}
|
||||
* @protected
|
||||
*/
|
||||
this.helper = new WebGLHelper({
|
||||
postProcesses: options.postProcesses,
|
||||
uniforms: options.uniforms
|
||||
});
|
||||
@@ -55,96 +79,151 @@ class WebGLLayerRenderer extends LayerRenderer {
|
||||
* @api
|
||||
*/
|
||||
getShaderCompileErrors() {
|
||||
return this.helper_.getShaderCompileErrors();
|
||||
return this.helper.getShaderCompileErrors();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pushes vertices and indices to the given buffers using the geometry coordinates and the following properties
|
||||
* from the feature:
|
||||
* - `color`
|
||||
* - `opacity`
|
||||
* - `size` (for points)
|
||||
* - `u0`, `v0`, `u1`, `v1` (for points)
|
||||
* - `rotateWithView` (for points)
|
||||
* - `width` (for lines)
|
||||
* Custom attributes can be designated using the `opt_attributes` argument, otherwise other properties on the
|
||||
* feature will be ignored.
|
||||
* @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer in which new vertices will be pushed.
|
||||
* @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer in which new indices will be pushed.
|
||||
* @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature in geojson format, coordinates
|
||||
* expressed in EPSG:4326.
|
||||
* @param {Array<string>} [opt_attributes] Custom attributes. An array of properties which will be read from the
|
||||
* feature and pushed in the buffer in the given order. Note: attributes can only be numerical! Any other type or
|
||||
* NaN will result in `0` being pushed in the buffer.
|
||||
* @param {Float32Array} instructions Instructons array in which to write.
|
||||
* @param {number} elementIndex Index from which render instructions will be written.
|
||||
* @param {number} x Point center X coordinate
|
||||
* @param {number} y Point center Y coordinate
|
||||
* @param {number} u0 Left texture coordinate
|
||||
* @param {number} v0 Bottom texture coordinate
|
||||
* @param {number} u1 Right texture coordinate
|
||||
* @param {number} v1 Top texture coordinate
|
||||
* @param {number} size Radius of the point
|
||||
* @param {number} opacity Opacity
|
||||
* @param {boolean} rotateWithView If true, the point will stay aligned with the view
|
||||
* @param {Array<number>} color Array holding red, green, blue, alpha values
|
||||
* @return {number} Index from which the next element should be written
|
||||
* @private
|
||||
*/
|
||||
export function pushFeatureToBuffer(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) {
|
||||
if (!geojsonFeature.geometry) {
|
||||
return;
|
||||
}
|
||||
switch (geojsonFeature.geometry.type) {
|
||||
case 'Point':
|
||||
pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
export function writePointFeatureInstructions(instructions, elementIndex, x, y, u0, v0, u1, v1, size, opacity, rotateWithView, color) {
|
||||
let i = elementIndex;
|
||||
instructions[i++] = x;
|
||||
instructions[i++] = y;
|
||||
instructions[i++] = u0;
|
||||
instructions[i++] = v0;
|
||||
instructions[i++] = u1;
|
||||
instructions[i++] = v1;
|
||||
instructions[i++] = size;
|
||||
instructions[i++] = opacity;
|
||||
instructions[i++] = rotateWithView ? 1 : 0;
|
||||
instructions[i++] = color[0];
|
||||
instructions[i++] = color[1];
|
||||
instructions[i++] = color[2];
|
||||
instructions[i++] = color[3];
|
||||
return i;
|
||||
}
|
||||
|
||||
const tmpArray_ = [];
|
||||
const bufferPositions_ = {vertexPosition: 0, indexPosition: 0};
|
||||
|
||||
export const POINT_INSTRUCTIONS_COUNT = 13;
|
||||
export const POINT_VERTEX_STRIDE = 12;
|
||||
|
||||
function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha) {
|
||||
buffer[pos + 0] = x;
|
||||
buffer[pos + 1] = y;
|
||||
buffer[pos + 2] = offsetX;
|
||||
buffer[pos + 3] = offsetY;
|
||||
buffer[pos + 4] = u;
|
||||
buffer[pos + 5] = v;
|
||||
buffer[pos + 6] = opacity;
|
||||
buffer[pos + 7] = rotateWithView;
|
||||
buffer[pos + 8] = red;
|
||||
buffer[pos + 9] = green;
|
||||
buffer[pos + 10] = blue;
|
||||
buffer[pos + 11] = alpha;
|
||||
}
|
||||
|
||||
function writeCustomAttrs(buffer, pos, customAttrs) {
|
||||
if (customAttrs.length) {
|
||||
buffer.set(customAttrs, pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object holding positions both in an index and a vertex buffer.
|
||||
* @typedef {Object} BufferPositions
|
||||
* @property {number} vertexPosition Position in the vertex buffer
|
||||
* @property {number} indexPosition Position in the index buffer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pushes a quad (two triangles) based on a point geometry
|
||||
* @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer
|
||||
* @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer
|
||||
* @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature
|
||||
* @param {Array<string>} [opt_attributes] Custom attributes
|
||||
* @param {Float32Array} instructions Array of render instructions for points.
|
||||
* @param {number} elementIndex Index from which render instructions will be read.
|
||||
* @param {Float32Array} vertexBuffer Buffer in the form of a typed array.
|
||||
* @param {Uint32Array} indexBuffer Buffer in the form of a typed array.
|
||||
* @param {BufferPositions} [bufferPositions] Buffer write positions; if not specified, positions will be set at 0.
|
||||
* @param {number} [count] Amount of render instructions that will be read. Default value is POINT_INSTRUCTIONS_COUNT
|
||||
* but a higher value can be provided; all values beyond the default count will be put in the vertices buffer as
|
||||
* is, thus allowing specifying custom attributes. Please note: this value should not vary inside the same buffer or
|
||||
* rendering will break.
|
||||
* @return {BufferPositions} New buffer positions where to write next
|
||||
* @property {number} vertexPosition New position in the vertex buffer where future writes should start.
|
||||
* @property {number} indexPosition New position in the index buffer where future writes should start.
|
||||
* @private
|
||||
*/
|
||||
function pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) {
|
||||
const stride = 12 + (opt_attributes !== undefined ? opt_attributes.length : 0);
|
||||
export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, bufferPositions, count) {
|
||||
const count_ = count > POINT_INSTRUCTIONS_COUNT ? count : POINT_INSTRUCTIONS_COUNT;
|
||||
|
||||
const x = geojsonFeature.geometry.coordinates[0];
|
||||
const y = geojsonFeature.geometry.coordinates[1];
|
||||
const u0 = geojsonFeature.properties.u0;
|
||||
const v0 = geojsonFeature.properties.v0;
|
||||
const u1 = geojsonFeature.properties.u1;
|
||||
const v1 = geojsonFeature.properties.v1;
|
||||
const size = geojsonFeature.properties.size;
|
||||
const opacity = geojsonFeature.properties.opacity;
|
||||
const rotateWithView = geojsonFeature.properties.rotateWithView;
|
||||
const color = geojsonFeature.properties.color;
|
||||
const red = color[0];
|
||||
const green = color[1];
|
||||
const blue = color[2];
|
||||
const alpha = color[3];
|
||||
const baseIndex = vertexBuffer.getArray().length / stride;
|
||||
const x = instructions[elementIndex + 0];
|
||||
const y = instructions[elementIndex + 1];
|
||||
const u0 = instructions[elementIndex + 2];
|
||||
const v0 = instructions[elementIndex + 3];
|
||||
const u1 = instructions[elementIndex + 4];
|
||||
const v1 = instructions[elementIndex + 5];
|
||||
const size = instructions[elementIndex + 6];
|
||||
const opacity = instructions[elementIndex + 7];
|
||||
const rotateWithView = instructions[elementIndex + 8];
|
||||
const red = instructions[elementIndex + 9];
|
||||
const green = instructions[elementIndex + 10];
|
||||
const blue = instructions[elementIndex + 11];
|
||||
const alpha = instructions[elementIndex + 12];
|
||||
|
||||
// the default vertex buffer stride is 12, plus additional custom values if any
|
||||
const baseStride = POINT_VERTEX_STRIDE;
|
||||
const stride = baseStride + count_ - POINT_INSTRUCTIONS_COUNT;
|
||||
|
||||
// read custom numerical attributes on the feature
|
||||
const customAttributeValues = tmpArray_;
|
||||
customAttributeValues.length = opt_attributes ? opt_attributes.length : 0;
|
||||
for (let i = 0; i < customAttributeValues.length; i++) {
|
||||
customAttributeValues[i] = parseFloat(geojsonFeature.properties[opt_attributes[i]]) || 0;
|
||||
const customAttrs = tmpArray_;
|
||||
customAttrs.length = count_ - POINT_INSTRUCTIONS_COUNT;
|
||||
for (let i = 0; i < customAttrs.length; i++) {
|
||||
customAttrs[i] = instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + i];
|
||||
}
|
||||
|
||||
let vPos = bufferPositions ? bufferPositions.vertexPosition : 0;
|
||||
let iPos = bufferPositions ? bufferPositions.indexPosition : 0;
|
||||
const baseIndex = vPos / stride;
|
||||
|
||||
// push vertices for each of the four quad corners (first standard then custom attributes)
|
||||
vertexBuffer.getArray().push(x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha);
|
||||
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
|
||||
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha);
|
||||
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
|
||||
vPos += stride;
|
||||
|
||||
vertexBuffer.getArray().push(x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha);
|
||||
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
|
||||
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha);
|
||||
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
|
||||
vPos += stride;
|
||||
|
||||
vertexBuffer.getArray().push(x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha);
|
||||
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
|
||||
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha);
|
||||
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
|
||||
vPos += stride;
|
||||
|
||||
vertexBuffer.getArray().push(x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha);
|
||||
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
|
||||
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha);
|
||||
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
|
||||
vPos += stride;
|
||||
|
||||
indexBuffer.getArray().push(
|
||||
baseIndex, baseIndex + 1, baseIndex + 3,
|
||||
baseIndex + 1, baseIndex + 2, baseIndex + 3
|
||||
);
|
||||
indexBuffer[iPos++] = baseIndex; indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 3;
|
||||
indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 2; indexBuffer[iPos++] = baseIndex + 3;
|
||||
|
||||
bufferPositions_.vertexPosition = vPos;
|
||||
bufferPositions_.indexPosition = iPos;
|
||||
|
||||
return bufferPositions_;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,9 +5,11 @@ import WebGLArrayBuffer from '../../webgl/Buffer.js';
|
||||
import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl.js';
|
||||
import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper.js';
|
||||
import GeometryType from '../../geom/GeometryType.js';
|
||||
import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from './Layer.js';
|
||||
import GeoJSON from '../../format/GeoJSON.js';
|
||||
import {getUid} from '../../util.js';
|
||||
import WebGLLayerRenderer, {
|
||||
getBlankTexture,
|
||||
POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE, WebGLWorkerMessageType,
|
||||
writePointFeatureInstructions
|
||||
} from './Layer.js';
|
||||
import ViewHint from '../../ViewHint.js';
|
||||
import {createEmpty, equals} from '../../extent.js';
|
||||
import {
|
||||
@@ -16,6 +18,7 @@ import {
|
||||
multiply as multiplyTransform,
|
||||
apply as applyTransform
|
||||
} from '../../transform.js';
|
||||
import {create as createWebGLWorker} from '../../worker/webgl.js';
|
||||
|
||||
const VERTEX_SHADER = `
|
||||
precision mediump float;
|
||||
@@ -208,15 +211,15 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
|
||||
this.sourceRevision_ = -1;
|
||||
|
||||
this.verticesBuffer_ = new WebGLArrayBuffer([], DYNAMIC_DRAW);
|
||||
this.indicesBuffer_ = new WebGLArrayBuffer([], DYNAMIC_DRAW);
|
||||
this.verticesBuffer_ = new WebGLArrayBuffer(ARRAY_BUFFER, DYNAMIC_DRAW);
|
||||
this.indicesBuffer_ = new WebGLArrayBuffer(ELEMENT_ARRAY_BUFFER, DYNAMIC_DRAW);
|
||||
|
||||
this.program_ = this.helper_.getProgram(
|
||||
this.program_ = this.helper.getProgram(
|
||||
options.fragmentShader || FRAGMENT_SHADER,
|
||||
options.vertexShader || VERTEX_SHADER
|
||||
);
|
||||
|
||||
this.helper_.useProgram(this.program_);
|
||||
this.helper.useProgram(this.program_);
|
||||
|
||||
this.sizeCallback_ = options.sizeCallback || function() {
|
||||
return 1;
|
||||
@@ -241,14 +244,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
return false;
|
||||
};
|
||||
|
||||
this.geojsonFormat_ = new GeoJSON();
|
||||
|
||||
/**
|
||||
* @type {Object<string, import("../../format/GeoJSON").GeoJSONFeature>}
|
||||
* @private
|
||||
*/
|
||||
this.geojsonFeatureCache_ = {};
|
||||
|
||||
this.previousExtent_ = createEmpty();
|
||||
|
||||
/**
|
||||
@@ -272,6 +267,30 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
* @private
|
||||
*/
|
||||
this.invertRenderTransform_ = createTransform();
|
||||
|
||||
/**
|
||||
* @type {Float32Array}
|
||||
* @private
|
||||
*/
|
||||
this.renderInstructions_ = new Float32Array(0);
|
||||
|
||||
this.worker_ = createWebGLWorker();
|
||||
this.worker_.addEventListener('message', function(event) {
|
||||
const received = event.data;
|
||||
if (received.type === WebGLWorkerMessageType.GENERATE_BUFFERS) {
|
||||
const projectionTransform = received.projectionTransform;
|
||||
this.verticesBuffer_.fromArrayBuffer(received.vertexBuffer);
|
||||
this.indicesBuffer_.fromArrayBuffer(received.indexBuffer);
|
||||
this.helper.flushBufferData(this.verticesBuffer_);
|
||||
this.helper.flushBufferData(this.indicesBuffer_);
|
||||
|
||||
// saves the projection transform for the current frame state
|
||||
this.renderTransform_ = projectionTransform;
|
||||
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
|
||||
|
||||
this.renderInstructions_ = new Float32Array(event.data.renderInstructions);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -285,11 +304,12 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
* @inheritDoc
|
||||
*/
|
||||
renderFrame(frameState) {
|
||||
const layerState = frameState.layerStatesArray[frameState.layerIndex];
|
||||
this.helper_.drawElements(0, this.indicesBuffer_.getArray().length);
|
||||
this.helper_.finalizeDraw(frameState);
|
||||
const canvas = this.helper_.getCanvas();
|
||||
const renderCount = this.indicesBuffer_.getArray() ? this.indicesBuffer_.getArray().length : 0;
|
||||
this.helper.drawElements(0, renderCount);
|
||||
this.helper.finalizeDraw(frameState);
|
||||
const canvas = this.helper.getCanvas();
|
||||
|
||||
const layerState = frameState.layerStatesArray[frameState.layerIndex];
|
||||
const opacity = layerState.opacity;
|
||||
if (opacity !== parseFloat(canvas.style.opacity)) {
|
||||
canvas.style.opacity = opacity;
|
||||
@@ -306,8 +326,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
const vectorSource = vectorLayer.getSource();
|
||||
const viewState = frameState.viewState;
|
||||
|
||||
// TODO: get this from somewhere...
|
||||
const stride = 12;
|
||||
const stride = POINT_VERTEX_STRIDE;
|
||||
|
||||
// the source has changed: clear the feature cache & reload features
|
||||
const sourceChanged = this.sourceRevision_ < vectorSource.getRevision();
|
||||
@@ -328,22 +347,22 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
}
|
||||
|
||||
// apply the current projection transform with the invert of the one used to fill buffers
|
||||
this.helper_.makeProjectionTransform(frameState, this.currentTransform_);
|
||||
this.helper.makeProjectionTransform(frameState, this.currentTransform_);
|
||||
multiplyTransform(this.currentTransform_, this.invertRenderTransform_);
|
||||
|
||||
this.helper_.prepareDraw(frameState);
|
||||
this.helper.prepareDraw(frameState);
|
||||
|
||||
// write new data
|
||||
this.helper_.bindBuffer(ARRAY_BUFFER, this.verticesBuffer_);
|
||||
this.helper_.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||
this.helper.bindBuffer(this.verticesBuffer_);
|
||||
this.helper.bindBuffer(this.indicesBuffer_);
|
||||
|
||||
const bytesPerFloat = Float32Array.BYTES_PER_ELEMENT;
|
||||
this.helper_.enableAttributeArray(DefaultAttrib.POSITION, 2, FLOAT, bytesPerFloat * stride, 0);
|
||||
this.helper_.enableAttributeArray(DefaultAttrib.OFFSETS, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 2);
|
||||
this.helper_.enableAttributeArray(DefaultAttrib.TEX_COORD, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 4);
|
||||
this.helper_.enableAttributeArray(DefaultAttrib.OPACITY, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 6);
|
||||
this.helper_.enableAttributeArray(DefaultAttrib.ROTATE_WITH_VIEW, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 7);
|
||||
this.helper_.enableAttributeArray(DefaultAttrib.COLOR, 4, FLOAT, bytesPerFloat * stride, bytesPerFloat * 8);
|
||||
this.helper.enableAttributeArray(DefaultAttrib.POSITION, 2, FLOAT, bytesPerFloat * stride, 0);
|
||||
this.helper.enableAttributeArray(DefaultAttrib.OFFSETS, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 2);
|
||||
this.helper.enableAttributeArray(DefaultAttrib.TEX_COORD, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 4);
|
||||
this.helper.enableAttributeArray(DefaultAttrib.OPACITY, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 6);
|
||||
this.helper.enableAttributeArray(DefaultAttrib.ROTATE_WITH_VIEW, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 7);
|
||||
this.helper.enableAttributeArray(DefaultAttrib.COLOR, 4, FLOAT, bytesPerFloat * stride, bytesPerFloat * 8);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -357,47 +376,59 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
|
||||
const vectorSource = vectorLayer.getSource();
|
||||
|
||||
this.verticesBuffer_.getArray().length = 0;
|
||||
this.indicesBuffer_.getArray().length = 0;
|
||||
|
||||
// saves the projection transform for the current frame state
|
||||
this.helper_.makeProjectionTransform(frameState, this.renderTransform_);
|
||||
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
|
||||
const projectionTransform = createTransform();
|
||||
this.helper.makeProjectionTransform(frameState, projectionTransform);
|
||||
|
||||
const features = vectorSource.getFeatures();
|
||||
const totalInstructionsCount = POINT_INSTRUCTIONS_COUNT * features.length;
|
||||
if (!this.renderInstructions_ || this.renderInstructions_.length !== totalInstructionsCount) {
|
||||
this.renderInstructions_ = new Float32Array(totalInstructionsCount);
|
||||
}
|
||||
|
||||
// loop on features to fill the buffer
|
||||
const features = vectorSource.getFeatures();
|
||||
let feature;
|
||||
const tmpCoords = [];
|
||||
let elementIndex = 0;
|
||||
for (let i = 0; i < features.length; i++) {
|
||||
feature = features[i];
|
||||
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let geojsonFeature = this.geojsonFeatureCache_[getUid(feature)];
|
||||
if (!geojsonFeature) {
|
||||
geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature);
|
||||
this.geojsonFeatureCache_[getUid(feature)] = geojsonFeature;
|
||||
tmpCoords[0] = this.coordCallback_(feature, 0);
|
||||
tmpCoords[1] = this.coordCallback_(feature, 1);
|
||||
applyTransform(projectionTransform, tmpCoords);
|
||||
|
||||
elementIndex = writePointFeatureInstructions(
|
||||
this.renderInstructions_,
|
||||
elementIndex,
|
||||
tmpCoords[0],
|
||||
tmpCoords[1],
|
||||
this.texCoordCallback_(feature, 0),
|
||||
this.texCoordCallback_(feature, 1),
|
||||
this.texCoordCallback_(feature, 2),
|
||||
this.texCoordCallback_(feature, 3),
|
||||
this.sizeCallback_(feature),
|
||||
this.opacityCallback_(feature),
|
||||
this.rotateWithViewCallback_(feature),
|
||||
this.colorCallback_(feature, this.colorArray_)
|
||||
);
|
||||
}
|
||||
|
||||
geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0);
|
||||
geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1);
|
||||
applyTransform(this.renderTransform_, geojsonFeature.geometry.coordinates);
|
||||
geojsonFeature.properties = geojsonFeature.properties || {};
|
||||
geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_);
|
||||
geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0);
|
||||
geojsonFeature.properties.v0 = this.texCoordCallback_(feature, 1);
|
||||
geojsonFeature.properties.u1 = this.texCoordCallback_(feature, 2);
|
||||
geojsonFeature.properties.v1 = this.texCoordCallback_(feature, 3);
|
||||
geojsonFeature.properties.size = this.sizeCallback_(feature);
|
||||
geojsonFeature.properties.opacity = this.opacityCallback_(feature);
|
||||
geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0;
|
||||
/** @type import('./Layer').WebGLWorkerGenerateBuffersMessage */
|
||||
const message = {
|
||||
type: WebGLWorkerMessageType.GENERATE_BUFFERS,
|
||||
renderInstructions: this.renderInstructions_.buffer
|
||||
};
|
||||
// additional properties will be sent back as-is by the worker
|
||||
message['projectionTransform'] = projectionTransform;
|
||||
|
||||
pushFeatureToBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature);
|
||||
this.worker_.postMessage(message, [this.renderInstructions_.buffer]);
|
||||
this.renderInstructions_ = null;
|
||||
}
|
||||
|
||||
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
|
||||
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGLPointsLayerRenderer;
|
||||
|
||||
@@ -23,6 +23,7 @@ import TileImage from './TileImage.js';
|
||||
* for version 1, 'default' for versions 2 and 3.
|
||||
* @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
|
||||
* Higher values can increase reprojection performance, but decrease precision.
|
||||
* @property {Array<number>} [resolutions] Supported resolutions as given in IIIF 'scaleFactors'
|
||||
* @property {import("../size.js").Size} size Size of the image [width, height].
|
||||
* @property {import("../size.js").Size[]} [sizes] Supported scaled image sizes.
|
||||
* Content of the IIIF info.json 'sizes' property, but as array of Size objects.
|
||||
@@ -37,7 +38,7 @@ import TileImage from './TileImage.js';
|
||||
* are supported), the default tilesize is 256.
|
||||
* @property {number} [transition]
|
||||
* @property {string} [url] Base URL of the IIIF Image service.
|
||||
* This should be the same as the IIIF Image @id.
|
||||
* This should be the same as the IIIF Image ID.
|
||||
* @property {Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
|
||||
* @property {number} [zDirection] Indicate which resolution should be used
|
||||
* by a renderer if the views resolution does not match any resolution of the tile source.
|
||||
@@ -63,6 +64,9 @@ class IIIF extends TileImage {
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
|
||||
/**
|
||||
* @type {Partial<Options>} options
|
||||
*/
|
||||
const options = opt_options || {};
|
||||
|
||||
let baseUrl = options.url || '';
|
||||
@@ -83,7 +87,7 @@ class IIIF extends TileImage {
|
||||
const extent = options.extent || [0, -height, width, 0];
|
||||
|
||||
const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0;
|
||||
const supportsListedTiles = tileSize != undefined && (Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0);
|
||||
const supportsListedTiles = tileSize != undefined && (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0);
|
||||
const supportsArbitraryTiling = supports != undefined && Array.isArray(supports) &&
|
||||
(supports.includes('regionByPx') || supports.includes('regionByPct')) &&
|
||||
(supports.includes('sizeByWh') || supports.includes('sizeByH') ||
|
||||
@@ -99,7 +103,7 @@ class IIIF extends TileImage {
|
||||
|
||||
if (supportsListedTiles || supportsArbitraryTiling) {
|
||||
if (tileSize != undefined) {
|
||||
if (Number.isInteger(tileSize) && tileSize > 0) {
|
||||
if (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0) {
|
||||
tileWidth = tileSize;
|
||||
tileHeight = tileSize;
|
||||
} else if (Array.isArray(tileSize) && tileSize.length > 0) {
|
||||
@@ -131,7 +135,7 @@ class IIIF extends TileImage {
|
||||
resolutions.push(Math.pow(2, i));
|
||||
}
|
||||
} else {
|
||||
const maxScaleFactor = Math.max([...resolutions]);
|
||||
const maxScaleFactor = Math.max(...resolutions);
|
||||
// TODO maxScaleFactor might not be a power to 2
|
||||
maxZoom = Math.round(Math.log(maxScaleFactor) / Math.LN2);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class ImageArcGISRest extends ImageSource {
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
|
||||
@@ -53,7 +53,7 @@ class ImageCanvasSource extends ImageSource {
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
|
||||
@@ -63,7 +63,7 @@ class ImageWMS extends ImageSource {
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
|
||||
@@ -75,7 +75,7 @@ class TileSource extends Source {
|
||||
if (tileGrid) {
|
||||
toSize(tileGrid.getTileSize(tileGrid.getMinZoom()), tileSize);
|
||||
}
|
||||
const canUseScreen = 'screen' in self;
|
||||
const canUseScreen = typeof screen !== 'undefined';
|
||||
const width = canUseScreen ? (screen.availWidth || screen.width) : 1920;
|
||||
const height = canUseScreen ? (screen.availHeight || screen.height) : 1080;
|
||||
cacheSize = 4 * Math.ceil(width / tileSize[0]) * Math.ceil(height / tileSize[1]);
|
||||
|
||||
@@ -64,7 +64,7 @@ class TileArcGISRest extends TileImage {
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
|
||||
@@ -81,7 +81,7 @@ class TileWMS extends TileImage {
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
const params = options.params || {};
|
||||
|
||||
|
||||
@@ -670,7 +670,7 @@ class VectorSource extends Source {
|
||||
|
||||
|
||||
/**
|
||||
* Get all features in the provided extent. Note that this returns an array of
|
||||
* Get all features whose bounding box intersects the provided extent. Note that this returns an array of
|
||||
* all features intersecting the given extent in random order (so it may include
|
||||
* features whose geometries do not intersect the extent).
|
||||
*
|
||||
|
||||
@@ -24,7 +24,7 @@ class CircleStyle extends RegularShape {
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || /** @type {Options} */ ({});
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
super({
|
||||
points: Infinity,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array<number>} 4x4 matrix representing a 3D identity transform.
|
||||
* @return {Array<number>} "4x4 matrix representing a 3D identity transform."
|
||||
*/
|
||||
export function create() {
|
||||
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
@@ -14,7 +14,7 @@ export function create() {
|
||||
/**
|
||||
* @param {Array<number>} mat4 Flattened 4x4 matrix receiving the result.
|
||||
* @param {import("../transform.js").Transform} transform Transformation matrix.
|
||||
* @return {Array<number>} 2D transformation matrix as flattened 4x4 matrix.
|
||||
* @return {Array<number>} "2D transformation matrix as flattened 4x4 matrix."
|
||||
*/
|
||||
export function fromTransform(mat4, transform) {
|
||||
mat4[0] = transform[0];
|
||||
|
||||
227
src/ol/webgl.js
227
src/ol/webgl.js
@@ -7,56 +7,6 @@
|
||||
* Constants taken from goog.webgl
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const ONE = 1;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const SRC_ALPHA = 0x0302;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const COLOR_ATTACHMENT0 = 0x8CE0;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const COLOR_BUFFER_BIT = 0x00004000;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TRIANGLES = 0x0004;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TRIANGLE_STRIP = 0x0005;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const ONE_MINUS_SRC_ALPHA = 0x0303;
|
||||
|
||||
|
||||
/**
|
||||
* Used by {@link module:ol/webgl/Helper~WebGLHelper} for buffers containing vertices data, such as
|
||||
* position, color, texture coordinate, etc. These vertices are then referenced by an index buffer
|
||||
@@ -106,41 +56,6 @@ export const STATIC_DRAW = 0x88E4;
|
||||
export const DYNAMIC_DRAW = 0x88E8;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const CULL_FACE = 0x0B44;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const BLEND = 0x0BE2;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const STENCIL_TEST = 0x0B90;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEPTH_TEST = 0x0B71;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const SCISSOR_TEST = 0x0C11;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
@@ -168,105 +83,6 @@ export const UNSIGNED_INT = 0x1405;
|
||||
*/
|
||||
export const FLOAT = 0x1406;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const RGBA = 0x1908;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const FRAGMENT_SHADER = 0x8B30;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const VERTEX_SHADER = 0x8B31;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const LINK_STATUS = 0x8B82;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const LINEAR = 0x2601;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TEXTURE_MAG_FILTER = 0x2800;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TEXTURE_MIN_FILTER = 0x2801;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TEXTURE_WRAP_S = 0x2802;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TEXTURE_WRAP_T = 0x2803;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TEXTURE_2D = 0x0DE1;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const TEXTURE0 = 0x84C0;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const CLAMP_TO_EDGE = 0x812F;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const COMPILE_STATUS = 0x8B81;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const FRAMEBUFFER = 0x8D40;
|
||||
|
||||
|
||||
/** end of goog.webgl constants
|
||||
*/
|
||||
|
||||
@@ -303,50 +119,21 @@ export function getContext(canvas, opt_attributes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Include debuggable shader sources. Default is `true`. This should be set to
|
||||
* `false` for production builds.
|
||||
* @type {boolean}
|
||||
*/
|
||||
export const DEBUG = true;
|
||||
|
||||
|
||||
/**
|
||||
* The maximum supported WebGL texture size in pixels. If WebGL is not
|
||||
* supported, the value is set to `undefined`.
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
let MAX_TEXTURE_SIZE; // value is set below
|
||||
|
||||
|
||||
/**
|
||||
* List of supported WebGL extensions.
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
let EXTENSIONS; // value is set below
|
||||
|
||||
let supportedExtensions;
|
||||
|
||||
/**
|
||||
* True if both OpenLayers and browser support WebGL.
|
||||
* @type {boolean}
|
||||
* @api
|
||||
* @return {Array<string>} List of supported WebGL extensions.
|
||||
*/
|
||||
let HAS = false;
|
||||
|
||||
//TODO Remove side effects
|
||||
if (typeof window !== 'undefined' && 'WebGLRenderingContext' in window) {
|
||||
try {
|
||||
export function getSupportedExtensions() {
|
||||
if (!supportedExtensions) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const gl = getContext(canvas);
|
||||
if (gl) {
|
||||
HAS = true;
|
||||
MAX_TEXTURE_SIZE = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_SIZE));
|
||||
EXTENSIONS = gl.getSupportedExtensions();
|
||||
}
|
||||
} catch (e) {
|
||||
// pass
|
||||
supportedExtensions = gl.getSupportedExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
export {HAS, MAX_TEXTURE_SIZE, EXTENSIONS};
|
||||
return supportedExtensions;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* @module ol/webgl/Buffer
|
||||
*/
|
||||
import {STATIC_DRAW, STREAM_DRAW, DYNAMIC_DRAW} from '../webgl.js';
|
||||
import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER} from '../webgl.js';
|
||||
import {assert} from '../asserts.js';
|
||||
|
||||
/**
|
||||
* Used to describe the intended usage for the data: `STATIC_DRAW`, `STREAM_DRAW`
|
||||
@@ -17,43 +19,109 @@ export const BufferUsage = {
|
||||
/**
|
||||
* @classdesc
|
||||
* Object used to store an array of data as well as usage information for that data.
|
||||
* See the documentation of [WebGLRenderingContext.bufferData](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bufferData) for more info.
|
||||
* Stores typed arrays internally, either Float32Array or Uint16/32Array depending on
|
||||
* the buffer type (ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER) and available extensions.
|
||||
*
|
||||
* To populate the array, you can either use:
|
||||
* * A size using `#ofSize(buffer)`
|
||||
* * An `ArrayBuffer` object using `#fromArrayBuffer(buffer)`
|
||||
* * A plain array using `#fromArray(array)`
|
||||
*
|
||||
* Note:
|
||||
* See the documentation of [WebGLRenderingContext.bufferData](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bufferData)
|
||||
* for more info on buffer usage.
|
||||
* @api
|
||||
*/
|
||||
class WebGLArrayBuffer {
|
||||
|
||||
/**
|
||||
* @param {Array<number>=} opt_arr Array.
|
||||
* @param {number=} opt_usage Usage, either `STATIC_DRAW`, `STREAM_DRAW` or `DYNAMIC_DRAW`. Default is `DYNAMIC_DRAW`.
|
||||
* @param {number} type Buffer type, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.
|
||||
* @param {number=} opt_usage Intended usage, either `STATIC_DRAW`, `STREAM_DRAW` or `DYNAMIC_DRAW`.
|
||||
* Default is `DYNAMIC_DRAW`.
|
||||
*/
|
||||
constructor(opt_arr, opt_usage) {
|
||||
constructor(type, opt_usage) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array<number>}
|
||||
* @type {Float32Array|Uint32Array}
|
||||
*/
|
||||
this.arr_ = opt_arr !== undefined ? opt_arr : [];
|
||||
this.array = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.usage_ = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW;
|
||||
this.type = type;
|
||||
|
||||
assert(type === ARRAY_BUFFER || type === ELEMENT_ARRAY_BUFFER, 62);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.usage = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Array<number>} Array.
|
||||
* Populates the buffer with an array of the given size (all values will be zeroes).
|
||||
* @param {number} size Array size
|
||||
*/
|
||||
ofSize(size) {
|
||||
this.array = new (getArrayClassForType(this.type))(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the buffer with an array of the given size (all values will be zeroes).
|
||||
* @param {Array<number>} array Numerical array
|
||||
*/
|
||||
fromArray(array) {
|
||||
this.array = (getArrayClassForType(this.type)).from(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the buffer with a raw binary array buffer.
|
||||
* @param {ArrayBuffer} buffer Raw binary buffer to populate the array with. Note that this buffer must have been
|
||||
* initialized for the same typed array class.
|
||||
*/
|
||||
fromArrayBuffer(buffer) {
|
||||
this.array = new (getArrayClassForType(this.type))(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number} Buffer type.
|
||||
*/
|
||||
getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Float32Array|Uint32Array} Array.
|
||||
*/
|
||||
getArray() {
|
||||
return this.arr_;
|
||||
return this.array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number} Usage.
|
||||
*/
|
||||
getUsage() {
|
||||
return this.usage_;
|
||||
return this.usage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a typed array constructor based on the given buffer type
|
||||
* @param {number} type Buffer type, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.
|
||||
* @returns {Float32ArrayConstructor|Uint32ArrayConstructor} The typed array class to use for this buffer.
|
||||
*/
|
||||
export function getArrayClassForType(type) {
|
||||
switch (type) {
|
||||
case ARRAY_BUFFER:
|
||||
return Float32Array;
|
||||
case ELEMENT_ARRAY_BUFFER:
|
||||
return Uint32Array;
|
||||
default:
|
||||
return Float32Array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
* @module ol/webgl/Helper
|
||||
*/
|
||||
import {getUid} from '../util.js';
|
||||
import {EXTENSIONS as WEBGL_EXTENSIONS} from '../webgl.js';
|
||||
import Disposable from '../Disposable.js';
|
||||
import {includes} from '../array.js';
|
||||
import {listen, unlistenAll} from '../events.js';
|
||||
import {clear} from '../obj.js';
|
||||
import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, TEXTURE_2D, TEXTURE_WRAP_S, TEXTURE_WRAP_T} from '../webgl.js';
|
||||
import ContextEventType from '../webgl/ContextEventType.js';
|
||||
import {
|
||||
compose as composeTransform,
|
||||
create as createTransform,
|
||||
reset as resetTransform,
|
||||
rotate as rotateTransform,
|
||||
scale as scaleTransform,
|
||||
translate as translateTransform
|
||||
scale as scaleTransform
|
||||
} from '../transform.js';
|
||||
import {create, fromTransform} from '../vec/mat4.js';
|
||||
import WebGLPostProcessingPass from './PostProcessingPass.js';
|
||||
import {getContext} from '../webgl.js';
|
||||
import {getContext, getSupportedExtensions} from '../webgl.js';
|
||||
import {includes} from '../array.js';
|
||||
import {assert} from '../asserts.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -258,15 +257,8 @@ class WebGLHelper extends Disposable {
|
||||
*/
|
||||
this.currentProgram_ = null;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.hasOESElementIndexUint = includes(WEBGL_EXTENSIONS, 'OES_element_index_uint');
|
||||
|
||||
// use the OES_element_index_uint extension if available
|
||||
if (this.hasOESElementIndexUint) {
|
||||
assert(includes(getSupportedExtensions(), 'OES_element_index_uint'), 63);
|
||||
gl.getExtension('OES_element_index_uint');
|
||||
}
|
||||
|
||||
listen(this.canvas_, ContextEventType.LOST,
|
||||
this.handleWebGLContextLost, this);
|
||||
@@ -347,11 +339,10 @@ 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.
|
||||
* @param {number} target Target, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.
|
||||
* @param {import("./Buffer").default} buffer Buffer.
|
||||
* @api
|
||||
*/
|
||||
bindBuffer(target, buffer) {
|
||||
bindBuffer(buffer) {
|
||||
const gl = this.getGL();
|
||||
const bufferKey = getUid(buffer);
|
||||
let bufferCache = this.bufferCache_[bufferKey];
|
||||
@@ -362,28 +353,19 @@ class WebGLHelper extends Disposable {
|
||||
webGlBuffer: webGlBuffer
|
||||
};
|
||||
}
|
||||
gl.bindBuffer(target, bufferCache.webGlBuffer);
|
||||
gl.bindBuffer(buffer.getType(), 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) {
|
||||
flushBufferData(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);
|
||||
} else if (target == ELEMENT_ARRAY_BUFFER) {
|
||||
arrayBuffer = this.hasOESElementIndexUint ?
|
||||
new Uint32Array(arr) : new Uint16Array(arr);
|
||||
}
|
||||
gl.bufferData(target, arrayBuffer, buffer.getUsage());
|
||||
this.bindBuffer(buffer);
|
||||
gl.bufferData(buffer.getType(), buffer.getArray(), buffer.getUsage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,9 +444,8 @@ class WebGLHelper extends Disposable {
|
||||
*/
|
||||
drawElements(start, end) {
|
||||
const gl = this.getGL();
|
||||
const elementType = this.hasOESElementIndexUint ?
|
||||
gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;
|
||||
const elementSize = this.hasOESElementIndexUint ? 4 : 2;
|
||||
const elementType = gl.UNSIGNED_INT;
|
||||
const elementSize = 4;
|
||||
|
||||
const numItems = end - start;
|
||||
const offsetInBytes = start * elementSize;
|
||||
@@ -691,10 +672,12 @@ class WebGLHelper extends Disposable {
|
||||
const center = frameState.viewState.center;
|
||||
|
||||
resetTransform(transform);
|
||||
scaleTransform(transform, 2 / (resolution * size[0]), 2 / (resolution * size[1]));
|
||||
rotateTransform(transform, -rotation);
|
||||
translateTransform(transform, -center[0], -center[1]);
|
||||
|
||||
composeTransform(transform,
|
||||
0, 0,
|
||||
2 / (resolution * size[0]), 2 / (resolution * size[1]),
|
||||
-rotation,
|
||||
-center[0], -center[1]
|
||||
);
|
||||
return transform;
|
||||
}
|
||||
|
||||
@@ -773,11 +756,11 @@ class WebGLHelper extends Disposable {
|
||||
|
||||
if (opt_wrapS !== undefined) {
|
||||
gl.texParameteri(
|
||||
TEXTURE_2D, TEXTURE_WRAP_S, opt_wrapS);
|
||||
gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, opt_wrapS);
|
||||
}
|
||||
if (opt_wrapT !== undefined) {
|
||||
gl.texParameteri(
|
||||
TEXTURE_2D, TEXTURE_WRAP_T, opt_wrapT);
|
||||
gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, opt_wrapT);
|
||||
}
|
||||
|
||||
return texture;
|
||||
|
||||
48
src/ol/worker/webgl.js
Normal file
48
src/ol/worker/webgl.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* A worker that does cpu-heavy tasks related to webgl rendering.
|
||||
* @module ol/worker/webgl
|
||||
*/
|
||||
import {
|
||||
POINT_INSTRUCTIONS_COUNT,
|
||||
POINT_VERTEX_STRIDE,
|
||||
WebGLWorkerMessageType,
|
||||
writePointFeatureToBuffers
|
||||
} from '../renderer/webgl/Layer.js';
|
||||
import {assign} from '../obj.js';
|
||||
|
||||
onmessage = event => {
|
||||
const received = event.data;
|
||||
if (received.type === WebGLWorkerMessageType.GENERATE_BUFFERS) {
|
||||
const renderInstructions = new Float32Array(received.renderInstructions);
|
||||
const customAttributesCount = received.customAttributesCount || 0;
|
||||
const instructionsCount = POINT_INSTRUCTIONS_COUNT + customAttributesCount;
|
||||
|
||||
const elementsCount = renderInstructions.length / instructionsCount;
|
||||
const indicesCount = elementsCount * 6;
|
||||
const verticesCount = elementsCount * 4 * (POINT_VERTEX_STRIDE + customAttributesCount);
|
||||
const indexBuffer = new Uint32Array(indicesCount);
|
||||
const vertexBuffer = new Float32Array(verticesCount);
|
||||
|
||||
let bufferPositions = null;
|
||||
for (let i = 0; i < renderInstructions.length; i += instructionsCount) {
|
||||
bufferPositions = writePointFeatureToBuffers(
|
||||
renderInstructions,
|
||||
i,
|
||||
vertexBuffer,
|
||||
indexBuffer,
|
||||
bufferPositions,
|
||||
instructionsCount);
|
||||
}
|
||||
|
||||
/** @type {import('../renderer/webgl/Layer').WebGLWorkerGenerateBuffersMessage} */
|
||||
const message = assign({
|
||||
vertexBuffer: vertexBuffer.buffer,
|
||||
indexBuffer: indexBuffer.buffer,
|
||||
renderInstructions: renderInstructions.buffer
|
||||
}, received);
|
||||
|
||||
postMessage(message, [vertexBuffer.buffer, indexBuffer.buffer, renderInstructions.buffer]);
|
||||
}
|
||||
};
|
||||
|
||||
export let create;
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +20,7 @@ async function build(input, {minify = true} = {}) {
|
||||
common(),
|
||||
resolve(),
|
||||
babel({
|
||||
'externalHelpers': true,
|
||||
'presets': [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"globals": {
|
||||
"IMAGE_TOLERANCE": false,
|
||||
"afterLoadText": false,
|
||||
"assertWebGL": false,
|
||||
"createMapDiv": true,
|
||||
"disposeMap": true,
|
||||
"expect": false,
|
||||
|
||||
@@ -8,7 +8,7 @@ import LinearRing from '../../../../src/ol/geom/LinearRing.js';
|
||||
import MultiPolygon from '../../../../src/ol/geom/MultiPolygon.js';
|
||||
import Point from '../../../../src/ol/geom/Point.js';
|
||||
import Polygon from '../../../../src/ol/geom/Polygon.js';
|
||||
import {fromLonLat, get as getProjection, toLonLat, transform} from '../../../../src/ol/proj.js';
|
||||
import {fromLonLat, get as getProjection, toLonLat, transform, Projection} from '../../../../src/ol/proj.js';
|
||||
|
||||
|
||||
describe('ol.format.GeoJSON', function() {
|
||||
@@ -260,6 +260,28 @@ describe('ol.format.GeoJSON', function() {
|
||||
expect(feature.getGeometry()).to.be.an(Point);
|
||||
});
|
||||
|
||||
it('transforms tile pixel coordinates', function() {
|
||||
const geojson = {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [1024, 1024]
|
||||
}
|
||||
};
|
||||
const format = new GeoJSON({
|
||||
dataProjection: new Projection({
|
||||
code: '',
|
||||
units: 'tile-pixels',
|
||||
extent: [0, 0, 4096, 4096]
|
||||
})
|
||||
});
|
||||
const feature = format.readFeature(geojson, {
|
||||
extent: [-180, -90, 180, 90],
|
||||
featureProjection: 'EPSG:3857'
|
||||
});
|
||||
expect(feature.getGeometry().getCoordinates()).to.eql([-135, 45]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#readFeatures', function() {
|
||||
|
||||
@@ -216,6 +216,47 @@ describe('ol.interaction.Translate', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('moving features, with filter option', function() {
|
||||
let translate;
|
||||
|
||||
beforeEach(function() {
|
||||
translate = new Translate({
|
||||
filter: function(feature, layer) {
|
||||
return feature == features[0];
|
||||
}
|
||||
});
|
||||
map.addInteraction(translate);
|
||||
});
|
||||
|
||||
it('moves a filter-passing feature', function() {
|
||||
const events = trackEvents(features[0], translate);
|
||||
|
||||
simulateEvent('pointermove', 10, 20);
|
||||
simulateEvent('pointerdown', 10, 20);
|
||||
simulateEvent('pointerdrag', 50, -40);
|
||||
simulateEvent('pointerup', 50, -40);
|
||||
const geometry = features[0].getGeometry();
|
||||
expect(geometry).to.be.a(Point);
|
||||
expect(geometry.getCoordinates()).to.eql([50, 40]);
|
||||
|
||||
validateEvents(events, [features[0]]);
|
||||
});
|
||||
|
||||
it('does not move a filter-discarded feature', function() {
|
||||
const events = trackEvents(features[0], translate);
|
||||
|
||||
simulateEvent('pointermove', 20, 30);
|
||||
simulateEvent('pointerdown', 20, 30);
|
||||
simulateEvent('pointerdrag', 50, -40);
|
||||
simulateEvent('pointerup', 50, -40);
|
||||
const geometry = features[1].getGeometry();
|
||||
expect(geometry).to.be.a(Point);
|
||||
expect(geometry.getCoordinates()).to.eql([20, -30]);
|
||||
|
||||
expect(events).to.be.empty();
|
||||
});
|
||||
});
|
||||
|
||||
describe('changes css cursor', function() {
|
||||
let element, translate;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ describe('ol.layer.Group', function() {
|
||||
opacity: 1,
|
||||
visible: true,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 0,
|
||||
@@ -158,6 +159,7 @@ describe('ol.layer.Group', function() {
|
||||
opacity: 0.5,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 10,
|
||||
@@ -199,6 +201,7 @@ describe('ol.layer.Group', function() {
|
||||
opacity: 0.5,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: groupExtent,
|
||||
zIndex: 0,
|
||||
@@ -239,6 +242,7 @@ describe('ol.layer.Group', function() {
|
||||
opacity: 0.3,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: groupExtent,
|
||||
zIndex: 10,
|
||||
@@ -255,6 +259,7 @@ describe('ol.layer.Group', function() {
|
||||
opacity: 0,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 0,
|
||||
@@ -269,6 +274,7 @@ describe('ol.layer.Group', function() {
|
||||
opacity: 1,
|
||||
visible: true,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 0,
|
||||
@@ -441,6 +447,7 @@ describe('ol.layer.Group', function() {
|
||||
opacity: 0.25,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 0,
|
||||
|
||||
@@ -49,6 +49,7 @@ describe('ol.layer.Layer', function() {
|
||||
opacity: 1,
|
||||
visible: true,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 0,
|
||||
@@ -84,6 +85,7 @@ describe('ol.layer.Layer', function() {
|
||||
opacity: 0.5,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 10,
|
||||
@@ -182,6 +184,7 @@ describe('ol.layer.Layer', function() {
|
||||
opacity: 0.33,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 10,
|
||||
@@ -198,6 +201,7 @@ describe('ol.layer.Layer', function() {
|
||||
opacity: 0,
|
||||
visible: false,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 0,
|
||||
@@ -212,6 +216,7 @@ describe('ol.layer.Layer', function() {
|
||||
opacity: 1,
|
||||
visible: true,
|
||||
managed: true,
|
||||
hasOverlay: false,
|
||||
sourceState: 'ready',
|
||||
extent: undefined,
|
||||
zIndex: 0,
|
||||
|
||||
@@ -5,7 +5,6 @@ import MapEvent from '../../../src/ol/MapEvent.js';
|
||||
import Overlay from '../../../src/ol/Overlay.js';
|
||||
import View from '../../../src/ol/View.js';
|
||||
import {LineString, Point} from '../../../src/ol/geom.js';
|
||||
import {TOUCH} from '../../../src/ol/has.js';
|
||||
import {focus} from '../../../src/ol/events/condition.js';
|
||||
import {defaults as defaultInteractions} from '../../../src/ol/interaction.js';
|
||||
import {get as getProjection} from '../../../src/ol/proj.js';
|
||||
@@ -45,7 +44,7 @@ describe('ol.Map', function() {
|
||||
it('creates the viewport', function() {
|
||||
const map = new Map({});
|
||||
const viewport = map.getViewport();
|
||||
const className = 'ol-viewport' + (TOUCH ? ' ol-touch' : '');
|
||||
const className = 'ol-viewport' + ('ontouchstart' in window ? ' ol-touch' : '');
|
||||
expect(viewport.className).to.be(className);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import Text from '../../../../../src/ol/style/Text.js';
|
||||
import {createXYZ} from '../../../../../src/ol/tilegrid.js';
|
||||
import VectorTileRenderType from '../../../../../src/ol/layer/VectorTileRenderType.js';
|
||||
import {getUid} from '../../../../../src/ol/util.js';
|
||||
import TileLayer from '../../../../../src/ol/layer/Tile.js';
|
||||
import XYZ from '../../../../../src/ol/source/XYZ.js';
|
||||
|
||||
|
||||
describe('ol.renderer.canvas.VectorTileLayer', function() {
|
||||
@@ -39,6 +41,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
|
||||
target.style.height = '256px';
|
||||
document.body.appendChild(target);
|
||||
map = new Map({
|
||||
pixelRatio: 1,
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
zoom: 0
|
||||
@@ -203,6 +206,25 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
|
||||
expect(Object.keys(tile.executorGroups)[1]).to.be(getUid(layer2));
|
||||
});
|
||||
|
||||
it('reuses render container and adds and removes overlay context', function(done) {
|
||||
map.getLayers().insertAt(0, new TileLayer({
|
||||
source: new XYZ({
|
||||
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);
|
||||
map.removeLayer(map.getLayers().item(1));
|
||||
map.renderSync();
|
||||
expect(document.querySelector('.ol-layer').childElementCount).to.be(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#prepareFrame', function() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from '../../../../../src/ol/renderer/webgl/Layer.js';
|
||||
import WebGLArrayBuffer from '../../../../../src/ol/webgl/Buffer.js';
|
||||
import WebGLLayerRenderer, {
|
||||
getBlankTexture, POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE,
|
||||
writePointFeatureInstructions, writePointFeatureToBuffers
|
||||
} from '../../../../../src/ol/renderer/webgl/Layer.js';
|
||||
import Layer from '../../../../../src/ol/layer/Layer.js';
|
||||
|
||||
|
||||
@@ -28,111 +30,211 @@ describe('ol.renderer.webgl.Layer', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('pushFeatureToBuffer', function() {
|
||||
let vertexBuffer, indexBuffer;
|
||||
describe('writePointFeatureInstructions', function() {
|
||||
let instructions;
|
||||
|
||||
beforeEach(function() {
|
||||
vertexBuffer = new WebGLArrayBuffer();
|
||||
indexBuffer = new WebGLArrayBuffer();
|
||||
instructions = new Float32Array(100);
|
||||
});
|
||||
|
||||
it('does nothing if the feature has no geometry', function() {
|
||||
const feature = {
|
||||
type: 'Feature',
|
||||
id: 'AFG',
|
||||
properties: {
|
||||
color: [0.5, 1, 0.2, 0.7],
|
||||
size: 3
|
||||
},
|
||||
geometry: null
|
||||
};
|
||||
pushFeatureToBuffer(vertexBuffer, indexBuffer, feature);
|
||||
expect(vertexBuffer.getArray().length).to.eql(0);
|
||||
expect(indexBuffer.getArray().length).to.eql(0);
|
||||
it('writes instructions corresponding to the given parameters', function() {
|
||||
const baseIndex = 17;
|
||||
writePointFeatureInstructions(instructions, baseIndex,
|
||||
1, 2, 3, 4, 5, 6,
|
||||
7, 8, true, [10, 11, 12, 13]);
|
||||
expect(instructions[baseIndex + 0]).to.eql(1);
|
||||
expect(instructions[baseIndex + 1]).to.eql(2);
|
||||
expect(instructions[baseIndex + 2]).to.eql(3);
|
||||
expect(instructions[baseIndex + 3]).to.eql(4);
|
||||
expect(instructions[baseIndex + 4]).to.eql(5);
|
||||
expect(instructions[baseIndex + 5]).to.eql(6);
|
||||
expect(instructions[baseIndex + 6]).to.eql(7);
|
||||
expect(instructions[baseIndex + 7]).to.eql(8);
|
||||
expect(instructions[baseIndex + 8]).to.eql(1);
|
||||
expect(instructions[baseIndex + 9]).to.eql(10);
|
||||
expect(instructions[baseIndex + 10]).to.eql(11);
|
||||
expect(instructions[baseIndex + 11]).to.eql(12);
|
||||
expect(instructions[baseIndex + 12]).to.eql(13);
|
||||
});
|
||||
|
||||
it('adds two triangles with the correct attributes for a point geometry', function() {
|
||||
const feature = {
|
||||
type: 'Feature',
|
||||
id: 'AFG',
|
||||
properties: {
|
||||
color: [0.5, 1, 0.2, 0.7],
|
||||
size: 3
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-75, 47]
|
||||
}
|
||||
};
|
||||
const attributePerVertex = 12;
|
||||
pushFeatureToBuffer(vertexBuffer, indexBuffer, feature);
|
||||
expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4);
|
||||
expect(indexBuffer.getArray().length).to.eql(6);
|
||||
it('correctly chains writes', function() {
|
||||
let baseIndex = 0;
|
||||
baseIndex = writePointFeatureInstructions(instructions, baseIndex,
|
||||
1, 2, 3, 4, 5, 6,
|
||||
7, 8, true, [10, 11, 12, 13]);
|
||||
baseIndex = writePointFeatureInstructions(instructions, baseIndex,
|
||||
1, 2, 3, 4, 5, 6,
|
||||
7, 8, true, [10, 11, 12, 13]);
|
||||
writePointFeatureInstructions(instructions, baseIndex,
|
||||
1, 2, 3, 4, 5, 6,
|
||||
7, 8, true, [10, 11, 12, 13]);
|
||||
expect(instructions[baseIndex + 0]).to.eql(1);
|
||||
expect(instructions[baseIndex + 1]).to.eql(2);
|
||||
expect(instructions[baseIndex + 2]).to.eql(3);
|
||||
expect(instructions[baseIndex + 3]).to.eql(4);
|
||||
expect(instructions[baseIndex + 4]).to.eql(5);
|
||||
expect(instructions[baseIndex + 5]).to.eql(6);
|
||||
expect(instructions[baseIndex + 6]).to.eql(7);
|
||||
expect(instructions[baseIndex + 7]).to.eql(8);
|
||||
expect(instructions[baseIndex + 8]).to.eql(1);
|
||||
expect(instructions[baseIndex + 9]).to.eql(10);
|
||||
expect(instructions[baseIndex + 10]).to.eql(11);
|
||||
expect(instructions[baseIndex + 11]).to.eql(12);
|
||||
expect(instructions[baseIndex + 12]).to.eql(13);
|
||||
});
|
||||
});
|
||||
|
||||
it('correctly sets indices & coordinates for several features', function() {
|
||||
const feature = {
|
||||
type: 'Feature',
|
||||
id: 'AFG',
|
||||
properties: {
|
||||
color: [0.5, 1, 0.2, 0.7],
|
||||
size: 3
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-75, 47]
|
||||
}
|
||||
};
|
||||
const attributePerVertex = 12;
|
||||
pushFeatureToBuffer(vertexBuffer, indexBuffer, feature);
|
||||
pushFeatureToBuffer(vertexBuffer, indexBuffer, feature);
|
||||
expect(vertexBuffer.getArray()[0]).to.eql(-75);
|
||||
expect(vertexBuffer.getArray()[1]).to.eql(47);
|
||||
expect(vertexBuffer.getArray()[0 + attributePerVertex]).to.eql(-75);
|
||||
expect(vertexBuffer.getArray()[1 + attributePerVertex]).to.eql(47);
|
||||
describe('writePointFeatureToBuffers', function() {
|
||||
let vertexBuffer, indexBuffer, instructions, elementIndex;
|
||||
|
||||
// first point
|
||||
expect(indexBuffer.getArray()[0]).to.eql(0);
|
||||
expect(indexBuffer.getArray()[1]).to.eql(1);
|
||||
expect(indexBuffer.getArray()[2]).to.eql(3);
|
||||
expect(indexBuffer.getArray()[3]).to.eql(1);
|
||||
expect(indexBuffer.getArray()[4]).to.eql(2);
|
||||
expect(indexBuffer.getArray()[5]).to.eql(3);
|
||||
beforeEach(function() {
|
||||
vertexBuffer = new Float32Array(100);
|
||||
indexBuffer = new Uint32Array(100);
|
||||
instructions = new Float32Array(100);
|
||||
elementIndex = 3;
|
||||
|
||||
// second point
|
||||
expect(indexBuffer.getArray()[6]).to.eql(4);
|
||||
expect(indexBuffer.getArray()[7]).to.eql(5);
|
||||
expect(indexBuffer.getArray()[8]).to.eql(7);
|
||||
expect(indexBuffer.getArray()[9]).to.eql(5);
|
||||
expect(indexBuffer.getArray()[10]).to.eql(6);
|
||||
expect(indexBuffer.getArray()[11]).to.eql(7);
|
||||
writePointFeatureInstructions(instructions, elementIndex,
|
||||
1, 2, 3, 4, 5, 6,
|
||||
7, 8, true, [10, 11, 12, 13]);
|
||||
});
|
||||
|
||||
it('correctly adds custom attributes', function() {
|
||||
const feature = {
|
||||
type: 'Feature',
|
||||
id: 'AFG',
|
||||
properties: {
|
||||
color: [0.5, 1, 0.2, 0.7],
|
||||
custom: 4,
|
||||
customString: '5',
|
||||
custom2: 12.4,
|
||||
customString2: 'abc'
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-75, 47]
|
||||
}
|
||||
};
|
||||
const attributePerVertex = 16;
|
||||
pushFeatureToBuffer(vertexBuffer, indexBuffer, feature, ['custom', 'custom2', 'customString', 'customString2']);
|
||||
expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4);
|
||||
expect(indexBuffer.getArray().length).to.eql(6);
|
||||
expect(vertexBuffer.getArray()[12]).to.eql(4);
|
||||
expect(vertexBuffer.getArray()[13]).to.eql(12.4);
|
||||
expect(vertexBuffer.getArray()[14]).to.eql(5);
|
||||
expect(vertexBuffer.getArray()[15]).to.eql(0);
|
||||
it('writes correctly to the buffers (without custom attributes)', function() {
|
||||
const stride = POINT_VERTEX_STRIDE;
|
||||
const positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer);
|
||||
|
||||
expect(vertexBuffer[0]).to.eql(1);
|
||||
expect(vertexBuffer[1]).to.eql(2);
|
||||
expect(vertexBuffer[2]).to.eql(-3.5);
|
||||
expect(vertexBuffer[3]).to.eql(-3.5);
|
||||
expect(vertexBuffer[4]).to.eql(3);
|
||||
expect(vertexBuffer[5]).to.eql(4);
|
||||
expect(vertexBuffer[6]).to.eql(8);
|
||||
expect(vertexBuffer[7]).to.eql(1);
|
||||
expect(vertexBuffer[8]).to.eql(10);
|
||||
expect(vertexBuffer[9]).to.eql(11);
|
||||
expect(vertexBuffer[10]).to.eql(12);
|
||||
expect(vertexBuffer[11]).to.eql(13);
|
||||
|
||||
expect(vertexBuffer[stride + 0]).to.eql(1);
|
||||
expect(vertexBuffer[stride + 1]).to.eql(2);
|
||||
expect(vertexBuffer[stride + 2]).to.eql(+3.5);
|
||||
expect(vertexBuffer[stride + 3]).to.eql(-3.5);
|
||||
expect(vertexBuffer[stride + 4]).to.eql(5);
|
||||
expect(vertexBuffer[stride + 5]).to.eql(4);
|
||||
|
||||
expect(vertexBuffer[stride * 2 + 0]).to.eql(1);
|
||||
expect(vertexBuffer[stride * 2 + 1]).to.eql(2);
|
||||
expect(vertexBuffer[stride * 2 + 2]).to.eql(+3.5);
|
||||
expect(vertexBuffer[stride * 2 + 3]).to.eql(+3.5);
|
||||
expect(vertexBuffer[stride * 2 + 4]).to.eql(5);
|
||||
expect(vertexBuffer[stride * 2 + 5]).to.eql(6);
|
||||
|
||||
expect(vertexBuffer[stride * 3 + 0]).to.eql(1);
|
||||
expect(vertexBuffer[stride * 3 + 1]).to.eql(2);
|
||||
expect(vertexBuffer[stride * 3 + 2]).to.eql(-3.5);
|
||||
expect(vertexBuffer[stride * 3 + 3]).to.eql(+3.5);
|
||||
expect(vertexBuffer[stride * 3 + 4]).to.eql(3);
|
||||
expect(vertexBuffer[stride * 3 + 5]).to.eql(6);
|
||||
|
||||
expect(indexBuffer[0]).to.eql(0);
|
||||
expect(indexBuffer[1]).to.eql(1);
|
||||
expect(indexBuffer[2]).to.eql(3);
|
||||
expect(indexBuffer[3]).to.eql(1);
|
||||
expect(indexBuffer[4]).to.eql(2);
|
||||
expect(indexBuffer[5]).to.eql(3);
|
||||
|
||||
expect(positions.indexPosition).to.eql(6);
|
||||
expect(positions.vertexPosition).to.eql(stride * 4);
|
||||
});
|
||||
|
||||
it('writes correctly to the buffers (with custom attributes)', function() {
|
||||
instructions[elementIndex + POINT_INSTRUCTIONS_COUNT] = 101;
|
||||
instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + 1] = 102;
|
||||
instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + 2] = 103;
|
||||
|
||||
const stride = POINT_VERTEX_STRIDE + 3;
|
||||
const positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer,
|
||||
undefined, POINT_INSTRUCTIONS_COUNT + 3);
|
||||
|
||||
expect(vertexBuffer[0]).to.eql(1);
|
||||
expect(vertexBuffer[1]).to.eql(2);
|
||||
expect(vertexBuffer[2]).to.eql(-3.5);
|
||||
expect(vertexBuffer[3]).to.eql(-3.5);
|
||||
expect(vertexBuffer[4]).to.eql(3);
|
||||
expect(vertexBuffer[5]).to.eql(4);
|
||||
expect(vertexBuffer[6]).to.eql(8);
|
||||
expect(vertexBuffer[7]).to.eql(1);
|
||||
expect(vertexBuffer[8]).to.eql(10);
|
||||
expect(vertexBuffer[9]).to.eql(11);
|
||||
expect(vertexBuffer[10]).to.eql(12);
|
||||
expect(vertexBuffer[11]).to.eql(13);
|
||||
|
||||
expect(vertexBuffer[12]).to.eql(101);
|
||||
expect(vertexBuffer[13]).to.eql(102);
|
||||
expect(vertexBuffer[14]).to.eql(103);
|
||||
|
||||
expect(vertexBuffer[stride + 12]).to.eql(101);
|
||||
expect(vertexBuffer[stride + 13]).to.eql(102);
|
||||
expect(vertexBuffer[stride + 14]).to.eql(103);
|
||||
|
||||
expect(vertexBuffer[stride * 2 + 12]).to.eql(101);
|
||||
expect(vertexBuffer[stride * 2 + 13]).to.eql(102);
|
||||
expect(vertexBuffer[stride * 2 + 14]).to.eql(103);
|
||||
|
||||
expect(vertexBuffer[stride * 3 + 12]).to.eql(101);
|
||||
expect(vertexBuffer[stride * 3 + 13]).to.eql(102);
|
||||
expect(vertexBuffer[stride * 3 + 14]).to.eql(103);
|
||||
|
||||
expect(indexBuffer[0]).to.eql(0);
|
||||
expect(indexBuffer[1]).to.eql(1);
|
||||
expect(indexBuffer[2]).to.eql(3);
|
||||
expect(indexBuffer[3]).to.eql(1);
|
||||
expect(indexBuffer[4]).to.eql(2);
|
||||
expect(indexBuffer[5]).to.eql(3);
|
||||
|
||||
expect(positions.indexPosition).to.eql(6);
|
||||
expect(positions.vertexPosition).to.eql(stride * 4);
|
||||
});
|
||||
|
||||
it('correctly chains buffer writes', function() {
|
||||
const stride = POINT_VERTEX_STRIDE;
|
||||
let positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer);
|
||||
positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, positions);
|
||||
positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, positions);
|
||||
|
||||
expect(vertexBuffer[0]).to.eql(1);
|
||||
expect(vertexBuffer[1]).to.eql(2);
|
||||
expect(vertexBuffer[2]).to.eql(-3.5);
|
||||
expect(vertexBuffer[3]).to.eql(-3.5);
|
||||
|
||||
expect(vertexBuffer[stride * 4 + 0]).to.eql(1);
|
||||
expect(vertexBuffer[stride * 4 + 1]).to.eql(2);
|
||||
expect(vertexBuffer[stride * 4 + 2]).to.eql(-3.5);
|
||||
expect(vertexBuffer[stride * 4 + 3]).to.eql(-3.5);
|
||||
|
||||
expect(vertexBuffer[stride * 8 + 0]).to.eql(1);
|
||||
expect(vertexBuffer[stride * 8 + 1]).to.eql(2);
|
||||
expect(vertexBuffer[stride * 8 + 2]).to.eql(-3.5);
|
||||
expect(vertexBuffer[stride * 8 + 3]).to.eql(-3.5);
|
||||
|
||||
expect(indexBuffer[6 + 0]).to.eql(4);
|
||||
expect(indexBuffer[6 + 1]).to.eql(5);
|
||||
expect(indexBuffer[6 + 2]).to.eql(7);
|
||||
expect(indexBuffer[6 + 3]).to.eql(5);
|
||||
expect(indexBuffer[6 + 4]).to.eql(6);
|
||||
expect(indexBuffer[6 + 5]).to.eql(7);
|
||||
|
||||
expect(indexBuffer[6 * 2 + 0]).to.eql(8);
|
||||
expect(indexBuffer[6 * 2 + 1]).to.eql(9);
|
||||
expect(indexBuffer[6 * 2 + 2]).to.eql(11);
|
||||
expect(indexBuffer[6 * 2 + 3]).to.eql(9);
|
||||
expect(indexBuffer[6 * 2 + 4]).to.eql(10);
|
||||
expect(indexBuffer[6 * 2 + 5]).to.eql(11);
|
||||
|
||||
expect(positions.indexPosition).to.eql(6 * 3);
|
||||
expect(positions.vertexPosition).to.eql(stride * 4 * 3);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getBlankTexture', function() {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import Feature from '../../../../../src/ol/Feature.js';
|
||||
import Point from '../../../../../src/ol/geom/Point.js';
|
||||
import LineString from '../../../../../src/ol/geom/LineString.js';
|
||||
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
||||
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
||||
import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer.js';
|
||||
import {get as getProjection} from '../../../../../src/ol/proj.js';
|
||||
import Polygon from '../../../../../src/ol/geom/Polygon.js';
|
||||
import ViewHint from '../../../../../src/ol/ViewHint.js';
|
||||
import {POINT_VERTEX_STRIDE, WebGLWorkerMessageType} from '../../../../../src/ol/renderer/webgl/Layer.js';
|
||||
|
||||
|
||||
describe('ol.renderer.webgl.PointsLayer', function() {
|
||||
@@ -52,44 +51,46 @@ describe('ol.renderer.webgl.PointsLayer', function() {
|
||||
projection: projection,
|
||||
resolution: 1,
|
||||
rotation: 0,
|
||||
center: [10, 10]
|
||||
center: [0, 0]
|
||||
},
|
||||
size: [256, 256],
|
||||
size: [2, 2],
|
||||
extent: [-100, -100, 100, 100]
|
||||
};
|
||||
});
|
||||
|
||||
it('calls WebGlHelper#prepareDraw', function() {
|
||||
const spy = sinon.spy(renderer.helper_, 'prepareDraw');
|
||||
const spy = sinon.spy(renderer.helper, 'prepareDraw');
|
||||
renderer.prepareFrame(frameState);
|
||||
expect(spy.called).to.be(true);
|
||||
});
|
||||
|
||||
it('fills up a buffer with 2 triangles per point', function() {
|
||||
it('fills up a buffer with 2 triangles per point', function(done) {
|
||||
layer.getSource().addFeature(new Feature({
|
||||
geometry: new Point([10, 20])
|
||||
}));
|
||||
renderer.prepareFrame(frameState);
|
||||
|
||||
const attributePerVertex = 12;
|
||||
expect(renderer.verticesBuffer_.getArray().length).to.eql(4 * attributePerVertex);
|
||||
expect(renderer.indicesBuffer_.getArray().length).to.eql(6);
|
||||
});
|
||||
|
||||
it('ignores geometries other than points', function() {
|
||||
layer.getSource().addFeature(new Feature({
|
||||
geometry: new LineString([[10, 20], [30, 20]])
|
||||
}));
|
||||
layer.getSource().addFeature(new Feature({
|
||||
geometry: new Polygon([[10, 20], [30, 20], [30, 10], [10, 20]])
|
||||
geometry: new Point([30, 40])
|
||||
}));
|
||||
renderer.prepareFrame(frameState);
|
||||
|
||||
expect(renderer.verticesBuffer_.getArray().length).to.eql(0);
|
||||
expect(renderer.indicesBuffer_.getArray().length).to.eql(0);
|
||||
const attributePerVertex = POINT_VERTEX_STRIDE;
|
||||
|
||||
renderer.worker_.addEventListener('message', function(event) {
|
||||
if (event.data.type !== WebGLWorkerMessageType.GENERATE_BUFFERS) {
|
||||
return;
|
||||
}
|
||||
expect(renderer.verticesBuffer_.getArray().length).to.eql(2 * 4 * attributePerVertex);
|
||||
expect(renderer.indicesBuffer_.getArray().length).to.eql(2 * 6);
|
||||
|
||||
expect(renderer.verticesBuffer_.getArray()[0]).to.eql(10);
|
||||
expect(renderer.verticesBuffer_.getArray()[1]).to.eql(20);
|
||||
expect(renderer.verticesBuffer_.getArray()[4 * attributePerVertex + 0]).to.eql(30);
|
||||
expect(renderer.verticesBuffer_.getArray()[4 * attributePerVertex + 1]).to.eql(40);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('clears the buffers when the features are gone', function() {
|
||||
it('clears the buffers when the features are gone', function(done) {
|
||||
const source = layer.getSource();
|
||||
source.addFeature(new Feature({
|
||||
geometry: new Point([10, 20])
|
||||
@@ -100,9 +101,15 @@ describe('ol.renderer.webgl.PointsLayer', function() {
|
||||
}));
|
||||
renderer.prepareFrame(frameState);
|
||||
|
||||
renderer.worker_.addEventListener('message', function(event) {
|
||||
if (event.data.type !== WebGLWorkerMessageType.GENERATE_BUFFERS) {
|
||||
return;
|
||||
}
|
||||
const attributePerVertex = 12;
|
||||
expect(renderer.verticesBuffer_.getArray().length).to.eql(4 * attributePerVertex);
|
||||
expect(renderer.indicesBuffer_.getArray().length).to.eql(6);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rebuilds the buffers only when not interacting or animating', function() {
|
||||
|
||||
@@ -1,51 +1,76 @@
|
||||
import WebGLArrayBuffer from '../../../../src/ol/webgl/Buffer.js';
|
||||
import WebGLArrayBuffer, {getArrayClassForType} from '../../../../src/ol/webgl/Buffer.js';
|
||||
import {
|
||||
ARRAY_BUFFER,
|
||||
ELEMENT_ARRAY_BUFFER,
|
||||
STATIC_DRAW,
|
||||
STREAM_DRAW
|
||||
} from '../../../../src/ol/webgl.js';
|
||||
|
||||
|
||||
describe('ol.webgl.Buffer', function() {
|
||||
|
||||
describe('constructor', function() {
|
||||
|
||||
describe('without an argument', function() {
|
||||
it('sets the default usage when not specified', function() {
|
||||
const b = new WebGLArrayBuffer(ARRAY_BUFFER);
|
||||
expect(b.getUsage()).to.be(STATIC_DRAW);
|
||||
});
|
||||
|
||||
it('sets the given usage when specified', function() {
|
||||
const b = new WebGLArrayBuffer(ARRAY_BUFFER, STREAM_DRAW);
|
||||
expect(b.getUsage()).to.be(STREAM_DRAW);
|
||||
});
|
||||
|
||||
it('raises an error if an incorrect type is used', function(done) {
|
||||
try {
|
||||
new WebGLArrayBuffer(1234);
|
||||
} catch (e) {
|
||||
done();
|
||||
}
|
||||
done(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getArrayClassForType', function() {
|
||||
it('returns the correct typed array constructor', function() {
|
||||
expect(getArrayClassForType(ARRAY_BUFFER)).to.be(Float32Array);
|
||||
expect(getArrayClassForType(ELEMENT_ARRAY_BUFFER)).to.be(Uint32Array);
|
||||
});
|
||||
});
|
||||
|
||||
describe('populate methods', function() {
|
||||
|
||||
let b;
|
||||
beforeEach(function() {
|
||||
b = new WebGLArrayBuffer();
|
||||
b = new WebGLArrayBuffer(ARRAY_BUFFER);
|
||||
});
|
||||
|
||||
it('constructs an empty instance', function() {
|
||||
expect(b.getArray()).to.be.empty();
|
||||
it('initializes the array using a size', function() {
|
||||
b.ofSize(12);
|
||||
expect(b.getArray().length).to.be(12);
|
||||
expect(b.getArray()[0]).to.be(0);
|
||||
expect(b.getArray()[11]).to.be(0);
|
||||
});
|
||||
|
||||
it('initializes the array using an array', function() {
|
||||
b.fromArray([1, 2, 3, 4, 5]);
|
||||
expect(b.getArray().length).to.be(5);
|
||||
expect(b.getArray()[0]).to.be(1);
|
||||
expect(b.getArray()[1]).to.be(2);
|
||||
expect(b.getArray()[2]).to.be(3);
|
||||
expect(b.getArray()[3]).to.be(4);
|
||||
expect(b.getArray()[4]).to.be(5);
|
||||
});
|
||||
|
||||
describe('with a single array argument', function() {
|
||||
|
||||
let b;
|
||||
beforeEach(function() {
|
||||
b = new WebGLArrayBuffer([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
it('constructs a populated instance', function() {
|
||||
expect(b.getArray()).to.eql([0, 1, 2, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an empty instance', function() {
|
||||
|
||||
let b;
|
||||
beforeEach(function() {
|
||||
b = new WebGLArrayBuffer();
|
||||
});
|
||||
|
||||
describe('getArray', function() {
|
||||
|
||||
it('returns an empty array', function() {
|
||||
expect(b.getArray()).to.be.empty();
|
||||
});
|
||||
|
||||
it('initializes the array using a size', function() {
|
||||
const a = Float32Array.of(1, 2, 3, 4, 5);
|
||||
b.fromArrayBuffer(a.buffer);
|
||||
expect(b.getArray().length).to.be(5);
|
||||
expect(b.getArray()[0]).to.be(1);
|
||||
expect(b.getArray()[1]).to.be(2);
|
||||
expect(b.getArray()[2]).to.be(3);
|
||||
expect(b.getArray()[3]).to.be(4);
|
||||
expect(b.getArray()[4]).to.be(5);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -213,7 +213,10 @@ describe('ol.webgl.WebGLHelper', function() {
|
||||
scaleTransform(expected, scaleX, scaleY);
|
||||
rotateTransform(expected, -frameState.viewState.rotation);
|
||||
translateTransform(expected, -frameState.viewState.center[0], -frameState.viewState.center[1]);
|
||||
expect(h.makeProjectionTransform(frameState, given)).to.eql(expected);
|
||||
|
||||
h.makeProjectionTransform(frameState, given);
|
||||
|
||||
expect(given.map(val => val.toFixed(15))).to.eql(expected.map(val => val.toFixed(15)));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
51
test/spec/ol/worker/webgl.test.js
Normal file
51
test/spec/ol/worker/webgl.test.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import {create} from '../../../../src/ol/worker/webgl.js';
|
||||
import {
|
||||
POINT_INSTRUCTIONS_COUNT,
|
||||
WebGLWorkerMessageType
|
||||
} from '../../../../src/ol/renderer/webgl/Layer.js';
|
||||
|
||||
|
||||
describe('ol/worker/webgl', function() {
|
||||
|
||||
let worker;
|
||||
beforeEach(function() {
|
||||
worker = create();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (worker) {
|
||||
worker.terminate();
|
||||
}
|
||||
worker = null;
|
||||
});
|
||||
|
||||
describe('messaging', function() {
|
||||
describe('GENERATE_BUFFERS', function() {
|
||||
it('responds with buffer data', function(done) {
|
||||
worker.addEventListener('error', done);
|
||||
|
||||
worker.addEventListener('message', function(event) {
|
||||
expect(event.data.type).to.eql(WebGLWorkerMessageType.GENERATE_BUFFERS);
|
||||
expect(event.data.renderInstructions.byteLength).to.greaterThan(0);
|
||||
expect(event.data.indexBuffer.byteLength).to.greaterThan(0);
|
||||
expect(event.data.vertexBuffer.byteLength).to.greaterThan(0);
|
||||
expect(event.data.testInt).to.be(101);
|
||||
expect(event.data.testString).to.be('abcd');
|
||||
done();
|
||||
});
|
||||
|
||||
const instructions = new Float32Array(POINT_INSTRUCTIONS_COUNT);
|
||||
|
||||
const message = {
|
||||
type: WebGLWorkerMessageType.GENERATE_BUFFERS,
|
||||
renderInstructions: instructions,
|
||||
testInt: 101,
|
||||
testString: 'abcd'
|
||||
};
|
||||
|
||||
worker.postMessage(message);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
import {equals} from '../src/ol/array.js';
|
||||
import {WEBGL} from '../src/ol/has.js';
|
||||
// avoid importing anything that results in an instanceof check
|
||||
// since these extensions are global, instanceof checks fail with modules
|
||||
|
||||
@@ -376,12 +375,6 @@ import {WEBGL} from '../src/ol/has.js';
|
||||
map.dispose();
|
||||
};
|
||||
|
||||
global.assertWebGL = function(map) {
|
||||
if (!WEBGL) {
|
||||
expect().fail('No WebGL support!');
|
||||
}
|
||||
};
|
||||
|
||||
function resembleCanvas(canvas, referenceImage, tolerance, done) {
|
||||
if (showMap) {
|
||||
const wrapper = document.createElement('div');
|
||||
@@ -443,21 +436,7 @@ import {WEBGL} from '../src/ol/has.js';
|
||||
return;
|
||||
}
|
||||
|
||||
let canvas;
|
||||
if (event.glContext) {
|
||||
const webglCanvas = event.glContext.getCanvas();
|
||||
expect(webglCanvas).to.be.a(HTMLCanvasElement);
|
||||
|
||||
// draw the WebGL canvas on a new canvas, because we can not create
|
||||
// a 2d context for that canvas because there is already a webgl context.
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.width = webglCanvas.width;
|
||||
canvas.height = webglCanvas.height;
|
||||
canvas.getContext('2d').drawImage(webglCanvas, 0, 0,
|
||||
webglCanvas.width, webglCanvas.height);
|
||||
} else {
|
||||
canvas = event.context.canvas;
|
||||
}
|
||||
const canvas = event.context.canvas;
|
||||
expect(canvas).to.be.a(HTMLCanvasElement);
|
||||
|
||||
resembleCanvas(canvas, referenceImage, tolerance, done);
|
||||
|
||||
Reference in New Issue
Block a user