Compare commits

...

193 Commits

Author SHA1 Message Date
ahocevar
a8b5dfa981 6.0.0-beta.15 2019-08-20 11:18:27 +02:00
Andreas Hocevar
42f1a7ed30 Merge pull request #9873 from ahocevar/another-vectortile-load-fix
Only check extent when a url tile coordinate is available
2019-08-20 11:08:04 +02:00
Andreas Hocevar
f3d6456876 Merge pull request #9876 from openlayers/greenkeeper/yargs-14.0.0
Update yargs to the latest version 🚀
2019-08-20 08:36:49 +02:00
greenkeeper[bot]
2ca13de4de chore(package): update lockfile package-lock.json 2019-08-19 23:21:53 +00:00
greenkeeper[bot]
87ae93ca2d chore(package): update yargs to version 14.0.0 2019-08-19 23:21:44 +00:00
ahocevar
5da32dbc5b More tests 2019-08-19 11:39:39 +02:00
ahocevar
6be2818f14 Handle empty url tile coord (no wrapx) 2019-08-19 11:01:03 +02:00
Andreas Hocevar
b602a6b33a Merge pull request #9871 from ahocevar/tile-load-key-change
Properly handle tile source key change
2019-08-19 08:08:31 +02:00
Andreas Hocevar
20a81ceb2f Merge pull request #9870 from ahocevar/declutter-multi-geometries
Declutter multi geometries per geometry instead of per feature
2019-08-19 08:06:39 +02:00
Andreas Hocevar
3d6cf24c26 Merge pull request #9872 from ahocevar/wrapx-extent
Check extent for wrapped tile coordinate
2019-08-18 18:10:24 +02:00
ahocevar
6a741d0504 Check extent for wrapped tile coordinate 2019-08-18 16:21:46 +02:00
ahocevar
aa55cce3ba Add rendering test for decluttered multipolygons 2019-08-18 15:43:41 +02:00
ahocevar
a5fbbef970 Always go through source tile change logic 2019-08-18 11:44:20 +02:00
ahocevar
25c8d93eba Fix test 2019-08-18 11:44:20 +02:00
Tim Schaub
eb294c78d1 Failing test 2019-08-18 08:58:59 +02:00
ahocevar
bd3f35eef0 Declutter multi geometries per geometry instead of per feature 2019-08-17 23:55:20 +02:00
Tim Schaub
f839b34594 Merge pull request #9869 from MoonE/master
Only get squared tolerance once per render
2019-08-17 07:23:22 -06:00
Tim Schaub
99462d3b53 Only get squared tolerance once per render 2019-08-17 07:10:10 -06:00
Maximilian Krög
200392785d Squared tolerance does not change for each style. 2019-08-17 11:15:06 +02:00
Tim Schaub
c0e5c4b7fe 6.0.0-beta.14 2019-08-16 07:03:28 -06:00
ahocevar
189ad24528 Adjust expected image for CircleCI 2019-08-16 13:22:18 +02:00
ahocevar
4aa11ecc94 Remove lint 2019-08-16 13:05:46 +02:00
Andreas Hocevar
e61c5c07bc Merge pull request #9389 from KaiVolland/tile-rendering
Replaces listener test in tile.tests.js and removes artifacts
2019-08-16 13:02:48 +02:00
Tim Schaub
0212ce6554 Merge pull request #9855 from tschaub/vector-tile-loading
Make sure vector tile load handler is called
2019-08-16 04:05:11 -06:00
Tim Schaub
b76a0379dd Merge pull request #9864 from tschaub/opacity
Properly update frameState.animate and deal with non-numeric layer opacity
2019-08-15 15:09:14 -06:00
Tim Schaub
e94c7b6c39 Only set frameState.animate true if in transition and alpha < 1 2019-08-15 12:55:27 -06:00
Tim Schaub
2c69ad2bb4 Throw on non-numeric opacity values 2019-08-15 12:55:13 -06:00
Tim Schaub
e8e7c46463 Merge pull request #9858 from tschaub/audit-fix
Run npm audit fix
2019-08-13 16:42:43 -06:00
Tim Schaub
abda7f4f1d Separate lookups for source tiles by tile coord and tile key 2019-08-13 16:40:01 -06:00
Tim Schaub
e8500c395c Run npm audit fix 2019-08-13 16:28:18 -06:00
Tim Schaub
6f5a066bab Merge pull request #9856 from openlayers/greenkeeper/webpack-4.39.2
Update webpack to the latest version 🚀
2019-08-13 16:26:50 -06:00
Tim Schaub
08816ec9f9 Merge pull request #9857 from openlayers/greenkeeper/karma-chrome-launcher-3.1.0
Update karma-chrome-launcher to the latest version 🚀
2019-08-13 16:26:16 -06:00
greenkeeper[bot]
fac659fa0a chore(package): update lockfile package-lock.json 2019-08-13 19:40:27 +00:00
greenkeeper[bot]
b0069c3c5d chore(package): update karma-chrome-launcher to version 3.1.0 2019-08-13 19:40:15 +00:00
greenkeeper[bot]
e7be28d2b5 chore(package): update lockfile package-lock.json 2019-08-13 18:10:17 +00:00
greenkeeper[bot]
15aa5ebc1f chore(package): update webpack to version 4.39.2 2019-08-13 18:10:00 +00:00
Tim Schaub
a9ad24cce2 6.0.0-beta.13 2019-08-12 21:22:15 -06:00
Andreas Hocevar
486eb205cb Merge pull request #9844 from ahocevar/empty-vectorrendertile
Set initial tile state to EMPTY when outside source extent
2019-08-11 21:50:50 +02:00
ahocevar
28e64f646f Set initial tile state to EMPTY when outside source extent 2019-08-11 00:13:18 +02:00
Andreas Hocevar
78cf32ae70 Merge pull request #9839 from mike-000/patch-1
Enable correct display of side-by-side example when not using the examples template
2019-08-09 00:40:38 +02:00
mike-000
a340da8f6e Update side-by-side.css 2019-08-08 22:48:08 +01:00
mike-000
336056f4f0 Update side-by-side.html 2019-08-08 22:40:37 +01:00
Andreas Hocevar
e31e4b7867 Merge pull request #9838 from ahocevar/wms-servertype
Make WMSServerType appear in API docs
2019-08-08 21:36:52 +02:00
ahocevar
683c0ef5dc Make WMSServerType appear in API docs 2019-08-08 21:29:29 +02:00
Andreas Hocevar
23179e9ac5 Merge pull request #9754 from mike-000/patch-1
Export PDF example extent corrections
2019-08-08 21:07:49 +02:00
mike-000
7cdfc33d15 Export PDF example extent corrections
Fixes #9460 and #9665

Replace calculateExtent and fit with getResolution and setResolution as the former do not produce the desired result when the view is rotated.

