Compare commits

...

172 Commits

Author SHA1 Message Date
ahocevar
fbe815853d Publish with beta tag 2019-05-30 15:58:19 +02:00
ahocevar
a50ef05565 Set version to 6.0.0-beta.9 2019-05-30 15:55:09 +02:00
Tim Schaub
93a607d846 Merge pull request #9615 from tschaub/import-specifiers
Include full filename in import specifier
2019-05-29 09:35:34 -06:00
Tim Schaub
deb00f20fe Include full filename in import specifier 2019-05-29 08:51:10 -06:00
Frédéric Junod
c4e465dcf9 Merge pull request #9592 from fredj/f9587_layerIndex
Remove `layerState` param from `prepareFrame` and `renderFrame` function
2019-05-29 09:27:38 +02:00
Frederic Junod
bfea858306 Add missing extension in import 2019-05-29 09:19:17 +02:00
Frédéric Junod
c92bf29677 Fix spelling
Co-Authored-By: Tim Schaub <tschaub@users.noreply.github.com>
2019-05-29 09:07:05 +02:00
Andreas Hocevar
6675f9be57 Merge pull request #9289 from sbrunner/vector-source-geom
Vector source geom
2019-05-28 11:23:31 +02:00
Frédéric Junod
1a6d67776b Merge pull request #9608 from fredj/md_link
Use markdown link syntax in jsdoc
2019-05-27 14:17:18 +02:00
Stéphane Brunner
889b6a9f43 Removes unnecessary type cast 2019-05-27 11:34:33 +02:00
Stéphane Brunner
970c1bcb66 Make the vector source geometry generic 2019-05-27 11:34:31 +02:00
Frederic Junod
2db953ceb1 Use markdown link syntax in jsdoc 2019-05-27 11:06:12 +02:00
Olivier Guyot
051cc68f24 Merge pull request #9596 from jahow/fix-source-missing-event
Vector source / prevent adding features with duplicate id in the collection
2019-05-26 10:53:58 +02:00
Frederic Junod
40c49a9ce5 Remove layerState param from prepareFrame and renderFrame function 2019-05-25 07:19:10 +02:00
Olivier Guyot
442fa907ce Vector source / prevent adding features with duplicate id...
...in the collection.

Previously two features with the same id could be pushed manually in the
features collection and stay there.

This would cause an error when clearing the source.

Fixes #6183.
2019-05-24 23:29:48 +02:00
Bart van den Eijnden
7044e30529 Merge pull request #9604 from bartvde/window-global
Use window instead of global in addEventListener
2019-05-24 15:17:07 +02:00
Frédéric Junod
7cfe047feb Merge pull request #9599 from openlayers/github_funding
Create FUNDING.yml
2019-05-24 15:14:26 +02:00
Tim Schaub
796f20385d Merge pull request #9598 from openlayers/greenkeeper/puppeteer-1.17.0
Update puppeteer to the latest version 🚀
2019-05-24 06:58:00 -06:00
bartvde
c76aa76743 Use window instead of global in addEventListener 2019-05-24 14:51:32 +02:00
Bart van den Eijnden
2969c9e8fb Merge pull request #9601 from bartvde/fix-addeventlistener2
Use global addEventListener in PluggableMap
2019-05-24 14:11:26 +02:00
bartvde
119c24faa2 Use global addEventListener in PluggableMap 2019-05-24 13:45:19 +02:00
Frédéric Junod
93711ea806 Create FUNDING.yml 2019-05-24 11:19:37 +02:00
Andreas Hocevar
96331c5d95 Merge pull request #9584 from ahocevar/reuse-render-target
Reuse render target
2019-05-24 10:33:36 +02:00
ahocevar
d8f41a9d73 Only reuse target when className is the same 2019-05-24 10:16:20 +02:00
greenkeeper[bot]
311900e441 chore(package): update puppeteer to version 1.17.0 2019-05-24 00:18:21 +00:00
ahocevar
bdb87f06f9 Rework tile image render queue 2019-05-23 15:55:29 +02:00
Frédéric Junod
ddb7da3733 Merge pull request #6217 from fredj/dragpan_primary
Add primaryAction condition to DragPan
2019-05-23 09:50:55 +02:00
Tim Schaub
a358521b2b Merge pull request #9594 from openlayers/greenkeeper/webpack-4.32.2
Update webpack to the latest version 🚀
2019-05-22 21:12:52 -06:00
greenkeeper[bot]
4f833501d7 chore(package): update webpack to version 4.32.2 2019-05-22 23:39:55 +00:00
ahocevar
ace5c65ee5 Smarter opacity handling 2019-05-22 16:47:23 +02:00
ahocevar
ae47d3df58 Use opacity from layer state 2019-05-22 16:47:22 +02:00
ahocevar
4c8effe6fa No tile transition when layer opacity is set 2019-05-22 16:47:22 +02:00
ahocevar
c56ad4363d Canvas opacity instead of css 2019-05-22 16:47:22 +02:00
ahocevar
7895b16043 Reuse container for raster and vector image layers 2019-05-22 16:47:20 +02:00
ahocevar
a45e704be2 Smarter reuse detection 2019-05-22 16:46:46 +02:00
ahocevar
d1f1b468b1 Reuse target for image layers 2019-05-22 16:46:46 +02:00
ahocevar
a55505b36a Reuse containers for tile, vector and vector tile layers 2019-05-22 16:46:40 +02:00
ahocevar
930318ab7a Clip high res tiles out of low res tiles only once 2019-05-22 15:57:15 +02:00
ahocevar
5fe9e06535 Use clipping to not render high res tiles on top of low res 2019-05-22 15:57:15 +02:00
ahocevar
606443bc6d Pass render target of previous layer to next layer 2019-05-22 15:56:51 +02:00
Frederic Junod
ec811bfa1f Add primaryAction condition to DragPan interaction 2019-05-22 15:21:17 +02:00
Tim Schaub
5e8d7f666e Merge pull request #9589 from openlayers/greenkeeper/webpack-4.32.1
Update webpack to the latest version 🚀
2019-05-22 06:35:21 -06:00
greenkeeper[bot]
4f9057f066 chore(package): update webpack to version 4.32.1 2019-05-22 06:59:26 +00:00
Tim Schaub
4ed6413635 Merge pull request #9586 from openlayers/greenkeeper/rollup-plugin-terser-5.0.0
Update rollup-plugin-terser to the latest version 🚀
2019-05-21 18:34:20 -06:00
greenkeeper[bot]
bb5ebaa1ab chore(package): update rollup-plugin-terser to version 5.0.0 2019-05-21 22:32:01 +00:00
Tim Schaub
c9491ed023 Merge pull request #9569 from engsterhold/patch-1
Fix for change in Zoomify.js/CustomTile signature
2019-05-21 06:52:51 -06:00
Tim Schaub
3db13fa3bb Merge pull request #9582 from tschaub/stale
Add app to mark inactive issues as stale
2019-05-21 06:49:56 -06:00
robert
7e4e113ac9 lint fix
Signed-off-by: robert <24939343+engsterhold@users.noreply.github.com>
2019-05-21 12:32:30 +02:00
Tim Schaub
dc4ce62fab Merge pull request #9581 from tschaub/update-webpack
Update webpack and use mode in the loader
2019-05-20 19:43:37 -06:00
Tim Schaub
6288744d70 Configure Stale app
See https://github.com/organizations/openlayers/settings/installations/1008384
2019-05-20 17:56:30 -06:00
Tim Schaub
c0339f167b Use mode from the loader context
See https://github.com/webpack/webpack/pull/9140.
2019-05-20 10:33:59 -06:00
greenkeeper[bot]
60007d8e3c chore(package): update webpack to version 4.32.0 2019-05-20 10:33:20 -06:00
Frédéric Junod
2322131b01 Merge pull request #9576 from fredj/has-geolocation
Remove geolocation detection from ol/has
2019-05-18 09:40:21 +02:00
Frederic Junod
39bb6a8ffe Remove geolocation detection from ol/has 2019-05-18 08:50:33 +02:00
Tim Schaub
0631a121c3 Merge pull request #9575 from tschaub/untwisted-exports
Rework exports for getUid and control defaults
2019-05-17 13:03:14 -06:00
Tim Schaub
c860f15f91 Break the circular dependency by extending a pluggable map 2019-05-17 12:38:32 -06:00
Tim Schaub
c10fd7aaab Avoid overwriting longname for getUid 2019-05-17 12:00:10 -06:00
Andreas Hocevar
7ced251d19 Merge pull request #9574 from jumpinjackie/update-mapguide-demo-server
Update OL MapGuide example to point to my demo MapGuide Server.
2019-05-17 19:49:39 +02:00
Frédéric Junod
637c823fa6 Merge pull request #9571 from fredj/misc_examples
Small examples improvements
2019-05-17 17:00:58 +02:00
Frederic Junod
2d655143c3 Add an Overlay in the export-map example 2019-05-17 16:45:29 +02:00
Jackie Ng
fba95ea3f0 Update OL MapGuide example to point to my demo MapGuide Server.
Fixes #9573
2019-05-18 00:21:53 +10:00
Frédéric Junod
40393cd9a4 Merge pull request #9572 from fredj/upgrade-notes_css
Add a note about CSS removal in upgrade-notes.md
2019-05-17 15:44:31 +02:00
Frederic Junod
10527bd221 Remove margin in full screen
In only affects Safari, in Chrome `margin: 0;` is in the default browser's style sheet.
2019-05-17 15:41:03 +02:00
Frederic Junod
4c2b11f6d7 Add a note about CSS removal in upgrade-notes.md 2019-05-17 15:21:51 +02:00
Frédéric Junod
512a39bafd Merge pull request #9570 from fredj/rm_media_print
Remove CSS print rules in ol.css
2019-05-17 14:58:55 +02:00
Frederic Junod
78c09a0ff6 Add svg icon example 2019-05-17 14:48:03 +02:00
Frederic Junod
6dc00b75e0 Update d3 version in d3 example 2019-05-17 14:27:20 +02:00
Frederic Junod
6b50f1555b Add webgl tag to heatmap layer example 2019-05-17 14:25:34 +02:00
Frederic Junod
a6f098f78b Remove CSS print rules in ol.css
Fixes #7909
2019-05-17 14:21:19 +02:00
engsterhold
f77b0941b8 fixed styling 2019-05-17 13:52:10 +02:00
Tim Schaub
6d06f81ca7 Merge pull request #9563 from tschaub/fragile-docs
Fix @abstract and @module annotations
2019-05-16 16:27:09 -06:00
Tim Schaub
908ecb39e3 Description below @abstract trips up JSDoc 2019-05-16 16:10:30 -06:00
Tim Schaub
6cad19e9d5 Description below @module trips up JSDoc 2019-05-16 16:09:30 -06:00
ahocevar
b0fae46aa6 Do not use css z-index 2019-05-16 22:29:32 +02:00
Andreas Hocevar
17d26acb2f Merge pull request #9560 from ahocevar/simplify-loading
Simplify loading detection loop
2019-05-16 22:27:59 +02:00
Andreas Hocevar
579fadd797 Merge pull request #9561 from ahocevar/map-memory-leak
Remove memory leak caused by label cache listeners
2019-05-16 22:26:56 +02:00
Tim Schaub
9693336d99 Merge pull request #9559 from tschaub/minify-worker
Minify worker
2019-05-16 14:20:11 -06:00
ahocevar
335648d613 Remove memory leak caused by label cache listeners 2019-05-16 21:04:55 +02:00
ahocevar
dc906f79e1 Simplify loading detection loop 2019-05-16 20:19:32 +02:00
Tim Schaub
34a8a484c4 Merge pull request #9558 from tschaub/unlisten
Stop listening for image decoding
2019-05-16 11:46:15 -06:00
Tim Schaub
0d489f2ea9 Minify worker in examples for a production build 2019-05-16 10:41:33 -06:00
Tim Schaub
d49e166506 Minify worker source 2019-05-16 10:35:18 -06:00
Frederic Junod
a0e6af425e Add tests for listenImage function, fix private variables initialization 2019-05-16 16:42:37 +02:00
engsterhold
e69b15d33e Bandaid fix for change in Zoomify.js/CustomTile signature
The recent addition of tilePixelRatio to the Zoomify.js/CustomTile constructor broke parameter binding in IIIF.js/IIIF.
The fix mirrors the change in the  Zoomify.js/Zoomify constructor
2019-05-16 16:10:58 +02:00
Tim Schaub
ee9a0bcd05 Stop listening for image decoding 2019-05-16 08:06:12 -06:00
Tim Schaub
442fbb13d2 Merge pull request #9550 from tschaub/worker
Setup for building workers
2019-05-16 07:02:34 -06:00
Frédéric Junod
fddc5bcc5b Merge pull request #9555 from fredj/rm_unused
Remove unused roundUpToPowerOfTwo function
2019-05-16 13:57:19 +02:00
Frederic Junod
c2058af13a Remove unused roundUpToPowerOfTwo function 2019-05-16 12:11:58 +02:00
Frederic Junod
bb022050ed Add 'webworker' lib into the TypeScript's compilerOptions 2019-05-16 09:49:29 +02:00
Frédéric Junod
a8e5cb1e12 Merge pull request #9553 from openlayers/greenkeeper/ol-mapbox-style-5.0.0-beta.2
chore(package): update ol-mapbox-style to version 5.0.0-beta.2
2019-05-16 09:43:56 +02:00
Frédéric Junod
1628ce8729 Merge pull request #9551 from fredj/img_decode
Use HTMLImageElement.decode if available
2019-05-16 09:30:55 +02:00
greenkeeper[bot]
37f96ddcfa chore(package): update ol-mapbox-style to version 5.0.0-beta.2 2019-05-16 07:21:23 +00:00
Andreas Hocevar
1fc75fdc68 Merge pull request #9500 from edellucien/kml_extended_data
KML string validation with extendedData
2019-05-16 09:20:03 +02:00
lucien
10c4ec0b37 Only use 'pushSerializeAndPop' later in code to write extendedData after 2019-05-16 09:10:40 +02:00
Frederic Junod
2e34dd0faf Use HTMLImageElement.decode if available 2019-05-16 08:53:29 +02:00
Tim Schaub
b7b37f9548 Add worker loader to the tests 2019-05-15 16:49:54 -06:00
Tim Schaub
05f13bb363 Add worker loader to the examples 2019-05-15 16:49:38 -06:00
Tim Schaub
e307410301 A task to inline workers 2019-05-15 16:38:06 -06:00
Tim Schaub
5a4541dadb Merge pull request #9534 from tschaub/simpler-mapbox-layer-example
Remove unnecessary parts from the Mapbox layer example
2019-05-15 14:00:46 -06:00
Andreas Hocevar
56ec6b093e Merge pull request #9548 from ahocevar/italic-labels
Avoid cut off labels for italic fonts
2019-05-15 16:58:17 +02:00
Frédéric Junod
61d753c803 Merge pull request #9549 from fredj/rm_unused
Remove unused getReplayTransform_ function in VectorTileLayer
2019-05-15 16:31:09 +02:00
Tim Schaub
f312706269 Remove unnecessary parts from the Mapbox layer example 2019-05-15 08:26:43 -06:00
Frederic Junod
f33ad5e025 Remove unused getReplayTransform_ function in VectorTileLayer 2019-05-15 15:10:54 +02:00
ahocevar
2e31f716ed Avoid cut off labels for italic text 2019-05-15 15:01:12 +02:00
Tim Schaub
eafb657264 Merge pull request #9545 from tschaub/default-view-extent
Avoid panning off the edge of the world
2019-05-15 04:58:29 -06:00
Tim Schaub
0cee259d0b Merge pull request #9539 from tschaub/event-error
Document existing error event type
2019-05-15 04:53:57 -06:00
Tim Schaub
be8797f355 Merge pull request #9541 from tschaub/rbush-three
Upgrade to rbush@3
2019-05-15 04:53:15 -06:00
Tim Schaub
d652bfc4a7 Merge pull request #9543 from tschaub/upgrade-jsdoc-plugin
Update jsdoc-plugin-typescript to version 2.0.1
2019-05-15 04:52:26 -06:00
Andreas Hocevar
11607caa81 Merge pull request #9536 from ahocevar/font-cache-hits
More font cache hits
2019-05-15 12:13:40 +02:00
ahocevar
41e958ea1f Split text into single chars or don't split at all 2019-05-15 11:55:29 +02:00
Olivier Guyot
39de2451bc Merge pull request #9537 from jahow/webgl-geojson-refactoring
WebGL / Points renderer refactoring
2019-05-15 11:44:40 +02:00
Olivier Guyot
e11e3c5f6e Webgl / minor function renaming 2019-05-15 11:30:24 +02:00
Olivier Guyot
c705775d75 Linting 2019-05-15 10:20:31 +02:00
Olivier Guyot
9ca75e9d43 Webgl points / rebuild buffers when source change 2019-05-15 10:18:53 +02:00
Olivier Guyot
5d2b7fe4bb Webgl points / use the helper ton compute the projection transform
The `WebGLHelper` class now provides a `makeProjectionTransform` method
that updates a transform to match the projection for a given frame state.

This also means that the WebGLHelper does not set the projection matrix
uniform anymore, this is the responsibility of the renderer as
the rendered coordinates will not be in world space from now on.
2019-05-15 10:18:47 +02:00
Olivier Guyot
fb455891ce Webgl points / make coordinates in view space to avoid precision glitches
Fixes #9479
2019-05-15 10:18:47 +02:00
Olivier Guyot
3e2e45ce6d Webgl helper / add the possibility to use transforms as custom uniforms
These will be translated into 4x4 matrices in GLSL.
2019-05-15 10:18:47 +02:00
Olivier Guyot
75eb62363a WebGL points / rebuild buffers only when extent changed 2019-05-15 10:18:47 +02:00
Olivier Guyot
523097903a WebGL points / rebuild buffers on every non animation frame 2019-05-15 10:18:22 +02:00
Olivier Guyot
c6d214b585 Webgl / implement support for custom attributes in webgl buffers
Only numerical attributes can be pushed in webgl buffers.
2019-05-15 10:15:55 +02:00
Frédéric Junod
c9604dbd69 Merge pull request #9546 from fredj/canvas_optim
Lazily create the hit detection context
2019-05-15 10:15:02 +02:00
Frédéric Junod
df37519858 Initialize hitDetectionContext to null
Co-Authored-By: Andreas Hocevar <andreas.hocevar@gmail.com>
2019-05-15 10:03:43 +02:00
Frederic Junod
10c6009ffa Lazily create the hit detection context 2019-05-15 09:52:57 +02:00
Olivier Guyot
27e520add4 WebGL points / use pushFeatureInBuffer util
Feature objects are converted to geoJSON with the properties
set for rendering.
2019-05-15 09:47:00 +02:00
Olivier Guyot
3a429d3f6c Webgl / add utils for pushing geojson geometries in webgl buffers
For now only point geometries are handled.
2019-05-15 09:44:41 +02:00
Olivier Guyot
5e36468245 Webgl / add a simple Layer renderer for common logic
This will eventually provides utilities for manipulating & rendering data
2019-05-15 09:43:29 +02:00
Olivier Guyot
a2d83f5358 WebGL points / add minimal tests 2019-05-15 09:42:15 +02:00
ahocevar
53b9718381 Bump version (beta) 2019-05-15 09:32:20 +02:00
Frédéric Junod
2de282ae49 Merge pull request #9533 from fredj/canvas_optim
Canvas optimizations
2019-05-15 08:24:57 +02:00
Tim Schaub
217c6ba764 Avoid panning off the edge of the world 2019-05-14 20:15:51 -06:00
greenkeeper[bot]
039f21274b chore(package): update jsdoc-plugin-typescript to version 2.0.1 2019-05-14 18:59:50 -06:00
Tim Schaub
09fdd30876 Upgrade to rbush@3 2019-05-14 18:43:08 -06:00
Tim Schaub
72551ab506 Merge pull request #9538 from tschaub/module-name
Correct the module annotation for the vector image layer renderer
2019-05-14 18:10:13 -06:00
Tim Schaub
811e5f60eb Document existing error event type 2019-05-14 14:28:00 -06:00
Tim Schaub
b1ea0fec50 Name the module like the module 2019-05-14 13:58:04 -06:00
Frederic Junod
98ee52c61c Move the transform matrix computation out of the for loop 2019-05-14 15:39:06 +02:00
Frederic Junod
f76b9bdd07 Only apply canvas transform if the tiles are scales 2019-05-14 15:33:44 +02:00
Frédéric Junod
3a1d927c41 Merge pull request #9532 from fredj/rm_prop_mapbox_example
Remove unused 'container' property in mapbox-layer example
2019-05-14 14:23:58 +02:00
Frederic Junod
9224bfab7d Remove unused 'container' property in mapbox-layer example 2019-05-14 13:48:26 +02:00
Frédéric Junod
b5535f3ba5 Merge pull request #9530 from fredj/spelling
Fix spelling
2019-05-14 09:26:53 +02:00
Frederic Junod
bfe6d29601 Fix spelling 2019-05-14 09:17:34 +02:00
Tim Schaub
807525b1db Merge pull request #9528 from openlayers/greenkeeper/puppeteer-1.16.0
Update puppeteer to the latest version 🚀
2019-05-13 21:01:49 -06:00
Tim Schaub
8504a49e17 Merge pull request #9489 from crubier/master
Fix Zoomify to display retina tiles
2019-05-13 19:05:31 -06:00
greenkeeper[bot]
83d317d16d chore(package): update puppeteer to version 1.16.0 2019-05-14 00:10:27 +00:00
Frédéric Junod
55b27c70b5 Merge pull request #9525 from jahow/custom-render-fn
Support a custom render function for the Layer class
2019-05-13 16:45:34 +02:00
Olivier Guyot
1c13893dd1 Mapbox Example / remove es6 shorthand
thanks @fredj

Co-Authored-By: Frédéric Junod <frederic.junod@camptocamp.com>
2019-05-13 16:38:10 +02:00
Olivier Guyot
4a1cda715d Layer / update doc to reflect custom render function support 2019-05-13 16:22:27 +02:00
Olivier Guyot
c9d1725f5c Composite renderer / trigger a render even when the source is undefined 2019-05-13 16:16:53 +02:00
Olivier Guyot
4bf314f9e6 Mapbox Example / simplify to use a custom render function 2019-05-13 16:16:53 +02:00
Olivier Guyot
14f580fe30 Layer / accepts a custom render function 2019-05-13 16:16:53 +02:00
Frédéric Junod
a2e6d4b324 Merge pull request #9522 from fredj/https
Use https urls for mapbox.com
2019-05-13 14:50:37 +02:00
Tim Schaub
dadc240038 Merge pull request #9520 from lutzhelm/iiif-doc
[WIP] Improve IIIF tile source and parser documentation
2019-05-13 06:47:09 -06:00
Andreas Hocevar
dd265623e4 Merge pull request #9523 from jahow/mapbox-simpler-example
Make the code simpler in the Mapbox example
2019-05-13 14:37:14 +02:00
Frederic Junod
23137cdab3 Use https urls for mapbox.com 2019-05-13 14:16:18 +02:00
Lutz Helm
1d08eb5752 Give a hint on how to parse IIIF info.json 2019-05-13 12:27:14 +02:00
Olivier Guyot
78cee60d91 Mapbox example / remove more unused code 2019-05-13 12:15:46 +02:00
Olivier Guyot
1deabaa1c0 Mapbox example / add mapbox css to avoid warning in console
Also remove manipulation of the mapbox canvas (not necessary anymore).
2019-05-13 11:42:28 +02:00
Olivier Guyot
88b6b18cc0 Mapbox example / remove overload of undocumented method
This was out of sync with mabox v54 and is not required anymore.
2019-05-13 11:38:45 +02:00
Andreas Hocevar
355fb41748 Merge pull request #9519 from jahow/mapbox-sync-render
Use synchronous render in Mapbox example
2019-05-13 11:37:26 +02:00
Olivier Guyot
7d2249f652 Mapbox example / trigger a synchronous redraw
This uses an undocumented method to trigger a synchronous redraw
instead of using the standard schedule from Mapbox. The CSS previously used
to make the OL and MB views match is not necessary anymore.

Reference: https://github.com/mapbox/mapbox-gl-js/issues/7893#issue-408992184
2019-05-13 11:16:36 +02:00
Lutz Helm
5e288ebedf Improve IIIF tile source doc 2019-05-13 11:15:20 +02:00
Lutz Helm
80eb8b62ce Display union types in IIIFInfo constructor doc 2019-05-13 11:14:43 +02:00
Lutz Helm
cdb36a3bf2 Expose IIIFInfo methods for doc 2019-05-13 11:04:28 +02:00
Lutz Helm
04a28854c9 Make IIIFInfo#Versions appear in doc
For whatever reasons, the possible values of `Versions` did not appear
in the built docs.
2019-05-13 10:49:38 +02:00
Andreas Hocevar
51cf9ee3a1 Merge pull request #9518 from ahocevar/hitdetect-no-declutter
Fix hit detection for multiple layers when decluttering is off
2019-05-13 09:23:43 +02:00
Andreas Hocevar
fbc8580539 Merge pull request #9517 from openlayers/greenkeeper/jsdoc-3.6.2
Update jsdoc to the latest version 🚀
2019-05-13 07:47:07 +02:00
ahocevar
eeec56fa94 Fix hit detection for multiple layers when decluttering is off 2019-05-12 23:32:44 +02:00
Andreas Hocevar
17d83b9c20 Merge pull request #9516 from ahocevar/ol-mapbox-style-5
Upgrade ol-mapbox-style to v5
2019-05-12 21:33:21 +02:00
greenkeeper[bot]
2c9f6b496f chore(package): update jsdoc to version 3.6.2 2019-05-12 16:51:14 +00:00
ahocevar
5006bbe13f Upgrade ol-mapbox-style to v5 2019-05-12 17:34:39 +02:00
Vincent Lecrubier
ea55f39298 Support tilePixelRatio in Zoomify to support retina 2019-05-08 22:52:14 +02:00
lucien
81f99f1579 Write placemark's ExtendedData tag after Style tag 2019-05-07 09:02:28 +02:00
127 changed files with 2145 additions and 1135 deletions

8
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: openlayers
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: # Replace with a single custom sponsorship URL

17
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- blocker
- regression
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -4,6 +4,28 @@
#### Backwards incompatible changes #### Backwards incompatible changes
#### Removal of `GEOLOCATION` constant from `ol/has`
If you were previously using this constant, you can check if `'geolocation'` is define in `navigator` instead.
```js
if ('geolocation' in navigator) {
// ...
}
```
#### Removal of CSS print rules
The CSS media print rules were removed from the `ol.css` file. To get the previous behavior, use the following CSS:
```css
@media print {
.ol-control {
display: none;
}
}
```
#### Removal of optional this arguments #### Removal of optional this arguments
The optional this (i.e. opt_this) arguments were removed from the following methods. The optional this (i.e. opt_this) arguments were removed from the following methods.
@@ -32,9 +54,9 @@ Previously, this options only constrained the view *center*. This behaviour can
As a side effect, the view `rotate` method is gone and has been replaced with `adjustRotation` which takes a delta as input. As a side effect, the view `rotate` method is gone and has been replaced with `adjustRotation` which takes a delta as input.
##### Zoom is constrained so only one world is visible ##### The view is constrained so only one world is visible
Previously, maps showed multiple worlds at low zoom levels. Now, the view is restricted to show only one world. To get the previous behavior, configure the `ol/View` with `multiWorld: true`. Previously, maps showed multiple worlds at low zoom levels. In addition, it used to be possible to pan off the north or south edge of the world. Now, the view is restricted to show only one world, and you cannot pan off the edge. To get the previous behavior, configure the `ol/View` with `multiWorld: true`.
##### Removal of deprecated methods ##### Removal of deprecated methods

