Compare commits

...

205 Commits

Author SHA1 Message Date
ahocevar
937b57c7d9 Update package version to 6.0.0-beta.3 2019-03-11 10:44:51 +01:00
Andreas Hocevar
dfabcc0c66 Merge pull request #9137 from jahow/add-view-constrain-extent-2
Refactor the way view constraints are handled & add a view extent constraint
2019-03-11 10:38:31 +01:00
Andreas Hocevar
3282c74de6 Merge pull request #9311 from openlayers/greenkeeper/pbf-3.2.0
Update pbf to the latest version 🚀
2019-03-11 10:29:17 +01:00
Andreas Hocevar
dee36639bd Merge pull request #9313 from ahocevar/interpolate-tests
Re-enable and fix disabled tests
2019-03-11 10:18:00 +01:00
Andreas Hocevar
5a578c00a7 Merge pull request #9314 from ahocevar/fix-transpile
Fix transpilation
2019-03-11 10:17:32 +01:00
ahocevar
e8243b73b5 Fix transpilation 2019-03-11 10:11:05 +01:00
ahocevar
404b04ae59 Re-enable and fix disabled tests 2019-03-11 09:30:04 +01:00
Andreas Hocevar
8a02a6f9f1 Merge pull request #9308 from ahocevar/vectortile-loader-projection
Simplify vector tile projection handling
2019-03-11 09:28:48 +01:00
Bart van den Eijnden
4a67bd86a8 Merge pull request #9312 from bartvde/stamen-minzoom
Update zoom levels for Stamen source
2019-03-11 09:17:50 +01:00
Frédéric Junod
feb1a9f31b Merge pull request #9310 from fredj/stamen_transition
Add transition options to Stamen source
2019-03-11 09:11:38 +01:00
greenkeeper[bot]
8a5720e8cb fix(package): update pbf to version 3.2.0 2019-03-11 07:51:32 +00:00
bartvde
9ad2924f00 Update zoom levels for Stamen source 2019-03-11 08:50:28 +01:00
Frederic Junod
0401aed6e3 Add transition options to Stamen source 2019-03-11 08:34:42 +01:00
ahocevar
b2722542fe Simplify vector tile projection handling 2019-03-10 09:37:58 +01:00
Andreas Hocevar
7002053678 Merge pull request #9307 from ahocevar/line-height
Take line-height into account when measuring text height
2019-03-09 10:07:16 +01:00
Frederic Junod
425cd19922 Add a new font line height option in vector-label example 2019-03-09 09:59:57 +01:00
ahocevar
4a7b3cde56 Take line-height into account when measuring text height 2019-03-09 08:25:09 +01:00
Frédéric Junod
9c1c71b8e5 Merge pull request #9306 from fredj/dispose_layer_renderer
Set the canvas size to 0 on dispose
2019-03-09 08:00:35 +01:00
Frederic Junod
6de566d95b Set the canvas size to 0 on dispose 2019-03-08 13:25:49 +01:00
Andreas Hocevar
a372ca0569 Merge pull request #9295 from ahocevar/update-ol-mapbox-style
Update ol-mapbox-style to fix broken mapbox-style example
2019-03-07 10:10:08 +01:00
Marc Jansen
1e06dd979e Merge pull request #9292 from KaiVolland/downgrade-puppeteer
Downgrades puppeteer to `~1.11.0`
2019-03-06 19:15:35 +01:00
ahocevar
c44e6ebe1d Update ol-mapbox-style to fix broken mapbox-style example 2019-03-06 19:13:33 +01:00
Kai Volland
c8fab42d75 Downgrades puppeteer to ~1.11.0
This downgrades puppeteer to version `~1.11.0`.
This will download the stable version of chromium
(72.0.3582.0). The current puppeteer version
(`^1.12.2`) downloads the beta version of
chromium (73.0.3679.0).
2019-03-06 16:53:45 +01:00
Andreas Hocevar
0995f95ef1 Fix typo in extent-constrained example
Co-Authored-By: jahow <olivier.guyot@camptocamp.com>
2019-03-04 18:11:15 +01:00
Olivier Guyot
447266cbe8 Better smoothing of resolution and center constraints
Previously the formula for the resolution constraint allowed going way past
the minimum zoom.
Also adjusted the center constraint to avoid a zigzag effect when going out
of resolution bounds.
2019-03-04 10:14:06 +01:00
Andreas Hocevar
d25a534dea Merge pull request #9285 from openlayers/greenkeeper/globby-9.1.0
Update globby to the latest version 🚀
2019-03-04 09:53:25 +01:00
greenkeeper[bot]
90749cd80f chore(package): update globby to version 9.1.0 2019-03-03 13:21:03 +00:00
Andreas Hocevar
f1aca67882 Merge pull request #9280 from openlayers/greenkeeper/ol-mapbox-style-4.0.0
Update ol-mapbox-style to the latest version 🚀
2019-03-01 09:56:49 +01:00
Tim Schaub
d7905896f7 Merge pull request #9266 from tschaub/vector-image-options
Full type definition for image vector layer options
2019-02-28 21:43:23 -07:00
greenkeeper[bot]
a7af9a0697 chore(package): update ol-mapbox-style to version 4.0.0 2019-02-28 16:31:06 +00:00
Andreas Hocevar
eea67868f7 Merge pull request #9279 from openlayers/greenkeeper/webpack-4.29.6
Update webpack to the latest version 🚀
2019-02-28 14:24:54 +01:00
Frédéric Junod
d99405feeb Merge pull request #9278 from fredj/bing_imagerySet
Update Bing's imagerySet used in examples
2019-02-28 12:59:17 +01:00
greenkeeper[bot]
14831a0d61 chore(package): update webpack to version 4.29.6 2019-02-28 10:53:28 +00:00
Marc Jansen
43bc53b20b Merge pull request #9270 from bvx89/patch-1
Update faq.md
2019-02-28 10:49:35 +01:00
Frederic Junod
c01dee0884 Update Bing's imagerySet used in examples 2019-02-28 10:43:59 +01:00
Andreas Hocevar
6fd83b9a8e Merge pull request #9277 from ahocevar/rotate-token
Rotate API token
2019-02-27 18:38:34 +01:00
ahocevar
05712acfef Rotate API token 2019-02-27 18:31:50 +01:00
Frédéric Junod
cd17f4b0d6 Merge pull request #9276 from fredj/graticule_cleanup
Use the constructor options instead of changing the private variables
2019-02-27 17:02:41 +01:00
Frederic Junod
7c7b43ee91 Use the constructor options instead of changing the private variables 2019-02-27 15:11:08 +01:00
Frédéric Junod
02f9411c6b Merge pull request #9275 from fredj/rm_setDeclutter
Remove setDeclutter function
2019-02-27 14:03:38 +01:00
Frederic Junod
1338a868e9 Remove setDeclutter function
Changing the declutter value after the layer creating doesn't work, remove the function to avoid confusion.
2019-02-27 11:41:08 +01:00
Lasse Brudeskar Vikås
6b1f9e681c Update faq.md 2019-02-27 11:30:55 +01:00
Andreas Hocevar
65d8b5f26b Merge pull request #9273 from chrismayer/9269-fix-gfi-examples
Fix WMS GetFeatureInfo examples
2019-02-27 10:15:16 +01:00
Christian Mayer
530e47d26c Remove IFrame usage in GetFeatureInfo img example
This replaces the loading of the GetFeatureInfo request via IFrame by a
fetch operation in the GetFeatureInfo image example.
2019-02-27 09:55:59 +01:00
Christian Mayer
85f2312370 Remove IFrame usage in GetFeatureInfo tile example
This replaces the loading of the GetFeatureInfo request via IFrame by a
fetch operation in the GetFeatureInfo tile example.
2019-02-27 09:55:59 +01:00
Lasse Brudeskar Vikås
301b45cb7c Update faq.md
Fixed typos in description, and tried to make the text more readable.
2019-02-26 16:13:06 +01:00
Frédéric Junod
3aa93014c0 Merge pull request #9246 from openlayers/mobile-full-screen
Add missing ol.css in mobile-full-screen example
2019-02-26 12:36:42 +01:00
Frederic Junod
734900f1d7 Add missing ol.css in mobile-full-screen example 2019-02-26 08:39:39 +01:00
Andreas Hocevar
fb2ea83838 Merge pull request #9268 from openlayers/greenkeeper/mocha-6.0.2
Update mocha to the latest version 🚀
2019-02-25 20:51:51 +01:00
greenkeeper[bot]
8534aaa304 chore(package): update mocha to version 6.0.2 2019-02-25 19:09:43 +00:00
Tim Schaub
36cf654f09 Full type definition for image vector layer options 2019-02-25 08:37:30 -07:00
Andreas Hocevar
4e8522e696 Merge pull request #9261 from openlayers/greenkeeper/coveralls-3.0.3
Update coveralls to the latest version 🚀
2019-02-25 15:43:05 +01:00
Frédéric Junod
88760de64b Merge pull request #9264 from tpluscode/patch-1
add question about resizing map element
2019-02-25 10:43:54 +01:00
Tomasz Pluskiewicz
b5f7705e1d Update faq.md 2019-02-25 10:02:52 +01:00
Tomasz Pluskiewicz
9efe10f747 add question about resizing map element 2019-02-25 09:42:54 +01:00
Frédéric Junod
73744de14e Merge pull request #9260 from fredj/rm_geom_param
Remove unused 'geometry' param from ol/render/canvas/Builder
2019-02-25 08:25:06 +01:00
greenkeeper[bot]
d6c18667f4 chore(package): update coveralls to version 3.0.3 2019-02-22 19:13:45 +00:00
Frederic Junod
cd28c8a301 Remove unused 'geometry' param from 'createFill' function 2019-02-22 16:52:32 +01:00
Frederic Junod
79c6cc5159 Remove unused 'geometry' param from '{begin,end}Geometry' functions 2019-02-22 16:45:25 +01:00
Frédéric Junod
345607be28 Merge pull request #9256 from openlayers/greenkeeper/mocha-6.0.1
Update mocha to the latest version 🚀
2019-02-22 15:14:35 +01:00
Olivier Guyot
f67baa0dc0 Interactions / fix zoom level when a zoom interaction ends 2019-02-22 15:04:54 +01:00
Olivier Guyot
75c379beeb View / allow specifying an anchor on interaction end
This also fixes a bug where the animation anchor would be lost when outside
the allowed resolution.
2019-02-22 15:04:54 +01:00
Olivier Guyot
405e206717 View / better names for getValid* and applyParameters_ methods 2019-02-22 15:04:54 +01:00
Olivier Guyot
c2af03f152 Update the View documentation & document breaking changes 2019-02-22 15:04:54 +01:00
Olivier Guyot
7835869582 Add an extent restriction on the mapbox layer example
This fixes a bug where the OL map would allow much larger resolution than
mapbox.
2019-02-22 15:01:30 +01:00
Olivier Guyot
78e8f23df5 View / add getValidCenter method to improve interactions
The DragPan, KeyboardPan and DragZoom interactions now make sure to
animate to a valid center/resolution target to avoid
a chained "resolve" animation which looks weird.

The `View.fit` method was also fixed to use this.
2019-02-22 15:01:30 +01:00
Olivier Guyot
433bccd207 Linting and fixes for unit tests 2019-02-22 15:01:30 +01:00
Olivier Guyot
ef6d17d817 View / add a 'smoothResolutionConstraint' options
When enabled (true by default), the resolution min/max values will be applied with
a smoothing effect for a better user experience.
2019-02-22 15:01:30 +01:00
Olivier Guyot
e023c144bb View / add adjust* methods to manipulate the view more easily
API changes:
* (breaking) the `rotate` method is gone
* the `adjustRotation`, `adjustResolution` and `adjustZoom` methods are now
  available and allow using an anchor.

This means interactions do not have to do the anchor computation themselves
and this also fix anchor computation when constraints must be applied.
2019-02-22 15:01:30 +01:00
Olivier Guyot
b5273babb5 View / handle resolutions array with length=1 2019-02-22 15:01:30 +01:00
Olivier Guyot
49662b019c View / add a constrainResolution option
This introduces a breaking change.

This options replaces the various `constrainResolution` options on interactions
and the `fit` method.

Since constraints are now the responsibility of the View, the fact that intermediary
zoom levels are allowed or not is now set in the view options.

By default, the view resolution is unconstrained.
2019-02-22 15:01:30 +01:00
jahow
48ad1ffcbf View / implement a smooth rebound effect when a max extent is given
This is done by applying the center constraint differently when we're in the
middle of an interaction/animation or not.

When the view is moving, the center constraint will restrain the given value
in an "elastic" way, using a logarithmic function.

This can be disabled using the `smoothCenterConstrain` view parameter.
2019-02-22 15:01:30 +01:00
jahow
cd186ada7f Add new tests for View & Interaction w/ fixes 2019-02-22 15:01:30 +01:00
Olivier Guyot
a6f65df8c4 View / add a resolveConstraints method to end interactions
This will help making sure that the view will come back to a "rested" state
once the interactions are over.

Interactions no longer need to handle the animation back to a rested state,
they simply call `endInteraction` with the desired duration and direction.
2019-02-22 15:01:30 +01:00
Olivier Guyot
1c5fd62e43 View / refactor how zoom and resolution are computed
This commit aims to simplify the computation of zoom and resolution in the
View class.

Previously zoom levels and resolution computations were mixed in different places,
ie resolution constraints, initial values, etc.

Now the View class only has the `getZoomForResolution` and `getResolutionForZoom`
methods to convert from one system to another.

Other than that, most computations use the resolution system internally.

The `constrainResolution` method also does not exist anymore, and is replaced
by `getValidResolution` and `getValidZoomLevel` public methods.
2019-02-22 15:01:30 +01:00
Olivier Guyot
1f379a06a4 View / add support for viewport extent constraint
This introduces a breaking change:

The `extent` view option now constrains the whole viewport and not just the
view center.
The option `constrainOnlyCenter` was added to keep the previous behaviour.

Constraining the whole viewport and not only the view center means
that the center and resolution constraints must be applied with a knowledge of
the viewport size.
2019-02-22 15:01:30 +01:00
jahow
e52fab636c View / apply constraints automatically based on hints
All constraints can now function differently if they are applied during
interaction or animation.
2019-02-22 15:01:30 +01:00
jahow
d991dfa54a View / remove constrainCenter method 2019-02-22 15:01:30 +01:00
jahow
c2c1aa01d3 View / removed the constrainRotation method 2019-02-22 15:01:30 +01:00
Olivier Guyot
e6c4b2ffd1 View / make the constrainResolution function private
Other classes should not need to worry about constraining the resolution
or not, as the View will eventually do this on its own.
2019-02-22 15:01:30 +01:00
Olivier Guyot
3c1e3779e2 View / add a method to compute a valid zoom level
The `getValidZoomLevel` apply the current resolution constraint to return
a value that is guaranteed valid.

This is used for interactions & controls which need a target value to work:
the +/- buttons, the zoom clider, the dragbox zoom and the mouse wheel zoom.
2019-02-22 15:01:30 +01:00
Olivier Guyot
4e1ece16ed View / implemented begin- and endInteraction methods 2019-02-22 15:01:30 +01:00
Olivier Guyot
1cb934dbe3 View / implement intermediate values for center/rot/res
The view now has targetCenter, targetRotation and targetResolution members.

These hold the new values given by set* methods. The actual view parameters are then changed by
calling `applyParameters_`.
2019-02-22 15:01:30 +01:00
Andreas Hocevar
767c765524 Merge pull request #9254 from petrsloup/tilejson-tilesize
Add tileSize option to ol/source/TileJSON
2019-02-22 11:14:19 +01:00
Petr Sloup
67ee5a41b0 Add note about ignoring tileSize TileJSON property 2019-02-22 09:05:18 +01:00
greenkeeper[bot]
5c280e8114 chore(package): update mocha to version 6.0.1 2019-02-21 22:59:15 +00:00
Petr Sloup
bc25097899 Add tileSize option to ol/source/TileJSON
The TileJSON spec does not specify the tile size
and there is no TileJSON property specifying the value.

Many providers nowadays provide 512x512 with TileJSON.
2019-02-21 19:00:07 +01:00
Andreas Hocevar
6ce499532c Merge pull request #9250 from ahocevar/clear-refresh
Clearer behaviour of clear() and refresh() on sources
2019-02-21 17:12:26 +01:00
Andreas Hocevar
386f2dc67c Merge pull request #9247 from openlayers/greenkeeper/copy-webpack-plugin-5.0.0
Update copy-webpack-plugin to the latest version 🚀
2019-02-21 15:17:54 +01:00
Andreas Hocevar
90034e4d48 Merge pull request #9251 from ahocevar/cache-size
Fix cache size calculation
2019-02-21 01:57:40 +01:00
ahocevar
4166c80c6e Test cache size 2019-02-21 01:35:40 +01:00
ahocevar
d06d00ccbb Fix typo 2019-02-21 01:29:15 +01:00
Andreas Hocevar
959dba169d Merge pull request #9110 from crubier/master
Add TilePixelRatio to Zoomify
2019-02-21 00:29:42 +01:00
Vincent Lecrubier
900adaaffb Update Zoomify.js 2019-02-20 22:40:24 +00:00
ahocevar
94cd126189 Add setUrl function and don't reset loaded extents in setLoader 2019-02-20 21:55:29 +01:00
ahocevar
1416a3d162 Add upgrade notes 2019-02-20 20:40:33 +01:00
ahocevar
e4873a9952 Improve documentation for ol/Source#refresh 2019-02-20 20:40:09 +01:00
ahocevar
8d1022046e Clear loaded extents when a new loader is set 2019-02-20 20:39:26 +01:00
ahocevar
f40cbf2cac Do not reload on clear(), but on refresh() 2019-02-20 20:39:04 +01:00
ahocevar
a0ba8dd8c6 Add a clear() method for tile sources 2019-02-20 20:37:58 +01:00
ahocevar
8557bd96b5 Test refresh() for image sources 2019-02-20 20:36:40 +01:00
greenkeeper[bot]
3d4f77be51 chore(package): update copy-webpack-plugin to version 5.0.0 2019-02-20 13:19:25 +00:00
Andreas Hocevar
62d82411c8 Merge pull request #9244 from huyngkh/stylus-support
add stylus only and touch only mode to drawing a shape
2019-02-20 11:30:11 +01:00
Huy Nguyen
c3709ef51a remove arrow function, add JSDoc and restore original Draw 2019-02-20 17:23:50 +07:00
Andreas Hocevar
071e9a4735 Merge pull request #9242 from openlayers/greenkeeper/marked-0.6.1
Update marked to the latest version 🚀
2019-02-20 08:42:13 +01:00
Huy Nguyen
0c889da99c add stylus and touch mode to drawing a shape 2019-02-20 13:55:22 +07:00
greenkeeper[bot]
a41f51c437 chore(package): update marked to version 0.6.1 2019-02-19 21:23:11 +00:00
Frédéric Junod
4cb3e24048 Merge pull request #9240 from fredj/text_rotateWithView
Add setRotateWithView function to ol/style/Text
2019-02-19 15:29:00 +01:00
Frederic Junod
4cb9b1eeb3 Add setRotateWithView function to ol/style/Text 2019-02-19 14:20:58 +01:00
Frederic Junod
f0a97ee460 Fix comments indentation 2019-02-19 14:12:14 +01:00
Andreas Hocevar
7c1e16abc3 Merge pull request #9230 from ahocevar/hitdetect-declutter
Consider all tiles for hit detection when decluttering
2019-02-19 12:58:07 +01:00
Andreas Hocevar
84995c688c Merge pull request #9237 from umbe1987/export-to-pdf-example
Fix zoom after export to PDF is done
2019-02-19 11:46:26 +01:00
Umberto Minora
cefb5d4d32 fixed indents checked with npm run lint 2019-02-19 11:25:12 +01:00
Umberto Minora
8eadb3d04c Fixed indent spaces so that hopefully ci/circleci: build does not fail 2019-02-19 10:57:21 +01:00
Umberto Minora
a6023a710a Cleared mixed spaces and tabs 2019-02-19 10:48:11 +01:00
Umberto Minora
33ac3e34ee constrainResolution: false on view fit instead of setting the zoom 2019-02-19 10:38:23 +01:00
Andreas Hocevar
3c64018b37 Merge pull request #9239 from ahocevar/tests-puppeteer
Run tests in Puppeteer and non headless mode
2019-02-19 09:43:13 +01:00
Frédéric Junod
b01970cb86 Merge pull request #9236 from fredj/cleanup
Move params list to the constructor function
2019-02-19 09:26:46 +01:00
Andreas Hocevar
ffa24bfd22 Merge pull request #9238 from openlayers/greenkeeper/mocha-6.0.0
Update mocha to the latest version 🚀
2019-02-18 23:57:33 +01:00
ahocevar
be17bfe85a Avoid render test issues by not running Puppeteer in headless mode 2019-02-18 23:54:20 +01:00
ahocevar
974684ed2b Use Puppeteer for tests 2019-02-18 23:54:20 +01:00
greenkeeper[bot]
f90a41a7ca chore(package): update mocha to version 6.0.0 2019-02-18 21:57:28 +00:00
Firstname Lastname
df847ae35c Fix zoom after export to PDF is done 2019-02-18 22:12:38 +01:00
Frederic Junod
746455a9b9 Move params list to the constructor function 2019-02-18 17:33:07 +01:00
Frederic Junod
3557247b27 Fix comments indentation 2019-02-18 17:31:36 +01:00
Frédéric Junod
658a4a9194 Merge pull request #9233 from fredj/rm_deprecated
Remove deprecated {get,set}SnapToPixel functions
2019-02-18 15:35:42 +01:00
Andreas Hocevar
ec6371d7a0 Merge pull request #9232 from openlayers/greenkeeper/webpack-4.29.5
Update webpack to the latest version 🚀
2019-02-18 14:41:58 +01:00
Frederic Junod
6bab8793a2 Remove deprecated {get,set}SnapToPixel functions 2019-02-18 14:07:27 +01:00
Andreas Hocevar
45b2e8885a Merge pull request #9231 from ahocevar/vectortile-extent
Set vector tile extent earlier
2019-02-18 13:48:25 +01:00
greenkeeper[bot]
40b5891ea7 chore(package): update webpack to version 4.29.5 2019-02-18 11:32:20 +00:00
ahocevar
667cadc403 Remove extent default so we catch errors when extent is not set 2019-02-18 12:07:10 +01:00
ahocevar
08be6cf9bc Set extent berfore features 2019-02-18 11:54:59 +01:00
ahocevar
fd07de39ad Consider all tiles for hit detection when decluttering 2019-02-18 11:40:52 +01:00
Vincent Lecrubier
44942dffb5 Update Zoomify.js 2019-02-18 10:04:02 +00:00
Andreas Hocevar
f7b1fe07d0 Merge pull request #9225 from openlayers/greenkeeper/webpack-4.29.4
Update webpack to the latest version 🚀
2019-02-16 17:46:04 +01:00
greenkeeper[bot]
9a7e5747eb chore(package): update webpack to version 4.29.4 2019-02-15 16:07:50 +00:00
Frédéric Junod
e4264b94ed Merge pull request #9224 from fredj/less_vendor_prefix
Remove vendor prefix for the 'transform' CSS property and fullscreen api
2019-02-15 16:09:24 +01:00
Frederic Junod
e9ecea3bb7 Remove 'moz' prefix for the Full Screen API
Browser support: https://caniuse.com/#feat=fullscreen
2019-02-15 14:37:49 +01:00
Frederic Junod
391dfc5025 Remove vendor prefix for the 'transform' CSS property
Browser support: https://caniuse.com/#feat=transforms2d
2019-02-15 14:29:15 +01:00
Frédéric Junod
3da449d77e Merge pull request #9222 from fredj/devDependencies
Update all dev dependencies
2019-02-15 12:49:08 +01:00
greenkeeper[bot]
7a66dc5774 chore(package): update yargs to version 13.2.0 2019-02-15 10:36:02 +01:00
greenkeeper[bot]
74f7b1974d chore(package): update @types/topojson-specification to version 1.0.1 2019-02-15 10:35:44 +01:00
greenkeeper[bot]
6b94aaa424 chore(package): update @types/arcgis-rest-api to version 10.4.4 2019-02-15 10:33:42 +01:00
greenkeeper[bot]
c4ed80dd92 chore(package): update @types/geojson to version 7946.0.6 2019-02-15 10:32:38 +01:00
greenkeeper[bot]
7a73638fb2 chore(package): update ol-mapbox-style to version 3.7.1 2019-02-15 10:32:21 +01:00
greenkeeper[bot]
0ece4a7dae chore(package): update webpack-dev-middleware to version 3.5.2 2019-02-15 10:31:59 +01:00
greenkeeper[bot]
6fe8b5c49d chore(package): update puppeteer to version 1.12.2 2019-02-15 10:30:37 +01:00
greenkeeper[bot]
2ba4be2661 chore(package): update webpack-cli to version 3.2.3 2019-02-15 10:30:15 +01:00
Andreas Hocevar
a2a3bda5c9 Merge pull request #9221 from ahocevar/remove-unused-sortbyzindex
Remove unused sortByZIndex function
2019-02-15 09:45:26 +01:00
Frédéric Junod
f28b7c7bcf Merge pull request #9220 from fredj/rm_extra_canvas_resize
Don't resize/clear the vector renderer canvas
2019-02-15 09:34:57 +01:00
Frédéric Junod
27d025327f Merge pull request #9217 from fredj/f_9211
Don't use loadImage function to avoid infinite loading loop
2019-02-15 08:47:59 +01:00
ahocevar
020f513ed5 Remove unused sortByZIndex function 2019-02-14 17:40:38 +01:00
Frederic Junod
30fb0b1ed1 Don't resize/clear the vector renderer canvas
Already done in the `renderFrame` function
2019-02-14 13:49:31 +01:00
Frederic Junod
40605d7c53 Don't use loadImage function to avoid infinite loading loop 2019-02-14 13:47:52 +01:00
Frédéric Junod
666c14d190 Merge pull request #9212 from fredj/cleanup
Remove unnecessary type cast
2019-02-14 09:41:15 +01:00
Andreas Hocevar
0a005527e7 Merge pull request #9207 from openlayers/greenkeeper/yargs-13.1.0
Update yargs to the latest version 🚀
2019-02-14 07:46:17 +01:00
Frederic Junod
c0a860a31f Remove unnecessary type cast 2019-02-13 11:42:22 +01:00
greenkeeper[bot]
3e82da4beb chore(package): update yargs to version 13.1.0 2019-02-12 06:35:53 +00:00
Andreas Hocevar
b71d7773d3 Merge pull request #9201 from ahocevar/drawable-child-range
Only consider child range with drawable tiles
2019-02-11 11:05:20 +01:00
Andreas Hocevar
7d5b2d8c3b Merge pull request #9204 from ahocevar/intellisense-notes-addendum
Add missing 'include' section
2019-02-10 22:50:32 +01:00
ahocevar
dc298b8895 Add missing 'include' section 2019-02-10 22:44:22 +01:00
Andreas Hocevar
693c763710 Merge pull request #9203 from ahocevar/intellisense-notes
Only promise what we can deliver regarding IntelliSense
2019-02-10 22:42:42 +01:00
Andreas Hocevar
680f3cde3c Merge pull request #9200 from ahocevar/vectorimage-note
Add upgrade note about renderMode: 'image' for vector layers
2019-02-10 09:55:25 +01:00
ahocevar
3ef6635c09 Only promise what we can deliver regarding IntelliSense 2019-02-10 09:10:01 +01:00
ahocevar
8028ce3ac5 Add upgrade note about renderMode: 'image' for vector layers 2019-02-10 08:47:16 +01:00
ahocevar
3557a13147 Only consider child range with drawable tiles 2019-02-09 19:35:31 +01:00
Frédéric Junod
f6e625f21a Merge pull request #9197 from fredj/cleanup
Remove unused opt_this param
2019-02-08 08:43:31 +01:00
Andreas Hocevar
d1c71c3c61 Merge pull request #9196 from ahocevar/pointresolution-scaleline
getPointResolution returns resolution in projection units
2019-02-07 15:32:32 +01:00
Frederic Junod
6f8ffddace Remove opt_this param in manageTilePyramid function 2019-02-07 13:18:52 +01:00
Frederic Junod
438736068e Remove opt_this param in forEach function 2019-02-07 13:07:36 +01:00
Andreas Hocevar
bfe8ee5309 Merge pull request #9195 from openlayers/greenkeeper/handlebars-4.1.0
Update handlebars to the latest version 🚀
2019-02-07 12:50:05 +01:00
ahocevar
703dadfcde Fix ScaleLine control now that getPointResolution works correctly 2019-02-07 12:26:18 +01:00
ahocevar
fecb8de769 getPointResolution returns proj units 2019-02-07 12:25:37 +01:00
ahocevar
b3bc78daec Provide center in the correct projection 2019-02-07 12:23:45 +01:00
ahocevar
d24ae3c2ac Fix expectation for degree point resolution 2019-02-07 12:22:18 +01:00
Frederic Junod
2879c0b6ad Remove opt_this param in forDataAtCoordinate function 2019-02-07 11:51:28 +01:00
greenkeeper[bot]
5ef1b51c02 chore(package): update handlebars to version 4.1.0 2019-02-07 10:45:15 +00:00
Frederic Junod
41d231a4c7 Remove opt_this param in forEachTileCoordParentTileRange function
The function is only called by `ol/renderer/canvas/TileLayer` and `opt_this` was always `null`.
2019-02-07 11:43:52 +01:00
Frédéric Junod
f4fe1babd2 Merge pull request #9194 from openlayers/greenkeeper/webpack-4.29.3
chore(package): update webpack to version 4.29.3
2019-02-07 11:08:44 +01:00
greenkeeper[bot]
efc85ed0ed chore(package): update webpack to version 4.29.3
Closes #9192
2019-02-07 09:29:38 +00:00
Andreas Hocevar
be16c2357d Merge pull request #9190 from romanzoller/multi-polygon-area
Fix MultiPolygon area calculation
2019-02-05 20:43:37 +01:00
Andreas Hocevar
88c213078e Merge pull request #9179 from gberaudo/allow_image_declutter
Allow declutter with image render mode
2019-02-05 20:29:15 +01:00
Roman Zoller
ee57b197e5 Fix offset passed from linearRingssAreOriented to linearRingsAreOriented
The offset needs to be set to the end of the previous Polygon,
see `offset = orientLinearRings(...)` in function orientLinearRingsArray

