Compare commits

...

103 Commits

Author SHA1 Message Date
ahocevar
5cc1ff0f45 Use beta tag 2019-06-24 09:54:41 +02:00
ahocevar
2df5f85018 Set version to 6.0.0-beta.10 2019-06-24 09:52:07 +02:00
Andreas Hocevar
4a0d6ce43c Merge pull request #9705 from ahocevar/vectortile-render-optimizations
Vector tile renderer optimizations and fixes
2019-06-24 09:50:56 +02:00
Andreas Hocevar
187969cbb3 Merge pull request #9706 from ahocevar/non-browser-screen-check
Do not use self to check browser features
2019-06-24 09:48:59 +02:00
ahocevar
8cdfc6e17c Do not use self to check browser features 2019-06-23 14:26:40 +02:00
ahocevar
9cd35d67a9 Fix clipping and clearing 2019-06-23 14:23:32 +02:00
ahocevar
4d2fa476a3 Simpler z sort 2019-06-23 14:22:56 +02:00
ahocevar
f864c05070 Less frame budget restrictions 2019-06-23 14:07:22 +02:00
ahocevar
3557271e5a Use Image.prototype.decode also in Safari 2019-06-23 14:06:14 +02:00
Andreas Hocevar
8a49e06ebd Merge pull request #9704 from ahocevar/vectortile-reuse-issue
Handle container reuse properly when layers are added/removed
2019-06-22 19:33:59 +02:00
ahocevar
6783f6adb9 Handle container reuse properly when layers are added/removed 2019-06-22 19:12:39 +02:00
Tim Schaub
5eb27f7704 Merge pull request #9703 from openlayers/greenkeeper/eslint-6.0.0
Update eslint to the latest version 🚀
2019-06-22 07:13:34 -06:00
greenkeeper[bot]
df240fe905 chore(package): update eslint to version 6.0.0 2019-06-22 04:08:47 +00:00
Frédéric Junod
300522e3cc Merge pull request #9701 from fredj/overlay_will-change
Remove will-change CSS rule for Overlay container
2019-06-21 16:36:27 +02:00
Tim Schaub
3ede6e32be Merge pull request #9702 from lutzhelm/typecheck-iiif
Fix several TypeScript type check problems
2019-06-21 07:53:59 -06:00
Tim Schaub
ce45a10063 Merge pull request #9700 from KaiVolland/9692-typescript
Solves typescript related issues
2019-06-21 07:48:15 -06:00
Tim Schaub
2438ef167b Merge pull request #9699 from petrsloup/maptiler-url-fix
Fix MapTiler API usage
2019-06-21 07:46:42 -06:00
Lutz Helm
d6dc7a926d Fix several TypeScript type check problems 2019-06-21 15:26:28 +02:00
Frederic Junod
6e6a2cae06 Use version 4 TileJSON from mapbox 2019-06-21 14:33:26 +02:00
Frederic Junod
a6ebad008f Remove will-change CSS rule for Overlay container
Fixes #9467
Fixes #9139