View File

@@ -5,6 +5,7 @@ import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import {OSM, Vector as VectorSource} from '../src/ol/source.js'; import {OSM, Vector as VectorSource} from '../src/ol/source.js';
import {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js'; import {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js';
/** @type {VectorSource<import("../src/ol/geom/SimpleGeometry.js").default>} */
const source = new VectorSource({ const source = new VectorSource({
url: 'data/geojson/switzerland.geojson', url: 'data/geojson/switzerland.geojson',
format: new GeoJSON() format: new GeoJSON()
@@ -51,21 +52,21 @@ const zoomtoswitzerland =
document.getElementById('zoomtoswitzerland'); document.getElementById('zoomtoswitzerland');
zoomtoswitzerland.addEventListener('click', function() { zoomtoswitzerland.addEventListener('click', function() {
const feature = source.getFeatures()[0]; const feature = source.getFeatures()[0];
const polygon = /** @type {import("../src/ol/geom/SimpleGeometry.js").default} */ (feature.getGeometry()); const polygon = feature.getGeometry();
view.fit(polygon, {padding: [170, 50, 30, 150]}); view.fit(polygon, {padding: [170, 50, 30, 150]});
}, false); }, false);
const zoomtolausanne = document.getElementById('zoomtolausanne'); const zoomtolausanne = document.getElementById('zoomtolausanne');
zoomtolausanne.addEventListener('click', function() { zoomtolausanne.addEventListener('click', function() {
const feature = source.getFeatures()[1]; const feature = source.getFeatures()[1];
const point = /** @type {import("../src/ol/geom/SimpleGeometry.js").default} */ (feature.getGeometry()); const point = feature.getGeometry();
view.fit(point, {padding: [170, 50, 30, 150], minResolution: 50}); view.fit(point, {padding: [170, 50, 30, 150], minResolution: 50});
}, false); }, false);
const centerlausanne = document.getElementById('centerlausanne'); const centerlausanne = document.getElementById('centerlausanne');
centerlausanne.addEventListener('click', function() { centerlausanne.addEventListener('click', function() {
const feature = source.getFeatures()[1]; const feature = source.getFeatures()[1];
const point = /** @type {import("../src/ol/geom/Point.js").default} */ (feature.getGeometry()); const point = feature.getGeometry();
const size = map.getSize(); const size = map.getSize();
view.centerOn(point.getCoordinates(), size, [570, 500]); view.centerOn(point.getCoordinates(), size, [570, 500]);
}, false); }, false);

View File

@@ -6,7 +6,7 @@ docs: >
The example loads TopoJSON geometries and uses d3 (<code>d3.geo.path</code>) to render these geometries to a SVG element. The example loads TopoJSON geometries and uses d3 (<code>d3.geo.path</code>) to render these geometries to a SVG element.
tags: "d3" tags: "d3"
resources: resources:
- https://unpkg.com/d3@4.12.0/build/d3.js - https://unpkg.com/d3@5.9.2/dist/d3.js
- https://unpkg.com/topojson@3.0.2/dist/topojson.js - https://unpkg.com/topojson@3.0.2/dist/topojson.js
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

4
examples/d3.js vendored
View File

@@ -2,7 +2,7 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import {getWidth, getCenter} from '../src/ol/extent.js'; import {getWidth, getCenter} from '../src/ol/extent.js';
import {Layer, Tile as TileLayer} from '../src/ol/layer.js'; import {Layer, Tile as TileLayer} from '../src/ol/layer.js';
import SourceState from '../src/ol/source/State'; import SourceState from '../src/ol/source/State.js';
import {fromLonLat, toLonLat} from '../src/ol/proj.js'; import {fromLonLat, toLonLat} from '../src/ol/proj.js';
import Stamen from '../src/ol/source/Stamen.js'; import Stamen from '../src/ol/source/Stamen.js';
@@ -85,7 +85,7 @@ const map = new Map({
/** /**
* Load the topojson data and create an ol/layer/Image for that data. * Load the topojson data and create an ol/layer/Image for that data.
*/ */
d3.json('data/topojson/us.json', function(error, us) { d3.json('data/topojson/us.json').then(function(us) {
const layer = new CanvasLayer({ const layer = new CanvasLayer({
features: topojson.feature(us, us.objects.counties) features: topojson.feature(us, us.objects.counties)

7
examples/data/square.svg Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg">
<g>
<rect width="20" height="20" style="fill:#fff" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 194 B

6
examples/export-map.css Normal file
View File

@@ -0,0 +1,6 @@
.overlay {
background-color: yellow;
border-radius: 6px;
padding: 4px;
white-space: nowrap;
}

View File

@@ -8,5 +8,8 @@ docs: >
tags: "export, png, openstreetmap" tags: "export, png, openstreetmap"
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<div style="display: none;">
<div class="overlay" id="null">Null Island</div>
</div>
<a id="export-png" class="btn btn-default"><i class="fa fa-download"></i> Download PNG</a> <a id="export-png" class="btn btn-default"><i class="fa fa-download"></i> Download PNG</a>
<a id="image-download" download="map.png"></a> <a id="image-download" download="map.png"></a>

View File

@@ -1,5 +1,6 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import Overlay from '../src/ol/Overlay.js';
import GeoJSON from '../src/ol/format/GeoJSON.js'; import GeoJSON from '../src/ol/format/GeoJSON.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js'; import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import {OSM, Vector as VectorSource} from '../src/ol/source.js'; import {OSM, Vector as VectorSource} from '../src/ol/source.js';
@@ -25,11 +26,17 @@ const map = new Map({
}) })
}); });
map.addOverlay(new Overlay({
position: [0, 0],
element: document.getElementById('null')
}));
// export options for html-to-image. // export options for html-to-image.
// See: https://github.com/bubkoo/html-to-image#options // See: https://github.com/bubkoo/html-to-image#options
const exportOptions = { const exportOptions = {
filter: function(element) { filter: function(element) {
return element.className.indexOf('ol-control') === -1; return element.className ? element.className.indexOf('ol-control') === -1 : true;
} }
}; };

View File

@@ -2,8 +2,8 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import OSM from '../src/ol/source/OSM.js'; import OSM from '../src/ol/source/OSM.js';
import {defaults as defaultControls} from '../src/ol/control/util'; import {defaults as defaultControls} from '../src/ol/control.js';
import ZoomSlider from '../src/ol/control/ZoomSlider'; import ZoomSlider from '../src/ol/control/ZoomSlider.js';
const view = new View({ const view = new View({
center: [328627.563458, 5921296.662223], center: [328627.563458, 5921296.662223],

View File

@@ -67,10 +67,10 @@ const routeFeature = new Feature({
type: 'route', type: 'route',
geometry: route geometry: route
}); });
const geoMarker = new Feature({ const geoMarker = /** @type Feature<import("../src/ol/geom/Point").default> */(new Feature({
type: 'geoMarker', type: 'geoMarker',
geometry: new Point(routeCoords[0]) geometry: new Point(routeCoords[0])
}); }));
const startMarker = new Feature({ const startMarker = new Feature({
type: 'icon', type: 'icon',
geometry: new Point(routeCoords[0]) geometry: new Point(routeCoords[0])
@@ -191,7 +191,7 @@ function stopAnimation(ended) {
// if animation cancelled set the marker at the beginning // if animation cancelled set the marker at the beginning
const coord = ended ? routeCoords[routeLength - 1] : routeCoords[0]; const coord = ended ? routeCoords[routeLength - 1] : routeCoords[0];
const geometry = /** @type {import("../src/ol/geom/Point").default} */ (geoMarker.getGeometry()); const geometry = geoMarker.getGeometry();
geometry.setCoordinates(coord); geometry.setCoordinates(coord);
//remove listener //remove listener
vectorLayer.un('postrender', moveFeature); vectorLayer.un('postrender', moveFeature);

View File

@@ -1,17 +1,16 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import Feature from '../src/ol/Feature'; import Feature from '../src/ol/Feature.js';
import Point from '../src/ol/geom/Point'; import Point from '../src/ol/geom/Point.js';
import VectorLayer from '../src/ol/layer/Vector'; import VectorLayer from '../src/ol/layer/Vector.js';
import {Vector} from '../src/ol/source'; import {Vector} from '../src/ol/source.js';
import {fromLonLat} from '../src/ol/proj'; import {fromLonLat} from '../src/ol/proj.js';
import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer'; import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer.js';
import {clamp, lerp} from '../src/ol/math'; import {clamp, lerp} from '../src/ol/math.js';
import Stamen from '../src/ol/source/Stamen'; import Stamen from '../src/ol/source/Stamen.js';
const vectorSource = new Vector({ const vectorSource = new Vector({
features: [],
attributions: 'NASA' attributions: 'NASA'
}); });

View File

@@ -1,5 +1,6 @@
.map:-webkit-full-screen { .map:-webkit-full-screen {
height: 100%; height: 100%;
margin: 0;
} }
.map:-ms-fullscreen { .map:-ms-fullscreen {
height: 100%; height: 100%;

View File

@@ -1,5 +1,6 @@
.fullscreen:-webkit-full-screen { .fullscreen:-webkit-full-screen {
height: 100%; height: 100%;
margin: 0;
} }
.fullscreen:-ms-fullscreen { .fullscreen:-ms-fullscreen {
height: 100%; height: 100%;

View File

@@ -1,5 +1,6 @@
.map:-webkit-full-screen { .map:-webkit-full-screen {
height: 100%; height: 100%;
margin: 0;
} }
.map:-ms-fullscreen { .map:-ms-fullscreen {
height: 100%; height: 100%;

View File

@@ -4,7 +4,7 @@ title: Earthquakes Heatmap
shortdesc: Demonstrates the use of a heatmap layer. shortdesc: Demonstrates the use of a heatmap layer.
docs: > docs: >
This example parses a KML file and renders the features as a <code>ol/layer/Heatmap</code> layer. This example parses a KML file and renders the features as a <code>ol/layer/Heatmap</code> layer.
tags: "heatmap, kml, vector, style" tags: "heatmap, kml, vector, style, webgl"
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<form> <form>

View File

@@ -25,7 +25,7 @@ rome.setStyle(new Style({
image: new Icon({ image: new Icon({
color: '#8959A8', color: '#8959A8',
crossOrigin: 'anonymous', crossOrigin: 'anonymous',
src: 'data/dot.png' src: 'data/square.svg'
}) })
})); }));

View File

@@ -1,14 +1,14 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import TileJSON from '../src/ol/source/TileJSON'; import TileJSON from '../src/ol/source/TileJSON.js';
import Feature from '../src/ol/Feature'; import Feature from '../src/ol/Feature.js';
import Point from '../src/ol/geom/Point'; import Point from '../src/ol/geom/Point.js';
import VectorLayer from '../src/ol/layer/Vector'; import VectorLayer from '../src/ol/layer/Vector.js';
import {Vector} from '../src/ol/source'; import {Vector} from '../src/ol/source.js';
import {fromLonLat} from '../src/ol/proj'; import {fromLonLat} from '../src/ol/proj.js';
import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer'; import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer.js';
import {lerp} from '../src/ol/math'; import {lerp} from '../src/ol/math.js';
const vectorSource = new Vector({ const vectorSource = new Vector({
features: [], features: [],

View File

@@ -7,8 +7,9 @@ docs: >
tags: "simple, mapbox, vector, tiles" tags: "simple, mapbox, vector, tiles"
resources: resources:
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.js - 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: cloak:
- key: ER67WIiPdCQvhgsUjoWK - key: ER67WIiPdCQvhgsUjoWK
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from https://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -1,183 +1,67 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import Layer from '../src/ol/layer/Layer'; import Layer from '../src/ol/layer/Layer.js';
import {assign} from '../src/ol/obj'; import {toLonLat, fromLonLat} from '../src/ol/proj.js';
import {toLonLat} from '../src/ol/proj';
import SourceState from '../src/ol/source/State';
import {Stroke, Style} from '../src/ol/style.js'; import {Stroke, Style} from '../src/ol/style.js';
import VectorLayer from '../src/ol/layer/Vector.js'; import VectorLayer from '../src/ol/layer/Vector.js';
import VectorSource from '../src/ol/source/Vector.js'; import VectorSource from '../src/ol/source/Vector.js';
import GeoJSON from '../src/ol/format/GeoJSON.js'; import GeoJSON from '../src/ol/format/GeoJSON.js';
class Mapbox extends Layer { const center = [-98.8, 37.9];
const key = 'ER67WIiPdCQvhgsUjoWK';
/** const mbMap = new mapboxgl.Map({
* @param {import('../src/ol/layer/Layer').Options} options Layer options. style: 'https://maps.tilehosting.com/styles/bright/style.json?key=' + key,
*/ attributionControl: false,
constructor(options) { boxZoom: false,
const baseOptions = assign({}, options); center: center,
super(baseOptions); container: 'map',
doubleClickZoom: false,
dragPan: false,
dragRotate: false,
interactive: false,
keyboard: false,
pitchWithRotate: false,
scrollZoom: false,
touchZoomRotate: false
});
this.baseOptions = baseOptions; const mbLayer = new Layer({
render: function(frameState) {
const canvas = mbMap.getCanvas();
const viewState = frameState.viewState;
/** const visible = mbLayer.getVisible();
* @private canvas.style.display = visible ? 'block' : 'none';
* @type boolean
*/
this.loaded = false;
this.initMap(); const opacity = mbLayer.getOpacity();
} canvas.style.opacity = opacity;
initMap() { // adjust view parameters in mapbox
const map = this.map_; const rotation = viewState.rotation;
const view = map.getView();
const center = toLonLat(view.getCenter(), view.getProjection());
this.centerLastRender = view.getCenter();
this.zoomLastRender = view.getZoom();
this.centerLastRender = view.getCenter();
this.zoomLastRender = view.getZoom();
const options = assign(this.baseOptions, {
attributionControl: false,
boxZoom: false,
center,
container: map.getTargetElement(),
doubleClickZoom: false,
dragPan: false,
dragRotate: false,
interactive: false,
keyboard: false,
pitchWithRotate: false,
scrollZoom: false,
touchZoomRotate: false,
zoom: view.getZoom() - 1
});
this.mbmap = new mapboxgl.Map(options);
this.mbmap.on('load', function() {
this.mbmap.getCanvas().remove();
this.loaded = true;
this.map_.render();
this.mbmap.getContainer().querySelector('.mapboxgl-control-container').remove();
}.bind(this));
this.mbmap.on('render', function() {
// Reset offset
if (this.centerNextRender) {
this.centerLastRender = this.centerNextRender;
}
if (this.zoomNextRender) {
this.zoomLastRender = this.zoomNextRender;
}
this.updateRenderedPosition(0, 0, 1);
}.bind(this));
}
/**
*
* @inheritDoc
*/
render(frameState) {
const map = this.map_;
const view = map.getView();
this.centerNextRender = view.getCenter();
const lastRender = map.getPixelFromCoordinate(this.centerLastRender);
const nextRender = map.getPixelFromCoordinate(this.centerNextRender);
this.zoomNextRender = view.getZoom();
const scale = Math.pow(2, this.zoomNextRender - this.zoomLastRender);
this.updateRenderedPosition(lastRender[0] - nextRender[0], lastRender[1] - nextRender[1], scale);
const rotation = frameState.viewState.rotation;
if (rotation) { if (rotation) {
this.mbmap.rotateTo(-rotation * 180 / Math.PI, { mbMap.rotateTo(-rotation * 180 / Math.PI, {
animate: false animate: false
}); });
} }
mbMap.jumpTo({
// Re-render mbmap center: toLonLat(viewState.center),
const center = toLonLat(this.centerNextRender, view.getProjection()); zoom: viewState.zoom - 1,
const zoom = view.getZoom() - 1; animate: false
this.mbmap.jumpTo({
center: center,
zoom: zoom
}); });
return this.mbmap.getCanvas();
// cancel the scheduled update & trigger synchronous redraw
// see https://github.com/mapbox/mapbox-gl-js/issues/7893#issue-408992184
// NOTE: THIS MIGHT BREAK WHEN UPDATING MAPBOX
if (mbMap._frame) {
mbMap._frame.cancel();
mbMap._frame = null;
}
mbMap._render();
return canvas;
} }
});
updateRenderedPosition(left, top, scale) {
const style = this.mbmap.getCanvas().style;
style.left = Math.round(left) + 'px';
style.top = Math.round(top) + 'px';
style.transform = 'scale(' + scale + ')';
}
setVisible(visible) {
super.setVisible(visible);
const canvas = this.mbmap.getCanvas();
canvas.style.display = visible ? 'block' : 'none';
}
setOpacity(opacity) {
super.setOpacity(opacity);
const canvas = this.mbmap.getCanvas();
canvas.style.opacity = opacity;
}
setZIndex(zindex) {
super.setZIndex(zindex);
const canvas = this.mbmap.getCanvas();
canvas.style.zIndex = zindex;
}
/**
* @inheritDoc
*/
getSourceState() {
return this.loaded ? SourceState.READY : SourceState.UNDEFINED;
}
setMap(map) {
this.map_ = map;
}
}
mapboxgl.Map.prototype._setupContainer = function _setupContainer() {
const container = this._container;
container.classList.add('mapboxgl-map');
const canvasContainer = this._canvasContainer = container.firstChild;
this._canvas = document.createElement('canvas');
canvasContainer.insertBefore(this._canvas, canvasContainer.firstChild);
this._canvas.style.position = 'absolute';
this._canvas.addEventListener('webglcontextlost', this._contextLost, false);
this._canvas.addEventListener('webglcontextrestored', this._contextRestored, false);
this._canvas.setAttribute('tabindex', '0');
this._canvas.setAttribute('aria-label', 'Map');
this._canvas.className = 'mapboxgl-canvas';
const dimensions = this._containerDimensions();
this._resizeCanvas(dimensions[0], dimensions[1]);
this._controlContainer = canvasContainer;
const controlContainer = this._controlContainer = document.createElement('div');
controlContainer.className = 'mapboxgl-control-container';
container.appendChild(controlContainer);
const positions = this._controlPositions = {};
['top-left', 'top-right', 'bottom-left', 'bottom-right'].forEach(function(positionName) {
const elem = document.createElement('div');
elem.className = 'mapboxgl-ctrl-' + positionName;
controlContainer.appendChild(elem);
positions[positionName] = elem;
});
};
const style = new Style({ const style = new Style({
stroke: new Stroke({ stroke: new Stroke({
@@ -197,21 +81,8 @@ const vectorLayer = new VectorLayer({
const map = new Map({ const map = new Map({
target: 'map', target: 'map',
view: new View({ view: new View({
center: [-10997148, 4569099], center: fromLonLat(center),
zoom: 4, zoom: 4
minZoom: 1, }),
extent: [-Infinity, -20048966.10, Infinity, 20048966.10], layers: [mbLayer, vectorLayer]
smoothExtentConstraint: false,
smoothResolutionConstraint: false
})
}); });
const key = 'ER67WIiPdCQvhgsUjoWK';
const mbLayer = new Mapbox({
map: map,
container: map.getTarget(),
style: 'https://maps.tilehosting.com/styles/bright/style.json?key=' + key
});
map.addLayer(mbLayer);
map.addLayer(vectorLayer);

View File

@@ -9,6 +9,6 @@ resources:
- resources/mapbox-streets-v6-style.js - resources/mapbox-streets-v6-style.js
cloak: cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q - key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from https://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -9,6 +9,6 @@ resources:
- resources/mapbox-streets-v6-style.js - resources/mapbox-streets-v6-style.js
cloak: cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q - key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from https://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -3,9 +3,9 @@ import View from '../src/ol/View.js';
import ImageLayer from '../src/ol/layer/Image.js'; import ImageLayer from '../src/ol/layer/Image.js';
import ImageMapGuide from '../src/ol/source/ImageMapGuide.js'; import ImageMapGuide from '../src/ol/source/ImageMapGuide.js';
const mdf = 'Library://Public/Samples/Sheboygan/Maps/Sheboygan.MapDefinition'; const mdf = 'Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition';
const agentUrl = const agentUrl =
'http://www.buoyshark.com/mapguide/mapagent/mapagent.fcgi?'; 'http://138.197.230.93:8008/mapguide/mapagent/mapagent.fcgi?';
const bounds = [ const bounds = [
-87.865114442365922, -87.865114442365922,
43.665065564837931, 43.665065564837931,
@@ -24,8 +24,9 @@ const map = new Map({
params: { params: {
MAPDEFINITION: mdf, MAPDEFINITION: mdf,
FORMAT: 'PNG', FORMAT: 'PNG',
USERNAME: 'OpenLayers', VERSION: '3.0.0',
PASSWORD: 'OpenLayers' USERNAME: 'OLGuest',
PASSWORD: 'olguest'
}, },
ratio: 2 ratio: 2
}) })

View File

@@ -11,7 +11,7 @@ docs: >
tags: "raster, pixel operation, flood" tags: "raster, pixel operation, flood"
cloak: cloak:
- key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg - key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from https://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<label> <label>

View File

@@ -8,7 +8,7 @@ docs: >
tags: "utfgrid, tilejson" tags: "utfgrid, tilejson"
cloak: cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q - key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from https://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<div style="display: none;"> <div style="display: none;">

View File

@@ -19,16 +19,25 @@ module.exports = {
context: src, context: src,
target: 'web', target: 'web',
entry: entry, entry: entry,
stats: 'minimal',
module: { module: {
rules: [{ rules: [{
test: /\.js$/,
use: { use: {
loader: 'buble-loader' loader: 'buble-loader'
}, },
test: /\.js$/,
include: [ include: [
path.join(__dirname, '..', '..', 'src'), path.join(__dirname, '..', '..', 'src'),
path.join(__dirname, '..') path.join(__dirname, '..')
] ]
}, {
test: /\.js$/,
use: {
loader: path.join(__dirname, './worker-loader.js')
},
include: [
path.join(__dirname, '../../src/ol/worker')
]
}] }]
}, },
optimization: { optimization: {

View File

@@ -0,0 +1,17 @@
const build = require('../../tasks/serialize-workers').build;
function loader() {
const callback = this.async();
const minify = this.mode === 'production';
build(this.resource, {minify})
.then(chunk => {
for (const filePath in chunk.modules) {
this.addDependency(filePath);
}
callback(null, chunk.code);
})
.catch(callback);
}
module.exports = loader;

10
examples/worker.html Normal file
View File

@@ -0,0 +1,10 @@
---
layout: example.html
title: Worker
shortdesc: This example should be deleted.
docs: >
When you move the map, a message is sent to a worker. In response, the woker sends a
message back with the version identifier.
tags: "worker"
---
<div id="map" class="map"></div>

35
examples/worker.js Normal file
View File

@@ -0,0 +1,35 @@
/* eslint-disable no-console */
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js';
import OSM from '../src/ol/source/OSM.js';
import {create as createVersionWorker} from '../src/ol/worker/version.js';
const map = new Map({
layers: [
new TileLayer({
source: new OSM()
})
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2
})
});
const worker = createVersionWorker();
worker.addEventListener('error', function(error) {
console.error('worker error', error);
});
worker.addEventListener('message', function(event) {
console.log('message from worker:', event.data);
});
map.on('moveend', function(event) {
const state = event.frameState.viewState;
worker.postMessage({zoom: state.zoom, center: state.center});
});

View File

@@ -11,5 +11,6 @@ tags: "zoomify, deep zoom, IIP, pixel, projection"
<select id="zoomifyProtocol"> <select id="zoomifyProtocol">
<option value="zoomify">Zoomify</option> <option value="zoomify">Zoomify</option>
<option value="iip">IIP</option> <option value="iip">IIP</option>
<option value="zoomifyretina">Zoomify Retina</option>
</select> </select>
</div> </div>

View File

@@ -12,6 +12,8 @@ const iipUrl = 'http://vips.vtech.fr/cgi-bin/iipsrv.fcgi?FIF=' + '/mnt/MD1/AD00/
const layer = new TileLayer({ const layer = new TileLayer({
source: new Zoomify({ source: new Zoomify({
tileSize: 256,
tilePixelRatio: 1,
url: zoomifyUrl, url: zoomifyUrl,
size: [imgWidth, imgHeight], size: [imgWidth, imgHeight],
crossOrigin: 'anonymous' crossOrigin: 'anonymous'
@@ -20,12 +22,15 @@ const layer = new TileLayer({
const extent = [0, -imgHeight, imgWidth, 0]; const extent = [0, -imgHeight, imgWidth, 0];
const resolutions = layer.getSource().getTileGrid().getResolutions();
const map = new Map({ const map = new Map({
layers: [layer], layers: [layer],
target: 'map', target: 'map',
view: new View({ view: new View({
// adjust zoom levels to those provided by the source // adjust zoom levels to those provided by the source
resolutions: layer.getSource().getTileGrid().getResolutions(), minResolution: resolutions[resolutions.length - 1],
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent // constrain the center: center cannot be set outside this extent
extent: extent extent: extent
}) })
@@ -36,17 +41,73 @@ const control = document.getElementById('zoomifyProtocol');
control.addEventListener('change', function(event) { control.addEventListener('change', function(event) {
const value = event.currentTarget.value; const value = event.currentTarget.value;
if (value === 'iip') { if (value === 'iip') {
layer.setSource(new Zoomify({ const extent = [0, -imgHeight, imgWidth, 0];
url: iipUrl, layer.setSource(
size: [imgWidth, imgHeight], new Zoomify({
crossOrigin: 'anonymous' tileSize: 256,
})); tilePixelRatio: 1,
url: iipUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
})
);
const resolutions = layer.getSource().getTileGrid().getResolutions();
map.setView(
new View({
// adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1],
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent
extent: extent
})
);
map.getView().fit(extent);
} else if (value === 'zoomify') { } else if (value === 'zoomify') {
layer.setSource(new Zoomify({ const extent = [0, -imgHeight, imgWidth, 0];
url: zoomifyUrl, layer.setSource(
size: [imgWidth, imgHeight], new Zoomify({
crossOrigin: 'anonymous' tileSize: 256,
})); tilePixelRatio: 1,
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
})
);
const resolutions = layer.getSource().getTileGrid().getResolutions();
map.setView(
new View({
// adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1],
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent
extent: extent
})
);
map.getView().fit(extent);
} else if (value === 'zoomifyretina') {
const pixelRatio = 4;
// Be careful! Image extent will be modified by pixel ratio
const extent = [0, -imgHeight / pixelRatio, imgWidth / pixelRatio, 0];
layer.setSource(
new Zoomify({
tileSize: 256 / pixelRatio,
tilePixelRatio: pixelRatio,
url: zoomifyUrl,
size: [imgWidth / pixelRatio, imgHeight / pixelRatio],
crossOrigin: 'anonymous'
})
);
const resolutions = layer.getSource().getTileGrid().getResolutions();
map.setView(
new View({
// adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1] / pixelRatio,
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent
extent: extent
})
);
map.getView().fit(extent);
} }
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "ol", "name": "ol",
"version": "6.0.0-beta.7", "version": "6.0.0-beta.9",
"description": "OpenLayers mapping library", "description": "OpenLayers mapping library",
"keywords": [ "keywords": [
"map", "map",
@@ -22,7 +22,7 @@
"build-index": "npm run build-package && node tasks/generate-index", "build-index": "npm run build-package && node tasks/generate-index",
"build-legacy": "shx rm -rf build && npm run build-index && webpack --config config/webpack-config-legacy-build.js && cleancss --source-map src/ol/ol.css -o build/legacy/ol.css", "build-legacy": "shx rm -rf build && npm run build-index && webpack --config config/webpack-config-legacy-build.js && cleancss --source-map src/ol/ol.css -o build/legacy/ol.css",
"copy-css": "shx cp src/ol/ol.css build/ol/ol.css", "copy-css": "shx cp src/ol/ol.css build/ol/ol.css",
"transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && tsc --project config/tsconfig-build.json", "transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && node tasks/serialize-workers && tsc --project config/tsconfig-build.json",
"typecheck": "tsc --pretty", "typecheck": "tsc --pretty",
"apidoc": "jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc" "apidoc": "jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc"
}, },
@@ -38,16 +38,15 @@
"dependencies": { "dependencies": {
"pbf": "3.2.0", "pbf": "3.2.0",
"pixelworks": "1.1.0", "pixelworks": "1.1.0",
"rbush": "2.0.2" "rbush": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.4.0", "@babel/core": "^7.4.0",
"@babel/preset-env": "^7.4.2", "@babel/preset-env": "^7.4.4",
"@openlayers/eslint-plugin": "^4.0.0-beta.2", "@openlayers/eslint-plugin": "^4.0.0-beta.2",
"@types/arcgis-rest-api": "^10.4.4", "@types/arcgis-rest-api": "^10.4.4",
"@types/geojson": "^7946.0.7", "@types/geojson": "^7946.0.7",
"@types/pbf": "^3.0.1", "@types/pbf": "^3.0.1",
"@types/rbush": "^2.0.2",
"@types/topojson-specification": "^1.0.1", "@types/topojson-specification": "^1.0.1",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"buble": "^0.19.7", "buble": "^0.19.7",
@@ -57,7 +56,7 @@
"copy-webpack-plugin": "^5.0.3", "copy-webpack-plugin": "^5.0.3",
"coveralls": "3.0.3", "coveralls": "3.0.3",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-config-openlayers": "^11.0.0", "eslint-config-openlayers": "^12.0.0",
"expect.js": "0.3.1", "expect.js": "0.3.1",
"front-matter": "^3.0.2", "front-matter": "^3.0.2",
"fs-extra": "^8.0.0", "fs-extra": "^8.0.0",
@@ -68,8 +67,8 @@
"istanbul": "0.4.5", "istanbul": "0.4.5",
"istanbul-instrumenter-loader": "^3.0.1", "istanbul-instrumenter-loader": "^3.0.1",
"jquery": "3.4.1", "jquery": "3.4.1",
"jsdoc": "3.6.1", "jsdoc": "3.6.2",
"jsdoc-plugin-typescript": "^2.0.0", "jsdoc-plugin-typescript": "^2.0.1",
"karma": "^4.1.0", "karma": "^4.1.0",
"karma-chrome-launcher": "2.2.0", "karma-chrome-launcher": "2.2.0",
"karma-coverage": "^1.1.2", "karma-coverage": "^1.1.2",
@@ -81,11 +80,16 @@
"loglevelnext": "^3.0.1", "loglevelnext": "^3.0.1",
"marked": "0.6.2", "marked": "0.6.2",
"mocha": "6.1.4", "mocha": "6.1.4",
"ol-mapbox-style": "^4.3.1", "ol-mapbox-style": "^5.0.0-beta.2",
"pixelmatch": "^4.0.2", "pixelmatch": "^4.0.2",
"pngjs": "^3.4.0", "pngjs": "^3.4.0",
"proj4": "2.5.0", "proj4": "2.5.0",
"puppeteer": "~1.15.0", "puppeteer": "~1.17.0",
"rollup": "^1.12.0",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^10.0.0",
"rollup-plugin-node-resolve": "^5.0.0",
"rollup-plugin-terser": "^5.0.0",
"serve-static": "^1.14.0", "serve-static": "^1.14.0",
"shx": "^0.3.2", "shx": "^0.3.2",
"sinon": "^7.3.2", "sinon": "^7.3.2",
@@ -93,7 +97,7 @@
"typescript": "^3.4.5", "typescript": "^3.4.5",
"url-polyfill": "^1.1.5", "url-polyfill": "^1.1.5",
"walk": "^2.3.9", "walk": "^2.3.9",
"webpack": "4.31.0", "webpack": "4.32.2",
"webpack-cli": "^3.3.2", "webpack-cli": "^3.3.2",
"webpack-dev-middleware": "^3.6.2", "webpack-dev-middleware": "^3.6.2",
"webpack-dev-server": "^3.3.1", "webpack-dev-server": "^3.3.1",

View File

@@ -1,10 +1,10 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
import {Heatmap as HeatmapLayer} from '../../../src/ol/layer'; import {Heatmap as HeatmapLayer} from '../../../src/ol/layer.js';
import VectorSource from '../../../src/ol/source/Vector'; import VectorSource from '../../../src/ol/source/Vector.js';
import KML from '../../../src/ol/format/KML'; import KML from '../../../src/ol/format/KML.js';
const vector = new HeatmapLayer({ const vector = new HeatmapLayer({
source: new VectorSource({ source: new VectorSource({

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1,30 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import {Group as LayerGroup, Tile as TileLayer} from '../../../src/ol/layer.js';
import XYZ from '../../../src/ol/source/XYZ.js';
new Map({
target: 'map',
view: new View({
center: [0, 0],
zoom: 3
}),
layers: new LayerGroup({
opacity: 0.75,
layers: [
new TileLayer({
opacity: 0.25,
source: new XYZ({
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg'
})
}),
new TileLayer({
source: new XYZ({
url: '/data/tiles/stamen-labels/{z}/{x}/{y}.png'
})
})
]
})
});
render();

View File

@@ -5,7 +5,7 @@ import {
get as getProjection, get as getProjection,
transform, transform,
transformExtent transformExtent
} from '../../../src/ol/proj'; } from '../../../src/ol/proj.js';
import ImageLayer from '../../../src/ol/layer/Image.js'; import ImageLayer from '../../../src/ol/layer/Image.js';
const center = transform([-122.416667, 37.783333], 'EPSG:4326', 'EPSG:3857'); const center = transform([-122.416667, 37.783333], 'EPSG:4326', 'EPSG:3857');

View File

@@ -5,9 +5,9 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import {fromLonLat} from '../../../src/ol/proj'; import {fromLonLat} from '../../../src/ol/proj.js';
import {transformExtent} from '../../../src/ol/proj.js'; import {transformExtent} from '../../../src/ol/proj.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
const center = fromLonLat([7, 50]); const center = fromLonLat([7, 50]);
const extent = transformExtent([2, 47, 10, 53], 'EPSG:4326', 'EPSG:3857'); const extent = transformExtent([2, 47, 10, 53], 'EPSG:4326', 'EPSG:3857');

View File

@@ -1,7 +1,7 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
import {createXYZ} from '../../../src/ol/tilegrid.js'; import {createXYZ} from '../../../src/ol/tilegrid.js';
const center = [-10997148, 4569099]; const center = [-10997148, 4569099];

View File

@@ -1,8 +1,8 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import {fromLonLat} from '../../../src/ol/proj'; import {fromLonLat} from '../../../src/ol/proj.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
const center = fromLonLat([8.6, 50.1]); const center = fromLonLat([8.6, 50.1]);

View File

@@ -1,8 +1,8 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import {fromLonLat} from '../../../src/ol/proj'; import {fromLonLat} from '../../../src/ol/proj.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
const center = fromLonLat([8.6, 50.1]); const center = fromLonLat([8.6, 50.1]);

View File

@@ -1,8 +1,8 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import {fromLonLat} from '../../../src/ol/proj'; import {fromLonLat} from '../../../src/ol/proj.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
const center = fromLonLat([8.6, 50.1]); const center = fromLonLat([8.6, 50.1]);

View File

@@ -1,8 +1,8 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import {fromLonLat} from '../../../src/ol/proj'; import {fromLonLat} from '../../../src/ol/proj.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
const center = fromLonLat([8.6, 50.1]); const center = fromLonLat([8.6, 50.1]);

View File

@@ -42,7 +42,8 @@ const layer = new VectorLayer({
const view = new View({ const view = new View({
center: [-9.5, 78], center: [-9.5, 78],
zoom: 2, zoom: 2,
projection: 'EPSG:4326' projection: 'EPSG:4326',
multiWorld: true
}); });
new Map({ new Map({
pixelRatio: 1, pixelRatio: 1,

View File

@@ -1,9 +1,9 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import VectorTileSource from '../../../src/ol/source/VectorTile'; import VectorTileSource from '../../../src/ol/source/VectorTile.js';
import MVT from '../../../src/ol/format/MVT'; import MVT from '../../../src/ol/format/MVT.js';
import {createXYZ} from '../../../src/ol/tilegrid'; import {createXYZ} from '../../../src/ol/tilegrid.js';
import VectorTileLayer from '../../../src/ol/layer/VectorTile'; import VectorTileLayer from '../../../src/ol/layer/VectorTile.js';
const map = new Map({ const map = new Map({
pixelRatio: 2, pixelRatio: 2,

View File

@@ -1,16 +1,16 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import VectorTileSource from '../../../src/ol/source/VectorTile'; import VectorTileSource from '../../../src/ol/source/VectorTile.js';
import MVT from '../../../src/ol/format/MVT'; import MVT from '../../../src/ol/format/MVT.js';
import {createXYZ} from '../../../src/ol/tilegrid'; import {createXYZ} from '../../../src/ol/tilegrid.js';
import VectorTileLayer from '../../../src/ol/layer/VectorTile'; import VectorTileLayer from '../../../src/ol/layer/VectorTile.js';
import VectorSource from '../../../src/ol/source/Vector'; import VectorSource from '../../../src/ol/source/Vector.js';
import Feature from '../../../src/ol/Feature'; import Feature from '../../../src/ol/Feature.js';
import Point from '../../../src/ol/geom/Point'; import Point from '../../../src/ol/geom/Point.js';
import VectorLayer from '../../../src/ol/layer/Vector'; import VectorLayer from '../../../src/ol/layer/Vector.js';
import Style from '../../../src/ol/style/Style'; import Style from '../../../src/ol/style/Style.js';
import CircleStyle from '../../../src/ol/style/Circle'; import CircleStyle from '../../../src/ol/style/Circle.js';
import Fill from '../../../src/ol/style/Fill'; import Fill from '../../../src/ol/style/Fill.js';
const vectorSource = new VectorSource({ const vectorSource = new VectorSource({
features: [ features: [

View File

@@ -1,9 +1,9 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import VectorTileSource from '../../../src/ol/source/VectorTile'; import VectorTileSource from '../../../src/ol/source/VectorTile.js';
import MVT from '../../../src/ol/format/MVT'; import MVT from '../../../src/ol/format/MVT.js';
import {createXYZ} from '../../../src/ol/tilegrid'; import {createXYZ} from '../../../src/ol/tilegrid.js';
import VectorTileLayer from '../../../src/ol/layer/VectorTile'; import VectorTileLayer from '../../../src/ol/layer/VectorTile.js';
const map = new Map({ const map = new Map({
layers: [ layers: [

View File

@@ -1,9 +1,9 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import VectorTileSource from '../../../src/ol/source/VectorTile'; import VectorTileSource from '../../../src/ol/source/VectorTile.js';
import MVT from '../../../src/ol/format/MVT'; import MVT from '../../../src/ol/format/MVT.js';
import {createXYZ} from '../../../src/ol/tilegrid'; import {createXYZ} from '../../../src/ol/tilegrid.js';
import VectorTileLayer from '../../../src/ol/layer/VectorTile'; import VectorTileLayer from '../../../src/ol/layer/VectorTile.js';
new Map({ new Map({
layers: [ layers: [

View File

@@ -9,12 +9,6 @@ import Point from '../../../src/ol/geom/Point.js';
const map = new Map({ const map = new Map({
layers: [ layers: [
new TileLayer({
source: new XYZ({
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg',
maxZoom: 3
})
}),
new VectorLayer({ new VectorLayer({
zIndex: 1, zIndex: 1,
style: new Style({ style: new Style({
@@ -27,6 +21,12 @@ const map = new Map({
url: '/data/countries.json', url: '/data/countries.json',
format: new GeoJSON() format: new GeoJSON()
}) })
}),
new TileLayer({
source: new XYZ({
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg',
maxZoom: 3
})
}) })
], ],
target: 'map', target: 'map',

View File

@@ -1,11 +1,11 @@
import Map from '../../../src/ol/Map.js'; import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js'; import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js'; import TileLayer from '../../../src/ol/layer/Tile.js';
import XYZ from '../../../src/ol/source/XYZ'; import XYZ from '../../../src/ol/source/XYZ.js';
import {Vector as VectorLayer} from '../../../src/ol/layer'; import {Vector as VectorLayer} from '../../../src/ol/layer.js';
import VectorSource from '../../../src/ol/source/Vector'; import VectorSource from '../../../src/ol/source/Vector.js';
import KML from '../../../src/ol/format/KML'; import KML from '../../../src/ol/format/KML.js';
import WebGLPointsLayerRenderer from '../../../src/ol/renderer/webgl/PointsLayer'; import WebGLPointsLayerRenderer from '../../../src/ol/renderer/webgl/PointsLayer.js';
class CustomLayer extends VectorLayer { class CustomLayer extends VectorLayer {
createRenderer() { createRenderer() {

View File

@@ -57,10 +57,11 @@ import BaseObject, {getChangeEventType} from './Object.js';
* ``` * ```
* *
* @api * @api
* @template {import("./geom/Geometry.js").default} Geometry
*/ */
class Feature extends BaseObject { class Feature extends BaseObject {
/** /**
* @param {import("./geom/Geometry.js").default|Object<string, *>=} opt_geometryOrProperties * @param {Geometry|Object<string, *>=} opt_geometryOrProperties
* You may pass a Geometry object directly, or an object literal containing * You may pass a Geometry object directly, or an object literal containing
* properties. If you pass an object literal, you may include a Geometry * properties. If you pass an object literal, you may include a Geometry
* associated with a `geometry` key. * associated with a `geometry` key.
@@ -106,7 +107,7 @@ class Feature extends BaseObject {
if (opt_geometryOrProperties) { if (opt_geometryOrProperties) {
if (typeof /** @type {?} */ (opt_geometryOrProperties).getSimplifiedGeometry === 'function') { if (typeof /** @type {?} */ (opt_geometryOrProperties).getSimplifiedGeometry === 'function') {
const geometry = /** @type {import("./geom/Geometry.js").default} */ (opt_geometryOrProperties); const geometry = /** @type {Geometry} */ (opt_geometryOrProperties);
this.setGeometry(geometry); this.setGeometry(geometry);
} else { } else {
/** @type {Object<string, *>} */ /** @type {Object<string, *>} */
@@ -140,13 +141,13 @@ class Feature extends BaseObject {
* Get the feature's default geometry. A feature may have any number of named * Get the feature's default geometry. A feature may have any number of named
* geometries. The "default" geometry (the one that is rendered by default) is * geometries. The "default" geometry (the one that is rendered by default) is
* set when calling {@link module:ol/Feature~Feature#setGeometry}. * set when calling {@link module:ol/Feature~Feature#setGeometry}.
* @return {import("./geom/Geometry.js").default|undefined} The default geometry for the feature. * @return {Geometry|undefined} The default geometry for the feature.
* @api * @api
* @observable * @observable
*/ */
getGeometry() { getGeometry() {
return ( return (
/** @type {import("./geom/Geometry.js").default|undefined} */ (this.get(this.geometryName_)) /** @type {Geometry|undefined} */ (this.get(this.geometryName_))
); );
} }
@@ -218,7 +219,7 @@ class Feature extends BaseObject {
/** /**
* Set the default geometry for the feature. This will update the property * Set the default geometry for the feature. This will update the property
* with the name returned by {@link module:ol/Feature~Feature#getGeometryName}. * with the name returned by {@link module:ol/Feature~Feature#getGeometryName}.
* @param {import("./geom/Geometry.js").default|undefined} geometry The new geometry. * @param {Geometry|undefined} geometry The new geometry.
* @api * @api
* @observable * @observable
*/ */

View File

@@ -6,7 +6,6 @@ import {listen} from './events.js';
import Event from './events/Event.js'; import Event from './events/Event.js';
import EventType from './events/EventType.js'; import EventType from './events/EventType.js';
import {circular as circularPolygon} from './geom/Polygon.js'; import {circular as circularPolygon} from './geom/Polygon.js';
import {GEOLOCATION} from './has.js';
import {toRadians} from './math.js'; import {toRadians} from './math.js';
import {get as getProjection, getTransformFromProjections, identityTransform} from './proj.js'; import {get as getProjection, getTransformFromProjections, identityTransform} from './proj.js';
@@ -83,7 +82,7 @@ class GeolocationError extends Event {
* window.console.log(geolocation.getPosition()); * window.console.log(geolocation.getPosition());
* }); * });
* *
* @fires error * @fires module:ol/events/Event~Event#event:error
* @api * @api
*/ */
class Geolocation extends BaseObject { class Geolocation extends BaseObject {
@@ -160,7 +159,7 @@ class Geolocation extends BaseObject {
* @private * @private
*/ */
handleTrackingChanged_() { handleTrackingChanged_() {
if (GEOLOCATION) { if ('geolocation' in navigator) {
const tracking = this.getTracking(); const tracking = this.getTracking();
if (tracking && this.watchId_ === undefined) { if (tracking && this.watchId_ === undefined) {
this.watchId_ = navigator.geolocation.watchPosition( this.watchId_ = navigator.geolocation.watchPosition(
@@ -204,12 +203,6 @@ class Geolocation extends BaseObject {
this.changed(); this.changed();
} }
/**
* Triggered when the Geolocation returns an error.
* @event error
* @api
*/
/** /**
* @private * @private
* @param {PositionError} error error object. * @param {PositionError} error error object.

View File

@@ -58,9 +58,9 @@ class ImageWrapper extends ImageBase {
/** /**
* @private * @private
* @type {Array<import("./events.js").EventsKey>} * @type {function():void}
*/ */
this.imageListenerKeys_ = null; this.unlisten_ = null;
/** /**
* @protected * @protected
@@ -120,13 +120,12 @@ class ImageWrapper extends ImageBase {
if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) { if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) {
this.state = ImageState.LOADING; this.state = ImageState.LOADING;
this.changed(); this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.imageLoadFunction_(this, this.src_); this.imageLoadFunction_(this, this.src_);
this.unlisten_ = listenImage(
this.image_,
this.handleImageLoad_.bind(this),
this.handleImageError_.bind(this)
);
} }
} }
@@ -143,10 +142,47 @@ class ImageWrapper extends ImageBase {
* @private * @private
*/ */
unlistenImage_() { unlistenImage_() {
this.imageListenerKeys_.forEach(unlistenByKey); if (this.unlisten_) {
this.imageListenerKeys_ = null; this.unlisten_();
this.unlisten_ = null;
}
} }
} }
/**
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image element.
* @param {function():any} loadHandler Load callback function.
* @param {function():any} errorHandler Error callback function.
* @return {function():void} Callback to stop listening.
*/
export function listenImage(image, loadHandler, errorHandler) {
const img = /** @type {HTMLImageElement} */ (image);
if (img.decode) {
const promise = img.decode();
let listening = true;
const unlisten = function() {
listening = false;
};
promise.then(function() {
if (listening) {
loadHandler();
}
}).catch(function(error) {
if (listening) {
errorHandler();
}
});
return unlisten;
}
const listenerKeys = [
listenOnce(img, EventType.LOAD, loadHandler),
listenOnce(img, EventType.ERROR, errorHandler)
];
return function unlisten() {
listenerKeys.forEach(unlistenByKey);
};
}
export default ImageWrapper; export default ImageWrapper;

View File

@@ -4,8 +4,7 @@
import Tile from './Tile.js'; import Tile from './Tile.js';
import TileState from './TileState.js'; import TileState from './TileState.js';
import {createCanvasContext2D} from './dom.js'; import {createCanvasContext2D} from './dom.js';
import {listenOnce, unlistenByKey} from './events.js'; import {listenImage} from './Image.js';
import EventType from './events/EventType.js';
class ImageTile extends Tile { class ImageTile extends Tile {
@@ -47,9 +46,9 @@ class ImageTile extends Tile {
/** /**
* @private * @private
* @type {Array<import("./events.js").EventsKey>} * @type {function():void}
*/ */
this.imageListenerKeys_ = null; this.unlisten_ = null;
/** /**
* @private * @private
@@ -134,13 +133,12 @@ class ImageTile extends Tile {
if (this.state == TileState.IDLE) { if (this.state == TileState.IDLE) {
this.state = TileState.LOADING; this.state = TileState.LOADING;
this.changed(); this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.tileLoadFunction_(this, this.src_); this.tileLoadFunction_(this, this.src_);
this.unlisten_ = listenImage(
this.image_,
this.handleImageLoad_.bind(this),
this.handleImageError_.bind(this)
);
} }
} }
@@ -150,8 +148,10 @@ class ImageTile extends Tile {
* @private * @private
*/ */
unlistenImage_() { unlistenImage_() {
this.imageListenerKeys_.forEach(unlistenByKey); if (this.unlisten_) {
this.imageListenerKeys_ = null; this.unlisten_();
this.unlisten_ = null;
}
} }
} }

View File

@@ -2,7 +2,7 @@
* @module ol/Map * @module ol/Map
*/ */
import PluggableMap from './PluggableMap.js'; import PluggableMap from './PluggableMap.js';
import {defaults as defaultControls} from './control/util.js'; import {defaults as defaultControls} from './control.js';
import {defaults as defaultInteractions} from './interaction.js'; import {defaults as defaultInteractions} from './interaction.js';
import {assign} from './obj.js'; import {assign} from './obj.js';
import CompositeMapRenderer from './renderer/Composite.js'; import CompositeMapRenderer from './renderer/Composite.js';

View File

@@ -39,10 +39,11 @@ import {create as createTransform, apply as applyTransform} from './transform.js
* @property {boolean} animate * @property {boolean} animate
* @property {import("./transform.js").Transform} coordinateToPixelTransform * @property {import("./transform.js").Transform} coordinateToPixelTransform
* @property {null|import("./extent.js").Extent} extent * @property {null|import("./extent.js").Extent} extent
* @property {Array<*>} declutterItems * @property {Array<DeclutterItems>} declutterItems
* @property {import("./coordinate.js").Coordinate} focus * @property {import("./coordinate.js").Coordinate} focus
* @property {number} index * @property {number} index
* @property {Array<import("./layer/Layer.js").State>} layerStatesArray * @property {Array<import("./layer/Layer.js").State>} layerStatesArray
* @property {number} layerIndex
* @property {import("./transform.js").Transform} pixelToCoordinateTransform * @property {import("./transform.js").Transform} pixelToCoordinateTransform
* @property {Array<PostRenderFunction>} postRenderFunctions * @property {Array<PostRenderFunction>} postRenderFunctions
* @property {import("./size.js").Size} size * @property {import("./size.js").Size} size
@@ -54,6 +55,13 @@ import {create as createTransform, apply as applyTransform} from './transform.js
*/ */
/**
* @typedef {Object} DeclutterItems
* @property {Array<*>} items Declutter items of an executor.
* @property {number} opacity Layer opacity.
*/
/** /**
* @typedef {function(PluggableMap, ?FrameState): any} PostRenderFunction * @typedef {function(PluggableMap, ?FrameState): any} PostRenderFunction
*/ */
@@ -293,6 +301,11 @@ class PluggableMap extends BaseObject {
*/ */
this.interactions = optionsInternal.interactions || new Collection(); this.interactions = optionsInternal.interactions || new Collection();
/**
* @type {import("./events.js").EventsKey}
*/
this.labelCacheListenerKey_;
/** /**
* @type {Collection<import("./Overlay.js").default>} * @type {Collection<import("./Overlay.js").default>}
* @private * @private
@@ -498,6 +511,24 @@ class PluggableMap extends BaseObject {
overlay.setMap(this); 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 * @inheritDoc
@@ -515,6 +546,7 @@ class PluggableMap extends BaseObject {
cancelAnimationFrame(this.animationDelayKey_); cancelAnimationFrame(this.animationDelayKey_);
this.animationDelayKey_ = undefined; this.animationDelayKey_ = undefined;
} }
this.detachLabelCache();
this.setTarget(null); this.setTarget(null);
super.disposeInternal(); super.disposeInternal();
} }
@@ -763,6 +795,21 @@ class PluggableMap extends BaseObject {
return layers; return layers;
} }
/**
* @return {boolean} Layers have sources that are still loading.
*/
getLoading() {
const layerStatesArray = this.getLayerGroup().getLayerStatesArray();
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layer = layerStatesArray[i].layer;
const source = /** @type {import("./layer/Layer.js").default} */ (layer).getSource();
if (source && source.loading) {
return true;
}
}
return false;
}
/** /**
* Get the pixel for a coordinate. This takes a coordinate in the map view * Get the pixel for a coordinate. This takes a coordinate in the map view
* projection and returns the corresponding pixel. * projection and returns the corresponding pixel.
@@ -950,7 +997,7 @@ class PluggableMap extends BaseObject {
} }
if (frameState && this.hasListener(RenderEventType.RENDERCOMPLETE) && !frameState.animate && if (frameState && this.hasListener(RenderEventType.RENDERCOMPLETE) && !frameState.animate &&
!this.tileQueue_.getTilesLoading() && !getLoading(this.getLayers().getArray())) { !this.tileQueue_.getTilesLoading() && !this.getLoading()) {
this.renderer_.dispatchRenderEvent(RenderEventType.RENDERCOMPLETE, frameState); this.renderer_.dispatchRenderEvent(RenderEventType.RENDERCOMPLETE, frameState);
} }
@@ -994,7 +1041,6 @@ class PluggableMap extends BaseObject {
} }
if (!targetElement) { if (!targetElement) {
this.renderer_.removeLayerRenderers();
removeNode(this.viewport_); removeNode(this.viewport_);
if (this.handleResize_ !== undefined) { if (this.handleResize_ !== undefined) {
removeEventListener(EventType.RESIZE, this.handleResize_, false); removeEventListener(EventType.RESIZE, this.handleResize_, false);
@@ -1012,7 +1058,7 @@ class PluggableMap extends BaseObject {
if (!this.handleResize_) { if (!this.handleResize_) {
this.handleResize_ = this.updateSize.bind(this); this.handleResize_ = this.updateSize.bind(this);
addEventListener(EventType.RESIZE, this.handleResize_, false); window.addEventListener(EventType.RESIZE, this.handleResize_, false);
} }
} }
@@ -1102,6 +1148,19 @@ class PluggableMap extends BaseObject {
this.animationDelay_(); this.animationDelay_();
} }
/**
* Redraws all text after new fonts have loaded
*/
redrawText() {
const layerStates = this.getLayerGroup().getLayerStatesArray();
for (let i = 0, ii = layerStates.length; i < ii; ++i) {
const layer = layerStates[i].layer;
if (layer.hasRenderer()) {
layer.getRenderer().handleFontsChanged();
}
}
}
/** /**
* Request a map rendering (at the next animation frame). * Request a map rendering (at the next animation frame).
* @api * @api
@@ -1173,13 +1232,14 @@ class PluggableMap extends BaseObject {
if (size !== undefined && hasArea(size) && view && view.isDef()) { if (size !== undefined && hasArea(size) && view && view.isDef()) {
const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined); const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);
viewState = view.getState(this.pixelRatio_); viewState = view.getState(this.pixelRatio_);
frameState = /** @type {FrameState} */ ({ frameState = {
animate: false, animate: false,
coordinateToPixelTransform: this.coordinateToPixelTransform_, coordinateToPixelTransform: this.coordinateToPixelTransform_,
declutterItems: previousFrameState ? previousFrameState.declutterItems : [], declutterItems: previousFrameState ? previousFrameState.declutterItems : [],
extent: extent, extent: extent,
focus: this.focus_ ? this.focus_ : viewState.center, focus: this.focus_ ? this.focus_ : viewState.center,
index: this.frameIndex_++, index: this.frameIndex_++,
layerIndex: 0,
layerStatesArray: this.getLayerGroup().getLayerStatesArray(), layerStatesArray: this.getLayerGroup().getLayerStatesArray(),
pixelRatio: this.pixelRatio_, pixelRatio: this.pixelRatio_,
pixelToCoordinateTransform: this.pixelToCoordinateTransform_, pixelToCoordinateTransform: this.pixelToCoordinateTransform_,
@@ -1192,7 +1252,7 @@ class PluggableMap extends BaseObject {
viewState: viewState, viewState: viewState,
viewHints: viewHints, viewHints: viewHints,
wantedTiles: {} wantedTiles: {}
}); };
} }
if (frameState) { if (frameState) {
@@ -1398,23 +1458,3 @@ function createOptionsInternal(options) {
} }
export default PluggableMap; export default PluggableMap;
/**
* @param {Array<import("./layer/Base.js").default>} layers Layers.
* @return {boolean} Layers have sources that are still loading.
*/
function getLoading(layers) {
for (let i = 0, ii = layers.length; i < ii; ++i) {
const layer = layers[i];
if (typeof /** @type {?} */ (layer).getLayers === 'function') {
return getLoading(/** @type {LayerGroup} */ (layer).getLayers().getArray());
} else {
const source = /** @type {import("./layer/Layer.js").default} */ (
layer).getSource();
if (source && source.loading) {
return true;
}
}
}
return false;
}

View File

@@ -21,9 +21,9 @@ import {clamp, modulo} from './math.js';
import {assign} from './obj.js'; import {assign} from './obj.js';
import {createProjection, METERS_PER_UNIT} from './proj.js'; import {createProjection, METERS_PER_UNIT} from './proj.js';
import Units from './proj/Units.js'; import Units from './proj/Units.js';
import {equals} from './coordinate'; import {equals} from './coordinate.js';
import {easeOut} from './easing'; import {easeOut} from './easing.js';
import {createMinMaxResolution} from './resolutionconstraint'; import {createMinMaxResolution} from './resolutionconstraint.js';
/** /**
@@ -1409,11 +1409,19 @@ function animationCallback(callback, returnValue) {
*/ */
export function createCenterConstraint(options) { export function createCenterConstraint(options) {
if (options.extent !== undefined) { if (options.extent !== undefined) {
return createExtent(options.extent, options.constrainOnlyCenter, const smooth = options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true;
options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true); return createExtent(options.extent, options.constrainOnlyCenter, smooth);
} else {
return centerNone;
} }
const projection = createProjection(options.projection, 'EPSG:3857');
if (options.multiWorld !== true && projection.isGlobal()) {
const extent = projection.getExtent().slice();
extent[0] = -Infinity;
extent[2] = Infinity;
return createExtent(extent, false, false);
}
return centerNone;
} }

View File

@@ -1,6 +1,10 @@
/** /**
* @module ol/control * @module ol/control
*/ */
import Collection from './Collection.js';
import Attribution from './control/Attribution.js';
import Rotate from './control/Rotate.js';
import Zoom from './control/Zoom.js';
export {default as Attribution} from './control/Attribution.js'; export {default as Attribution} from './control/Attribution.js';
export {default as Control} from './control/Control.js'; export {default as Control} from './control/Control.js';
@@ -12,4 +16,59 @@ export {default as ScaleLine} from './control/ScaleLine.js';
export {default as Zoom} from './control/Zoom.js'; export {default as Zoom} from './control/Zoom.js';
export {default as ZoomSlider} from './control/ZoomSlider.js'; export {default as ZoomSlider} from './control/ZoomSlider.js';
export {default as ZoomToExtent} from './control/ZoomToExtent.js'; export {default as ZoomToExtent} from './control/ZoomToExtent.js';
export {defaults} from './control/util.js';
/**
* @typedef {Object} DefaultsOptions
* @property {boolean} [attribution=true] Include
* {@link module:ol/control/Attribution~Attribution}.
* @property {import("./control/Attribution.js").Options} [attributionOptions]
* Options for {@link module:ol/control/Attribution~Attribution}.
* @property {boolean} [rotate=true] Include
* {@link module:ol/control/Rotate~Rotate}.
* @property {import("./control/Rotate.js").Options} [rotateOptions] Options
* for {@link module:ol/control/Rotate~Rotate}.
* @property {boolean} [zoom] Include {@link module:ol/control/Zoom~Zoom}.
* @property {import("./control/Zoom.js").Options} [zoomOptions] Options for
* {@link module:ol/control/Zoom~Zoom}.
* @api
*/
/**
* Set of controls included in maps by default. Unless configured otherwise,
* this returns a collection containing an instance of each of the following
* controls:
* * {@link module:ol/control/Zoom~Zoom}
* * {@link module:ol/control/Rotate~Rotate}
* * {@link module:ol/control/Attribution~Attribution}
*
* @param {DefaultsOptions=} opt_options
* Defaults options.
* @return {Collection<import("./control/Control.js").default>}
* Controls.
* @api
*/
export function defaults(opt_options) {
const options = opt_options ? opt_options : {};
const controls = new Collection();
const zoomControl = options.zoom !== undefined ? options.zoom : true;
if (zoomControl) {
controls.push(new Zoom(options.zoomOptions));
}
const rotateControl = options.rotate !== undefined ? options.rotate : true;
if (rotateControl) {
controls.push(new Rotate(options.rotateOptions));
}
const attributionControl = options.attribution !== undefined ?
options.attribution : true;
if (attributionControl) {
controls.push(new Attribution(options.attributionOptions));
}
return controls;
}

View File

@@ -1,8 +1,8 @@
/** /**
* @module ol/control/OverviewMap * @module ol/control/OverviewMap
*/ */
import Collection from '../Collection.js'; import PluggableMap from '../PluggableMap.js';
import Map from '../Map.js'; import CompositeMapRenderer from '../renderer/Composite.js';
import MapEventType from '../MapEventType.js'; import MapEventType from '../MapEventType.js';
import MapProperty from '../MapProperty.js'; import MapProperty from '../MapProperty.js';
import {getChangeEventType} from '../Object.js'; import {getChangeEventType} from '../Object.js';
@@ -35,6 +35,13 @@ const MAX_RATIO = 0.75;
const MIN_RATIO = 0.1; const MIN_RATIO = 0.1;
class ControlledMap extends PluggableMap {
createRenderer() {
return new CompositeMapRenderer(this);
}
}
/** /**
* @typedef {Object} Options * @typedef {Object} Options
* @property {string} [className='ol-overviewmap'] CSS class name. * @property {string} [className='ol-overviewmap'] CSS class name.
@@ -143,12 +150,10 @@ class OverviewMap extends Control {
this.ovmapDiv_.className = 'ol-overviewmap-map'; this.ovmapDiv_.className = 'ol-overviewmap-map';
/** /**
* @type {import("../Map.js").default} * @type {ControlledMap}
* @private * @private
*/ */
this.ovmap_ = new Map({ this.ovmap_ = new ControlledMap({
controls: new Collection(),
interactions: new Collection(),
view: options.view view: options.view
}); });
const ovmap = this.ovmap_; const ovmap = this.ovmap_;

View File

@@ -1,65 +0,0 @@
/**
* @module ol/control/util
*/
import Collection from '../Collection.js';
import Attribution from './Attribution.js';
import Rotate from './Rotate.js';
import Zoom from './Zoom.js';
/**
* @typedef {Object} DefaultsOptions
* @property {boolean} [attribution=true] Include
* {@link module:ol/control/Attribution~Attribution}.
* @property {import("./Attribution.js").Options} [attributionOptions]
* Options for {@link module:ol/control/Attribution~Attribution}.
* @property {boolean} [rotate=true] Include
* {@link module:ol/control/Rotate~Rotate}.
* @property {import("./Rotate.js").Options} [rotateOptions] Options
* for {@link module:ol/control/Rotate~Rotate}.
* @property {boolean} [zoom] Include {@link module:ol/control/Zoom~Zoom}.
* @property {import("./Zoom.js").Options} [zoomOptions] Options for
* {@link module:ol/control/Zoom~Zoom}.
* @api
*/
/**
* Set of controls included in maps by default. Unless configured otherwise,
* this returns a collection containing an instance of each of the following
* controls:
* * {@link module:ol/control/Zoom~Zoom}
* * {@link module:ol/control/Rotate~Rotate}
* * {@link module:ol/control/Attribution~Attribution}
*
* @param {DefaultsOptions=} opt_options
* Defaults options.
* @return {Collection<import("./Control.js").default>}
* Controls.
* @function module:ol/control.defaults
* @api
*/
export function defaults(opt_options) {
const options = opt_options ? opt_options : {};
const controls = new Collection();
const zoomControl = options.zoom !== undefined ? options.zoom : true;
if (zoomControl) {
controls.push(new Zoom(options.zoomOptions));
}
const rotateControl = options.rotate !== undefined ? options.rotate : true;
if (rotateControl) {
controls.push(new Rotate(options.rotateOptions));
}
const attributionControl = options.attribution !== undefined ?
options.attribution : true;
if (attributionControl) {
controls.push(new Attribution(options.attributionOptions));
}
return controls;
}

View File

@@ -14,6 +14,13 @@ export default {
*/ */
CHANGE: 'change', CHANGE: 'change',
/**
* Generic error event. Triggered when an error occurs.
* @event module:ol/events/Event~Event#error
* @api
*/
ERROR: 'error',
CLEAR: 'clear', CLEAR: 'clear',
CONTEXTMENU: 'contextmenu', CONTEXTMENU: 'contextmenu',
CLICK: 'click', CLICK: 'click',
@@ -21,7 +28,6 @@ export default {
DRAGENTER: 'dragenter', DRAGENTER: 'dragenter',
DRAGOVER: 'dragover', DRAGOVER: 'dragover',
DROP: 'drop', DROP: 'drop',
ERROR: 'error',
KEYDOWN: 'keydown', KEYDOWN: 'keydown',
KEYPRESS: 'keypress', KEYPRESS: 'keypress',
LOAD: 'load', LOAD: 'load',

View File

@@ -97,9 +97,10 @@ import {assert} from '../asserts.js';
*/ */
/** /**
* @enum {string} * Enum representing the major IIIF Image API versions
*/ * @enum {string}
export const Versions = { */
const Versions = {
VERSION1: 'version1', VERSION1: 'version1',
VERSION2: 'version2', VERSION2: 'version2',
VERSION3: 'version3' VERSION3: 'version3'
@@ -275,7 +276,7 @@ versionFunctions[Versions.VERSION3] = generateVersion3Options;
class IIIFInfo { class IIIFInfo {
/** /**
* @param {ImageInformationResponse1_0|ImageInformationResponse1_1|ImageInformationResponse2|ImageInformationResponse3|string} imageInfo * @param {string|ImageInformationResponse1_0|ImageInformationResponse1_1|ImageInformationResponse2|ImageInformationResponse3} imageInfo
* Deserialized image information JSON response object or JSON response as string * Deserialized image information JSON response object or JSON response as string
*/ */
constructor(imageInfo) { constructor(imageInfo) {
@@ -283,8 +284,9 @@ class IIIFInfo {
} }
/** /**
* @param {Object|string} imageInfo Deserialized image information JSON response * @param {string|ImageInformationResponse1_0|ImageInformationResponse1_1|ImageInformationResponse2|ImageInformationResponse3} imageInfo
* object or JSON response as string * Deserialized image information JSON response object or JSON response as string
* @api
*/ */
setImageInfo(imageInfo) { setImageInfo(imageInfo) {
if (typeof imageInfo == 'string') { if (typeof imageInfo == 'string') {
@@ -296,6 +298,7 @@ class IIIFInfo {
/** /**
* @returns {Versions} Major IIIF version. * @returns {Versions} Major IIIF version.
* @api
*/ */
getImageApiVersion() { getImageApiVersion() {
if (this.imageInfo === undefined) { if (this.imageInfo === undefined) {
@@ -394,6 +397,7 @@ class IIIFInfo {
/** /**
* @param {PreferredOptions} opt_preferredOptions Optional options for preferred format and quality. * @param {PreferredOptions} opt_preferredOptions Optional options for preferred format and quality.
* @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options. * @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options.
* @api
*/ */
getTileSourceOptions(opt_preferredOptions) { getTileSourceOptions(opt_preferredOptions) {
const options = opt_preferredOptions || {}, const options = opt_preferredOptions || {},
@@ -424,3 +428,4 @@ class IIIFInfo {
} }
export default IIIFInfo; export default IIIFInfo;
export {Versions};

View File

@@ -2684,13 +2684,6 @@ function writePlacemark(node, feature, objectStack) {
return !filter[v]; return !filter[v];
}); });
if (keys.length > 0) {
const sequence = makeSequence(properties, keys);
const namesAndValues = {names: keys, values: sequence};
pushSerializeAndPop(context, PLACEMARK_SERIALIZERS,
EXTENDEDDATA_NODE_FACTORY, [namesAndValues], objectStack);
}
const styleFunction = feature.getStyleFunction(); const styleFunction = feature.getStyleFunction();
if (styleFunction) { if (styleFunction) {
// FIXME the styles returned by the style function are supposed to be // FIXME the styles returned by the style function are supposed to be
@@ -2713,6 +2706,13 @@ function writePlacemark(node, feature, objectStack) {
pushSerializeAndPop(context, PLACEMARK_SERIALIZERS, pushSerializeAndPop(context, PLACEMARK_SERIALIZERS,
OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
if (keys.length > 0) {
const sequence = makeSequence(properties, keys);
const namesAndValues = {names: keys, values: sequence};
pushSerializeAndPop(context, PLACEMARK_SERIALIZERS,
EXTENDEDDATA_NODE_FACTORY, [namesAndValues], objectStack);
}
// serialize geometry // serialize geometry
const options = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[0]); const options = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[0]);
let geometry = feature.getGeometry(); let geometry = feature.getGeometry();

View File

@@ -174,9 +174,8 @@ class Geometry extends BaseObject {
/** /**
* Create a simplified version of this geometry. For linestrings, this uses * Create a simplified version of this geometry. For linestrings, this uses
* the the {@link * the [Douglas Peucker](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm)
* https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm * algorithm. For polygons, a quantization-based
* Douglas Peucker} algorithm. For polygons, a quantization-based
* simplification is used to preserve topology. * simplification is used to preserve topology.
* @param {number} tolerance The tolerance distance for simplification. * @param {number} tolerance The tolerance distance for simplification.
* @return {Geometry} A new, simplified version of the original geometry. * @return {Geometry} A new, simplified version of the original geometry.

View File

@@ -16,8 +16,8 @@ import {lerp} from '../../math.js';
* @param {function(string, string, Object<string, number>):number} measureAndCacheTextWidth Measure and cache text width. * @param {function(string, string, Object<string, number>):number} measureAndCacheTextWidth Measure and cache text width.
* @param {string} font The font. * @param {string} font The font.
* @param {Object<string, number>} cache A cache of measured widths. * @param {Object<string, number>} cache A cache of measured widths.
* @return {Array<Array<*>>} The result array of null if `maxAngle` was * @return {Array<Array<*>>} The result array (or null if `maxAngle` was
* exceeded. Entries of the array are x, y, anchorX, angle, chunk. * exceeded). Entries of the array are x, y, anchorX, angle, chunk.
*/ */
export function drawTextOnPath( export function drawTextOnPath(
flatCoordinates, offset, end, stride, text, startM, maxAngle, scale, measureAndCacheTextWidth, font, cache) { flatCoordinates, offset, end, stride, text, startM, maxAngle, scale, measureAndCacheTextWidth, font, cache) {
@@ -35,16 +35,13 @@ export function drawTextOnPath(
let y2 = flatCoordinates[offset + 1]; let y2 = flatCoordinates[offset + 1];
let segmentM = 0; let segmentM = 0;
let segmentLength = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); let segmentLength = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
let angleChanged = false;
let chunk = ''; let index, previousAngle;
let chunkLength = 0;
let data, index, previousAngle;
for (let i = 0; i < numChars; ++i) { for (let i = 0; i < numChars; ++i) {
index = reverse ? numChars - i - 1 : i; index = reverse ? numChars - i - 1 : i;
const char = text.charAt(index); const char = text[index];
chunk = reverse ? char + chunk : chunk + char; const charLength = scale * measureAndCacheTextWidth(font, char, cache);
const charLength = scale * measureAndCacheTextWidth(font, chunk, cache) - chunkLength;
chunkLength += charLength;
const charM = startM + charLength / 2; const charM = startM + charLength / 2;
while (offset < end - stride && segmentM + segmentLength < charM) { while (offset < end - stride && segmentM + segmentLength < charM) {
x1 = x2; x1 = x2;
@@ -62,33 +59,18 @@ export function drawTextOnPath(
} }
if (previousAngle !== undefined) { if (previousAngle !== undefined) {
let delta = angle - previousAngle; let delta = angle - previousAngle;
angleChanged = angleChanged || delta !== 0;
delta += (delta > Math.PI) ? -2 * Math.PI : (delta < -Math.PI) ? 2 * Math.PI : 0; delta += (delta > Math.PI) ? -2 * Math.PI : (delta < -Math.PI) ? 2 * Math.PI : 0;
if (Math.abs(delta) > maxAngle) { if (Math.abs(delta) > maxAngle) {
return null; return null;
} }
} }
previousAngle = angle;
const interpolate = segmentPos / segmentLength; const interpolate = segmentPos / segmentLength;
const x = lerp(x1, x2, interpolate); const x = lerp(x1, x2, interpolate);
const y = lerp(y1, y2, interpolate); const y = lerp(y1, y2, interpolate);
if (previousAngle == angle) { result[index] = [x, y, charLength / 2, angle, char];
if (reverse) {
data[0] = x;
data[1] = y;
data[2] = charLength / 2;
}
data[4] = chunk;
} else {
chunk = char;
chunkLength = charLength;
data = [x, y, charLength / 2, angle, chunk];
if (reverse) {
result.unshift(data);
} else {
result.push(data);
}
previousAngle = angle;
}
startM += charLength; startM += charLength;
} }
return result; return angleChanged ? result : [[result[0][0], result[0][1], result[0][2], result[0][3], text]];
} }

View File

@@ -40,15 +40,6 @@ export const MAC = ua.indexOf('macintosh') !== -1;
export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1; export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
/**
* Is HTML5 geolocation supported in the current browser?
* @const
* @type {boolean}
* @api
*/
export const GEOLOCATION = 'geolocation' in navigator;
/** /**
* True if browser supports touch events. * True if browser supports touch events.
* @const * @const

View File

@@ -3,7 +3,7 @@
*/ */
import {scale as scaleCoordinate, rotate as rotateCoordinate} from '../coordinate.js'; import {scale as scaleCoordinate, rotate as rotateCoordinate} from '../coordinate.js';
import {easeOut} from '../easing.js'; import {easeOut} from '../easing.js';
import {noModifierKeys} from '../events/condition.js'; import {noModifierKeys, primaryAction} from '../events/condition.js';
import {FALSE} from '../functions.js'; import {FALSE} from '../functions.js';
import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js'; import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js';
@@ -12,7 +12,7 @@ import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js
* @typedef {Object} Options * @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean * @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
* to indicate whether that event should be handled. * to indicate whether that event should be handled.
* Default is {@link module:ol/events/condition~noModifierKeys}. * Default is {@link module:ol/events/condition~noModifierKeys} and {@link module:ol/events/condition~primaryAction}.
* @property {import("../Kinetic.js").default} [kinetic] Kinetic inertia to apply to the pan. * @property {import("../Kinetic.js").default} [kinetic] Kinetic inertia to apply to the pan.
*/ */
@@ -59,7 +59,7 @@ class DragPan extends PointerInteraction {
* @private * @private
* @type {import("../events/condition.js").Condition} * @type {import("../events/condition.js").Condition}
*/ */
this.condition_ = options.condition ? options.condition : noModifierKeys; this.condition_ = options.condition ? options.condition : defaultCondition;
/** /**
* @private * @private
@@ -166,4 +166,12 @@ class DragPan extends PointerInteraction {
} }
} }
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent Browser event.
* @return {boolean} Combined condition result.
*/
function defaultCondition(mapBrowserEvent) {
return noModifierKeys(mapBrowserEvent) && primaryAction(mapBrowserEvent);
}
export default DragPan; export default DragPan;

View File

@@ -373,7 +373,7 @@ class Draw extends PointerInteraction {
/** /**
* Sketch point. * Sketch point.
* @type {Feature} * @type {Feature<Point>}
* @private * @private
*/ */
this.sketchPoint_ = null; this.sketchPoint_ = null;
@@ -387,7 +387,7 @@ class Draw extends PointerInteraction {
/** /**
* Sketch line. Used when drawing polygon. * Sketch line. Used when drawing polygon.
* @type {Feature} * @type {Feature<LineString>}
* @private * @private
*/ */
this.sketchLine_ = null; this.sketchLine_ = null;
@@ -669,7 +669,7 @@ class Draw extends PointerInteraction {
this.sketchPoint_ = new Feature(new Point(coordinates)); this.sketchPoint_ = new Feature(new Point(coordinates));
this.updateSketchFeatures_(); this.updateSketchFeatures_();
} else { } else {
const sketchPointGeom = /** @type {Point} */ (this.sketchPoint_.getGeometry()); const sketchPointGeom = this.sketchPoint_.getGeometry();
sketchPointGeom.setCoordinates(coordinates); sketchPointGeom.setCoordinates(coordinates);
} }
} }
@@ -711,7 +711,7 @@ class Draw extends PointerInteraction {
*/ */
modifyDrawing_(event) { modifyDrawing_(event) {
let coordinate = event.coordinate; let coordinate = event.coordinate;
const geometry = /** @type {import("../geom/SimpleGeometry.js").default} */ (this.sketchFeature_.getGeometry()); const geometry = this.sketchFeature_.getGeometry();
let coordinates, last; let coordinates, last;
if (this.mode_ === Mode.POINT) { if (this.mode_ === Mode.POINT) {
last = this.sketchCoords_; last = this.sketchCoords_;
@@ -730,7 +730,7 @@ class Draw extends PointerInteraction {
last[1] = coordinate[1]; last[1] = coordinate[1];
this.geometryFunction_(/** @type {!LineCoordType} */ (this.sketchCoords_), geometry); this.geometryFunction_(/** @type {!LineCoordType} */ (this.sketchCoords_), geometry);
if (this.sketchPoint_) { if (this.sketchPoint_) {
const sketchPointGeom = /** @type {Point} */ (this.sketchPoint_.getGeometry()); const sketchPointGeom = this.sketchPoint_.getGeometry();
sketchPointGeom.setCoordinates(coordinate); sketchPointGeom.setCoordinates(coordinate);
} }
/** @type {LineString} */ /** @type {LineString} */
@@ -740,8 +740,8 @@ class Draw extends PointerInteraction {
if (!this.sketchLine_) { if (!this.sketchLine_) {
this.sketchLine_ = new Feature(); this.sketchLine_ = new Feature();
} }
const ring = /** @type {Polygon} */ (geometry).getLinearRing(0); const ring = geometry.getLinearRing(0);
sketchLineGeom = /** @type {LineString} */ (this.sketchLine_.getGeometry()); sketchLineGeom = this.sketchLine_.getGeometry();
if (!sketchLineGeom) { if (!sketchLineGeom) {
sketchLineGeom = new LineString(ring.getFlatCoordinates(), ring.getLayout()); sketchLineGeom = new LineString(ring.getFlatCoordinates(), ring.getLayout());
this.sketchLine_.setGeometry(sketchLineGeom); this.sketchLine_.setGeometry(sketchLineGeom);
@@ -751,7 +751,7 @@ class Draw extends PointerInteraction {
sketchLineGeom.changed(); sketchLineGeom.changed();
} }
} else if (this.sketchLineCoords_) { } else if (this.sketchLineCoords_) {
sketchLineGeom = /** @type {LineString} */ (this.sketchLine_.getGeometry()); sketchLineGeom = this.sketchLine_.getGeometry();
sketchLineGeom.setCoordinates(this.sketchLineCoords_); sketchLineGeom.setCoordinates(this.sketchLineCoords_);
} }
this.updateSketchFeatures_(); this.updateSketchFeatures_();
@@ -764,7 +764,7 @@ class Draw extends PointerInteraction {
*/ */
addToDrawing_(event) { addToDrawing_(event) {
const coordinate = event.coordinate; const coordinate = event.coordinate;
const geometry = /** @type {import("../geom/SimpleGeometry.js").default} */ (this.sketchFeature_.getGeometry()); const geometry = this.sketchFeature_.getGeometry();
let done; let done;
let coordinates; let coordinates;
if (this.mode_ === Mode.LINE_STRING) { if (this.mode_ === Mode.LINE_STRING) {
@@ -808,7 +808,7 @@ class Draw extends PointerInteraction {
if (!this.sketchFeature_) { if (!this.sketchFeature_) {
return; return;
} }
const geometry = /** @type {import("../geom/SimpleGeometry.js").default} */ (this.sketchFeature_.getGeometry()); const geometry = this.sketchFeature_.getGeometry();
let coordinates; let coordinates;
/** @type {LineString} */ /** @type {LineString} */
let sketchLineGeom; let sketchLineGeom;
@@ -822,7 +822,7 @@ class Draw extends PointerInteraction {
} else if (this.mode_ === Mode.POLYGON) { } else if (this.mode_ === Mode.POLYGON) {
coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0]; coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0];
coordinates.splice(-2, 1); coordinates.splice(-2, 1);
sketchLineGeom = /** @type {LineString} */ (this.sketchLine_.getGeometry()); sketchLineGeom = this.sketchLine_.getGeometry();
sketchLineGeom.setCoordinates(coordinates); sketchLineGeom.setCoordinates(coordinates);
this.geometryFunction_(this.sketchCoords_, geometry); this.geometryFunction_(this.sketchCoords_, geometry);
} }
@@ -846,7 +846,7 @@ class Draw extends PointerInteraction {
return; return;
} }
let coordinates = this.sketchCoords_; let coordinates = this.sketchCoords_;
const geometry = /** @type {import("../geom/SimpleGeometry.js").default} */ (sketchFeature.getGeometry()); const geometry = sketchFeature.getGeometry();
if (this.mode_ === Mode.LINE_STRING) { if (this.mode_ === Mode.LINE_STRING) {
// remove the redundant last point // remove the redundant last point
coordinates.pop(); coordinates.pop();
@@ -900,12 +900,12 @@ class Draw extends PointerInteraction {
* Extend an existing geometry by adding additional points. This only works * Extend an existing geometry by adding additional points. This only works
* on features with `LineString` geometries, where the interaction will * on features with `LineString` geometries, where the interaction will
* extend lines by adding points to the end of the coordinates array. * extend lines by adding points to the end of the coordinates array.
* @param {!Feature} feature Feature to be extended. * @param {!Feature<LineString>} feature Feature to be extended.
* @api * @api
*/ */
extend(feature) { extend(feature) {
const geometry = feature.getGeometry(); const geometry = feature.getGeometry();
const lineString = /** @type {LineString} */ (geometry); const lineString = geometry;
this.sketchFeature_ = feature; this.sketchFeature_ = feature;
this.sketchCoords_ = lineString.getCoordinates(); this.sketchCoords_ = lineString.getCoordinates();
const last = this.sketchCoords_[this.sketchCoords_.length - 1]; const last = this.sketchCoords_[this.sketchCoords_.length - 1];

View File

@@ -126,7 +126,7 @@ class Extent extends PointerInteraction {
/** /**
* Feature for displaying the visible pointer * Feature for displaying the visible pointer
* @type {Feature} * @type {Feature<Point>}
* @private * @private
*/ */
this.vertexFeature_ = null; this.vertexFeature_ = null;
@@ -265,7 +265,7 @@ class Extent extends PointerInteraction {
this.vertexFeature_ = vertexFeature; this.vertexFeature_ = vertexFeature;
this.vertexOverlay_.getSource().addFeature(vertexFeature); this.vertexOverlay_.getSource().addFeature(vertexFeature);
} else { } else {
const geometry = /** @type {Point} */ (vertexFeature.getGeometry()); const geometry = vertexFeature.getGeometry();
geometry.setCoordinates(vertex); geometry.setCoordinates(vertex);
} }
return vertexFeature; return vertexFeature;

View File

@@ -660,7 +660,7 @@ class Modify extends PointerInteraction {
this.vertexFeature_ = vertexFeature; this.vertexFeature_ = vertexFeature;
this.overlay_.getSource().addFeature(vertexFeature); this.overlay_.getSource().addFeature(vertexFeature);
} else { } else {
const geometry = /** @type {Point} */ (vertexFeature.getGeometry()); const geometry = vertexFeature.getGeometry();
geometry.setCoordinates(coordinates); geometry.setCoordinates(coordinates);
} }
return vertexFeature; return vertexFeature;
@@ -785,7 +785,7 @@ class Modify extends PointerInteraction {
const vertexFeature = this.vertexFeature_; const vertexFeature = this.vertexFeature_;
if (vertexFeature) { if (vertexFeature) {
const insertVertices = []; const insertVertices = [];
const geometry = /** @type {Point} */ (vertexFeature.getGeometry()); const geometry = vertexFeature.getGeometry();
const vertex = geometry.getCoordinates(); const vertex = geometry.getCoordinates();
const vertexExtent = boundingExtent([vertex]); const vertexExtent = boundingExtent([vertex]);
const segmentDataMatches = this.rBush_.getInExtent(vertexExtent); const segmentDataMatches = this.rBush_.getInExtent(vertexExtent);

View File

@@ -83,6 +83,9 @@ class BaseLayer extends BaseObject {
} }
/** /**
* This method is not meant to be called by layers or layer renderers because the state
* is incorrect if the layer is included in a layer group.
*
* @param {boolean=} opt_managed Layer is managed. * @param {boolean=} opt_managed Layer is managed.
* @return {import("./Layer.js").State} Layer state. * @return {import("./Layer.js").State} Layer state.
*/ */

View File

@@ -3,27 +3,27 @@
*/ */
import VectorLayer from './Vector.js'; import VectorLayer from './Vector.js';
import {assign} from '../obj.js'; import {assign} from '../obj.js';
import {degreesToStringHDMS} from '../coordinate'; import {degreesToStringHDMS} from '../coordinate.js';
import Text from '../style/Text'; import Text from '../style/Text.js';
import Fill from '../style/Fill'; import Fill from '../style/Fill.js';
import Stroke from '../style/Stroke'; import Stroke from '../style/Stroke.js';
import LineString from '../geom/LineString.js'; import LineString from '../geom/LineString.js';
import VectorSource from '../source/Vector'; import VectorSource from '../source/Vector.js';
import { import {
equivalent as equivalentProjection, equivalent as equivalentProjection,
get as getProjection, get as getProjection,
getTransform, getTransform,
transformExtent transformExtent
} from '../proj'; } from '../proj.js';
import {getCenter, intersects, equals, getIntersection, isEmpty} from '../extent'; import {getCenter, intersects, equals, getIntersection, isEmpty} from '../extent.js';
import {clamp} from '../math'; import {clamp} from '../math.js';
import Style from '../style/Style'; import Style from '../style/Style.js';
import Feature from '../Feature'; import Feature from '../Feature.js';
import {bbox} from '../loadingstrategy'; import {bbox} from '../loadingstrategy.js';
import {meridian, parallel} from '../geom/flat/geodesic'; import {meridian, parallel} from '../geom/flat/geodesic.js';
import GeometryLayout from '../geom/GeometryLayout'; import GeometryLayout from '../geom/GeometryLayout.js';
import Point from '../geom/Point'; import Point from '../geom/Point.js';
import Collection from '../Collection'; import Collection from '../Collection.js';
/** /**

View File

@@ -6,7 +6,7 @@ import {getChangeEventType} from '../Object.js';
import {createCanvasContext2D} from '../dom.js'; import {createCanvasContext2D} from '../dom.js';
import VectorLayer from './Vector.js'; import VectorLayer from './Vector.js';
import {assign} from '../obj.js'; import {assign} from '../obj.js';
import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer'; import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer.js';
/** /**

View File

@@ -10,6 +10,10 @@ import {assign} from '../obj.js';
import RenderEventType from '../render/EventType.js'; import RenderEventType from '../render/EventType.js';
import SourceState from '../source/State.js'; import SourceState from '../source/State.js';
/**
* @typedef {function(import("../PluggableMap.js").FrameState):HTMLElement} RenderFunction
*/
/** /**
* @typedef {Object} Options * @typedef {Object} Options
@@ -29,6 +33,8 @@ import SourceState from '../source/State.js';
* the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after * the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after
* construction. * construction.
* @property {import("../PluggableMap.js").default} [map] Map. * @property {import("../PluggableMap.js").default} [map] Map.
* @property {RenderFunction} [render] Render function. Takes the frame state as input and is expected to return an
* HTML element. Will overwrite the default rendering for the layer.
*/ */
@@ -47,8 +53,10 @@ import SourceState from '../source/State.js';
/** /**
* @classdesc * @classdesc
* Abstract base class; normally only used for creating subclasses and not * Base class from which all layer types are derived. This should only be instantiated
* instantiated in apps. * in the case where a custom layer is be added to the map with a custom `render` function.
* Such a function can be specified in the `options` object, and is expected to return an HTML element.
*
* A visual representation of raster or vector map data. * A visual representation of raster or vector map data.
* Layers group together those properties that pertain to how the data is to be * Layers group together those properties that pertain to how the data is to be
* displayed, irrespective of the source of that data. * displayed, irrespective of the source of that data.
@@ -64,6 +72,7 @@ import SourceState from '../source/State.js';
* @fires import("../render/Event.js").RenderEvent#postrender * @fires import("../render/Event.js").RenderEvent#postrender
* *
* @template {import("../source/Source.js").default} SourceType * @template {import("../source/Source.js").default} SourceType
* @api
*/ */
class Layer extends BaseLayer { class Layer extends BaseLayer {
/** /**
@@ -100,6 +109,11 @@ class Layer extends BaseLayer {
*/ */
this.renderer_ = null; this.renderer_ = null;
// Overwrite default render method with a custom one
if (options.render) {
this.render = options.render;
}
if (options.map) { if (options.map) {
this.setMap(options.map); this.setMap(options.map);
} }
@@ -175,13 +189,15 @@ class Layer extends BaseLayer {
* In charge to manage the rendering of the layer. One layer type is * In charge to manage the rendering of the layer. One layer type is
* bounded with one layer renderer. * bounded with one layer renderer.
* @param {?import("../PluggableMap.js").FrameState} frameState Frame state. * @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
* @param {HTMLElement} target Target which the renderer may (but need not) use
* for rendering its content.
* @return {HTMLElement} The rendered element. * @return {HTMLElement} The rendered element.
*/ */
render(frameState) { render(frameState, target) {
const layerRenderer = this.getRenderer(); const layerRenderer = this.getRenderer();
const layerState = this.getLayerState();
if (layerRenderer.prepareFrame(frameState, layerState)) { if (layerRenderer.prepareFrame(frameState)) {
return layerRenderer.renderFrame(frameState, layerState); return layerRenderer.renderFrame(frameState, target);
} }
} }
@@ -240,6 +256,13 @@ class Layer extends BaseLayer {
return this.renderer_; return this.renderer_;
} }
/**
* @return {boolean} The layer has a renderer.
*/
hasRenderer() {
return !!this.renderer_;
}
/** /**
* Create a renderer for this layer. * Create a renderer for this layer.
* @return {import("../renderer/Layer.js").default} A layer renderer. * @return {import("../renderer/Layer.js").default} A layer renderer.

View File

@@ -1,7 +1,6 @@
/** /**
* @module ol/math * @module ol/math
*/ */
import {assert} from './asserts.js';
/** /**
* Takes a number and clamps it to within the provided bounds. * Takes a number and clamps it to within the provided bounds.
@@ -43,16 +42,6 @@ export const cosh = (function() {
}()); }());
/**
* @param {number} x X.
* @return {number} The smallest power of two greater than or equal to x.
*/
export function roundUpToPowerOfTwo(x) {
assert(0 < x, 29); // `x` must be greater than `0`
return Math.pow(2, Math.ceil(Math.log(x) / Math.LN2));
}
/** /**
* Returns the square of the closest distance between the point (x, y) and the * Returns the square of the closest distance between the point (x, y) and the
* line segment (x1, y1) to (x2, y2). * line segment (x1, y1) to (x2, y2).

View File

@@ -127,11 +127,6 @@
right: .5em; right: .5em;
top: .5em; top: .5em;
} }
@media print {
.ol-control {
display: none;
}
}
.ol-control button { .ol-control button {
display: block; display: block;

View File

@@ -122,9 +122,10 @@ export function renderDeclutterItems(frameState, declutterTree) {
} }
const items = frameState.declutterItems; const items = frameState.declutterItems;
for (let z = items.length - 1; z >= 0; --z) { for (let z = items.length - 1; z >= 0; --z) {
const zIndexItems = items[z]; const item = items[z];
const zIndexItems = item.items;
for (let i = 0, ii = zIndexItems.length; i < ii; i += 3) { for (let i = 0, ii = zIndexItems.length; i < ii; i += 3) {
declutterTree = zIndexItems[i].renderDeclutter(zIndexItems[i + 1], zIndexItems[i + 2], declutterTree); declutterTree = zIndexItems[i].renderDeclutter(zIndexItems[i + 1], zIndexItems[i + 2], item.opacity, declutterTree);
} }
} }
items.length = 0; items.length = 0;

View File

@@ -403,7 +403,7 @@ export function drawImage(context,
context.drawImage(image, originX, originY, w, h, x, y, w * scale, h * scale); context.drawImage(image, originX, originY, w, h, x, y, w * scale, h * scale);
if (alpha) { if (opacity != 1) {
context.globalAlpha = alpha; context.globalAlpha = alpha;
} }
if (transform) { if (transform) {

View File

@@ -21,7 +21,7 @@ import {
import {createCanvasContext2D} from '../../dom.js'; import {createCanvasContext2D} from '../../dom.js';
import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js'; import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
import Disposable from '../../Disposable.js'; import Disposable from '../../Disposable.js';
import rbush from 'rbush'; import RBush from 'rbush';
/** /**
@@ -193,9 +193,10 @@ class Executor extends Disposable {
const width = measureTextWidths(textState.font, lines, widths); const width = measureTextWidths(textState.font, lines, widths);
const lineHeight = measureTextHeight(textState.font); const lineHeight = measureTextHeight(textState.font);
const height = lineHeight * numLines; const height = lineHeight * numLines;
const renderWidth = (width + strokeWidth); const renderWidth = width + strokeWidth;
const context = createCanvasContext2D( const context = createCanvasContext2D(
Math.ceil(renderWidth * scale), // make canvas 2 pixels wider to account for italic text width measurement errors
Math.ceil((renderWidth + 2) * scale),
Math.ceil((height + strokeWidth) * scale)); Math.ceil((height + strokeWidth) * scale));
label = context.canvas; label = context.canvas;
labelCache.set(key, label); labelCache.set(key, label);
@@ -220,7 +221,7 @@ class Executor extends Disposable {
context.textBaseline = 'middle'; context.textBaseline = 'middle';
context.textAlign = 'center'; context.textAlign = 'center';
const leftRight = (0.5 - align); const leftRight = (0.5 - align);
const x = align * label.width / scale + leftRight * strokeWidth; const x = align * renderWidth + leftRight * strokeWidth;
let i; let i;
if (strokeKey) { if (strokeKey) {
for (i = 0; i < numLines; ++i) { for (i = 0; i < numLines; ++i) {
@@ -361,10 +362,12 @@ class Executor extends Disposable {
const declutterArgs = intersects ? const declutterArgs = intersects ?
[context, transform ? transform.slice(0) : null, opacity, image, originX, originY, w, h, x, y, scale] : [context, transform ? transform.slice(0) : null, opacity, image, originX, originY, w, h, x, y, scale] :
null; null;
if (declutterArgs && fillStroke) { if (declutterArgs) {
declutterArgs.push(fillInstruction, strokeInstruction, p1, p2, p3, p4); if (fillStroke) {
declutterArgs.push(fillInstruction, strokeInstruction, p1, p2, p3, p4);
}
declutterGroup.push(declutterArgs);
} }
declutterGroup.push(declutterArgs);
} else if (intersects) { } else if (intersects) {
if (fillStroke) { if (fillStroke) {
this.replayTextBackground_(context, p1, p2, p3, p4, this.replayTextBackground_(context, p1, p2, p3, p4,
@@ -413,10 +416,11 @@ class Executor extends Disposable {
/** /**
* @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group. * @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group.
* @param {import("../../Feature.js").FeatureLike} feature Feature. * @param {import("../../Feature.js").FeatureLike} feature Feature.
* @param {number} opacity Layer opacity.
* @param {?} declutterTree Declutter tree. * @param {?} declutterTree Declutter tree.
* @return {?} Declutter tree. * @return {?} Declutter tree.
*/ */
renderDeclutter(declutterGroup, feature, declutterTree) { renderDeclutter(declutterGroup, feature, opacity, declutterTree) {
if (declutterGroup && declutterGroup.length > 5) { if (declutterGroup && declutterGroup.length > 5) {
const groupCount = declutterGroup[4]; const groupCount = declutterGroup[4];
if (groupCount == 1 || groupCount == declutterGroup.length - 5) { if (groupCount == 1 || groupCount == declutterGroup.length - 5) {
@@ -429,19 +433,25 @@ class Executor extends Disposable {
value: feature value: feature
}; };
if (!declutterTree) { if (!declutterTree) {
declutterTree = rbush(9, undefined); declutterTree = new RBush(9);
} }
if (!declutterTree.collides(box)) { if (!declutterTree.collides(box)) {
declutterTree.insert(box); declutterTree.insert(box);
for (let j = 5, jj = declutterGroup.length; j < jj; ++j) { for (let j = 5, jj = declutterGroup.length; j < jj; ++j) {
const declutterData = /** @type {Array} */ (declutterGroup[j]); const declutterData = /** @type {Array} */ (declutterGroup[j]);
if (declutterData) { const context = declutterData[0];
if (declutterData.length > 11) { const currentAlpha = context.globalAlpha;
this.replayTextBackground_(declutterData[0], if (currentAlpha !== opacity) {
declutterData[13], declutterData[14], declutterData[15], declutterData[16], context.globalAlpha = opacity;
declutterData[11], declutterData[12]); }
} if (declutterData.length > 11) {
drawImage.apply(undefined, declutterData); this.replayTextBackground_(declutterData[0],
declutterData[13], declutterData[14], declutterData[15], declutterData[16],
declutterData[11], declutterData[12]);
}
drawImage.apply(undefined, declutterData);
if (currentAlpha !== opacity) {
context.globalAlpha = currentAlpha;
} }
} }
} }
@@ -471,7 +481,9 @@ class Executor extends Disposable {
const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline]; const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline];
const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0; const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
const anchorX = align * label.width / pixelRatio + 2 * (0.5 - align) * strokeWidth; // Remove the 2 pixels we added in getTextImage() for the anchor
const width = label.width / pixelRatio - 2 * textState.scale;
const anchorX = align * width + 2 * (0.5 - align) * strokeWidth;
const anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth; const anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth;
return { return {

View File

@@ -82,7 +82,7 @@ class ExecutorGroup extends Disposable {
* @private * @private
* @type {CanvasRenderingContext2D} * @type {CanvasRenderingContext2D}
*/ */
this.hitDetectionContext_ = createCanvasContext2D(1, 1); this.hitDetectionContext_ = null;
/** /**
* @private * @private
@@ -137,8 +137,11 @@ class ExecutorGroup extends Disposable {
executors[key].disposeInternal(); executors[key].disposeInternal();
} }
} }
const canvas = this.hitDetectionContext_.canvas; if (this.hitDetectionContext_) {
canvas.width = canvas.height = 0; const canvas = this.hitDetectionContext_.canvas;
canvas.width = canvas.height = 0;
}
super.disposeInternal(); super.disposeInternal();
} }
@@ -187,6 +190,10 @@ class ExecutorGroup extends Disposable {
1 / resolution, -1 / resolution, 1 / resolution, -1 / resolution,
-rotation, -rotation,
-coordinate[0], -coordinate[1]); -coordinate[0], -coordinate[1]);
if (!this.hitDetectionContext_) {
this.hitDetectionContext_ = createCanvasContext2D(contextSize, contextSize);
}
const context = this.hitDetectionContext_; const context = this.hitDetectionContext_;
if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) { if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) {
@@ -423,10 +430,11 @@ export function getCircleArray(radius) {
* @param {!Object<string, Array<*>>} declutterReplays Declutter replays. * @param {!Object<string, Array<*>>} declutterReplays Declutter replays.
* @param {CanvasRenderingContext2D} context Context. * @param {CanvasRenderingContext2D} context Context.
* @param {number} rotation Rotation. * @param {number} rotation Rotation.
* @param {number} opacity Opacity.
* @param {boolean} snapToPixel Snap point symbols and text to integer pixels. * @param {boolean} snapToPixel Snap point symbols and text to integer pixels.
* @param {Array<Array<*>>} declutterItems Declutter items. * @param {Array<import("../../PluggableMap.js").DeclutterItems>} declutterItems Declutter items.
*/ */
export function replayDeclutter(declutterReplays, context, rotation, snapToPixel, declutterItems) { export function replayDeclutter(declutterReplays, context, rotation, opacity, snapToPixel, declutterItems) {
const zs = Object.keys(declutterReplays).map(Number).sort(numberSafeCompareFunction); const zs = Object.keys(declutterReplays).map(Number).sort(numberSafeCompareFunction);
const skippedFeatureUids = {}; const skippedFeatureUids = {};
for (let z = 0, zz = zs.length; z < zz; ++z) { for (let z = 0, zz = zs.length; z < zz; ++z) {
@@ -436,7 +444,10 @@ export function replayDeclutter(declutterReplays, context, rotation, snapToPixel
const executor = executorData[i++]; const executor = executorData[i++];
if (executor !== currentExecutor) { if (executor !== currentExecutor) {
currentExecutor = executor; currentExecutor = executor;
declutterItems.push(executor.declutterItems); declutterItems.push({
items: executor.declutterItems,
opacity: opacity
});
} }
const transform = executorData[i++]; const transform = executorData[i++];
executor.execute(context, transform, rotation, skippedFeatureUids, snapToPixel); executor.execute(context, transform, rotation, skippedFeatureUids, snapToPixel);

View File

@@ -8,6 +8,7 @@ import RenderEventType from '../render/EventType.js';
import MapRenderer from './Map.js'; import MapRenderer from './Map.js';
import SourceState from '../source/State.js'; import SourceState from '../source/State.js';
import {replaceChildren} from '../dom.js'; import {replaceChildren} from '../dom.js';
import {labelCache} from '../render/canvas.js';
/** /**
@@ -22,6 +23,7 @@ class CompositeMapRenderer extends MapRenderer {
*/ */
constructor(map) { constructor(map) {
super(map); super(map);
map.attachLabelCache(labelCache);
/** /**
* @private * @private
@@ -79,24 +81,26 @@ class CompositeMapRenderer extends MapRenderer {
this.calculateMatrices2D(frameState); this.calculateMatrices2D(frameState);
this.dispatchRenderEvent(RenderEventType.PRECOMPOSE, frameState); this.dispatchRenderEvent(RenderEventType.PRECOMPOSE, frameState);
const layerStatesArray = frameState.layerStatesArray; const layerStatesArray = frameState.layerStatesArray.sort(function(a, b) {
return a.zIndex - b.zIndex;
});
const viewResolution = frameState.viewState.resolution; const viewResolution = frameState.viewState.resolution;
this.children_.length = 0; this.children_.length = 0;
let previousElement = null;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i]; const layerState = layerStatesArray[i];
if (!visibleAtResolution(layerState, viewResolution) || layerState.sourceState != SourceState.READY) { frameState.layerIndex = i;
if (!visibleAtResolution(layerState, viewResolution) ||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
continue; continue;
} }
const layer = layerState.layer; const layer = layerState.layer;
const element = layer.render(frameState); const element = layer.render(frameState, previousElement);
if (element) { if (element !== previousElement) {
const zIndex = layerState.zIndex;
if (zIndex !== element.style.zIndex) {
element.style.zIndex = zIndex === Infinity ? Number.MAX_SAFE_INTEGER : zIndex;
}
this.children_.push(element); this.children_.push(element);
previousElement = element;
} }
} }
super.renderFrame(frameState); super.renderFrame(frameState);
@@ -110,7 +114,6 @@ class CompositeMapRenderer extends MapRenderer {
this.renderedVisible_ = true; this.renderedVisible_ = true;
} }
this.scheduleRemoveUnusedLayerRenderers(frameState);
this.scheduleExpireIconCache(frameState); this.scheduleExpireIconCache(frameState);
} }
@@ -127,11 +130,8 @@ class CompositeMapRenderer extends MapRenderer {
for (let i = numLayers - 1; i >= 0; --i) { for (let i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i]; const layerState = layerStates[i];
const layer = layerState.layer; const layer = layerState.layer;
if (visibleAtResolution(layerState, viewResolution) && layerFilter(layer)) { if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter(layer)) {
const layerRenderer = this.getLayerRenderer(layer); const layerRenderer = layer.getRenderer();
if (!layerRenderer) {
continue;
}
const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance); const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance);
if (data) { if (data) {
const result = callback(layer, data); const result = callback(layer, data);

View File

@@ -29,10 +29,9 @@ class LayerRenderer extends Observable {
* Determine whether render should be called. * Determine whether render should be called.
* @abstract * @abstract
* @param {import("../PluggableMap.js").FrameState} frameState Frame state. * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../layer/Layer.js").State} layerState Layer state.
* @return {boolean} Layer is ready to be rendered. * @return {boolean} Layer is ready to be rendered.
*/ */
prepareFrame(frameState, layerState) { prepareFrame(frameState) {
return abstract(); return abstract();
} }
@@ -40,10 +39,10 @@ class LayerRenderer extends Observable {
* Render the layer. * Render the layer.
* @abstract * @abstract
* @param {import("../PluggableMap.js").FrameState} frameState Frame state. * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../layer/Layer.js").State} layerState Layer state. * @param {HTMLElement} target Target that may be used to render content to.
* @return {HTMLElement} The rendered element. * @return {HTMLElement} The rendered element.
*/ */
renderFrame(frameState, layerState) { renderFrame(frameState, target) {
return abstract(); return abstract();
} }
@@ -115,6 +114,12 @@ class LayerRenderer extends Observable {
return this.layer_; return this.layer_;
} }
/**
* Perform action necessary to get the layer rendered after new fonts have loaded
* @abstract
*/
handleFontsChanged() {}
/** /**
* Handle changes in image state. * Handle changes in image state.
* @param {import("../events/Event.js").default} event Image change event. * @param {import("../events/Event.js").default} event Image change event.

View File

@@ -3,8 +3,6 @@
*/ */
import {abstract, getUid} from '../util.js'; import {abstract, getUid} from '../util.js';
import Disposable from '../Disposable.js'; import Disposable from '../Disposable.js';
import {listen, unlistenByKey} from '../events.js';
import EventType from '../events/EventType.js';
import {getWidth} from '../extent.js'; import {getWidth} from '../extent.js';
import {TRUE} from '../functions.js'; import {TRUE} from '../functions.js';
import {visibleAtResolution} from '../layer/Layer.js'; import {visibleAtResolution} from '../layer/Layer.js';
@@ -34,18 +32,6 @@ class MapRenderer extends Disposable {
*/ */
this.declutterTree_ = null; this.declutterTree_ = null;
/**
* @private
* @type {!Object<string, import("./Layer.js").default>}
*/
this.layerRenderers_ = {};
/**
* @private
* @type {Object<string, import("../events.js").EventsKey>}
*/
this.layerRendererListeners_ = {};
} }
/** /**
@@ -75,15 +61,6 @@ class MapRenderer extends Disposable {
makeInverse(pixelToCoordinateTransform, coordinateToPixelTransform); makeInverse(pixelToCoordinateTransform, coordinateToPixelTransform);
} }
/**
* Removes all layer renderers.
*/
removeLayerRenderers() {
for (const key in this.layerRenderers_) {
this.removeLayerRendererByKey_(key).dispose();
}
}
/** /**
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate. * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {import("../PluggableMap.js").FrameState} frameState FrameState. * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
@@ -149,8 +126,8 @@ class MapRenderer extends Disposable {
for (i = numLayers - 1; i >= 0; --i) { for (i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i]; const layerState = layerStates[i];
const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer); const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer);
if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) {
const layerRenderer = this.getLayerRenderer(layer); const layerRenderer = layer.getRenderer();
const source = layer.getSource(); const source = layer.getSource();
if (layerRenderer && source) { if (layerRenderer && source) {
const callback = forEachFeatureAtCoordinate.bind(null, layerState.managed); const callback = forEachFeatureAtCoordinate.bind(null, layerState.managed);
@@ -203,35 +180,6 @@ class MapRenderer extends Disposable {
return hasFeature !== undefined; return hasFeature !== undefined;
} }
/**
* @param {import("../layer/Layer.js").default} layer Layer.
* @protected
* @return {import("./Layer.js").default} Layer renderer. May return null.
*/
getLayerRenderer(layer) {
const layerKey = getUid(layer);
if (layerKey in this.layerRenderers_) {
return this.layerRenderers_[layerKey];
}
const renderer = layer.getRenderer();
if (!renderer) {
return null;
}
this.layerRenderers_[layerKey] = renderer;
this.layerRendererListeners_[layerKey] = listen(renderer, EventType.CHANGE, this.handleLayerRendererChange_, this);
return renderer;
}
/**
* @protected
* @return {Object<string, import("./Layer.js").default>} Layer renderers.
*/
getLayerRenderers() {
return this.layerRenderers_;
}
/** /**
* @return {import("../PluggableMap.js").default} Map. * @return {import("../PluggableMap.js").default} Map.
*/ */
@@ -239,29 +187,6 @@ class MapRenderer extends Disposable {
return this.map_; return this.map_;
} }
/**
* Handle changes in a layer renderer.
* @private
*/
handleLayerRendererChange_() {
this.map_.render();
}
/**
* @param {string} layerKey Layer key.
* @return {import("./Layer.js").default} Layer renderer.
* @private
*/
removeLayerRendererByKey_(layerKey) {
const layerRenderer = this.layerRenderers_[layerKey];
delete this.layerRenderers_[layerKey];
unlistenByKey(this.layerRendererListeners_[layerKey]);
delete this.layerRendererListeners_[layerKey];
return layerRenderer;
}
/** /**
* Render. * Render.
* @param {?import("../PluggableMap.js").FrameState} frameState Frame state. * @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
@@ -279,21 +204,6 @@ class MapRenderer extends Disposable {
frameState.postRenderFunctions.push(expireIconCache); frameState.postRenderFunctions.push(expireIconCache);
} }
} }
/**
* @param {!import("../PluggableMap.js").FrameState} frameState Frame state.
* @protected
*/
scheduleRemoveUnusedLayerRenderers(frameState) {
const layerStatesMap = getLayerStatesMap(frameState.layerStatesArray);
for (const layerKey in this.layerRenderers_) {
if (!(layerKey in layerStatesMap)) {
frameState.postRenderFunctions.push(function() {
this.removeLayerRendererByKey_(layerKey).dispose();
}.bind(this));
}
}
}
} }
@@ -305,15 +215,4 @@ function expireIconCache(map, frameState) {
iconImageCache.expire(); iconImageCache.expire();
} }
/**
* @param {Array<import("../layer/Layer.js").State>} layerStatesArray Layer states array.
* @return {Object<string, import("../layer/Layer.js").State>} States mapped by layer uid.
*/
function getLayerStatesMap(layerStatesArray) {
return layerStatesArray.reduce(function(acc, state) {
acc[getUid(state.layer)] = state;
return acc;
}, {});
}
export default MapRenderer; export default MapRenderer;

View File

@@ -38,7 +38,8 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
prepareFrame(frameState, layerState) { prepareFrame(frameState) {
const layerState = frameState.layerStatesArray[frameState.layerIndex];
const pixelRatio = frameState.pixelRatio; const pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState; const viewState = frameState.viewState;
const viewResolution = viewState.resolution; const viewResolution = viewState.resolution;
@@ -72,11 +73,12 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
renderFrame(frameState, layerState) { renderFrame(frameState, target) {
const image = this.image_; const image = this.image_;
const imageExtent = image.getExtent(); const imageExtent = image.getExtent();
const imageResolution = image.getResolution(); const imageResolution = image.getResolution();
const imagePixelRatio = image.getPixelRatio(); const imagePixelRatio = image.getPixelRatio();
const layerState = frameState.layerStatesArray[frameState.layerIndex];
const pixelRatio = frameState.pixelRatio; const pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState; const viewState = frameState.viewState;
const viewCenter = viewState.center; const viewCenter = viewState.center;
@@ -101,13 +103,15 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
); );
makeInverse(this.inversePixelTransform_, this.pixelTransform_); makeInverse(this.inversePixelTransform_, this.pixelTransform_);
this.useContainer(target, this.pixelTransform_, layerState.opacity);
const context = this.context; const context = this.context;
const canvas = context.canvas; const canvas = context.canvas;
if (canvas.width != width || canvas.height != height) { if (canvas.width != width || canvas.height != height) {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
} else { } else if (!this.containerReused) {
context.clearRect(0, 0, width, height); context.clearRect(0, 0, width, height);
} }
@@ -138,8 +142,17 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
this.preRender(context, frameState); this.preRender(context, frameState);
if (dw >= 0.5 && dh >= 0.5) { if (dw >= 0.5 && dh >= 0.5) {
const opacity = layerState.opacity;
let previousAlpha;
if (opacity !== 1) {
previousAlpha = this.context.globalAlpha;
this.context.globalAlpha = opacity;
}
this.context.drawImage(img, 0, 0, +img.width, +img.height, this.context.drawImage(img, 0, 0, +img.width, +img.height,
Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh)); Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
if (opacity !== 1) {
this.context.globalAlpha = previousAlpha;
}
} }
this.postRender(context, frameState); this.postRender(context, frameState);
@@ -147,17 +160,12 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
context.restore(); context.restore();
} }
const opacity = layerState.opacity;
if (opacity !== parseFloat(canvas.style.opacity)) {
canvas.style.opacity = opacity;
}
const canvasTransform = transformToString(this.pixelTransform_); const canvasTransform = transformToString(this.pixelTransform_);
if (canvasTransform !== canvas.style.transform) { if (canvasTransform !== canvas.style.transform) {
canvas.style.transform = canvasTransform; canvas.style.transform = canvasTransform;
} }
return canvas; return this.container;
} }

View File

@@ -7,7 +7,7 @@ import RenderEvent from '../../render/Event.js';
import RenderEventType from '../../render/EventType.js'; import RenderEventType from '../../render/EventType.js';
import {rotateAtOffset} from '../../render/canvas.js'; import {rotateAtOffset} from '../../render/canvas.js';
import LayerRenderer from '../Layer.js'; import LayerRenderer from '../Layer.js';
import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js'; import {create as createTransform, apply as applyTransform, compose as composeTransform, toString as transformToString} from '../../transform.js';
/** /**
* @abstract * @abstract
@@ -21,6 +21,12 @@ class CanvasLayerRenderer extends LayerRenderer {
super(layer); super(layer);
/**
* @protected
* @type {HTMLElement}
*/
this.container = null;
/** /**
* @protected * @protected
* @type {number} * @type {number}
@@ -55,20 +61,57 @@ class CanvasLayerRenderer extends LayerRenderer {
* @protected * @protected
* @type {CanvasRenderingContext2D} * @type {CanvasRenderingContext2D}
*/ */
this.context = createCanvasContext2D(); this.context = null;
/**
* @type {boolean}
*/
this.containerReused = false;
const canvas = this.context.canvas;
canvas.style.position = 'absolute';
canvas.style.transformOrigin = 'top left';
canvas.className = this.getLayer().getClassName();
} }
/** /**
* @inheritDoc * Get a rendering container from an existing target, if compatible.
* @param {HTMLElement} target Potential render target.
* @param {import("../../transform").Transform} transform Transform.
* @param {number} opacity Opacity.
*/ */
disposeInternal() { useContainer(target, transform, opacity) {
this.context.canvas.width = this.context.canvas.height = 0; const layerClassName = this.getLayer().getClassName();
super.disposeInternal(); let container, context;
if (target && target.style.opacity === '' && target.className === layerClassName) {
const canvas = target.firstElementChild;
if (canvas instanceof HTMLCanvasElement) {
context = canvas.getContext('2d');
}
}
if (context && context.canvas.style.transform === transformToString(transform)) {
// Container of the previous layer renderer can be used.
this.container = target;
this.context = context;
this.containerReused = true;
} else if (this.containerReused) {
// Previously reused container cannot be used any more.
this.container = null;
this.context = null;
this.containerReused = false;
}
if (!this.container) {
container = document.createElement('div');
container.className = layerClassName;
let style = container.style;
style.position = 'absolute';
style.width = '100%';
style.height = '100%';
context = createCanvasContext2D();
const canvas = context.canvas;
container.appendChild(canvas);
style = canvas.style;
style.position = 'absolute';
style.transformOrigin = 'top left';
this.container = container;
this.context = context;
}
} }
/** /**

View File

@@ -125,7 +125,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
prepareFrame(frameState, layerState) { prepareFrame(frameState) {
return true; return true;
} }
@@ -137,8 +137,8 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
* @inheritDoc * @inheritDoc
* @returns {HTMLElement} The rendered element. * @returns {HTMLElement} The rendered element.
*/ */
renderFrame(frameState, layerState) { renderFrame(frameState, target) {
const context = this.context; const layerState = frameState.layerStatesArray[frameState.layerIndex];
const viewState = frameState.viewState; const viewState = frameState.viewState;
const projection = viewState.projection; const projection = viewState.projection;
const viewResolution = viewState.resolution; const viewResolution = viewState.resolution;
@@ -224,7 +224,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
} }
const canvas = context.canvas;
const canvasScale = tileResolution / viewResolution; const canvasScale = tileResolution / viewResolution;
// set forward and inverse pixel transforms // set forward and inverse pixel transforms
@@ -234,6 +233,11 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
rotation, rotation,
-width / 2, -height / 2 -width / 2, -height / 2
); );
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 // set scale transform for calculating tile positions on the canvas
@@ -247,7 +251,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
if (canvas.width != width || canvas.height != height) { if (canvas.width != width || canvas.height != height) {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
} else { } else if (!this.containerReused) {
context.clearRect(0, 0, width, height); context.clearRect(0, 0, width, height);
} }
@@ -259,7 +263,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
this.renderedTiles.length = 0; this.renderedTiles.length = 0;
/** @type {Array<number>} */ /** @type {Array<number>} */
const zs = Object.keys(tilesToDrawByZ).map(Number); let zs = Object.keys(tilesToDrawByZ).map(Number);
zs.sort(function(a, b) { zs.sort(function(a, b) {
if (a === z) { if (a === z) {
return 1; return 1;
@@ -270,7 +274,14 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
} }
}); });
for (let i = 0, ii = zs.length; i < ii; ++i) { let clips, clipZs, currentClip;
if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) {
zs = zs.reverse();
} else {
clips = [];
clipZs = [];
}
for (let i = zs.length - 1; i >= 0; --i) {
const currentZ = zs[i]; const currentZ = zs[i];
const currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection); const currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
const currentResolution = tileGrid.getResolution(currentZ); const currentResolution = tileGrid.getResolution(currentZ);
@@ -298,8 +309,36 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const y = Math.round(floatY); const y = Math.round(floatY);
const w = nextX - x; const w = nextX - x;
const h = nextY - y; const h = nextY - y;
const transition = z === currentZ;
this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, z === currentZ); if (clips && (!transition || tile.getAlpha(getUid(this), frameState.time) === 1)) {
// 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];
for (let i = 0, ii = clips.length; i < ii; ++i) {
if (z !== currentZ && currentZ < clipZs[i]) {
const clip = clips[i];
context.beginPath();
// counter-clockwise (outer ring) for current tile
context.moveTo(currentClip[0], currentClip[1]);
context.lineTo(currentClip[2], currentClip[3]);
context.lineTo(currentClip[4], currentClip[5]);
context.lineTo(currentClip[6], currentClip[7]);
// clockwise (inner ring) for higher z tile
context.moveTo(clip[6], clip[7]);
context.lineTo(clip[4], clip[5]);
context.lineTo(clip[2], clip[3]);
context.lineTo(clip[0], clip[1]);
context.clip();
}
}
clips.push(currentClip);
clipZs.push(currentZ);
}
this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity);
if (clips) {
context.restore();
}
this.renderedTiles.push(tile); this.renderedTiles.push(tile);
this.updateUsedTiles(frameState.usedTiles, tileSource, tile); this.updateUsedTiles(frameState.usedTiles, tileSource, tile);
} }
@@ -322,17 +361,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
context.restore(); context.restore();
} }
const opacity = layerState.opacity;
if (opacity !== parseFloat(canvas.style.opacity)) {
canvas.style.opacity = opacity;
}
const canvasTransform = transformToString(this.pixelTransform_); const canvasTransform = transformToString(this.pixelTransform_);
if (canvasTransform !== canvas.style.transform) { if (canvasTransform !== canvas.style.transform) {
canvas.style.transform = canvasTransform; canvas.style.transform = canvasTransform;
} }
return canvas; return this.container;
} }
/** /**
@@ -344,19 +378,15 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
* @param {number} h Height of the tile. * @param {number} h Height of the tile.
* @param {number} gutter Tile gutter. * @param {number} gutter Tile gutter.
* @param {boolean} transition Apply an alpha transition. * @param {boolean} transition Apply an alpha transition.
* @param {number} opacity Opacity.
*/ */
drawTileImage(tile, frameState, x, y, w, h, gutter, transition) { drawTileImage(tile, frameState, x, y, w, h, gutter, transition, opacity) {
const image = this.getTileImage(tile); const image = this.getTileImage(tile);
if (!image) { if (!image) {
return; return;
} }
const uid = getUid(this); const uid = getUid(this);
const alpha = transition ? tile.getAlpha(uid, frameState.time) : 1; const alpha = opacity * (transition ? tile.getAlpha(uid, frameState.time) : 1);
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
const tileSource = tileLayer.getSource();
if (alpha === 1 && !tileSource.getOpaque(frameState.viewState.projection)) {
this.context.clearRect(x, y, w, h);
}
const alphaChanged = alpha !== this.context.globalAlpha; const alphaChanged = alpha !== this.context.globalAlpha;
if (alphaChanged) { if (alphaChanged) {
this.context.save(); this.context.save();

View File

@@ -1,5 +1,5 @@
/** /**
* @module ol/renderer/canvas/ImageLayer * @module ol/renderer/canvas/VectorImageLayer
*/ */
import ImageCanvas from '../../ImageCanvas.js'; import ImageCanvas from '../../ImageCanvas.js';
import ViewHint from '../../ViewHint.js'; import ViewHint from '../../ViewHint.js';
@@ -56,7 +56,14 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
prepareFrame(frameState, layerState) { handleFontsChanged() {
this.vectorRenderer_.handleFontsChanged();
}
/**
* @inheritDoc
*/
prepareFrame(frameState) {
const pixelRatio = frameState.pixelRatio; const pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState; const viewState = frameState.viewState;
const viewResolution = viewState.resolution; const viewResolution = viewState.resolution;
@@ -71,6 +78,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && !isEmpty(renderedExtent)) { if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && !isEmpty(renderedExtent)) {
let skippedFeatures = this.skippedFeatures_; let skippedFeatures = this.skippedFeatures_;
vectorRenderer.useContainer(null, null, 1);
const context = vectorRenderer.context; const context = vectorRenderer.context;
const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, { const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, {
declutterItems: [], declutterItems: [],
@@ -84,10 +92,10 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
})); }));
const newSkippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); const newSkippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort();
const image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) { const image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) {
if (vectorRenderer.prepareFrame(imageFrameState, layerState) && if (vectorRenderer.prepareFrame(imageFrameState) &&
(vectorRenderer.replayGroupChanged || (vectorRenderer.replayGroupChanged ||
!equals(skippedFeatures, newSkippedFeatures))) { !equals(skippedFeatures, newSkippedFeatures))) {
vectorRenderer.renderFrame(imageFrameState, layerState); vectorRenderer.renderFrame(imageFrameState, null);
renderDeclutterItems(imageFrameState, null); renderDeclutterItems(imageFrameState, null);
skippedFeatures = newSkippedFeatures; skippedFeatures = newSkippedFeatures;
callback(); callback();

View File

@@ -3,10 +3,7 @@
*/ */
import {getUid} from '../../util.js'; import {getUid} from '../../util.js';
import ViewHint from '../../ViewHint.js'; import ViewHint from '../../ViewHint.js';
import {listen, unlisten} from '../../events.js';
import EventType from '../../events/EventType.js';
import {buffer, createEmpty, containsExtent, getWidth} from '../../extent.js'; import {buffer, createEmpty, containsExtent, getWidth} from '../../extent.js';
import {labelCache} from '../../render/canvas.js';
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js'; import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
import ExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js'; import ExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import CanvasLayerRenderer from './Layer.js'; import CanvasLayerRenderer from './Layer.js';
@@ -68,39 +65,42 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
* @type {boolean} * @type {boolean}
*/ */
this.replayGroupChanged = true; this.replayGroupChanged = true;
listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
disposeInternal() { useContainer(target, transform, opacity) {
unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); if (opacity < 1) {
super.disposeInternal(); target = null;
}
super.useContainer(target, transform, opacity);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
renderFrame(frameState, layerState) { renderFrame(frameState, target) {
const pixelRatio = frameState.pixelRatio;
const layerState = frameState.layerStatesArray[frameState.layerIndex];
// set forward and inverse pixel transforms
makeScale(this.pixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
this.useContainer(target, this.pixelTransform_, layerState.opacity);
const context = this.context; const context = this.context;
const canvas = context.canvas; const canvas = context.canvas;
const replayGroup = this.replayGroup_; const replayGroup = this.replayGroup_;
if (!replayGroup || replayGroup.isEmpty()) { if (!replayGroup || replayGroup.isEmpty()) {
if (canvas.width > 0) { if (!this.containerReused && canvas.width > 0) {
canvas.width = 0; canvas.width = 0;
} }
return canvas; return this.container;
} }
const pixelRatio = frameState.pixelRatio;
// set forward and inverse pixel transforms
makeScale(this.pixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
// resize and clear // resize and clear
const width = Math.round(frameState.size[0] * pixelRatio); const width = Math.round(frameState.size[0] * pixelRatio);
const height = Math.round(frameState.size[1] * pixelRatio); const height = Math.round(frameState.size[1] * pixelRatio);
@@ -111,7 +111,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
if (canvas.style.transform !== canvasTransform) { if (canvas.style.transform !== canvasTransform) {
canvas.style.transform = canvasTransform; canvas.style.transform = canvasTransform;
} }
} else { } else if (!this.containerReused) {
context.clearRect(0, 0, width, height); context.clearRect(0, 0, width, height);
} }
@@ -165,7 +165,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
if (declutterReplays) { if (declutterReplays) {
const viewHints = frameState.viewHints; const viewHints = frameState.viewHints;
const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]); const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
replayDeclutter(declutterReplays, context, rotation, hifi, frameState.declutterItems); replayDeclutter(declutterReplays, context, rotation, 1, hifi, frameState.declutterItems);
} }
if (clipped) { if (clipped) {
@@ -175,11 +175,12 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
this.postRender(context, frameState); this.postRender(context, frameState);
const opacity = layerState.opacity; const opacity = layerState.opacity;
if (opacity !== parseFloat(canvas.style.opacity)) { const container = this.container;
canvas.style.opacity = opacity; if (opacity !== parseFloat(container.style.opacity)) {
container.style.opacity = opacity === 1 ? '' : opacity;
} }
return canvas; return this.container;
} }
/** /**
@@ -191,7 +192,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
} else { } else {
const resolution = frameState.viewState.resolution; const resolution = frameState.viewState.resolution;
const rotation = frameState.viewState.rotation; const rotation = frameState.viewState.rotation;
const layer = this.getLayer(); const layer = /** @type {import("../../layer/Vector").default} */ (this.getLayer());
/** @type {!Object<string, boolean>} */ /** @type {!Object<string, boolean>} */
const features = {}; const features = {};
const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {},
@@ -205,15 +206,15 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
features[key] = true; features[key] = true;
return callback(feature, layer); return callback(feature, layer);
} }
}, declutteredFeatures); }, layer.getDeclutter() ? declutteredFeatures : null);
return result; return result;
} }
} }
/** /**
* @param {import("../../events/Event.js").default} event Event. * @inheritDoc
*/ */
handleFontsChanged_(event) { handleFontsChanged() {
const layer = this.getLayer(); const layer = this.getLayer();
if (layer.getVisible() && this.replayGroup_) { if (layer.getVisible() && this.replayGroup_) {
layer.changed(); layer.changed();
@@ -232,7 +233,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
prepareFrame(frameState, layerState) { prepareFrame(frameState) {
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
const vectorSource = vectorLayer.getSource(); const vectorSource = vectorLayer.getSource();

View File

@@ -5,19 +5,17 @@ import {getUid} from '../../util.js';
import {createCanvasContext2D} from '../../dom.js'; import {createCanvasContext2D} from '../../dom.js';
import TileState from '../../TileState.js'; import TileState from '../../TileState.js';
import ViewHint from '../../ViewHint.js'; import ViewHint from '../../ViewHint.js';
import {listen, unlisten, unlistenByKey} from '../../events.js'; import {listen, unlistenByKey} from '../../events.js';
import EventType from '../../events/EventType.js'; import EventType from '../../events/EventType.js';
import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js'; import {buffer, containsCoordinate, equals, getIntersection, intersects} from '../../extent.js';
import VectorTileRenderType from '../../layer/VectorTileRenderType.js'; import VectorTileRenderType from '../../layer/VectorTileRenderType.js';
import ReplayType from '../../render/canvas/BuilderType.js'; import ReplayType from '../../render/canvas/BuilderType.js';
import {labelCache} from '../../render/canvas.js';
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js'; import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
import CanvasTileLayerRenderer from './TileLayer.js'; import CanvasTileLayerRenderer from './TileLayer.js';
import {getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js'; import {getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js';
import { import {
apply as applyTransform, apply as applyTransform,
create as createTransform, create as createTransform,
compose as composeTransform,
reset as resetTransform, reset as resetTransform,
scale as scaleTransform, scale as scaleTransform,
translate as translateTransform, translate as translateTransform,
@@ -26,7 +24,6 @@ import {
makeInverse makeInverse
} from '../../transform.js'; } from '../../transform.js';
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js'; import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import {clear, isEmpty} from '../../obj.js';
/** /**
@@ -61,32 +58,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
constructor(layer) { constructor(layer) {
super(layer); super(layer);
const baseCanvas = this.context.canvas;
/** /**
* @private * @private
* @type {CanvasRenderingContext2D} * @type {CanvasRenderingContext2D}
*/ */
this.overlayContext_ = createCanvasContext2D(); this.overlayContext_ = null;
const overlayCanvas = this.overlayContext_.canvas;
overlayCanvas.style.position = 'absolute';
overlayCanvas.style.transformOrigin = 'top left';
const container = document.createElement('div');
const style = container.style;
style.position = 'absolute';
style.width = '100%';
style.height = '100%';
container.appendChild(baseCanvas);
container.appendChild(overlayCanvas);
/**
* @private
* @type {HTMLElement}
*/
this.container_ = container;
/** /**
* The transform for rendered pixels to viewport CSS pixels for the overlay canvas. * The transform for rendered pixels to viewport CSS pixels for the overlay canvas.
@@ -133,26 +109,48 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
// Use nearest lower resolution. // Use nearest lower resolution.
this.zDirection = 1; this.zDirection = 1;
listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
disposeInternal() { useContainer(target, transform, opacity) {
unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); let overlayContext;
this.overlayContext_.canvas.width = this.overlayContext_.canvas.height = 0; if (target && target.childElementCount === 2) {
super.disposeInternal(); overlayContext = target.lastElementChild.getContext('2d');
if (!overlayContext) {
target = null;
}
}
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 (!this.overlayContext_) {
const overlayContext = createCanvasContext2D();
const style = overlayContext.canvas.style;
style.position = 'absolute';
style.transformOrigin = 'top left';
this.overlayContext_ = overlayContext;
}
if (this.container.childElementCount === 1) {
this.container.appendChild(this.overlayContext_.canvas);
}
} }
/** /**
* @param {import("../../VectorRenderTile.js").default} tile Tile. * @param {import("../../VectorRenderTile.js").default} tile Tile.
* @param {number} pixelRatio Pixel ratio. * @param {number} pixelRatio Pixel ratio.
* @param {import("../../proj/Projection").default} projection Projection. * @param {import("../../proj/Projection").default} projection Projection.
* @param {boolean} queue Queue tile for rendering.
* @return {boolean} Tile needs to be rendered.
*/ */
prepareTile(tile, pixelRatio, projection) { prepareTile(tile, pixelRatio, projection, queue) {
let render = false;
const tileUid = getUid(tile); const tileUid = getUid(tile);
const state = tile.getState(); const state = tile.getState();
if (((state === TileState.LOADED && tile.hifi) || if (((state === TileState.LOADED && tile.hifi) ||
@@ -164,9 +162,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (state === TileState.LOADED || state === TileState.ERROR) { if (state === TileState.LOADED || state === TileState.ERROR) {
this.updateExecutorGroup_(tile, pixelRatio, projection); this.updateExecutorGroup_(tile, pixelRatio, projection);
if (this.tileImageNeedsRender_(tile, pixelRatio, projection)) { if (this.tileImageNeedsRender_(tile, pixelRatio, projection)) {
this.renderTileImageQueue_[tileUid] = tile; render = true;
if (queue) {
this.renderTileImageQueue_[tileUid] = tile;
}
} }
} }
return render;
} }
/** /**
@@ -182,7 +184,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
tile.wantedResolution = resolution; tile.wantedResolution = resolution;
const tileUid = getUid(tile); const tileUid = getUid(tile);
if (!(tileUid in this.tileListenerKeys_)) { if (!(tileUid in this.tileListenerKeys_)) {
const listenerKey = listen(tile, EventType.CHANGE, this.prepareTile.bind(this, tile, pixelRatio, projection)); const listenerKey = listen(tile, EventType.CHANGE, this.prepareTile.bind(this, tile, pixelRatio, projection, true));
this.tileListenerKeys_[tileUid] = listenerKey; this.tileListenerKeys_[tileUid] = listenerKey;
} }
} else { } else {
@@ -191,7 +193,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (hifi || !tile.wantedResolution) { if (hifi || !tile.wantedResolution) {
tile.wantedResolution = resolution; tile.wantedResolution = resolution;
} }
this.prepareTile(tile, pixelRatio, projection); const render = this.prepareTile(tile, pixelRatio, projection, false);
if (render) {
this.renderTileImage_(tile, frameState);
}
} }
return tile; return tile;
} }
@@ -213,13 +218,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
prepareFrame(frameState, layerState) { prepareFrame(frameState) {
const layerRevision = this.getLayer().getRevision(); const layerRevision = this.getLayer().getRevision();
if (this.renderedLayerRevision_ != layerRevision) { if (this.renderedLayerRevision_ != layerRevision) {
this.renderedTiles.length = 0; this.renderedTiles.length = 0;
} }
this.renderedLayerRevision_ = layerRevision; this.renderedLayerRevision_ = layerRevision;
return super.prepareFrame(frameState, layerState); return super.prepareFrame(frameState);
} }
/** /**
@@ -357,45 +362,16 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
features[key] = true; features[key] = true;
return callback(feature, layer); return callback(feature, layer);
} }
}, declutteredFeatures); }, layer.getDeclutter() ? declutteredFeatures : null);
} }
} }
return found; return found;
} }
/** /**
* @param {import("../../VectorTile.js").default} tile Tile. * @inheritDoc
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @return {import("../../transform.js").Transform} transform Transform.
* @private
*/ */
getReplayTransform_(tile, frameState) { handleFontsChanged() {
const layer = this.getLayer();
const source = layer.getSource();
const tileGrid = source.getTileGrid();
const tileCoord = tile.tileCoord;
const tileResolution = tileGrid.getResolution(tileCoord[0]);
const viewState = frameState.viewState;
const pixelRatio = frameState.pixelRatio;
const renderResolution = viewState.resolution / pixelRatio;
const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent);
const center = viewState.center;
const origin = getTopLeft(tileExtent);
const size = frameState.size;
const offsetX = Math.round(pixelRatio * size[0] / 2);
const offsetY = Math.round(pixelRatio * size[1] / 2);
return composeTransform(this.tmpTransform_,
offsetX, offsetY,
tileResolution / renderResolution, tileResolution / renderResolution,
viewState.rotation,
(origin[0] - center[0]) / tileResolution,
(center[1] - origin[1]) / tileResolution);
}
/**
* @param {import("../../events/Event.js").default} event Event.
*/
handleFontsChanged_(event) {
const layer = this.getLayer(); const layer = this.getLayer();
if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) { if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) {
layer.changed(); layer.changed();
@@ -414,26 +390,30 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
renderFrame(frameState, layerState) { renderFrame(frameState, target) {
super.renderFrame(frameState, layerState);
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const viewHints = frameState.viewHints; const viewHints = frameState.viewHints;
const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]); const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
this.renderQueuedTileImages_(hifi, frameState);
super.renderFrame(frameState, target);
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const renderMode = layer.getRenderMode(); const renderMode = layer.getRenderMode();
if (renderMode === VectorTileRenderType.IMAGE) { if (renderMode === VectorTileRenderType.IMAGE) {
this.renderTileImages_(hifi, frameState); return this.container;
return this.container_;
} }
if (!isEmpty(this.renderTileImageQueue_) && !this.extentChanged) { const source = layer.getSource();
this.renderTileImages_(hifi, frameState); // Unqueue tiles from the image queue when we don't need any more
return this.container_; const usedTiles = frameState.usedTiles[getUid(source)];
for (const tileUid in this.renderTileImageQueue_) {
if (!(tileUid in usedTiles)) {
delete this.renderTileImageQueue_[tileUid];
}
} }
const context = this.overlayContext_; const context = this.overlayContext_;
const declutterReplays = layer.getDeclutter() ? {} : null; const declutterReplays = layer.getDeclutter() ? {} : null;
const source = layer.getSource();
const replayTypes = VECTOR_REPLAYS[renderMode]; const replayTypes = VECTOR_REPLAYS[renderMode];
const pixelRatio = frameState.pixelRatio; const pixelRatio = frameState.pixelRatio;
const rotation = frameState.viewState.rotation; const rotation = frameState.viewState.rotation;
@@ -454,13 +434,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (canvas.style.transform !== canvasTransform) { if (canvas.style.transform !== canvasTransform) {
canvas.style.transform = canvasTransform; canvas.style.transform = canvasTransform;
} }
} else { } else if (!this.containerReused) {
context.clearRect(0, 0, width, height); context.clearRect(0, 0, width, height);
} }
const tiles = this.renderedTiles; const tiles = this.renderedTiles;
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection); const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
const clips = []; const clips = [];
const clipZs = [];
for (let i = tiles.length - 1; i >= 0; --i) { for (let i = tiles.length - 1; i >= 0; --i) {
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]); const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]);
if (tile.getState() == TileState.ABORT) { if (tile.getState() == TileState.ABORT) {
@@ -471,6 +452,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tileExtent[0]; const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tileExtent[0];
const transform = this.getRenderTransform(frameState, width, height, worldOffset); const transform = this.getRenderTransform(frameState, width, height, worldOffset);
const executorGroups = tile.executorGroups[getUid(layer)]; const executorGroups = tile.executorGroups[getUid(layer)];
let clipped = false;
for (let t = 0, tt = executorGroups.length; t < tt; ++t) { for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
const executorGroup = executorGroups[t]; const executorGroup = executorGroups[t];
if (!executorGroup.hasExecutors(replayTypes)) { if (!executorGroup.hasExecutors(replayTypes)) {
@@ -478,9 +460,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
continue; continue;
} }
const currentZ = tile.tileCoord[0]; const currentZ = tile.tileCoord[0];
let zs, currentClip; let currentClip;
if (!declutterReplays) { if (!declutterReplays && !clipped) {
zs = [];
currentClip = executorGroup.getClipCoords(transform); currentClip = executorGroup.getClipCoords(transform);
context.save(); context.save();
@@ -488,7 +469,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
// already filled by a higher resolution tile // already filled by a higher resolution tile
for (let j = 0, jj = clips.length; j < jj; ++j) { for (let j = 0, jj = clips.length; j < jj; ++j) {
const clip = clips[j]; const clip = clips[j];
if (currentZ < zs[j]) { if (currentZ < clipZs[j]) {
context.beginPath(); context.beginPath();
// counter-clockwise (outer ring) for current tile // counter-clockwise (outer ring) for current tile
context.moveTo(currentClip[0], currentClip[1]); context.moveTo(currentClip[0], currentClip[1]);
@@ -505,51 +486,38 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
} }
} }
executorGroup.execute(context, transform, rotation, {}, hifi, replayTypes, declutterReplays); executorGroup.execute(context, transform, rotation, {}, hifi, replayTypes, declutterReplays);
if (!declutterReplays) { if (!declutterReplays && !clipped) {
context.restore(); context.restore();
clips.push(currentClip); clips.push(currentClip);
zs.push(currentZ); clipZs.push(currentZ);
clipped = true;
} }
} }
} }
if (declutterReplays) { if (declutterReplays) {
replayDeclutter(declutterReplays, context, rotation, hifi, frameState.declutterItems); const layerState = frameState.layerStatesArray[frameState.layerIndex];
replayDeclutter(declutterReplays, context, rotation, layerState.opacity, hifi, frameState.declutterItems);
} }
const opacity = layerState.opacity; return this.container;
if (opacity !== parseFloat(canvas.style.opacity)) {
canvas.style.opacity = opacity;
}
// Now that we have rendered the tiles we have already, let's prepare new tile images
// for the next frame
this.renderTileImages_(hifi, frameState);
return this.container_;
} }
/** /**
* @param {boolean} hifi We have time to render a high fidelity map image. * @param {boolean} hifi We have time to render a high fidelity map image.
* @param {import('../../PluggableMap.js').FrameState} frameState Frame state. * @param {import('../../PluggableMap.js').FrameState} frameState Frame state.
*/ */
renderTileImages_(hifi, frameState) { renderQueuedTileImages_(hifi, frameState) {
// When we don't have time to render hifi, only render tiles until we have used up // When we don't have time to render hifi, only render tiles until we have used up
// half of the frame budget of 16 ms // half of the frame budget of 16 ms
for (const uid in this.renderTileImageQueue_) { for (const uid in this.renderTileImageQueue_) {
if (!hifi && Date.now() - frameState.time > 8) { if (!hifi && Date.now() - frameState.time > 8) {
frameState.animate = true;
break; break;
} }
const tile = this.renderTileImageQueue_[uid]; const tile = this.renderTileImageQueue_[uid];
frameState.animate = true;
delete this.renderTileImageQueue_[uid]; delete this.renderTileImageQueue_[uid];
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); this.renderTileImage_(tile, frameState);
const viewState = frameState.viewState;
const tileGrid = layer.getSource().getTileGridForProjection(viewState.projection);
const tileResolution = tileGrid.getResolution(tile.tileCoord[0]);
const renderPixelRatio = frameState.pixelRatio / tile.wantedResolution * tileResolution;
this.renderTileImage_(tile, frameState.pixelRatio, renderPixelRatio, viewState.projection);
} }
clear(this.renderTileImageQueue_);
} }
/** /**
@@ -596,40 +564,47 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
/** /**
* @param {import("../../VectorRenderTile.js").default} tile Tile. * @param {import("../../VectorRenderTile.js").default} tile Tile.
* @param {number} pixelRatio Pixel ratio. * @param {import("../../PluggableMap").FrameState} frameState Frame state.
* @param {number} renderPixelRatio Render pixel ratio.
* @param {import("../../proj/Projection.js").default} projection Projection.
* @private * @private
*/ */
renderTileImage_(tile, pixelRatio, renderPixelRatio, projection) { renderTileImage_(tile, frameState) {
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const replayState = tile.getReplayState(layer); const replayState = tile.getReplayState(layer);
const revision = layer.getRevision(); const revision = layer.getRevision();
const executorGroups = tile.executorGroups[getUid(layer)]; const executorGroups = tile.executorGroups[getUid(layer)];
replayState.renderedTileRevision = revision; replayState.renderedTileRevision = revision;
replayState.renderedTileZ = tile.sourceZ; replayState.renderedTileZ = tile.sourceZ;
const tileCoord = tile.wrappedTileCoord; const tileCoord = tile.wrappedTileCoord;
const z = tileCoord[0]; const z = tileCoord[0];
const source = layer.getSource(); const source = layer.getSource();
let pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState;
const projection = viewState.projection;
const tileGrid = source.getTileGridForProjection(projection); const tileGrid = source.getTileGridForProjection(projection);
const tileResolution = tileGrid.getResolution(tile.tileCoord[0]);
const renderPixelRatio = frameState.pixelRatio / tile.wantedResolution * tileResolution;
const resolution = tileGrid.getResolution(z); const resolution = tileGrid.getResolution(z);
const context = tile.getContext(layer); const context = tile.getContext(layer);
// Increase tile size when overzooming for low pixel ratio, to avoid blurry tiles // Increase tile size when overzooming for low pixel ratio, to avoid blurry tiles
pixelRatio = Math.max(pixelRatio, renderPixelRatio / pixelRatio); pixelRatio = Math.max(pixelRatio, renderPixelRatio / pixelRatio);
const size = source.getTilePixelSize(z, pixelRatio, projection); const size = source.getTilePixelSize(z, pixelRatio, projection);
context.canvas.width = size[0]; context.canvas.width = size[0];
context.canvas.height = size[1]; context.canvas.height = size[1];
const canvasTransform = resetTransform(this.tmpTransform_);
const renderScale = pixelRatio / renderPixelRatio; const renderScale = pixelRatio / renderPixelRatio;
scaleTransform(canvasTransform, renderScale, renderScale); if (renderScale !== 1) {
context.setTransform.apply(context, canvasTransform); const canvasTransform = resetTransform(this.tmpTransform_);
scaleTransform(canvasTransform, renderScale, renderScale);
context.setTransform.apply(context, canvasTransform);
}
const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent);
const pixelScale = renderPixelRatio / resolution;
const transform = resetTransform(this.tmpTransform_);
scaleTransform(transform, pixelScale, -pixelScale);
translateTransform(transform, -tileExtent[0], -tileExtent[3]);
for (let i = 0, ii = executorGroups.length; i < ii; ++i) { for (let i = 0, ii = executorGroups.length; i < ii; ++i) {
const executorGroup = executorGroups[i]; const executorGroup = executorGroups[i];
const pixelScale = renderPixelRatio / resolution;
const transform = resetTransform(this.tmpTransform_);
scaleTransform(transform, pixelScale, -pixelScale);
translateTransform(transform, -tileExtent[0], -tileExtent[3]);
executorGroup.execute(context, transform, 0, {}, true, IMAGE_REPLAYS[layer.getRenderMode()]); executorGroup.execute(context, transform, 0, {}, true, IMAGE_REPLAYS[layer.getRenderMode()]);
} }
replayState.renderedTileResolution = tile.wantedResolution; replayState.renderedTileResolution = tile.wantedResolution;

View File

@@ -0,0 +1,162 @@
/**
* @module ol/renderer/webgl/Layer
*/
import LayerRenderer from '../Layer.js';
import WebGLHelper from '../../webgl/Helper.js';
/**
* @typedef {Object} PostProcessesOptions
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
* the main canvas that will then be sampled up (useful for saving resource on blur steps).
* @property {string} [vertexShader] Vertex shader source
* @property {string} [fragmentShader] Fragment shader source
* @property {Object.<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process step
*/
/**
* @typedef {Object} Options
* @property {Object.<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process steps
* @property {Array<PostProcessesOptions>} [postProcesses] Post-processes definitions
*/
/**
* @classdesc
* Base WebGL renderer class.
* Holds all logic related to data manipulation & some common rendering logic
*/
class WebGLLayerRenderer extends LayerRenderer {
/**
* @param {import("../../layer/Layer.js").default} layer Layer.
* @param {Options=} [opt_options] Options.
*/
constructor(layer, opt_options) {
super(layer);
const options = opt_options || {};
this.helper_ = new WebGLHelper({
postProcesses: options.postProcesses,
uniforms: options.uniforms
});
}
/**
* @inheritDoc
*/
disposeInternal() {
super.disposeInternal();
}
/**
* Will return the last shader compilation errors. If no error happened, will return null;
* @return {string|null} Errors, or null if last compilation was successful
* @api
*/
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.
*/
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;
}
}
const tmpArray_ = [];
/**
* 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
* @private
*/
function pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) {
const stride = 12 + (opt_attributes !== undefined ? opt_attributes.length : 0);
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;
// 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;
}
// 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);
vertexBuffer.getArray().push(x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha);
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
vertexBuffer.getArray().push(x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha);
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
vertexBuffer.getArray().push(x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha);
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
indexBuffer.getArray().push(
baseIndex, baseIndex + 1, baseIndex + 3,
baseIndex + 1, baseIndex + 2, baseIndex + 3
);
}
/**
* Returns a texture of 1x1 pixel, white
* @private
* @return {ImageData} Image data.
*/
export function getBlankTexture() {
const canvas = document.createElement('canvas');
const image = canvas.getContext('2d').createImageData(1, 1);
image.data[0] = image.data[1] = image.data[2] = image.data[3] = 255;
return image;
}
export default WebGLLayerRenderer;

View File

@@ -1,11 +1,21 @@
/** /**
* @module ol/renderer/webgl/PointsLayer * @module ol/renderer/webgl/PointsLayer
*/ */
import LayerRenderer from '../Layer'; import WebGLArrayBuffer from '../../webgl/Buffer.js';
import WebGLArrayBuffer from '../../webgl/Buffer'; import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl.js';
import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl'; import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper.js';
import WebGLHelper, {DefaultAttrib} from '../../webgl/Helper'; import GeometryType from '../../geom/GeometryType.js';
import GeometryType from '../../geom/GeometryType'; import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from './Layer.js';
import GeoJSON from '../../format/GeoJSON.js';
import {getUid} from '../../util.js';
import ViewHint from '../../ViewHint.js';
import {createEmpty, equals} from '../../extent.js';
import {
create as createTransform,
makeInverse as makeInverseTransform,
multiply as multiplyTransform,
apply as applyTransform
} from '../../transform.js';
const VERTEX_SHADER = ` const VERTEX_SHADER = `
precision mediump float; precision mediump float;
@@ -15,15 +25,15 @@ const VERTEX_SHADER = `
attribute vec2 a_offsets; attribute vec2 a_offsets;
attribute float a_opacity; attribute float a_opacity;
attribute vec4 a_color; attribute vec4 a_color;
uniform mat4 u_projectionMatrix; uniform mat4 u_projectionMatrix;
uniform mat4 u_offsetScaleMatrix; uniform mat4 u_offsetScaleMatrix;
uniform mat4 u_offsetRotateMatrix; uniform mat4 u_offsetRotateMatrix;
varying vec2 v_texCoord; varying vec2 v_texCoord;
varying float v_opacity; varying float v_opacity;
varying vec4 v_color; varying vec4 v_color;
void main(void) { void main(void) {
mat4 offsetMatrix = u_offsetScaleMatrix; mat4 offsetMatrix = u_offsetScaleMatrix;
if (a_rotateWithView == 1.0) { if (a_rotateWithView == 1.0) {
@@ -38,13 +48,13 @@ const VERTEX_SHADER = `
const FRAGMENT_SHADER = ` const FRAGMENT_SHADER = `
precision mediump float; precision mediump float;
uniform sampler2D u_texture; uniform sampler2D u_texture;
varying vec2 v_texCoord; varying vec2 v_texCoord;
varying float v_opacity; varying float v_opacity;
varying vec4 v_color; varying vec4 v_color;
void main(void) { void main(void) {
if (v_opacity == 0.0) { if (v_opacity == 0.0) {
discard; discard;
@@ -55,15 +65,6 @@ const FRAGMENT_SHADER = `
gl_FragColor.rgb *= gl_FragColor.a; gl_FragColor.rgb *= gl_FragColor.a;
}`; }`;
/**
* @typedef {Object} PostProcessesOptions
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
* the main canvas that will then be sampled up (useful for saving resource on blur steps).
* @property {string} [vertexShader] Vertex shader source
* @property {string} [fragmentShader] Fragment shader source
* @property {Object.<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process step
*/
/** /**
* @typedef {Object} Options * @typedef {Object} Options
* @property {function(import("../../Feature").default):number} [sizeCallback] Will be called on every feature in the * @property {function(import("../../Feature").default):number} [sizeCallback] Will be called on every feature in the
@@ -91,7 +92,7 @@ const FRAGMENT_SHADER = `
* @property {string} [fragmentShader] Fragment shader source * @property {string} [fragmentShader] Fragment shader source
* @property {Object.<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process steps * @property {Object.<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process steps
* Please note that `u_texture` is reserved for the main texture slot. * Please note that `u_texture` is reserved for the main texture slot.
* @property {Array<PostProcessesOptions>} [postProcesses] Post-processes definitions * @property {Array<import("./Layer").PostProcessesOptions>} [postProcesses] Post-processes definitions
*/ */
/** /**
@@ -186,22 +187,23 @@ const FRAGMENT_SHADER = `
* *
* @api * @api
*/ */
class WebGLPointsLayerRenderer extends LayerRenderer { class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
/** /**
* @param {import("../../layer/Vector.js").default} vectorLayer Vector layer. * @param {import("../../layer/Vector.js").default} vectorLayer Vector layer.
* @param {Options=} [opt_options] Options. * @param {Options=} [opt_options] Options.
*/ */
constructor(vectorLayer, opt_options) { constructor(vectorLayer, opt_options) {
super(vectorLayer);
const options = opt_options || {}; const options = opt_options || {};
const uniforms = options.uniforms || {}; const uniforms = options.uniforms || {};
uniforms.u_texture = options.texture || this.getDefaultTexture(); uniforms.u_texture = options.texture || getBlankTexture();
this.helper_ = new WebGLHelper({ const projectionMatrixTransform = createTransform();
postProcesses: options.postProcesses, uniforms[DefaultUniform.PROJECTION_MATRIX] = projectionMatrixTransform;
uniforms: uniforms
super(vectorLayer, {
uniforms: uniforms,
postProcesses: options.postProcesses
}); });
this.sourceRevision_ = -1; this.sourceRevision_ = -1;
@@ -220,7 +222,7 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
return 1; return 1;
}; };
this.coordCallback_ = options.coordCallback || function(feature, index) { this.coordCallback_ = options.coordCallback || function(feature, index) {
const geom = /** @type {import("../../geom/Point").default} */ (feature.getGeometry()); const geom = feature.getGeometry();
return geom.getCoordinates()[index]; return geom.getCoordinates()[index];
}; };
this.opacityCallback_ = options.opacityCallback || function() { this.opacityCallback_ = options.opacityCallback || function() {
@@ -238,6 +240,38 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
this.rotateWithViewCallback_ = options.rotateWithViewCallback || function() { this.rotateWithViewCallback_ = options.rotateWithViewCallback || function() {
return false; return false;
}; };
this.geojsonFormat_ = new GeoJSON();
/**
* @type {Object<string, import("../../format/GeoJSON").GeoJSONFeature>}
* @private
*/
this.geojsonFeatureCache_ = {};
this.previousExtent_ = createEmpty();
/**
* This transform is updated on every frame and is the composition of:
* - invert of the world->screen transform that was used when rebuilding buffers (see `this.renderTransform_`)
* - current world->screen transform
* @type {import("../../transform.js").Transform}
* @private
*/
this.currentTransform_ = projectionMatrixTransform;
/**
* This transform is updated when buffers are rebuilt and converts world space coordinates to screen space
* @type {import("../../transform.js").Transform}
* @private
*/
this.renderTransform_ = createTransform();
/**
* @type {import("../../transform.js").Transform}
* @private
*/
this.invertRenderTransform_ = createTransform();
} }
/** /**
@@ -250,7 +284,8 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
/** /**
* @inheritDoc * @inheritDoc
*/ */
renderFrame(frameState, layerState) { renderFrame(frameState) {
const layerState = frameState.layerStatesArray[frameState.layerIndex];
this.helper_.drawElements(0, this.indicesBuffer_.getArray().length); this.helper_.drawElements(0, this.indicesBuffer_.getArray().length);
this.helper_.finalizeDraw(frameState); this.helper_.finalizeDraw(frameState);
const canvas = this.helper_.getCanvas(); const canvas = this.helper_.getCanvas();
@@ -269,58 +304,35 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
prepareFrame(frameState) { prepareFrame(frameState) {
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
const vectorSource = vectorLayer.getSource(); const vectorSource = vectorLayer.getSource();
const viewState = frameState.viewState;
// TODO: get this from somewhere...
const stride = 12; const stride = 12;
this.helper_.prepareDraw(frameState); // the source has changed: clear the feature cache & reload features
const sourceChanged = this.sourceRevision_ < vectorSource.getRevision();
if (this.sourceRevision_ < vectorSource.getRevision()) { if (sourceChanged) {
this.sourceRevision_ = vectorSource.getRevision(); this.sourceRevision_ = vectorSource.getRevision();
this.verticesBuffer_.getArray().length = 0; this.geojsonFeatureCache_ = {};
this.indicesBuffer_.getArray().length = 0;
const viewState = frameState.viewState;
const projection = viewState.projection; const projection = viewState.projection;
const resolution = viewState.resolution; const resolution = viewState.resolution;
// loop on features to fill the buffer
vectorSource.loadFeatures([-Infinity, -Infinity, Infinity, Infinity], resolution, projection); vectorSource.loadFeatures([-Infinity, -Infinity, Infinity, Infinity], resolution, projection);
vectorSource.forEachFeature((feature) => {
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
return;
}
const x = this.coordCallback_(feature, 0);
const y = this.coordCallback_(feature, 1);
const u0 = this.texCoordCallback_(feature, 0);
const v0 = this.texCoordCallback_(feature, 1);
const u1 = this.texCoordCallback_(feature, 2);
const v1 = this.texCoordCallback_(feature, 3);
const size = this.sizeCallback_(feature);
const opacity = this.opacityCallback_(feature);
const rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0;
const color = this.colorCallback_(feature, this.colorArray_);
const red = color[0];
const green = color[1];
const blue = color[2];
const alpha = color[3];
const baseIndex = this.verticesBuffer_.getArray().length / stride;
this.verticesBuffer_.getArray().push(
x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha,
x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha,
x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha,
x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha
);
this.indicesBuffer_.getArray().push(
baseIndex, baseIndex + 1, baseIndex + 3,
baseIndex + 1, baseIndex + 2, baseIndex + 3
);
});
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
} }
const viewNotMoving = !frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING];
const extentChanged = !equals(this.previousExtent_, frameState.extent);
if ((sourceChanged || extentChanged) && viewNotMoving) {
this.rebuildBuffers_(frameState);
this.previousExtent_ = frameState.extent.slice();
}
// apply the current projection transform with the invert of the one used to fill buffers
this.helper_.makeProjectionTransform(frameState, this.currentTransform_);
multiplyTransform(this.currentTransform_, this.invertRenderTransform_);
this.helper_.prepareDraw(frameState);
// write new data // write new data
this.helper_.bindBuffer(ARRAY_BUFFER, this.verticesBuffer_); this.helper_.bindBuffer(ARRAY_BUFFER, this.verticesBuffer_);
this.helper_.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); this.helper_.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
@@ -337,24 +349,54 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
} }
/** /**
* Will return the last shader compilation errors. If no error happened, will return null; * Rebuild internal webgl buffers based on current view extent; costly, should not be called too much
* @return {string|null} Errors, or null if last compilation was successful * @param {import("../../PluggableMap").FrameState} frameState Frame state.
* @api
*/
getShaderCompileErrors() {
return this.helper_.getShaderCompileErrors();
}
/**
* Returns a texture of 1x1 pixel, white
* @private * @private
* @return {ImageData} Image data.
*/ */
getDefaultTexture() { rebuildBuffers_(frameState) {
const canvas = document.createElement('canvas'); const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
const image = canvas.getContext('2d').createImageData(1, 1); const vectorSource = vectorLayer.getSource();
image.data[0] = image.data[1] = image.data[2] = image.data[3] = 255;
return image; 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_);
// loop on features to fill the buffer
const features = vectorSource.getFeatures();
let feature;
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;
}
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;
pushFeatureToBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature);
}
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
} }
} }

View File

@@ -2,8 +2,8 @@
* @module ol/resolutionconstraint * @module ol/resolutionconstraint
*/ */
import {linearFindNearest} from './array.js'; import {linearFindNearest} from './array.js';
import {getHeight, getWidth} from './extent'; import {getHeight, getWidth} from './extent.js';
import {clamp} from './math'; import {clamp} from './math.js';
/** /**

View File

@@ -76,7 +76,7 @@ class Cluster extends VectorSource {
* @protected * @protected
*/ */
this.geometryFunction = options.geometryFunction || function(feature) { this.geometryFunction = options.geometryFunction || function(feature) {
const geometry = /** @type {Point} */ (feature.getGeometry()); const geometry = feature.getGeometry();
assert(geometry.getType() == GeometryType.POINT, assert(geometry.getType() == GeometryType.POINT,
10); // The default `geometryFunction` can only handle `Point` geometries 10); // The default `geometryFunction` can only handle `Point` geometries
return geometry; return geometry;

View File

@@ -56,6 +56,11 @@ function formatPercentage(percentage) {
*/ */
class IIIF extends TileImage { class IIIF extends TileImage {
/**
* @param {Options} opt_options Tile source options. Use {@link import("../format/IIIFInfo.js").IIIFInfo}
* to parse Image API service information responses into constructor options.
* @api
*/
constructor(opt_options) { constructor(opt_options) {
const options = opt_options || {}; const options = opt_options || {};
@@ -70,6 +75,7 @@ class IIIF extends TileImage {
const width = size[0]; const width = size[0];
const height = size[1]; const height = size[1];
const tileSize = options.tileSize; const tileSize = options.tileSize;
const tilePixelRatio = options.tilePixelRatio || 1;
const format = options.format || 'jpg'; const format = options.format || 'jpg';
const quality = options.quality || (options.version == Versions.VERSION1 ? 'native' : 'default'); const quality = options.quality || (options.version == Versions.VERSION1 ? 'native' : 'default');
let resolutions = options.resolutions || []; let resolutions = options.resolutions || [];
@@ -255,7 +261,7 @@ class IIIF extends TileImage {
return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format; return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format;
}; };
const IiifTileClass = CustomTile.bind(null, tileGrid); const IiifTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid);
super({ super({
attributions: options.attributions, attributions: options.attributions,

View File

@@ -7,7 +7,7 @@ import {createCanvasContext2D} from '../dom.js';
import {listen} from '../events.js'; import {listen} from '../events.js';
import Event from '../events/Event.js'; import Event from '../events/Event.js';
import EventType from '../events/EventType.js'; import EventType from '../events/EventType.js';
import {Processor} from 'pixelworks/lib/index'; import {Processor} from 'pixelworks/lib/index.js';
import {equals, getCenter, getHeight, getWidth} from '../extent.js'; import {equals, getCenter, getHeight, getWidth} from '../extent.js';
import ImageLayer from '../layer/Image.js'; import ImageLayer from '../layer/Image.js';
import TileLayer from '../layer/Tile.js'; import TileLayer from '../layer/Tile.js';
@@ -214,6 +214,7 @@ class RasterSource extends ImageSource {
extent: null, extent: null,
focus: null, focus: null,
index: 0, index: 0,
layerIndex: 0,
layerStatesArray: getLayerStatesArray(this.layers_), layerStatesArray: getLayerStatesArray(this.layers_),
pixelRatio: 1, pixelRatio: 1,
pixelToCoordinateTransform: createTransform(), pixelToCoordinateTransform: createTransform(),
@@ -358,7 +359,8 @@ class RasterSource extends ImageSource {
const len = this.layers_.length; const len = this.layers_.length;
const imageDatas = new Array(len); const imageDatas = new Array(len);
for (let i = 0; i < len; ++i) { for (let i = 0; i < len; ++i) {
const imageData = getImageData(this.layers_[i], frameState, frameState.layerStatesArray[i]); frameState.layerIndex = i;
const imageData = getImageData(this.layers_[i], frameState);
if (imageData) { if (imageData) {
imageDatas[i] = imageData; imageDatas[i] = imageData;
} else { } else {
@@ -430,21 +432,24 @@ let sharedContext = null;
* Get image data from a layer. * Get image data from a layer.
* @param {import("../layer/Layer.js").default} layer Layer to render. * @param {import("../layer/Layer.js").default} layer Layer to render.
* @param {import("../PluggableMap.js").FrameState} frameState The frame state. * @param {import("../PluggableMap.js").FrameState} frameState The frame state.
* @param {import("../layer/Layer.js").State} layerState The layer state.
* @return {ImageData} The image data. * @return {ImageData} The image data.
*/ */
function getImageData(layer, frameState, layerState) { function getImageData(layer, frameState) {
const renderer = layer.getRenderer(); const renderer = layer.getRenderer();
if (!renderer) { if (!renderer) {
throw new Error('Unsupported layer type: ' + layer); throw new Error('Unsupported layer type: ' + layer);
} }
if (!renderer.prepareFrame(frameState, layerState)) { if (!renderer.prepareFrame(frameState)) {
return null; return null;
} }
const width = frameState.size[0]; const width = frameState.size[0];
const height = frameState.size[1]; const height = frameState.size[1];
const element = renderer.renderFrame(frameState, layerState); const container = renderer.renderFrame(frameState, null);
let element;
if (container) {
element = container.firstElementChild;
}
if (!(element instanceof HTMLCanvasElement)) { if (!(element instanceof HTMLCanvasElement)) {
throw new Error('Unsupported rendered element: ' + element); throw new Error('Unsupported rendered element: ' + element);
} }

View File

@@ -4,7 +4,7 @@
// FIXME check order of async callbacks // FIXME check order of async callbacks
/** /**
* See http://mapbox.com/developers/api/. * See https://mapbox.com/developers/api/.
*/ */

View File

@@ -35,12 +35,13 @@ import RBush from '../structs/RBush.js';
* @classdesc * @classdesc
* Events emitted by {@link module:ol/source/Vector} instances are instances of this * Events emitted by {@link module:ol/source/Vector} instances are instances of this
* type. * type.
* @template {import("../geom/Geometry.js").default} Geometry
*/ */
export class VectorSourceEvent extends Event { export class VectorSourceEvent extends Event {
/** /**
* @param {string} type Type. * @param {string} type Type.
* @param {import("../Feature.js").default=} opt_feature Feature. * @param {import("../Feature.js").default<Geometry>=} opt_feature Feature.
*/ */
constructor(type, opt_feature) { constructor(type, opt_feature) {
@@ -48,7 +49,7 @@ export class VectorSourceEvent extends Event {
/** /**
* The feature being added or removed. * The feature being added or removed.
* @type {import("../Feature.js").default|undefined} * @type {import("../Feature.js").default<Geometry>|undefined}
* @api * @api
*/ */
this.feature = opt_feature; this.feature = opt_feature;
@@ -154,8 +155,9 @@ export class VectorSourceEvent extends Event {
* by this source are suitable for editing. See {@link module:ol/source/VectorTile~VectorTile} for * by this source are suitable for editing. See {@link module:ol/source/VectorTile~VectorTile} for
* vector data that is optimized for rendering. * vector data that is optimized for rendering.
* *
* @fires VectorSourceEvent * @fires VectorSourceEvent<Geometry>
* @api * @api
* @template {import("../geom/Geometry.js").default} Geometry
*/ */
class VectorSource extends Source { class VectorSource extends Source {
/** /**
@@ -215,7 +217,7 @@ class VectorSource extends Source {
/** /**
* @private * @private
* @type {RBush<import("../Feature.js").default>} * @type {RBush<import("../Feature.js").default<Geometry>>}
*/ */
this.featuresRtree_ = useSpatialIndex ? new RBush() : null; this.featuresRtree_ = useSpatialIndex ? new RBush() : null;
@@ -227,21 +229,21 @@ class VectorSource extends Source {
/** /**
* @private * @private
* @type {!Object<string, import("../Feature.js").default>} * @type {!Object<string, import("../Feature.js").default<Geometry>>}
*/ */
this.nullGeometryFeatures_ = {}; this.nullGeometryFeatures_ = {};
/** /**
* A lookup of features by id (the return from feature.getId()). * A lookup of features by id (the return from feature.getId()).
* @private * @private
* @type {!Object<string, import("../Feature.js").default>} * @type {!Object<string, import("../Feature.js").default<Geometry>>}
*/ */
this.idIndex_ = {}; this.idIndex_ = {};
/** /**
* A lookup of features without id (keyed by getUid(feature)). * A lookup of features without id (keyed by getUid(feature)).
* @private * @private
* @type {!Object<string, import("../Feature.js").default>} * @type {!Object<string, import("../Feature.js").default<Geometry>>}
*/ */
this.undefIdIndex_ = {}; this.undefIdIndex_ = {};
@@ -253,7 +255,7 @@ class VectorSource extends Source {
/** /**
* @private * @private
* @type {Collection<import("../Feature.js").default>} * @type {Collection<import("../Feature.js").default<Geometry>>}
*/ */
this.featuresCollection_ = null; this.featuresCollection_ = null;
@@ -282,7 +284,10 @@ class VectorSource extends Source {
* instead. A feature will not be added to the source if feature with * instead. A feature will not be added to the source if feature with
* the same id is already there. The reason for this behavior is to avoid * the same id is already there. The reason for this behavior is to avoid
* feature duplication when using bbox or tile loading strategies. * feature duplication when using bbox or tile loading strategies.
* @param {import("../Feature.js").default} feature Feature to add. * Note: this also applies if an {@link module:ol/Collection} is used for features,
* meaning that if a feature with a duplicate id is added in the collection, it will
* be removed from it right away.
* @param {import("../Feature.js").default<Geometry>} feature Feature to add.
* @api * @api
*/ */
addFeature(feature) { addFeature(feature) {
@@ -293,13 +298,16 @@ class VectorSource extends Source {
/** /**
* Add a feature without firing a `change` event. * Add a feature without firing a `change` event.
* @param {import("../Feature.js").default} feature Feature. * @param {import("../Feature.js").default<Geometry>} feature Feature.
* @protected * @protected
*/ */
addFeatureInternal(feature) { addFeatureInternal(feature) {
const featureKey = getUid(feature); const featureKey = getUid(feature);
if (!this.addToIndex_(featureKey, feature)) { if (!this.addToIndex_(featureKey, feature)) {
if (this.featuresCollection_) {
this.featuresCollection_.remove(feature);
}
return; return;
} }
@@ -322,7 +330,7 @@ class VectorSource extends Source {
/** /**
* @param {string} featureKey Unique identifier for the feature. * @param {string} featureKey Unique identifier for the feature.
* @param {import("../Feature.js").default} feature The feature. * @param {import("../Feature.js").default<Geometry>} feature The feature.
* @private * @private
*/ */
setupChangeEvents_(featureKey, feature) { setupChangeEvents_(featureKey, feature) {
@@ -337,7 +345,7 @@ class VectorSource extends Source {
/** /**
* @param {string} featureKey Unique identifier for the feature. * @param {string} featureKey Unique identifier for the feature.
* @param {import("../Feature.js").default} feature The feature. * @param {import("../Feature.js").default<Geometry>} feature The feature.
* @return {boolean} The feature is "valid", in the sense that it is also a * @return {boolean} The feature is "valid", in the sense that it is also a
* candidate for insertion into the Rtree. * candidate for insertion into the Rtree.
* @private * @private
@@ -362,7 +370,7 @@ class VectorSource extends Source {
/** /**
* Add a batch of features to the source. * Add a batch of features to the source.
* @param {Array<import("../Feature.js").default>} features Features to add. * @param {Array<import("../Feature.js").default<Geometry>>} features Features to add.
* @api * @api
*/ */
addFeatures(features) { addFeatures(features) {
@@ -373,7 +381,7 @@ class VectorSource extends Source {
/** /**
* Add features without firing a `change` event. * Add features without firing a `change` event.
* @param {Array<import("../Feature.js").default>} features Features. * @param {Array<import("../Feature.js").default<Geometry>>} features Features.
* @protected * @protected
*/ */
addFeaturesInternal(features) { addFeaturesInternal(features) {
@@ -414,14 +422,14 @@ class VectorSource extends Source {
/** /**
* @param {!Collection<import("../Feature.js").default>} collection Collection. * @param {!Collection<import("../Feature.js").default<Geometry>>} collection Collection.
* @private * @private
*/ */
bindFeaturesCollection_(collection) { bindFeaturesCollection_(collection) {
let modifyingCollection = false; let modifyingCollection = false;
listen(this, VectorEventType.ADDFEATURE, listen(this, VectorEventType.ADDFEATURE,
/** /**
* @param {VectorSourceEvent} evt The vector source event * @param {VectorSourceEvent<Geometry>} evt The vector source event
*/ */
function(evt) { function(evt) {
if (!modifyingCollection) { if (!modifyingCollection) {
@@ -432,7 +440,7 @@ class VectorSource extends Source {
}); });
listen(this, VectorEventType.REMOVEFEATURE, listen(this, VectorEventType.REMOVEFEATURE,
/** /**
* @param {VectorSourceEvent} evt The vector source event * @param {VectorSourceEvent<Geometry>} evt The vector source event
*/ */
function(evt) { function(evt) {
if (!modifyingCollection) { if (!modifyingCollection) {
@@ -448,7 +456,7 @@ class VectorSource extends Source {
function(evt) { function(evt) {
if (!modifyingCollection) { if (!modifyingCollection) {
modifyingCollection = true; modifyingCollection = true;
this.addFeature(/** @type {import("../Feature.js").default} */ (evt.element)); this.addFeature(/** @type {import("../Feature.js").default<Geometry>} */ (evt.element));
modifyingCollection = false; modifyingCollection = false;
} }
}, this); }, this);
@@ -459,7 +467,7 @@ class VectorSource extends Source {
function(evt) { function(evt) {
if (!modifyingCollection) { if (!modifyingCollection) {
modifyingCollection = true; modifyingCollection = true;
this.removeFeature(/** @type {import("../Feature.js").default} */ (evt.element)); this.removeFeature(/** @type {import("../Feature.js").default<Geometry>} */ (evt.element));
modifyingCollection = false; modifyingCollection = false;
} }
}, this); }, this);
@@ -512,7 +520,7 @@ class VectorSource extends Source {
* stop and the function will return the same value. * stop and the function will return the same value.
* Note: this function only iterate through the feature that have a defined geometry. * Note: this function only iterate through the feature that have a defined geometry.
* *
* @param {function(import("../Feature.js").default): T} callback Called with each feature * @param {function(import("../Feature.js").default<Geometry>): T} callback Called with each feature
* on the source. Return a truthy value to stop iteration. * on the source. Return a truthy value to stop iteration.
* @return {T|undefined} The return value from the last call to the callback. * @return {T|undefined} The return value from the last call to the callback.
* @template T * @template T
@@ -534,7 +542,7 @@ class VectorSource extends Source {
* value. * value.
* *
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate. * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {function(import("../Feature.js").default): T} callback Called with each feature * @param {function(import("../Feature.js").default<Geometry>): T} callback Called with each feature
* whose goemetry contains the provided coordinate. * whose goemetry contains the provided coordinate.
* @return {T|undefined} The return value from the last call to the callback. * @return {T|undefined} The return value from the last call to the callback.
* @template T * @template T
@@ -565,7 +573,7 @@ class VectorSource extends Source {
* features, equivalent to {@link module:ol/source/Vector~VectorSource#forEachFeature #forEachFeature()}. * features, equivalent to {@link module:ol/source/Vector~VectorSource#forEachFeature #forEachFeature()}.
* *
* @param {import("../extent.js").Extent} extent Extent. * @param {import("../extent.js").Extent} extent Extent.
* @param {function(import("../Feature.js").default): T} callback Called with each feature * @param {function(import("../Feature.js").default<Geometry>): T} callback Called with each feature
* whose bounding box intersects the provided extent. * whose bounding box intersects the provided extent.
* @return {T|undefined} The return value from the last call to the callback. * @return {T|undefined} The return value from the last call to the callback.
* @template T * @template T
@@ -589,7 +597,7 @@ class VectorSource extends Source {
* {@link module:ol/source/Vector~VectorSource#forEachFeatureInExtent #forEachFeatureInExtent()} method instead. * {@link module:ol/source/Vector~VectorSource#forEachFeatureInExtent #forEachFeatureInExtent()} method instead.
* *
* @param {import("../extent.js").Extent} extent Extent. * @param {import("../extent.js").Extent} extent Extent.
* @param {function(import("../Feature.js").default): T} callback Called with each feature * @param {function(import("../Feature.js").default<Geometry>): T} callback Called with each feature
* whose geometry intersects the provided extent. * whose geometry intersects the provided extent.
* @return {T|undefined} The return value from the last call to the callback. * @return {T|undefined} The return value from the last call to the callback.
* @template T * @template T
@@ -598,7 +606,7 @@ class VectorSource extends Source {
forEachFeatureIntersectingExtent(extent, callback) { forEachFeatureIntersectingExtent(extent, callback) {
return this.forEachFeatureInExtent(extent, return this.forEachFeatureInExtent(extent,
/** /**
* @param {import("../Feature.js").default} feature Feature. * @param {import("../Feature.js").default<Geometry>} feature Feature.
* @return {T|undefined} The return value from the last call to the callback. * @return {T|undefined} The return value from the last call to the callback.
*/ */
function(feature) { function(feature) {
@@ -617,7 +625,7 @@ class VectorSource extends Source {
* Get the features collection associated with this source. Will be `null` * Get the features collection associated with this source. Will be `null`
* unless the source was configured with `useSpatialIndex` set to `false`, or * unless the source was configured with `useSpatialIndex` set to `false`, or
* with an {@link module:ol/Collection} as `features`. * with an {@link module:ol/Collection} as `features`.
* @return {Collection<import("../Feature.js").default>} The collection of features. * @return {Collection<import("../Feature.js").default<Geometry>>} The collection of features.
* @api * @api
*/ */
getFeaturesCollection() { getFeaturesCollection() {
@@ -627,7 +635,7 @@ class VectorSource extends Source {
/** /**
* Get all features on the source in random order. * Get all features on the source in random order.
* @return {Array<import("../Feature.js").default>} Features. * @return {Array<import("../Feature.js").default<Geometry>>} Features.
* @api * @api
*/ */
getFeatures() { getFeatures() {
@@ -641,7 +649,7 @@ class VectorSource extends Source {
} }
} }
return ( return (
/** @type {Array<import("../Feature.js").default>} */ (features) /** @type {Array<import("../Feature.js").default<Geometry>>} */ (features)
); );
} }
@@ -649,7 +657,7 @@ class VectorSource extends Source {
/** /**
* Get all features whose geometry intersects the provided coordinate. * Get all features whose geometry intersects the provided coordinate.
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate. * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @return {Array<import("../Feature.js").default>} Features. * @return {Array<import("../Feature.js").default<Geometry>>} Features.
* @api * @api
*/ */
getFeaturesAtCoordinate(coordinate) { getFeaturesAtCoordinate(coordinate) {
@@ -669,7 +677,7 @@ class VectorSource extends Source {
* This method is not available when the source is configured with * This method is not available when the source is configured with
* `useSpatialIndex` set to `false`. * `useSpatialIndex` set to `false`.
* @param {import("../extent.js").Extent} extent Extent. * @param {import("../extent.js").Extent} extent Extent.
* @return {Array<import("../Feature.js").default>} Features. * @return {Array<import("../Feature.js").default<Geometry>>} Features.
* @api * @api
*/ */
getFeaturesInExtent(extent) { getFeaturesInExtent(extent) {
@@ -683,10 +691,10 @@ class VectorSource extends Source {
* This method is not available when the source is configured with * This method is not available when the source is configured with
* `useSpatialIndex` set to `false`. * `useSpatialIndex` set to `false`.
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate. * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {function(import("../Feature.js").default):boolean=} opt_filter Feature filter function. * @param {function(import("../Feature.js").default<Geometry>):boolean=} opt_filter Feature filter function.
* The filter function will receive one argument, the {@link module:ol/Feature feature} * The filter function will receive one argument, the {@link module:ol/Feature feature}
* and it should return a boolean value. By default, no filtering is made. * and it should return a boolean value. By default, no filtering is made.
* @return {import("../Feature.js").default} Closest feature. * @return {import("../Feature.js").default<Geometry>} Closest feature.
* @api * @api
*/ */
getClosestFeatureToCoordinate(coordinate, opt_filter) { getClosestFeatureToCoordinate(coordinate, opt_filter) {
@@ -706,7 +714,7 @@ class VectorSource extends Source {
const filter = opt_filter ? opt_filter : TRUE; const filter = opt_filter ? opt_filter : TRUE;
this.featuresRtree_.forEachInExtent(extent, this.featuresRtree_.forEachInExtent(extent,
/** /**
* @param {import("../Feature.js").default} feature Feature. * @param {import("../Feature.js").default<Geometry>} feature Feature.
*/ */
function(feature) { function(feature) {
if (filter(feature)) { if (filter(feature)) {
@@ -753,7 +761,7 @@ class VectorSource extends Source {
* `source.getFeatureById(2)` will return a feature with id `'2'` or `2`. * `source.getFeatureById(2)` will return a feature with id `'2'` or `2`.
* *
* @param {string|number} id Feature identifier. * @param {string|number} id Feature identifier.
* @return {import("../Feature.js").default} The feature (or `null` if not found). * @return {import("../Feature.js").default<Geometry>} The feature (or `null` if not found).
* @api * @api
*/ */
getFeatureById(id) { getFeatureById(id) {
@@ -797,7 +805,7 @@ class VectorSource extends Source {
* @private * @private
*/ */
handleFeatureChange_(event) { handleFeatureChange_(event) {
const feature = /** @type {import("../Feature.js").default} */ (event.target); const feature = /** @type {import("../Feature.js").default<Geometry>} */ (event.target);
const featureKey = getUid(feature); const featureKey = getUid(feature);
const geometry = feature.getGeometry(); const geometry = feature.getGeometry();
if (!geometry) { if (!geometry) {
@@ -845,7 +853,7 @@ class VectorSource extends Source {
/** /**
* Returns true if the feature is contained within the source. * Returns true if the feature is contained within the source.
* @param {import("../Feature.js").default} feature Feature. * @param {import("../Feature.js").default<Geometry>} feature Feature.
* @return {boolean} Has feature. * @return {boolean} Has feature.
* @api * @api
*/ */
@@ -927,7 +935,7 @@ class VectorSource extends Source {
* Remove a single feature from the source. If you want to remove all features * Remove a single feature from the source. If you want to remove all features
* at once, use the {@link module:ol/source/Vector~VectorSource#clear #clear()} method * at once, use the {@link module:ol/source/Vector~VectorSource#clear #clear()} method
* instead. * instead.
* @param {import("../Feature.js").default} feature Feature to remove. * @param {import("../Feature.js").default<Geometry>} feature Feature to remove.
* @api * @api
*/ */
removeFeature(feature) { removeFeature(feature) {
@@ -946,7 +954,7 @@ class VectorSource extends Source {
/** /**
* Remove feature without firing a `change` event. * Remove feature without firing a `change` event.
* @param {import("../Feature.js").default} feature Feature. * @param {import("../Feature.js").default<Geometry>} feature Feature.
* @protected * @protected
*/ */
removeFeatureInternal(feature) { removeFeatureInternal(feature) {
@@ -967,7 +975,7 @@ class VectorSource extends Source {
/** /**
* Remove a feature from the id index. Called internally when the feature id * Remove a feature from the id index. Called internally when the feature id
* may have changed. * may have changed.
* @param {import("../Feature.js").default} feature The feature. * @param {import("../Feature.js").default<Geometry>} feature The feature.
* @return {boolean} Removed the feature from the index. * @return {boolean} Removed the feature from the index.
* @private * @private
*/ */

View File

@@ -26,6 +26,7 @@ const TierSizeCalculation = {
export class CustomTile extends ImageTile { export class CustomTile extends ImageTile {
/** /**
* @param {number} tilePixelRatio Tile pixel ratio to display the tile
* @param {import("../tilegrid/TileGrid.js").default} tileGrid TileGrid that the tile belongs to. * @param {import("../tilegrid/TileGrid.js").default} tileGrid TileGrid that the tile belongs to.
* @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @param {TileState} state State. * @param {TileState} state State.
@@ -34,8 +35,7 @@ export class CustomTile extends ImageTile {
* @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function. * @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function.
* @param {import("../Tile.js").Options=} opt_options Tile options. * @param {import("../Tile.js").Options=} opt_options Tile options.
*/ */
constructor(tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { constructor(tilePixelRatio, tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) {
super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options);
/** /**
@@ -48,8 +48,11 @@ export class CustomTile extends ImageTile {
* @private * @private
* @type {import("../size.js").Size} * @type {import("../size.js").Size}
*/ */
this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])); this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])).map(
function(x) {
return x * tilePixelRatio;
}
);
} }
/** /**
@@ -143,6 +146,7 @@ class Zoomify extends TileImage {
const extent = options.extent || [0, -size[1], size[0], 0]; const extent = options.extent || [0, -size[1], size[0], 0];
const tierSizeInTiles = []; const tierSizeInTiles = [];
const tileSize = options.tileSize || DEFAULT_TILE_SIZE; const tileSize = options.tileSize || DEFAULT_TILE_SIZE;
const tilePixelRatio = options.tilePixelRatio || 1;
let tileSizeForTierSizeCalculation = tileSize; let tileSizeForTierSizeCalculation = tileSize;
switch (tierSizeCalculation) { switch (tierSizeCalculation) {
@@ -242,14 +246,14 @@ class Zoomify extends TileImage {
const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate)); const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate));
const ZoomifyTileClass = CustomTile.bind(null, tileGrid); const ZoomifyTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid);
super({ super({
attributions: options.attributions, attributions: options.attributions,
cacheSize: options.cacheSize, cacheSize: options.cacheSize,
crossOrigin: options.crossOrigin, crossOrigin: options.crossOrigin,
projection: options.projection, projection: options.projection,
tilePixelRatio: options.tilePixelRatio, tilePixelRatio: tilePixelRatio,
reprojectionErrorThreshold: options.reprojectionErrorThreshold, reprojectionErrorThreshold: options.reprojectionErrorThreshold,
tileClass: ZoomifyTileClass, tileClass: ZoomifyTileClass,
tileGrid: tileGrid, tileGrid: tileGrid,

View File

@@ -2,7 +2,7 @@
* @module ol/structs/RBush * @module ol/structs/RBush
*/ */
import {getUid} from '../util.js'; import {getUid} from '../util.js';
import rbush from 'rbush'; import RBush_ from 'rbush';
import {createOrUpdate, equals} from '../extent.js'; import {createOrUpdate, equals} from '../extent.js';
import {isEmpty} from '../obj.js'; import {isEmpty} from '../obj.js';
@@ -31,7 +31,7 @@ class RBush {
/** /**
* @private * @private
*/ */
this.rbush_ = rbush(opt_maxEntries, undefined); this.rbush_ = new RBush_(opt_maxEntries);
/** /**
* A mapping between the objects added to this rbush wrapper * A mapping between the objects added to this rbush wrapper

View File

@@ -3,11 +3,12 @@
*/ */
import {createCanvasContext2D} from '../dom.js'; import {createCanvasContext2D} from '../dom.js';
import {listenOnce, unlistenByKey} from '../events.js';
import EventTarget from '../events/Target.js'; import EventTarget from '../events/Target.js';
import EventType from '../events/EventType.js'; import EventType from '../events/EventType.js';
import ImageState from '../ImageState.js'; import ImageState from '../ImageState.js';
import {shared as iconImageCache} from './IconImageCache.js'; import {shared as iconImageCache} from './IconImageCache.js';
import {listenImage} from '../Image.js';
class IconImage extends EventTarget { class IconImage extends EventTarget {
/** /**
@@ -52,9 +53,9 @@ class IconImage extends EventTarget {
/** /**
* @private * @private
* @type {Array<import("../events.js").EventsKey>} * @type {function():void}
*/ */
this.imageListenerKeys_ = null; this.unlisten_ = null;
/** /**
* @private * @private
@@ -185,17 +186,16 @@ class IconImage extends EventTarget {
load() { load() {
if (this.imageState_ == ImageState.IDLE) { if (this.imageState_ == ImageState.IDLE) {
this.imageState_ = ImageState.LOADING; this.imageState_ = ImageState.LOADING;
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
try { try {
/** @type {HTMLImageElement} */ (this.image_).src = this.src_; /** @type {HTMLImageElement} */ (this.image_).src = this.src_;
} catch (e) { } catch (e) {
this.handleImageError_(); this.handleImageError_();
} }
this.unlisten_ = listenImage(
this.image_,
this.handleImageLoad_.bind(this),
this.handleImageError_.bind(this)
);
} }
} }
@@ -233,8 +233,10 @@ class IconImage extends EventTarget {
* @private * @private
*/ */
unlistenImage_() { unlistenImage_() {
this.imageListenerKeys_.forEach(unlistenByKey); if (this.unlisten_) {
this.imageListenerKeys_ = null; this.unlisten_();
this.unlisten_ = null;
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More