Fixes #9189
2019-02-05 17:56:52 +01:00
Roman Zoller
2c859b1196 Rename misnamed functions in geom/flat/orient
- Rename linearRingIsOriented => linearRingsAreOriented
  The function checks all linear rings of a Polygon, so
  the plural "rings" is more appropriate
- Rename linearRingsAreOriented => linearRingssAreOriented
  The double s is appropriate because the check is done for
  all Polygons of a MultiPolygon

This commit restores the function names from OpenLayers v4,
they were changed (wrongly IMHO) in #7820.
2019-02-05 17:49:21 +01:00
Roman Zoller
c4be22b1b6 Add failing test for MultiPolygon#getArea 2019-02-05 17:48:42 +01:00
Guillaume Beraudo
b00b877ca1 Update upgrade notes 2019-02-05 17:25:00 +01:00
Guillaume Beraudo
168edac4a6 Allow declutter with image render mode
Using declutter in image render mode is legitimate: each tile is
decluttered, avoiding symbol/text overlaps.
2019-02-05 17:25:00 +01:00
Frédéric Junod
caa9153dd0 Merge pull request #9187 from fredj/cleanup
Remove unnecessary type cast
2019-02-05 16:36:16 +01:00
Frederic Junod
ba02320fcc Remove unused layerState param 2019-02-05 11:46:12 +01:00
Frederic Junod
427c73ddf9 Remove unnecessary type cast
`npx tsc` output is the same before and after the changes.
2019-02-05 11:46:12 +01:00
Frédéric Junod
2c3ffdfdf8 Merge pull request #9186 from fredj/cleanup
Simplify typing in EsriJSON format
2019-02-05 10:57:36 +01:00
Frederic Junod
97b21145c4 Simplify typing in EsriJSON format 2019-02-05 10:27:26 +01:00
Frédéric Junod
2428c0984e Merge pull request #9161 from fredj/template_type
Use type template for the source type of layers
2019-02-05 10:07:52 +01:00
Frédéric Junod
e0aad192af Merge pull request #9184 from fredj/devDependencies
Update all dev dependencies
2019-02-05 09:22:03 +01:00
Frédéric Junod
e34806ad70 Merge pull request #9185 from openlayers/greenkeeper/webpack-4.29.1
Update webpack to the latest version 🚀
2019-02-05 09:21:22 +01:00
greenkeeper[bot]
a0fde2b24d chore(package): update webpack to version 4.29.1 2019-02-04 18:43:37 +00:00
Frederic Junod
abfe6b4359 Update jsdoc-plugin-typescript to version 1.0.7 2019-02-04 16:27:25 +01:00
greenkeeper[bot]
acd8c34535 chore(package): update terser-webpack-plugin to version 1.2.2 2019-02-04 16:14:46 +01:00
greenkeeper[bot]
1a1c7dec87 chore(package): update puppeteer to version 1.12.1 2019-02-04 16:14:36 +01:00
greenkeeper[bot]
5e4ee77968 chore(package): update eslint to version 5.13.0 2019-02-04 16:14:28 +01:00
greenkeeper[bot]
9f54e9cb4d chore(package): update ol-mapbox-style to version 3.6.4 2019-02-04 16:14:17 +01:00
Frederic Junod
0b53a3229b Remove unneeded type cast 2019-02-04 09:58:49 +01:00
Frederic Junod
5318d52036 Use type template for the source type of layers 2019-02-04 09:58:48 +01:00
Vincent Lecrubier
4d54ad0daf Update Zoomify.js 2019-01-12 13:44:17 +01:00
Vincent Lecrubier
37b1cca027 Add TilePixelRatio to Zoomify 2019-01-12 13:23:18 +01:00
140 changed files with 2520 additions and 1779 deletions

View File