Chrome doesn't force a composition if the `will-change` CSS rule is different from `transform` or `opacity`.
See https://bugs.chromium.org/p/chromium/issues/detail?id=960953
2019-06-21 14:22:46 +02:00
Kai Volland
ed5d4aaf6d Fixes typing issue in mat4.js 2019-06-21 13:35:40 +02:00
Kai Volland
f507efe77d Fixes typing issue in Composite.js 2019-06-21 13:31:27 +02:00
Kai Volland
83173cd385 Fixes typing issue in VectorTile.js 2019-06-21 13:30:35 +02:00
Kai Volland
b6b91abc27 Fixes typing issue in DragPan.js 2019-06-21 13:28:56 +02:00
Petr Sloup
05c791efcd Fix MapTiler API usage 2019-06-21 11:22:24 +02:00
Tim Schaub
2342626a57 Merge pull request #9698 from openlayers/greenkeeper/puppeteer-1.18.0
Update puppeteer to the latest version 🚀
2019-06-20 23:33:59 -06:00
greenkeeper[bot]
7c0a0a4786 chore(package): update puppeteer to version 1.18.0 2019-06-20 22:24:07 +00:00
Frédéric Junod
a35ca03e07 Merge pull request #9636 from fredj/rm_from_webgl
Remove and move code from ol/webgl
2019-06-20 16:16:27 +02:00
Tim Schaub
c522454af1 Merge pull request #9695 from openlayers/greenkeeper/webpack-4.35.0
Update webpack to the latest version 🚀
2019-06-20 07:16:29 -06:00
Frederic Junod
ef10834eb3 Remove unused constants from ol/webgl
And change the `EXTENSIONS` constant to a function
2019-06-20 10:25:22 +02:00
greenkeeper[bot]
8efad7bcff chore(package): update webpack to version 4.35.0 2019-06-20 05:08:42 +00:00
Andreas Hocevar
51c8886d60 Merge pull request #9684 from ahocevar/doc-nav
List complete classes in doc navigation
2019-06-17 15:37:19 +02:00
Frédéric Junod
34a8702202 Merge pull request #9685 from fredj/rm_glContext
Remove glContext from RenderEvent
2019-06-17 14:10:51 +02:00
Frederic Junod
1f00da8d83 Remove glContext from RenderEvent 2019-06-17 13:52:56 +02:00
ahocevar
8d793ad6ec List complete classes in doc navigation 2019-06-17 13:10:02 +02:00
Andreas Hocevar
bd8a7bff16 Merge pull request #9681 from ahocevar/apidoc-fixed-and-improved
Remove duplicates from api docs and make navigation more usable
2019-06-17 08:27:48 +02:00
ahocevar
d059689856 Index classes under modulues 2019-06-17 08:15:49 +02:00
ahocevar
4ea28333f8 Avoid duplicates in api docs 2019-06-17 08:15:17 +02:00
Andreas Hocevar
edebf35135 Merge pull request #9677 from ahocevar/featureformat-tilepixels
Add support for tile pixel projection in feature formats
2019-06-15 08:22:28 +02:00
Frédéric Junod
90b645daa5 Merge pull request #9679 from fredj/cleanup
Set pixelTransform and inversePixelTransform as protected
2019-06-15 07:53:15 +02:00
Andreas Hocevar
9eac927b45 Merge pull request #9678 from ahocevar/geography-class
Update geography class urls
2019-06-14 23:20:47 +02:00
Frederic Junod
ec5e8bfa50 Set pixelTransform and inversePixelTransform as protected
The variables are used in child classes
2019-06-14 14:52:28 +02:00
ahocevar
124e984a6d Fix and simplify geojson-vt example 2019-06-14 14:28:37 +02:00
ahocevar
76f44efbb6 Add support for tile pixels to feature formats 2019-06-14 14:28:37 +02:00
ahocevar
ddaaf6a860 Update geography class urls 2019-06-14 12:53:52 +02:00
Tim Schaub
92c16ecdf5 Merge pull request #9673 from openlayers/greenkeeper/webpack-4.34.0
Update webpack to the latest version 🚀
2019-06-12 19:15:28 -06:00
greenkeeper[bot]
635d7a9189 chore(package): update webpack to version 4.34.0 2019-06-12 23:40:39 +00:00
Tim Schaub
fc6ca5eaf3 Merge pull request #9660 from openlayers/greenkeeper/pixelmatch-5.0.0
Update pixelmatch to the latest version 🚀
2019-06-07 04:08:01 -06:00
greenkeeper[bot]
803f960ded chore(package): update pixelmatch to version 5.0.0 2019-06-07 07:52:43 +00:00
Frédéric Junod
ef1b672323 Merge pull request #9658 from fredj/f9628
Don't use HTMLImageElement.decode method on Safari
2019-06-06 16:34:57 +02:00
Frederic Junod
002dc34455 Don't use HTMLImageElement.decode method on Safari
The decode function is supported by Safari but not when the image is a svg file.
2019-06-06 16:15:36 +02:00
Frédéric Junod
cc8389d588 Merge pull request #9657 from fredj/getFeaturesInExtent_doc
Better documentation for getFeaturesInExtent function
2019-06-06 14:23:11 +02:00
Frederic Junod
a8e0e4e960 Better documentation for getFeaturesInExtent function 2019-06-06 14:08:25 +02:00
Olivier Guyot
9ae7256048 Merge pull request #9562 from jahow/webgl-worker
Optimize the WebGL points renderer using a worker
2019-06-06 08:48:29 +02:00
Frederic Junod
e036767746 Use version 4 TileJSON from mapbox 2019-06-06 08:34:31 +02:00
Frédéric Junod
9a49c91fdd Merge pull request #9653 from openlayers/greenkeeper/coveralls-3.0.4
Update coveralls to the latest version 🚀
2019-06-06 08:19:45 +02:00
greenkeeper[bot]
37ff7f547b chore(package): update coveralls to version 3.0.4 2019-06-06 00:10:14 +00:00
Marc Jansen
c97a728531 Merge pull request #9394 from KaiVolland/docs-import-functions
Introduces import snippets for static functions
2019-06-05 22:20:15 +02:00
Olivier Guyot
811bff0430 Merge pull request #9638 from agpixdev/translate-interaction-filter
#9625: Add filter function to translate interaction
2019-06-05 15:32:29 +02:00
Olivier Guyot
2412fe0211 Webgl / remove handling of element_index_uint extension
From now on we will assume this extension is always enabled.