Export the viewport instead of the map div (which doesn't resize) and specify the width and height in the export options

This is a minimal fix for the two issues.  Further enhancement including a fallback to export layer canvases where the browser doesn't support html-to-image could be done separately if necessary.
2019-08-08 19:41:51 +02:00
Andreas Hocevar
776dab81b8 Merge pull request #9834 from ahocevar/nextzen
Use nextzen for osm-vector-tiles example
2019-08-08 18:38:56 +02:00
ahocevar
182f1448a9 Use nextzen for osm-vector-tiles example 2019-08-08 12:57:55 +02:00
Andreas Hocevar
9b6fcf8d7b Merge pull request #9833 from ahocevar/remove-package-lock
Remove accidentally added package-lock.json
2019-08-08 12:52:33 +02:00
ahocevar
bd1c8db7a3 Remove accidentally added package-lock.json 2019-08-08 12:35:24 +02:00
Tim Schaub
0fb638fcb6 Merge pull request #9817 from openlayers/greenkeeper/webpack-4.39.1
Update webpack to the latest version 🚀
2019-08-07 06:38:10 -06:00
Tim Schaub
effb95b322 Merge pull request #9827 from openlayers/greenkeeper/coveralls-3.0.6
Update coveralls to the latest version 🚀
2019-08-07 06:37:12 -06:00
Tim Schaub
e5ac326f1c Merge pull request #9826 from tschaub/publish-options
Accept additional args when publishing
2019-08-07 06:33:14 -06:00
greenkeeper[bot]
2d0d06842d chore(package): update lockfile package-lock.json 2019-08-07 07:41:55 +00:00
greenkeeper[bot]
7355906c3a chore(package): update coveralls to version 3.0.6 2019-08-07 07:41:48 +00:00
Andreas Hocevar
ec10cda088 Merge pull request #9812 from ahocevar/zdirection
Make zDirection configurable on tile source
2019-08-07 08:56:23 +02:00
Tim Schaub
d59eed5d3b Accept additional args when publishing 2019-08-07 00:01:57 -04:00
Tim Schaub
dec44a7202 6.0.0-beta.12 2019-08-06 23:49:25 -04:00
Tim Schaub
082e160212 Merge pull request #9824 from tschaub/zooms
Support zoom limits for layers
2019-08-06 21:32:26 -04:00
Tim Schaub
65ad4932f4 Support zoom limits for layers 2019-08-06 21:19:09 -04:00
greenkeeper[bot]
d4980754ad chore(package): update webpack to version 4.39.1 2019-08-02 12:51:21 +00:00
Andreas Hocevar
c767091faf Merge pull request #9816 from openlayers/greenkeeper/webpack-4.39.0
Update webpack to the latest version 🚀
2019-08-02 13:28:07 +02:00
greenkeeper[bot]
d8aec039a9 chore(package): update webpack to version 4.39.0 2019-08-01 16:07:39 +00:00
ahocevar
e07ff9c04e Make zDirection configurable on tile source 2019-07-30 16:50:01 +02:00
Marc Jansen
230205c3fd Merge pull request #9804 from mike-000/9799
Replacement for the Shared Views example
2019-07-26 21:50:31 +02:00
mike-000
c66347c365 Update side-by-side.js 2019-07-26 16:31:20 +01:00
Frédéric Junod
bf2a13138d Merge pull request #9803 from openlayers/mp_cleanup
Remove unused variable in MousePosition control
2019-07-26 15:52:09 +02:00
Andreas Hocevar
8f85b1ba23 Merge pull request #9802 from ahocevar/empty-image
Introduce EMPTY image state to deal with images outside the view extent
2019-07-26 15:35:30 +02:00
mike-000
576fc6a82e Update side-by-side.js 2019-07-26 14:23:49 +01:00
mike-000
1e1a6d0939 Update side-by-side.js 2019-07-26 14:17:42 +01:00
mike-000
8034b72231 Update side-by-side.html 2019-07-26 14:02:37 +01:00
Frederic Junod
2d2f4eb1a2 Remove unused variable in MousePosition control
`this.lastMouseMovePixel_` value is never read.
2019-07-26 14:54:14 +02:00
mike-000
ac9b435a74 Create side-by-side.js 2019-07-26 13:36:51 +01:00
mike-000
9946554575 Create side-by-side.css 2019-07-26 13:34:28 +01:00
mike-000
e6458222c1 Create side-by-side.html 2019-07-26 13:32:28 +01:00
ahocevar
62d8760420 Introduce EMPTY image state to deal with images outside the view extent 2019-07-26 13:47:35 +02:00
Frédéric Junod
1cc92b4751 Merge pull request #9801 from openlayers/greenkeeper/webpack-4.38.0
Update webpack to the latest version 🚀
2019-07-26 11:45:25 +02:00
greenkeeper[bot]
8bf0284443 chore(package): update webpack to version 4.38.0 2019-07-26 07:50:01 +00:00
Andreas Hocevar
17551d2f08 Merge pull request #9795 from openlayers/greenkeeper/webpack-4.37.0
Update webpack to the latest version 🚀
2019-07-24 01:37:38 +02:00
greenkeeper[bot]
3f47a7e7be chore(package): update webpack to version 4.37.0 2019-07-23 21:39:52 +02:00
Andreas Hocevar
0cc1166a2d Merge pull request #9797 from ahocevar/vectortile-false-positives
Avoid false positives for line and polygon hit detection
2019-07-23 21:32:22 +02:00
Marc Jansen
6d09acc0d3 Merge pull request #9794 from marcjansen/fix-lint
Fix linting error (import extension missing)
2019-07-23 20:10:22 +02:00
ahocevar
ba9cdd3aa2 Fix rendering tests 2019-07-23 19:28:28 +02:00
ahocevar
387f797f23 Avoid false positives for line and polygon hit detection 2019-07-23 19:24:27 +02:00
Marc Jansen
f6fba61b47 Fix linting error (import extension missing) 2019-07-23 11:52:31 +02:00
Marc Jansen
15cbd69457 Merge pull request #9792 from marcjansen/better-getgetlegendgraphicurl
Improve documentation of getGetLegendGraphicUrl
2019-07-23 11:38:32 +02:00
Andreas Hocevar
3dca01490f Merge pull request #9793 from openlayers/greenkeeper/puppeteer-1.19.0
Update puppeteer to the latest version 🚀
2019-07-23 10:18:25 +02:00
Marc Jansen
b9c2ef389c Merge pull request #9375 from KaiVolland/reproj-rendering
Replaces reproj image.tests.js and tile.tests.js
2019-07-23 08:20:28 +02:00
greenkeeper[bot]
6000b296e2 chore(package): update puppeteer to version 1.19.0 2019-07-23 05:09:59 +00:00
Marc Jansen
2869ed245c Improve documentation of getGetLegendGraphicUrl
This also adresses one review comment by @jahow on #9762.
2019-07-22 17:02:30 +02:00
Marc Jansen
993bf0d2cb Merge pull request #9762 from KlausBenndorf/get-legend-request
Get legend request
2019-07-22 13:54:01 +02:00
Andreas Hocevar
2dda7127ed Merge pull request #9789 from ahocevar/decode-if-src-only
Use Image.prototype.decode only when src is already set
2019-07-22 09:18:56 +02:00
ahocevar
3cef9f2e00 Use Image.prototype.decode only when src is already set 2019-07-19 17:09:24 +02:00
Andreas Hocevar
6948bb7ebb Merge pull request #9778 from ahocevar/faster-getsourcetiles
Return existing source tiles if at target resolution
2019-07-19 16:55:25 +02:00
Frédéric Junod
7a0619447c Merge pull request #9787 from openlayers/greenkeeper/mocha-6.2.0
Update mocha to the latest version 🚀
2019-07-19 11:40:41 +02:00
greenkeeper[bot]
30925b4250 chore(package): update mocha to version 6.2.0 2019-07-18 22:39:09 +00:00
Andreas Hocevar
f232622ad4 Merge pull request #9786 from openlayers/greenkeeper/webpack-4.36.1
chore(package): update webpack to version 4.36.1
2019-07-18 11:18:59 +02:00
greenkeeper[bot]
3a20993d1f chore(package): update webpack to version 4.36.1
Closes #9781
2019-07-17 14:16:32 +00:00
ahocevar
612dd6fe28 Return existing source tiles if at target resolution 2019-07-16 22:09:24 +02:00
Tim Schaub
dc3ab508d4 Merge pull request #9622 from kekel87/master
Avoid mutating input in EsriJSON format
2019-07-15 14:35:58 -07:00
Andreas Hocevar
ff063caa4e Merge pull request #9755 from ahocevar/font-loading
Font loading improvements
2019-07-15 12:21:28 +02:00
Olivier Guyot
a629b7e5f2 Merge pull request #9767 from jahow/webgl-hit-retina
Fix hit detection for webgl layers on retina devices
2019-07-15 09:06:59 +02:00
Andreas Hocevar
99c920d393 Merge pull request #9774 from ahocevar/observable-properties-docs
Fix observable properties documentation
2019-07-15 00:46:53 +02:00
Tim Schaub
26950a2fca Merge pull request #9775 from openlayers/greenkeeper/jsdoc-3.6.3
Update jsdoc to the latest version 🚀
2019-07-14 11:11:46 -07:00
greenkeeper[bot]
5aabff5821 chore(package): update jsdoc to version 3.6.3 2019-07-14 17:36:55 +00:00
Michael Parry
a8e31dddee FIx #9510: EsriJSON readGeometry should not mutate input 2019-07-14 18:56:49 +02:00
ahocevar
b6bd35c15c Fix observable properties documentation 2019-07-14 10:15:00 +02:00
Frédéric Junod
5222a0ffd6 Merge pull request #9772 from openlayers/greenkeeper/karma-chrome-launcher-3.0.0
Update karma-chrome-launcher to the latest version 🚀
2019-07-13 08:42:57 +02:00
Frédéric Junod
f43898bac1 Merge pull request #9769 from openlayers/greenkeeper/coveralls-3.0.5
Update coveralls to the latest version 🚀
2019-07-13 08:42:24 +02:00
Frédéric Junod
dd683855f2 Merge pull request #9759 from openlayers/greenkeeper/marked-0.7.0
Update marked to the latest version 🚀
2019-07-13 08:41:54 +02:00
greenkeeper[bot]
6812e13490 chore(package): update karma-chrome-launcher to version 3.0.0 2019-07-12 23:38:02 +00:00
greenkeeper[bot]
e8794e78a1 chore(package): update coveralls to version 3.0.5 2019-07-12 02:40:13 +00:00
Olivier Guyot
2f3e8d04f1 Webgl points / fix hit detection for retina devices 2019-07-11 00:05:55 +02:00
jahow
a0ff08b734 Webgl points / improve tests
Now testing with two features and adding a call to `prepareFrame`
without which the final render transform was off.
Also added a test with a pixelratio > 1.
2019-07-11 00:05:55 +02:00
Andreas Hocevar
32325bc4ab Merge pull request #9763 from openlayers/greenkeeper/webpack-4.35.3
Update webpack to the latest version 🚀
2019-07-08 17:34:23 +02:00
greenkeeper[bot]
0ddc7da2f8 chore(package): update webpack to version 4.35.3 2019-07-08 14:33:48 +00:00
simonseyock
c0000e745e make resolution in getGetLegendGraphicUrl optional 2019-07-08 15:14:13 +02:00
Kai Volland
f493f01bd0 Introduces getGetLegendGraphic method
Adds the `getGetLegendGraphic` method to `ImageWMS` and `TileWMS` source.
Also adds an example and corresponding tests.
2019-07-08 15:14:13 +02:00
greenkeeper[bot]
22e1bb0e2f chore(package): update marked to version 0.7.0 2019-07-06 04:14:39 +00:00
Frédéric Junod
7e3ed92ba6 Merge pull request #9757 from fredj/canvas_types
Use CanvasLineJoin and CanvasLienCap types instead of string
2019-07-05 17:55:17 +02:00
Frederic Junod
ad51c7c08f Use CanvasLineCap type instead of string 2019-07-05 11:11:35 +02:00
Frederic Junod
7a8a0c18ae Use CanvasLineJoin type instead of string 2019-07-05 11:07:10 +02:00
Frédéric Junod
5cff84522a Merge pull request #9745 from fredj/misc_webgl
Simplify heatmap's shaders
2019-07-05 08:27:08 +02:00
Frédéric Junod
562f14fc83 Merge pull request #9748 from fredj/rm_hit_transform
Remove hit detection transforms from ol/renderer/webgl/PointsLayer
2019-07-05 08:21:04 +02:00
Andreas Hocevar
79fc4bafe6 Merge pull request #9752 from mike-000/mike-000-9750
Custom Animation example and the multiWorld view constraint
2019-07-04 23:04:05 +02:00
ahocevar
4b48997a0b Check font style and weight in addition to family 2019-07-04 16:12:35 +02:00
ahocevar
ab2d97d49b Don't give up too early when waiting for fonts 2019-07-04 16:11:39 +02:00
ahocevar
5616c535b0 Clean up properly when clearing label cache 2019-07-04 16:10:28 +02:00
ahocevar
b4306da7bb Fix font comparison, less context.font operations 2019-07-04 16:06:13 +02:00
Frédéric Junod
32d1d9214c Merge pull request #9751 from logan-bounet/set-minarea-in-dragzoom
Add minArea typedef and ctor prop in DragZoom
2019-07-04 09:34:41 +02:00
mike-000
12d21689c6 Stop the default multiWorld constraint being applied on Custom Animation example
For zoom: 1 to be reached the default extent constraint must be removed by adding multiWorld: true
2019-07-03 17:36:07 +01:00
Logan BOUNET
87d80be915 Add minArea typedef and ctor prop in DragZoom 2019-07-03 10:29:13 -04:00
Frederic Junod
89295b0359 Remove hit detection transforms from ol/renderer/webgl/PointsLayer 2019-07-03 10:38:13 +02:00
Andreas Hocevar
621a573dd8 Merge pull request #9747 from ahocevar/format-setprojection-tilepixels-only
Set world projection only for tile-pixels units
2019-07-03 08:25:25 +02:00
Frederic Junod
73032db345 Remove unused 'v_screenCoord' from fragment shader 2019-07-03 08:16:37 +02:00
Tim Schaub
1f86818250 Merge pull request #9744 from openlayers/greenkeeper/webpack-4.35.2
chore(package): update webpack to version 4.35.2
2019-07-02 16:03:22 -06:00
ahocevar
534afae345 Set world projection only for tile-pixels units 2019-07-02 23:03:22 +02:00
Tim Schaub
ac3072d888 Merge pull request #9743 from lutzhelm/iiif-v3beta
Finalize IIIF Image API version 3
2019-07-02 08:28:14 -06:00
Lutz Helm
018a7e2753 Remove superfluous comma 2019-07-02 13:50:29 +02:00
Lutz Helm
3cc18b6ea1 Test full region zoom levels with IIIF Image API v3 tile source 2019-07-02 13:39:23 +02:00
Lutz Helm
0507132a21 Correct and test IIIF version 3 image info parser 2019-07-02 13:21:09 +02:00
Lutz Helm
7147dce4d0 Remove unused code in IIIFInfo parser 2019-07-02 09:52:08 +02:00
Lutz Helm
cc976dd4ad Adjust IIIF info parsing to 3.0-beta spec
- respect `preferredFormats`
- correct compliance level profile formats and features
2019-07-01 17:51:52 +02:00
greenkeeper[bot]
dfd7b1f24f chore(package): update webpack to version 4.35.2
Closes #9740
2019-07-01 13:04:21 +00:00
Frederic Junod
bb35a03704 Don't rotate the points of the heatmap layer 2019-07-01 11:29:27 +02:00
Frederic Junod
ed340d7e2c Remove unused 'resolution' uniform from fragment shader 2019-07-01 10:00:55 +02:00
Frederic Junod
24e4f41452 Use size property from the frameState instead of creating new array 2019-07-01 09:44:37 +02:00
Andreas Hocevar
f44a199bae Merge pull request #9738 from openlayers/greenkeeper/marked-0.6.3
Update marked to the latest version 🚀
2019-06-30 12:36:05 +02:00
greenkeeper[bot]
37af683630 chore(package): update marked to version 0.6.3 2019-06-30 02:13:29 +00:00
Andreas Hocevar
bb756bdc2c Merge pull request #9737 from openlayers/greenkeeper/globby-10.0.0
Update globby to the latest version 🚀
2019-06-29 23:01:20 +02:00
greenkeeper[bot]
f3ea4e0301 chore(package): update globby to version 10.0.0 2019-06-29 18:02:47 +00:00
Andreas Hocevar
4a3e4c7c23 Merge pull request #9733 from ahocevar/apidoc-fixes
Get rid of JSDoc warnings and fix fires arguments
2019-06-29 14:40:08 +02:00
ahocevar
cb77f9e3dd Get rid of JSDoc warnings and fix fires arguments 2019-06-29 10:25:35 +02:00
Andreas Hocevar
8ff29f8431 Merge pull request #9732 from ahocevar/vectortile-empty-key
Fix EMPTY state and source key handling
2019-06-29 10:18:53 +02:00
ahocevar
c4937bc716 Fix EMPTY state and source key handling 2019-06-28 23:25:36 +02:00
Frédéric Junod
abb696d0fa Merge pull request #9724 from fredj/fix_import_in_tutorial
Fix wrong import in tutorial
2019-06-28 16:13:17 +02:00
Frédéric Junod
75a0a0ed49 Merge pull request #9722 from fredj/rm_unused
Remove unused param in getState function
2019-06-28 16:13:02 +02:00
Olivier Guyot
d9a4f37f75 Merge pull request #9655 from jahow/webgl-interaction
Add hit detection on the WebGL points renderer
2019-06-28 11:22:03 +02:00
Frederic Junod
933a6297bb Remove unused hasFeatureAtCoordinate from ol/renderer/Layer 2019-06-28 11:10:33 +02:00
jahow
3bca9b5297 Webgl / use feature index for hit detection in points layer
For each feature its opacity value index is encoded on 4 bytes
in the color values, and the uid is stored in the opacity
value, allowing for a much higher range of uids to be read.
2019-06-28 09:12:20 +02:00
Olivier Guyot
28b99767f8 Webgl points / read only one pixel for feature hit detection
Also implements `hasFeatureAtCoordinate`.

`hitTolerance` is not supported for now.
2019-06-28 09:12:20 +02:00
Olivier Guyot
e852294938 Webgl / improve reading of render targets data
Now two methods are available: `readAll` and `readPixel`,
and the data from the render target is not re-read every time unless
`clearCachedData` is called.
2019-06-28 09:12:20 +02:00
Olivier Guyot
917950a32b Improve icon-sprite-webgl example to include hit detection 2019-06-28 09:12:20 +02:00
Olivier Guyot
5ffca0633c Webgl Points / Add support for feature hit detection
For now only `forEachFeatureAtCoordinate` is implemented.

Each time the viewport is rendered, another similar render pass is
done using the specific hit detection instructions. Feature uid's are
encoded in the r,g,b,a channels and can then be decoded on the fly.

Note: the `readPixels` operation is taking a lot of time,
around 10-20ms each frame.
2019-06-28 09:12:20 +02:00
Olivier Guyot
38920867fb Webgl Helper / Add a method to prepare drawing to render targets 2019-06-28 09:12:20 +02:00
Olivier Guyot
6224d749c4 WebGL / Introduced the WebGLRenderTarget class
This utility class simplifies rendering to a texture & reading the
results of the render.

It also allows clearing its content before a new render.
2019-06-28 09:12:20 +02:00
Olivier Guyot
1257ade199 Webgl renderer / rename function to avoid confusion
`getBlankTexture` was too close to `WebGLHelper#createTexture`
2019-06-28 09:12:20 +02:00
Olivier Guyot
f25a16d90c Webgl helper / rework create texture utils
Now only one util is available: `createTexture`, which
is tested and allows binding an image and reusing an existing texture.
2019-06-28 09:12:20 +02:00
Olivier Guyot
b6425187de Remove unused WebGLShader class 2019-06-28 09:06:16 +02:00
Olivier Guyot
2b5e5459ab Webgl points / add hit detection buffers generation
Hit detection is done by rendering features with their id encoded in the
color attribute. A parallel set of render instructions and a second
vertex buffer is used specifically for that.
2019-06-28 09:06:16 +02:00
Olivier Guyot
8145b358c0 Webgl renderer / add id encode/decode utils 2019-06-28 09:06:16 +02:00
Olivier Guyot
034e0be76f Vector Source / index all features by uid
Previously features were indexed by uid only when they
did not have a defined id.
A new method was added: `getFeatureByUid`. This is not part of
the public API.
This will facilitate the lookup of features for hit detection.
2019-06-28 09:06:16 +02:00
Andreas Hocevar
6b60b25e8b Merge pull request #9728 from ahocevar/remove-package-lock
Remove accidently added package-lock.json
2019-06-28 09:04:39 +02:00
ahocevar
9bceccb5d1 Remove accidently added package-lock.json 2019-06-27 22:05:20 +02:00
Frederic Junod
0497225d5d Fix wrong import in tutorial 2019-06-27 17:01:46 +02:00
Frederic Junod
49a65568d8 Remove unused param in getState function 2019-06-27 15:07:59 +02:00
Tim Schaub
a4b76b2a00 Merge pull request #9719 from petrsloup/special-maptiler-key
Use special MapTiler API key dedicated for OpenLayers examples
2019-06-25 17:41:32 -06:00
Petr Sloup
61fdf965ba Use special MapTiler API key dedicated for OpenLayers examples 2019-06-25 16:34:58 +02:00
Frédéric Junod
9e928b996c Merge pull request #9714 from fredj/f9662
Update the circle style after setRadius calls
2019-06-25 16:15:48 +02:00
Frédéric Junod
3d60dfdeeb Merge pull request #9718 from fredj/cleanup
Remove unused variable in ol/renderer/webgl/PointsLayer
2019-06-25 15:37:23 +02:00
Frederic Junod
54c670de77 Remove unused variable in ol/renderer/webgl/PointsLayer 2019-06-25 15:24:00 +02:00
Frederic Junod
9aa11d2e11 Update the circle style after setRadius calls 2019-06-25 10:42:52 +02:00
Frederic Junod
0d6368d4ec Remove underscore from protected function, fix indentation, remove typecast 2019-06-25 10:40:11 +02:00
Frédéric Junod
caac90e8d7 Merge pull request #9711 from fredj/viewParams_space
Remove extra space for the 'viewParams' attribute
2019-06-25 08:29:22 +02:00
Tim Schaub
7464cedff7 6.0.0-beta.11 2019-06-24 17:06:04 -06:00
Tim Schaub
51c49e36bc Merge pull request #9712 from tschaub/mvt-id
Allow an arbitrary tag to be used as feature id
2019-06-24 16:42:06 -06:00
Andreas Hocevar
b29099a278 Merge pull request #9709 from ahocevar/circleci-puppeteer
Try to make rendering tests run again in puppeteer
2019-06-24 21:59:32 +02:00
Tim Schaub
08c494dd11 Allow an arbitrary tag to be used as feature id 2019-06-24 12:01:12 -06:00
Frederic Junod
c428421586 Remove extra space for the 'viewParams' attribute 2019-06-24 16:28:04 +02:00
ahocevar
c0a2549285 Try to make rendering tests run again in puppeteer 2019-06-24 10:20:47 +02:00
Kai Volland
c9939d2bff Replaces reproj image.tests.js and tile.tests.js
Transforms the old rendering tests for
ReprojImage and ReprojTile to the new
rendering test approach.
2019-04-03 11:01:32 +02:00
Kai Volland
94edf32540 Removes artifacts of old rendering test approach
Data and comparison images of the old rendering test approach
are removed as every test is transformed to the new approach.
2019-04-01 15:56:03 +02:00
Kai Volland
91d2f98774 Replaces listener test in tile.tests.js
Transforms the old render listener test to
the new rendering test approach.
2019-04-01 15:53:19 +02:00
193 changed files with 14296 additions and 1029 deletions

View File

@@ -31,6 +31,24 @@ function hasApiMembers(doclet) {
}
function includeAugments(doclet) {
// Make sure that `observables` and `fires` are taken from an already processed `class` doclet.
// This is necessary because JSDoc generates multiple doclets with the same longname.
const cls = classes[doclet.longname];
if (cls.observables && !doclet.observables) {
doclet.observables = cls.observables;
}
if (doclet.fires && cls.fires) {
for (let i = 0, ii = cls.fires.length; i < ii; ++i) {
const fires = cls.fires[i];
if (doclet.fires.indexOf(fires) == -1) {
doclet.fires.push(fires);
}
}
}
if (cls.fires && !doclet.fires) {
doclet.fires = cls.fires;
}
const augments = doclet.augments;
if (augments) {
let cls;
@@ -148,7 +166,7 @@ exports.handlers = {
// constructor from the docs.
doclet._hideConstructor = true;
includeAugments(doclet);
} else if (doclet.undocumented !== false && !doclet._hideConstructor && !(doclet.kind == 'typedef' && doclet.longname in types)) {
} else if (!doclet._hideConstructor && !(doclet.kind == 'typedef' && doclet.longname in types)) {
// Remove all other undocumented symbols
doclet.undocumented = true;
}

View File

@@ -236,3 +236,7 @@ A `WebGLArrayBuffer` must either be of type `ELEMENT_ARRAY_BUFFER` or `ARRAY_BUF
### 63
Support for the `OES_element_index_uint` WebGL extension is mandatory for WebGL layers.
### 64
Layer opacity must be a number.

View File

@@ -75,7 +75,7 @@ The above snippets can be combined into a single script that renders a map with
import Map from 'ol/Map';
import View from 'ol/View';
import OSM from 'ol/source/OSM';
import TileLayer from 'ol/source/Tile';
import TileLayer from 'ol/layer/Tile';
new Map({
layers: [

View File

@@ -65,18 +65,18 @@ exportButton.addEventListener('click', function() {
const width = Math.round(dim[0] * resolution / 25.4);
const height = Math.round(dim[1] * resolution / 25.4);
const size = map.getSize();
const extent = map.getView().calculateExtent(size);
const viewResolution = map.getView().getResolution();
map.once('rendercomplete', function() {
toJpeg(map.getTargetElement(), exportOptions).then(function(dataUrl) {
exportOptions.width = width;
exportOptions.height = height;
toJpeg(map.getViewport(), exportOptions).then(function(dataUrl) {
const pdf = new jsPDF('landscape', undefined, format);
pdf.addImage(dataUrl, 'JPEG', 0, 0, dim[0], dim[1]);
pdf.save('map.pdf');
// Reset original map size
map.setSize(size);
map.getView().fit(extent, {
size: size
});
map.getView().setResolution(viewResolution);
exportButton.disabled = false;
document.body.style.cursor = 'auto';
});
@@ -85,6 +85,7 @@ exportButton.addEventListener('click', function() {
// Set print size
const printSize = [width, height];
map.setSize(printSize);
map.getView().fit(extent, {size: printSize});
const scaling = Math.min(width / size[0], height / size[1]);
map.getView().setResolution(viewResolution / scaling);
}, false);

View File

@@ -21,7 +21,8 @@ const map = new Map({
target: 'map',
view: new View({
center: [0, 0],
zoom: 1
zoom: 1,
multiWorld: true
})
});

View File

@@ -18,3 +18,4 @@ cloak:
value: Your Mapbox access token from https://mapbox.com/ here
---
<div id="map" class="map"></div>
<div>Current sighting: <span id="info"></span></div>

View File

@@ -103,7 +103,7 @@ function loadData() {
loadData();
new Map({
const map = new Map({
layers: [
new TileLayer({
source: new TileJSON({
@@ -121,3 +121,18 @@ new Map({
zoom: 2
})
});
const info = document.getElementById('info');
map.on('pointermove', function(evt) {
if (map.getView().getInteracting()) {
return;
}
const pixel = evt.pixel;
info.innerText = '';
map.forEachFeatureAtPixel(pixel, function(feature) {
const datetime = feature.get('datetime');
const duration = feature.get('duration');
const shape = feature.get('shape');
info.innerText = 'On ' + datetime + ', lasted ' + duration + ' seconds and had a "' + shape + '" shape.';
});
});

View File

@@ -0,0 +1,13 @@
---
layout: example.html
title: Layer Zoom Limits
shortdesc: Using minZoom and maxZoom to control layer visibility.
docs: >
Layers support `minZoom` and `maxZoom` options for controlling visibility based on the view's zoom level.
If min or max zoom are set, the layer will only be visible at zoom levels greater than the `minZoom` and
less than or equal to the `maxZoom`. After construction, the layer's `setMinZoom` and `setMaxZoom` can
be used to set limits. This example shows an OSM layer at zoom levels 14 and lower and a USGS layer at
zoom levels higher than 14.
tags: "minZoom, maxZoom, layer"
---
<div id="map" class="map"></div>

View File

@@ -0,0 +1,33 @@
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 XYZ from '../src/ol/source/XYZ.js';
import {transformExtent, fromLonLat} from '../src/ol/proj.js';
const mapExtent = [-112.261791, 35.983744, -112.113981, 36.132062];
const map = new Map({
target: 'map',
layers: [
new TileLayer({
maxZoom: 14, // visible at zoom levels 14 and below
source: new OSM()
}),
new TileLayer({
minZoom: 14, // visible at zoom levels above 14
source: new XYZ({
attributions: 'Tiles © USGS, rendered with ' +
'<a href="http://www.maptiler.com/">MapTiler</a>',
url: 'https://tileserver.maptiler.com/grandcanyon/{z}/{x}/{y}.png'
})
})
],
view: new View({
center: fromLonLat([-112.18688965, 36.057944835]),
zoom: 15,
maxZoom: 18,
extent: transformExtent(mapExtent, 'EPSG:4326', 'EPSG:3857'),
constrainOnlyCenter: true
})
});

View File

@@ -9,7 +9,7 @@ resources:
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.js
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.css
cloak:
- key: ER67WIiPdCQvhgsUjoWK
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Get your own API key at https://www.maptiler.com/cloud/
---
<div id="map" class="map"></div>

View File

@@ -8,7 +8,7 @@ import VectorSource from '../src/ol/source/Vector.js';
import GeoJSON from '../src/ol/format/GeoJSON.js';
const center = [-98.8, 37.9];
const key = 'ER67WIiPdCQvhgsUjoWK';
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const mbMap = new mapboxgl.Map({
style: 'https://api.maptiler.com/maps/bright/style.json?key=' + key,

View File

@@ -4,7 +4,7 @@ title: Vector tiles created from a Mapbox Style object
shortdesc: Example of using ol-mapbox-style with tiles from tilehosting.com.
tags: "vector tiles, mapbox style, ol-mapbox-style, maptiler"
cloak:
- key: ER67WIiPdCQvhgsUjoWK
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Get your own API key at https://www.maptiler.com/cloud/
---
<!doctype html>

View File

@@ -1,3 +1,3 @@
import apply from 'ol-mapbox-style';
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=ER67WIiPdCQvhgsUjoWK');
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB');

View File

@@ -6,7 +6,7 @@ docs: >
A simple vector tiles map with Mapzen vector tiles. This example uses the TopoJSON format's `layerName` option to determine the layer ("water", "roads", "buildings") for styling. **Note**: [`ol/format/MVT`] is an even more efficient format for vector tiles.
tags: "vector, tiles, osm, mapzen"
cloak:
- key: vector-tiles-5eJz6JX
value: Your Mapzen API key from https://mapzen.com/developers
- key: uZNs91nMR-muUTP99MyBSg
value: Your Nextzen API key from https://developers.nextzen.org/
---
<div id="map" class="map"></div>

View File

@@ -6,7 +6,7 @@ import {fromLonLat} from '../src/ol/proj.js';
import VectorTileSource from '../src/ol/source/VectorTile.js';
import {Fill, Stroke, Style} from '../src/ol/style.js';
const key = 'vector-tiles-5eJz6JX';
const key = 'uZNs91nMR-muUTP99MyBSg';
const roadStyleCache = {};
const roadColor = {
@@ -67,7 +67,7 @@ const map = new Map({
layers: ['water', 'roads', 'buildings']
}),
maxZoom: 19,
url: 'https://tile.mapzen.com/mapzen/vector/v1/all/{z}/{x}/{y}.topojson?api_key=' + key
url: 'https://tile.nextzen.org/tilezen/vector/v1/all/{z}/{x}/{y}.topojson?api_key=' + key
}),
style: function(feature, resolution) {
switch (feature.get('layer')) {

10
examples/side-by-side.css Normal file
View File

@@ -0,0 +1,10 @@
@media (min-width: 800px) {
.wrapper {
display: flex;
}
.half {
padding: 0 10px;
width: 50%;
float: left;
}
}

View File

@@ -0,0 +1,21 @@
---
layout: example.html
title: Shared Views
shortdesc: Two maps share view properties
docs: >
Two maps (one Road, one Aerial) share the same center, resolution and rotation.
tags: "side-by-side, bing, bing-maps"
cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
---
<div class="wrapper">
<div class="half">
<h4>Road</h4>
<div id="roadMap" class="map"></div>
</div>
<div class="half">
<h4>Aerial</h4>
<div id="aerialMap" class="map"></div>
</div>
</div>

37
examples/side-by-side.js Normal file
View File

@@ -0,0 +1,37 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js';
import BingMaps from '../src/ol/source/BingMaps.js';
const roadLayer = new TileLayer({
source: new BingMaps({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
imagerySet: 'RoadOnDemand',
maxZoom: 19
})
});
const aerialLayer = new TileLayer({
source: new BingMaps({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
imagerySet: 'Aerial',
maxZoom: 19
})
});
const view = new View({
center: [-6655.5402445057125, 6709968.258934638],
zoom: 13
});
const map1 = new Map({
target: 'roadMap',
layers: [roadLayer],
view: view
});
const map2 = new Map({
target: 'aerialMap',
layers: [aerialLayer],
view: view
});

View File

@@ -5,7 +5,7 @@ shortdesc: Label decluttering with a custom renderer.
resources:
- https://cdn.polyfill.io/v2/polyfill.min.js?features=Set"
docs: >
Decluttering is used to avoid overlapping labels with `overflow: true` set on the text style. For MultiPolygon geometries, only the widest polygon is selected in a custom `geometry` function.
Decluttering is used to avoid overlapping labels. The `overflow: true` setting on the text style makes it so labels that do not fit within the bounds of a polygon are also included.
tags: "vector, decluttering, labels"
---
<div id="map" class="map"></div>

View File

@@ -1,6 +1,5 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import {getWidth} from '../src/ol/extent.js';
import GeoJSON from '../src/ol/format/GeoJSON.js';
import VectorLayer from '../src/ol/layer/Vector.js';
import VectorSource from '../src/ol/source/Vector.js';
@@ -15,23 +14,6 @@ const map = new Map({
});
const labelStyle = new Style({
geometry: function(feature) {
let geometry = feature.getGeometry();
if (geometry.getType() == 'MultiPolygon') {
// Only render label for the widest polygon of a multipolygon
const polygons = geometry.getPolygons();
let widest = 0;
for (let i = 0, ii = polygons.length; i < ii; ++i) {
const polygon = polygons[i];
const width = getWidth(polygon.getExtent());
if (width > widest) {
widest = width;
geometry = polygon;
}
}
}
return geometry;
},
text: new Text({
font: '12px Calibri,sans-serif',
overflow: true,

View File

@@ -0,0 +1,15 @@
---
layout: example.html
title: WMS GetLegendGraphic
shortdesc: Example of a WMS GetLegendGraphic.
docs: >
WMS supports the [getLegendGraphic request](https://docs.geoserver.org/latest/en/user/services/wms/get_legend_graphic/index.html).
This example demonstrates the use of the `getGetLegendGraphicUrl` method.
As a legend can be responsive to the scale it is updated on every change of the resolution.
tags: "GetLegendGraphic, getGetLegendGraphicURL, WMS"
---
<div id="map" class="map"></div>
Legend:
<div><img id="legend" src=""/></div>

View File

@@ -0,0 +1,47 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import {Image as ImageLayer, Tile as TileLayer} from '../src/ol/layer.js';
import ImageWMS from '../src/ol/source/ImageWMS.js';
import OSM from '../src/ol/source/OSM.js';
const wmsSource = new ImageWMS({
url: 'https://ahocevar.com/geoserver/wms',
params: {'LAYERS': 'topp:states'},
ratio: 1,
serverType: 'geoserver'
});
const updateLegend = function(resolution) {
const graphicUrl = wmsSource.getGetLegendGraphicUrl(resolution);
const img = document.getElementById('legend');
img.src = graphicUrl;
};
const layers = [
new TileLayer({
source: new OSM()
}),
new ImageLayer({
extent: [-13884991, 2870341, -7455066, 6338219],
source: wmsSource
})
];
const map = new Map({
layers: layers,
target: 'map',
view: new View({
center: [-10997148, 4569099],
zoom: 4
})
});
// Initial legend
const resolution = map.getView().getResolution();
updateLegend(resolution);
// Update the legend when the resolution changes
map.getView().on('change:resolution', function(event) {
const resolution = event.target.getResolution();
updateLegend(resolution);
});

11295
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ol",
"version": "6.0.0-beta.10",
"version": "6.0.0-beta.15",
"description": "OpenLayers mapping library",
"keywords": [
"map",
@@ -54,23 +54,23 @@
"chaikin-smooth": "^1.0.4",
"clean-css-cli": "4.3.0",
"copy-webpack-plugin": "^5.0.3",
"coveralls": "3.0.4",
"coveralls": "3.0.6",
"eslint": "^6.0.0",
"eslint-config-openlayers": "^12.0.0",
"expect.js": "0.3.1",
"front-matter": "^3.0.2",
"fs-extra": "^8.0.0",
"glob": "^7.1.4",
"globby": "^9.2.0",
"globby": "^10.0.0",
"handlebars": "4.1.2",
"html-to-image": "^0.1.0",
"istanbul": "0.4.5",
"istanbul-instrumenter-loader": "^3.0.1",
"jquery": "3.4.1",
"jsdoc": "3.6.2",
"jsdoc": "3.6.3",
"jsdoc-plugin-typescript": "^2.0.1",
"karma": "^4.1.0",
"karma-chrome-launcher": "2.2.0",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "^1.1.2",
"karma-coverage-istanbul-reporter": "^2.0.5",
"karma-firefox-launcher": "^1.1.0",
@@ -78,13 +78,13 @@
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.0-rc.2",
"loglevelnext": "^3.0.1",
"marked": "0.6.2",
"mocha": "6.1.4",
"ol-mapbox-style": "^5.0.0-beta.2",
"marked": "0.7.0",
"mocha": "6.2.0",
"ol-mapbox-style": "^5.0.0-beta.3",
"pixelmatch": "^5.0.0",
"pngjs": "^3.4.0",
"proj4": "2.5.0",
"puppeteer": "~1.18.0",
"puppeteer": "~1.19.0",
"rollup": "^1.12.0",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^10.0.0",
@@ -97,11 +97,11 @@
"typescript": "^3.4.5",
"url-polyfill": "^1.1.5",
"walk": "^2.3.9",
"webpack": "4.35.0",
"webpack": "4.39.2",
"webpack-cli": "^3.3.2",
"webpack-dev-middleware": "^3.6.2",
"webpack-dev-server": "^3.3.1",
"yargs": "^13.2.2"
"yargs": "^14.0.0"
},
"eslintConfig": {
"extends": "openlayers",

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,57 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import Feature from '../../../src/ol/Feature.js';
import Point from '../../../src/ol/geom/Point.js';
import VectorLayer from '../../../src/ol/layer/Vector.js';
import VectorSource from '../../../src/ol/source/Vector.js';
import Circle from '../../../src/ol/style/Circle.js';
import Style from '../../../src/ol/style/Style.js';
import Stroke from '../../../src/ol/style/Stroke.js';
const vectorSource = new VectorSource();
vectorSource.addFeatures([
new Feature({
geometry: new Point([-50, 50]),
radius: 10
}),
new Feature({
geometry: new Point([50, -50]),
radius: 20
}),
new Feature({
geometry: new Point([50, 50]),
radius: 30
})
]);
const style = new Style({
image: new Circle({
radius: 1,
stroke: new Stroke({
color: '#00f',
width: 3
})
})
});
new Map({
pixelRatio: 1,
layers: [
new VectorLayer({
source: vectorSource,
style: function(feature) {
style.getImage().setRadius(feature.get('radius'));
return style;
}
})
],
target: 'map',
view: new View({
center: [0, 0],
resolution: 1
})
});
render();

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@@ -0,0 +1,46 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import TileLayer from '../../../src/ol/layer/Tile.js';
import {transform, fromLonLat} from '../../../src/ol/proj.js';
import XYZ from '../../../src/ol/source/XYZ.js';
import CircleStyle from '../../../src/ol/style/Circle.js';
import Fill from '../../../src/ol/style/Fill.js';
import Stroke from '../../../src/ol/style/Stroke.js';
import Point from '../../../src/ol/geom/Point.js';
import {getVectorContext} from '../../../src/ol/render.js';
const center = fromLonLat([8.6, 50.1]);
const layer = new TileLayer({
source: new XYZ({
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg',
transition: 0
})
});
const onRender = function(event) {
const context = event.context;
context.restore();
const vectorContext = getVectorContext(event);
vectorContext.setImageStyle(new CircleStyle({
radius: 12,
fill: new Fill({color: 'yellow'}),
stroke: new Stroke({color: 'red', width: 1})
}));
vectorContext.drawPoint(new Point(
transform([13, 37], 'EPSG:4326', 'EPSG:3857')));
};
layer.on('postrender', onRender);
const map = new Map({
layers: [
],
target: 'map',
view: new View({
center: center,
zoom: 3
})
});
map.addLayer(layer);
render();

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1,55 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import GeoJSON from '../../../src/ol/format/GeoJSON.js';
import VectorLayer from '../../../src/ol/layer/Vector.js';
import VectorSource from '../../../src/ol/source/Vector.js';
import {Fill, Stroke, Style, Text} from '../../../src/ol/style.js';
const map = new Map({
target: 'map',
view: new View({
center: [-17465028, 2331736],
zoom: 5
})
});
const labelStyle = new Style({
text: new Text({
font: '16px Ubuntu',
overflow: true,
fill: new Fill({
color: '#000'
}),
stroke: new Stroke({
color: '#fff',
width: 3
})
})
});
const countryStyle = new Style({
fill: new Fill({
color: 'rgba(255, 255, 255, 0.6)'
}),
stroke: new Stroke({
color: '#319FD3',
width: 1
})
});
const style = [countryStyle, labelStyle];
const vectorLayer = new VectorLayer({
source: new VectorSource({
url: '/data/countries.json',
format: new GeoJSON()
}),
style: function(feature) {
labelStyle.getText().setText(feature.get('name'));
return style;
},
declutter: true
});
map.addLayer(vectorLayer);
render({tolerance: 0.007});

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,31 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import Static from '../../../src/ol/source/ImageStatic.js';
import {
get as getProjection,
transformExtent
} from '../../../src/ol/proj.js';
import ImageLayer from '../../../src/ol/layer/Image.js';
const source = new Static({
url: '/data/tiles/osm/5/5/12.png',
imageExtent: transformExtent([-123, 37, -122, 38], 'EPSG:4326', 'EPSG:3857'),
projection: getProjection('EPSG:3857')
});
new Map({
pixelRatio: 1,
target: 'map',
layers: [new ImageLayer({
source: source
})],
view: new View({
center: [-122.416667, 37.783333],
zoom: 8,
projection: 'EPSG:4326'
})
});
render({
tolerance: 0.001
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,36 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import XYZ from '../../../src/ol/source/XYZ.js';
import TileLayer from '../../../src/ol/layer/Tile.js';
import {toLonLat, get} from '../../../src/ol/proj.js';
import {createXYZ, createForProjection} from '../../../src/ol/tilegrid.js';
const tileGrid = createXYZ();
const extent = tileGrid.getTileCoordExtent([5, 5, 12]);
const center = [(extent[0] + extent[2]) / 2, extent[1]];
const source = new XYZ({
transition: 0,
minZoom: 5,
maxZoom: 5,
url: '/data/tiles/osm/{z}/{x}/{y}.png'
});
source.setTileGridForProjection(get('EPSG:4326'), createForProjection(get('EPSG:4326'), 7, [64, 64]));
new Map({
pixelRatio: 1,
target: 'map',
layers: [
new TileLayer({
source: source
})
],
view: new View({
projection: 'EPSG:4326',
center: toLonLat(center),
zoom: 5
})
});
render();

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -0,0 +1,41 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import XYZ from '../../../src/ol/source/XYZ.js';
import TileLayer from '../../../src/ol/layer/Tile.js';
import {get, transform} from '../../../src/ol/proj.js';
import {register} from '../../../src/ol/proj/proj4.js';
import proj4 from 'proj4';
proj4.defs('EPSG:5070',
'+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=23 +lon_0=-96 +x_0=0 ' +
'+y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
register(proj4);
const proj5070 = get('EPSG:5070');
proj5070.setExtent([-6e6, 0, 4e6, 6e6]);
const center4326 = [-118.125, 31.95];
const center = transform(center4326, 'EPSG:4326', 'EPSG:5070');
const source = new XYZ({
transition: 0,
minZoom: 5,
maxZoom: 5,
url: '/data/tiles/osm/{z}/{x}/{y}.png'
});
new Map({
pixelRatio: 1,
target: 'map',
layers: [
new TileLayer({
source: source
})
],
view: new View({
projection: 'EPSG:5070',
center: center,
zoom: 4
})
});
render();

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,40 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import XYZ from '../../../src/ol/source/XYZ.js';
import TileLayer from '../../../src/ol/layer/Tile.js';
import {get, transform} from '../../../src/ol/proj.js';
import {register} from '../../../src/ol/proj/proj4.js';
import proj4 from 'proj4';
proj4.defs('ESRI:54009', '+proj=moll +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
register(proj4);
const proj54009 = get('ESRI:54009');
proj54009.setExtent([-18e6, -9e6, 18e6, 9e6]);
const center4326 = [-118.125, 31.95];
const center = transform(center4326, 'EPSG:4326', 'ESRI:54009');
const source = new XYZ({
transition: 0,
minZoom: 5,
maxZoom: 5,
url: '/data/tiles/osm/{z}/{x}/{y}.png'
});
new Map({
pixelRatio: 1,
target: 'map',
layers: [
new TileLayer({
source: source
})
],
view: new View({
projection: 'ESRI:54009',
center: center,
zoom: 6
})
});
render();

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,40 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import XYZ from '../../../src/ol/source/XYZ.js';
import TileLayer from '../../../src/ol/layer/Tile.js';
import {get, transform} from '../../../src/ol/proj.js';
import {register} from '../../../src/ol/proj/proj4.js';
import proj4 from 'proj4';
proj4.defs('merc_180', '+proj=merc +lon_0=180 +units=m +no_defs');
register(proj4);
const merc = get('merc_180');
merc.setExtent([-20026376.39, -20048966.10, 20026376.39, 20048966.10]);
const center4326 = [180, 0];
const center = transform(center4326, 'EPSG:4326', 'merc_180');
const source = new XYZ({
projection: 'EPSG:4326',
minZoom: 0,
maxZoom: 0,
url: '/data/tiles/4326/{z}/{x}/{y}.png'
});
new Map({
pixelRatio: 1,
target: 'map',
layers: [
new TileLayer({
source: source
})
],
view: new View({
projection: 'merc_180',
center: center,
zoom: 0
})
});
render();

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -0,0 +1,38 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import XYZ from '../../../src/ol/source/XYZ.js';
import TileLayer from '../../../src/ol/layer/Tile.js';
import {toLonLat} from '../../../src/ol/proj.js';
import {createXYZ} from '../../../src/ol/tilegrid.js';
const tileGrid = createXYZ({tileSize: [512, 256]});
const extent = tileGrid.getTileCoordExtent([5, 3, 12]);
const center = [
(extent[0] + extent[2]) / 2,
(extent[1] + extent[3]) / 2
];
const source = new XYZ({
projection: 'EPSG:3857',
minZoom: 5,
maxZoom: 5,
url: '/data/tiles/512x256/{z}/{x}/{y}.png',
tileSize: [512, 256]
});
new Map({
pixelRatio: 1,
target: 'map',
layers: [
new TileLayer({
source: source
})
],
view: new View({
projection: 'EPSG:4326',
center: toLonLat(center),
zoom: 5
})
});
render();

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -0,0 +1,40 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import XYZ from '../../../src/ol/source/XYZ.js';
import TileLayer from '../../../src/ol/layer/Tile.js';
import {get, transform} from '../../../src/ol/proj.js';
import {register} from '../../../src/ol/proj/proj4.js';
import proj4 from 'proj4';
proj4.defs('EPSG:3413', '+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 ' +
'+k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
register(proj4);
const proj3413 = get('EPSG:3413');
proj3413.setExtent([-4194304, -4194304, 4194304, 4194304]);
const center4326 = [0, 90];
const center = transform(center4326, 'EPSG:4326', 'EPSG:3413');
const source = new XYZ({
maxZoom: 0,
projection: 'EPSG:4326',
url: '/data/tiles/4326/{z}/{x}/{y}.png'
});
new Map({
pixelRatio: 1,
target: 'map',
layers: [
new TileLayer({
source: source
})
],
view: new View({
projection: 'EPSG:3413',
center: center,
zoom: 0
})
});
render();

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -357,7 +357,7 @@ if (require.main === module) {
option('puppeteer-args', {
describe: 'Additional args for Puppeteer',
type: 'array',
default: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : []
default: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'] : []
}).
parse();

View File

@@ -159,7 +159,7 @@ class ImageWrapper extends ImageBase {
export function listenImage(image, loadHandler, errorHandler) {
const img = /** @type {HTMLImageElement} */ (image);
if (IMAGE_DECODE) {
if (img.src && IMAGE_DECODE) {
const promise = img.decode();
let listening = true;
const unlisten = function() {

View File

@@ -9,5 +9,6 @@ export default {
IDLE: 0,
LOADING: 1,
LOADED: 2,
ERROR: 3
ERROR: 3,
EMPTY: 4
};

View File

@@ -1224,7 +1224,7 @@ class PluggableMap extends BaseObject {
let frameState = null;
if (size !== undefined && hasArea(size) && view && view.isDef()) {
const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);
viewState = view.getState(this.pixelRatio_);
viewState = view.getState();
frameState = {
animate: false,
coordinateToPixelTransform: this.coordinateToPixelTransform_,

View File

@@ -927,10 +927,9 @@ class View extends BaseObject {
}
/**
* @param {number} pixelRatio Pixel ratio for center rounding.
* @return {State} View state.
*/
getState(pixelRatio) {
getState() {
const center = /** @type {import("./coordinate.js").Coordinate} */ (this.getCenter());
const projection = this.getProjection();
const resolution = /** @type {number} */ (this.getResolution());
@@ -1148,7 +1147,6 @@ class View extends BaseObject {
* constraint will apply.
* @param {number} ratio The ratio to apply on the view resolution.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation.
* @observable
* @api
*/
adjustResolution(ratio, opt_anchor) {
@@ -1180,7 +1178,6 @@ class View extends BaseObject {
* constraint will apply.
* @param {number} delta Relative value to add to the zoom rotation, in radians.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The rotation center.
* @observable
* @api
*/
adjustRotation(delta, opt_anchor) {

View File

@@ -7,7 +7,7 @@ import {CLASS_CONTROL, CLASS_UNSELECTABLE, CLASS_COLLAPSED} from '../css.js';
import {removeChildren, replaceNode} from '../dom.js';
import {listen} from '../events.js';
import EventType from '../events/EventType.js';
import {visibleAtResolution} from '../layer/Layer.js';
import {inView} from '../layer/Layer.js';
/**
@@ -170,10 +170,9 @@ class Attribution extends Control {
const visibleAttributions = [];
const layerStatesArray = frameState.layerStatesArray;
const resolution = frameState.viewState.resolution;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
if (!visibleAtResolution(layerState, resolution)) {
if (!inView(layerState, frameState.viewState)) {
continue;
}

View File

@@ -108,12 +108,6 @@ class MousePosition extends Control {
*/
this.transform_ = null;
/**
* @private
* @type {import("../pixel.js").Pixel}
*/
this.lastMouseMovePixel_ = null;
}
/**
@@ -156,8 +150,7 @@ class MousePosition extends Control {
*/
handleMouseMove(event) {
const map = this.getMap();
this.lastMouseMovePixel_ = map.getEventPixel(event);
this.updateHTML_(this.lastMouseMovePixel_);
this.updateHTML_(map.getEventPixel(event));
}
/**
@@ -166,7 +159,6 @@ class MousePosition extends Control {
*/
handleMouseOut(event) {
this.updateHTML_(null);
this.lastMouseMovePixel_ = null;
}
/**

View File

@@ -2,6 +2,13 @@
* @module ol/css
*/
/**
* @typedef {Object} FontParameters
* @property {Array<string>} families
* @property {string} style
* @property {string} weight
*/
/**
* The CSS class for hidden feature.
@@ -62,10 +69,13 @@ export const CLASS_COLLAPSED = 'ol-collapsed';
* Get the list of font families from a font spec. Note that this doesn't work
* for font families that have commas in them.
* @param {string} The CSS font property.
* @return {Object<string>} The font families (or null if the input spec is invalid).
* @return {FontParameters} The font families (or null if the input spec is invalid).
*/
export const getFontFamilies = (function() {
export const getFontParameters = (function() {
let style;
/**
* @type {Object<string, FontParameters>}
*/
const cache = {};
return function(font) {
if (!style) {
@@ -74,11 +84,18 @@ export const getFontFamilies = (function() {
if (!(font in cache)) {
style.font = font;
const family = style.fontFamily;
const fontWeight = style.fontWeight;
const fontStyle = style.fontStyle;
style.font = '';
if (!family) {
return null;
}
cache[font] = family.split(/,\s?/);
const families = family.split(/,\s?/);
cache[font] = {
families: families,
weight: fontWeight,
style: fontStyle
};
}
return cache[font];
};

View File

@@ -256,10 +256,10 @@ function readGeometry(object, opt_options) {
const rings = convertRings(esriJSONPolygon.rings, layout);
if (rings.length === 1) {
type = GeometryType.POLYGON;
object['rings'] = rings[0];
object = Object.assign({}, object, {['rings']: rings[0]});
} else {
type = GeometryType.MULTI_POLYGON;
object['rings'] = rings;
object = Object.assign({}, object, {['rings']: rings});
}
}
const geometryReader = GEOMETRY_READERS[type];

View File

@@ -4,6 +4,7 @@
import {assign} from '../obj.js';
import {abstract} from '../util.js';
import {get as getProjection, equivalent as equivalentProjection, transformExtent} from '../proj.js';
import Units from '../proj/Units.js';
/**
@@ -89,8 +90,9 @@ class FeatureFormat {
let options;
if (opt_options) {
let dataProjection = opt_options.dataProjection ?
opt_options.dataProjection : this.readProjection(source);
if (opt_options.extent) {
getProjection(opt_options.dataProjection) : this.readProjection(source);
if (opt_options.extent &&
dataProjection && dataProjection.getUnits() === Units.TILE_PIXELS) {
dataProjection = getProjection(dataProjection);
dataProjection.setWorldExtent(opt_options.extent);
}

View File

@@ -94,6 +94,8 @@ import {assert} from '../asserts.js';
* image service additional to the ones indicated by the compliance level.
* @property {Array<string>} [extraFeatures] Additional supported features whose support
* is not indicated by the compliance level.
* @property {Array<string>} [preferredFormats] Image formats that should preferrably
* be used.
*/
/**
@@ -156,15 +158,15 @@ IIIF_PROFILE_VALUES[Versions.VERSION3] = {
qualities: ['default']
},
'level1': {
supports: ['regionByPx', 'regionSquare', 'sizeByW', 'sizeByH'],
supports: ['regionByPx', 'regionSquare', 'sizeByW', 'sizeByH', 'sizeByWh'],
formats: ['jpg'],
qualities: ['default']
},
'level2': {
supports: ['regionByPx', 'regionSquare', 'regionByPct',
'sizeByW', 'sizeByH', 'sizeByPct', 'sizeByConfinedWh', 'sizeByWh'],
formats: ['jpg'],
qualities: ['default', 'bitonal']
formats: ['jpg', 'png'],
qualities: ['default']
}
};
IIIF_PROFILE_VALUES['none'] = {
@@ -231,7 +233,16 @@ function generateVersion2Options(iiifInfo) {
}
function generateVersion3Options(iiifInfo) {
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures();
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures(),
formats = iiifInfo.imageInfo.extraFormats === undefined ? levelProfile.formats :
[...levelProfile.formats, ...iiifInfo.imageInfo.extraFormats],
preferredFormat = iiifInfo.imageInfo.preferredFormats !== undefined && Array.isArray(iiifInfo.imageInfo.preferredFormats) &&
iiifInfo.imageInfo.preferredFormats.length > 0 ?
iiifInfo.imageInfo.preferredFormats.filter(function(format) {
return ['jpg', 'png', 'gif'].includes(format);
}).reduce(function(acc, format) {
return acc === undefined && formats.includes(format) ? format : acc;
}, undefined) : undefined;
return {
url: iiifInfo.imageInfo['id'],
sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) {
@@ -251,13 +262,10 @@ function generateVersion3Options(iiifInfo) {
})[0],
supports: iiifInfo.imageInfo.extraFeatures === undefined ? levelProfile.supports :
[...levelProfile.supports, ...iiifInfo.imageInfo.extraFeatures],
formats: iiifInfo.imageInfo.extraFormats === undefined ? levelProfile.formats :
[...levelProfile.formats, ...iiifInfo.imageInfo.extraFormats],
formats: formats,
qualities: iiifInfo.imageInfo.extraQualities === undefined ? levelProfile.qualities :
[...levelProfile.supports, ...iiifInfo.imageInfo.extraQualities],
maxWidth: undefined,
maxHeight: undefined,
maxArea: undefined
[...levelProfile.qualities, ...iiifInfo.imageInfo.extraQualities],
preferredFormat: preferredFormat
};
}
@@ -414,7 +422,8 @@ class IIIFInfo {
version: version,
size: [this.imageInfo.width, this.imageInfo.height],
sizes: imageOptions.sizes,
format: imageOptions.formats.includes(options.format) ? options.format : 'jpg',
format: options.format !== undefined && imageOptions.formats.includes(options.format) ? options.format :
imageOptions.preferredFormat !== undefined ? imageOptions.preferredFormat : 'jpg',
supports: imageOptions.supports,
quality: options.quality && imageOptions.qualities.includes(options.quality) ?
options.quality : imageOptions.qualities.includes('native') ? 'native' : 'default',

View File

@@ -31,6 +31,7 @@ import {get} from '../proj.js';
* @property {string} [geometryName='geometry'] Geometry name to use when creating features.
* @property {string} [layerName='layer'] Name of the feature attribute that holds the layer name.
* @property {Array<string>} [layers] Layers to read features from. If not provided, features will be read from all
* @property {string} [idProperty] Optional property that will be assigned as the feature id and removed from the properties.
* layers.
*/
@@ -84,6 +85,12 @@ class MVT extends FeatureFormat {
*/
this.layers_ = options.layers ? options.layers : null;
/**
* @private
* @type {string}
*/
this.idProperty_ = options.idProperty;
}
/**
@@ -164,8 +171,16 @@ class MVT extends FeatureFormat {
}
let feature;
const id = rawFeature.id;
const values = rawFeature.properties;
let id;
if (!this.idProperty_) {
id = rawFeature.id;
} else {
id = values[this.idProperty_];
delete values[this.idProperty_];
}
values[this.layerName_] = rawFeature.layer.name;
const flatCoordinates = [];

View File

@@ -408,7 +408,7 @@ class WFS extends XMLFeature {
node.setAttribute('count', String(options.count));
}
if (options.viewParams !== undefined) {
node.setAttribute('viewParams ', options.viewParams);
node.setAttribute('viewParams', options.viewParams);
}
filter = options.filter;
if (options.bbox) {

View File

@@ -17,6 +17,8 @@ import DragBox from './DragBox.js';
* Default is {@link module:ol/events/condition~shiftKeyOnly}.
* @property {number} [duration=200] Animation duration in milliseconds.
* @property {boolean} [out=false] Use interaction for zooming out.
* @property {number} [minArea=64] The minimum area of the box in pixel, this value is used by the parent default
* `boxEndCondition` function.
*/
@@ -42,6 +44,7 @@ class DragZoom extends DragBox {
super({
condition: condition,
className: options.className || 'ol-dragzoom',
minArea: options.minArea,
onBoxEnd: onBoxEnd
});

View File

@@ -6,6 +6,7 @@ import BaseObject from '../Object.js';
import LayerProperty from './Property.js';
import {clamp} from '../math.js';
import {assign} from '../obj.js';
import {assert} from '../asserts.js';
/**
@@ -23,6 +24,10 @@ import {assign} from '../obj.js';
* visible.
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible.
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
* visible.
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
* be visible.
*/
@@ -48,8 +53,11 @@ class BaseLayer extends BaseObject {
* @type {Object<string, *>}
*/
const properties = assign({}, options);
properties[LayerProperty.OPACITY] =
options.opacity !== undefined ? options.opacity : 1;
options.opacity !== undefined ? options.opacity : 1;
assert(typeof properties[LayerProperty.OPACITY] === 'number', 64); // Layer opacity must be a number
properties[LayerProperty.VISIBLE] =
options.visible !== undefined ? options.visible : true;
properties[LayerProperty.Z_INDEX] = options.zIndex;
@@ -57,6 +65,10 @@ class BaseLayer extends BaseObject {
options.maxResolution !== undefined ? options.maxResolution : Infinity;
properties[LayerProperty.MIN_RESOLUTION] =
options.minResolution !== undefined ? options.minResolution : 0;
properties[LayerProperty.MIN_ZOOM] =
options.minZoom !== undefined ? options.minZoom : -Infinity;
properties[LayerProperty.MAX_ZOOM] =
options.maxZoom !== undefined ? options.maxZoom : Infinity;
/**
* @type {string}
@@ -103,6 +115,8 @@ class BaseLayer extends BaseObject {
state.zIndex = this.getZIndex() || (state.managed === false ? Infinity : 0);
state.maxResolution = this.getMaxResolution();
state.minResolution = Math.max(this.getMinResolution(), 0);
state.minZoom = this.getMinZoom();
state.maxZoom = this.getMaxZoom();
this.state_ = state;
return state;
@@ -161,6 +175,26 @@ class BaseLayer extends BaseObject {
return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION));
}
/**
* Return the minimum zoom level of the layer.
* @return {number} The minimum zoom level of the layer.
* @observable
* @api
*/
getMinZoom() {
return /** @type {number} */ (this.get(LayerProperty.MIN_ZOOM));
}
/**
* Return the maximum zoom level of the layer.
* @return {number} The maximum zoom level of the layer.
* @observable
* @api
*/
getMaxZoom() {
return /** @type {number} */ (this.get(LayerProperty.MAX_ZOOM));
}
/**
* Return the opacity of the layer (between 0 and 1).
* @return {number} The opacity of the layer.
@@ -231,6 +265,30 @@ class BaseLayer extends BaseObject {
this.set(LayerProperty.MIN_RESOLUTION, minResolution);
}
/**
* Set the maximum zoom (exclusive) at which the layer is visible.
* Note that the zoom levels for layer visibility are based on the
* view zoom level, which may be different from a tile source zoom level.
* @param {number} maxZoom The maximum zoom of the layer.
* @observable
* @api
*/
setMaxZoom(maxZoom) {
this.set(LayerProperty.MAX_ZOOM, maxZoom);
}
/**
* Set the minimum zoom (inclusive) at which the layer is visible.
* Note that the zoom levels for layer visibility are based on the
* view zoom level, which may be different from a tile source zoom level.
* @param {number} minZoom The minimum zoom of the layer.
* @observable
* @api
*/
setMinZoom(minZoom) {
this.set(LayerProperty.MIN_ZOOM, minZoom);
}
/**
* Set the opacity of the layer, allowed values range from 0 to 1.
* @param {number} opacity The opacity of the layer.
@@ -238,6 +296,7 @@ class BaseLayer extends BaseObject {
* @api
*/
setOpacity(opacity) {
assert(typeof opacity === 'number', 64); // Layer opacity must be a number
this.set(LayerProperty.OPACITY, opacity);
}

View File

@@ -29,6 +29,10 @@ import SourceState from '../source/State.js';
* visible.
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible.
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
* visible.
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
* be visible.
* @property {Array<import("./Base.js").default>|import("../Collection.js").default<import("./Base.js").default>} [layers] Child layers.
*/
@@ -215,6 +219,10 @@ class LayerGroup extends BaseLayer {
layerState.maxResolution, ownLayerState.maxResolution);
layerState.minResolution = Math.max(
layerState.minResolution, ownLayerState.minResolution);
layerState.minZoom = Math.max(
layerState.minZoom, ownLayerState.minZoom);
layerState.maxZoom = Math.min(
layerState.maxZoom, ownLayerState.maxZoom);
if (ownLayerState.extent !== undefined) {
if (layerState.extent !== undefined) {
layerState.extent = getIntersection(layerState.extent, ownLayerState.extent);

View File

@@ -183,31 +183,24 @@ class Heatmap extends VectorLayer {
precision mediump float;
attribute vec2 a_position;
attribute vec2 a_texCoord;
attribute float a_rotateWithView;
attribute vec2 a_offsets;
attribute float a_opacity;
uniform mat4 u_projectionMatrix;
uniform mat4 u_offsetScaleMatrix;
uniform mat4 u_offsetRotateMatrix;
uniform float u_size;
varying vec2 v_texCoord;
varying float v_opacity;
void main(void) {
mat4 offsetMatrix = u_offsetScaleMatrix;
if (a_rotateWithView == 1.0) {
offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;
}
vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0);
vec4 offsets = u_offsetScaleMatrix * vec4(a_offsets, 0.0, 0.0);
gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets * u_size;
v_texCoord = a_texCoord;
v_opacity = a_opacity;
}`,
fragmentShader: `
precision mediump float;
uniform float u_resolution;
uniform float u_blurSlope;
varying vec2 v_texCoord;
@@ -226,10 +219,7 @@ class Heatmap extends VectorLayer {
}.bind(this),
u_blurSlope: function() {
return this.get(Property.RADIUS) / Math.max(1, this.get(Property.BLUR));
}.bind(this),
u_resolution: function(frameState) {
return frameState.viewState.resolution;
}
}.bind(this)
},
postProcesses: [
{
@@ -240,7 +230,6 @@ class Heatmap extends VectorLayer {
uniform sampler2D u_gradientTexture;
varying vec2 v_texCoord;
varying vec2 v_screenCoord;
void main() {
vec4 color = texture2D(u_image, v_texCoord);

View File

@@ -50,6 +50,8 @@ import SourceState from '../source/State.js';
* @property {number} zIndex
* @property {number} maxResolution
* @property {number} minResolution
* @property {number} minZoom
* @property {number} maxZoom
*/
/**
@@ -277,17 +279,22 @@ class Layer extends BaseLayer {
/**
* Return `true` if the layer is visible, and if the passed resolution is
* between the layer's minResolution and maxResolution. The comparison is
* inclusive for `minResolution` and exclusive for `maxResolution`.
* Return `true` if the layer is visible and if the provided view state
* has resolution and zoom levels that are in range of the layer's min/max.
* @param {State} layerState Layer state.
* @param {number} resolution Resolution.
* @return {boolean} The layer is visible at the given resolution.
* @param {import("../View.js").State} viewState View state.
* @return {boolean} The layer is visible at the given view state.
*/
export function visibleAtResolution(layerState, resolution) {
return layerState.visible && resolution >= layerState.minResolution &&
resolution < layerState.maxResolution;
export function inView(layerState, viewState) {
if (!layerState.visible) {
return false;
}
const resolution = viewState.resolution;
if (resolution < layerState.minResolution || resolution >= layerState.maxResolution) {
return false;
}
const zoom = viewState.zoom;
return zoom > layerState.minZoom && zoom <= layerState.maxZoom;
}
export default Layer;

View File

@@ -12,5 +12,7 @@ export default {
Z_INDEX: 'zIndex',
MAX_RESOLUTION: 'maxResolution',
MIN_RESOLUTION: 'minResolution',
MAX_ZOOM: 'maxZoom',
MIN_ZOOM: 'minZoom',
SOURCE: 'source'
};

View File

@@ -106,9 +106,9 @@ class VectorContext {
/**
* @param {import("../style/Text.js").default} textStyle Text style.
* @param {import("./canvas.js").DeclutterGroup=} opt_declutterGroup Declutter.
* @param {import("./canvas.js").DeclutterGroups=} opt_declutterGroups Declutter.
*/
setTextStyle(textStyle, opt_declutterGroup) {}
setTextStyle(textStyle, opt_declutterGroups) {}
}
export default VectorContext;

View File

@@ -1,7 +1,7 @@
/**
* @module ol/render/canvas
*/
import {getFontFamilies} from '../css.js';
import {getFontParameters} from '../css.js';
import {createCanvasContext2D} from '../dom.js';
import {clear} from '../obj.js';
import {create as createTransform} from '../transform.js';
@@ -18,19 +18,19 @@ import LabelCache from './canvas/LabelCache.js';
* @typedef {Object} FillStrokeState
* @property {import("../colorlike.js").ColorLike} [currentFillStyle]
* @property {import("../colorlike.js").ColorLike} [currentStrokeStyle]
* @property {string} [currentLineCap]
* @property {CanvasLineCap} [currentLineCap]
* @property {Array<number>} currentLineDash
* @property {number} [currentLineDashOffset]
* @property {string} [currentLineJoin]
* @property {CanvasLineJoin} [currentLineJoin]
* @property {number} [currentLineWidth]
* @property {number} [currentMiterLimit]
* @property {number} [lastStroke]
* @property {import("../colorlike.js").ColorLike} [fillStyle]
* @property {import("../colorlike.js").ColorLike} [strokeStyle]
* @property {string} [lineCap]
* @property {CanvasLineCap} [lineCap]
* @property {Array<number>} lineDash
* @property {number} [lineDashOffset]
* @property {string} [lineJoin]
* @property {CanvasLineJoin} [lineJoin]
* @property {number} [lineWidth]
* @property {number} [miterLimit]
*/
@@ -38,10 +38,10 @@ import LabelCache from './canvas/LabelCache.js';
/**
* @typedef {Object} StrokeState
* @property {string} lineCap
* @property {CanvasLineCap} lineCap
* @property {Array<number>} lineDash
* @property {number} lineDashOffset
* @property {string} lineJoin
* @property {CanvasLineJoin} lineJoin
* @property {number} lineWidth
* @property {number} miterLimit
* @property {import("../colorlike.js").ColorLike} strokeStyle
@@ -62,7 +62,6 @@ import LabelCache from './canvas/LabelCache.js';
* @property {Array<number>} [padding]
*/
/**
* Container for decluttered replay instructions that need to be rendered or
* omitted together, i.e. when styles render both an image and text, or for the
@@ -76,6 +75,12 @@ import LabelCache from './canvas/LabelCache.js';
*/
/**
* Declutter groups for support of multi geometries.
* @typedef {Array<DeclutterGroup>} DeclutterGroups
*/
/**
* @const
* @type {string}
@@ -92,7 +97,7 @@ export const defaultFillStyle = '#000';
/**
* @const
* @type {string}
* @type {CanvasLineCap}
*/
export const defaultLineCap = 'round';
@@ -113,7 +118,7 @@ export const defaultLineDashOffset = 0;
/**
* @const
* @type {string}
* @type {CanvasLineJoin}
*/
export const defaultLineJoin = 'round';
@@ -180,6 +185,10 @@ export const checkedFonts = {};
*/
let measureContext = null;
/**
* @type {string}
*/
let measureFont;
/**
* @type {!Object<string, number>}
@@ -192,7 +201,7 @@ export const textHeights = {};
* @param {string} fontSpec CSS font spec.
*/
export const checkFont = (function() {
const retries = 60;
const retries = 100;
const checked = checkedFonts;
const size = '32px ';
const referenceFonts = ['monospace', 'serif'];
@@ -200,32 +209,30 @@ export const checkFont = (function() {
const text = 'wmytzilWMYTZIL@#/&?$%10\uF013';
let interval, referenceWidth;
function isAvailable(font) {
/**
* @param {string} fontStyle Css font-style
* @param {string} fontWeight Css font-weight
* @param {*} fontFamily Css font-family
* @return {boolean} Font with style and weight is available
*/
function isAvailable(fontStyle, fontWeight, fontFamily) {
const context = getMeasureContext();
// Check weight ranges according to
// https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#Fallback_weights
for (let weight = 100; weight <= 700; weight += 300) {
const fontWeight = weight + ' ';
let available = true;
for (let i = 0; i < len; ++i) {
const referenceFont = referenceFonts[i];
context.font = fontWeight + size + referenceFont;
referenceWidth = context.measureText(text).width;
if (font != referenceFont) {
context.font = fontWeight + size + font + ',' + referenceFont;
const width = context.measureText(text).width;
// If width and referenceWidth are the same, then the fallback was used
// instead of the font we wanted, so the font is not available.
available = available && width != referenceWidth;
}
}
if (available) {
// Consider font available when it is available in one weight range.
//FIXME With this we miss rare corner cases, so we should consider
//FIXME checking availability for each requested weight range.
return true;
let available = true;
for (let i = 0; i < len; ++i) {
const referenceFont = referenceFonts[i];
context.font = fontStyle + ' ' + fontWeight + ' ' + size + referenceFont;
referenceWidth = context.measureText(text).width;
if (fontFamily != referenceFont) {
context.font = fontStyle + ' ' + fontWeight + ' ' + size + fontFamily + ',' + referenceFont;
const width = context.measureText(text).width;
// If width and referenceWidth are the same, then the fallback was used
// instead of the font we wanted, so the font is not available.
available = available && width != referenceWidth;
}
}
if (available) {
return true;
}
return false;
}
@@ -233,12 +240,15 @@ export const checkFont = (function() {
let done = true;
for (const font in checked) {
if (checked[font] < retries) {
if (isAvailable(font)) {
if (isAvailable.apply(this, font.split('\n'))) {
checked[font] = retries;
clear(textHeights);
// Make sure that loaded fonts are picked up by Safari
measureContext = null;
labelCache.clear();
measureFont = undefined;
if (labelCache.getCount()) {
labelCache.clear();
}
} else {
++checked[font];
done = false;
@@ -252,16 +262,18 @@ export const checkFont = (function() {
}
return function(fontSpec) {
const fontFamilies = getFontFamilies(fontSpec);
if (!fontFamilies) {
const font = getFontParameters(fontSpec);
if (!font) {
return;
}
for (let i = 0, ii = fontFamilies.length; i < ii; ++i) {
const fontFamily = fontFamilies[i];
if (!(fontFamily in checked)) {
checked[fontFamily] = retries;
if (!isAvailable(fontFamily)) {
checked[fontFamily] = 0;
const families = font.families;
for (let i = 0, ii = families.length; i < ii; ++i) {
const family = families[i];
const key = font.style + '\n' + font.weight + '\n' + family;
if (!(key in checked)) {
checked[key] = retries;
if (!isAvailable(font.style, font.weight, family)) {
checked[key] = 0;
if (interval === undefined) {
interval = setInterval(check, 32);
}
@@ -317,8 +329,8 @@ export const measureTextHeight = (function() {
*/
export function measureTextWidth(font, text) {
const measureContext = getMeasureContext();
if (font != measureContext.font) {
measureContext.font = font;
if (font != measureFont) {
measureContext.font = measureFont = font;
}
return measureContext.measureText(text).width;
}

View File

@@ -40,10 +40,10 @@ class BuilderGroup {
this.declutter_ = declutter;
/**
* @type {import("../canvas.js").DeclutterGroup}
* @type {import("../canvas.js").DeclutterGroups}
* @private
*/
this.declutterGroup_ = null;
this.declutterGroups_ = null;
/**
* @private
@@ -78,17 +78,17 @@ class BuilderGroup {
/**
* @param {boolean} group Group with previous builder.
* @return {Array<*>} The resulting instruction group.
* @return {import("../canvas").DeclutterGroups} The resulting instruction groups.
*/
addDeclutter(group) {
let declutter = null;
if (this.declutter_) {
if (group) {
declutter = this.declutterGroup_;
/** @type {number} */ (declutter[4])++;
declutter = this.declutterGroups_;
/** @type {number} */ (declutter[0][4])++;
} else {
declutter = this.declutterGroup_ = createEmpty();
declutter.push(1);
declutter = this.declutterGroups_ = [createEmpty()];
declutter[0].push(1);
}
}
return declutter;

View File

@@ -207,8 +207,8 @@ class Executor extends Disposable {
if (strokeKey) {
context.strokeStyle = strokeState.strokeStyle;
context.lineWidth = strokeWidth;
context.lineCap = /** @type {CanvasLineCap} */ (strokeState.lineCap);
context.lineJoin = /** @type {CanvasLineJoin} */ (strokeState.lineJoin);
context.lineCap = strokeState.lineCap;
context.lineJoin = strokeState.lineJoin;
context.miterLimit = strokeState.miterLimit;
if (context.setLineDash && strokeState.lineDash.length) {
context.setLineDash(strokeState.lineDash);
@@ -535,7 +535,7 @@ class Executor extends Disposable {
const ii = instructions.length; // end of instructions
let d = 0; // data index
let dd; // end of per-instruction data
let anchorX, anchorY, prevX, prevY, roundX, roundY, declutterGroup, image, text, textKey;
let anchorX, anchorY, prevX, prevY, roundX, roundY, declutterGroup, declutterGroups, image, text, textKey;
let strokeKey, fillKey;
let pendingFill = 0;
let pendingStroke = 0;
@@ -633,7 +633,7 @@ class Executor extends Disposable {
// Remaining arguments in DRAW_IMAGE are in alphabetical order
anchorX = /** @type {number} */ (instruction[4]);
anchorY = /** @type {number} */ (instruction[5]);
declutterGroup = featureCallback ? null : /** @type {import("../canvas.js").DeclutterGroup} */ (instruction[6]);
declutterGroups = featureCallback ? null : instruction[6];
let height = /** @type {number} */ (instruction[7]);
const opacity = /** @type {number} */ (instruction[8]);
const originX = /** @type {number} */ (instruction[9]);
@@ -643,7 +643,6 @@ class Executor extends Disposable {
const scale = /** @type {number} */ (instruction[13]);
let width = /** @type {number} */ (instruction[14]);
if (!image && instruction.length >= 19) {
// create label images
text = /** @type {string} */ (instruction[18]);
@@ -679,25 +678,41 @@ class Executor extends Disposable {
rotation += viewRotation;
}
let widthIndex = 0;
let declutterGroupIndex = 0;
for (; d < dd; d += 2) {
if (geometryWidths && geometryWidths[widthIndex++] < width / this.pixelRatio) {
continue;
}
if (declutterGroups) {
const index = Math.floor(declutterGroupIndex);
if (declutterGroups.length < index + 1) {
declutterGroup = createEmpty();
declutterGroup.push(declutterGroups[0][4]);
declutterGroups.push(declutterGroup);
}
declutterGroup = declutterGroups[index];
}
this.replayImage_(context,
pixelCoordinates[d], pixelCoordinates[d + 1], image, anchorX, anchorY,
declutterGroup, height, opacity, originX, originY, rotation, scale,
snapToPixel, width, padding,
backgroundFill ? /** @type {Array<*>} */ (lastFillInstruction) : null,
backgroundStroke ? /** @type {Array<*>} */ (lastStrokeInstruction) : null);
if (declutterGroup) {
if (declutterGroupIndex === Math.floor(declutterGroupIndex)) {
this.declutterItems.push(this, declutterGroup, feature);
}
declutterGroupIndex += 1 / declutterGroup[4];
}
}
this.declutterItems.push(this, declutterGroup, feature);
++i;
break;
case CanvasInstruction.DRAW_CHARS:
const begin = /** @type {number} */ (instruction[1]);
const end = /** @type {number} */ (instruction[2]);
const baseline = /** @type {number} */ (instruction[3]);
declutterGroup = featureCallback ? null : /** @type {import("../canvas.js").DeclutterGroup} */ (instruction[4]);
declutterGroup = featureCallback ? null : instruction[4];
const overflow = /** @type {number} */ (instruction[5]);
fillKey = /** @type {string} */ (instruction[6]);
const maxAngle = /** @type {number} */ (instruction[7]);

View File

@@ -16,9 +16,9 @@ class CanvasImageBuilder extends CanvasBuilder {
/**
* @private
* @type {import("../canvas.js").DeclutterGroup}
* @type {import("../canvas.js").DeclutterGroups}
*/
this.declutterGroup_ = null;
this.declutterGroups_ = null;
/**
* @private
@@ -121,14 +121,14 @@ class CanvasImageBuilder extends CanvasBuilder {
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.width_
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.width_
]);
@@ -151,14 +151,14 @@ class CanvasImageBuilder extends CanvasBuilder {
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.width_
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.width_
]);
@@ -189,7 +189,7 @@ class CanvasImageBuilder extends CanvasBuilder {
/**
* @inheritDoc
*/
setImageStyle(imageStyle, declutterGroup) {
setImageStyle(imageStyle, declutterGroups) {
const anchor = imageStyle.getAnchor();
const size = imageStyle.getSize();
const hitDetectionImage = imageStyle.getHitDetectionImage(1);
@@ -197,7 +197,7 @@ class CanvasImageBuilder extends CanvasBuilder {
const origin = imageStyle.getOrigin();
this.anchorX_ = anchor[0];
this.anchorY_ = anchor[1];
this.declutterGroup_ = /** @type {import("../canvas.js").DeclutterGroup} */ (declutterGroup);
this.declutterGroups_ = /** @type {import("../canvas.js").DeclutterGroups} */ (declutterGroups);
this.hitDetectionImage_ = hitDetectionImage;
this.image_ = image;
this.height_ = size[1];

View File

@@ -692,12 +692,12 @@ class CanvasImmediateRenderer extends VectorContext {
const context = this.context_;
const contextStrokeState = this.contextStrokeState_;
if (!contextStrokeState) {
context.lineCap = /** @type {CanvasLineCap} */ (strokeState.lineCap);
context.lineCap = strokeState.lineCap;
if (context.setLineDash) {
context.setLineDash(strokeState.lineDash);
context.lineDashOffset = strokeState.lineDashOffset;
}
context.lineJoin = /** @type {CanvasLineJoin} */ (strokeState.lineJoin);
context.lineJoin = strokeState.lineJoin;
context.lineWidth = strokeState.lineWidth;
context.miterLimit = strokeState.miterLimit;
context.strokeStyle = strokeState.strokeStyle;
@@ -712,7 +712,7 @@ class CanvasImmediateRenderer extends VectorContext {
};
} else {
if (contextStrokeState.lineCap != strokeState.lineCap) {
contextStrokeState.lineCap = context.lineCap = /** @type {CanvasLineCap} */ (strokeState.lineCap);
contextStrokeState.lineCap = context.lineCap = strokeState.lineCap;
}
if (context.setLineDash) {
if (!equals(contextStrokeState.lineDash, strokeState.lineDash)) {
@@ -724,7 +724,7 @@ class CanvasImmediateRenderer extends VectorContext {
}
}
if (contextStrokeState.lineJoin != strokeState.lineJoin) {
contextStrokeState.lineJoin = context.lineJoin = /** @type {CanvasLineJoin} */ (strokeState.lineJoin);
contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin;
}
if (contextStrokeState.lineWidth != strokeState.lineWidth) {
contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth;

View File

@@ -21,8 +21,8 @@ class LabelCache extends LRUCache {
}
clear() {
super.clear();
this.consumers = {};
super.clear();
}
/**

View File

@@ -40,9 +40,9 @@ class CanvasTextBuilder extends CanvasBuilder {
/**
* @private
* @type {import("../canvas.js").DeclutterGroup}
* @type {import("../canvas.js").DeclutterGroups}
*/
this.declutterGroup_;
this.declutterGroups_;
/**
* @private
@@ -201,7 +201,10 @@ class CanvasTextBuilder extends CanvasBuilder {
}
end = this.coordinates.length;
flatOffset = ends[o];
this.drawChars_(begin, end, this.declutterGroup_);
const declutterGroup = this.declutterGroups_ ?
(o === 0 ? this.declutterGroups_[0] : [].concat(this.declutterGroups_[0])) :
null;
this.drawChars_(begin, end, declutterGroup);
begin = end;
}
this.endGeometry(feature);
@@ -274,7 +277,7 @@ class CanvasTextBuilder extends CanvasBuilder {
// render time.
const pixelRatio = this.pixelRatio;
this.instructions.push([CanvasInstruction.DRAW_IMAGE, begin, end,
null, NaN, NaN, this.declutterGroup_, NaN, 1, 0, 0,
null, NaN, NaN, this.declutterGroups_, NaN, 1, 0, 0,
this.textRotateWithView_, this.textRotation_, 1, NaN,
textState.padding == defaultPadding ?
defaultPadding : textState.padding.map(function(p) {
@@ -285,7 +288,7 @@ class CanvasTextBuilder extends CanvasBuilder {
this.textOffsetX_, this.textOffsetY_, geometryWidths
]);
this.hitDetectionInstructions.push([CanvasInstruction.DRAW_IMAGE, begin, end,
null, NaN, NaN, this.declutterGroup_, NaN, 1, 0, 0,
null, NaN, NaN, this.declutterGroups_, NaN, 1, 0, 0,
this.textRotateWithView_, this.textRotation_, 1 / this.pixelRatio, NaN,
textState.padding,
!!textState.backgroundFill, !!textState.backgroundStroke,
@@ -379,12 +382,12 @@ class CanvasTextBuilder extends CanvasBuilder {
/**
* @inheritDoc
*/
setTextStyle(textStyle, declutterGroup) {
setTextStyle(textStyle, declutterGroups) {
let textState, fillState, strokeState;
if (!textStyle) {
this.text_ = '';
} else {
this.declutterGroup_ = /** @type {import("../canvas.js").DeclutterGroup} */ (declutterGroup);
this.declutterGroups_ = /** @type {import("../canvas.js").DeclutterGroups} */ (declutterGroups);
const textFillStyle = textStyle.getFill();
if (!textFillStyle) {

View File

@@ -2,7 +2,7 @@
* @module ol/renderer/Composite
*/
import {CLASS_UNSELECTABLE} from '../css.js';
import {visibleAtResolution} from '../layer/Layer.js';
import {inView} from '../layer/Layer.js';
import RenderEvent from '../render/Event.js';
import RenderEventType from '../render/EventType.js';
import MapRenderer from './Map.js';
@@ -95,7 +95,7 @@ class CompositeMapRenderer extends MapRenderer {
const layerStatesArray = frameState.layerStatesArray.sort(function(a, b) {
return a.zIndex - b.zIndex;
});
const viewResolution = frameState.viewState.resolution;
const viewState = frameState.viewState;
this.children_.length = 0;
let hasOverlay = false;
@@ -104,7 +104,7 @@ class CompositeMapRenderer extends MapRenderer {
const layerState = layerStatesArray[i];
hasOverlay = hasOverlay || layerState.hasOverlay;
frameState.layerIndex = i;
if (!visibleAtResolution(layerState, viewResolution) ||
if (!inView(layerState, viewState) ||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
continue;
}
@@ -142,7 +142,6 @@ class CompositeMapRenderer extends MapRenderer {
*/
forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, layerFilter) {
const viewState = frameState.viewState;
const viewResolution = viewState.resolution;
const layerStates = frameState.layerStatesArray;
const numLayers = layerStates.length;
@@ -150,7 +149,7 @@ class CompositeMapRenderer extends MapRenderer {
for (let i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i];
const layer = layerState.layer;
if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter(layer)) {
if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter(layer)) {
const layerRenderer = layer.getRenderer();
const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance);
if (data) {

View File

@@ -132,15 +132,6 @@ class LayerRenderer extends Observable {
}
}
/**
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
* @return {boolean} Is there a feature at the given coordinate?
*/
hasFeatureAtCoordinate(coordinate, frameState) {
return false;
}
/**
* Load the image if not already loaded, and register the image change
* listener if needed.

View File

@@ -5,7 +5,7 @@ import {abstract, getUid} from '../util.js';
import Disposable from '../Disposable.js';
import {getWidth} from '../extent.js';
import {TRUE} from '../functions.js';
import {visibleAtResolution} from '../layer/Layer.js';
import {inView} from '../layer/Layer.js';
import {shared as iconImageCache} from '../style/IconImageCache.js';
import {compose as composeTransform, makeInverse} from '../transform.js';
import {renderDeclutterItems} from '../render.js';
@@ -87,7 +87,6 @@ class MapRenderer extends Disposable {
) {
let result;
const viewState = frameState.viewState;
const viewResolution = viewState.resolution;
/**
* @param {boolean} managed Managed layer.
@@ -126,7 +125,7 @@ class MapRenderer extends Disposable {
for (i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i];
const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer);
if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) {
if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter.call(thisArg2, layer)) {
const layerRenderer = layer.getRenderer();
const source = layer.getSource();
if (layerRenderer && source) {

View File

@@ -63,12 +63,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
* @type {import("../../TileRange.js").default}
*/
this.tmpTileRange_ = new TileRange(0, 0, 0, 0);
/**
* @protected
* @type {number}
*/
this.zDirection = 0;
}
/**
@@ -151,8 +145,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const tileSource = tileLayer.getSource();
const sourceRevision = tileSource.getRevision();
const tileGrid = tileSource.getTileGridForProjection(projection);
const zDirection = tileSource.zDirection === undefined ? this.zDirection : tileSource.zDirection;
const z = tileGrid.getZForResolution(viewResolution, zDirection);
const z = tileGrid.getZForResolution(viewResolution, tileSource.zDirection);
const tileResolution = tileGrid.getResolution(z);
let extent = frameState.extent;
@@ -384,7 +377,8 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
return;
}
const uid = getUid(this);
const alpha = opacity * (transition ? tile.getAlpha(uid, frameState.time) : 1);
const tileAlpha = transition ? tile.getAlpha(uid, frameState.time) : 1;
const alpha = opacity * tileAlpha;
const alphaChanged = alpha !== this.context.globalAlpha;
if (alphaChanged) {
this.context.save();
@@ -396,7 +390,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
if (alphaChanged) {
this.context.restore();
}
if (alpha !== 1) {
if (tileAlpha !== 1) {
frameState.animate = true;
} else if (transition) {
tile.endTransition(uid);

View File

@@ -299,6 +299,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
vectorSource.loadFeatures(extent, resolution, projection);
const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio);
/**
* @param {import("../../Feature.js").default} feature Feature.
* @this {CanvasVectorLayerRenderer}
@@ -310,11 +312,11 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
styles = styleFunction(feature, resolution);
}
if (styles) {
const dirty = this.renderFeature(
feature, resolution, pixelRatio, styles, replayGroup);
const dirty = this.renderFeature(feature, squaredTolerance, styles, replayGroup);
this.dirty_ = this.dirty_ || dirty;
}
}.bind(this);
if (vectorLayerRenderOrder) {
/** @type {Array<import("../../Feature.js").default>} */
const features = [];
@@ -350,13 +352,12 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
/**
* @param {import("../../Feature.js").default} feature Feature.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @param {number} squaredTolerance Squared render tolerance.
* @param {import("../../style/Style.js").default|Array<import("../../style/Style.js").default>} styles The style or array of styles.
* @param {import("../../render/canvas/BuilderGroup.js").default} builderGroup Builder group.
* @return {boolean} `true` if an image is loading.
*/
renderFeature(feature, resolution, pixelRatio, styles, builderGroup) {
renderFeature(feature, squaredTolerance, styles, builderGroup) {
if (!styles) {
return false;
}
@@ -364,14 +365,12 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
if (Array.isArray(styles)) {
for (let i = 0, ii = styles.length; i < ii; ++i) {
loading = renderFeature(
builderGroup, feature, styles[i],
getSquaredRenderTolerance(resolution, pixelRatio),
builderGroup, feature, styles[i], squaredTolerance,
this.handleStyleImageChange_, this) || loading;
}
} else {
loading = renderFeature(
builderGroup, feature, styles,
getSquaredRenderTolerance(resolution, pixelRatio),
builderGroup, feature, styles, squaredTolerance,
this.handleStyleImageChange_, this);
}
return loading;

View File

@@ -24,6 +24,7 @@ import {
makeInverse
} from '../../transform.js';
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import {clear} from '../../obj.js';
/**
@@ -112,8 +113,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
this.tmpTransform_ = createTransform();
// Use nearest lower resolution.
this.zDirection = 1;
}
/**
@@ -343,11 +342,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
let i, ii;
for (i = 0, ii = renderedTiles.length; i < ii; ++i) {
const tile = renderedTiles[i];
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
const tileContainsCoordinate = containsCoordinate(tileExtent, coordinate);
if (!declutter) {
// When not decluttering, we only need to consider the tile that contains the given
// coordinate, because each feature will be rendered for each tile that contains it.
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
if (!containsCoordinate(tileExtent, coordinate)) {
if (!tileContainsCoordinate) {
continue;
}
}
@@ -360,13 +361,15 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @return {?} Callback result.
*/
function(feature) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
if (tileContainsCoordinate || (declutteredFeatures && declutteredFeatures.indexOf(feature) !== -1)) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
}
}, layer.getDeclutter() ? declutteredFeatures : null);
}
@@ -378,6 +381,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc
*/
handleFontsChanged() {
clear(this.renderTileImageQueue_);
const layer = this.getLayer();
if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) {
layer.changed();

View File

@@ -81,6 +81,7 @@ class WebGLLayerRenderer extends LayerRenderer {
getShaderCompileErrors() {
return this.helper.getShaderCompileErrors();
}
}
@@ -231,11 +232,47 @@ export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuf
* @private
* @return {ImageData} Image data.
*/
export function getBlankTexture() {
export function getBlankImageData() {
const canvas = document.createElement('canvas');
const image = canvas.getContext('2d').createImageData(1, 1);
image.data[0] = image.data[1] = image.data[2] = image.data[3] = 255;
return image;
}
/**
* Generates a color array based on a numerical id
* Note: the range for each component is 0 to 1 with 256 steps
* @param {number} id Id
* @param {Array<number>} [opt_array] Reusable array
* @return {Array<number>} Color array containing the encoded id
*/
export function colorEncodeId(id, opt_array) {
const array = opt_array || [];
const radix = 256;
const divide = radix - 1;
array[0] = Math.floor(id / radix / radix / radix) / divide;
array[1] = (Math.floor(id / radix / radix) % radix) / divide;
array[2] = (Math.floor(id / radix) % radix) / divide;
array[3] = (id % radix) / divide;
return array;
}
/**
* Reads an id from a color-encoded array
* Note: the expected range for each component is 0 to 1 with 256 steps.
* @param {Array<number>} color Color array containing the encoded id
* @return {number} Decoded id
*/
export function colorDecodeId(color) {
let id = 0;
const radix = 256;
const mult = radix - 1;
id += Math.round(color[0] * radix * radix * radix * mult);
id += Math.round(color[1] * radix * radix * mult);
id += Math.round(color[2] * radix * mult);
id += Math.round(color[3] * mult);
return id;
}
export default WebGLLayerRenderer;

View File

@@ -6,7 +6,9 @@ import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../web
import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper.js';
import GeometryType from '../../geom/GeometryType.js';
import WebGLLayerRenderer, {
getBlankTexture,
colorDecodeId,
colorEncodeId,
getBlankImageData,
POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE, WebGLWorkerMessageType,
writePointFeatureInstructions
} from './Layer.js';
@@ -19,6 +21,8 @@ import {
apply as applyTransform
} from '../../transform.js';
import {create as createWebGLWorker} from '../../worker/webgl.js';
import {getUid} from '../../util.js';
import WebGLRenderTarget from '../../webgl/RenderTarget.js';
const VERTEX_SHADER = `
precision mediump float;
@@ -68,6 +72,26 @@ const FRAGMENT_SHADER = `
gl_FragColor.rgb *= gl_FragColor.a;
}`;
const HIT_FRAGMENT_SHADER = `
precision mediump float;
uniform sampler2D u_texture;
varying vec2 v_texCoord;
varying float v_opacity;
varying vec4 v_color;
void main(void) {
if (v_opacity == 0.0) {
discard;
}
vec4 textureColor = texture2D(u_texture, v_texCoord);
if (textureColor.a < 0.1) {
discard;
}
gl_FragColor = v_color;
}`;
/**
* @typedef {Object} Options
* @property {function(import("../../Feature").default):number} [sizeCallback] Will be called on every feature in the
@@ -87,7 +111,7 @@ const FRAGMENT_SHADER = `
* source to compute the opacity of the quad on screen (from 0 to 1). This is only done on source change.
* Note: this is multiplied with the color of the point which can also have an alpha value < 1.
* @property {function(import("../../Feature").default):boolean} [rotateWithViewCallback] Will be called on every feature in the
* source to compute whether the quad on screen must stay upwards (`false`) or follow the view rotation (`true`).
* source to compute whether the quad on screen must stay upwards (`false`) or follow the view rotation (`true`). Default is `false`.
* This is only done on source change.
* @property {HTMLCanvasElement|HTMLImageElement|ImageData} [texture] Texture to use on points. `texCoordCallback` and `sizeCallback`
* must be defined for this to have any effect.
@@ -200,7 +224,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const options = opt_options || {};
const uniforms = options.uniforms || {};
uniforms.u_texture = options.texture || getBlankTexture();
uniforms.u_texture = options.texture || getBlankImageData();
const projectionMatrixTransform = createTransform();
uniforms[DefaultUniform.PROJECTION_MATRIX] = projectionMatrixTransform;
@@ -212,14 +236,17 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.sourceRevision_ = -1;
this.verticesBuffer_ = new WebGLArrayBuffer(ARRAY_BUFFER, DYNAMIC_DRAW);
this.hitVerticesBuffer_ = new WebGLArrayBuffer(ARRAY_BUFFER, DYNAMIC_DRAW);
this.indicesBuffer_ = new WebGLArrayBuffer(ELEMENT_ARRAY_BUFFER, DYNAMIC_DRAW);
this.program_ = this.helper.getProgram(
options.fragmentShader || FRAGMENT_SHADER,
options.vertexShader || VERTEX_SHADER
);
this.helper.useProgram(this.program_);
this.hitProgram_ = this.helper.getProgram(
HIT_FRAGMENT_SHADER,
options.vertexShader || VERTEX_SHADER
);
this.sizeCallback_ = options.sizeCallback || function() {
return 1;
@@ -274,32 +301,45 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
*/
this.renderInstructions_ = new Float32Array(0);
/**
* These instructions are used for hit detection
* @type {Float32Array}
* @private
*/
this.hitRenderInstructions_ = new Float32Array(0);
/**
* @type {WebGLRenderTarget}
* @private
*/
this.hitRenderTarget_ = new WebGLRenderTarget(this.helper);
this.worker_ = createWebGLWorker();
this.worker_.addEventListener('message', function(event) {
const received = event.data;
if (received.type === WebGLWorkerMessageType.GENERATE_BUFFERS) {
const projectionTransform = received.projectionTransform;
this.verticesBuffer_.fromArrayBuffer(received.vertexBuffer);
if (received.hitDetection) {
this.hitVerticesBuffer_.fromArrayBuffer(received.vertexBuffer);
this.helper.flushBufferData(this.hitVerticesBuffer_);
} else {
this.verticesBuffer_.fromArrayBuffer(received.vertexBuffer);
this.helper.flushBufferData(this.verticesBuffer_);
}
this.indicesBuffer_.fromArrayBuffer(received.indexBuffer);
this.helper.flushBufferData(this.verticesBuffer_);
this.helper.flushBufferData(this.indicesBuffer_);
// saves the projection transform for the current frame state
this.renderTransform_ = projectionTransform;
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
this.renderInstructions_ = new Float32Array(event.data.renderInstructions);
if (received.hitDetection) {
this.hitRenderInstructions_ = new Float32Array(event.data.renderInstructions);
} else {
this.renderInstructions_ = new Float32Array(event.data.renderInstructions);
}
}
}.bind(this));
}
/**
* @inheritDoc
*/
disposeInternal() {
super.disposeInternal();
}
/**
* @inheritDoc
*/
@@ -315,6 +355,9 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
canvas.style.opacity = opacity;
}
this.renderHitDetection(frameState);
this.hitRenderTarget_.clearCachedData();
return canvas;
}
@@ -332,7 +375,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const sourceChanged = this.sourceRevision_ < vectorSource.getRevision();
if (sourceChanged) {
this.sourceRevision_ = vectorSource.getRevision();
this.geojsonFeatureCache_ = {};
const projection = viewState.projection;
const resolution = viewState.resolution;
@@ -350,6 +392,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.helper.makeProjectionTransform(frameState, this.currentTransform_);
multiplyTransform(this.currentTransform_, this.invertRenderTransform_);
this.helper.useProgram(this.program_);
this.helper.prepareDraw(frameState);
// write new data
@@ -385,11 +428,16 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
if (!this.renderInstructions_ || this.renderInstructions_.length !== totalInstructionsCount) {
this.renderInstructions_ = new Float32Array(totalInstructionsCount);
}
if (!this.hitRenderInstructions_ || this.hitRenderInstructions_.length !== totalInstructionsCount) {
this.hitRenderInstructions_ = new Float32Array(totalInstructionsCount);
}
// loop on features to fill the buffer
let feature;
const tmpCoords = [];
const tmpColor = [];
let elementIndex = 0;
let u0, v0, u1, v1, size, opacity, rotateWithView, color;
for (let i = 0; i < features.length; i++) {
feature = features[i];
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
@@ -400,19 +448,45 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
tmpCoords[1] = this.coordCallback_(feature, 1);
applyTransform(projectionTransform, tmpCoords);
elementIndex = writePointFeatureInstructions(
u0 = this.texCoordCallback_(feature, 0);
v0 = this.texCoordCallback_(feature, 1);
u1 = this.texCoordCallback_(feature, 2);
v1 = this.texCoordCallback_(feature, 3);
size = this.sizeCallback_(feature);
opacity = this.opacityCallback_(feature);
rotateWithView = this.rotateWithViewCallback_(feature);
color = this.colorCallback_(feature, this.colorArray_);
writePointFeatureInstructions(
this.renderInstructions_,
elementIndex,
tmpCoords[0],
tmpCoords[1],
this.texCoordCallback_(feature, 0),
this.texCoordCallback_(feature, 1),
this.texCoordCallback_(feature, 2),
this.texCoordCallback_(feature, 3),
this.sizeCallback_(feature),
this.opacityCallback_(feature),
this.rotateWithViewCallback_(feature),
this.colorCallback_(feature, this.colorArray_)
u0,
v0,
u1,
v1,
size,
opacity,
rotateWithView,
color
);
// for hit detection, the feature uid is saved in the opacity value
// and the index of the opacity value is encoded in the color values
elementIndex = writePointFeatureInstructions(
this.hitRenderInstructions_,
elementIndex,
tmpCoords[0],
tmpCoords[1],
u0,
v0,
u1,
v1,
size,
opacity > 0 ? Number(getUid(feature)) : 0,
rotateWithView,
colorEncodeId(elementIndex + 7, tmpColor)
);
}
@@ -423,12 +497,73 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
};
// additional properties will be sent back as-is by the worker
message['projectionTransform'] = projectionTransform;
this.worker_.postMessage(message, [this.renderInstructions_.buffer]);
this.renderInstructions_ = null;
/** @type import('./Layer').WebGLWorkerGenerateBuffersMessage */
const hitMessage = {
type: WebGLWorkerMessageType.GENERATE_BUFFERS,
renderInstructions: this.hitRenderInstructions_.buffer
};
hitMessage['projectionTransform'] = projectionTransform;
hitMessage['hitDetection'] = true;
this.worker_.postMessage(hitMessage, [this.hitRenderInstructions_.buffer]);
this.hitRenderInstructions_ = null;
}
/**
* @inheritDoc
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
if (!this.hitRenderInstructions_) {
return;
}
const pixel = applyTransform(frameState.coordinateToPixelTransform, coordinate.slice());
const data = this.hitRenderTarget_.readPixel(pixel[0], pixel[1]);
const color = [
data[0] / 255,
data[1] / 255,
data[2] / 255,
data[3] / 255
];
const index = colorDecodeId(color);
const opacity = this.hitRenderInstructions_[index];
const uid = Math.floor(opacity).toString();
const source = this.getLayer().getSource();
const feature = source.getFeatureByUid(uid);
if (feature) {
return callback(feature, this.getLayer());
}
}
/**
* Render the hit detection data to the corresponding render target
* @param {import("../../PluggableMap.js").FrameState} frameState current frame state
*/
renderHitDetection(frameState) {
this.hitRenderTarget_.setSize(frameState.size);
this.helper.useProgram(this.hitProgram_);
this.helper.prepareDrawToRenderTarget(frameState, this.hitRenderTarget_, true);
this.helper.bindBuffer(this.hitVerticesBuffer_);
this.helper.bindBuffer(this.indicesBuffer_);
const stride = POINT_VERTEX_STRIDE;
const bytesPerFloat = Float32Array.BYTES_PER_ELEMENT;
this.helper.enableAttributeArray(DefaultAttrib.POSITION, 2, FLOAT, bytesPerFloat * stride, 0);
this.helper.enableAttributeArray(DefaultAttrib.OFFSETS, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 2);
this.helper.enableAttributeArray(DefaultAttrib.TEX_COORD, 2, FLOAT, bytesPerFloat * stride, bytesPerFloat * 4);
this.helper.enableAttributeArray(DefaultAttrib.OPACITY, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 6);
this.helper.enableAttributeArray(DefaultAttrib.ROTATE_WITH_VIEW, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 7);
this.helper.enableAttributeArray(DefaultAttrib.COLOR, 4, FLOAT, bytesPerFloat * stride, bytesPerFloat * 8);
const renderCount = this.indicesBuffer_.getArray() ? this.indicesBuffer_.getArray().length : 0;
this.helper.drawElements(0, renderCount);
}
}
export default WebGLPointsLayerRenderer;