@@ -41,14 +41,13 @@ See the following examples for more detail on bundling OpenLayers with your appl
* Using [Parcel](https://github.com/openlayers/ol-parcel) * Using [Parcel](https://github.com/openlayers/ol-parcel)
* Using [Browserify](https://github.com/openlayers/ol-browserify) * Using [Browserify](https://github.com/openlayers/ol-browserify)
## TypeScript and VS Code IntelliSense support ## IntelliSense support and type checking for VS Code
The `ol` package contains a `src/` folder with JSDoc annotated sources. TypeScript can get type definitions from these sources with a `tsconfig.json` like this: The `ol` package contains a `src/` folder with JSDoc annotated sources. TypeScript can get type definitions from these sources with a `jsconfig.json` config file in the project root:
```js ```js
{ {
"compilerOptions": { "compilerOptions": {
// Enable JavaScript support "checkJs": true,
"allowJs": true,
// Point to the JSDoc typed sources when using modules from the ol package // Point to the JSDoc typed sources when using modules from the ol package
"baseUrl": "./", "baseUrl": "./",
"paths": { "paths": {
@@ -57,11 +56,14 @@ The `ol` package contains a `src/` folder with JSDoc annotated sources. TypeScri
} }
}, },
"include": [ "include": [
// Include JavaScript files from the ol package "**/*.js",
"node_modules/ol/**/*.js" "node_modules/ol/**/*.js"
] ]
} }
``` ```
Project template with this configuration: https://gist.github.com/9a7253cb4712e8bf38d75d8ac898e36c.
Note that the above only works when authoring in plain JavaScript. For similar configurations with a `tsconfig.json` in TypeScript projects, your mileage may vary.
## Supported Browsers ## Supported Browsers

View File

@@ -4,11 +4,31 @@
#### Backwards incompatible changes #### Backwards incompatible changes
##### Removal of the deprecated "inherits" function ##### The `setCenter`, `setZoom`, `setResolution` and `setRotation` methods on `ol/View` do not bypass constraints anymore
Previously, these methods allowed setting values that were inconsistent with the given view constraints.
This is no longer the case and all changes to the view state now follow the same logic:
target values are provided and constraints are applied on these to determine the actual values to be used.
##### Removal of the `constrainResolution` option on `View.fit`, `PinchZoom`, `MouseWheelZoom` and `ol/interaction.js`
The `constrainResolution` option is now only supported by the `View` class. A `View.setResolutionConstrained` method was added as well.
Generally, the responsibility of applying center/rotation/resolutions constraints was moved from interactions and controls to the `View` class.
##### The view `extent` option now applies to the whole viewport
Previously, this options only constrained the view *center*. This behaviour can still be obtained by specifying `constrainCenterOnly` in the view options.
As a side effect, the view `rotate` method is gone and has been replaced with `adjustRotation` which takes a delta as input.
##### Removal of deprecated methods
The `inherits` function that was used to inherit the prototype methods from one constructor into another has been removed. The `inherits` function that was used to inherit the prototype methods from one constructor into another has been removed.
The standard ECMAScript classes should be used instead. The standard ECMAScript classes should be used instead.
The deprecated `getSnapToPixel` and `setSnapToPixel` functions from the `ImageStyle` class have been removed.
##### New internal tile coordinates ##### New internal tile coordinates
Previously, the internal tile coordinates used in the library had an unusual row order the origin of the tile coordinate system was at the top left as expected, but the rows increased upwards. This meant that all tile coordinates within a tile grid's extent had negative `y` values. Previously, the internal tile coordinates used in the library had an unusual row order the origin of the tile coordinate system was at the top left as expected, but the rows increased upwards. This meant that all tile coordinates within a tile grid's extent had negative `y` values.
@@ -60,6 +80,10 @@ In addition (this should be exceedingly rare), if you previously created a `ol/t
If you were previously using `VectorTile` layers with `renderMode: 'vector'`, you have to remove this configuration option. That mode was removed. `'hybrid'` (default) and `'image'` are still available. If you were previously using `VectorTile` layers with `renderMode: 'vector'`, you have to remove this configuration option. That mode was removed. `'hybrid'` (default) and `'image'` are still available.
##### Removal of the "renderMode" option for vector layers
If you were previously using `Vector` layers with `renderMode: 'image'`, you have to remove this configuration option. Instead, use the new `ol/layer/VectorImage` layer with your `ol/source/Vector`.
##### New `prerender` and `postrender` layer events replace old `precompose`, `render` and `postcompose` events ##### New `prerender` and `postrender` layer events replace old `precompose`, `render` and `postcompose` events
If you were previously registering for `precompose` and `postcompose` events, you should now register for `prerender` and `postrender` events on layers. Instead of the previous `render` event, you should now listen for `postrender`. Layers are no longer composed to a single Canvas element. Instead, they are added to the map viewport as individual elements. If you were previously registering for `precompose` and `postcompose` events, you should now register for `prerender` and `postrender` events on layers. Instead of the previous `render` event, you should now listen for `postrender`. Layers are no longer composed to a single Canvas element. Instead, they are added to the map viewport as individual elements.
@@ -93,6 +117,58 @@ Due to the constraint above (layers can only be added to a single map), the over
Previously, a graticule was not a layer. Now it is. See the graticule example for details on how to add a graticule layer to your map. Previously, a graticule was not a layer. Now it is. See the graticule example for details on how to add a graticule layer to your map.
##### `ol/format/Feature` API change
The `getLastExtent()` method, which was required for custom `tileLoadFunction`s in `ol/source/Vector`, has been removed because it is no longer needed (see below).
##### `ol/VectorTile` API changes
* Removal of the `getProjection()` and `setProjection()` methods. These were used in custom `tileLoadFunction`s on `ol/source/VectorTile`, which work differently now (see below).
* Removal of the `getExtent()` and `setExtent()` methods. These were used in custom `tileLoadFunction`s on `ol/source/VectorTile`, which work differently now (see below).
##### Custom tileLoadFunction on a VectorTile source needs changes
Previously, applications needed to call `setProjection()` and `setExtent()` on the tile in a custom `tileLoadFunction` on `ol/source/VectorTile`. The format's `getLastExtent()` method was used to get the extent. All this is no longer needed. Instead, the `extent` (first argument to the loader function) and `projection` (third argument to the loader function) are simply passed as `extent` and `featureProjection` options to the format's `readFeatures()` method.
Example for an old `tileLoadFunction`:
```js
function(tile, url) {
tile.setLoader(function() {
fetch(url).then(function(response) {
response.arrayBuffer().then(function(data) {
var format = tile.getFormat();
tile.setProjection(format.readProjection(data));
tile.setFeatures(format.readFeatures(data, {
// featureProjection is not required for ol/format/MVT
featureProjection: map.getView().getProjection()
}));
tile.setExtent(format.getLastExtent());
})
})
}
});
```
This function needs to be changed to:
```js
function(tile, url) {
tile.setLoader(function(extent, resolution, projection) {
fetch(url).then(function(response) {
response.arrayBuffer().then(function(data) {
var format = tile.getFormat();
tile.setFeatures(format.readFeatures(data, {
// extent is only required for ol/format/MVT
extent: extent,
featureProjection: projection
}));
})
})
}
});
```
##### Drop of support for the experimental WebGL renderer ##### Drop of support for the experimental WebGL renderer
The WebGL map and layers renderers are gone, replaced by a `WebGLHelper` function that provides a lightweight, The WebGL map and layers renderers are gone, replaced by a `WebGLHelper` function that provides a lightweight,
@@ -118,8 +194,19 @@ The removed classes and components are:
Following the removal of the experimental WebGL renderer, the AtlasManager has been removed as well. The atlas was only used by this renderer. Following the removal of the experimental WebGL renderer, the AtlasManager has been removed as well. The atlas was only used by this renderer.
The non API `getChecksum` functions of the style is also removed. The non API `getChecksum` functions of the style is also removed.
##### Change of the behavior of the vector source's clear() and refresh() methods
The `ol/source/Vector#clear()` method no longer triggers a reload of the data from the server. If you were previously using `clear()` to refetch from the server, you now have to use `refresh()`.
The `ol/source/Vector#refresh()` method now removes all features from the source and triggers a reload of the data from the server. If you were previously using the `refresh()` method to re-render a vector layer, you should instead call `ol/layer/Vector#changed()`.
#### Other changes #### Other changes
##### Allow declutter in image render mode
It is now possible to configure vector tile layers with `declutter: true` and `renderMode: 'image'`. However, note that decluttering will be done per tile, resulting in labels and point symbols getting cut off at tile boundaries.
Until now, using both options forced the render mode to be `hybrid`.
##### Always load tiles while animating or interacting ##### Always load tiles while animating or interacting
`ol/PluggableMap` and subclasses no longer support the `loadTilesWhileAnimating` and `loadTilesWhileInteracting` options. These options were used to enable tile loading during animations and interactions. With the new DOM composition render strategy, it is no longer necessary to postpone tile loading until after animations or interactions. `ol/PluggableMap` and subclasses no longer support the `loadTilesWhileAnimating` and `loadTilesWhileInteracting` options. These options were used to enable tile loading during animations and interactions. With the new DOM composition render strategy, it is no longer necessary to postpone tile loading until after animations or interactions.

View File

@@ -37,7 +37,7 @@
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */ /* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */

View File

@@ -21,7 +21,7 @@ Table of contents:
* [Why aren't there any features in my source?](#why-aren-t-there-any-features-in-my-source-) * [Why aren't there any features in my source?](#why-aren-t-there-any-features-in-my-source-)
* [How do I force a re-render of the map?](#how-do-i-force-a-re-render-of-the-map-) * [How do I force a re-render of the map?](#how-do-i-force-a-re-render-of-the-map-)
* [Why are my features not found?](#why-are-my-features-not-found-) * [Why are my features not found?](#why-are-my-features-not-found-)
* [Why is zooming or clicking off, inaccurate?](#user-content-why-is-zooming-or-clicking-off-inaccurate)
## What projection is OpenLayers using? ## What projection is OpenLayers using?
@@ -371,3 +371,30 @@ const vectorLayer = new VectorLayer({
``` ```
The recommended value is the size of the largest symbol, line width or label. The recommended value is the size of the largest symbol, line width or label.
## Why is zooming or clicking in the map off/inaccurate?
OpenLayers does not update the map when the container element is resized. This can be caused by progressive updates
to CSS styles or manually resizing the map. When that happens, any interaction will become inaccurate: the map would zoom in and out, and end up not being centered on the pointer. This makes it hard to do certain interactions, e.g. selecting the desired feature.
There is currently no built-in way to react to element's size changes, as [Resize Observer API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) is only implemented in Chrome.
There is however an easy to use [polyfill](https://github.com/que-etc/resize-observer-polyfill):
```javascript
import Map from 'ol/Map';
import ResizeObserver from 'resize-observer-polyfill';
const mapElement = document.querySelector('#map')
const map = new Map({
target: mapElement
})
const sizeObserver = new ResizeObserver(() => {
map.updateSize()
})
sizeObserver.observe(mapElement)
// called when the map is destroyed
// sizeObserver.disconnect()
```

View File

@@ -12,9 +12,8 @@ cloak:
<div id="map" class="map"></div> <div id="map" class="map"></div>
<select id="layer-select"> <select id="layer-select">
<option value="Aerial">Aerial</option> <option value="Aerial">Aerial</option>
<option value="AerialWithLabels" selected>Aerial with labels</option> <option value="AerialWithLabelsOnDemand" selected>Aerial with labels</option>
<option value="Road">Road (static)</option> <option value="RoadOnDemand">Road</option>
<option value="RoadOnDemand">Road (dynamic)</option> <option value="CanvasDark">Road dark</option>
<option value="collinsBart">Collins Bart</option> <option value="OrdnanceSurvey">Ordnance Survey</option>
<option value="ordnanceSurvey">Ordnance Survey</option>
</select> </select>

View File

@@ -5,12 +5,11 @@ import BingMaps from '../src/ol/source/BingMaps.js';
const styles = [ const styles = [
'Road',
'RoadOnDemand', 'RoadOnDemand',
'Aerial', 'Aerial',
'AerialWithLabels', 'AerialWithLabelsOnDemand',
'collinsBart', 'CanvasDark',
'ordnanceSurvey' 'OrdnanceSurvey'
]; ];
const layers = []; const layers = [];
let i, ii; let i, ii;

View File

@@ -21,8 +21,6 @@ tags: "center, rotation, openstreetmap"
<div class="padding-bottom"></div> <div class="padding-bottom"></div>
<div class="center"></div> <div class="center"></div>
</div> </div>
<button id="zoomtoswitzerlandbest">Zoom to Switzerland</button> (best fit),<br/> <button id="zoomtoswitzerland">Zoom to Switzerland</button> (best fit).<br/>
<button id="zoomtoswitzerlandconstrained">Zoom to Switzerland</button> (respect resolution constraint).<br/>
<button id="zoomtoswitzerlandnearest">Zoom to Switzerland</button> (nearest),<br/>
<button id="zoomtolausanne">Zoom to Lausanne</button> (with min resolution),<br/> <button id="zoomtolausanne">Zoom to Lausanne</button> (with min resolution),<br/>
<button id="centerlausanne">Center on Lausanne</button> <button id="centerlausanne">Center on Lausanne</button>

View File

@@ -47,29 +47,14 @@ const map = new Map({
view: view view: view
}); });
const zoomtoswitzerlandbest = document.getElementById('zoomtoswitzerlandbest'); const zoomtoswitzerland =
zoomtoswitzerlandbest.addEventListener('click', function() { document.getElementById('zoomtoswitzerland');
const feature = source.getFeatures()[0]; zoomtoswitzerland.addEventListener('click', function() {
const polygon = /** @type {import("../src/ol/geom/SimpleGeometry.js").default} */ (feature.getGeometry());
view.fit(polygon, {padding: [170, 50, 30, 150], constrainResolution: false});
}, false);
const zoomtoswitzerlandconstrained =
document.getElementById('zoomtoswitzerlandconstrained');
zoomtoswitzerlandconstrained.addEventListener('click', function() {
const feature = source.getFeatures()[0]; const feature = source.getFeatures()[0];
const polygon = /** @type {import("../src/ol/geom/SimpleGeometry.js").default} */ (feature.getGeometry()); const polygon = /** @type {import("../src/ol/geom/SimpleGeometry.js").default} */ (feature.getGeometry());
view.fit(polygon, {padding: [170, 50, 30, 150]}); view.fit(polygon, {padding: [170, 50, 30, 150]});
}, false); }, false);
const zoomtoswitzerlandnearest =
document.getElementById('zoomtoswitzerlandnearest');
zoomtoswitzerlandnearest.addEventListener('click', function() {
const feature = source.getFeatures()[0];
const polygon = /** @type {import("../src/ol/geom/SimpleGeometry.js").default} */ (feature.getGeometry());
view.fit(polygon, {padding: [170, 50, 30, 150], nearest: true});
}, false);
const zoomtolausanne = document.getElementById('zoomtolausanne'); const zoomtolausanne = document.getElementById('zoomtolausanne');
zoomtolausanne.addEventListener('click', function() { zoomtolausanne.addEventListener('click', function() {
const feature = source.getFeatures()[1]; const feature = source.getFeatures()[1];

View File

@@ -26,9 +26,8 @@ const styleFunction = function(feature) {
scale = size / 10; scale = size / 10;
let style = styleCache[size]; let style = styleCache[size];
if (!style) { if (!style) {
const canvas = /** @type {HTMLCanvasElement} */ (document.createElement('canvas')); const canvas = document.createElement('canvas');
const vectorContext = toContext( const vectorContext = toContext(canvas.getContext('2d'),
/** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')),
{size: [size, size], pixelRatio: 1}); {size: [size, size], pixelRatio: 1});
vectorContext.setStyle(new Style({ vectorContext.setStyle(new Style({
fill: new Fill({color: 'rgba(255, 153, 0, 0.4)'}), fill: new Fill({color: 'rgba(255, 153, 0, 0.4)'}),

View File

@@ -63,7 +63,10 @@ exportButton.addEventListener('click', function() {
pdf.save('map.pdf'); pdf.save('map.pdf');
// Reset original map size // Reset original map size
map.setSize(size); map.setSize(size);
map.getView().fit(extent, {size}); map.getView().fit(extent, {
size: size,
constrainResolution: false
});
exportButton.disabled = false; exportButton.disabled = false;
document.body.style.cursor = 'auto'; document.body.style.cursor = 'auto';
}); });

View File

@@ -0,0 +1,9 @@
---
layout: example.html
title: Constrained Extent
shortdesc: Example of a view with a constrained extent.
docs: >
This map has a view that is constrained in an extent. This is done using the `extent` view option. Please note that specifying `constrainOnlyCenter: true` would only apply the extent restriction to the view center.
tags: "view, extent, constrain, restrict"
---
<div id="map" class="map"></div>

View File

@@ -0,0 +1,25 @@
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 {defaults as defaultControls} from '../src/ol/control/util';
import ZoomSlider from '../src/ol/control/ZoomSlider';
const view = new View({
center: [328627.563458, 5921296.662223],
zoom: 8,
extent: [-572513.341856, 5211017.966314,
916327.095083, 6636950.728974]
});
new Map({
layers: [
new TileLayer({
source: new OSM()
})
],
keyboardEventTarget: document,
target: 'map',
view: view,
controls: defaultControls().extend([new ZoomSlider()])
});

View File

@@ -133,7 +133,7 @@ const map = new Map({
layers: [ layers: [
new TileLayer({ new TileLayer({
source: new BingMaps({ source: new BingMaps({
imagerySet: 'AerialWithLabels', imagerySet: 'AerialWithLabelsOnDemand',
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5' key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
}) })
}), }),

View File

@@ -1,6 +1,3 @@
.map:-moz-full-screen {
height: 100%;
}
.map:-webkit-full-screen { .map:-webkit-full-screen {
height: 100%; height: 100%;
} }

View File

@@ -1,6 +1,3 @@
.fullscreen:-moz-full-screen {
height: 100%;
}
.fullscreen:-webkit-full-screen { .fullscreen:-webkit-full-screen {
height: 100%; height: 100%;
} }

View File

@@ -1,6 +1,3 @@
.map:-moz-full-screen {
height: 100%;
}
.map:-webkit-full-screen { .map:-webkit-full-screen {
height: 100%; height: 100%;
} }

View File

@@ -33,8 +33,11 @@ map.on('singleclick', function(evt) {
evt.coordinate, viewResolution, 'EPSG:3857', evt.coordinate, viewResolution, 'EPSG:3857',
{'INFO_FORMAT': 'text/html'}); {'INFO_FORMAT': 'text/html'});
if (url) { if (url) {
document.getElementById('info').innerHTML = fetch(url)
'<iframe seamless src="' + url + '"></iframe>'; .then((response) => response.text())
.then((html) => {
document.getElementById('info').innerHTML = html;
});
} }
}); });

View File

@@ -33,8 +33,11 @@ map.on('singleclick', function(evt) {
evt.coordinate, viewResolution, 'EPSG:3857', evt.coordinate, viewResolution, 'EPSG:3857',
{'INFO_FORMAT': 'text/html'}); {'INFO_FORMAT': 'text/html'});
if (url) { if (url) {
document.getElementById('info').innerHTML = fetch(url)
'<iframe seamless src="' + url + '"></iframe>'; .then((response) => response.text())
.then((html) => {
document.getElementById('info').innerHTML = html;
});
} }
}); });

View File

@@ -4,16 +4,11 @@ title: Interaction Options
shortdesc: Shows interaction options for custom scroll and zoom behavior. shortdesc: Shows interaction options for custom scroll and zoom behavior.
docs: > docs: >
This example uses a custom `ol/interaction/defaults` configuration: This example uses a custom `ol/interaction/defaults` configuration:
by default, wheel/trackpad zoom and drag panning is always active, which
* By default, wheel/trackpad zoom and drag panning is always active, which can be unexpected on pages with a lot of scrollable content and an embedded
can be unexpected on pages with a lot of scrollable content and an embedded map. To perform wheel/trackpad zoom and drag-pan actions only when the map
map. To perform wheel/trackpad zoom and drag-pan actions only when the map has the focus, set `onFocusOnly: true` as option. This requires a map div
has the focus, set `onFocusOnly: true` as option. This requires a map div with a `tabindex` attribute set.
with a `tabindex` attribute set.
* By default, pinch-zoom and wheel/trackpad zoom interactions can leave the
map at fractional zoom levels. If instead you want to constrain
wheel/trackpad zooming to integer zoom levels, set
`constrainResolution: true`.
tags: "trackpad, mousewheel, zoom, scroll, interaction, fractional" tags: "trackpad, mousewheel, zoom, scroll, interaction, fractional"
--- ---
<div tabindex="1" id="map" class="map"></div> <div tabindex="1" id="map" class="map"></div>

View File

@@ -7,7 +7,7 @@ import OSM from '../src/ol/source/OSM.js';
const map = new Map({ const map = new Map({
interactions: defaultInteractions({ interactions: defaultInteractions({
constrainResolution: true, onFocusOnly: true onFocusOnly: true
}), }),
layers: [ layers: [
new TileLayer({ new TileLayer({

View File

@@ -7,7 +7,7 @@ import BingMaps from '../src/ol/source/BingMaps.js';
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'; const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5';
const roads = new TileLayer({ const roads = new TileLayer({
source: new BingMaps({key: key, imagerySet: 'Road'}) source: new BingMaps({key: key, imagerySet: 'RoadOnDemand'})
}); });
const imagery = new TileLayer({ const imagery = new TileLayer({

View File

@@ -203,7 +203,11 @@ const map = new Map({
target: 'map', target: 'map',
view: new View({ view: new View({
center: [-10997148, 4569099], center: [-10997148, 4569099],
zoom: 4 zoom: 4,
minZoom: 1,
extent: [-Infinity, -20048966.10, Infinity, 20048966.10],
smoothExtentConstraint: false,
smoothResolutionConstraint: false
}) })
}); });

View File

@@ -8,7 +8,7 @@ tags: "mapbox, vector, tiles, mobile"
resources: resources:
- resources/mapbox-streets-v6-style.js - resources/mapbox-streets-v6-style.js
cloak: cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg - key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from http://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -8,7 +8,7 @@ import {Fill, Icon, Stroke, Style, Text} from '../src/ol/style.js';
import TileGrid from '../src/ol/tilegrid/TileGrid.js'; import TileGrid from '../src/ol/tilegrid/TileGrid.js';
const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg'; const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q';
// Calculation of resolutions that match zoom levels 1, 3, 5, 7, 9, 11, 13, 15. // Calculation of resolutions that match zoom levels 1, 3, 5, 7, 9, 11, 13, 15.
const resolutions = []; const resolutions = [];

View File

@@ -8,7 +8,7 @@ tags: "simple, mapbox, vector, tiles"
resources: resources:
- resources/mapbox-streets-v6-style.js - resources/mapbox-streets-v6-style.js
cloak: cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg - key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from http://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -6,7 +6,7 @@ import VectorTileSource from '../src/ol/source/VectorTile.js';
import {Fill, Icon, Stroke, Style, Text} from '../src/ol/style.js'; import {Fill, Icon, Stroke, Style, Text} from '../src/ol/style.js';
const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg'; const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q';
const map = new Map({ const map = new Map({
layers: [ layers: [

View File

@@ -14,6 +14,7 @@ cloak:
<meta http-equiv="X-UA-Compatible" content="chrome=1"> <meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width"> <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<title>Mobile full screen example</title> <title>Mobile full screen example</title>
<link rel="stylesheet" href="../css/ol.css" type="text/css">
<style type="text/css"> <style type="text/css">
html, body, .map { html, body, .map {
margin: 0; margin: 0;

View File

@@ -15,7 +15,7 @@ const map = new Map({
new TileLayer({ new TileLayer({
source: new BingMaps({ source: new BingMaps({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
imagerySet: 'Road' imagerySet: 'RoadOnDemand'
}) })
}) })
], ],

View File

@@ -5,7 +5,7 @@ shortdesc: Restrict pinch zooming to integer zoom levels.
docs: > docs: >
By default, the `ol/interaction/PinchZoom` can leave the map at fractional zoom levels. By default, the `ol/interaction/PinchZoom` can leave the map at fractional zoom levels.
If instead you want to constrain pinch zooming to integer zoom levels, set If instead you want to constrain pinch zooming to integer zoom levels, set
<code>constrainResolution: true</code> when constructing the interaction. <code>constrainResolution: true</code> when constructing the view.
tags: "pinch, zoom, interaction" tags: "pinch, zoom, interaction"
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -6,10 +6,8 @@ import OSM from '../src/ol/source/OSM.js';
const map = new Map({ const map = new Map({
interactions: defaultInteractions({pinchZoom: false}).extend([ interactions: defaultInteractions().extend([
new PinchZoom({ new PinchZoom()
constrainResolution: true // force zooming to a integer zoom
})
]), ]),
layers: [ layers: [
new TileLayer({ new TileLayer({
@@ -19,6 +17,7 @@ const map = new Map({
target: 'map', target: 'map',
view: new View({ view: new View({
center: [0, 0], center: [0, 0],
zoom: 2 zoom: 2,
constrainResolution: true
}) })
}); });

View File

@@ -29,7 +29,7 @@ const map2 = new Map({
preload: 0, // default value preload: 0, // default value
source: new BingMaps({ source: new BingMaps({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
imagerySet: 'AerialWithLabels' imagerySet: 'AerialWithLabelsOnDemand'
}) })
}) })
], ],

View File

@@ -7,7 +7,7 @@ docs: >
Tiles made with [TileMill](http://tilemill.com). Hosting on MapBox.com or with open-source [TileServer](https://github.com/klokantech/tileserver-php/). Tiles made with [TileMill](http://tilemill.com). Hosting on MapBox.com or with open-source [TileServer](https://github.com/klokantech/tileserver-php/).
tags: "utfgrid, tilejson" tags: "utfgrid, tilejson"
cloak: cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg - key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from http://mapbox.com/ here value: Your Mapbox access token from http://mapbox.com/ here
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>

View File

@@ -5,7 +5,7 @@ import TileLayer from '../src/ol/layer/Tile.js';
import TileJSON from '../src/ol/source/TileJSON.js'; import TileJSON from '../src/ol/source/TileJSON.js';
import UTFGrid from '../src/ol/source/UTFGrid.js'; import UTFGrid from '../src/ol/source/UTFGrid.js';
const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg'; const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q';
const mapLayer = new TileLayer({ const mapLayer = new TileLayer({
source: new TileJSON({ source: new TileJSON({

View File

@@ -103,6 +103,9 @@ tags: "geojson, vector, openstreetmap, label"
<label>Size: </label> <label>Size: </label>
<input type="text" value="12px" id="points-size" /> <input type="text" value="12px" id="points-size" />
<br /> <br />
<label>Line height: </label>
<input type="text" value="1" id="points-height" />
<br />
<label>Offset X:</label> <label>Offset X:</label>
<input type="text" value="0" id="points-offset-x" /> <input type="text" value="0" id="points-offset-x" />
<br /> <br />
@@ -212,6 +215,9 @@ tags: "geojson, vector, openstreetmap, label"
<label>Size: </label> <label>Size: </label>
<input type="text" value="12px" id="lines-size" /> <input type="text" value="12px" id="lines-size" />
<br /> <br />
<label>Line height: </label>
<input type="text" value="1.2" id="lines-height" />
<br />
<label>Offset X:</label> <label>Offset X:</label>
<input type="text" value="0" id="lines-offset-x" /> <input type="text" value="0" id="lines-offset-x" />
<br /> <br />
@@ -321,6 +327,9 @@ tags: "geojson, vector, openstreetmap, label"
<label>Size: </label> <label>Size: </label>
<input type="text" value="10px" id="polygons-size" /> <input type="text" value="10px" id="polygons-size" />
<br /> <br />
<label>Line height: </label>
<input type="text" value="1" id="polygons-height" />
<br />
<label>Offset X:</label> <label>Offset X:</label>
<input type="text" value="0" id="polygons-offset-x" /> <input type="text" value="0" id="polygons-offset-x" />
<br /> <br />

View File

@@ -16,6 +16,7 @@ const myDom = {
font: document.getElementById('points-font'), font: document.getElementById('points-font'),
weight: document.getElementById('points-weight'), weight: document.getElementById('points-weight'),
size: document.getElementById('points-size'), size: document.getElementById('points-size'),
height: document.getElementById('points-height'),
offsetX: document.getElementById('points-offset-x'), offsetX: document.getElementById('points-offset-x'),
offsetY: document.getElementById('points-offset-y'), offsetY: document.getElementById('points-offset-y'),
color: document.getElementById('points-color'), color: document.getElementById('points-color'),
@@ -34,6 +35,7 @@ const myDom = {
maxangle: document.getElementById('lines-maxangle'), maxangle: document.getElementById('lines-maxangle'),
overflow: document.getElementById('lines-overflow'), overflow: document.getElementById('lines-overflow'),
size: document.getElementById('lines-size'), size: document.getElementById('lines-size'),
height: document.getElementById('lines-height'),
offsetX: document.getElementById('lines-offset-x'), offsetX: document.getElementById('lines-offset-x'),
offsetY: document.getElementById('lines-offset-y'), offsetY: document.getElementById('lines-offset-y'),
color: document.getElementById('lines-color'), color: document.getElementById('lines-color'),
@@ -52,6 +54,7 @@ const myDom = {
maxangle: document.getElementById('polygons-maxangle'), maxangle: document.getElementById('polygons-maxangle'),
overflow: document.getElementById('polygons-overflow'), overflow: document.getElementById('polygons-overflow'),
size: document.getElementById('polygons-size'), size: document.getElementById('polygons-size'),
height: document.getElementById('polygons-height'),
offsetX: document.getElementById('polygons-offset-x'), offsetX: document.getElementById('polygons-offset-x'),
offsetY: document.getElementById('polygons-offset-y'), offsetY: document.getElementById('polygons-offset-y'),
color: document.getElementById('polygons-color'), color: document.getElementById('polygons-color'),
@@ -84,6 +87,7 @@ const createTextStyle = function(feature, resolution, dom) {
const align = dom.align.value; const align = dom.align.value;
const baseline = dom.baseline.value; const baseline = dom.baseline.value;
const size = dom.size.value; const size = dom.size.value;
const height = dom.height.value;
const offsetX = parseInt(dom.offsetX.value, 10); const offsetX = parseInt(dom.offsetX.value, 10);
const offsetY = parseInt(dom.offsetY.value, 10); const offsetY = parseInt(dom.offsetY.value, 10);
const weight = dom.weight.value; const weight = dom.weight.value;
@@ -98,7 +102,7 @@ const createTextStyle = function(feature, resolution, dom) {
document.getElementsByTagName('head')[0].appendChild(openSans); document.getElementsByTagName('head')[0].appendChild(openSans);
openSansAdded = true; openSansAdded = true;
} }
const font = weight + ' ' + size + ' ' + dom.font.value; const font = weight + ' ' + size + '/' + height + ' ' + dom.font.value;
const fillColor = dom.color.value; const fillColor = dom.color.value;
const outlineColor = dom.outline.value; const outlineColor = dom.outline.value;
const outlineWidth = parseInt(dom.outlineWidth.value, 10); const outlineWidth = parseInt(dom.outlineWidth.value, 10);

View File

@@ -1,6 +1,6 @@
{ {
"name": "ol", "name": "ol",
"version": "5.3.0", "version": "6.0.0-beta.3",
"description": "OpenLayers mapping library", "description": "OpenLayers mapping library",
"keywords": [ "keywords": [
"map", "map",
@@ -36,35 +36,35 @@
"url": "https://github.com/openlayers/openlayers/issues" "url": "https://github.com/openlayers/openlayers/issues"
}, },
"dependencies": { "dependencies": {
"pbf": "3.1.0", "pbf": "3.2.0",
"pixelworks": "1.1.0", "pixelworks": "1.1.0",
"rbush": "2.0.2" "rbush": "2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@openlayers/eslint-plugin": "^4.0.0-beta.1", "@openlayers/eslint-plugin": "^4.0.0-beta.1",
"@types/arcgis-rest-api": "^10.4.3", "@types/arcgis-rest-api": "^10.4.4",
"@types/geojson": "^7946.0.5", "@types/geojson": "^7946.0.6",
"@types/pbf": "^3.0.1", "@types/pbf": "^3.0.1",
"@types/rbush": "^2.0.2", "@types/rbush": "^2.0.2",
"@types/topojson-specification": "^1.0.0", "@types/topojson-specification": "^1.0.1",
"buble": "^0.19.6", "buble": "^0.19.6",
"buble-loader": "^0.5.1", "buble-loader": "^0.5.1",
"chaikin-smooth": "^1.0.4", "chaikin-smooth": "^1.0.4",
"clean-css-cli": "4.2.1", "clean-css-cli": "4.2.1",
"copy-webpack-plugin": "^4.6.0", "copy-webpack-plugin": "^5.0.0",
"coveralls": "3.0.1", "coveralls": "3.0.3",
"eslint": "^5.8.0", "eslint": "^5.13.0",
"eslint-config-openlayers": "^11.0.0", "eslint-config-openlayers": "^11.0.0",
"expect.js": "0.3.1", "expect.js": "0.3.1",
"front-matter": "^3.0.1", "front-matter": "^3.0.1",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"glob": "^7.1.2", "glob": "^7.1.2",
"globby": "^8.0.1", "globby": "^9.1.0",
"handlebars": "4.0.11", "handlebars": "4.1.0",
"istanbul": "0.4.5", "istanbul": "0.4.5",
"jquery": "3.3.1", "jquery": "3.3.1",
"jsdoc": "3.5.5", "jsdoc": "3.5.5",
"jsdoc-plugin-typescript": "^1.0.6", "jsdoc-plugin-typescript": "^1.0.7",
"karma": "^4.0.0", "karma": "^4.0.0",
"karma-chrome-launcher": "2.2.0", "karma-chrome-launcher": "2.2.0",
"karma-coverage": "^1.1.2", "karma-coverage": "^1.1.2",
@@ -73,25 +73,25 @@
"karma-sourcemap-loader": "^0.3.7", "karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.0-rc.2", "karma-webpack": "^4.0.0-rc.2",
"loglevelnext": "^3.0.0", "loglevelnext": "^3.0.0",
"marked": "0.6.0", "marked": "0.6.1",
"mocha": "5.2.0", "mocha": "6.0.2",
"ol-mapbox-style": "^3.6.3", "ol-mapbox-style": "^4.1.0",
"pixelmatch": "^4.0.2", "pixelmatch": "^4.0.2",
"pngjs": "^3.3.3", "pngjs": "^3.3.3",
"proj4": "2.5.0", "proj4": "2.5.0",
"puppeteer": "^1.11.0", "puppeteer": "~1.11.0",
"serve-static": "^1.13.2", "serve-static": "^1.13.2",
"shx": "^0.3.2", "shx": "^0.3.2",
"sinon": "^7.2.3", "sinon": "^7.2.3",
"terser-webpack-plugin": "^1.2.1", "terser-webpack-plugin": "^1.2.2",
"typescript": "^3.2.2", "typescript": "^3.2.2",
"url-polyfill": "^1.1.3", "url-polyfill": "^1.1.3",
"walk": "^2.3.9", "walk": "^2.3.9",
"webpack": "4.29.0", "webpack": "4.29.6",
"webpack-cli": "^3.2.0", "webpack-cli": "^3.2.3",
"webpack-dev-middleware": "^3.5.1", "webpack-dev-middleware": "^3.5.2",
"webpack-dev-server": "^3.1.14", "webpack-dev-server": "^3.1.14",
"yargs": "^12.0.2" "yargs": "^13.2.0"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "openlayers", "extends": "openlayers",

View File

@@ -351,7 +351,7 @@ if (require.main === module) {
option('headless', { option('headless', {
describe: 'Launch Puppeteer in headless mode', describe: 'Launch Puppeteer in headless mode',
type: 'boolean', type: 'boolean',
default: process.env.CI ? false : true default: false
}). }).
option('puppeteer-args', { option('puppeteer-args', {
describe: 'Additional args for Puppeteer', describe: 'Additional args for Puppeteer',

View File

@@ -4,13 +4,6 @@
import Tile from './Tile.js'; import Tile from './Tile.js';
import TileState from './TileState.js'; import TileState from './TileState.js';
/**
* @const
* @type {import("./extent.js").Extent}
*/
const DEFAULT_EXTENT = [0, 0, 4096, 4096];
class VectorTile extends Tile { class VectorTile extends Tile {
/** /**
@@ -31,10 +24,10 @@ class VectorTile extends Tile {
this.consumers = 0; this.consumers = 0;
/** /**
* @private * Extent of this tile; set by the source.
* @type {import("./extent.js").Extent} * @type {import("./extent.js").Extent}
*/ */
this.extent_ = null; this.extent = null;
/** /**
* @private * @private
@@ -55,11 +48,16 @@ class VectorTile extends Tile {
this.loader_; this.loader_;
/** /**
* Data projection * Feature projection of this tile; set by the source.
* @private
* @type {import("./proj/Projection.js").default} * @type {import("./proj/Projection.js").default}
*/ */
this.projection_ = null; this.projection = null;
/**
* Resolution of this tile; set by the source.
* @type {number}
*/
this.resolution;
/** /**
* @private * @private
@@ -83,15 +81,6 @@ class VectorTile extends Tile {
super.disposeInternal(); super.disposeInternal();
} }
/**
* Gets the extent of the vector tile.
* @return {import("./extent.js").Extent} The extent.
* @api
*/
getExtent() {
return this.extent_ || DEFAULT_EXTENT;
}
/** /**
* Get the feature format assigned for reading this tile's features. * Get the feature format assigned for reading this tile's features.
* @return {import("./format/Feature.js").default} Feature format. * @return {import("./format/Feature.js").default} Feature format.
@@ -102,8 +91,7 @@ class VectorTile extends Tile {
} }
/** /**
* Get the features for this tile. Geometries will be in the projection returned * Get the features for this tile. Geometries will be in the view projection.
* by {@link module:ol/VectorTile~VectorTile#getProjection}.
* @return {Array<import("./Feature.js").FeatureLike>} Features. * @return {Array<import("./Feature.js").FeatureLike>} Features.
* @api * @api
*/ */
@@ -118,16 +106,6 @@ class VectorTile extends Tile {
return this.url_; return this.url_;
} }
/**
* Get the feature projection of features returned by
* {@link module:ol/VectorTile~VectorTile#getFeatures}.
* @return {import("./proj/Projection.js").default} Feature projection.
* @api
*/
getProjection() {
return this.projection_;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -135,7 +113,7 @@ class VectorTile extends Tile {
if (this.state == TileState.IDLE) { if (this.state == TileState.IDLE) {
this.setState(TileState.LOADING); this.setState(TileState.LOADING);
this.tileLoadFunction_(this, this.url_); this.tileLoadFunction_(this, this.url_);
this.loader_(null, NaN, null); this.loader_(this.extent, this.resolution, this.projection);
} }
} }
@@ -143,12 +121,9 @@ class VectorTile extends Tile {
* Handler for successful tile load. * Handler for successful tile load.
* @param {Array<import("./Feature.js").default>} features The loaded features. * @param {Array<import("./Feature.js").default>} features The loaded features.
* @param {import("./proj/Projection.js").default} dataProjection Data projection. * @param {import("./proj/Projection.js").default} dataProjection Data projection.
* @param {import("./extent.js").Extent} extent Extent.
*/ */
onLoad(features, dataProjection, extent) { onLoad(features, dataProjection) {
this.setProjection(dataProjection);
this.setFeatures(features); this.setFeatures(features);
this.setExtent(extent);
} }
/** /**
@@ -158,22 +133,6 @@ class VectorTile extends Tile {
this.setState(TileState.ERROR); this.setState(TileState.ERROR);
} }
/**
* Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s
* `tileLoadFunction`. Sets the extent of the vector tile. This is only required
* for tiles in projections with `tile-pixels` as units. The extent should be
* set to `[0, 0, tilePixelSize, tilePixelSize]`, where `tilePixelSize` is
* calculated by multiplying the tile size with the tile pixel ratio. For
* sources using {@link module:ol/format/MVT~MVT} as feature format, the
* {@link module:ol/format/MVT~MVT#getLastExtent} method will return the correct
* extent. The default is `[0, 0, 4096, 4096]`.
* @param {import("./extent.js").Extent} extent The extent.
* @api
*/
setExtent(extent) {
this.extent_ = extent;
}
/** /**
* Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`.
* Sets the features for the tile. * Sets the features for the tile.
@@ -185,17 +144,6 @@ class VectorTile extends Tile {
this.setState(TileState.LOADED); this.setState(TileState.LOADED);
} }
/**
* Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`.
* Sets the projection of the features that were added with
* {@link module:ol/VectorTile~VectorTile#setFeatures}.
* @param {import("./proj/Projection.js").default} projection Feature projection.
* @api
*/
setProjection(projection) {
this.projection_ = projection;
}
/** /**
* Set the feature loader for reading this tile's features. * Set the feature loader for reading this tile's features.
* @param {import("./featureloader.js").FeatureLoader} loader Feature loader. * @param {import("./featureloader.js").FeatureLoader} loader Feature loader.

View File

@@ -21,6 +21,9 @@ import {clamp, modulo} from './math.js';
import {assign} from './obj.js'; import {assign} from './obj.js';
import {createProjection, METERS_PER_UNIT} from './proj.js'; import {createProjection, METERS_PER_UNIT} from './proj.js';
import Units from './proj/Units.js'; import Units from './proj/Units.js';
import {equals} from './coordinate';
import {easeOut} from './easing';
import {createMinMaxResolution} from './resolutionconstraint';
/** /**
@@ -58,9 +61,8 @@ import Units from './proj/Units.js';
* @property {!Array<number>} [padding=[0, 0, 0, 0]] Padding (in pixels) to be * @property {!Array<number>} [padding=[0, 0, 0, 0]] Padding (in pixels) to be
* cleared inside the view. Values in the array are top, right, bottom and left * cleared inside the view. Values in the array are top, right, bottom and left
* padding. * padding.
* @property {boolean} [constrainResolution=true] Constrain the resolution. * @property {boolean} [nearest=false] If the view `constrainResolution` option is `true`,
* @property {boolean} [nearest=false] If `constrainResolution` is `true`, get * get the nearest extent instead of the closest that actually fits the view.
* the nearest extent instead of the closest that actually fits the view.
* @property {number} [minResolution=0] Minimum resolution that we zoom to. * @property {number} [minResolution=0] Minimum resolution that we zoom to.
* @property {number} [maxZoom] Maximum zoom level that we zoom to. If * @property {number} [maxZoom] Maximum zoom level that we zoom to. If
* `minResolution` is given, this property is ignored. * `minResolution` is given, this property is ignored.
@@ -92,7 +94,12 @@ import Units from './proj/Units.js';
* used. The `constrainRotation` option has no effect if `enableRotation` is * used. The `constrainRotation` option has no effect if `enableRotation` is
* `false`. * `false`.
* @property {import("./extent.js").Extent} [extent] The extent that constrains the * @property {import("./extent.js").Extent} [extent] The extent that constrains the
* center, in other words, center cannot be set outside this extent. * view, in other words, nothing outside of this extent can be visible on the map.
* @property {boolean} [constrainOnlyCenter=false] If true, the extent
* constraint will only apply to the view center and not the whole extent.
* @property {boolean} [smoothExtentConstraint=true] If true, the extent
* constraint will be applied smoothly, i.e. allow the view to go slightly outside
* of the given `extent`.
* @property {number} [maxResolution] The maximum resolution used to determine * @property {number} [maxResolution] The maximum resolution used to determine
* the resolution constraint. It is used together with `minResolution` (or * the resolution constraint. It is used together with `minResolution` (or
* `maxZoom`) and `zoomFactor`. If unspecified it is calculated in such a way * `maxZoom`) and `zoomFactor`. If unspecified it is calculated in such a way
@@ -113,6 +120,12 @@ import Units from './proj/Units.js';
* resolution constraint. It is used together with `maxZoom` (or * resolution constraint. It is used together with `maxZoom` (or
* `minResolution`) and `zoomFactor`. Note that if `maxResolution` is also * `minResolution`) and `zoomFactor`. Note that if `maxResolution` is also
* provided, it is given precedence over `minZoom`. * provided, it is given precedence over `minZoom`.
* @property {boolean} [constrainResolution=false] If true, the view will always
* animate to the closest zoom level after an interaction; false means
* intermediary zoom levels are allowed.
* @property {boolean} [smoothResolutionConstraint=true] If true, the resolution
* min/max values will be applied smoothly, i. e. allow the view to exceed slightly
* the given resolution or zoom bounds.
* @property {import("./proj.js").ProjectionLike} [projection='EPSG:3857'] The * @property {import("./proj.js").ProjectionLike} [projection='EPSG:3857'] The
* projection. The default is Spherical Mercator. * projection. The default is Spherical Mercator.
* @property {number} [resolution] The initial resolution for the view. The * @property {number} [resolution] The initial resolution for the view. The
@@ -126,10 +139,9 @@ import Units from './proj/Units.js';
* @property {number} [rotation=0] The initial rotation for the view in radians * @property {number} [rotation=0] The initial rotation for the view in radians
* (positive rotation clockwise, 0 means North). * (positive rotation clockwise, 0 means North).
* @property {number} [zoom] Only used if `resolution` is not defined. Zoom * @property {number} [zoom] Only used if `resolution` is not defined. Zoom
* level used to calculate the initial resolution for the view. The initial * level used to calculate the initial resolution for the view.
* resolution is determined using the {@link #constrainResolution} method. * @property {number} [zoomFactor=2] The zoom factor used to compute the
* @property {number} [zoomFactor=2] The zoom factor used to determine the * corresponding resolution.
* resolution constraint.
*/ */
@@ -143,7 +155,7 @@ import Units from './proj/Units.js';
* of the animation. If `zoom` is also provided, this option will be ignored. * of the animation. If `zoom` is also provided, this option will be ignored.
* @property {number} [rotation] The rotation of the view at the end of * @property {number} [rotation] The rotation of the view at the end of
* the animation. * the animation.
* @property {import("./coordinate.js").Coordinate} [anchor] Optional anchor to remained fixed * @property {import("./coordinate.js").Coordinate} [anchor] Optional anchor to remain fixed
* during a rotation or resolution animation. * during a rotation or resolution animation.
* @property {number} [duration=1000] The duration of the animation in milliseconds. * @property {number} [duration=1000] The duration of the animation in milliseconds.
* @property {function(number):number} [easing] The easing function used * @property {function(number):number} [easing] The easing function used
@@ -184,7 +196,12 @@ const DEFAULT_MIN_ZOOM = 0;
* and `rotation`. Each state has a corresponding getter and setter, e.g. * and `rotation`. Each state has a corresponding getter and setter, e.g.
* `getCenter` and `setCenter` for the `center` state. * `getCenter` and `setCenter` for the `center` state.
* *
* An View has a `projection`. The projection determines the * The `zoom` state is actually not saved on the view: all computations
* internally use the `resolution` state. Still, the `setZoom` and `getZoom`
* methods are available, as well as `getResolutionForZoom` and
* `getZoomForResolution` to switch from one system to the other.
*
* A View has a `projection`. The projection determines the
* coordinate system of the center, and its units determine the units of the * coordinate system of the center, and its units determine the units of the
* resolution (projection units per pixel). The default projection is * resolution (projection units per pixel). The default projection is
* Spherical Mercator (EPSG:3857). * Spherical Mercator (EPSG:3857).
@@ -192,28 +209,19 @@ const DEFAULT_MIN_ZOOM = 0;
* ### The constraints * ### The constraints
* *
* `setCenter`, `setResolution` and `setRotation` can be used to change the * `setCenter`, `setResolution` and `setRotation` can be used to change the
* states of the view. Any value can be passed to the setters. And the value * states of the view, but any constraint defined in the constructor will
* that is passed to a setter will effectively be the value set in the view, * be applied along the way.
* and returned by the corresponding getter.
* *
* But a View object also has a *resolution constraint*, a * A View object can have a *resolution constraint*, a *rotation constraint*
* *rotation constraint* and a *center constraint*. * and a *center constraint*.
* *
* As said above, no constraints are applied when the setters are used to set * The *resolution constraint* typically restricts min/max values and
* new states for the view. Applying constraints is done explicitly through * snaps to specific resolutions. It is determined by the following
* the use of the `constrain*` functions (`constrainResolution` and * options: `resolutions`, `maxResolution`, `maxZoom`, and `zoomFactor`.
* `constrainRotation` and `constrainCenter`). * If `resolutions` is set, the other three options are ignored. See
* * documentation for each option for more information. By default, the view
* The main users of the constraints are the interactions and the * only has a min/max restriction and allow intermediary zoom levels when
* controls. For example, double-clicking on the map changes the view to * pinch-zooming for example.
* the "next" resolution. And releasing the fingers after pinch-zooming
* snaps to the closest resolution (with an animation).
*
* The *resolution constraint* snaps to specific resolutions. It is
* determined by the following options: `resolutions`, `maxResolution`,
* `maxZoom`, and `zoomFactor`. If `resolutions` is set, the other three
* options are ignored. See documentation for each option for more
* information.
* *
* The *rotation constraint* snaps to specific angles. It is determined * The *rotation constraint* snaps to specific angles. It is determined
* by the following options: `enableRotation` and `constrainRotation`. * by the following options: `enableRotation` and `constrainRotation`.
@@ -221,9 +229,31 @@ const DEFAULT_MIN_ZOOM = 0;
* horizontal. * horizontal.
* *
* The *center constraint* is determined by the `extent` option. By * The *center constraint* is determined by the `extent` option. By
* default the center is not constrained at all. * default the view center is not constrained at all.
* *
* @api * ### Changing the view state
*
* It is important to note that `setZoom`, `setResolution`, `setCenter` and
* `setRotation` are subject to the above mentioned constraints. As such, it
* may sometimes not be possible to know in advance the resulting state of the
* View. For example, calling `setResolution(10)` does not guarantee that
* `getResolution()` will return `10`.
*
* A consequence of this is that, when applying a delta on the view state, one
* should use `adjustCenter`, `adjustRotation`, `adjustZoom` and `adjustResolution`
* rather than the corresponding setters. This will let view do its internal
* computations. Besides, the `adjust*` methods also take an `opt_anchor`
* argument which allows specifying an origin for the transformation.
*
* ### Interacting with the view
*
* View constraints are usually only applied when the view is *at rest*, meaning that
* no interaction or animation is ongoing. As such, if the user puts the view in a
* state that is not equivalent to a constrained one (e.g. rotating the view when
* the snap angle is 0), an animation will be triggered at the interaction end to
* put back the view to a stable state;
*
* @api
*/ */
class View extends BaseObject { class View extends BaseObject {
@@ -262,6 +292,24 @@ class View extends BaseObject {
*/ */
this.projection_ = createProjection(options.projection, 'EPSG:3857'); this.projection_ = createProjection(options.projection, 'EPSG:3857');
/**
* @private
* @type {import("./coordinate.js").Coordinate|undefined}
*/
this.targetCenter_ = null;
/**
* @private
* @type {number|undefined}
*/
this.targetResolution_;
/**
* @private
* @type {number|undefined}
*/
this.targetRotation_;
this.applyOptions_(options); this.applyOptions_(options);
} }
@@ -275,8 +323,6 @@ class View extends BaseObject {
* @type {Object<string, *>} * @type {Object<string, *>}
*/ */
const properties = {}; const properties = {};
properties[ViewProperty.CENTER] = options.center !== undefined ?
options.center : null;
const resolutionConstraintInfo = createResolutionConstraint(options); const resolutionConstraintInfo = createResolutionConstraint(options);
@@ -324,19 +370,20 @@ class View extends BaseObject {
rotation: rotationConstraint rotation: rotationConstraint
}; };
this.setRotation(options.rotation !== undefined ? options.rotation : 0);
this.setCenter(options.center !== undefined ? options.center : null);
if (options.resolution !== undefined) { if (options.resolution !== undefined) {
properties[ViewProperty.RESOLUTION] = options.resolution; this.setResolution(options.resolution);
} else if (options.zoom !== undefined) { } else if (options.zoom !== undefined) {
properties[ViewProperty.RESOLUTION] = this.constrainResolution(
this.maxResolution_, options.zoom - this.minZoom_);
if (this.resolutions_) { // in case map zoom is out of min/max zoom range if (this.resolutions_) { // in case map zoom is out of min/max zoom range
properties[ViewProperty.RESOLUTION] = clamp( const resolution = this.getResolutionForZoom(options.zoom);
Number(this.getResolution() || properties[ViewProperty.RESOLUTION]), this.setResolution(clamp(resolution,
this.minResolution_, this.maxResolution_); this.minResolution_, this.maxResolution_));
} else {
this.setZoom(options.zoom);
} }
} }
properties[ViewProperty.ROTATION] = options.rotation !== undefined ? options.rotation : 0;
this.setProperties(properties); this.setProperties(properties);
/** /**
@@ -432,9 +479,9 @@ class View extends BaseObject {
return; return;
} }
let start = Date.now(); let start = Date.now();
let center = this.getCenter().slice(); let center = this.targetCenter_.slice();
let resolution = this.getResolution(); let resolution = this.targetResolution_;
let rotation = this.getRotation(); let rotation = this.targetRotation_;
const series = []; const series = [];
for (let i = 0; i < animationCount; ++i) { for (let i = 0; i < animationCount; ++i) {
const options = /** @type {AnimationOptions} */ (arguments[i]); const options = /** @type {AnimationOptions} */ (arguments[i]);
@@ -450,14 +497,13 @@ class View extends BaseObject {
if (options.center) { if (options.center) {
animation.sourceCenter = center; animation.sourceCenter = center;
animation.targetCenter = options.center; animation.targetCenter = options.center.slice();
center = animation.targetCenter; center = animation.targetCenter;
} }
if (options.zoom !== undefined) { if (options.zoom !== undefined) {
animation.sourceResolution = resolution; animation.sourceResolution = resolution;
animation.targetResolution = this.constrainResolution( animation.targetResolution = this.getResolutionForZoom(options.zoom);
this.maxResolution_, options.zoom - this.minZoom_, 0);
resolution = animation.targetResolution; resolution = animation.targetResolution;
} else if (options.resolution) { } else if (options.resolution) {
animation.sourceResolution = resolution; animation.sourceResolution = resolution;
@@ -556,28 +602,31 @@ class View extends BaseObject {
const y1 = animation.targetCenter[1]; const y1 = animation.targetCenter[1];
const x = x0 + progress * (x1 - x0); const x = x0 + progress * (x1 - x0);
const y = y0 + progress * (y1 - y0); const y = y0 + progress * (y1 - y0);
this.set(ViewProperty.CENTER, [x, y]); this.targetCenter_ = [x, y];
} }
if (animation.sourceResolution && animation.targetResolution) { if (animation.sourceResolution && animation.targetResolution) {
const resolution = progress === 1 ? const resolution = progress === 1 ?
animation.targetResolution : animation.targetResolution :
animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution); animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);
if (animation.anchor) { if (animation.anchor) {
this.set(ViewProperty.CENTER, const size = this.getSizeFromViewport_(this.getRotation());
this.calculateCenterZoom(resolution, animation.anchor)); const constrainedResolution = this.constraints_.resolution(resolution, 0, size, true);
this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor);
} }
this.set(ViewProperty.RESOLUTION, resolution); this.targetResolution_ = resolution;
this.applyTargetState_(true);
} }
if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) { if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) {
const rotation = progress === 1 ? const rotation = progress === 1 ?
modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI : modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI :
animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation); animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation);
if (animation.anchor) { if (animation.anchor) {
this.set(ViewProperty.CENTER, const constrainedRotation = this.constraints_.rotation(rotation, true);
this.calculateCenterRotate(rotation, animation.anchor)); this.targetCenter_ = this.calculateCenterRotate(constrainedRotation, animation.anchor);
} }
this.set(ViewProperty.ROTATION, rotation); this.targetRotation_ = rotation;
} }
this.applyTargetState_(true);
more = true; more = true;
if (!animation.complete) { if (!animation.complete) {
break; break;
@@ -597,6 +646,10 @@ class View extends BaseObject {
if (more && this.updateAnimationKey_ === undefined) { if (more && this.updateAnimationKey_ === undefined) {
this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_); this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_);
} }
if (!this.getAnimating()) {
setTimeout(this.resolveConstraints_.bind(this), 0);
}
} }
/** /**
@@ -634,9 +687,10 @@ class View extends BaseObject {
/** /**
* @private * @private
* @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size
* @return {import("./size.js").Size} Viewport size or `[100, 100]` when no viewport is found. * @return {import("./size.js").Size} Viewport size or `[100, 100]` when no viewport is found.
*/ */
getSizeFromViewport_() { getSizeFromViewport_(opt_rotation) {
const size = [100, 100]; const size = [100, 100];
const selector = '.ol-viewport[data-view="' + getUid(this) + '"]'; const selector = '.ol-viewport[data-view="' + getUid(this) + '"]';
const element = document.querySelector(selector); const element = document.querySelector(selector);
@@ -645,45 +699,15 @@ class View extends BaseObject {
size[0] = parseInt(metrics.width, 10); size[0] = parseInt(metrics.width, 10);
size[1] = parseInt(metrics.height, 10); size[1] = parseInt(metrics.height, 10);
} }
if (opt_rotation) {
const w = size[0];
const h = size[1];
size[0] = Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation));
size[1] = Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation));
}
return size; return size;
} }
/**
* Get the constrained center of this view.
* @param {import("./coordinate.js").Coordinate|undefined} center Center.
* @return {import("./coordinate.js").Coordinate|undefined} Constrained center.
* @api
*/
constrainCenter(center) {
return this.constraints_.center(center);
}
/**
* Get the constrained resolution of this view.
* @param {number|undefined} resolution Resolution.
* @param {number=} opt_delta Delta. Default is `0`.
* @param {number=} opt_direction Direction. Default is `0`.
* @return {number|undefined} Constrained resolution.
* @api
*/
constrainResolution(resolution, opt_delta, opt_direction) {
const delta = opt_delta || 0;
const direction = opt_direction || 0;
return this.constraints_.resolution(resolution, delta, direction);
}
/**
* Get the constrained rotation of this view.
* @param {number|undefined} rotation Rotation.
* @param {number=} opt_delta Delta. Default is `0`.
* @return {number|undefined} Constrained rotation.
* @api
*/
constrainRotation(rotation, opt_delta) {
const delta = opt_delta || 0;
return this.constraints_.rotation(rotation, delta);
}
/** /**
* Get the view center. * Get the view center.
* @return {import("./coordinate.js").Coordinate|undefined} The center of the view. * @return {import("./coordinate.js").Coordinate|undefined} The center of the view.
@@ -793,6 +817,15 @@ class View extends BaseObject {
this.applyOptions_(this.getUpdatedOptions_({minZoom: zoom})); this.applyOptions_(this.getUpdatedOptions_({minZoom: zoom}));
} }
/**
* Set whether the view shoud allow intermediary zoom levels.
* @param {boolean} enabled Whether the resolution is constrained.
* @api
*/
setConstrainResolution(enabled) {
this.applyOptions_(this.getUpdatedOptions_({constrainResolution: enabled}));
}
/** /**
* Get the view projection. * Get the view projection.
* @return {import("./proj/Projection.js").default} The projection of the view. * @return {import("./proj/Projection.js").default} The projection of the view.
@@ -914,9 +947,9 @@ class View extends BaseObject {
} }
/** /**
* Get the current zoom level. If you configured your view with a resolutions * Get the current zoom level. This method may return non-integer zoom levels
* array (this is rare), this method may return non-integer zoom levels (so * if the view does not constrain the resolution, or if an interaction or
* the zoom level is not safe to use as an index into a resolutions array). * animation is underway.
* @return {number|undefined} Zoom. * @return {number|undefined} Zoom.
* @api * @api
*/ */
@@ -961,8 +994,16 @@ class View extends BaseObject {
* @api * @api
*/ */
getResolutionForZoom(zoom) { getResolutionForZoom(zoom) {
return /** @type {number} */ (this.constrainResolution( if (this.resolutions_) {
this.maxResolution_, zoom - this.minZoom_, 0)); if (this.resolutions_.length <= 1) {
return 0;
}
const baseLevel = clamp(Math.floor(zoom), 0, this.resolutions_.length - 2);
const zoomFactor = this.resolutions_[baseLevel] / this.resolutions_[baseLevel + 1];
return this.resolutions_[baseLevel] / Math.pow(zoomFactor, clamp(zoom - baseLevel, 0, 1));
} else {
return this.maxResolution_ / Math.pow(this.zoomFactor_, zoom - this.minZoom_);
}
} }
/** /**
@@ -998,15 +1039,12 @@ class View extends BaseObject {
} }
const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0]; const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0];
const constrainResolution = options.constrainResolution !== undefined ?
options.constrainResolution : true;
const nearest = options.nearest !== undefined ? options.nearest : false; const nearest = options.nearest !== undefined ? options.nearest : false;
let minResolution; let minResolution;
if (options.minResolution !== undefined) { if (options.minResolution !== undefined) {
minResolution = options.minResolution; minResolution = options.minResolution;
} else if (options.maxZoom !== undefined) { } else if (options.maxZoom !== undefined) {
minResolution = this.constrainResolution( minResolution = this.getResolutionForZoom(options.maxZoom);
this.maxResolution_, options.maxZoom - this.minZoom_, 0);
} else { } else {
minResolution = 0; minResolution = 0;
} }
@@ -1036,14 +1074,7 @@ class View extends BaseObject {
[size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]); [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);
resolution = isNaN(resolution) ? minResolution : resolution = isNaN(resolution) ? minResolution :
Math.max(resolution, minResolution); Math.max(resolution, minResolution);
if (constrainResolution) { resolution = this.getConstrainedResolution(resolution, nearest ? 0 : 1);
let constrainedResolution = this.constrainResolution(resolution, 0, 0);
if (!nearest && constrainedResolution < resolution) {
constrainedResolution = this.constrainResolution(
constrainedResolution, -1, 0);
}
resolution = constrainedResolution;
}
// calculate center // calculate center
sinAngle = -sinAngle; // go back to original rotation sinAngle = -sinAngle; // go back to original rotation
@@ -1059,13 +1090,14 @@ class View extends BaseObject {
if (options.duration !== undefined) { if (options.duration !== undefined) {
this.animate({ this.animate({
resolution: resolution, resolution: resolution,
center: center, center: this.getConstrainedCenter(center, resolution),
duration: options.duration, duration: options.duration,
easing: options.easing easing: options.easing
}, callback); }, callback);
} else { } else {
this.setResolution(resolution); this.targetResolution_ = resolution;
this.setCenter(center); this.targetCenter_ = center;
this.applyTargetState_(false, true);
animationCallback(callback, true); animationCallback(callback, true);
} }
} }
@@ -1104,30 +1136,74 @@ class View extends BaseObject {
} }
/** /**
* Rotate the view around a given coordinate. * Adds relative coordinates to the center of the view. Any extent constraint will apply.
* @param {number} rotation New rotation value for the view. * @param {import("./coordinate.js").Coordinate} deltaCoordinates Relative value to add.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The rotation center.
* @api * @api
*/ */
rotate(rotation, opt_anchor) { adjustCenter(deltaCoordinates) {
if (opt_anchor !== undefined) { const center = this.targetCenter_;
const center = this.calculateCenterRotate(rotation, opt_anchor); this.setCenter([center[0] + deltaCoordinates[0], center[1] + deltaCoordinates[1]]);
this.setCenter(center);
}
this.setRotation(rotation);
} }
/** /**
* Set the center of the current view. * Multiply the view resolution by a ratio, optionally using an anchor. Any resolution
* 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) {
const isMoving = this.getAnimating() || this.getInteracting();
const size = this.getSizeFromViewport_(this.getRotation());
const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving);
if (opt_anchor !== undefined) {
this.targetCenter_ = this.calculateCenterZoom(newResolution, opt_anchor);
}
this.targetResolution_ *= ratio;
this.applyTargetState_();
}
/**
* Adds a value to the view zoom level, optionally using an anchor. Any resolution
* constraint will apply.
* @param {number} delta Relative value to add to the zoom level.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation.
* @api
*/
adjustZoom(delta, opt_anchor) {
this.adjustResolution(Math.pow(this.zoomFactor_, -delta), opt_anchor);
}
/**
* Adds a value to the view rotation, optionally using an anchor. Any rotation
* 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) {
const isMoving = this.getAnimating() || this.getInteracting();
const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving);
if (opt_anchor !== undefined) {
this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor);
}
this.targetRotation_ += delta;
this.applyTargetState_();
}
/**
* Set the center of the current view. Any extent constraint will apply.
* @param {import("./coordinate.js").Coordinate|undefined} center The center of the view. * @param {import("./coordinate.js").Coordinate|undefined} center The center of the view.
* @observable * @observable
* @api * @api
*/ */
setCenter(center) { setCenter(center) {
this.set(ViewProperty.CENTER, center); this.targetCenter_ = center;
if (this.getAnimating()) { this.applyTargetState_();
this.cancelAnimations();
}
} }
/** /**
@@ -1142,39 +1218,166 @@ class View extends BaseObject {
} }
/** /**
* Set the resolution for this view. * Set the resolution for this view. Any resolution constraint will apply.
* @param {number|undefined} resolution The resolution of the view. * @param {number|undefined} resolution The resolution of the view.
* @observable * @observable
* @api * @api
*/ */
setResolution(resolution) { setResolution(resolution) {
this.set(ViewProperty.RESOLUTION, resolution); this.targetResolution_ = resolution;
if (this.getAnimating()) { this.applyTargetState_();
this.cancelAnimations();
}
} }
/** /**
* Set the rotation for this view. * Set the rotation for this view. Any rotation constraint will apply.
* @param {number} rotation The rotation of the view in radians. * @param {number} rotation The rotation of the view in radians.
* @observable * @observable
* @api * @api
*/ */
setRotation(rotation) { setRotation(rotation) {
this.set(ViewProperty.ROTATION, rotation); this.targetRotation_ = rotation;
if (this.getAnimating()) { this.applyTargetState_();
this.cancelAnimations();
}
} }
/** /**
* Zoom to a specific zoom level. * Zoom to a specific zoom level. Any resolution constrain will apply.
* @param {number} zoom Zoom level. * @param {number} zoom Zoom level.
* @api * @api
*/ */
setZoom(zoom) { setZoom(zoom) {
this.setResolution(this.getResolutionForZoom(zoom)); this.setResolution(this.getResolutionForZoom(zoom));
} }
/**
* Recompute rotation/resolution/center based on target values.
* Note: we have to compute rotation first, then resolution and center considering that
* parameters can influence one another in case a view extent constraint is present.
* @param {boolean=} opt_doNotCancelAnims Do not cancel animations.
* @param {boolean=} opt_forceMoving Apply constraints as if the view is moving.
* @private
*/
applyTargetState_(opt_doNotCancelAnims, opt_forceMoving) {
const isMoving = this.getAnimating() || this.getInteracting() || opt_forceMoving;
// compute rotation
const newRotation = this.constraints_.rotation(this.targetRotation_, isMoving);
const size = this.getSizeFromViewport_(newRotation);
const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving);
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving);
if (this.get(ViewProperty.ROTATION) !== newRotation) {
this.set(ViewProperty.ROTATION, newRotation);
}
if (this.get(ViewProperty.RESOLUTION) !== newResolution) {
this.set(ViewProperty.RESOLUTION, newResolution);
}
if (!this.get(ViewProperty.CENTER) || !equals(this.get(ViewProperty.CENTER), newCenter)) {
this.set(ViewProperty.CENTER, newCenter);
}
if (this.getAnimating() && !opt_doNotCancelAnims) {
this.cancelAnimations();
}
}
/**
* If any constraints need to be applied, an animation will be triggered.
* This is typically done on interaction end.
* @param {number=} opt_duration The animation duration in ms.
* @param {number=} opt_resolutionDirection Which direction to zoom.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation.
* @private
*/
resolveConstraints_(opt_duration, opt_resolutionDirection, opt_anchor) {
const duration = opt_duration !== undefined ? opt_duration : 200;
const direction = opt_resolutionDirection || 0;
const newRotation = this.constraints_.rotation(this.targetRotation_);
const size = this.getSizeFromViewport_(newRotation);
const newResolution = this.constraints_.resolution(this.targetResolution_, direction, size);
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size);
if (this.getResolution() !== newResolution ||
this.getRotation() !== newRotation ||
!this.getCenter() ||
!equals(this.getCenter(), newCenter)) {
if (this.getAnimating()) {
this.cancelAnimations();
}
this.animate({
rotation: newRotation,
center: newCenter,
resolution: newResolution,
duration: duration,
easing: easeOut,
anchor: opt_anchor
});
}
}
/**
* Notify the View that an interaction has started.
* @api
*/
beginInteraction() {
this.setHint(ViewHint.INTERACTING, 1);
}
/**
* Notify the View that an interaction has ended. The view state will be resolved
* to a stable one if needed (depending on its constraints).
* @param {number=} opt_duration Animation duration in ms.
* @param {number=} opt_resolutionDirection Which direction to zoom.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation.
* @api
*/
endInteraction(opt_duration, opt_resolutionDirection, opt_anchor) {
this.setHint(ViewHint.INTERACTING, -1);
this.resolveConstraints_(opt_duration, opt_resolutionDirection, opt_anchor);
}
/**
* Get a valid position for the view center according to the current constraints.
* @param {import("./coordinate.js").Coordinate|undefined} targetCenter Target center position.
* @param {number=} opt_targetResolution Target resolution. If not supplied, the current one will be used.
* This is useful to guess a valid center position at a different zoom level.
* @return {import("./coordinate.js").Coordinate|undefined} Valid center position.
*/
getConstrainedCenter(targetCenter, opt_targetResolution) {
const size = this.getSizeFromViewport_(this.getRotation());
return this.constraints_.center(targetCenter, opt_targetResolution || this.getResolution(), size);
}
/**
* Get a valid zoom level according to the current view constraints.
* @param {number|undefined} targetZoom Target zoom.
* @param {number=} opt_direction Direction. Default is `0`. Specify `-1` or `1` to return
* the available value respectively lower or greater than the target one. Leaving `0` will simply choose
* the nearest available value.
* @return {number|undefined} Valid zoom level.
*/
getConstrainedZoom(targetZoom, opt_direction) {
const targetRes = this.getResolutionForZoom(targetZoom);
return this.getZoomForResolution(this.getConstrainedResolution(targetRes));
}
/**
* Get a valid resolution according to the current view constraints.
* @param {number|undefined} targetResolution Target resolution.
* @param {number=} opt_direction Direction. Default is `0`. Specify `-1` or `1` to return
* the available value respectively lower or greater than the target one. Leaving `0` will simply choose
* the nearest available value.
* @return {number|undefined} Valid resolution.
*/
getConstrainedResolution(targetResolution, opt_direction) {
const direction = opt_direction || 0;
const size = this.getSizeFromViewport_(this.getRotation());
return this.constraints_.resolution(targetResolution, direction, size);
}
} }
@@ -1195,7 +1398,8 @@ function animationCallback(callback, returnValue) {
*/ */
export function createCenterConstraint(options) { export function createCenterConstraint(options) {
if (options.extent !== undefined) { if (options.extent !== undefined) {
return createExtent(options.extent); return createExtent(options.extent, options.constrainOnlyCenter,
options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true);
} else { } else {
return centerNone; return centerNone;
} }
@@ -1226,13 +1430,22 @@ export function createResolutionConstraint(options) {
const zoomFactor = options.zoomFactor !== undefined ? const zoomFactor = options.zoomFactor !== undefined ?
options.zoomFactor : defaultZoomFactor; options.zoomFactor : defaultZoomFactor;
const smooth =
options.smoothResolutionConstraint !== undefined ? options.smoothResolutionConstraint : true;
if (options.resolutions !== undefined) { if (options.resolutions !== undefined) {
const resolutions = options.resolutions; const resolutions = options.resolutions;
maxResolution = resolutions[minZoom]; maxResolution = resolutions[minZoom];
minResolution = resolutions[maxZoom] !== undefined ? minResolution = resolutions[maxZoom] !== undefined ?
resolutions[maxZoom] : resolutions[resolutions.length - 1]; resolutions[maxZoom] : resolutions[resolutions.length - 1];
resolutionConstraint = createSnapToResolutions(
resolutions); if (options.constrainResolution) {
resolutionConstraint = createSnapToResolutions(resolutions, smooth,
!options.constrainOnlyCenter && options.extent);
} else {
resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth,
!options.constrainOnlyCenter && options.extent);
}
} else { } else {
// calculate the default min and max resolution // calculate the default min and max resolution
const projection = createProjection(options.projection, 'EPSG:3857'); const projection = createProjection(options.projection, 'EPSG:3857');
@@ -1276,8 +1489,14 @@ export function createResolutionConstraint(options) {
Math.log(maxResolution / minResolution) / Math.log(zoomFactor)); Math.log(maxResolution / minResolution) / Math.log(zoomFactor));
minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom); minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom);
resolutionConstraint = createSnapToPower( if (options.constrainResolution) {
zoomFactor, maxResolution, maxZoom - minZoom); resolutionConstraint = createSnapToPower(
zoomFactor, maxResolution, minResolution, smooth,
!options.constrainOnlyCenter && options.extent);
} else {
resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth,
!options.constrainOnlyCenter && options.extent);
}
} }
return {constraint: resolutionConstraint, maxResolution: maxResolution, return {constraint: resolutionConstraint, maxResolution: maxResolution,
minResolution: minResolution, minZoom: minZoom, zoomFactor: zoomFactor}; minResolution: minResolution, minZoom: minZoom, zoomFactor: zoomFactor};

View File

@@ -5,26 +5,57 @@ import {clamp} from './math.js';
/** /**
* @typedef {function((import("./coordinate.js").Coordinate|undefined)): (import("./coordinate.js").Coordinate|undefined)} Type * @typedef {function((import("./coordinate.js").Coordinate|undefined), number, import("./size.js").Size, boolean=): (import("./coordinate.js").Coordinate|undefined)} Type
*/ */
/** /**
* @param {import("./extent.js").Extent} extent Extent. * @param {import("./extent.js").Extent} extent Extent.
* @param {boolean} onlyCenter If true, the constraint will only apply to the view center.
* @param {boolean} smooth If true, the view will be able to go slightly out of the given extent
* (only during interaction and animation).
* @return {Type} The constraint. * @return {Type} The constraint.
*/ */
export function createExtent(extent) { export function createExtent(extent, onlyCenter, smooth) {
return ( return (
/** /**
* @param {import("./coordinate.js").Coordinate=} center Center. * @param {import("./coordinate.js").Coordinate|undefined} center Center.
* @param {number} resolution Resolution.
* @param {import("./size.js").Size} size Viewport size; unused if `onlyCenter` was specified.
* @param {boolean=} opt_isMoving True if an interaction or animation is in progress.
* @return {import("./coordinate.js").Coordinate|undefined} Center. * @return {import("./coordinate.js").Coordinate|undefined} Center.
*/ */
function(center) { function(center, resolution, size, opt_isMoving) {
if (center) { if (center) {
return [ const viewWidth = onlyCenter ? 0 : size[0] * resolution;
clamp(center[0], extent[0], extent[2]), const viewHeight = onlyCenter ? 0 : size[1] * resolution;
clamp(center[1], extent[1], extent[3]) let minX = extent[0] + viewWidth / 2;
]; let maxX = extent[2] - viewWidth / 2;
let minY = extent[1] + viewHeight / 2;
let maxY = extent[3] - viewHeight / 2;
// note: when zooming out of bounds, min and max values for x and y may
// end up inverted (min > max); this has to be accounted for
if (minX > maxX) {
minX = maxX = (maxX + minX) / 2;
}
if (minY > maxY) {
minY = maxY = (maxY + minY) / 2;
}
let x = clamp(center[0], minX, maxX);
let y = clamp(center[1], minY, maxY);
const ratio = 30 * resolution;
// during an interaction, allow some overscroll
if (opt_isMoving && smooth) {
x += -ratio * Math.log(1 + Math.max(0, minX - center[0]) / ratio) +
ratio * Math.log(1 + Math.max(0, center[0] - maxX) / ratio);
y += -ratio * Math.log(1 + Math.max(0, minY - center[1]) / ratio) +
ratio * Math.log(1 + Math.max(0, center[1] - maxY) / ratio);
}
return [x, y];
} else { } else {
return undefined; return undefined;
} }

View File

@@ -7,7 +7,7 @@ import {replaceNode} from '../dom.js';
import {listen} from '../events.js'; import {listen} from '../events.js';
import EventType from '../events/EventType.js'; import EventType from '../events/EventType.js';
const events = ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange']; const events = ['fullscreenchange', 'webkitfullscreenchange', 'MSFullscreenChange'];
/** /**
* @typedef {Object} Options * @typedef {Object} Options
@@ -209,7 +209,6 @@ function isFullScreenSupported() {
const body = document.body; const body = document.body;
return !!( return !!(
body.webkitRequestFullscreen || body.webkitRequestFullscreen ||
(body.mozRequestFullScreen && document.mozFullScreenEnabled) ||
(body.msRequestFullscreen && document.msFullscreenEnabled) || (body.msRequestFullscreen && document.msFullscreenEnabled) ||
(body.requestFullscreen && document.fullscreenEnabled) (body.requestFullscreen && document.fullscreenEnabled)
); );
@@ -220,8 +219,7 @@ function isFullScreenSupported() {
*/ */
function isFullScreen() { function isFullScreen() {
return !!( return !!(
document.webkitIsFullScreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement || document.fullscreenElement
document.msFullscreenElement || document.fullscreenElement
); );
} }
@@ -234,8 +232,6 @@ function requestFullScreen(element) {
element.requestFullscreen(); element.requestFullscreen();
} else if (element.msRequestFullscreen) { } else if (element.msRequestFullscreen) {
element.msRequestFullscreen(); element.msRequestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) { } else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen(); element.webkitRequestFullscreen();
} }
@@ -246,9 +242,7 @@ function requestFullScreen(element) {
* @param {HTMLElement} element Element to request fullscreen * @param {HTMLElement} element Element to request fullscreen
*/ */
function requestFullScreenWithKeys(element) { function requestFullScreenWithKeys(element) {
if (element.mozRequestFullScreenWithKeys) { if (element.webkitRequestFullscreen) {
element.mozRequestFullScreenWithKeys();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen(); element.webkitRequestFullscreen();
} else { } else {
requestFullScreen(element); requestFullScreen(element);
@@ -263,8 +257,6 @@ function exitFullScreen() {
document.exitFullscreen(); document.exitFullscreen();
} else if (document.msExitFullscreen) { } else if (document.msExitFullscreen) {
document.msExitFullscreen(); document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) { } else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen(); document.webkitExitFullscreen();
} }

View File

@@ -169,8 +169,6 @@ export function render(mapEvent) {
this.element.classList.remove(CLASS_HIDDEN); this.element.classList.remove(CLASS_HIDDEN);
} }
} }
this.label_.style.msTransform = transform;
this.label_.style.webkitTransform = transform;
this.label_.style.transform = transform; this.label_.style.transform = transform;
} }
this.rotation_ = rotation; this.rotation_ = rotation;

View File

@@ -201,20 +201,12 @@ class ScaleLine extends Control {
ProjUnits.METERS; ProjUnits.METERS;
let pointResolution = let pointResolution =
getPointResolution(projection, viewState.resolution, center, pointResolutionUnits); getPointResolution(projection, viewState.resolution, center, pointResolutionUnits);
if (projection.getUnits() != ProjUnits.DEGREES && projection.getMetersPerUnit()
&& pointResolutionUnits == ProjUnits.METERS) {
pointResolution *= projection.getMetersPerUnit();
}
let nominalCount = this.minWidth_ * pointResolution; let nominalCount = this.minWidth_ * pointResolution;
let suffix = ''; let suffix = '';
if (units == Units.DEGREES) { if (units == Units.DEGREES) {
const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES]; const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES];
if (projection.getUnits() == ProjUnits.DEGREES) { nominalCount *= metersPerDegree;
nominalCount *= metersPerDegree;
} else {
pointResolution /= metersPerDegree;
}
if (nominalCount < metersPerDegree / 60) { if (nominalCount < metersPerDegree / 60) {
suffix = '\u2033'; // seconds suffix = '\u2033'; // seconds
pointResolution *= 3600; pointResolution *= 3600;

View File

@@ -114,20 +114,20 @@ class Zoom extends Control {
// upon it // upon it
return; return;
} }
const currentResolution = view.getResolution(); const currentZoom = view.getZoom();
if (currentResolution) { if (currentZoom !== undefined) {
const newResolution = view.constrainResolution(currentResolution, delta); const newZoom = view.getConstrainedZoom(currentZoom + delta);
if (this.duration_ > 0) { if (this.duration_ > 0) {
if (view.getAnimating()) { if (view.getAnimating()) {
view.cancelAnimations(); view.cancelAnimations();
} }
view.animate({ view.animate({
resolution: newResolution, zoom: newZoom,
duration: this.duration_, duration: this.duration_,
easing: easeOut easing: easeOut
}); });
} else { } else {
view.setResolution(newResolution); view.setZoom(newZoom);
} }
} }
} }

View File

@@ -1,7 +1,6 @@
/** /**
* @module ol/control/ZoomSlider * @module ol/control/ZoomSlider
*/ */
import ViewHint from '../ViewHint.js';
import Control from './Control.js'; import Control from './Control.js';
import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js';
import {easeOut} from '../easing.js'; import {easeOut} from '../easing.js';
@@ -102,13 +101,13 @@ class ZoomSlider extends Control {
* @type {number|undefined} * @type {number|undefined}
* @private * @private
*/ */
this.previousX_; this.startX_;
/** /**
* @type {number|undefined} * @type {number|undefined}
* @private * @private
*/ */
this.previousY_; this.startY_;
/** /**
* The calculated thumb size (border box plus margins). Set when initSlider_ * The calculated thumb size (border box plus margins). Set when initSlider_
@@ -218,9 +217,10 @@ class ZoomSlider extends Control {
event.offsetY - this.thumbSize_[1] / 2); event.offsetY - this.thumbSize_[1] / 2);
const resolution = this.getResolutionForPosition_(relativePosition); const resolution = this.getResolutionForPosition_(relativePosition);
const zoom = view.getConstrainedZoom(view.getZoomForResolution(resolution));
view.animate({ view.animate({
resolution: view.constrainResolution(resolution), zoom: zoom,
duration: this.duration_, duration: this.duration_,
easing: easeOut easing: easeOut
}); });
@@ -233,9 +233,10 @@ class ZoomSlider extends Control {
*/ */
handleDraggerStart_(event) { handleDraggerStart_(event) {
if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) { if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) {
this.getMap().getView().setHint(ViewHint.INTERACTING, 1); const element = /** @type {HTMLElement} */ (this.element.firstElementChild);
this.previousX_ = event.clientX; this.getMap().getView().beginInteraction();
this.previousY_ = event.clientY; this.startX_ = event.clientX - parseFloat(element.style.left);
this.startY_ = event.clientY - parseFloat(element.style.top);
this.dragging_ = true; this.dragging_ = true;
if (this.dragListenerKeys_.length === 0) { if (this.dragListenerKeys_.length === 0) {
@@ -259,15 +260,11 @@ class ZoomSlider extends Control {
*/ */
handleDraggerDrag_(event) { handleDraggerDrag_(event) {
if (this.dragging_) { if (this.dragging_) {
const element = /** @type {HTMLElement} */ (this.element.firstElementChild); const deltaX = event.clientX - this.startX_;
const deltaX = event.clientX - this.previousX_ + parseFloat(element.style.left); const deltaY = event.clientY - this.startY_;
const deltaY = event.clientY - this.previousY_ + parseFloat(element.style.top);
const relativePosition = this.getRelativePosition_(deltaX, deltaY); const relativePosition = this.getRelativePosition_(deltaX, deltaY);
this.currentResolution_ = this.getResolutionForPosition_(relativePosition); this.currentResolution_ = this.getResolutionForPosition_(relativePosition);
this.getMap().getView().setResolution(this.currentResolution_); this.getMap().getView().setResolution(this.currentResolution_);
this.setThumbPosition_(this.currentResolution_);
this.previousX_ = event.clientX;
this.previousY_ = event.clientY;
} }
} }
@@ -279,17 +276,11 @@ class ZoomSlider extends Control {
handleDraggerEnd_(event) { handleDraggerEnd_(event) {
if (this.dragging_) { if (this.dragging_) {
const view = this.getMap().getView(); const view = this.getMap().getView();
view.setHint(ViewHint.INTERACTING, -1); view.endInteraction();
view.animate({
resolution: view.constrainResolution(this.currentResolution_),
duration: this.duration_,
easing: easeOut
});
this.dragging_ = false; this.dragging_ = false;
this.previousX_ = undefined; this.startX_ = undefined;
this.previousY_ = undefined; this.startY_ = undefined;
this.dragListenerKeys_.forEach(unlistenByKey); this.dragListenerKeys_.forEach(unlistenByKey);
this.dragListenerKeys_.length = 0; this.dragListenerKeys_.length = 0;
} }
@@ -356,7 +347,7 @@ class ZoomSlider extends Control {
*/ */
getPositionForResolution_(res) { getPositionForResolution_(res) {
const fn = this.getMap().getView().getValueForResolutionFunction(); const fn = this.getMap().getView().getValueForResolutionFunction();
return 1 - fn(res); return clamp(1 - fn(res), 0, 1);
} }
} }
@@ -375,10 +366,8 @@ export function render(mapEvent) {
this.initSlider_(); this.initSlider_();
} }
const res = mapEvent.frameState.viewState.resolution; const res = mapEvent.frameState.viewState.resolution;
if (res !== this.currentResolution_) { this.currentResolution_ = res;
this.currentResolution_ = res; this.setThumbPosition_(res);
this.setThumbPosition_(res);
}
} }

View File

@@ -10,14 +10,14 @@
* @return {CanvasRenderingContext2D} The context. * @return {CanvasRenderingContext2D} The context.
*/ */
export function createCanvasContext2D(opt_width, opt_height) { export function createCanvasContext2D(opt_width, opt_height) {
const canvas = /** @type {HTMLCanvasElement} */ (document.createElement('canvas')); const canvas = document.createElement('canvas');
if (opt_width) { if (opt_width) {
canvas.width = opt_width; canvas.width = opt_width;
} }
if (opt_height) { if (opt_height) {
canvas.height = opt_height; canvas.height = opt_height;
} }
return /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); return canvas.getContext('2d');
} }

View File

@@ -230,6 +230,33 @@ export const mouseOnly = function(mapBrowserEvent) {
return pointerEvent.pointerType == 'mouse'; return pointerEvent.pointerType == 'mouse';
}; };
/**
* Return `true` if the event originates from a touchable device.
*
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} True if the event originates from a touchable device.
* @api
*/
export const touchOnly = function(mapBrowserEvent) {
const pointerEvt = /** @type {import("../MapBrowserPointerEvent").default} */ (mapBrowserEvent).pointerEvent;
assert(pointerEvt !== undefined, 56); // mapBrowserEvent must originate from a pointer event
// see http://www.w3.org/TR/pointerevents/#widl-PointerEvent-pointerType
return pointerEvt.pointerType === 'touch';
};
/**
* Return `true` if the event originates from a digital pen.
*
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} True if the event originates from a digital pen.
* @api
*/
export const penOnly = function(mapBrowserEvent) {
const pointerEvt = /** @type {import("../MapBrowserPointerEvent").default} */ (mapBrowserEvent).pointerEvent;
assert(pointerEvt !== undefined, 56); // mapBrowserEvent must originate from a pointer event
// see http://www.w3.org/TR/pointerevents/#widl-PointerEvent-pointerType
return pointerEvt.pointerType === 'pen';
};
/** /**
* Return `true` if the event originates from a primary pointer in * Return `true` if the event originates from a primary pointer in

View File

@@ -83,9 +83,11 @@ export function loadFeaturesXhr(url, format, success, failure) {
source = /** @type {ArrayBuffer} */ (xhr.response); source = /** @type {ArrayBuffer} */ (xhr.response);
} }
if (source) { if (source) {
success.call(this, format.readFeatures(source, success.call(this, format.readFeatures(source, {
{featureProjection: projection}), extent: extent,
format.readProjection(source), format.getLastExtent()); featureProjection: projection
}),
format.readProjection(source));
} else { } else {
failure.call(this); failure.call(this);
} }

View File

@@ -410,15 +410,15 @@ function readPolygonGeometry(object) {
/** /**
* @param {import("../geom/Geometry.js").default} geometry Geometry. * @param {import("../geom/Point.js").default} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options. * @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {EsriJSONPoint} EsriJSON geometry. * @return {EsriJSONPoint} EsriJSON geometry.
*/ */
function writePointGeometry(geometry, opt_options) { function writePointGeometry(geometry, opt_options) {
const coordinates = /** @type {import("../geom/Point.js").default} */ (geometry).getCoordinates(); const coordinates = geometry.getCoordinates();
/** @type {EsriJSONPoint} */ /** @type {EsriJSONPoint} */
let esriJSON; let esriJSON;
const layout = /** @type {import("../geom/Point.js").default} */ (geometry).getLayout(); const layout = geometry.getLayout();
if (layout === GeometryLayout.XYZ) { if (layout === GeometryLayout.XYZ) {
esriJSON = { esriJSON = {
x: coordinates[0], x: coordinates[0],
@@ -466,12 +466,11 @@ function getHasZM(geometry) {
/** /**
* @param {import("../geom/Geometry.js").default} geometry Geometry. * @param {import("../geom/LineString.js").default} lineString Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options. * @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {EsriJSONPolyline} EsriJSON geometry. * @return {EsriJSONPolyline} EsriJSON geometry.
*/ */
function writeLineStringGeometry(geometry, opt_options) { function writeLineStringGeometry(lineString, opt_options) {
const lineString = /** @type {import("../geom/LineString.js").default} */ (geometry);
const hasZM = getHasZM(lineString); const hasZM = getHasZM(lineString);
return { return {
hasZ: hasZM.hasZ, hasZ: hasZM.hasZ,
@@ -484,12 +483,11 @@ function writeLineStringGeometry(geometry, opt_options) {
/** /**
* @param {import("../geom/Geometry.js").default} geometry Geometry. * @param {import("../geom/Polygon.js").default} polygon Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options. * @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {EsriJSONPolygon} EsriJSON geometry. * @return {EsriJSONPolygon} EsriJSON geometry.
*/ */
function writePolygonGeometry(geometry, opt_options) { function writePolygonGeometry(polygon, opt_options) {
const polygon = /** @type {import("../geom/Polygon.js").default} */ (geometry);
// Esri geometries use the left-hand rule // Esri geometries use the left-hand rule
const hasZM = getHasZM(polygon); const hasZM = getHasZM(polygon);
return { return {
@@ -501,12 +499,11 @@ function writePolygonGeometry(geometry, opt_options) {
/** /**
* @param {import("../geom/Geometry.js").default} geometry Geometry. * @param {import("../geom/MultiLineString.js").default} multiLineString Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options. * @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {EsriJSONPolyline} EsriJSON geometry. * @return {EsriJSONPolyline} EsriJSON geometry.
*/ */
function writeMultiLineStringGeometry(geometry, opt_options) { function writeMultiLineStringGeometry(multiLineString, opt_options) {
const multiLineString = /** @type {import("../geom/MultiLineString.js").default} */ (geometry);
const hasZM = getHasZM(multiLineString); const hasZM = getHasZM(multiLineString);
return { return {
hasZ: hasZM.hasZ, hasZ: hasZM.hasZ,
@@ -517,12 +514,11 @@ function writeMultiLineStringGeometry(geometry, opt_options) {
/** /**
* @param {import("../geom/Geometry.js").default} geometry Geometry. * @param {import("../geom/MultiPoint.js").default} multiPoint Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options. * @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {EsriJSONMultipoint} EsriJSON geometry. * @return {EsriJSONMultipoint} EsriJSON geometry.
*/ */
function writeMultiPointGeometry(geometry, opt_options) { function writeMultiPointGeometry(multiPoint, opt_options) {
const multiPoint = /** @type {import("../geom/MultiPoint.js").default} */ (geometry);
const hasZM = getHasZM(multiPoint); const hasZM = getHasZM(multiPoint);
return { return {
hasZ: hasZM.hasZ, hasZ: hasZM.hasZ,
@@ -533,13 +529,13 @@ function writeMultiPointGeometry(geometry, opt_options) {
/** /**
* @param {import("../geom/Geometry.js").default} geometry Geometry. * @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options. * @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {EsriJSONPolygon} EsriJSON geometry. * @return {EsriJSONPolygon} EsriJSON geometry.
*/ */
function writeMultiPolygonGeometry(geometry, opt_options) { function writeMultiPolygonGeometry(geometry, opt_options) {
const hasZM = getHasZM(/** @type {import("../geom/MultiPolygon.js").default} */(geometry)); const hasZM = getHasZM(geometry);
const coordinates = /** @type {import("../geom/MultiPolygon.js").default} */ (geometry).getCoordinates(false); const coordinates = geometry.getCoordinates(false);
const output = []; const output = [];
for (let i = 0; i < coordinates.length; i++) { for (let i = 0; i < coordinates.length; i++) {
for (let x = coordinates[i].length - 1; x >= 0; x--) { for (let x = coordinates[i].length - 1; x >= 0; x--) {

View File

@@ -111,14 +111,6 @@ class FeatureFormat {
}, options); }, options);
} }
/**
* Get the extent from the source of the last {@link readFeatures} call.
* @return {import("../extent.js").Extent} Tile extent.
*/
getLastExtent() {
return null;
}
/** /**
* @abstract * @abstract
* @return {import("./FormatType.js").default} Format. * @return {import("./FormatType.js").default} Format.

View File

@@ -19,6 +19,7 @@ import {linearRingIsClockwise} from '../geom/flat/orient.js';
import Projection from '../proj/Projection.js'; import Projection from '../proj/Projection.js';
import Units from '../proj/Units.js'; import Units from '../proj/Units.js';
import RenderFeature from '../render/Feature.js'; import RenderFeature from '../render/Feature.js';
import {get} from '../proj.js';
/** /**
@@ -83,12 +84,6 @@ class MVT extends FeatureFormat {
*/ */
this.layers_ = options.layers ? options.layers : null; this.layers_ = options.layers ? options.layers : null;
/**
* @private
* @type {import("../extent.js").Extent}
*/
this.extent_ = null;
} }
/** /**
@@ -159,10 +154,10 @@ class MVT extends FeatureFormat {
* @private * @private
* @param {PBF} pbf PBF * @param {PBF} pbf PBF
* @param {Object} rawFeature Raw Mapbox feature. * @param {Object} rawFeature Raw Mapbox feature.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options. * @param {import("./Feature.js").ReadOptions} options Read options.
* @return {import("../Feature.js").FeatureLike} Feature. * @return {import("../Feature.js").FeatureLike} Feature.
*/ */
createFeature_(pbf, rawFeature, opt_options) { createFeature_(pbf, rawFeature, options) {
const type = rawFeature.type; const type = rawFeature.type;
if (type === 0) { if (type === 0) {
return null; return null;
@@ -181,6 +176,7 @@ class MVT extends FeatureFormat {
if (this.featureClass_ === RenderFeature) { if (this.featureClass_ === RenderFeature) {
feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id); feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
feature.transform(options.dataProjection, options.featureProjection);
} else { } else {
let geom; let geom;
if (geometryType == GeometryType.POLYGON) { if (geometryType == GeometryType.POLYGON) {
@@ -213,7 +209,7 @@ class MVT extends FeatureFormat {
if (this.geometryName_) { if (this.geometryName_) {
feature.setGeometryName(this.geometryName_); feature.setGeometryName(this.geometryName_);
} }
const geometry = transformGeometryWithOptions(geom, false, this.adaptOptions(opt_options)); const geometry = transformGeometryWithOptions(geom, false, options);
feature.setGeometry(geometry); feature.setGeometry(geometry);
feature.setId(id); feature.setId(id);
feature.setProperties(values, true); feature.setProperties(values, true);
@@ -222,14 +218,6 @@ class MVT extends FeatureFormat {
return feature; return feature;
} }
/**
* @inheritDoc
* @api
*/
getLastExtent() {
return this.extent_;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -238,15 +226,22 @@ class MVT extends FeatureFormat {
} }
/** /**
* @inheritDoc * Read all features.
*
* @param {ArrayBuffer} source Source.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options.
* @return {Array<import("../Feature.js").FeatureLike>} Features.
* @api * @api
*/ */
readFeatures(source, opt_options) { readFeatures(source, opt_options) {
const layers = this.layers_; const layers = this.layers_;
const options = /** @type {import("./Feature.js").ReadOptions} */ (this.adaptOptions(opt_options));
const dataProjection = get(options.dataProjection);
dataProjection.setWorldExtent(options.extent);
options.dataProjection = dataProjection;
const pbf = new PBF(/** @type {ArrayBuffer} */ (source)); const pbf = new PBF(/** @type {ArrayBuffer} */ (source));
const pbfLayers = pbf.readFields(layersPBFReader, {}); const pbfLayers = pbf.readFields(layersPBFReader, {});
/** @type {Array<import("../Feature.js").FeatureLike>} */
const features = []; const features = [];
for (const name in pbfLayers) { for (const name in pbfLayers) {
if (layers && layers.indexOf(name) == -1) { if (layers && layers.indexOf(name) == -1) {
@@ -254,11 +249,13 @@ class MVT extends FeatureFormat {
} }
const pbfLayer = pbfLayers[name]; const pbfLayer = pbfLayers[name];
const extent = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
dataProjection.setExtent(extent);
for (let i = 0, ii = pbfLayer.length; i < ii; ++i) { for (let i = 0, ii = pbfLayer.length; i < ii; ++i) {
const rawFeature = readRawFeature(pbf, pbfLayer, i); const rawFeature = readRawFeature(pbf, pbfLayer, i);
features.push(this.createFeature_(pbf, rawFeature)); features.push(this.createFeature_(pbf, rawFeature, options));
} }
this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
} }
return features; return features;

View File

@@ -718,8 +718,7 @@ class WKT extends TextFeature {
* @inheritDoc * @inheritDoc
*/ */
writeGeometryText(geometry, opt_options) { writeGeometryText(geometry, opt_options) {
return encode(/** @type {import("../geom/Geometry.js").default} */ ( return encode(transformGeometryWithOptions(geometry, true, opt_options));
transformGeometryWithOptions(geometry, true, opt_options)));
} }
} }

View File

@@ -16,7 +16,7 @@ import {deflateMultiCoordinatesArray} from './flat/deflate.js';
import {inflateMultiCoordinatesArray} from './flat/inflate.js'; import {inflateMultiCoordinatesArray} from './flat/inflate.js';
import {getInteriorPointsOfMultiArray} from './flat/interiorpoint.js'; import {getInteriorPointsOfMultiArray} from './flat/interiorpoint.js';
import {intersectsLinearRingMultiArray} from './flat/intersectsextent.js'; import {intersectsLinearRingMultiArray} from './flat/intersectsextent.js';
import {linearRingsAreOriented, orientLinearRingsArray} from './flat/orient.js'; import {linearRingssAreOriented, orientLinearRingsArray} from './flat/orient.js';
import {quantizeMultiArray} from './flat/simplify.js'; import {quantizeMultiArray} from './flat/simplify.js';
/** /**
@@ -251,7 +251,7 @@ class MultiPolygon extends SimpleGeometry {
getOrientedFlatCoordinates() { getOrientedFlatCoordinates() {
if (this.orientedRevision_ != this.getRevision()) { if (this.orientedRevision_ != this.getRevision()) {
const flatCoordinates = this.flatCoordinates; const flatCoordinates = this.flatCoordinates;
if (linearRingsAreOriented( if (linearRingssAreOriented(
flatCoordinates, 0, this.endss_, this.stride)) { flatCoordinates, 0, this.endss_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates; this.orientedFlatCoordinates_ = flatCoordinates;
} else { } else {

View File

@@ -16,7 +16,7 @@ import {deflateCoordinatesArray} from './flat/deflate.js';
import {inflateCoordinatesArray} from './flat/inflate.js'; import {inflateCoordinatesArray} from './flat/inflate.js';
import {getInteriorPointOfArray} from './flat/interiorpoint.js'; import {getInteriorPointOfArray} from './flat/interiorpoint.js';
import {intersectsLinearRingArray} from './flat/intersectsextent.js'; import {intersectsLinearRingArray} from './flat/intersectsextent.js';
import {linearRingIsOriented, orientLinearRings} from './flat/orient.js'; import {linearRingsAreOriented, orientLinearRings} from './flat/orient.js';
import {quantizeArray} from './flat/simplify.js'; import {quantizeArray} from './flat/simplify.js';
import {modulo} from '../math.js'; import {modulo} from '../math.js';
@@ -266,7 +266,7 @@ class Polygon extends SimpleGeometry {
getOrientedFlatCoordinates() { getOrientedFlatCoordinates() {
if (this.orientedRevision_ != this.getRevision()) { if (this.orientedRevision_ != this.getRevision()) {
const flatCoordinates = this.flatCoordinates; const flatCoordinates = this.flatCoordinates;
if (linearRingIsOriented( if (linearRingsAreOriented(
flatCoordinates, 0, this.ends_, this.stride)) { flatCoordinates, 0, this.ends_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates; this.orientedFlatCoordinates_ = flatCoordinates;
} else { } else {

View File

@@ -41,7 +41,7 @@ export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
* (counter-clockwise exterior ring and clockwise interior rings). * (counter-clockwise exterior ring and clockwise interior rings).
* @return {boolean} Rings are correctly oriented. * @return {boolean} Rings are correctly oriented.
*/ */
export function linearRingIsOriented(flatCoordinates, offset, ends, stride, opt_right) { export function linearRingsAreOriented(flatCoordinates, offset, ends, stride, opt_right) {
const right = opt_right !== undefined ? opt_right : false; const right = opt_right !== undefined ? opt_right : false;
for (let i = 0, ii = ends.length; i < ii; ++i) { for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i]; const end = ends[i];
@@ -75,12 +75,16 @@ export function linearRingIsOriented(flatCoordinates, offset, ends, stride, opt_
* (counter-clockwise exterior ring and clockwise interior rings). * (counter-clockwise exterior ring and clockwise interior rings).
* @return {boolean} Rings are correctly oriented. * @return {boolean} Rings are correctly oriented.
*/ */
export function linearRingsAreOriented(flatCoordinates, offset, endss, stride, opt_right) { export function linearRingssAreOriented(flatCoordinates, offset, endss, stride, opt_right) {
for (let i = 0, ii = endss.length; i < ii; ++i) { for (let i = 0, ii = endss.length; i < ii; ++i) {
if (!linearRingIsOriented( const ends = endss[i];
flatCoordinates, offset, endss[i], stride, opt_right)) { if (!linearRingsAreOriented(
flatCoordinates, offset, ends, stride, opt_right)) {
return false; return false;
} }
if (ends.length) {
offset = ends[ends.length - 1];
}
} }
return true; return true;
} }

View File

@@ -11,21 +11,19 @@
* @param {number} offset Offset. * @param {number} offset Offset.
* @param {number} end End. * @param {number} end End.
* @param {number} stride Stride. * @param {number} stride Stride.
* @param {function(this: S, import("../../coordinate.js").Coordinate, import("../../coordinate.js").Coordinate): T} callback Function * @param {function(import("../../coordinate.js").Coordinate, import("../../coordinate.js").Coordinate): T} callback Function
* called for each segment. * called for each segment.
* @param {S=} opt_this The object to be used as the value of 'this'
* within callback.
* @return {T|boolean} Value. * @return {T|boolean} Value.
* @template T,S * @template T
*/ */
export function forEach(flatCoordinates, offset, end, stride, callback, opt_this) { export function forEach(flatCoordinates, offset, end, stride, callback) {
const point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]]; const point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]];
const point2 = []; const point2 = [];
let ret; let ret;
for (; (offset + stride) < end; offset += stride) { for (; (offset + stride) < end; offset += stride) {
point2[0] = flatCoordinates[offset + stride]; point2[0] = flatCoordinates[offset + stride];
point2[1] = flatCoordinates[offset + stride + 1]; point2[1] = flatCoordinates[offset + stride + 1];
ret = callback.call(opt_this, point1, point2); ret = callback(point1, point2);
if (ret) { if (ret) {
return ret; return ret;
} }

View File

@@ -44,8 +44,6 @@ export {default as Translate} from './interaction/Translate.js';
* focus. This affects the `MouseWheelZoom` and `DragPan` interactions and is * focus. This affects the `MouseWheelZoom` and `DragPan` interactions and is
* useful when page scroll is desired for maps that do not have the browser's * useful when page scroll is desired for maps that do not have the browser's
* focus. * focus.
* @property {boolean} [constrainResolution=false] Zoom to the closest integer
* zoom level after the wheel/trackpad or pinch gesture ends.
* @property {boolean} [doubleClickZoom=true] Whether double click zoom is * @property {boolean} [doubleClickZoom=true] Whether double click zoom is
* desired. * desired.
* @property {boolean} [keyboard=true] Whether keyboard interaction is desired. * @property {boolean} [keyboard=true] Whether keyboard interaction is desired.
@@ -127,7 +125,6 @@ export function defaults(opt_options) {
const pinchZoom = options.pinchZoom !== undefined ? options.pinchZoom : true; const pinchZoom = options.pinchZoom !== undefined ? options.pinchZoom : true;
if (pinchZoom) { if (pinchZoom) {
interactions.push(new PinchZoom({ interactions.push(new PinchZoom({
constrainResolution: options.constrainResolution,
duration: options.zoomDuration duration: options.zoomDuration
})); }));
} }
@@ -146,7 +143,6 @@ export function defaults(opt_options) {
if (mouseWheelZoom) { if (mouseWheelZoom) {
interactions.push(new MouseWheelZoom({ interactions.push(new MouseWheelZoom({
condition: options.onFocusOnly ? focus : undefined, condition: options.onFocusOnly ? focus : undefined,
constrainResolution: options.constrainResolution,
duration: options.zoomDuration duration: options.zoomDuration
})); }));
} }

View File

@@ -1,8 +1,7 @@
/** /**
* @module ol/interaction/DragPan * @module ol/interaction/DragPan
*/ */
import ViewHint from '../ViewHint.js'; import {scale as scaleCoordinate, rotate as rotateCoordinate} from '../coordinate.js';
import {scale as scaleCoordinate, rotate as rotateCoordinate, add as addCoordinate} from '../coordinate.js';
import {easeOut} from '../easing.js'; import {easeOut} from '../easing.js';
import {noModifierKeys} from '../events/condition.js'; import {noModifierKeys} from '../events/condition.js';
import {FALSE} from '../functions.js'; import {FALSE} from '../functions.js';
@@ -74,10 +73,6 @@ class DragPan extends PointerInteraction {
* @inheritDoc * @inheritDoc
*/ */
handleDragEvent(mapBrowserEvent) { handleDragEvent(mapBrowserEvent) {
if (!this.panning_) {
this.panning_ = true;
this.getMap().getView().setHint(ViewHint.INTERACTING, 1);
}
const targetPointers = this.targetPointers; const targetPointers = this.targetPointers;
const centroid = centroidFromPointers(targetPointers); const centroid = centroidFromPointers(targetPointers);
if (targetPointers.length == this.lastPointersCount_) { if (targetPointers.length == this.lastPointersCount_) {
@@ -85,16 +80,15 @@ class DragPan extends PointerInteraction {
this.kinetic_.update(centroid[0], centroid[1]); this.kinetic_.update(centroid[0], centroid[1]);
} }
if (this.lastCentroid) { if (this.lastCentroid) {
const deltaX = this.lastCentroid[0] - centroid[0]; const delta = [
const deltaY = centroid[1] - this.lastCentroid[1]; this.lastCentroid[0] - centroid[0],
centroid[1] - this.lastCentroid[1]
];
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
const view = map.getView(); const view = map.getView();
let center = [deltaX, deltaY]; scaleCoordinate(delta, view.getResolution());
scaleCoordinate(center, view.getResolution()); rotateCoordinate(delta, view.getRotation());
rotateCoordinate(center, view.getRotation()); view.adjustCenter(delta);
addCoordinate(center, view.getCenter());
center = view.constrainCenter(center);
view.setCenter(center);
} }
} else if (this.kinetic_) { } else if (this.kinetic_) {
// reset so we don't overestimate the kinetic energy after // reset so we don't overestimate the kinetic energy after
@@ -122,14 +116,14 @@ class DragPan extends PointerInteraction {
centerpx[1] - distance * Math.sin(angle) centerpx[1] - distance * Math.sin(angle)
]); ]);
view.animate({ view.animate({
center: view.constrainCenter(dest), center: view.getConstrainedCenter(dest),
duration: 500, duration: 500,
easing: easeOut easing: easeOut
}); });
} }
if (this.panning_) { if (this.panning_) {
this.panning_ = false; this.panning_ = false;
view.setHint(ViewHint.INTERACTING, -1); view.endInteraction();
} }
return false; return false;
} else { } else {
@@ -153,7 +147,11 @@ class DragPan extends PointerInteraction {
this.lastCentroid = null; this.lastCentroid = null;
// stop any current animation // stop any current animation
if (view.getAnimating()) { if (view.getAnimating()) {
view.setCenter(mapBrowserEvent.frameState.viewState.center); view.cancelAnimations();
}
if (!this.panning_) {
this.panning_ = true;
this.getMap().getView().beginInteraction();
} }
if (this.kinetic_) { if (this.kinetic_) {
this.kinetic_.begin(); this.kinetic_.begin();

View File

@@ -2,10 +2,8 @@
* @module ol/interaction/DragRotate * @module ol/interaction/DragRotate
*/ */
import {disable} from '../rotationconstraint.js'; import {disable} from '../rotationconstraint.js';
import ViewHint from '../ViewHint.js';
import {altShiftKeysOnly, mouseOnly, mouseActionButton} from '../events/condition.js'; import {altShiftKeysOnly, mouseOnly, mouseActionButton} from '../events/condition.js';
import {FALSE} from '../functions.js'; import {FALSE} from '../functions.js';
import {rotate, rotateWithoutConstraints} from './Interaction.js';
import PointerInteraction from './Pointer.js'; import PointerInteraction from './Pointer.js';
@@ -80,8 +78,7 @@ class DragRotate extends PointerInteraction {
Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2); Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2);
if (this.lastAngle_ !== undefined) { if (this.lastAngle_ !== undefined) {
const delta = theta - this.lastAngle_; const delta = theta - this.lastAngle_;
const rotation = view.getRotation(); view.adjustRotation(-delta);
rotateWithoutConstraints(view, rotation - delta);
} }
this.lastAngle_ = theta; this.lastAngle_ = theta;
} }
@@ -97,9 +94,7 @@ class DragRotate extends PointerInteraction {
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
const view = map.getView(); const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1); view.endInteraction(this.duration_);
const rotation = view.getRotation();
rotate(view, rotation, undefined, this.duration_);
return false; return false;
} }
@@ -114,7 +109,7 @@ class DragRotate extends PointerInteraction {
if (mouseActionButton(mapBrowserEvent) && this.condition_(mapBrowserEvent)) { if (mouseActionButton(mapBrowserEvent) && this.condition_(mapBrowserEvent)) {
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
map.getView().setHint(ViewHint.INTERACTING, 1); map.getView().beginInteraction();
this.lastAngle_ = undefined; this.lastAngle_ = undefined;
return true; return true;
} else { } else {

View File

@@ -1,10 +1,7 @@
/** /**
* @module ol/interaction/DragRotateAndZoom * @module ol/interaction/DragRotateAndZoom
*/ */
import {disable} from '../rotationconstraint.js';
import ViewHint from '../ViewHint.js';
import {shiftKeyOnly, mouseOnly} from '../events/condition.js'; import {shiftKeyOnly, mouseOnly} from '../events/condition.js';
import {rotate, rotateWithoutConstraints, zoom, zoomWithoutConstraints} from './Interaction.js';
import PointerInteraction from './Pointer.js'; import PointerInteraction from './Pointer.js';
@@ -88,14 +85,13 @@ class DragRotateAndZoom extends PointerInteraction {
const theta = Math.atan2(deltaY, deltaX); const theta = Math.atan2(deltaY, deltaX);
const magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY); const magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
const view = map.getView(); const view = map.getView();
if (view.getConstraints().rotation !== disable && this.lastAngle_ !== undefined) { if (this.lastAngle_ !== undefined) {
const angleDelta = theta - this.lastAngle_; const angleDelta = this.lastAngle_ - theta;
rotateWithoutConstraints(view, view.getRotation() - angleDelta); view.adjustRotation(angleDelta);
} }
this.lastAngle_ = theta; this.lastAngle_ = theta;
if (this.lastMagnitude_ !== undefined) { if (this.lastMagnitude_ !== undefined) {
const resolution = this.lastMagnitude_ * (view.getResolution() / magnitude); view.adjustResolution(this.lastMagnitude_ / magnitude);
zoomWithoutConstraints(view, resolution);
} }
if (this.lastMagnitude_ !== undefined) { if (this.lastMagnitude_ !== undefined) {
this.lastScaleDelta_ = this.lastMagnitude_ / magnitude; this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;
@@ -113,10 +109,8 @@ class DragRotateAndZoom extends PointerInteraction {
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
const view = map.getView(); const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1); const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
const direction = this.lastScaleDelta_ - 1; view.endInteraction(this.duration_, direction);
rotate(view, view.getRotation());
zoom(view, view.getResolution(), undefined, this.duration_, direction);
this.lastScaleDelta_ = 0; this.lastScaleDelta_ = 0;
return false; return false;
} }
@@ -130,7 +124,7 @@ class DragRotateAndZoom extends PointerInteraction {
} }
if (this.condition_(mapBrowserEvent)) { if (this.condition_(mapBrowserEvent)) {
mapBrowserEvent.map.getView().setHint(ViewHint.INTERACTING, 1); mapBrowserEvent.map.getView().beginInteraction();
this.lastAngle_ = undefined; this.lastAngle_ = undefined;
this.lastMagnitude_ = undefined; this.lastMagnitude_ = undefined;
return true; return true;

View File

@@ -80,11 +80,8 @@ function onBoxEnd() {
extent = mapExtent; extent = mapExtent;
} }
const resolution = view.constrainResolution( const resolution = view.getConstrainedResolution(view.getResolutionForExtent(extent, size));
view.getResolutionForExtent(extent, size)); const center = view.getConstrainedCenter(getCenter(extent), resolution);
let center = getCenter(extent);
center = view.constrainCenter(center);
view.animate({ view.animate({
resolution: resolution, resolution: resolution,

View File

@@ -891,7 +891,7 @@ class Draw extends PointerInteraction {
this.sketchFeature_ = null; this.sketchFeature_ = null;
this.sketchPoint_ = null; this.sketchPoint_ = null;
this.sketchLine_ = null; this.sketchLine_ = null;
/** @type {VectorSource} */ (this.overlay_.getSource()).clear(true); this.overlay_.getSource().clear(true);
} }
return sketchFeature; return sketchFeature;
} }
@@ -930,7 +930,7 @@ class Draw extends PointerInteraction {
if (this.sketchPoint_) { if (this.sketchPoint_) {
sketchFeatures.push(this.sketchPoint_); sketchFeatures.push(this.sketchPoint_);
} }
const overlaySource = /** @type {VectorSource} */ (this.overlay_.getSource()); const overlaySource = this.overlay_.getSource();
overlaySource.clear(true); overlaySource.clear(true);
overlaySource.addFeatures(sketchFeatures); overlaySource.addFeatures(sketchFeatures);
} }

View File

@@ -242,7 +242,7 @@ class ExtentInteraction extends PointerInteraction {
extentFeature = new Feature(polygonFromExtent(extent)); extentFeature = new Feature(polygonFromExtent(extent));
} }
this.extentFeature_ = extentFeature; this.extentFeature_ = extentFeature;
/** @type {VectorSource} */ (this.extentOverlay_.getSource()).addFeature(extentFeature); this.extentOverlay_.getSource().addFeature(extentFeature);
} else { } else {
if (!extent) { if (!extent) {
extentFeature.setGeometry(undefined); extentFeature.setGeometry(undefined);
@@ -263,7 +263,7 @@ class ExtentInteraction extends PointerInteraction {
if (!vertexFeature) { if (!vertexFeature) {
vertexFeature = new Feature(new Point(vertex)); vertexFeature = new Feature(new Point(vertex));
this.vertexFeature_ = vertexFeature; this.vertexFeature_ = vertexFeature;
/** @type {VectorSource} */ (this.vertexOverlay_.getSource()).addFeature(vertexFeature); this.vertexOverlay_.getSource().addFeature(vertexFeature);
} else { } else {
const geometry = /** @type {Point} */ (vertexFeature.getGeometry()); const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
geometry.setCoordinates(vertex); geometry.setCoordinates(vertex);

View File

@@ -4,7 +4,6 @@
import BaseObject from '../Object.js'; import BaseObject from '../Object.js';
import {easeOut, linear} from '../easing.js'; import {easeOut, linear} from '../easing.js';
import InteractionProperty from './Property.js'; import InteractionProperty from './Property.js';
import {clamp} from '../math.js';
/** /**
@@ -111,77 +110,15 @@ class Interaction extends BaseObject {
export function pan(view, delta, opt_duration) { export function pan(view, delta, opt_duration) {
const currentCenter = view.getCenter(); const currentCenter = view.getCenter();
if (currentCenter) { if (currentCenter) {
const center = view.constrainCenter( const center = [currentCenter[0] + delta[0], currentCenter[1] + delta[1]];
[currentCenter[0] + delta[0], currentCenter[1] + delta[1]]); view.animate({
if (opt_duration) { duration: opt_duration !== undefined ? opt_duration : 250,
view.animate({ easing: linear,
duration: opt_duration, center: view.getConstrainedCenter(center)
easing: linear, });
center: center
});
} else {
view.setCenter(center);
}
} }
} }
/**
* @param {import("../View.js").default} view View.
* @param {number|undefined} rotation Rotation.
* @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate.
* @param {number=} opt_duration Duration.
*/
export function rotate(view, rotation, opt_anchor, opt_duration) {
rotation = view.constrainRotation(rotation, 0);
rotateWithoutConstraints(view, rotation, opt_anchor, opt_duration);
}
/**
* @param {import("../View.js").default} view View.
* @param {number|undefined} rotation Rotation.
* @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate.
* @param {number=} opt_duration Duration.
*/
export function rotateWithoutConstraints(view, rotation, opt_anchor, opt_duration) {
if (rotation !== undefined) {
const currentRotation = view.getRotation();
const currentCenter = view.getCenter();
if (currentRotation !== undefined && currentCenter && opt_duration > 0) {
view.animate({
rotation: rotation,
anchor: opt_anchor,
duration: opt_duration,
easing: easeOut
});
} else {
view.rotate(rotation, opt_anchor);
}
}
}
/**
* @param {import("../View.js").default} view View.
* @param {number|undefined} resolution Resolution to go to.
* @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate.
* @param {number=} opt_duration Duration.
* @param {number=} opt_direction Zooming direction; > 0 indicates
* zooming out, in which case the constraints system will select
* the largest nearest resolution; < 0 indicates zooming in, in
* which case the constraints system will select the smallest
* nearest resolution; == 0 indicates that the zooming direction
* is unknown/not relevant, in which case the constraints system
* will select the nearest resolution. If not defined 0 is
* assumed.
*/
export function zoom(view, resolution, opt_anchor, opt_duration, opt_direction) {
resolution = view.constrainResolution(resolution, 0, opt_direction);
zoomWithoutConstraints(view, resolution, opt_anchor, opt_duration);
}
/** /**
* @param {import("../View.js").default} view View. * @param {import("../View.js").default} view View.
* @param {number} delta Delta from previous zoom level. * @param {number} delta Delta from previous zoom level.
@@ -189,63 +126,24 @@ export function zoom(view, resolution, opt_anchor, opt_duration, opt_direction)
* @param {number=} opt_duration Duration. * @param {number=} opt_duration Duration.
*/ */
export function zoomByDelta(view, delta, opt_anchor, opt_duration) { export function zoomByDelta(view, delta, opt_anchor, opt_duration) {
const currentResolution = view.getResolution(); const currentZoom = view.getZoom();
let resolution = view.constrainResolution(currentResolution, delta, 0);
if (resolution !== undefined) { if (currentZoom === undefined) {
const resolutions = view.getResolutions(); return;
resolution = clamp(
resolution,
view.getMinResolution() || resolutions[resolutions.length - 1],
view.getMaxResolution() || resolutions[0]);
} }
// If we have a constraint on center, we need to change the anchor so that the const newZoom = view.getConstrainedZoom(currentZoom + delta);
// new center is within the extent. We first calculate the new center, apply const newResolution = view.getResolutionForZoom(newZoom);
// the constraint to it, and then calculate back the anchor
if (opt_anchor && resolution !== undefined && resolution !== currentResolution) {
const currentCenter = view.getCenter();
let center = view.calculateCenterZoom(resolution, opt_anchor);
center = view.constrainCenter(center);
opt_anchor = [ if (view.getAnimating()) {
(resolution * currentCenter[0] - currentResolution * center[0]) / view.cancelAnimations();
(resolution - currentResolution),
(resolution * currentCenter[1] - currentResolution * center[1]) /
(resolution - currentResolution)
];
}
zoomWithoutConstraints(view, resolution, opt_anchor, opt_duration);
}
/**
* @param {import("../View.js").default} view View.
* @param {number|undefined} resolution Resolution to go to.
* @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate.
* @param {number=} opt_duration Duration.
*/
export function zoomWithoutConstraints(view, resolution, opt_anchor, opt_duration) {
if (resolution) {
const currentResolution = view.getResolution();
const currentCenter = view.getCenter();
if (currentResolution !== undefined && currentCenter &&
resolution !== currentResolution && opt_duration) {
view.animate({
resolution: resolution,
anchor: opt_anchor,
duration: opt_duration,
easing: easeOut
});
} else {
if (opt_anchor) {
const center = view.calculateCenterZoom(resolution, opt_anchor);
view.setCenter(center);
}
view.setResolution(resolution);
}
} }
view.animate({
resolution: newResolution,
anchor: opt_anchor,
duration: opt_duration !== undefined ? opt_duration : 250,
easing: easeOut
});
} }
export default Interaction; export default Interaction;

View File

@@ -368,7 +368,7 @@ class Modify extends PointerInteraction {
// Remove the vertex feature if the collection of canditate features // Remove the vertex feature if the collection of canditate features
// is empty. // is empty.
if (this.vertexFeature_ && this.features_.getLength() === 0) { if (this.vertexFeature_ && this.features_.getLength() === 0) {
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_); this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null; this.vertexFeature_ = null;
} }
unlisten(feature, EventType.CHANGE, unlisten(feature, EventType.CHANGE,
@@ -407,7 +407,7 @@ class Modify extends PointerInteraction {
*/ */
setActive(active) { setActive(active) {
if (this.vertexFeature_ && !active) { if (this.vertexFeature_ && !active) {
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_); this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null; this.vertexFeature_ = null;
} }
super.setActive(active); super.setActive(active);
@@ -658,7 +658,7 @@ class Modify extends PointerInteraction {
if (!vertexFeature) { if (!vertexFeature) {
vertexFeature = new Feature(new Point(coordinates)); vertexFeature = new Feature(new Point(coordinates));
this.vertexFeature_ = vertexFeature; this.vertexFeature_ = vertexFeature;
/** @type {VectorSource} */ (this.overlay_.getSource()).addFeature(vertexFeature); this.overlay_.getSource().addFeature(vertexFeature);
} else { } else {
const geometry = /** @type {Point} */ (vertexFeature.getGeometry()); const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
geometry.setCoordinates(coordinates); geometry.setCoordinates(coordinates);
@@ -944,7 +944,7 @@ class Modify extends PointerInteraction {
} }
} }
if (this.vertexFeature_) { if (this.vertexFeature_) {
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_); this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null; this.vertexFeature_ = null;
} }
} }
@@ -1139,7 +1139,7 @@ class Modify extends PointerInteraction {
} }
this.updateSegmentIndices_(geometry, index, segmentData.depth, -1); this.updateSegmentIndices_(geometry, index, segmentData.depth, -1);
if (this.vertexFeature_) { if (this.vertexFeature_) {
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_); this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null; this.vertexFeature_ = null;
} }
dragSegments.length = 0; dragSegments.length = 0;

View File

@@ -1,9 +1,7 @@
/** /**
* @module ol/interaction/MouseWheelZoom * @module ol/interaction/MouseWheelZoom
*/ */
import ViewHint from '../ViewHint.js';
import {always} from '../events/condition.js'; import {always} from '../events/condition.js';
import {easeOut} from '../easing.js';
import EventType from '../events/EventType.js'; import EventType from '../events/EventType.js';
import {DEVICE_PIXEL_RATIO, FIREFOX, SAFARI} from '../has.js'; import {DEVICE_PIXEL_RATIO, FIREFOX, SAFARI} from '../has.js';
import Interaction, {zoomByDelta} from './Interaction.js'; import Interaction, {zoomByDelta} from './Interaction.js';
@@ -34,9 +32,6 @@ export const Mode = {
* {@link module:ol/events/condition~always}. * {@link module:ol/events/condition~always}.
* @property {number} [duration=250] Animation duration in milliseconds. * @property {number} [duration=250] Animation duration in milliseconds.
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds. * @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
* @property {boolean} [constrainResolution=false] When using a trackpad or
* magic mouse, zoom to the closest integer zoom level after the scroll gesture
* ends.
* @property {boolean} [useAnchor=true] Enable zooming using the mouse's * @property {boolean} [useAnchor=true] Enable zooming using the mouse's
* location as the anchor. When set to `false`, zooming in and out will zoom to * location as the anchor. When set to `false`, zooming in and out will zoom to
* the center of the screen instead of zooming on the mouse's location. * the center of the screen instead of zooming on the mouse's location.
@@ -62,7 +57,13 @@ class MouseWheelZoom extends Interaction {
* @private * @private
* @type {number} * @type {number}
*/ */
this.delta_ = 0; this.totalDelta_ = 0;
/**
* @private
* @type {number}
*/
this.lastDelta_ = 0;
/** /**
* @private * @private
@@ -82,12 +83,6 @@ class MouseWheelZoom extends Interaction {
*/ */
this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true; this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true;
/**
* @private
* @type {boolean}
*/
this.constrainResolution_ = options.constrainResolution || false;
/** /**
* @private * @private
* @type {import("../events/condition.js").Condition} * @type {import("../events/condition.js").Condition}
@@ -137,22 +132,15 @@ class MouseWheelZoom extends Interaction {
*/ */
this.trackpadDeltaPerZoom_ = 300; this.trackpadDeltaPerZoom_ = 300;
/**
* The zoom factor by which scroll zooming is allowed to exceed the limits.
* @private
* @type {number}
*/
this.trackpadZoomBuffer_ = 1.5;
} }
/** /**
* @private * @private
*/ */
decrementInteractingHint_() { endInteraction_() {
this.trackpadTimeoutId_ = undefined; this.trackpadTimeoutId_ = undefined;
const view = this.getMap().getView(); const view = this.getMap().getView();
view.setHint(ViewHint.INTERACTING, -1); view.endInteraction(undefined, Math.sign(this.lastDelta_), this.lastAnchor_);
} }
/** /**
@@ -199,6 +187,8 @@ class MouseWheelZoom extends Interaction {
if (delta === 0) { if (delta === 0) {
return false; return false;
} else {
this.lastDelta_ = delta;
} }
const now = Date.now(); const now = Date.now();
@@ -218,55 +208,15 @@ class MouseWheelZoom extends Interaction {
if (this.trackpadTimeoutId_) { if (this.trackpadTimeoutId_) {
clearTimeout(this.trackpadTimeoutId_); clearTimeout(this.trackpadTimeoutId_);
} else { } else {
view.setHint(ViewHint.INTERACTING, 1); view.beginInteraction();
}
this.trackpadTimeoutId_ = setTimeout(this.decrementInteractingHint_.bind(this), this.trackpadEventGap_);
let resolution = view.getResolution() * Math.pow(2, delta / this.trackpadDeltaPerZoom_);
const minResolution = view.getMinResolution();
const maxResolution = view.getMaxResolution();
let rebound = 0;
if (resolution < minResolution) {
resolution = Math.max(resolution, minResolution / this.trackpadZoomBuffer_);
rebound = 1;
} else if (resolution > maxResolution) {
resolution = Math.min(resolution, maxResolution * this.trackpadZoomBuffer_);
rebound = -1;
}
if (this.lastAnchor_) {
const center = view.calculateCenterZoom(resolution, this.lastAnchor_);
view.setCenter(view.constrainCenter(center));
}
view.setResolution(resolution);
if (rebound === 0 && this.constrainResolution_) {
view.animate({
resolution: view.constrainResolution(resolution, delta > 0 ? -1 : 1),
easing: easeOut,
anchor: this.lastAnchor_,
duration: this.duration_
});
}
if (rebound > 0) {
view.animate({
resolution: minResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
} else if (rebound < 0) {
view.animate({
resolution: maxResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
} }
this.trackpadTimeoutId_ = setTimeout(this.endInteraction_.bind(this), this.trackpadEventGap_);
view.adjustZoom(-delta / this.trackpadDeltaPerZoom_, this.lastAnchor_);
this.startTime_ = now; this.startTime_ = now;
return false; return false;
} }
this.delta_ += delta; this.totalDelta_ += delta;
const timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0); const timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0);
@@ -286,10 +236,10 @@ class MouseWheelZoom extends Interaction {
view.cancelAnimations(); view.cancelAnimations();
} }
const maxDelta = MAX_DELTA; const maxDelta = MAX_DELTA;
const delta = clamp(this.delta_, -maxDelta, maxDelta); const delta = clamp(this.totalDelta_, -maxDelta, maxDelta);
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_); zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
this.mode_ = undefined; this.mode_ = undefined;
this.delta_ = 0; this.totalDelta_ = 0;
this.lastAnchor_ = null; this.lastAnchor_ = null;
this.startTime_ = undefined; this.startTime_ = undefined;
this.timeoutId_ = undefined; this.timeoutId_ = undefined;

View File

@@ -1,9 +1,7 @@
/** /**
* @module ol/interaction/PinchRotate * @module ol/interaction/PinchRotate
*/ */
import ViewHint from '../ViewHint.js';
import {FALSE} from '../functions.js'; import {FALSE} from '../functions.js';
import {rotate, rotateWithoutConstraints} from './Interaction.js';
import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js'; import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js';
import {disable} from '../rotationconstraint.js'; import {disable} from '../rotationconstraint.js';
@@ -118,9 +116,8 @@ class PinchRotate extends PointerInteraction {
// rotate // rotate
if (this.rotating_) { if (this.rotating_) {
const rotation = view.getRotation();
map.render(); map.render();
rotateWithoutConstraints(view, rotation + rotationDelta, this.anchor_); view.adjustRotation(rotationDelta, this.anchor_);
} }
} }
@@ -131,11 +128,7 @@ class PinchRotate extends PointerInteraction {
if (this.targetPointers.length < 2) { if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
const view = map.getView(); const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1); view.endInteraction(this.duration_);
if (this.rotating_) {
const rotation = view.getRotation();
rotate(view, rotation, this.anchor_, this.duration_);
}
return false; return false;
} else { } else {
return true; return true;
@@ -153,7 +146,7 @@ class PinchRotate extends PointerInteraction {
this.rotating_ = false; this.rotating_ = false;
this.rotationDelta_ = 0.0; this.rotationDelta_ = 0.0;
if (!this.handlingDownUpSequence) { if (!this.handlingDownUpSequence) {
map.getView().setHint(ViewHint.INTERACTING, 1); map.getView().beginInteraction();
} }
return true; return true;
} else { } else {

View File

@@ -1,17 +1,13 @@
/** /**
* @module ol/interaction/PinchZoom * @module ol/interaction/PinchZoom
*/ */
import ViewHint from '../ViewHint.js';
import {FALSE} from '../functions.js'; import {FALSE} from '../functions.js';
import {zoom, zoomWithoutConstraints} from './Interaction.js';
import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js'; import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js';
/** /**
* @typedef {Object} Options * @typedef {Object} Options
* @property {number} [duration=400] Animation duration in milliseconds. * @property {number} [duration=400] Animation duration in milliseconds.
* @property {boolean} [constrainResolution=false] Zoom to the closest integer
* zoom level after the pinch gesture ends.
*/ */
@@ -37,12 +33,6 @@ class PinchZoom extends PointerInteraction {
super(pointerOptions); super(pointerOptions);
/**
* @private
* @type {boolean}
*/
this.constrainResolution_ = options.constrainResolution || false;
/** /**
* @private * @private
* @type {import("../coordinate.js").Coordinate} * @type {import("../coordinate.js").Coordinate}
@@ -91,17 +81,6 @@ class PinchZoom extends PointerInteraction {
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
const view = map.getView(); const view = map.getView();
const resolution = view.getResolution();
const maxResolution = view.getMaxResolution();
const minResolution = view.getMinResolution();
let newResolution = resolution * scaleDelta;
if (newResolution > maxResolution) {
scaleDelta = maxResolution / resolution;
newResolution = maxResolution;
} else if (newResolution < minResolution) {
scaleDelta = minResolution / resolution;
newResolution = minResolution;
}
if (scaleDelta != 1.0) { if (scaleDelta != 1.0) {
this.lastScaleDelta_ = scaleDelta; this.lastScaleDelta_ = scaleDelta;
@@ -116,7 +95,7 @@ class PinchZoom extends PointerInteraction {
// scale, bypass the resolution constraint // scale, bypass the resolution constraint
map.render(); map.render();
zoomWithoutConstraints(view, newResolution, this.anchor_); view.adjustResolution(scaleDelta, this.anchor_);
} }
/** /**
@@ -126,17 +105,8 @@ class PinchZoom extends PointerInteraction {
if (this.targetPointers.length < 2) { if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
const view = map.getView(); const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1); const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
const resolution = view.getResolution(); view.endInteraction(this.duration_, direction);
if (this.constrainResolution_ ||
resolution < view.getMinResolution() ||
resolution > view.getMaxResolution()) {
// Zoom to final resolution, with an animation, and provide a
// direction not to zoom out/in if user was pinching in/out.
// Direction is > 0 if pinching out, and < 0 if pinching in.
const direction = this.lastScaleDelta_ - 1;
zoom(view, resolution, this.anchor_, this.duration_, direction);
}
return false; return false;
} else { } else {
return true; return true;
@@ -153,7 +123,7 @@ class PinchZoom extends PointerInteraction {
this.lastDistance_ = undefined; this.lastDistance_ = undefined;
this.lastScaleDelta_ = 1; this.lastScaleDelta_ = 1;
if (!this.handlingDownUpSequence) { if (!this.handlingDownUpSequence) {
map.getView().setHint(ViewHint.INTERACTING, 1); map.getView().beginInteraction();
} }
return true; return true;
} else { } else {

View File

@@ -275,7 +275,7 @@ class Select extends Interaction {
* @api * @api
*/ */
getFeatures() { getFeatures() {
return /** @type {VectorSource} */ (this.featureOverlay_.getSource()).getFeaturesCollection(); return this.featureOverlay_.getSource().getFeaturesCollection();
} }
/** /**

View File

@@ -34,6 +34,7 @@ import Layer from './Layer.js';
* property on the layer object; for example, setting `title: 'My Title'` in the * property on the layer object; for example, setting `title: 'My Title'` in the
* options means that `title` is observable, and has get/set accessors. * options means that `title` is observable, and has get/set accessors.
* *
* @extends {Layer<import("../source/Image.js").default>}
* @api * @api
*/ */
class BaseImageLayer extends Layer { class BaseImageLayer extends Layer {
@@ -48,12 +49,4 @@ class BaseImageLayer extends Layer {
} }
/**
* Return the associated {@link module:ol/source/Image source} of the image layer.
* @function
* @return {import("../source/Image.js").default} Source.
* @api
*/
BaseImageLayer.prototype.getSource;
export default BaseImageLayer; export default BaseImageLayer;

View File

@@ -39,6 +39,7 @@ import {assign} from '../obj.js';
* property on the layer object; for example, setting `title: 'My Title'` in the * property on the layer object; for example, setting `title: 'My Title'` in the
* options means that `title` is observable, and has get/set accessors. * options means that `title` is observable, and has get/set accessors.
* *
* @extends {Layer<import("../source/Tile.js").default>}
* @api * @api
*/ */
class BaseTileLayer extends Layer { class BaseTileLayer extends Layer {
@@ -102,13 +103,4 @@ class BaseTileLayer extends Layer {
} }
/**
* Return the associated {@link module:ol/source/Tile tilesource} of the layer.
* @function
* @return {import("../source/Tile.js").default} Source.
* @api
*/
BaseTileLayer.prototype.getSource;
export default BaseTileLayer; export default BaseTileLayer;

View File

@@ -61,6 +61,8 @@ const Property = {
* property on the layer object; for example, setting `title: 'My Title'` in the * property on the layer object; for example, setting `title: 'My Title'` in the
* options means that `title` is observable, and has get/set accessors. * options means that `title` is observable, and has get/set accessors.
* *
* @template {import("../source/Vector.js").default|import("../source/VectorTile.js").default} VectorSourceType
* @extends {Layer<VectorSourceType>}
* @api * @api
*/ */
class BaseVectorLayer extends Layer { class BaseVectorLayer extends Layer {
@@ -131,13 +133,6 @@ class BaseVectorLayer extends Layer {
return this.declutter_; return this.declutter_;
} }
/**
* @param {boolean} declutter Declutter.
*/
setDeclutter(declutter) {
this.declutter_ = declutter;
}
/** /**
* @return {number|undefined} Render buffer. * @return {number|undefined} Render buffer.
*/ */
@@ -219,13 +214,4 @@ class BaseVectorLayer extends Layer {
} }
/**
* Return the associated {@link module:ol/source/Vector vectorsource} of the layer.
* @function
* @return {import("../source/Vector.js").default} Source.
* @api
*/
BaseVectorLayer.prototype.getSource;
export default BaseVectorLayer; export default BaseVectorLayer;

View File

@@ -150,7 +150,11 @@ class Graticule extends VectorLayer {
constructor(opt_options) { constructor(opt_options) {
const options = opt_options ? opt_options : {}; const options = opt_options ? opt_options : {};
const baseOptions = assign({}, options); const baseOptions = assign({
updateWhileAnimating: true,
updateWhileInteracting: true,
renderBuffer: 0
}, options);
delete baseOptions.maxLines; delete baseOptions.maxLines;
delete baseOptions.strokeStyle; delete baseOptions.strokeStyle;
@@ -422,11 +426,6 @@ class Graticule extends VectorLayer {
this.setRenderOrder(null); this.setRenderOrder(null);
this.renderBuffer_ = 0;
this.updateWhileAnimating_ = true;
this.updateWhileInteracting_ = true;
this.tmpExtent_ = null; this.tmpExtent_ = null;
} }
@@ -437,7 +436,7 @@ class Graticule extends VectorLayer {
* @param {import("../proj/Projection.js").default} projection Projection * @param {import("../proj/Projection.js").default} projection Projection
*/ */
loaderFunction(extent, resolution, projection) { loaderFunction(extent, resolution, projection) {
const source = /** @type {import("../source/Vector.js").default} */ (this.getSource()); const source = this.getSource();
// only consider the intersection between our own extent & the requested one // only consider the intersection between our own extent & the requested one
const layerExtent = this.getExtent() || [-Infinity, -Infinity, Infinity, Infinity]; const layerExtent = this.getExtent() || [-Infinity, -Infinity, Infinity, Infinity];

View File

@@ -62,6 +62,8 @@ import SourceState from '../source/State.js';
* *
* @fires import("../render/Event.js").RenderEvent#prerender * @fires import("../render/Event.js").RenderEvent#prerender
* @fires import("../render/Event.js").RenderEvent#postrender * @fires import("../render/Event.js").RenderEvent#postrender
*
* @template {import("../source/Source.js").default} SourceType
*/ */
class Layer extends BaseLayer { class Layer extends BaseLayer {
/** /**
@@ -106,7 +108,7 @@ class Layer extends BaseLayer {
getChangeEventType(LayerProperty.SOURCE), getChangeEventType(LayerProperty.SOURCE),
this.handleSourcePropertyChange_, this); this.handleSourcePropertyChange_, this);
const source = options.source ? options.source : null; const source = options.source ? /** @type {SourceType} */ (options.source) : null;
this.setSource(source); this.setSource(source);
} }
@@ -130,15 +132,12 @@ class Layer extends BaseLayer {
/** /**
* Get the layer source. * Get the layer source.
* @return {import("../source/Source.js").default} The layer source (or `null` if not yet set). * @return {SourceType} The layer source (or `null` if not yet set).
* @observable * @observable
* @api * @api
*/ */
getSource() { getSource() {
const source = this.get(LayerProperty.SOURCE); return /** @type {SourceType} */ (this.get(LayerProperty.SOURCE)) || null;
return (
/** @type {import("../source/Source.js").default} */ (source) || null
);
} }
/** /**
@@ -227,7 +226,7 @@ class Layer extends BaseLayer {
/** /**
* Set the layer source. * Set the layer source.
* @param {import("../source/Source.js").default} source The layer source. * @param {SourceType} source The layer source.
* @observable * @observable
* @api * @api
*/ */

View File

@@ -17,6 +17,7 @@ import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js';
* property on the layer object; for example, setting `title: 'My Title'` in the * property on the layer object; for example, setting `title: 'My Title'` in the
* options means that `title` is observable, and has get/set accessors. * options means that `title` is observable, and has get/set accessors.
* *
* @extends {BaseVectorLayer<import("../source/Vector.js").default>}
* @api * @api
*/ */
class VectorLayer extends BaseVectorLayer { class VectorLayer extends BaseVectorLayer {

View File

@@ -6,7 +6,41 @@ import {assign} from '../obj.js';
import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.js'; import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.js';
/** /**
* @typedef {import("./BaseVector.js").Options} Options * @typedef {Object} Options
* @property {number} [opacity=1] Opacity (0, 1).
* @property {boolean} [visible=true] Visibility.
* @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be
* rendered outside of this extent.
* @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers
* will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed
* for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`
* method was used.
* @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be
* visible.
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible.
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
* features before rendering. By default features are drawn in the order that they are created. Use
* `null` to avoid the sort, but get an undefined draw order.
* @property {number} [renderBuffer=100] The buffer in pixels around the viewport extent used by the
* renderer when getting features from the vector source for the rendering or hit-detection.
* Recommended value: the size of the largest symbol, line width or label.
* @property {import("../source/Vector.js").default} [source] Source.
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
* this layer in its layers collection, and the layer will be rendered on top. This is useful for
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
* use {@link module:ol/Map#addLayer}.
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
* means higher priority.
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will
* be recreated during animations. This means that no vectors will be shown clipped, but the
* setting will have a performance impact for large amounts of vector data. When set to `false`,
* batches will be recreated when no animation is active.
* @property {boolean} [updateWhileInteracting=false] When set to `true`, feature batches will
* be recreated during interactions. See also `updateWhileAnimating`.
* @property {number} [imageRatio=1] Ratio by which the rendered extent should be larger than the * @property {number} [imageRatio=1] Ratio by which the rendered extent should be larger than the
* viewport extent. A larger ratio avoids cut images during panning, but will cause a decrease in performance. * viewport extent. A larger ratio avoids cut images during panning, but will cause a decrease in performance.
*/ */

View File

@@ -33,12 +33,13 @@ import {assign} from '../obj.js';
* point symbol or line width. * point symbol or line width.
* @property {import("./VectorTileRenderType.js").default|string} [renderMode='hybrid'] Render mode for vector tiles: * @property {import("./VectorTileRenderType.js").default|string} [renderMode='hybrid'] Render mode for vector tiles:
* * `'image'`: Vector tiles are rendered as images. Great performance, but point symbols and texts * * `'image'`: Vector tiles are rendered as images. Great performance, but point symbols and texts
* are always rotated with the view and pixels are scaled during zoom animations. * are always rotated with the view and pixels are scaled during zoom animations. When `declutter`
* is set to `true`, the decluttering is done per tile resulting in labels and point symbols getting
* cut off at tile boundaries.
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels are scaled during zoom * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels are scaled during zoom
* animations. Point symbols and texts are accurately rendered as vectors and can stay upright on * animations. Point symbols and texts are accurately rendered as vectors and can stay upright on
* rotated views. * rotated views.
* *
* When `declutter` is set to `true`, `'hybrid'` will be used instead of `'image'`.
* @property {import("../source/VectorTile.js").default} [source] Source. * @property {import("../source/VectorTile.js").default} [source] Source.
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
* this layer in its layers collection, and the layer will be rendered on top. This is useful for * this layer in its layers collection, and the layer will be rendered on top. This is useful for
@@ -46,8 +47,7 @@ import {assign} from '../obj.js';
* use {@link module:ol/Map#addLayer}. * use {@link module:ol/Map#addLayer}.
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all * @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index * image and text styles, and the priority is defined by the z-index of the style. Lower z-index
* means higher priority. When set to `true`, a `renderMode` of `'image'` will be overridden with * means higher priority.
* `'hybrid'`.
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See * @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined. * {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will be * @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will be
@@ -70,6 +70,7 @@ import {assign} from '../obj.js';
* options means that `title` is observable, and has get/set accessors. * options means that `title` is observable, and has get/set accessors.
* *
* @param {Options=} opt_options Options. * @param {Options=} opt_options Options.
* @extends {BaseVectorLayer<import("../source/VectorTile.js").default>}
* @api * @api
*/ */
class VectorTileLayer extends BaseVectorLayer { class VectorTileLayer extends BaseVectorLayer {
@@ -85,16 +86,12 @@ class VectorTileLayer extends BaseVectorLayer {
super(/** @type {import("./Vector.js").Options} */ (baseOptions)); super(/** @type {import("./Vector.js").Options} */ (baseOptions));
let renderMode = options.renderMode || VectorTileRenderType.HYBRID; const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
assert(renderMode == undefined || assert(renderMode == undefined ||
renderMode == VectorTileRenderType.IMAGE || renderMode == VectorTileRenderType.IMAGE ||
renderMode == VectorTileRenderType.HYBRID, renderMode == VectorTileRenderType.HYBRID,
28); // `renderMode` must be `'image'` or `'hybrid'` 28); // `renderMode` must be `'image'` or `'hybrid'`
if (options.declutter && renderMode == VectorTileRenderType.IMAGE) {
renderMode = VectorTileRenderType.HYBRID;
}
/** /**
* @private * @private
* @type {VectorTileRenderType} * @type {VectorTileRenderType}
@@ -165,11 +162,4 @@ class VectorTileLayer extends BaseVectorLayer {
} }
/**
* Return the associated {@link module:ol/source/VectorTile vectortilesource} of the layer.
* @function
* @return {import("../source/VectorTile.js").default} Source.
* @api
*/
VectorTileLayer.prototype.getSource;
export default VectorTileLayer; export default VectorTileLayer;

View File

@@ -188,6 +188,12 @@ export function getPointResolution(projection, resolution, point, opt_units) {
const getter = projection.getPointResolutionFunc(); const getter = projection.getPointResolutionFunc();
if (getter) { if (getter) {
pointResolution = getter(resolution, point); pointResolution = getter(resolution, point);
if (opt_units && opt_units !== projection.getUnits()) {
const metersPerUnit = projection.getMetersPerUnit();
if (metersPerUnit) {
pointResolution = pointResolution * metersPerUnit / METERS_PER_UNIT[opt_units];
}
}
} else { } else {
const units = projection.getUnits(); const units = projection.getUnits();
if (units == Units.DEGREES && !opt_units || opt_units == Units.DEGREES) { if (units == Units.DEGREES && !opt_units || opt_units == Units.DEGREES) {

View File

@@ -19,7 +19,7 @@ import {METERS_PER_UNIT} from './Units.js';
* @property {function(number, import("../coordinate.js").Coordinate):number} [getPointResolution] * @property {function(number, import("../coordinate.js").Coordinate):number} [getPointResolution]
* Function to determine resolution at a point. The function is called with a * Function to determine resolution at a point. The function is called with a
* `{number}` view resolution and an `{import("../coordinate.js").Coordinate}` as arguments, and returns * `{number}` view resolution and an `{import("../coordinate.js").Coordinate}` as arguments, and returns
* the `{number}` resolution at the passed coordinate. If this is `undefined`, * the `{number}` resolution in projection units at the passed coordinate. If this is `undefined`,
* the default {@link module:ol/proj#getPointResolution} function will be used. * the default {@link module:ol/proj#getPointResolution} function will be used.
*/ */

View File

@@ -22,7 +22,7 @@ class RenderBox extends Disposable {
* @type {HTMLDivElement} * @type {HTMLDivElement}
* @private * @private
*/ */
this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); this.element_ = document.createElement('div');
this.element_.style.position = 'absolute'; this.element_.style.position = 'absolute';
this.element_.className = 'ol-box ' + className; this.element_.className = 'ol-box ' + className;

View File

@@ -22,81 +22,82 @@ const tmpTransform = createTransform();
* Lightweight, read-only, {@link module:ol/Feature~Feature} and {@link module:ol/geom/Geometry~Geometry} like * Lightweight, read-only, {@link module:ol/Feature~Feature} and {@link module:ol/geom/Geometry~Geometry} like
* structure, optimized for vector tile rendering and styling. Geometry access * structure, optimized for vector tile rendering and styling. Geometry access
* through the API is limited to getting the type and extent of the geometry. * through the API is limited to getting the type and extent of the geometry.
*
* @param {GeometryType} type Geometry type.
* @param {Array<number>} flatCoordinates Flat coordinates. These always need
* to be right-handed for polygons.
* @param {Array<number>|Array<Array<number>>} ends Ends or Endss.
* @param {Object<string, *>} properties Properties.
* @param {number|string|undefined} id Feature id.
*/ */
class RenderFeature { class RenderFeature {
/**
* @param {GeometryType} type Geometry type.
* @param {Array<number>} flatCoordinates Flat coordinates. These always need
* to be right-handed for polygons.
* @param {Array<number>|Array<Array<number>>} ends Ends or Endss.
* @param {Object<string, *>} properties Properties.
* @param {number|string|undefined} id Feature id.
*/
constructor(type, flatCoordinates, ends, properties, id) { constructor(type, flatCoordinates, ends, properties, id) {
/** /**
* @private * @private
* @type {import("../extent.js").Extent|undefined} * @type {import("../extent.js").Extent|undefined}
*/ */
this.extent_; this.extent_;
/** /**
* @private * @private
* @type {number|string|undefined} * @type {number|string|undefined}
*/ */
this.id_ = id; this.id_ = id;
/** /**
* @private * @private
* @type {GeometryType} * @type {GeometryType}
*/ */
this.type_ = type; this.type_ = type;
/** /**
* @private * @private
* @type {Array<number>} * @type {Array<number>}
*/ */
this.flatCoordinates_ = flatCoordinates; this.flatCoordinates_ = flatCoordinates;
/** /**
* @private * @private
* @type {Array<number>} * @type {Array<number>}
*/ */
this.flatInteriorPoints_ = null; this.flatInteriorPoints_ = null;
/** /**
* @private * @private
* @type {Array<number>} * @type {Array<number>}
*/ */
this.flatMidpoints_ = null; this.flatMidpoints_ = null;
/** /**
* @private * @private
* @type {Array<number>|Array<Array<number>>} * @type {Array<number>|Array<Array<number>>}
*/ */
this.ends_ = ends; this.ends_ = ends;
/** /**
* @private * @private
* @type {Object<string, *>} * @type {Object<string, *>}
*/ */
this.properties_ = properties; this.properties_ = properties;
} }
/** /**
* Get a feature property by its key. * Get a feature property by its key.
* @param {string} key Key * @param {string} key Key
* @return {*} Value for the requested key. * @return {*} Value for the requested key.
* @api * @api
*/ */
get(key) { get(key) {
return this.properties_[key]; return this.properties_[key];
} }
/** /**
* Get the extent of this feature's geometry. * Get the extent of this feature's geometry.
* @return {import("../extent.js").Extent} Extent. * @return {import("../extent.js").Extent} Extent.
* @api * @api
*/ */
getExtent() { getExtent() {
if (!this.extent_) { if (!this.extent_) {
this.extent_ = this.type_ === GeometryType.POINT ? this.extent_ = this.type_ === GeometryType.POINT ?
@@ -109,8 +110,8 @@ class RenderFeature {
} }
/** /**
* @return {Array<number>} Flat interior points. * @return {Array<number>} Flat interior points.
*/ */
getFlatInteriorPoint() { getFlatInteriorPoint() {
if (!this.flatInteriorPoints_) { if (!this.flatInteriorPoints_) {
const flatCenter = getCenter(this.getExtent()); const flatCenter = getCenter(this.getExtent());
@@ -121,8 +122,8 @@ class RenderFeature {
} }
/** /**
* @return {Array<number>} Flat interior points. * @return {Array<number>} Flat interior points.
*/ */
getFlatInteriorPoints() { getFlatInteriorPoints() {
if (!this.flatInteriorPoints_) { if (!this.flatInteriorPoints_) {
const flatCenters = linearRingssCenter( const flatCenters = linearRingssCenter(
@@ -134,8 +135,8 @@ class RenderFeature {
} }
/** /**
* @return {Array<number>} Flat midpoint. * @return {Array<number>} Flat midpoint.
*/ */
getFlatMidpoint() { getFlatMidpoint() {
if (!this.flatMidpoints_) { if (!this.flatMidpoints_) {
this.flatMidpoints_ = interpolatePoint( this.flatMidpoints_ = interpolatePoint(
@@ -145,8 +146,8 @@ class RenderFeature {
} }
/** /**
* @return {Array<number>} Flat midpoints. * @return {Array<number>} Flat midpoints.
*/ */
getFlatMidpoints() { getFlatMidpoints() {
if (!this.flatMidpoints_) { if (!this.flatMidpoints_) {
this.flatMidpoints_ = []; this.flatMidpoints_ = [];
@@ -165,28 +166,28 @@ class RenderFeature {
} }
/** /**
* Get the feature identifier. This is a stable identifier for the feature and * Get the feature identifier. This is a stable identifier for the feature and
* is set when reading data from a remote source. * is set when reading data from a remote source.
* @return {number|string|undefined} Id. * @return {number|string|undefined} Id.
* @api * @api
*/ */
getId() { getId() {
return this.id_; return this.id_;
} }
/** /**
* @return {Array<number>} Flat coordinates. * @return {Array<number>} Flat coordinates.
*/ */
getOrientedFlatCoordinates() { getOrientedFlatCoordinates() {
return this.flatCoordinates_; return this.flatCoordinates_;
} }
/** /**
* For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when * For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when
* determining the geometry type in style function (see {@link #getType}). * determining the geometry type in style function (see {@link #getType}).
* @return {RenderFeature} Feature. * @return {RenderFeature} Feature.
* @api * @api
*/ */
getGeometry() { getGeometry() {
return this; return this;
} }
@@ -200,17 +201,17 @@ class RenderFeature {
} }
/** /**
* Get the feature properties. * Get the feature properties.
* @return {Object<string, *>} Feature properties. * @return {Object<string, *>} Feature properties.
* @api * @api
*/ */
getProperties() { getProperties() {
return this.properties_; return this.properties_;
} }
/** /**
* @return {number} Stride. * @return {number} Stride.
*/ */
getStride() { getStride() {
return 2; return 2;
} }
@@ -223,21 +224,21 @@ class RenderFeature {
} }
/** /**
* Get the type of this feature's geometry. * Get the type of this feature's geometry.
* @return {GeometryType} Geometry type. * @return {GeometryType} Geometry type.
* @api * @api
*/ */
getType() { getType() {
return this.type_; return this.type_;
} }
/** /**
* Transform geometry coordinates from tile pixel space to projected. * Transform geometry coordinates from tile pixel space to projected.
* The SRS of the source and destination are expected to be the same. * The SRS of the source and destination are expected to be the same.
* *
* @param {import("../proj.js").ProjectionLike} source The current projection * @param {import("../proj.js").ProjectionLike} source The current projection
* @param {import("../proj.js").ProjectionLike} destination The desired projection. * @param {import("../proj.js").ProjectionLike} destination The desired projection.
*/ */
transform(source, destination) { transform(source, destination) {
source = getProjection(source); source = getProjection(source);
const pixelExtent = source.getExtent(); const pixelExtent = source.getExtent();

View File

@@ -288,22 +288,22 @@ function getMeasureContext() {
* @return {import("../size.js").Size} Measurement. * @return {import("../size.js").Size} Measurement.
*/ */
export const measureTextHeight = (function() { export const measureTextHeight = (function() {
let span; let div;
const heights = textHeights; const heights = textHeights;
return function(font) { return function(font) {
let height = heights[font]; let height = heights[font];
if (height == undefined) { if (height == undefined) {
if (!span) { if (!div) {
span = document.createElement('span'); div = document.createElement('div');
span.textContent = 'M'; div.innerHTML = 'M';
span.style.margin = span.style.padding = '0 !important'; div.style.margin = div.style.padding = '0 !important';
span.style.position = 'absolute !important'; div.style.position = 'absolute !important';
span.style.left = '-99999px !important'; div.style.left = '-99999px !important';
} }
span.style.font = font; div.style.font = font;
document.body.appendChild(span); document.body.appendChild(div);
height = heights[font] = span.offsetHeight; height = heights[font] = div.offsetHeight;
document.body.removeChild(span); document.body.removeChild(div);
} }
return height; return height;
}; };

View File

@@ -206,7 +206,7 @@ class CanvasBuilder extends VectorContext {
* @inheritDoc. * @inheritDoc.
*/ */
drawCustom(geometry, feature, renderer) { drawCustom(geometry, feature, renderer) {
this.beginGeometry(geometry, feature); this.beginGeometry(feature);
const type = geometry.getType(); const type = geometry.getType();
const stride = geometry.getStride(); const stride = geometry.getStride();
const builderBegin = this.coordinates.length; const builderBegin = this.coordinates.length;
@@ -248,15 +248,14 @@ class CanvasBuilder extends VectorContext {
this.instructions.push([CanvasInstruction.CUSTOM, this.instructions.push([CanvasInstruction.CUSTOM,
builderBegin, builderEnd, geometry, renderer]); builderBegin, builderEnd, geometry, renderer]);
} }
this.endGeometry(geometry, feature); this.endGeometry(feature);
} }
/** /**
* @protected * @protected
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature. * @param {import("../../Feature.js").FeatureLike} feature Feature.
*/ */
beginGeometry(geometry, feature) { beginGeometry(feature) {
this.beginGeometryInstruction1_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0]; this.beginGeometryInstruction1_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0];
this.instructions.push(this.beginGeometryInstruction1_); this.instructions.push(this.beginGeometryInstruction1_);
this.beginGeometryInstruction2_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0]; this.beginGeometryInstruction2_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0];
@@ -353,10 +352,9 @@ class CanvasBuilder extends VectorContext {
/** /**
* @param {import("../canvas.js").FillStrokeState} state State. * @param {import("../canvas.js").FillStrokeState} state State.
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
* @return {Array<*>} Fill instruction. * @return {Array<*>} Fill instruction.
*/ */
createFill(state, geometry) { createFill(state) {
const fillStyle = state.fillStyle; const fillStyle = state.fillStyle;
/** @type {Array<*>} */ /** @type {Array<*>} */
const fillInstruction = [CanvasInstruction.SET_FILL_STYLE, fillStyle]; const fillInstruction = [CanvasInstruction.SET_FILL_STYLE, fillStyle];
@@ -389,14 +387,13 @@ class CanvasBuilder extends VectorContext {
/** /**
* @param {import("../canvas.js").FillStrokeState} state State. * @param {import("../canvas.js").FillStrokeState} state State.
* @param {function(this:CanvasBuilder, import("../canvas.js").FillStrokeState, (import("../../geom/Geometry.js").default|import("../Feature.js").default)):Array<*>} createFill Create fill. * @param {function(this:CanvasBuilder, import("../canvas.js").FillStrokeState):Array<*>} createFill Create fill.
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
*/ */
updateFillStyle(state, createFill, geometry) { updateFillStyle(state, createFill) {
const fillStyle = state.fillStyle; const fillStyle = state.fillStyle;
if (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle) { if (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle) {
if (fillStyle !== undefined) { if (fillStyle !== undefined) {
this.instructions.push(createFill.call(this, state, geometry)); this.instructions.push(createFill.call(this, state));
} }
state.currentFillStyle = fillStyle; state.currentFillStyle = fillStyle;
} }
@@ -435,10 +432,9 @@ class CanvasBuilder extends VectorContext {
} }
/** /**
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature. * @param {import("../../Feature.js").FeatureLike} feature Feature.
*/ */
endGeometry(geometry, feature) { endGeometry(feature) {
this.beginGeometryInstruction1_[2] = this.instructions.length; this.beginGeometryInstruction1_[2] = this.instructions.length;
this.beginGeometryInstruction1_ = null; this.beginGeometryInstruction1_ = null;
this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length; this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length;

View File

@@ -143,6 +143,8 @@ class ExecutorGroup extends Disposable {
executors[key].disposeInternal(); executors[key].disposeInternal();
} }
} }
const canvas = this.hitDetectionContext_.canvas;
canvas.width = canvas.height = 0;
super.disposeInternal(); super.disposeInternal();
} }

View File

@@ -113,7 +113,7 @@ class CanvasImageBuilder extends CanvasBuilder {
if (!this.image_) { if (!this.image_) {
return; return;
} }
this.beginGeometry(pointGeometry, feature); this.beginGeometry(feature);
const flatCoordinates = pointGeometry.getFlatCoordinates(); const flatCoordinates = pointGeometry.getFlatCoordinates();
const stride = pointGeometry.getStride(); const stride = pointGeometry.getStride();
const myBegin = this.coordinates.length; const myBegin = this.coordinates.length;
@@ -132,7 +132,7 @@ class CanvasImageBuilder extends CanvasBuilder {
this.originX_, this.originY_, this.rotateWithView_, this.rotation_, this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.width_ this.scale_, this.width_
]); ]);
this.endGeometry(pointGeometry, feature); this.endGeometry(feature);
} }
/** /**
@@ -142,7 +142,7 @@ class CanvasImageBuilder extends CanvasBuilder {
if (!this.image_) { if (!this.image_) {
return; return;
} }
this.beginGeometry(multiPointGeometry, feature); this.beginGeometry(feature);
const flatCoordinates = multiPointGeometry.getFlatCoordinates(); const flatCoordinates = multiPointGeometry.getFlatCoordinates();
const stride = multiPointGeometry.getStride(); const stride = multiPointGeometry.getStride();
const myBegin = this.coordinates.length; const myBegin = this.coordinates.length;
@@ -162,7 +162,7 @@ class CanvasImageBuilder extends CanvasBuilder {
this.originX_, this.originY_, this.rotateWithView_, this.rotation_, this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.width_ this.scale_, this.width_
]); ]);
this.endGeometry(multiPointGeometry, feature); this.endGeometry(feature);
} }
/** /**

View File

@@ -44,7 +44,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
return; return;
} }
this.updateStrokeStyle(state, this.applyStroke); this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(lineStringGeometry, feature); this.beginGeometry(feature);
this.hitDetectionInstructions.push([ this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE, CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
@@ -54,7 +54,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
const stride = lineStringGeometry.getStride(); const stride = lineStringGeometry.getStride();
this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
this.hitDetectionInstructions.push(strokeInstruction); this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(lineStringGeometry, feature); this.endGeometry(feature);
} }
/** /**
@@ -68,7 +68,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
return; return;
} }
this.updateStrokeStyle(state, this.applyStroke); this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(multiLineStringGeometry, feature); this.beginGeometry(feature);
this.hitDetectionInstructions.push([ this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE, CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
@@ -82,7 +82,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride); offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride);
} }
this.hitDetectionInstructions.push(strokeInstruction); this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(multiLineStringGeometry, feature); this.endGeometry(feature);
} }
/** /**

View File

@@ -72,8 +72,8 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (fillStyle === undefined && strokeStyle === undefined) { if (fillStyle === undefined && strokeStyle === undefined) {
return; return;
} }
this.setFillStrokeStyles_(circleGeometry); this.setFillStrokeStyles_();
this.beginGeometry(circleGeometry, feature); this.beginGeometry(feature);
if (state.fillStyle !== undefined) { if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([ this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE, CanvasInstruction.SET_FILL_STYLE,
@@ -103,7 +103,7 @@ class CanvasPolygonBuilder extends CanvasBuilder {
this.instructions.push(strokeInstruction); this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction); this.hitDetectionInstructions.push(strokeInstruction);
} }
this.endGeometry(circleGeometry, feature); this.endGeometry(feature);
} }
/** /**
@@ -116,8 +116,8 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (fillStyle === undefined && strokeStyle === undefined) { if (fillStyle === undefined && strokeStyle === undefined) {
return; return;
} }
this.setFillStrokeStyles_(polygonGeometry); this.setFillStrokeStyles_();
this.beginGeometry(polygonGeometry, feature); this.beginGeometry(feature);
if (state.fillStyle !== undefined) { if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([ this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE, CanvasInstruction.SET_FILL_STYLE,
@@ -135,7 +135,7 @@ class CanvasPolygonBuilder extends CanvasBuilder {
const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates(); const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
const stride = polygonGeometry.getStride(); const stride = polygonGeometry.getStride();
this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride); this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride);
this.endGeometry(polygonGeometry, feature); this.endGeometry(feature);
} }
/** /**
@@ -148,8 +148,8 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (fillStyle === undefined && strokeStyle === undefined) { if (fillStyle === undefined && strokeStyle === undefined) {
return; return;
} }
this.setFillStrokeStyles_(multiPolygonGeometry); this.setFillStrokeStyles_();
this.beginGeometry(multiPolygonGeometry, feature); this.beginGeometry(feature);
if (state.fillStyle !== undefined) { if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([ this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE, CanvasInstruction.SET_FILL_STYLE,
@@ -170,7 +170,7 @@ class CanvasPolygonBuilder extends CanvasBuilder {
for (let i = 0, ii = endss.length; i < ii; ++i) { for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride); offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride);
} }
this.endGeometry(multiPolygonGeometry, feature); this.endGeometry(feature);
} }
/** /**
@@ -195,13 +195,12 @@ class CanvasPolygonBuilder extends CanvasBuilder {
/** /**
* @private * @private
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
*/ */
setFillStrokeStyles_(geometry) { setFillStrokeStyles_() {
const state = this.state; const state = this.state;
const fillStyle = state.fillStyle; const fillStyle = state.fillStyle;
if (fillStyle !== undefined) { if (fillStyle !== undefined) {
this.updateFillStyle(state, this.createFill, geometry); this.updateFillStyle(state, this.createFill);
} }
if (state.strokeStyle !== undefined) { if (state.strokeStyle !== undefined) {
this.updateStrokeStyle(state, this.applyStroke); this.updateStrokeStyle(state, this.applyStroke);

View File

@@ -184,7 +184,7 @@ class CanvasTextBuilder extends CanvasBuilder {
ends.push(endss[i][0]); ends.push(endss[i][0]);
} }
} }
this.beginGeometry(geometry, feature); this.beginGeometry(feature);
const textAlign = textState.textAlign; const textAlign = textState.textAlign;
let flatOffset = 0; let flatOffset = 0;
let flatEnd; let flatEnd;
@@ -204,7 +204,7 @@ class CanvasTextBuilder extends CanvasBuilder {
this.drawChars_(begin, end, this.declutterGroup_); this.drawChars_(begin, end, this.declutterGroup_);
begin = end; begin = end;
} }
this.endGeometry(geometry, feature); this.endGeometry(feature);
} else { } else {
@@ -258,8 +258,8 @@ class CanvasTextBuilder extends CanvasBuilder {
if (textState.backgroundFill || textState.backgroundStroke) { if (textState.backgroundFill || textState.backgroundStroke) {
this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke); this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke);
if (textState.backgroundFill) { if (textState.backgroundFill) {
this.updateFillStyle(this.state, this.createFill, geometry); this.updateFillStyle(this.state, this.createFill);
this.hitDetectionInstructions.push(this.createFill(this.state, geometry)); this.hitDetectionInstructions.push(this.createFill(this.state));
} }
if (textState.backgroundStroke) { if (textState.backgroundStroke) {
this.updateStrokeStyle(this.state, this.applyStroke); this.updateStrokeStyle(this.state, this.applyStroke);
@@ -267,7 +267,7 @@ class CanvasTextBuilder extends CanvasBuilder {
} }
} }
this.beginGeometry(geometry, feature); this.beginGeometry(feature);
// The image is unknown at this stage so we pass null; it will be computed at render time. // The image is unknown at this stage so we pass null; it will be computed at render time.
// For clarity, we pass NaN for offsetX, offsetY, width and height, which will be computed at // For clarity, we pass NaN for offsetX, offsetY, width and height, which will be computed at
@@ -293,7 +293,7 @@ class CanvasTextBuilder extends CanvasBuilder {
this.textOffsetX_, this.textOffsetY_, geometryWidths this.textOffsetX_, this.textOffsetY_, geometryWidths
]); ]);
this.endGeometry(geometry, feature); this.endGeometry(feature);
} }
} }

View File

@@ -221,10 +221,8 @@ class LayerRenderer extends Observable {
* @param {import("../extent.js").Extent} extent Extent. * @param {import("../extent.js").Extent} extent Extent.
* @param {number} currentZ Current Z. * @param {number} currentZ Current Z.
* @param {number} preload Load low resolution tiles up to 'preload' levels. * @param {number} preload Load low resolution tiles up to 'preload' levels.
* @param {function(this: T, import("../Tile.js").default)=} opt_tileCallback Tile callback. * @param {function(import("../Tile.js").default)=} opt_tileCallback Tile callback.
* @param {T=} opt_this Object to use as `this` in `opt_tileCallback`.
* @protected * @protected
* @template T
*/ */
manageTilePyramid( manageTilePyramid(
frameState, frameState,
@@ -235,8 +233,7 @@ class LayerRenderer extends Observable {
extent, extent,
currentZ, currentZ,
preload, preload,
opt_tileCallback, opt_tileCallback
opt_this
) { ) {
const tileSourceKey = getUid(tileSource); const tileSourceKey = getUid(tileSource);
if (!(tileSourceKey in frameState.wantedTiles)) { if (!(tileSourceKey in frameState.wantedTiles)) {
@@ -261,7 +258,7 @@ class LayerRenderer extends Observable {
} }
} }
if (opt_tileCallback !== undefined) { if (opt_tileCallback !== undefined) {
opt_tileCallback.call(opt_this, tile); opt_tileCallback(tile);
} }
} else { } else {
tileSource.useTile(z, x, y, projection); tileSource.useTile(z, x, y, projection);

View File

@@ -305,12 +305,4 @@ function getLayerStatesMap(layerStatesArray) {
}, {}); }, {});
} }
/**
* @param {import("../layer/Layer.js").State} state1 First layer state.
* @param {import("../layer/Layer.js").State} state2 Second layer state.
* @return {number} The zIndex difference.
*/
export function sortByZIndex(state1, state2) {
return state1.zIndex - state2.zIndex;
}
export default MapRenderer; export default MapRenderer;

View File

@@ -43,8 +43,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
const viewState = frameState.viewState; const viewState = frameState.viewState;
const viewResolution = viewState.resolution; const viewResolution = viewState.resolution;
const imageLayer = /** @type {import("../../layer/Image.js").default} */ (this.getLayer()); const imageSource = this.getLayer().getSource();
const imageSource = /** @type {import("../../source/Image.js").default} */ (imageLayer.getSource());
const hints = frameState.viewHints; const hints = frameState.viewHints;

View File

@@ -63,6 +63,14 @@ class CanvasLayerRenderer extends LayerRenderer {
canvas.className = this.getLayer().getClassName(); canvas.className = this.getLayer().getClassName();
} }
/**
* @inheritDoc
*/
disposeInternal() {
this.context.canvas.width = this.context.canvas.height = 0;
super.disposeInternal();
}
/** /**
* @param {CanvasRenderingContext2D} context Context. * @param {CanvasRenderingContext2D} context Context.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state. * @param {import("../../PluggableMap.js").FrameState} frameState Frame state.

View File

@@ -88,7 +88,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
*/ */
getTile(z, x, y, pixelRatio, projection) { getTile(z, x, y, pixelRatio, projection) {
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer()); const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
const tileSource = /** @type {import("../../source/Tile.js").default} */ (tileLayer.getSource()); const tileSource = tileLayer.getSource();
let tile = tileSource.getTile(z, x, y, pixelRatio, projection); let tile = tileSource.getTile(z, x, y, pixelRatio, projection);
if (tile.getState() == TileState.ERROR) { if (tile.getState() == TileState.ERROR) {
if (!tileLayer.getUseInterimTilesOnError()) { if (!tileLayer.getUseInterimTilesOnError()) {
@@ -105,6 +105,16 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
return tile; return tile;
} }
/**
* @inheritDoc
*/
loadedTileCallback(tiles, zoom, tile) {
if (this.isDrawableTile(tile)) {
return super.loadedTileCallback(tiles, zoom, tile);
}
return false;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -130,7 +140,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const pixelRatio = frameState.pixelRatio; const pixelRatio = frameState.pixelRatio;
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer()); const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
const tileSource = /** @type {import("../../source/Tile.js").default} */ (tileLayer.getSource()); const tileSource = tileLayer.getSource();
const sourceRevision = tileSource.getRevision(); const sourceRevision = tileSource.getRevision();
const tileGrid = tileSource.getTileGridForProjection(projection); const tileGrid = tileSource.getTileGridForProjection(projection);
const z = tileGrid.getZForResolution(viewResolution, this.zDirection); const z = tileGrid.getZForResolution(viewResolution, this.zDirection);
@@ -199,7 +209,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
covered = findLoadedTiles(z + 1, childTileRange); covered = findLoadedTiles(z + 1, childTileRange);
} }
if (!covered) { if (!covered) {
tileGrid.forEachTileCoordParentTileRange(tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); tileGrid.forEachTileCoordParentTileRange(tile.tileCoord, findLoadedTiles, tmpTileRange, tmpExtent);
} }
} }
@@ -281,7 +291,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const w = nextX - x; const w = nextX - x;
const h = nextY - y; const h = nextY - y;
this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ); this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, z === currentZ);
this.renderedTiles.push(tile); this.renderedTiles.push(tile);
this.updateUsedTiles(frameState.usedTiles, tileSource, tile); this.updateUsedTiles(frameState.usedTiles, tileSource, tile);
} }
@@ -318,7 +328,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
/** /**
* @param {import("../../Tile.js").default} tile Tile. * @param {import("../../Tile.js").default} tile Tile.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state. * @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../layer/Layer.js").State} layerState Layer state.
* @param {number} x Left of the tile. * @param {number} x Left of the tile.
* @param {number} y Top of the tile. * @param {number} y Top of the tile.
* @param {number} w Width of the tile. * @param {number} w Width of the tile.
@@ -326,7 +335,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
* @param {number} gutter Tile gutter. * @param {number} gutter Tile gutter.
* @param {boolean} transition Apply an alpha transition. * @param {boolean} transition Apply an alpha transition.
*/ */
drawTileImage(tile, frameState, layerState, x, y, w, h, gutter, transition) { drawTileImage(tile, frameState, x, y, w, h, gutter, transition) {
const image = this.getTileImage(tile); const image = this.getTileImage(tile);
if (!image) { if (!image) {
return; return;
@@ -334,7 +343,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const uid = getUid(this); const uid = getUid(this);
const alpha = transition ? tile.getAlpha(uid, frameState.time) : 1; const alpha = transition ? tile.getAlpha(uid, frameState.time) : 1;
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer()); const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
const tileSource = /** @type {import("../../source/Tile.js").default} */ (tileLayer.getSource()); const tileSource = tileLayer.getSource();
if (alpha === 1 && !tileSource.getOpaque(frameState.viewState.projection)) { if (alpha === 1 && !tileSource.getOpaque(frameState.viewState.projection)) {
this.context.clearRect(x, y, w, h); this.context.clearRect(x, y, w, h);
} }

View File

@@ -8,6 +8,9 @@ import {getHeight, getWidth, isEmpty, scaleFromCenter} from '../../extent.js';
import {assign} from '../../obj.js'; import {assign} from '../../obj.js';
import CanvasImageLayerRenderer from './ImageLayer.js'; import CanvasImageLayerRenderer from './ImageLayer.js';
import CanvasVectorLayerRenderer from './VectorLayer.js'; import CanvasVectorLayerRenderer from './VectorLayer.js';
import {listen} from '../../events.js';
import EventType from '../../events/EventType.js';
import ImageState from '../../ImageState.js';
/** /**
* @classdesc * @classdesc
@@ -82,17 +85,19 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
if (vectorRenderer.prepareFrame(imageFrameState, layerState) && if (vectorRenderer.prepareFrame(imageFrameState, layerState) &&
(vectorRenderer.replayGroupChanged || (vectorRenderer.replayGroupChanged ||
!equals(skippedFeatures, newSkippedFeatures))) { !equals(skippedFeatures, newSkippedFeatures))) {
context.canvas.width = imageFrameState.size[0] * pixelRatio;
context.canvas.height = imageFrameState.size[1] * pixelRatio;
vectorRenderer.renderFrame(imageFrameState, layerState); vectorRenderer.renderFrame(imageFrameState, layerState);
skippedFeatures = newSkippedFeatures; skippedFeatures = newSkippedFeatures;
callback(); callback();
} }
}); });
if (this.loadImage(image)) {
this.image_ = image; listen(image, EventType.CHANGE, function() {
this.skippedFeatures_ = skippedFeatures; if (image.getState() === ImageState.LOADED) {
} this.image_ = image;
this.skippedFeatures_ = skippedFeatures;
}
}, this);
image.load();
} }
if (this.image_) { if (this.image_) {

View File

@@ -129,7 +129,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
const projection = viewState.projection; const projection = viewState.projection;
const rotation = viewState.rotation; const rotation = viewState.rotation;
const projectionExtent = projection.getExtent(); const projectionExtent = projection.getExtent();
const vectorSource = /** @type {import("../../source/Vector.js").default} */ (this.getLayer().getSource()); const vectorSource = this.getLayer().getSource();
// clipped rendering if layer extent is set // clipped rendering if layer extent is set
const clipExtent = layerState.extent; const clipExtent = layerState.extent;
@@ -196,7 +196,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
} else { } else {
const resolution = frameState.viewState.resolution; const resolution = frameState.viewState.resolution;
const rotation = frameState.viewState.rotation; const rotation = frameState.viewState.rotation;
const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const layer = this.getLayer();
/** @type {!Object<string, boolean>} */ /** @type {!Object<string, boolean>} */
const features = {}; const features = {};
const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {},
@@ -239,7 +239,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
*/ */
prepareFrame(frameState, layerState) { prepareFrame(frameState, layerState) {
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
const vectorSource = /** @type {import("../../source/Vector.js").default} */ (vectorLayer.getSource()); const vectorSource = vectorLayer.getSource();
const animating = frameState.viewHints[ViewHint.ANIMATING]; const animating = frameState.viewHints[ViewHint.ANIMATING];
const interacting = frameState.viewHints[ViewHint.INTERACTING]; const interacting = frameState.viewHints[ViewHint.INTERACTING];

View File

@@ -10,8 +10,6 @@ import EventType from '../../events/EventType.js';
import rbush from 'rbush'; import rbush from 'rbush';
import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js'; import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js';
import VectorTileRenderType from '../../layer/VectorTileRenderType.js'; import VectorTileRenderType from '../../layer/VectorTileRenderType.js';
import {equivalent as equivalentProjection} from '../../proj.js';
import Units from '../../proj/Units.js';
import ReplayType from '../../render/canvas/BuilderType.js'; import ReplayType from '../../render/canvas/BuilderType.js';
import {labelCache} from '../../render/canvas.js'; import {labelCache} from '../../render/canvas.js';
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js'; import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
@@ -152,6 +150,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/ */
disposeInternal() { disposeInternal() {
unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this);
this.overlayContext_.canvas.width = this.overlayContext_.canvas.height = 0;
super.disposeInternal(); super.disposeInternal();
} }
@@ -205,16 +204,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc * @inheritDoc
*/ */
getTileImage(tile) { getTileImage(tile) {
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer()); return tile.getImage(this.getLayer());
return tile.getImage(tileLayer);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
prepareFrame(frameState, layerState) { prepareFrame(frameState, layerState) {
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const layerRevision = this.getLayer().getRevision();
const layerRevision = layer.getRevision();
if (this.renderedLayerRevision_ != layerRevision) { if (this.renderedLayerRevision_ != layerRevision) {
this.renderedTiles.length = 0; this.renderedTiles.length = 0;
} }
@@ -229,9 +226,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @private * @private
*/ */
updateExecutorGroup_(tile, pixelRatio, projection) { updateExecutorGroup_(tile, pixelRatio, projection) {
const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const revision = layer.getRevision(); const revision = layer.getRevision();
const renderOrder = /** @type {import("../../render.js").OrderFunction} */ (layer.getRenderOrder()) || null; const renderOrder = layer.getRenderOrder() || null;
const builderState = tile.getReplayState(layer); const builderState = tile.getReplayState(layer);
if (!builderState.dirty && builderState.renderedRevision == revision && if (!builderState.dirty && builderState.renderedRevision == revision &&
@@ -239,7 +236,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
return; return;
} }
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource()); const source = layer.getSource();
const sourceTileGrid = source.getTileGrid(); const sourceTileGrid = source.getTileGrid();
const tileGrid = source.getTileGridForProjection(projection); const tileGrid = source.getTileGridForProjection(projection);
const zoom = tile.tileCoord[0]; const zoom = tile.tileCoord[0];
@@ -265,12 +262,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const sharedExtent = getIntersection(tileExtent, sourceTileExtent); const sharedExtent = getIntersection(tileExtent, sourceTileExtent);
const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null : const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null :
buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent); buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent);
const tileProjection = sourceTile.getProjection();
let reproject = false;
if (!equivalentProjection(projection, tileProjection)) {
reproject = true;
sourceTile.setProjection(projection);
}
builderState.dirty = false; builderState.dirty = false;
const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution, const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution,
pixelRatio, !!this.declutterTree_); pixelRatio, !!this.declutterTree_);
@@ -299,15 +290,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
} }
for (let i = 0, ii = features.length; i < ii; ++i) { for (let i = 0, ii = features.length; i < ii; ++i) {
const feature = features[i]; const feature = features[i];
if (reproject) {
if (tileProjection.getUnits() == Units.TILE_PIXELS) {
// projected tile extent
tileProjection.setWorldExtent(sourceTileExtent);
// tile extent in tile pixel space
tileProjection.setExtent(sourceTile.getExtent());
}
feature.getGeometry().transform(tileProjection, projection);
}
if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) { if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) {
render.call(this, feature); render.call(this, feature);
} }
@@ -334,21 +316,24 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const rotation = frameState.viewState.rotation; const rotation = frameState.viewState.rotation;
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance; hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
const layer = this.getLayer(); const layer = this.getLayer();
const source = /** @type {import("../../source/VectorTile").default} */ (layer.getSource()); const source = layer.getSource();
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection); const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
/** @type {!Object<string, boolean>} */ /** @type {!Object<string, boolean>} */
const features = {}; const features = {};
const renderedTiles = /** @type {Array<import("../../VectorRenderTile.js").default>} */ (this.renderedTiles); const renderedTiles = /** @type {Array<import("../../VectorRenderTile.js").default>} */ (this.renderedTiles);
let bufferedExtent, found; let found;
let i, ii; let i, ii;
for (i = 0, ii = renderedTiles.length; i < ii; ++i) { for (i = 0, ii = renderedTiles.length; i < ii; ++i) {
const tile = renderedTiles[i]; const tile = renderedTiles[i];
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); if (!this.declutterTree_) {
bufferedExtent = buffer(tileExtent, hitTolerance * resolution, bufferedExtent); // When not decluttering, we only need to consider the tile that contains the given
if (!containsCoordinate(bufferedExtent, coordinate)) { // coordinate, because each feature will be rendered for each tile that contains it.
continue; const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
if (!containsCoordinate(tileExtent, coordinate)) {
continue;
}
} }
const executorGroups = tile.executorGroups[getUid(layer)]; const executorGroups = tile.executorGroups[getUid(layer)];
for (let t = 0, tt = executorGroups.length; t < tt; ++t) { for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
@@ -359,7 +344,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @return {?} Callback result. * @return {?} Callback result.
*/ */
function(feature) { function(feature) {
const key = getUid(feature); let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) { if (!(key in features)) {
features[key] = true; features[key] = true;
return callback.call(thisArg, feature, layer); return callback.call(thisArg, feature, layer);
@@ -378,7 +366,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/ */
getReplayTransform_(tile, frameState) { getReplayTransform_(tile, frameState) {
const layer = this.getLayer(); const layer = this.getLayer();
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource()); const source = layer.getSource();
const tileGrid = source.getTileGrid(); const tileGrid = source.getTileGrid();
const tileCoord = tile.tileCoord; const tileCoord = tile.tileCoord;
const tileResolution = tileGrid.getResolution(tileCoord[0]); const tileResolution = tileGrid.getResolution(tileCoord[0]);
@@ -435,7 +423,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const context = this.overlayContext_; const context = this.overlayContext_;
const declutterReplays = layer.getDeclutter() ? {} : null; const declutterReplays = layer.getDeclutter() ? {} : null;
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource()); const source = layer.getSource();
const replayTypes = VECTOR_REPLAYS[renderMode]; const replayTypes = VECTOR_REPLAYS[renderMode];
const pixelRatio = frameState.pixelRatio; const pixelRatio = frameState.pixelRatio;
const rotation = frameState.viewState.rotation; const rotation = frameState.viewState.rotation;
@@ -547,6 +535,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const tile = this.renderTileImageQueue_[uid]; const tile = this.renderTileImageQueue_[uid];
frameState.animate = true; frameState.animate = true;
delete this.renderTileImageQueue_[uid]; delete this.renderTileImageQueue_[uid];
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
if (this.declutterTree_ && layer.getRenderMode() === VectorTileRenderType.IMAGE) {
this.declutterTree_.clear();
}
this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection); this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection);
} }
clear(this.renderTileImageQueue_); clear(this.renderTileImageQueue_);
@@ -608,7 +600,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
replayState.renderedTileZ = tile.sourceZ; replayState.renderedTileZ = tile.sourceZ;
const tileCoord = tile.wrappedTileCoord; const tileCoord = tile.wrappedTileCoord;
const z = tileCoord[0]; const z = tileCoord[0];
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource()); const source = layer.getSource();
const tileGrid = source.getTileGridForProjection(projection); const tileGrid = source.getTileGridForProjection(projection);
const resolution = tileGrid.getResolution(z); const resolution = tileGrid.getResolution(z);
const context = tile.getContext(layer); const context = tile.getContext(layer);

View File

@@ -264,7 +264,7 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
*/ */
prepareFrame(frameState) { prepareFrame(frameState) {
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
const vectorSource = /** @type {import("../../source/Vector.js").default} */ (vectorLayer.getSource()); const vectorSource = vectorLayer.getSource();
const stride = 12; const stride = 12;

View File

@@ -2,37 +2,87 @@
* @module ol/resolutionconstraint * @module ol/resolutionconstraint
*/ */
import {linearFindNearest} from './array.js'; import {linearFindNearest} from './array.js';
import {clamp} from './math.js'; import {getHeight, getWidth} from './extent';
import {clamp} from './math';
/** /**
* @typedef {function((number|undefined), number, number): (number|undefined)} Type * @typedef {function((number|undefined), number, import("./size.js").Size, boolean=): (number|undefined)} Type
*/ */
/**
* Returns a modified resolution taking into acocunt the viewport size and maximum
* allowed extent.
* @param {number} resolution Resolution
* @param {import("./extent.js").Extent=} maxExtent Maximum allowed extent.
* @param {import("./size.js").Size} viewportSize Viewport size.
* @return {number} Capped resolution.
*/
function getViewportClampedResolution(resolution, maxExtent, viewportSize) {
const xResolution = getWidth(maxExtent) / viewportSize[0];
const yResolution = getHeight(maxExtent) / viewportSize[1];
return Math.min(resolution, Math.min(xResolution, yResolution));
}
/**
* Returns a modified resolution to be between maxResolution and minResolution while
* still allowing the value to be slightly out of bounds.
* Note: the computation is based on the logarithm function (ln):
* - at 1, ln(x) is 0
* - above 1, ln(x) keeps increasing but at a much slower pace than x
* The final result is clamped to prevent getting too far away from bounds.
* @param {number} resolution Resolution.
* @param {number} maxResolution Max resolution.
* @param {number} minResolution Min resolution.
* @return {number} Smoothed resolution.
*/
function getSmoothClampedResolution(resolution, maxResolution, minResolution) {
let result = Math.min(resolution, maxResolution);
const ratio = 50;
result *= Math.log(1 + ratio * Math.max(0, resolution / maxResolution - 1)) / ratio + 1;
if (minResolution) {
result = Math.max(result, minResolution);
result /= Math.log(1 + ratio * Math.max(0, minResolution / resolution - 1)) / ratio + 1;
}
return clamp(result, minResolution / 2, maxResolution * 2);
}
/** /**
* @param {Array<number>} resolutions Resolutions. * @param {Array<number>} resolutions Resolutions.
* @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.
* @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent.
* @return {Type} Zoom function. * @return {Type} Zoom function.
*/ */
export function createSnapToResolutions(resolutions) { export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent) {
return ( return (
/** /**
* @param {number|undefined} resolution Resolution. * @param {number|undefined} resolution Resolution.
* @param {number} delta Delta.
* @param {number} direction Direction. * @param {number} direction Direction.
* @param {import("./size.js").Size} size Viewport size.
* @param {boolean=} opt_isMoving True if an interaction or animation is in progress.
* @return {number|undefined} Resolution. * @return {number|undefined} Resolution.
*/ */
function(resolution, delta, direction) { function(resolution, direction, size, opt_isMoving) {
if (resolution !== undefined) { if (resolution !== undefined) {
let z = linearFindNearest(resolutions, resolution, direction); const maxResolution = resolutions[0];
z = clamp(z + delta, 0, resolutions.length - 1); const minResolution = resolutions[resolutions.length - 1];
const index = Math.floor(z); const cappedMaxRes = opt_maxExtent ?
if (z != index && index < resolutions.length - 1) { getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
const power = resolutions[index] / resolutions[index + 1]; maxResolution;
return resolutions[index] / Math.pow(power, z - index);
} else { // during interacting or animating, allow intermediary values
return resolutions[index]; if (opt_isMoving) {
const smooth = opt_smooth !== undefined ? opt_smooth : true;
if (!smooth) {
return clamp(resolution, minResolution, cappedMaxRes);
}
return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
} }
const capped = Math.min(cappedMaxRes, resolution);
const z = Math.floor(linearFindNearest(resolutions, capped, direction));
return resolutions[z];
} else { } else {
return undefined; return undefined;
} }
@@ -44,29 +94,78 @@ export function createSnapToResolutions(resolutions) {
/** /**
* @param {number} power Power. * @param {number} power Power.
* @param {number} maxResolution Maximum resolution. * @param {number} maxResolution Maximum resolution.
* @param {number=} opt_maxLevel Maximum level. * @param {number=} opt_minResolution Minimum resolution.
* @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.
* @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent.
* @return {Type} Zoom function. * @return {Type} Zoom function.
*/ */
export function createSnapToPower(power, maxResolution, opt_maxLevel) { export function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent) {
return ( return (
/** /**
* @param {number|undefined} resolution Resolution. * @param {number|undefined} resolution Resolution.
* @param {number} delta Delta.
* @param {number} direction Direction. * @param {number} direction Direction.
* @param {import("./size.js").Size} size Viewport size.
* @param {boolean=} opt_isMoving True if an interaction or animation is in progress.
* @return {number|undefined} Resolution. * @return {number|undefined} Resolution.
*/ */
function(resolution, delta, direction) { function(resolution, direction, size, opt_isMoving) {
if (resolution !== undefined) { if (resolution !== undefined) {
const offset = -direction / 2 + 0.5; const cappedMaxRes = opt_maxExtent ?
const oldLevel = Math.floor( getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
Math.log(maxResolution / resolution) / Math.log(power) + offset); maxResolution;
let newLevel = Math.max(oldLevel + delta, 0); const minResolution = opt_minResolution !== undefined ? opt_minResolution : 0;
if (opt_maxLevel !== undefined) {
newLevel = Math.min(newLevel, opt_maxLevel); // during interacting or animating, allow intermediary values
if (opt_isMoving) {
const smooth = opt_smooth !== undefined ? opt_smooth : true;
if (!smooth) {
return clamp(resolution, minResolution, cappedMaxRes);
}
return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
} }
return maxResolution / Math.pow(power, newLevel);
const offset = -direction * (0.5 - 1e-9) + 0.5;
const capped = Math.min(cappedMaxRes, resolution);
const zoomLevel = Math.floor(
Math.log(maxResolution / capped) / Math.log(power) + offset);
const newResolution = maxResolution / Math.pow(power, zoomLevel);
return clamp(newResolution, minResolution, cappedMaxRes);
} else { } else {
return undefined; return undefined;
} }
}); });
} }
/**
* @param {number} maxResolution Max resolution.
* @param {number} minResolution Min resolution.
* @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.
* @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent.
* @return {Type} Zoom function.
*/
export function createMinMaxResolution(maxResolution, minResolution, opt_smooth, opt_maxExtent) {
return (
/**
* @param {number|undefined} resolution Resolution.
* @param {number} direction Direction.
* @param {import("./size.js").Size} size Viewport size.
* @param {boolean=} opt_isMoving True if an interaction or animation is in progress.
* @return {number|undefined} Resolution.
*/
function(resolution, direction, size, opt_isMoving) {
if (resolution !== undefined) {
const cappedMaxRes = opt_maxExtent ?
getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
maxResolution;
const smooth = opt_smooth !== undefined ? opt_smooth : true;
if (!smooth || !opt_isMoving) {
return clamp(resolution, minResolution, cappedMaxRes);
}
return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
} else {
return undefined;
}
}
);
}

View File

@@ -5,16 +5,15 @@ import {toRadians} from './math.js';
/** /**
* @typedef {function((number|undefined), number): (number|undefined)} Type * @typedef {function((number|undefined), boolean=): (number|undefined)} Type
*/ */
/** /**
* @param {number|undefined} rotation Rotation. * @param {number|undefined} rotation Rotation.
* @param {number} delta Delta.
* @return {number|undefined} Rotation. * @return {number|undefined} Rotation.
*/ */
export function disable(rotation, delta) { export function disable(rotation) {
if (rotation !== undefined) { if (rotation !== undefined) {
return 0; return 0;
} else { } else {
@@ -25,12 +24,11 @@ export function disable(rotation, delta) {
/** /**
* @param {number|undefined} rotation Rotation. * @param {number|undefined} rotation Rotation.
* @param {number} delta Delta.
* @return {number|undefined} Rotation. * @return {number|undefined} Rotation.
*/ */
export function none(rotation, delta) { export function none(rotation) {
if (rotation !== undefined) { if (rotation !== undefined) {
return rotation + delta; return rotation;
} else { } else {
return undefined; return undefined;
} }
@@ -46,12 +44,16 @@ export function createSnapToN(n) {
return ( return (
/** /**
* @param {number|undefined} rotation Rotation. * @param {number|undefined} rotation Rotation.
* @param {number} delta Delta. * @param {boolean=} opt_isMoving True if an interaction or animation is in progress.
* @return {number|undefined} Rotation. * @return {number|undefined} Rotation.
*/ */
function(rotation, delta) { function(rotation, opt_isMoving) {
if (opt_isMoving) {
return rotation;
}
if (rotation !== undefined) { if (rotation !== undefined) {
rotation = Math.floor((rotation + delta) / theta + 0.5) * theta; rotation = Math.floor(rotation / theta + 0.5) * theta;
return rotation; return rotation;
} else { } else {
return undefined; return undefined;
@@ -69,15 +71,19 @@ export function createSnapToZero(opt_tolerance) {
return ( return (
/** /**
* @param {number|undefined} rotation Rotation. * @param {number|undefined} rotation Rotation.
* @param {number} delta Delta. * @param {boolean} opt_isMoving True if an interaction or animation is in progress.
* @return {number|undefined} Rotation. * @return {number|undefined} Rotation.
*/ */
function(rotation, delta) { function(rotation, opt_isMoving) {
if (opt_isMoving) {
return rotation;
}
if (rotation !== undefined) { if (rotation !== undefined) {
if (Math.abs(rotation + delta) <= tolerance) { if (Math.abs(rotation) <= tolerance) {
return 0; return 0;
} else { } else {
return rotation + delta; return rotation;
} }
} else { } else {
return undefined; return undefined;

View File

@@ -145,7 +145,7 @@ class Source extends BaseObject {
} }
/** /**
* Refreshes the source and finally dispatches a 'change' event. * Refreshes the source. The source will be cleared, and data from the server will be reloaded.
* @api * @api
*/ */
refresh() { refresh() {

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