An error message have been added in the unlikely scenario of a lack
of support.
2019-06-05 14:46:15 +02:00
Tim Schaub
32084a7fce Merge pull request #9649 from openlayers/greenkeeper/webpack-4.33.0
Update webpack to the latest version 🚀
2019-06-04 20:11:55 -07:00
André Garneau
7817cf31c6 Changes following code review
Add a type for FilterFunction and add tests for filter option.
2019-06-04 17:45:28 -04:00
greenkeeper[bot]
2e5aac8dba chore(package): update webpack to version 4.33.0 2019-06-04 20:02:28 +00:00
Frédéric Junod
8b08996703 Merge pull request #9644 from fredj/webgl_layer_compose
Use compose function from ol/transform
2019-06-04 15:35:05 +02:00
Andreas Hocevar
1fbbc56f93 Merge pull request #9645 from ahocevar/no-used-tiles
Handle case of no used tiles
2019-06-04 15:26:27 +02:00
ahocevar
8f77a300de Handle case of no used tiles 2019-06-04 14:07:23 +02:00
Frederic Junod
91d49b26b5 Round the transform values in makeProjectionTransform test 2019-06-04 14:00:39 +02:00
Frederic Junod
02ce2f4bec Use compose function from ol/transform 2019-06-04 13:23:12 +02:00
Andreas Hocevar
d4f98c8e59 Merge pull request #9640 from ahocevar/base-docs
Import options directly instead of importing a typedef
2019-06-04 09:30:38 +02:00
Andreas Hocevar
ab0860a22a Merge pull request #9641 from ahocevar/less-proj-imports
Only import what's needed from projections
2019-06-04 09:27:05 +02:00
ahocevar
dd478167a0 Only import what's needed from projections 2019-06-04 09:14:08 +02:00
ahocevar
2df911e0a8 Import options directly instead of importing a typedef 2019-06-04 08:41:08 +02:00
Andreas Hocevar
e9939ecc38 Merge pull request #9637 from ahocevar/detach-labelcache-on-settarget
Detach label cache on Map#setTarget()
2019-06-03 16:03:30 +02:00
Frédéric Junod
d058439756 Merge pull request #9633 from fredj/rm_typecast
Mark the `layers` property as optional in OverviewMap constructor
2019-06-03 15:46:23 +02:00
ahocevar
8c21c9196d Move label cache listener management to the renderer 2019-06-03 15:13:30 +02:00
ahocevar
68b7831daf Listen to label cache again when we have a target 2019-06-03 14:28:20 +02:00
ahocevar
153e06e4d5 Detach label cache on Map#setTarget() 2019-06-03 14:14:13 +02:00
Andreas Hocevar
9bedfb7cb0 Merge pull request #9634 from ahocevar/remove-old-android-workaround
Remove old Android workaround
2019-06-03 10:17:44 +02:00
ahocevar
1bd23a0c32 Remove old Android workaround 2019-06-03 10:03:53 +02:00
Frederic Junod
50343afa61 Remove unnecessary typecasts in constructors options 2019-06-03 09:08:41 +02:00
Frederic Junod
a9e1ebccd3 Mark the layers property as optional in OverviewMap constructor 2019-06-03 09:07:20 +02:00
Andreas Hocevar
9672142c1e Merge pull request #9629 from ahocevar/reuse-vectortile-overlay
Clear overlay canvas when reusing containers
2019-06-02 18:20:24 +02:00
ahocevar
6123be726a Handle layer removal on shared containers 2019-06-02 13:59:06 +02:00
ahocevar
05d8517686 Clear overlay canvas when reusing containers 2019-06-02 11:07:30 +02:00
Frederic Junod
87d5f4c8bc Use 'helper' from WebGLHelper in tests 2019-06-01 15:35:31 +02:00
Frederic Junod
06be00bbd5 Webgl Helper / add an API for OESElementIndexUint extension 2019-06-01 15:35:27 +02:00
Frederic Junod
7fb113c3dc Mark 'helper' variable protected 2019-06-01 15:33:36 +02:00
Olivier Guyot
98b0c65450 Rendering tests / add custom web worker loader
Also includes a change in the rollup-babel plugin to avoid
adding helpers as dependencies (which would give out errors).
2019-06-01 15:33:36 +02:00
Olivier Guyot
698816030e Webgl points / fix unit tests 2019-06-01 15:33:36 +02:00
Olivier Guyot
03e70bd10e Webgl points / handle using short instead of int for indices
This is controlled by the availability of the OES_element_index_uint
webgl extension.
2019-06-01 15:33:36 +02:00
Olivier Guyot
e0983cb1c6 Webgl worker / add tests, some typing and documentation
The worker currently works by receiving GENERATE_BUFFERS messages and
will send back the same kind of message, with the generated buffers
attached. All properties of the original message are kept, so that
when a GENERATE_BUFFERS message comes back to the main thread it
is possible to know what and how the buffers where generated.