View File

@@ -51,10 +51,7 @@ class ReprojImage extends ImageBase {
const sourceExtent = triangulation.calculateSourceExtent();
const sourceImage = getImageFunction(sourceExtent, sourceResolution, pixelRatio);
let state = ImageState.LOADED;
if (sourceImage) {
state = ImageState.IDLE;
}
const state = sourceImage ? ImageState.IDLE : ImageState.EMPTY;
const sourcePixelRatio = sourceImage ? sourceImage.getPixelRatio() : 1;
super(targetExtent, targetResolution, sourcePixelRatio, state);

View File

@@ -269,7 +269,8 @@ class BingMaps extends TileImage {
const attributions = [];
const viewState = frameState.viewState;
const tileGrid = this.getTileGrid();
const tileCoord = tileGrid.getTileCoordForCoordAndResolution(viewState.center, viewState.resolution);
const z = tileGrid.getZForResolution(viewState.resolution, this.zDirection);
const tileCoord = tileGrid.getTileCoordForCoordAndZ(viewState.center, z);
const zoom = tileCoord[0];
resource.imageryProviders.map(function(imageryProvider) {
let intersecting = false;

View File

@@ -40,8 +40,8 @@ import TileImage from './TileImage.js';
* @property {string} [url] Base URL of the IIIF Image service.
* This should be the same as the IIIF Image ID.
* @property {Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
* @property {number} [zDirection] Indicate which resolution should be used
* by a renderer if the views resolution does not match any resolution of the tile source.
* @property {number} [zDirection=0] Indicate which resolution should be used
* by a renderer if the view resolution does not match any resolution of the tile source.
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
* will be used. If -1, the nearest higher resolution will be used.
*/
@@ -65,7 +65,7 @@ class IIIF extends TileImage {
constructor(opt_options) {
/**
* @type {Partial<Options>} options
* @type {Partial<Options>}
*/
const options = opt_options || {};

View File

@@ -191,6 +191,45 @@ class ImageWMS extends ImageSource {
1, sourceProjectionObj || projectionObj, baseParams);
}
/**
* Return the GetLegendGraphic URL, optionally optimized for the passed
* resolution and possibly including any passed specific parameters. Returns
* `undefined` if the GetLegendGraphic URL cannot be constructed.
*
* @param {number} [resolution] Resolution. If set to undefined, `SCALE`
* will not be calculated and included in URL.
* @param {Object} [params] GetLegendGraphic params. Default `FORMAT` is
* `image/png`. `VERSION` should not be specified here.
* @return {string|undefined} GetLegendGraphic URL.
* @api
*/
getGetLegendGraphicUrl(resolution, params) {
const layers = this.params_.LAYERS;
const isSingleLayer = !Array.isArray(layers) || this.params_['LAYERS'].length === 1;
if (this.url_ === undefined || !isSingleLayer) {
return undefined;
}
const baseParams = {
'SERVICE': 'WMS',
'VERSION': DEFAULT_WMS_VERSION,
'REQUEST': 'GetLegendGraphic',
'FORMAT': 'image/png',
'LAYER': layers
};
if (resolution !== undefined) {
const mpu = this.getProjection() ? this.getProjection().getMetersPerUnit() : 1;
const dpi = 25.4 / 0.28;
const inchesPerMeter = 39.37;
baseParams['SCALE'] = resolution * mpu * inchesPerMeter * dpi;
}
assign(baseParams, params);
return appendParams(/** @type {string} */ (this.url_), baseParams);
}
/**
* Get the user-provided params, i.e. those passed to the constructor through
* the "params" option, and possibly updated using the updateParams method.

View File

@@ -24,6 +24,7 @@ import {wrapX, getForProjection as getTileGridForProjection} from '../tilegrid.j
* @property {boolean} [wrapX=true]
* @property {number} [transition]
* @property {string} [key]
* @property {number} [zDirection=0]
*/
@@ -110,9 +111,9 @@ class TileSource extends Source {
* by a renderer if the views resolution does not match any resolution of the tile source.
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
* will be used. If -1, the nearest higher resolution will be used.
* @type {number=}
* @type {number}
*/
this.zDirection;
this.zDirection = options.zDirection ? options.zDirection : 0;
}
/**

View File

@@ -80,6 +80,11 @@ class LabeledTile extends Tile {
* @property {import("../proj.js").ProjectionLike} [projection='EPSG:3857'] Optional projection.
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid.
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
* @property {number} [zDirection=0] Set to `1` when debugging `VectorTile` sources with
* a default configuration. Indicates which resolution should be used by a renderer if
* the view resolution does not match any resolution of the tile source. If 0, the nearest
* resolution will be used. If 1, the nearest lower resolution will be used. If -1, the
* nearest higher resolution will be used.
*/
@@ -106,7 +111,8 @@ class TileDebug extends XYZ {
opaque: false,
projection: options.projection,
tileGrid: options.tileGrid,
wrapX: options.wrapX !== undefined ? options.wrapX : true
wrapX: options.wrapX !== undefined ? options.wrapX : true,
zDirection: options.zDirection
});
}

View File

@@ -52,6 +52,10 @@ import {getForProjection as getTileGridForProjection} from '../tilegrid.js';
* @property {number} [transition] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {string} [key] Optional tile key for proper cache fetching
* @property {number} [zDirection=0] Indicate which resolution should be used
* by a renderer if the view resolution does not match any resolution of the tile source.
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
* will be used. If -1, the nearest higher resolution will be used.
*/
@@ -84,7 +88,8 @@ class TileImage extends UrlTile {
wrapX: options.wrapX,
transition: options.transition,
key: options.key,
attributionsCollapsible: options.attributionsCollapsible
attributionsCollapsible: options.attributionsCollapsible,
zDirection: options.zDirection
});
/**

View File

@@ -168,7 +168,8 @@ class TileWMS extends TileImage {
tileGrid = this.getTileGridForProjection(projectionObj);
}
const tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, resolution);
const z = tileGrid.getZForResolution(resolution, this.zDirection);
const tileCoord = tileGrid.getTileCoordForCoordAndZ(coordinate, z);
if (tileGrid.getResolutions().length <= tileCoord[0]) {
return undefined;
@@ -211,6 +212,45 @@ class TileWMS extends TileImage {
1, sourceProjectionObj || projectionObj, baseParams);
}
/**
* Return the GetLegendGraphic URL, optionally optimized for the passed
* resolution and possibly including any passed specific parameters. Returns
* `undefined` if the GetLegendGraphic URL cannot be constructed.
*
* @param {number} [resolution] Resolution. If set to undefined, `SCALE`
* will not be calculated and included in URL.
* @param {Object} [params] GetLegendGraphic params. Default `FORMAT` is
* `image/png`. `VERSION` should not be specified here.
* @return {string|undefined} GetLegendGraphic URL.
* @api
*/
getGetLegendGraphicUrl(resolution, params) {
const layers = this.params_.LAYERS;
const isSingleLayer = !Array.isArray(layers) || this.params_['LAYERS'].length === 1;
if (this.urls[0] === undefined || !isSingleLayer) {
return undefined;
}
const baseParams = {
'SERVICE': 'WMS',
'VERSION': DEFAULT_WMS_VERSION,
'REQUEST': 'GetLegendGraphic',
'FORMAT': 'image/png',
'LAYER': layers
};
if (resolution !== undefined) {
const mpu = this.getProjection() ? this.getProjection().getMetersPerUnit() : 1;
const dpi = 25.4 / 0.28;
const inchesPerMeter = 39.37;
baseParams['SCALE'] = resolution * mpu * inchesPerMeter * dpi;
}
assign(baseParams, params);
return appendParams(/** @type {string} */ (this.urls[0]), baseParams);
}
/**
* @inheritDoc
*/

View File

@@ -385,8 +385,8 @@ class UTFGrid extends TileSource {
forDataAtCoordinateAndResolution(
coordinate, resolution, callback, opt_request) {
if (this.tileGrid) {
const tileCoord = this.tileGrid.getTileCoordForCoordAndResolution(
coordinate, resolution);
const z = this.tileGrid.getZForResolution(resolution, this.zDirection);
const tileCoord = this.tileGrid.getTileCoordForCoordAndZ(coordinate, z);
const tile = /** @type {!CustomTile} */(this.getTile(
tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection()));
tile.forDataAtCoordinate(coordinate, callback, opt_request);

View File

@@ -25,6 +25,7 @@ import {getKeyZXY} from '../tilecoord.js';
* @property {boolean} [wrapX=true]
* @property {number} [transition]
* @property {string} [key]
* @property {number} [zDirection=0]
*/
@@ -51,7 +52,8 @@ class UrlTile extends TileSource {
wrapX: options.wrapX,
transition: options.transition,
key: options.key,
attributionsCollapsible: options.attributionsCollapsible
attributionsCollapsible: options.attributionsCollapsible,
zDirection: options.zDirection
});
/**

View File

@@ -155,7 +155,7 @@ export class VectorSourceEvent extends Event {
* by this source are suitable for editing. See {@link module:ol/source/VectorTile~VectorTile} for
* vector data that is optimized for rendering.
*
* @fires VectorSourceEvent<Geometry>
* @fires VectorSourceEvent
* @api
* @template {import("../geom/Geometry.js").default} Geometry
*/
@@ -241,11 +241,11 @@ class VectorSource extends Source {
this.idIndex_ = {};
/**
* A lookup of features without id (keyed by getUid(feature)).
* A lookup of features by uid (using getUid(feature)).
* @private
* @type {!Object<string, import("../Feature.js").default<Geometry>>}
*/
this.undefIdIndex_ = {};
this.uidIndex_ = {};
/**
* @private
@@ -359,10 +359,11 @@ class VectorSource extends Source {
} else {
valid = false;
}
} else {
assert(!(featureKey in this.undefIdIndex_),
}
if (valid) {
assert(!(featureKey in this.uidIndex_),
30); // The passed `feature` was already added to the source
this.undefIdIndex_[featureKey] = feature;
this.uidIndex_[featureKey] = feature;
}
return valid;
}
@@ -489,7 +490,7 @@ class VectorSource extends Source {
if (!this.featuresCollection_) {
this.featureChangeKeys_ = {};
this.idIndex_ = {};
this.undefIdIndex_ = {};
this.uidIndex_ = {};
}
} else {
if (this.featuresRtree_) {
@@ -770,6 +771,18 @@ class VectorSource extends Source {
}
/**
* Get a feature by its internal unique identifier (using `getUid`).
*
* @param {string} uid Feature identifier.
* @return {import("../Feature.js").default<Geometry>} The feature (or `null` if not found).
*/
getFeatureByUid(uid) {
const feature = this.uidIndex_[uid];
return feature !== undefined ? feature : null;
}
/**
* Get the format associated with this source.
*
@@ -831,20 +844,13 @@ class VectorSource extends Source {
const id = feature.getId();
if (id !== undefined) {
const sid = id.toString();
if (featureKey in this.undefIdIndex_) {
delete this.undefIdIndex_[featureKey];
if (this.idIndex_[sid] !== feature) {
this.removeFromIdIndex_(feature);
this.idIndex_[sid] = feature;
} else {
if (this.idIndex_[sid] !== feature) {
this.removeFromIdIndex_(feature);
this.idIndex_[sid] = feature;
}
}
} else {
if (!(featureKey in this.undefIdIndex_)) {
this.removeFromIdIndex_(feature);
this.undefIdIndex_[featureKey] = feature;
}
this.removeFromIdIndex_(feature);
this.uidIndex_[featureKey] = feature;
}
this.changed();
this.dispatchEvent(new VectorSourceEvent(
@@ -862,7 +868,7 @@ class VectorSource extends Source {
if (id !== undefined) {
return id in this.idIndex_;
} else {
return getUid(feature) in this.undefIdIndex_;
return getUid(feature) in this.uidIndex_;
}
}
@@ -964,9 +970,8 @@ class VectorSource extends Source {
const id = feature.getId();
if (id !== undefined) {
delete this.idIndex_[id.toString()];
} else {
delete this.undefIdIndex_[featureKey];
}
delete this.uidIndex_[featureKey];
this.dispatchEvent(new VectorSourceEvent(
VectorEventType.REMOVEFEATURE, feature));
}

View File

@@ -9,7 +9,7 @@ import {toSize} from '../size.js';
import UrlTile from './UrlTile.js';
import {getKeyZXY, getKey} from '../tilecoord.js';
import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid.js';
import {buffer as bufferExtent, getIntersection} from '../extent.js';
import {buffer as bufferExtent, getIntersection, intersects} from '../extent.js';
import {listen, unlistenByKey} from '../events.js';
import EventType from '../events/EventType.js';
import {loadFeaturesXhr} from '../featureloader.js';
@@ -64,6 +64,10 @@ import {equals} from '../array.js';
* When set to `false`, only one world
* will be rendered. When set to `true`, tiles will be wrapped horizontally to
* render multiple worlds.
* @property {number} [zDirection=1] Indicate which resolution should be used
* by a renderer if the view resolution does not match any resolution of the tile source.
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
* will be used. If -1, the nearest higher resolution will be used.
*/
@@ -109,7 +113,8 @@ class VectorTile extends UrlTile {
url: options.url,
urls: options.urls,
wrapX: options.wrapX === undefined ? true : options.wrapX,
transition: options.transition
transition: options.transition,
zDirection: options.zDirection === undefined ? 1 : options.zDirection
});
/**
@@ -127,7 +132,7 @@ class VectorTile extends UrlTile {
* @private
* @type {Object<string, import("../VectorTile.js").default>}
*/
this.sourceTiles_ = {};
this.sourceTileByCoordKey_ = {};
/**
* @private
@@ -168,7 +173,7 @@ class VectorTile extends UrlTile {
*/
clear() {
this.tileCache.clear();
this.sourceTiles_ = {};
this.sourceTileByCoordKey_ = {};
this.sourceTilesByTileKey_ = {};
}
@@ -179,34 +184,40 @@ class VectorTile extends UrlTile {
* @return {Array<import("../VectorTile").default>} Tile keys.
*/
getSourceTiles(pixelRatio, projection, tile) {
const sourceTiles = [];
const urlTileCoord = tile.wrappedTileCoord;
if (urlTileCoord) {
const tileGrid = this.getTileGridForProjection(projection);
const extent = tileGrid.getTileCoordExtent(urlTileCoord);
const z = urlTileCoord[0];
const resolution = tileGrid.getResolution(z);
// make extent 1 pixel smaller so we don't load tiles for < 0.5 pixel render space
bufferExtent(extent, -1 / resolution, extent);
const sourceTileGrid = this.tileGrid;
const sourceExtent = sourceTileGrid.getExtent();
if (sourceExtent) {
getIntersection(extent, sourceExtent, extent);
}
const sourceZ = sourceTileGrid.getZForResolution(resolution, 1);
const minZoom = sourceTileGrid.getMinZoom();
const tileGrid = this.getTileGridForProjection(projection);
const extent = tileGrid.getTileCoordExtent(urlTileCoord);
const z = urlTileCoord[0];
const resolution = tileGrid.getResolution(z);
// make extent 1 pixel smaller so we don't load tiles for < 0.5 pixel render space
bufferExtent(extent, -1 / resolution, extent);
const sourceTileGrid = this.tileGrid;
const sourceExtent = sourceTileGrid.getExtent();
if (sourceExtent) {
getIntersection(extent, sourceExtent, extent);
}
const sourceZ = sourceTileGrid.getZForResolution(resolution, 1);
const minZoom = sourceTileGrid.getMinZoom();
let loadedZ = sourceZ + 1;
let covered, empty;
const previousSourceTiles = this.sourceTilesByTileKey_[tile.getKey()];
let sourceTiles, covered, empty, loadedZ;
if (previousSourceTiles && previousSourceTiles.length > 0 && previousSourceTiles[0].tileCoord[0] === sourceZ) {
sourceTiles = previousSourceTiles;
covered = true;
empty = false;
loadedZ = sourceZ;
} else {
sourceTiles = [];
loadedZ = sourceZ + 1;
do {
--loadedZ;
covered = true;
empty = true;
sourceTileGrid.forEachTileCoord(extent, loadedZ, function(sourceTileCoord) {
const tileKey = getKey(sourceTileCoord);
const coordKey = getKey(sourceTileCoord);
let sourceTile;
if (tileKey in this.sourceTiles_) {
sourceTile = this.sourceTiles_[tileKey];
if (coordKey in this.sourceTileByCoordKey_) {
sourceTile = this.sourceTileByCoordKey_[coordKey];
const state = sourceTile.getState();
if (state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY) {
empty = empty && state === TileState.EMPTY;
@@ -215,17 +226,17 @@ class VectorTile extends UrlTile {
}
} else if (loadedZ === sourceZ) {
const tileUrl = this.tileUrlFunction(sourceTileCoord, pixelRatio, projection);
sourceTile = new this.tileClass(sourceTileCoord,
tileUrl == undefined ? TileState.EMPTY : TileState.IDLE,
tileUrl == undefined ? '' : tileUrl,
this.format_, this.tileLoadFunction);
sourceTile.extent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
sourceTile.projection = projection;
sourceTile.resolution = sourceTileGrid.getResolution(sourceTileCoord[0]);
this.sourceTiles_[tileKey] = sourceTile;
empty = empty && sourceTile.getState() === TileState.EMPTY;
listen(sourceTile, EventType.CHANGE, this.handleTileChange, this);
sourceTile.load();
if (tileUrl !== undefined) {
sourceTile = new this.tileClass(sourceTileCoord, TileState.IDLE, tileUrl,
this.format_, this.tileLoadFunction);
sourceTile.extent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
sourceTile.projection = projection;
sourceTile.resolution = sourceTileGrid.getResolution(sourceTileCoord[0]);
this.sourceTileByCoordKey_[coordKey] = sourceTile;
empty = false;
listen(sourceTile, EventType.CHANGE, this.handleTileChange, this);
sourceTile.load();
}
} else {
empty = false;
}
@@ -237,7 +248,7 @@ class VectorTile extends UrlTile {
tile.loadingSourceTiles++;
const key = listen(sourceTile, EventType.CHANGE, function() {
const state = sourceTile.getState();
const sourceTileKey = getKey(sourceTile.tileCoord);
const sourceTileKey = sourceTile.getKey();
if (state === TileState.LOADED || state === TileState.ERROR) {
if (state === TileState.LOADED) {
unlistenByKey(key);
@@ -259,19 +270,19 @@ class VectorTile extends UrlTile {
sourceTiles.length = 0;
}
} while (!covered && loadedZ > minZoom);
if (!empty && tile.getState() === TileState.IDLE) {
tile.setState(TileState.LOADING);
}
if (covered || empty) {
tile.hifi = sourceZ === loadedZ;
tile.sourceZ = loadedZ;
const previousSourceTiles = this.sourceTilesByTileKey_[getKey(tile.tileCoord)];
if (tile.getState() < TileState.LOADED) {
tile.setState(empty ? TileState.EMPTY : TileState.LOADED);
} else if (!previousSourceTiles || !equals(sourceTiles, previousSourceTiles)) {
this.removeSourceTiles(tile);
this.addSourceTiles(tile, sourceTiles);
}
}
if (!empty && tile.getState() === TileState.IDLE) {
tile.setState(TileState.LOADING);
}
if (covered || empty) {
tile.hifi = sourceZ === loadedZ;
tile.sourceZ = loadedZ;
if (tile.getState() < TileState.LOADED) {
tile.setState(empty ? TileState.EMPTY : TileState.LOADED);
} else if (!previousSourceTiles || !equals(sourceTiles, previousSourceTiles)) {
this.removeSourceTiles(tile);
this.addSourceTiles(tile, sourceTiles);
}
}
return sourceTiles;
@@ -282,7 +293,7 @@ class VectorTile extends UrlTile {
* @param {Array<import("../VectorTile").default>} sourceTiles Source tiles.
*/
addSourceTiles(tile, sourceTiles) {
this.sourceTilesByTileKey_[getKey(tile.tileCoord)] = sourceTiles;
this.sourceTilesByTileKey_[tile.getKey()] = sourceTiles;
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
sourceTiles[i].consumers++;
}
@@ -292,7 +303,7 @@ class VectorTile extends UrlTile {
* @param {VectorRenderTile} tile Tile.
*/
removeSourceTiles(tile) {
const tileKey = getKey(tile.tileCoord);
const tileKey = tile.getKey();
if (tileKey in this.sourceTilesByTileKey_) {
const sourceTiles = this.sourceTilesByTileKey_[tileKey];
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
@@ -300,7 +311,7 @@ class VectorTile extends UrlTile {
sourceTile.consumers--;
if (sourceTile.consumers === 0) {
sourceTile.dispose();
delete this.sourceTiles_[getKey(sourceTile.tileCoord)];
delete this.sourceTileByCoordKey_[getKey(sourceTile.tileCoord)];
}
}
}
@@ -311,27 +322,44 @@ class VectorTile extends UrlTile {
* @inheritDoc
*/
getTile(z, x, y, pixelRatio, projection) {
const tileCoordKey = getKeyZXY(z, x, y);
if (this.tileCache.containsKey(tileCoordKey)) {
return (
/** @type {!import("../Tile.js").default} */ (this.tileCache.get(tileCoordKey))
);
} else {
const tileCoord = [z, x, y];
const urlTileCoord = this.getTileCoordForTileUrlFunction(
tileCoord, projection);
const tile = new VectorRenderTile(
tileCoord,
urlTileCoord !== null ? TileState.IDLE : TileState.EMPTY,
urlTileCoord,
this.tileGrid,
this.getSourceTiles.bind(this, pixelRatio, projection),
this.removeSourceTiles.bind(this));
tile.key = this.getRevision().toString();
this.tileCache.set(tileCoordKey, tile);
return tile;
const coordKey = getKeyZXY(z, x, y);
const key = this.getKey();
let tile;
if (this.tileCache.containsKey(coordKey)) {
tile = /** @type {!import("../Tile.js").default} */ (this.tileCache.get(coordKey));
if (tile.key === key) {
return tile;
}
}
const tileCoord = [z, x, y];
let urlTileCoord = this.getTileCoordForTileUrlFunction(tileCoord, projection);
const sourceExtent = this.getTileGrid().getExtent();
if (urlTileCoord && sourceExtent) {
const tileGrid = this.getTileGridForProjection(projection);
const tileExtent = tileGrid.getTileCoordExtent(urlTileCoord);
// make extent 1 pixel smaller so we don't load tiles for < 0.5 pixel render space
bufferExtent(tileExtent, -1 / tileGrid.getResolution(z), tileExtent);
if (!intersects(sourceExtent, tileExtent)) {
urlTileCoord = null;
}
}
const newTile = new VectorRenderTile(
tileCoord,
urlTileCoord !== null ? TileState.IDLE : TileState.EMPTY,
urlTileCoord,
this.tileGrid,
this.getSourceTiles.bind(this, pixelRatio, projection),
this.removeSourceTiles.bind(this));
newTile.key = key;
if (tile) {
newTile.interimTile = tile;
newTile.refreshInterimChain();
this.tileCache.replace(coordKey, newTile);
} else {
this.tileCache.set(coordKey, newTile);
}
return newTile;
}
/**

View File

@@ -9,8 +9,24 @@
* @enum {string}
*/
export default {
/**
* HiDPI support for [Carmenta Server](https://www.carmenta.com/en/products/carmenta-server)
* @api
*/
CARMENTA_SERVER: 'carmentaserver',
/**
* HiDPI support for [GeoServer](https://geoserver.org/)
* @api
*/
GEOSERVER: 'geoserver',
/**
* HiDPI support for [MapServer](https://mapserver.org/)
* @api
*/
MAPSERVER: 'mapserver',
/**
* HiDPI support for [QGIS](https://qgis.org/)
* @api
*/
QGIS: 'qgis'
};

View File

@@ -41,6 +41,10 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
* @property {number} [transition] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {number} [zDirection=0] Indicate which resolution should be used
* by a renderer if the view resolution does not match any resolution of the tile source.
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
* will be used. If -1, the nearest higher resolution will be used.
*/
@@ -94,7 +98,8 @@ class XYZ extends TileImage {
urls: options.urls,
wrapX: options.wrapX !== undefined ? options.wrapX : true,
transition: options.transition,
attributionsCollapsible: options.attributionsCollapsible
attributionsCollapsible: options.attributionsCollapsible,
zDirection: options.zDirection
});
}

View File

@@ -114,8 +114,8 @@ export class CustomTile extends ImageTile {
* @property {number} [transition] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {number} [tileSize=256] Tile size. Same tile size is used for all zoom levels.
* @property {number} [zDirection] Indicate which resolution should be used
* by a renderer if the views resolution does not match any resolution of the tile source.
* @property {number} [zDirection=0] Indicate which resolution should be used
* by a renderer if the view resolution does not match any resolution of the tile source.
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
* will be used. If -1, the nearest higher resolution will be used.
*/

View File

@@ -53,13 +53,14 @@ class CircleStyle extends RegularShape {
}
/**
* Set the circle radius.
*
* @param {number} radius Circle radius.
* @api
*/
* Set the circle radius.
*
* @param {number} radius Circle radius.
* @api
*/
setRadius(radius) {
this.radius_ = radius;
this.render();
}
}

View File

@@ -30,10 +30,10 @@ import ImageStyle from './Image.js';
* @property {import("../colorlike.js").ColorLike} [strokeStyle]
* @property {number} strokeWidth
* @property {number} size
* @property {string} lineCap
* @property {CanvasLineCap} lineCap
* @property {Array<number>} lineDash
* @property {number} lineDashOffset
* @property {string} lineJoin
* @property {CanvasLineJoin} lineJoin
* @property {number} miterLimit
*/
@@ -142,7 +142,7 @@ class RegularShape extends ImageStyle {
*/
this.hitDetectionImageSize_ = null;
this.render_();
this.render();
}
@@ -301,9 +301,9 @@ class RegularShape extends ImageStyle {
/**
* @protected
*/
render_() {
let lineCap = '';
let lineJoin = '';
render() {
let lineCap = defaultLineCap;
let lineJoin = defaultLineJoin;
let miterLimit = 0;
let lineDash = null;
let lineDashOffset = 0;
@@ -338,7 +338,6 @@ class RegularShape extends ImageStyle {
let size = 2 * (this.radius_ + strokeWidth) + 1;
/** @type {RenderOptions} */
const renderOptions = {
strokeStyle: strokeStyle,
strokeWidth: strokeWidth,
@@ -418,8 +417,8 @@ class RegularShape extends ImageStyle {
context.setLineDash(renderOptions.lineDash);
context.lineDashOffset = renderOptions.lineDashOffset;
}
context.lineCap = /** @type {CanvasLineCap} */ (renderOptions.lineCap);
context.lineJoin = /** @type {CanvasLineJoin} */ (renderOptions.lineJoin);
context.lineCap = renderOptions.lineCap;
context.lineJoin = renderOptions.lineJoin;
context.miterLimit = renderOptions.miterLimit;
context.stroke();
}

View File

@@ -8,8 +8,8 @@
* @property {import("../color.js").Color|import("../colorlike.js").ColorLike} [color] A color, gradient or pattern.
* See {@link module:ol/color~Color} and {@link module:ol/colorlike~ColorLike} for possible formats.
* Default null; if null, the Canvas/renderer default black will be used.
* @property {string} [lineCap='round'] Line cap style: `butt`, `round`, or `square`.
* @property {string} [lineJoin='round'] Line join style: `bevel`, `round`, or `miter`.
* @property {CanvasLineCap} [lineCap='round'] Line cap style: `butt`, `round`, or `square`.
* @property {CanvasLineJoin} [lineJoin='round'] Line join style: `bevel`, `round`, or `miter`.
* @property {Array<number>} [lineDash] Line dash pattern. Default is `undefined` (no dash).
* Please note that Internet Explorer 10 and lower do not support the `setLineDash` method on
* the `CanvasRenderingContext2D` and therefore this option will have no visual effect in these browsers.
@@ -43,7 +43,7 @@ class Stroke {
/**
* @private
* @type {string|undefined}
* @type {CanvasLineCap|undefined}
*/
this.lineCap_ = options.lineCap;
@@ -61,7 +61,7 @@ class Stroke {
/**
* @private
* @type {string|undefined}
* @type {CanvasLineJoin|undefined}
*/
this.lineJoin_ = options.lineJoin;
@@ -107,7 +107,7 @@ class Stroke {
/**
* Get the line cap type for the stroke.
* @return {string|undefined} Line cap.
* @return {CanvasLineCap|undefined} Line cap.
* @api
*/
getLineCap() {
@@ -134,7 +134,7 @@ class Stroke {
/**
* Get the line join type for the stroke.
* @return {string|undefined} Line join.
* @return {CanvasLineJoin|undefined} Line join.
* @api
*/
getLineJoin() {
@@ -172,7 +172,7 @@ class Stroke {
/**
* Set the line cap.
*
* @param {string|undefined} lineCap Line cap.
* @param {CanvasLineCap|undefined} lineCap Line cap.
* @api
*/
setLineCap(lineCap) {
@@ -208,7 +208,7 @@ class Stroke {
/**
* Set the line join.
*
* @param {string|undefined} lineJoin Line join.
* @param {CanvasLineJoin|undefined} lineJoin Line join.
* @api
*/
setLineJoin(lineJoin) {

View File

@@ -432,6 +432,31 @@ class WebGLHelper extends Disposable {
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.useProgram(this.currentProgram_);
this.applyFrameState(frameState);
this.applyUniforms(frameState);
}
/**
* Clear the render target & bind it for future draw operations.
* This is similar to `prepareDraw`, only post processes will not be applied.
* Note: the whole viewport will be drawn to the render target, regardless of its size.
* @param {import("../PluggableMap.js").FrameState} frameState current frame state
* @param {import("./RenderTarget.js").default} renderTarget Render target to draw to
* @param {boolean} [opt_disableAlphaBlend] If true, no alpha blending will happen.
*/
prepareDrawToRenderTarget(frameState, renderTarget, opt_disableAlphaBlend) {
const gl = this.getGL();
gl.bindFramebuffer(gl.FRAMEBUFFER, renderTarget.getFramebuffer());
gl.viewport(0, 0, frameState.size[0], frameState.size[1]);
gl.bindTexture(gl.TEXTURE_2D, renderTarget.getTexture());
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, opt_disableAlphaBlend ? gl.ZERO : gl.ONE_MINUS_SRC_ALPHA);
gl.useProgram(this.currentProgram_);
this.applyFrameState(frameState);
this.applyUniforms(frameState);
}
@@ -742,57 +767,36 @@ class WebGLHelper extends Disposable {
// TODO: shutdown program
/**
* TODO: these are not used and should be reworked
* @param {number=} opt_wrapS wrapS.
* @param {number=} opt_wrapT wrapT.
* @return {WebGLTexture} The texture.
* Will create or reuse a given webgl texture and apply the given size. If no image data
* specified, the texture will be empty, otherwise image data will be used and the `size`
* parameter will be ignored.
* Note: wrap parameters are set to clamp to edge, min filter is set to linear.
* @param {Array<number>} size Expected size of the texture
* @param {ImageData|HTMLImageElement|HTMLCanvasElement} [opt_data] Image data/object to bind to the texture
* @param {WebGLTexture} [opt_texture] Existing texture to reuse
* @return {WebGLTexture} The generated texture
* @api
*/
createTextureInternal(opt_wrapS, opt_wrapT) {
createTexture(size, opt_data, opt_texture) {
const gl = this.getGL();
const texture = gl.createTexture();
const texture = opt_texture || gl.createTexture();
// set params & size
const level = 0;
const internalFormat = gl.RGBA;
const border = 0;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
if (opt_data) {
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, format, type, opt_data);
} else {
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, size[0], size[1], border, format, type, null);
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
if (opt_wrapS !== undefined) {
gl.texParameteri(
gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, opt_wrapS);
}
if (opt_wrapT !== undefined) {
gl.texParameteri(
gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, opt_wrapT);
}
return texture;
}
/**
* TODO: these are not used and should be reworked
* @param {number} width Width.
* @param {number} height Height.
* @param {number=} opt_wrapS wrapS.
* @param {number=} opt_wrapT wrapT.
* @return {WebGLTexture} The texture.
*/
createEmptyTexture(width, height, opt_wrapS, opt_wrapT) {
const gl = this.getGL();
const texture = this.createTextureInternal(opt_wrapS, opt_wrapT);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
return texture;
}
/**
* TODO: these are not used and should be reworked
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image.
* @param {number=} opt_wrapS wrapS.
* @param {number=} opt_wrapT wrapT.
* @return {WebGLTexture} The texture.
*/
createTexture(image, opt_wrapS, opt_wrapT) {
const gl = this.getGL();
const texture = this.createTextureInternal(opt_wrapS, opt_wrapT);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
return texture;
}
}

View File

@@ -0,0 +1,164 @@
/**
* A wrapper class to simplify rendering to a texture instead of the final canvas
* @module ol/webgl/RenderTarget
*/
import {equals} from '../array.js';
// for pixel color reading
const tmpArray4 = new Uint8Array(4);
/**
* @classdesc
* This class is a wrapper around the association of both a `WebGLTexture` and a `WebGLFramebuffer` instances,
* simplifying initialization and binding for rendering.
* @api
*/
class WebGLRenderTarget {
/**
* @param {import("./Helper.js").default} helper WebGL helper; mandatory.
* @param {Array<number>} [opt_size] Expected size of the render target texture; note: this can be changed later on.
*/
constructor(helper, opt_size) {
/**
* @private
* @type {import("./Helper.js").default}
*/
this.helper_ = helper;
const gl = helper.getGL();
/**
* @private
* @type {WebGLTexture}
*/
this.texture_ = gl.createTexture();
/**
* @private
* @type {WebGLFramebuffer}
*/
this.framebuffer_ = gl.createFramebuffer();
/**
* @type {Array<number>}
* @private
*/
this.size_ = opt_size || [1, 1];
/**
* @type {Uint8Array}
* @private
*/
this.data_ = new Uint8Array(0);
/**
* @type {boolean}
* @private
*/
this.dataCacheDirty_ = true;
this.updateSize_();
}
/**
* Changes the size of the render target texture. Note: will do nothing if the size
* is already the same.
* @param {Array<number>} size Expected size of the render target texture
* @api
*/
setSize(size) {
if (equals(size, this.size_)) {
return;
}
this.size_[0] = size[0];
this.size_[1] = size[1];
this.updateSize_();
}
/**
* Returns the size of the render target texture
* @return {Array<number>} Size of the render target texture
* @api
*/
getSize() {
return this.size_;
}
/**
* This will cause following calls to `#readAll` or `#readPixel` to download the content of the
* render target into memory, which is an expensive operation.
* This content will be kept in cache but should be cleared after each new render.
* @api
*/
clearCachedData() {
this.dataCacheDirty_ = true;
}
/**
* Returns the full content of the frame buffer as a series of r, g, b, a components
* in the 0-255 range (unsigned byte).
* @return {Uint8Array} Integer array of color values
* @api
*/
readAll() {
if (this.dataCacheDirty_) {
const size = this.size_;
const gl = this.helper_.getGL();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer_);
gl.readPixels(0, 0, size[0], size[1], gl.RGBA, gl.UNSIGNED_BYTE, this.data_);
this.dataCacheDirty_ = false;
}
return this.data_;
}
/**
* Reads one pixel of the frame buffer as an array of r, g, b, a components
* in the 0-255 range (unsigned byte).
* @param {number} x Pixel coordinate
* @param {number} y Pixel coordinate
* @returns {Uint8Array} Integer array with one color value (4 components)
* @api
*/
readPixel(x, y) {
this.readAll();
const index = Math.floor(x) + (this.size_[1] - Math.floor(y) - 1) * this.size_[0];
tmpArray4[0] = this.data_[index * 4];
tmpArray4[1] = this.data_[index * 4 + 1];
tmpArray4[2] = this.data_[index * 4 + 2];
tmpArray4[3] = this.data_[index * 4 + 3];
return tmpArray4;
}
/**
* @return {WebGLTexture} Texture to render to
*/
getTexture() {
return this.texture_;
}
/**
* @return {WebGLFramebuffer} Frame buffer of the render target
*/
getFramebuffer() {
return this.framebuffer_;
}
/**
* @private
*/
updateSize_() {
const size = this.size_;
const gl = this.helper_.getGL();
this.texture_ = this.helper_.createTexture(size, null, this.texture_);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer_);
gl.viewport(0, 0, size[0], size[1]);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture_, 0);
this.data_ = new Uint8Array(size[0] * size[1] * 4);
}
}
export default WebGLRenderTarget;

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