This is typically used for the `projectionTransform` matrix, and
will also be necessary when working with tiles.
2019-06-01 15:33:36 +02:00
Olivier Guyot
a366803cdd Webgl / use the new buffer API in helper & the points renderer 2019-06-01 15:33:36 +02:00
Olivier Guyot
33d007ce01 Webgl buffer / now stores data in typed arrays
The `WebGLBuffer` class API was changed in order to allow populating
the internal array in different ways.
2019-06-01 15:33:31 +02:00
Olivier Guyot
8566cfc227 Webgl points / get back the instructions array from the worker
This means we won't have to recreate a binary buffer (through a typed array)
on every `rebuildBuffer` call.
2019-06-01 15:25:24 +02:00
Olivier Guyot
65be907095 Webgl points / shifts the buffer write logic in a worker
The worker receives a transferable array of instructions
and sends back two transferable arrays (vertex and index buffer).
The projection transform is also sent so that when the main thread
receives the buffers from the worker it also knows which projection to
apply when rendering the geometries.
2019-06-01 15:25:11 +02:00
Olivier Guyot
532b8194b1 Webgl points / use the new typed-array based utils for buffers
`rebuildBuffer` is still a very CPU intensive task and is blocking the
main thread.
2019-06-01 15:20:04 +02:00
Olivier Guyot
eb912d95ca Webgl / refactor utilities to work only on typed arrays
The base webgl renderer module now has two types of utilities:
* `writeXFeatureInstructions` will write a series of values in a given
typed array, which represent how a given feature will be rendered; for points,
this means position, size, color, etc.
* `writeXFeatureToBuffers` will fill up the given index & vertex buffers
with values based on the provided render instructions

As such, the logic for rendering features is:
user-input style > instructions array >(*) index/vertex buffers > draw

(*) this transformation is intended to be done on a worker.
2019-06-01 15:19:37 +02:00
Frédéric Junod
245ded43d4 Merge pull request #9626 from fredj/rm_from_has
Remove TOUCH, POINTER and MSPOINTER from ol/has
2019-06-01 12:09:40 +01:00
Frederic Junod
7c215b2532 Remove TOUCH, POINTER and MSPOINTER from ol/has 2019-06-01 07:18:23 +02:00
Frédéric Junod
579d05a0cb Merge pull request #9624 from fredj/cleanup
Remove typecasts in ol/control/OverviewMap
2019-06-01 06:13:02 +01:00
André Garneau
b42ee8ca0f Add filter function to translate interaction
Add a filter function to the translate interaction. This filter is similar to the one present in the select interaction options and provides the ability to dynamically filter which features to include in the translate interaction. Adding the existing "features" options allows further filtering to take place. In this case the filter is first applied, and features that are passing the filtering need to be present in the "features" options to be further kept. The default filter function always return true (no filtering).
2019-05-31 14:51:13 -04:00
Frederic Junod
ede864c823 Remove typecasts in ol/control/OverviewMap
The `options` type was not correctly detected by the typescript compiler.
2019-05-31 16:18:15 +02:00
Kai Volland
9860699d5f Introduces import snippets for static functions
Adds snippets for import of modules and their
exported functions to the API documentation.
2019-04-04 10:07:55 +02:00
80 changed files with 1170 additions and 854 deletions

View File

@@ -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) {

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}
});

View File

@@ -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);

View File

@@ -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 } ?>

View File

@@ -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 ?>

View File

@@ -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>

View File

@@ -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.

View File

@@ -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({

View File

@@ -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

View File

@@ -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: ''
})
});

View File

@@ -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>

View File

@@ -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'
})
}),

View File

@@ -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: ''
})
});

View File

@@ -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>

View File

@@ -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,

View File

@@ -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">

View File

@@ -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');

View File

@@ -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">

View File

@@ -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'
})
})

View File

@@ -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'
})
})

View File

@@ -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

View File

@@ -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",

View File

@@ -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')
]
}]
}
};

View File

@@ -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;
}

View File

@@ -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();
}
/**

View File

@@ -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_);
}
}

View File

@@ -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');

View File

@@ -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
};
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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_

View File

@@ -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();

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 ||

View File

@@ -63,10 +63,6 @@
border: 1px solid black;
}
.ol-overlay-container {
will-change: left,right,top,bottom;
}
.ol-unsupported {
display: none;
}

View File

@@ -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));
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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_;
}
/**

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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]);

View File

@@ -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,

View File

@@ -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 || {};

View File

@@ -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).
*

View File

@@ -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,

View File

@@ -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];

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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
View 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;

View File

@@ -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

View File

@@ -20,6 +20,7 @@ async function build(input, {minify = true} = {}) {
common(),
resolve(),
babel({
'externalHelpers': true,
'presets': [
[
'@babel/preset-env',

View File

@@ -5,7 +5,6 @@
"globals": {
"IMAGE_TOLERANCE": false,
"afterLoadText": false,
"assertWebGL": false,
"createMapDiv": true,
"disposeMap": true,
"expect": false,

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);
});

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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);
});
});

View File

@@ -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)));
});
});

View 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);
});
});
});
});

View File

@@ -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);