Compare commits
205 Commits
v6.0.0-bet
...
v6.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
937b57c7d9 | ||
|
|
dfabcc0c66 | ||
|
|
3282c74de6 | ||
|
|
dee36639bd | ||
|
|
5a578c00a7 | ||
|
|
e8243b73b5 | ||
|
|
404b04ae59 | ||
|
|
8a02a6f9f1 | ||
|
|
4a67bd86a8 | ||
|
|
feb1a9f31b | ||
|
|
8a5720e8cb | ||
|
|
9ad2924f00 | ||
|
|
0401aed6e3 | ||
|
|
b2722542fe | ||
|
|
7002053678 | ||
|
|
425cd19922 | ||
|
|
4a7b3cde56 | ||
|
|
9c1c71b8e5 | ||
|
|
6de566d95b | ||
|
|
a372ca0569 | ||
|
|
1e06dd979e | ||
|
|
c44e6ebe1d | ||
|
|
c8fab42d75 | ||
|
|
0995f95ef1 | ||
|
|
447266cbe8 | ||
|
|
d25a534dea | ||
|
|
90749cd80f | ||
|
|
f1aca67882 | ||
|
|
d7905896f7 | ||
|
|
a7af9a0697 | ||
|
|
eea67868f7 | ||
|
|
d99405feeb | ||
|
|
14831a0d61 | ||
|
|
43bc53b20b | ||
|
|
c01dee0884 | ||
|
|
6fd83b9a8e | ||
|
|
05712acfef | ||
|
|
cd17f4b0d6 | ||
|
|
7c7b43ee91 | ||
|
|
02f9411c6b | ||
|
|
1338a868e9 | ||
|
|
6b1f9e681c | ||
|
|
65d8b5f26b | ||
|
|
530e47d26c | ||
|
|
85f2312370 | ||
|
|
301b45cb7c | ||
|
|
3aa93014c0 | ||
|
|
734900f1d7 | ||
|
|
fb2ea83838 | ||
|
|
8534aaa304 | ||
|
|
36cf654f09 | ||
|
|
4e8522e696 | ||
|
|
88760de64b | ||
|
|
b5f7705e1d | ||
|
|
9efe10f747 | ||
|
|
73744de14e | ||
|
|
d6c18667f4 | ||
|
|
cd28c8a301 | ||
|
|
79c6cc5159 | ||
|
|
345607be28 | ||
|
|
f67baa0dc0 | ||
|
|
75c379beeb | ||
|
|
405e206717 | ||
|
|
c2af03f152 | ||
|
|
7835869582 | ||
|
|
78e8f23df5 | ||
|
|
433bccd207 | ||
|
|
ef6d17d817 | ||
|
|
e023c144bb | ||
|
|
b5273babb5 | ||
|
|
49662b019c | ||
|
|
48ad1ffcbf | ||
|
|
cd186ada7f | ||
|
|
a6f65df8c4 | ||
|
|
1c5fd62e43 | ||
|
|
1f379a06a4 | ||
|
|
e52fab636c | ||
|
|
d991dfa54a | ||
|
|
c2c1aa01d3 | ||
|
|
e6c4b2ffd1 | ||
|
|
3c1e3779e2 | ||
|
|
4e1ece16ed | ||
|
|
1cb934dbe3 | ||
|
|
767c765524 | ||
|
|
67ee5a41b0 | ||
|
|
5c280e8114 | ||
|
|
bc25097899 | ||
|
|
6ce499532c | ||
|
|
386f2dc67c | ||
|
|
90034e4d48 | ||
|
|
4166c80c6e | ||
|
|
d06d00ccbb | ||
|
|
959dba169d | ||
|
|
900adaaffb | ||
|
|
94cd126189 | ||
|
|
1416a3d162 | ||
|
|
e4873a9952 | ||
|
|
8d1022046e | ||
|
|
f40cbf2cac | ||
|
|
a0ba8dd8c6 | ||
|
|
8557bd96b5 | ||
|
|
3d4f77be51 | ||
|
|
62d82411c8 | ||
|
|
c3709ef51a | ||
|
|
071e9a4735 | ||
|
|
0c889da99c | ||
|
|
a41f51c437 | ||
|
|
4cb3e24048 | ||
|
|
4cb9b1eeb3 | ||
|
|
f0a97ee460 | ||
|
|
7c1e16abc3 | ||
|
|
84995c688c | ||
|
|
cefb5d4d32 | ||
|
|
8eadb3d04c | ||
|
|
a6023a710a | ||
|
|
33ac3e34ee | ||
|
|
3c64018b37 | ||
|
|
b01970cb86 | ||
|
|
ffa24bfd22 | ||
|
|
be17bfe85a | ||
|
|
974684ed2b | ||
|
|
f90a41a7ca | ||
|
|
df847ae35c | ||
|
|
746455a9b9 | ||
|
|
3557247b27 | ||
|
|
658a4a9194 | ||
|
|
ec6371d7a0 | ||
|
|
6bab8793a2 | ||
|
|
45b2e8885a | ||
|
|
40b5891ea7 | ||
|
|
667cadc403 | ||
|
|
08be6cf9bc | ||
|
|
fd07de39ad | ||
|
|
44942dffb5 | ||
|
|
f7b1fe07d0 | ||
|
|
9a7e5747eb | ||
|
|
e4264b94ed | ||
|
|
e9ecea3bb7 | ||
|
|
391dfc5025 | ||
|
|
3da449d77e | ||
|
|
7a66dc5774 | ||
|
|
74f7b1974d | ||
|
|
6b94aaa424 | ||
|
|
c4ed80dd92 | ||
|
|
7a73638fb2 | ||
|
|
0ece4a7dae | ||
|
|
6fe8b5c49d | ||
|
|
2ba4be2661 | ||
|
|
a2a3bda5c9 | ||
|
|
f28b7c7bcf | ||
|
|
27d025327f | ||
|
|
020f513ed5 | ||
|
|
30fb0b1ed1 | ||
|
|
40605d7c53 | ||
|
|
666c14d190 | ||
|
|
0a005527e7 | ||
|
|
c0a860a31f | ||
|
|
3e82da4beb | ||
|
|
b71d7773d3 | ||
|
|
7d5b2d8c3b | ||
|
|
dc298b8895 | ||
|
|
693c763710 | ||
|
|
680f3cde3c | ||
|
|
3ef6635c09 | ||
|
|
8028ce3ac5 | ||
|
|
3557a13147 | ||
|
|
f6e625f21a | ||
|
|
d1c71c3c61 | ||
|
|
6f8ffddace | ||
|
|
438736068e | ||
|
|
bfe8ee5309 | ||
|
|
703dadfcde | ||
|
|
fecb8de769 | ||
|
|
b3bc78daec | ||
|
|
d24ae3c2ac | ||
|
|
2879c0b6ad | ||
|
|
5ef1b51c02 | ||
|
|
41d231a4c7 | ||
|
|
f4fe1babd2 | ||
|
|
efc85ed0ed | ||
|
|
be16c2357d | ||
|
|
88c213078e | ||
|
|
ee57b197e5 | ||
|
|
2c859b1196 | ||
|
|
c4be22b1b6 | ||
|
|
b00b877ca1 | ||
|
|
168edac4a6 | ||
|
|
caa9153dd0 | ||
|
|
ba02320fcc | ||
|
|
427c73ddf9 | ||
|
|
2c3ffdfdf8 | ||
|
|
97b21145c4 | ||
|
|
2428c0984e | ||
|
|
e0aad192af | ||
|
|
e34806ad70 | ||
|
|
a0fde2b24d | ||
|
|
abfe6b4359 | ||
|
|
acd8c34535 | ||
|
|
1a1c7dec87 | ||
|
|
5e4ee77968 | ||
|
|
9f54e9cb4d | ||
|
|
0b53a3229b | ||
|
|
5318d52036 | ||
|
|
4d54ad0daf | ||
|
|
37b1cca027 |
12
README.md
12
README.md
@@ -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 [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
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable JavaScript support
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
// Point to the JSDoc typed sources when using modules from the ol package
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
@@ -57,11 +56,14 @@ The `ol` package contains a `src/` folder with JSDoc annotated sources. TypeScri
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
// Include JavaScript files from the ol package
|
||||
"**/*.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
|
||||
|
||||
|
||||
@@ -4,11 +4,31 @@
|
||||
|
||||
#### 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 standard ECMAScript classes should be used instead.
|
||||
|
||||
The deprecated `getSnapToPixel` and `setSnapToPixel` functions from the `ImageStyle` class have been removed.
|
||||
|
||||
##### 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.
|
||||
@@ -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.
|
||||
|
||||
##### 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
|
||||
|
||||
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.
|
||||
|
||||
##### `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
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
##### 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
|
||||
|
||||
`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.
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* 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. */
|
||||
// "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. */
|
||||
|
||||
29
doc/faq.md
29
doc/faq.md
@@ -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-)
|
||||
* [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 is zooming or clicking off, inaccurate?](#user-content-why-is-zooming-or-clicking-off-inaccurate)
|
||||
|
||||
## 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.
|
||||
|
||||
## 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()
|
||||
```
|
||||
|
||||
@@ -12,9 +12,8 @@ cloak:
|
||||
<div id="map" class="map"></div>
|
||||
<select id="layer-select">
|
||||
<option value="Aerial">Aerial</option>
|
||||
<option value="AerialWithLabels" selected>Aerial with labels</option>
|
||||
<option value="Road">Road (static)</option>
|
||||
<option value="RoadOnDemand">Road (dynamic)</option>
|
||||
<option value="collinsBart">Collins Bart</option>
|
||||
<option value="ordnanceSurvey">Ordnance Survey</option>
|
||||
<option value="AerialWithLabelsOnDemand" selected>Aerial with labels</option>
|
||||
<option value="RoadOnDemand">Road</option>
|
||||
<option value="CanvasDark">Road dark</option>
|
||||
<option value="OrdnanceSurvey">Ordnance Survey</option>
|
||||
</select>
|
||||
|
||||
@@ -5,12 +5,11 @@ import BingMaps from '../src/ol/source/BingMaps.js';
|
||||
|
||||
|
||||
const styles = [
|
||||
'Road',
|
||||
'RoadOnDemand',
|
||||
'Aerial',
|
||||
'AerialWithLabels',
|
||||
'collinsBart',
|
||||
'ordnanceSurvey'
|
||||
'AerialWithLabelsOnDemand',
|
||||
'CanvasDark',
|
||||
'OrdnanceSurvey'
|
||||
];
|
||||
const layers = [];
|
||||
let i, ii;
|
||||
|
||||
@@ -21,8 +21,6 @@ tags: "center, rotation, openstreetmap"
|
||||
<div class="padding-bottom"></div>
|
||||
<div class="center"></div>
|
||||
</div>
|
||||
<button id="zoomtoswitzerlandbest">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="zoomtoswitzerland">Zoom to Switzerland</button> (best fit).<br/>
|
||||
<button id="zoomtolausanne">Zoom to Lausanne</button> (with min resolution),<br/>
|
||||
<button id="centerlausanne">Center on Lausanne</button>
|
||||
|
||||
@@ -47,29 +47,14 @@ const map = new Map({
|
||||
view: view
|
||||
});
|
||||
|
||||
const zoomtoswitzerlandbest = document.getElementById('zoomtoswitzerlandbest');
|
||||
zoomtoswitzerlandbest.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], constrainResolution: false});
|
||||
}, false);
|
||||
|
||||
const zoomtoswitzerlandconstrained =
|
||||
document.getElementById('zoomtoswitzerlandconstrained');
|
||||
zoomtoswitzerlandconstrained.addEventListener('click', function() {
|
||||
const zoomtoswitzerland =
|
||||
document.getElementById('zoomtoswitzerland');
|
||||
zoomtoswitzerland.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]});
|
||||
}, 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');
|
||||
zoomtolausanne.addEventListener('click', function() {
|
||||
const feature = source.getFeatures()[1];
|
||||
|
||||
@@ -26,9 +26,8 @@ const styleFunction = function(feature) {
|
||||
scale = size / 10;
|
||||
let style = styleCache[size];
|
||||
if (!style) {
|
||||
const canvas = /** @type {HTMLCanvasElement} */ (document.createElement('canvas'));
|
||||
const vectorContext = toContext(
|
||||
/** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')),
|
||||
const canvas = document.createElement('canvas');
|
||||
const vectorContext = toContext(canvas.getContext('2d'),
|
||||
{size: [size, size], pixelRatio: 1});
|
||||
vectorContext.setStyle(new Style({
|
||||
fill: new Fill({color: 'rgba(255, 153, 0, 0.4)'}),
|
||||
|
||||
@@ -63,7 +63,10 @@ exportButton.addEventListener('click', function() {
|
||||
pdf.save('map.pdf');
|
||||
// Reset original map size
|
||||
map.setSize(size);
|
||||
map.getView().fit(extent, {size});
|
||||
map.getView().fit(extent, {
|
||||
size: size,
|
||||
constrainResolution: false
|
||||
});
|
||||
exportButton.disabled = false;
|
||||
document.body.style.cursor = 'auto';
|
||||
});
|
||||
|
||||
9
examples/extent-constrained.html
Normal file
9
examples/extent-constrained.html
Normal 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>
|
||||
25
examples/extent-constrained.js
Normal file
25
examples/extent-constrained.js
Normal 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()])
|
||||
});
|
||||
@@ -133,7 +133,7 @@ const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new BingMaps({
|
||||
imagerySet: 'AerialWithLabels',
|
||||
imagerySet: 'AerialWithLabelsOnDemand',
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
|
||||
})
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
.map:-moz-full-screen {
|
||||
height: 100%;
|
||||
}
|
||||
.map:-webkit-full-screen {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
.fullscreen:-moz-full-screen {
|
||||
height: 100%;
|
||||
}
|
||||
.fullscreen:-webkit-full-screen {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
.map:-moz-full-screen {
|
||||
height: 100%;
|
||||
}
|
||||
.map:-webkit-full-screen {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,11 @@ map.on('singleclick', function(evt) {
|
||||
evt.coordinate, viewResolution, 'EPSG:3857',
|
||||
{'INFO_FORMAT': 'text/html'});
|
||||
if (url) {
|
||||
document.getElementById('info').innerHTML =
|
||||
'<iframe seamless src="' + url + '"></iframe>';
|
||||
fetch(url)
|
||||
.then((response) => response.text())
|
||||
.then((html) => {
|
||||
document.getElementById('info').innerHTML = html;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -33,8 +33,11 @@ map.on('singleclick', function(evt) {
|
||||
evt.coordinate, viewResolution, 'EPSG:3857',
|
||||
{'INFO_FORMAT': 'text/html'});
|
||||
if (url) {
|
||||
document.getElementById('info').innerHTML =
|
||||
'<iframe seamless src="' + url + '"></iframe>';
|
||||
fetch(url)
|
||||
.then((response) => response.text())
|
||||
.then((html) => {
|
||||
document.getElementById('info').innerHTML = html;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,16 +4,11 @@ title: Interaction Options
|
||||
shortdesc: Shows interaction options for custom scroll and zoom behavior.
|
||||
docs: >
|
||||
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
|
||||
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
|
||||
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"
|
||||
---
|
||||
<div tabindex="1" id="map" class="map"></div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import OSM from '../src/ol/source/OSM.js';
|
||||
|
||||
const map = new Map({
|
||||
interactions: defaultInteractions({
|
||||
constrainResolution: true, onFocusOnly: true
|
||||
onFocusOnly: true
|
||||
}),
|
||||
layers: [
|
||||
new TileLayer({
|
||||
|
||||
@@ -7,7 +7,7 @@ import BingMaps from '../src/ol/source/BingMaps.js';
|
||||
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5';
|
||||
|
||||
const roads = new TileLayer({
|
||||
source: new BingMaps({key: key, imagerySet: 'Road'})
|
||||
source: new BingMaps({key: key, imagerySet: 'RoadOnDemand'})
|
||||
});
|
||||
|
||||
const imagery = new TileLayer({
|
||||
|
||||
@@ -203,7 +203,11 @@ const map = new Map({
|
||||
target: 'map',
|
||||
view: new View({
|
||||
center: [-10997148, 4569099],
|
||||
zoom: 4
|
||||
zoom: 4,
|
||||
minZoom: 1,
|
||||
extent: [-Infinity, -20048966.10, Infinity, 20048966.10],
|
||||
smoothExtentConstraint: false,
|
||||
smoothResolutionConstraint: false
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ tags: "mapbox, vector, tiles, mobile"
|
||||
resources:
|
||||
- resources/mapbox-streets-v6-style.js
|
||||
cloak:
|
||||
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg
|
||||
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
|
||||
value: Your Mapbox access token from http://mapbox.com/ here
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {Fill, Icon, Stroke, Style, Text} from '../src/ol/style.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.
|
||||
const resolutions = [];
|
||||
|
||||
@@ -8,7 +8,7 @@ tags: "simple, mapbox, vector, tiles"
|
||||
resources:
|
||||
- resources/mapbox-streets-v6-style.js
|
||||
cloak:
|
||||
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg
|
||||
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
|
||||
value: Your Mapbox access token from http://mapbox.com/ here
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import VectorTileSource from '../src/ol/source/VectorTile.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({
|
||||
layers: [
|
||||
|
||||
@@ -14,6 +14,7 @@ cloak:
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
||||
<title>Mobile full screen example</title>
|
||||
<link rel="stylesheet" href="../css/ol.css" type="text/css">
|
||||
<style type="text/css">
|
||||
html, body, .map {
|
||||
margin: 0;
|
||||
|
||||
@@ -15,7 +15,7 @@ const map = new Map({
|
||||
new TileLayer({
|
||||
source: new BingMaps({
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
||||
imagerySet: 'Road'
|
||||
imagerySet: 'RoadOnDemand'
|
||||
})
|
||||
})
|
||||
],
|
||||
|
||||
@@ -5,7 +5,7 @@ shortdesc: Restrict pinch zooming to integer zoom levels.
|
||||
docs: >
|
||||
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
|
||||
<code>constrainResolution: true</code> when constructing the interaction.
|
||||
<code>constrainResolution: true</code> when constructing the view.
|
||||
tags: "pinch, zoom, interaction"
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -6,10 +6,8 @@ import OSM from '../src/ol/source/OSM.js';
|
||||
|
||||
|
||||
const map = new Map({
|
||||
interactions: defaultInteractions({pinchZoom: false}).extend([
|
||||
new PinchZoom({
|
||||
constrainResolution: true // force zooming to a integer zoom
|
||||
})
|
||||
interactions: defaultInteractions().extend([
|
||||
new PinchZoom()
|
||||
]),
|
||||
layers: [
|
||||
new TileLayer({
|
||||
@@ -19,6 +17,7 @@ const map = new Map({
|
||||
target: 'map',
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
zoom: 2
|
||||
zoom: 2,
|
||||
constrainResolution: true
|
||||
})
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ const map2 = new Map({
|
||||
preload: 0, // default value
|
||||
source: new BingMaps({
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
||||
imagerySet: 'AerialWithLabels'
|
||||
imagerySet: 'AerialWithLabelsOnDemand'
|
||||
})
|
||||
})
|
||||
],
|
||||
|
||||
@@ -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/).
|
||||
tags: "utfgrid, tilejson"
|
||||
cloak:
|
||||
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg
|
||||
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
|
||||
value: Your Mapbox access token from http://mapbox.com/ here
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import TileLayer from '../src/ol/layer/Tile.js';
|
||||
import TileJSON from '../src/ol/source/TileJSON.js';
|
||||
import UTFGrid from '../src/ol/source/UTFGrid.js';
|
||||
|
||||
const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg';
|
||||
const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q';
|
||||
|
||||
const mapLayer = new TileLayer({
|
||||
source: new TileJSON({
|
||||
|
||||
@@ -103,6 +103,9 @@ tags: "geojson, vector, openstreetmap, label"
|
||||
<label>Size: </label>
|
||||
<input type="text" value="12px" id="points-size" />
|
||||
<br />
|
||||
<label>Line height: </label>
|
||||
<input type="text" value="1" id="points-height" />
|
||||
<br />
|
||||
<label>Offset X:</label>
|
||||
<input type="text" value="0" id="points-offset-x" />
|
||||
<br />
|
||||
@@ -212,6 +215,9 @@ tags: "geojson, vector, openstreetmap, label"
|
||||
<label>Size: </label>
|
||||
<input type="text" value="12px" id="lines-size" />
|
||||
<br />
|
||||
<label>Line height: </label>
|
||||
<input type="text" value="1.2" id="lines-height" />
|
||||
<br />
|
||||
<label>Offset X:</label>
|
||||
<input type="text" value="0" id="lines-offset-x" />
|
||||
<br />
|
||||
@@ -321,6 +327,9 @@ tags: "geojson, vector, openstreetmap, label"
|
||||
<label>Size: </label>
|
||||
<input type="text" value="10px" id="polygons-size" />
|
||||
<br />
|
||||
<label>Line height: </label>
|
||||
<input type="text" value="1" id="polygons-height" />
|
||||
<br />
|
||||
<label>Offset X:</label>
|
||||
<input type="text" value="0" id="polygons-offset-x" />
|
||||
<br />
|
||||
|
||||
@@ -16,6 +16,7 @@ const myDom = {
|
||||
font: document.getElementById('points-font'),
|
||||
weight: document.getElementById('points-weight'),
|
||||
size: document.getElementById('points-size'),
|
||||
height: document.getElementById('points-height'),
|
||||
offsetX: document.getElementById('points-offset-x'),
|
||||
offsetY: document.getElementById('points-offset-y'),
|
||||
color: document.getElementById('points-color'),
|
||||
@@ -34,6 +35,7 @@ const myDom = {
|
||||
maxangle: document.getElementById('lines-maxangle'),
|
||||
overflow: document.getElementById('lines-overflow'),
|
||||
size: document.getElementById('lines-size'),
|
||||
height: document.getElementById('lines-height'),
|
||||
offsetX: document.getElementById('lines-offset-x'),
|
||||
offsetY: document.getElementById('lines-offset-y'),
|
||||
color: document.getElementById('lines-color'),
|
||||
@@ -52,6 +54,7 @@ const myDom = {
|
||||
maxangle: document.getElementById('polygons-maxangle'),
|
||||
overflow: document.getElementById('polygons-overflow'),
|
||||
size: document.getElementById('polygons-size'),
|
||||
height: document.getElementById('polygons-height'),
|
||||
offsetX: document.getElementById('polygons-offset-x'),
|
||||
offsetY: document.getElementById('polygons-offset-y'),
|
||||
color: document.getElementById('polygons-color'),
|
||||
@@ -84,6 +87,7 @@ const createTextStyle = function(feature, resolution, dom) {
|
||||
const align = dom.align.value;
|
||||
const baseline = dom.baseline.value;
|
||||
const size = dom.size.value;
|
||||
const height = dom.height.value;
|
||||
const offsetX = parseInt(dom.offsetX.value, 10);
|
||||
const offsetY = parseInt(dom.offsetY.value, 10);
|
||||
const weight = dom.weight.value;
|
||||
@@ -98,7 +102,7 @@ const createTextStyle = function(feature, resolution, dom) {
|
||||
document.getElementsByTagName('head')[0].appendChild(openSans);
|
||||
openSansAdded = true;
|
||||
}
|
||||
const font = weight + ' ' + size + ' ' + dom.font.value;
|
||||
const font = weight + ' ' + size + '/' + height + ' ' + dom.font.value;
|
||||
const fillColor = dom.color.value;
|
||||
const outlineColor = dom.outline.value;
|
||||
const outlineWidth = parseInt(dom.outlineWidth.value, 10);
|
||||
|
||||
40
package.json
40
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ol",
|
||||
"version": "5.3.0",
|
||||
"version": "6.0.0-beta.3",
|
||||
"description": "OpenLayers mapping library",
|
||||
"keywords": [
|
||||
"map",
|
||||
@@ -36,35 +36,35 @@
|
||||
"url": "https://github.com/openlayers/openlayers/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"pbf": "3.1.0",
|
||||
"pbf": "3.2.0",
|
||||
"pixelworks": "1.1.0",
|
||||
"rbush": "2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openlayers/eslint-plugin": "^4.0.0-beta.1",
|
||||
"@types/arcgis-rest-api": "^10.4.3",
|
||||
"@types/geojson": "^7946.0.5",
|
||||
"@types/arcgis-rest-api": "^10.4.4",
|
||||
"@types/geojson": "^7946.0.6",
|
||||
"@types/pbf": "^3.0.1",
|
||||
"@types/rbush": "^2.0.2",
|
||||
"@types/topojson-specification": "^1.0.0",
|
||||
"@types/topojson-specification": "^1.0.1",
|
||||
"buble": "^0.19.6",
|
||||
"buble-loader": "^0.5.1",
|
||||
"chaikin-smooth": "^1.0.4",
|
||||
"clean-css-cli": "4.2.1",
|
||||
"copy-webpack-plugin": "^4.6.0",
|
||||
"coveralls": "3.0.1",
|
||||
"eslint": "^5.8.0",
|
||||
"copy-webpack-plugin": "^5.0.0",
|
||||
"coveralls": "3.0.3",
|
||||
"eslint": "^5.13.0",
|
||||
"eslint-config-openlayers": "^11.0.0",
|
||||
"expect.js": "0.3.1",
|
||||
"front-matter": "^3.0.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"globby": "^8.0.1",
|
||||
"handlebars": "4.0.11",
|
||||
"globby": "^9.1.0",
|
||||
"handlebars": "4.1.0",
|
||||
"istanbul": "0.4.5",
|
||||
"jquery": "3.3.1",
|
||||
"jsdoc": "3.5.5",
|
||||
"jsdoc-plugin-typescript": "^1.0.6",
|
||||
"jsdoc-plugin-typescript": "^1.0.7",
|
||||
"karma": "^4.0.0",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-coverage": "^1.1.2",
|
||||
@@ -73,25 +73,25 @@
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^4.0.0-rc.2",
|
||||
"loglevelnext": "^3.0.0",
|
||||
"marked": "0.6.0",
|
||||
"mocha": "5.2.0",
|
||||
"ol-mapbox-style": "^3.6.3",
|
||||
"marked": "0.6.1",
|
||||
"mocha": "6.0.2",
|
||||
"ol-mapbox-style": "^4.1.0",
|
||||
"pixelmatch": "^4.0.2",
|
||||
"pngjs": "^3.3.3",
|
||||
"proj4": "2.5.0",
|
||||
"puppeteer": "^1.11.0",
|
||||
"puppeteer": "~1.11.0",
|
||||
"serve-static": "^1.13.2",
|
||||
"shx": "^0.3.2",
|
||||
"sinon": "^7.2.3",
|
||||
"terser-webpack-plugin": "^1.2.1",
|
||||
"terser-webpack-plugin": "^1.2.2",
|
||||
"typescript": "^3.2.2",
|
||||
"url-polyfill": "^1.1.3",
|
||||
"walk": "^2.3.9",
|
||||
"webpack": "4.29.0",
|
||||
"webpack-cli": "^3.2.0",
|
||||
"webpack-dev-middleware": "^3.5.1",
|
||||
"webpack": "4.29.6",
|
||||
"webpack-cli": "^3.2.3",
|
||||
"webpack-dev-middleware": "^3.5.2",
|
||||
"webpack-dev-server": "^3.1.14",
|
||||
"yargs": "^12.0.2"
|
||||
"yargs": "^13.2.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "openlayers",
|
||||
|
||||
@@ -351,7 +351,7 @@ if (require.main === module) {
|
||||
option('headless', {
|
||||
describe: 'Launch Puppeteer in headless mode',
|
||||
type: 'boolean',
|
||||
default: process.env.CI ? false : true
|
||||
default: false
|
||||
}).
|
||||
option('puppeteer-args', {
|
||||
describe: 'Additional args for Puppeteer',
|
||||
|
||||
@@ -4,13 +4,6 @@
|
||||
import Tile from './Tile.js';
|
||||
import TileState from './TileState.js';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {import("./extent.js").Extent}
|
||||
*/
|
||||
const DEFAULT_EXTENT = [0, 0, 4096, 4096];
|
||||
|
||||
|
||||
class VectorTile extends Tile {
|
||||
|
||||
/**
|
||||
@@ -31,10 +24,10 @@ class VectorTile extends Tile {
|
||||
this.consumers = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Extent of this tile; set by the source.
|
||||
* @type {import("./extent.js").Extent}
|
||||
*/
|
||||
this.extent_ = null;
|
||||
this.extent = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -55,11 +48,16 @@ class VectorTile extends Tile {
|
||||
this.loader_;
|
||||
|
||||
/**
|
||||
* Data projection
|
||||
* @private
|
||||
* Feature projection of this tile; set by the source.
|
||||
* @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
|
||||
@@ -83,15 +81,6 @@ class VectorTile extends Tile {
|
||||
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.
|
||||
* @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
|
||||
* by {@link module:ol/VectorTile~VectorTile#getProjection}.
|
||||
* Get the features for this tile. Geometries will be in the view projection.
|
||||
* @return {Array<import("./Feature.js").FeatureLike>} Features.
|
||||
* @api
|
||||
*/
|
||||
@@ -118,16 +106,6 @@ class VectorTile extends Tile {
|
||||
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
|
||||
*/
|
||||
@@ -135,7 +113,7 @@ class VectorTile extends Tile {
|
||||
if (this.state == TileState.IDLE) {
|
||||
this.setState(TileState.LOADING);
|
||||
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.
|
||||
* @param {Array<import("./Feature.js").default>} features The loaded features.
|
||||
* @param {import("./proj/Projection.js").default} dataProjection Data projection.
|
||||
* @param {import("./extent.js").Extent} extent Extent.
|
||||
*/
|
||||
onLoad(features, dataProjection, extent) {
|
||||
this.setProjection(dataProjection);
|
||||
onLoad(features, dataProjection) {
|
||||
this.setFeatures(features);
|
||||
this.setExtent(extent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,22 +133,6 @@ class VectorTile extends Tile {
|
||||
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`.
|
||||
* Sets the features for the tile.
|
||||
@@ -185,17 +144,6 @@ class VectorTile extends Tile {
|
||||
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.
|
||||
* @param {import("./featureloader.js").FeatureLoader} loader Feature loader.
|
||||
|
||||
499
src/ol/View.js
499
src/ol/View.js
@@ -21,6 +21,9 @@ import {clamp, modulo} from './math.js';
|
||||
import {assign} from './obj.js';
|
||||
import {createProjection, METERS_PER_UNIT} from './proj.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
|
||||
* cleared inside the view. Values in the array are top, right, bottom and left
|
||||
* padding.
|
||||
* @property {boolean} [constrainResolution=true] Constrain the resolution.
|
||||
* @property {boolean} [nearest=false] If `constrainResolution` is `true`, get
|
||||
* the nearest extent instead of the closest that actually fits the view.
|
||||
* @property {boolean} [nearest=false] If the view `constrainResolution` option is `true`,
|
||||
* get the nearest extent instead of the closest that actually fits the view.
|
||||
* @property {number} [minResolution=0] Minimum resolution that we zoom to.
|
||||
* @property {number} [maxZoom] Maximum zoom level that we zoom to. If
|
||||
* `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
|
||||
* `false`.
|
||||
* @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
|
||||
* the resolution constraint. It is used together with `minResolution` (or
|
||||
* `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
|
||||
* `minResolution`) and `zoomFactor`. Note that if `maxResolution` is also
|
||||
* 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
|
||||
* projection. The default is Spherical Mercator.
|
||||
* @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
|
||||
* (positive rotation clockwise, 0 means North).
|
||||
* @property {number} [zoom] Only used if `resolution` is not defined. Zoom
|
||||
* level used to calculate the initial resolution for the view. The initial
|
||||
* resolution is determined using the {@link #constrainResolution} method.
|
||||
* @property {number} [zoomFactor=2] The zoom factor used to determine the
|
||||
* resolution constraint.
|
||||
* level used to calculate the initial resolution for the view.
|
||||
* @property {number} [zoomFactor=2] The zoom factor used to compute the
|
||||
* corresponding resolution.
|
||||
*/
|
||||
|
||||
|
||||
@@ -143,7 +155,7 @@ import Units from './proj/Units.js';
|
||||
* 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
|
||||
* 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.
|
||||
* @property {number} [duration=1000] The duration of the animation in milliseconds.
|
||||
* @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.
|
||||
* `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
|
||||
* resolution (projection units per pixel). The default projection is
|
||||
* Spherical Mercator (EPSG:3857).
|
||||
@@ -192,28 +209,19 @@ const DEFAULT_MIN_ZOOM = 0;
|
||||
* ### The constraints
|
||||
*
|
||||
* `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
|
||||
* that is passed to a setter will effectively be the value set in the view,
|
||||
* and returned by the corresponding getter.
|
||||
* states of the view, but any constraint defined in the constructor will
|
||||
* be applied along the way.
|
||||
*
|
||||
* But a View object also has a *resolution constraint*, a
|
||||
* *rotation constraint* and a *center constraint*.
|
||||
* A View object can have a *resolution constraint*, a *rotation constraint*
|
||||
* and a *center constraint*.
|
||||
*
|
||||
* As said above, no constraints are applied when the setters are used to set
|
||||
* new states for the view. Applying constraints is done explicitly through
|
||||
* the use of the `constrain*` functions (`constrainResolution` and
|
||||
* `constrainRotation` and `constrainCenter`).
|
||||
*
|
||||
* The main users of the constraints are the interactions and the
|
||||
* controls. For example, double-clicking on the map changes the view to
|
||||
* 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 *resolution constraint* typically restricts min/max values and
|
||||
* 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. By default, the view
|
||||
* only has a min/max restriction and allow intermediary zoom levels when
|
||||
* pinch-zooming for example.
|
||||
*
|
||||
* The *rotation constraint* snaps to specific angles. It is determined
|
||||
* by the following options: `enableRotation` and `constrainRotation`.
|
||||
@@ -221,7 +229,29 @@ const DEFAULT_MIN_ZOOM = 0;
|
||||
* horizontal.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ### 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
|
||||
*/
|
||||
@@ -262,6 +292,24 @@ class View extends BaseObject {
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -275,8 +323,6 @@ class View extends BaseObject {
|
||||
* @type {Object<string, *>}
|
||||
*/
|
||||
const properties = {};
|
||||
properties[ViewProperty.CENTER] = options.center !== undefined ?
|
||||
options.center : null;
|
||||
|
||||
const resolutionConstraintInfo = createResolutionConstraint(options);
|
||||
|
||||
@@ -324,19 +370,20 @@ class View extends BaseObject {
|
||||
rotation: rotationConstraint
|
||||
};
|
||||
|
||||
this.setRotation(options.rotation !== undefined ? options.rotation : 0);
|
||||
this.setCenter(options.center !== undefined ? options.center : null);
|
||||
if (options.resolution !== undefined) {
|
||||
properties[ViewProperty.RESOLUTION] = options.resolution;
|
||||
this.setResolution(options.resolution);
|
||||
} 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
|
||||
properties[ViewProperty.RESOLUTION] = clamp(
|
||||
Number(this.getResolution() || properties[ViewProperty.RESOLUTION]),
|
||||
this.minResolution_, this.maxResolution_);
|
||||
const resolution = this.getResolutionForZoom(options.zoom);
|
||||
this.setResolution(clamp(resolution,
|
||||
this.minResolution_, this.maxResolution_));
|
||||
} else {
|
||||
this.setZoom(options.zoom);
|
||||
}
|
||||
}
|
||||
properties[ViewProperty.ROTATION] = options.rotation !== undefined ? options.rotation : 0;
|
||||
|
||||
this.setProperties(properties);
|
||||
|
||||
/**
|
||||
@@ -432,9 +479,9 @@ class View extends BaseObject {
|
||||
return;
|
||||
}
|
||||
let start = Date.now();
|
||||
let center = this.getCenter().slice();
|
||||
let resolution = this.getResolution();
|
||||
let rotation = this.getRotation();
|
||||
let center = this.targetCenter_.slice();
|
||||
let resolution = this.targetResolution_;
|
||||
let rotation = this.targetRotation_;
|
||||
const series = [];
|
||||
for (let i = 0; i < animationCount; ++i) {
|
||||
const options = /** @type {AnimationOptions} */ (arguments[i]);
|
||||
@@ -450,14 +497,13 @@ class View extends BaseObject {
|
||||
|
||||
if (options.center) {
|
||||
animation.sourceCenter = center;
|
||||
animation.targetCenter = options.center;
|
||||
animation.targetCenter = options.center.slice();
|
||||
center = animation.targetCenter;
|
||||
}
|
||||
|
||||
if (options.zoom !== undefined) {
|
||||
animation.sourceResolution = resolution;
|
||||
animation.targetResolution = this.constrainResolution(
|
||||
this.maxResolution_, options.zoom - this.minZoom_, 0);
|
||||
animation.targetResolution = this.getResolutionForZoom(options.zoom);
|
||||
resolution = animation.targetResolution;
|
||||
} else if (options.resolution) {
|
||||
animation.sourceResolution = resolution;
|
||||
@@ -556,28 +602,31 @@ class View extends BaseObject {
|
||||
const y1 = animation.targetCenter[1];
|
||||
const x = x0 + progress * (x1 - x0);
|
||||
const y = y0 + progress * (y1 - y0);
|
||||
this.set(ViewProperty.CENTER, [x, y]);
|
||||
this.targetCenter_ = [x, y];
|
||||
}
|
||||
if (animation.sourceResolution && animation.targetResolution) {
|
||||
const resolution = progress === 1 ?
|
||||
animation.targetResolution :
|
||||
animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);
|
||||
if (animation.anchor) {
|
||||
this.set(ViewProperty.CENTER,
|
||||
this.calculateCenterZoom(resolution, animation.anchor));
|
||||
const size = this.getSizeFromViewport_(this.getRotation());
|
||||
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) {
|
||||
const rotation = progress === 1 ?
|
||||
modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI :
|
||||
animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation);
|
||||
if (animation.anchor) {
|
||||
this.set(ViewProperty.CENTER,
|
||||
this.calculateCenterRotate(rotation, animation.anchor));
|
||||
const constrainedRotation = this.constraints_.rotation(rotation, true);
|
||||
this.targetCenter_ = this.calculateCenterRotate(constrainedRotation, animation.anchor);
|
||||
}
|
||||
this.set(ViewProperty.ROTATION, rotation);
|
||||
this.targetRotation_ = rotation;
|
||||
}
|
||||
this.applyTargetState_(true);
|
||||
more = true;
|
||||
if (!animation.complete) {
|
||||
break;
|
||||
@@ -597,6 +646,10 @@ class View extends BaseObject {
|
||||
if (more && this.updateAnimationKey_ === undefined) {
|
||||
this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_);
|
||||
}
|
||||
|
||||
if (!this.getAnimating()) {
|
||||
setTimeout(this.resolveConstraints_.bind(this), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -634,9 +687,10 @@ class View extends BaseObject {
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
getSizeFromViewport_() {
|
||||
getSizeFromViewport_(opt_rotation) {
|
||||
const size = [100, 100];
|
||||
const selector = '.ol-viewport[data-view="' + getUid(this) + '"]';
|
||||
const element = document.querySelector(selector);
|
||||
@@ -645,45 +699,15 @@ class View extends BaseObject {
|
||||
size[0] = parseInt(metrics.width, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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
|
||||
* array (this is rare), this method may return non-integer zoom levels (so
|
||||
* the zoom level is not safe to use as an index into a resolutions array).
|
||||
* Get the current zoom level. This method may return non-integer zoom levels
|
||||
* if the view does not constrain the resolution, or if an interaction or
|
||||
* animation is underway.
|
||||
* @return {number|undefined} Zoom.
|
||||
* @api
|
||||
*/
|
||||
@@ -961,8 +994,16 @@ class View extends BaseObject {
|
||||
* @api
|
||||
*/
|
||||
getResolutionForZoom(zoom) {
|
||||
return /** @type {number} */ (this.constrainResolution(
|
||||
this.maxResolution_, zoom - this.minZoom_, 0));
|
||||
if (this.resolutions_) {
|
||||
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 constrainResolution = options.constrainResolution !== undefined ?
|
||||
options.constrainResolution : true;
|
||||
const nearest = options.nearest !== undefined ? options.nearest : false;
|
||||
let minResolution;
|
||||
if (options.minResolution !== undefined) {
|
||||
minResolution = options.minResolution;
|
||||
} else if (options.maxZoom !== undefined) {
|
||||
minResolution = this.constrainResolution(
|
||||
this.maxResolution_, options.maxZoom - this.minZoom_, 0);
|
||||
minResolution = this.getResolutionForZoom(options.maxZoom);
|
||||
} else {
|
||||
minResolution = 0;
|
||||
}
|
||||
@@ -1036,14 +1074,7 @@ class View extends BaseObject {
|
||||
[size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);
|
||||
resolution = isNaN(resolution) ? minResolution :
|
||||
Math.max(resolution, minResolution);
|
||||
if (constrainResolution) {
|
||||
let constrainedResolution = this.constrainResolution(resolution, 0, 0);
|
||||
if (!nearest && constrainedResolution < resolution) {
|
||||
constrainedResolution = this.constrainResolution(
|
||||
constrainedResolution, -1, 0);
|
||||
}
|
||||
resolution = constrainedResolution;
|
||||
}
|
||||
resolution = this.getConstrainedResolution(resolution, nearest ? 0 : 1);
|
||||
|
||||
// calculate center
|
||||
sinAngle = -sinAngle; // go back to original rotation
|
||||
@@ -1059,13 +1090,14 @@ class View extends BaseObject {
|
||||
if (options.duration !== undefined) {
|
||||
this.animate({
|
||||
resolution: resolution,
|
||||
center: center,
|
||||
center: this.getConstrainedCenter(center, resolution),
|
||||
duration: options.duration,
|
||||
easing: options.easing
|
||||
}, callback);
|
||||
} else {
|
||||
this.setResolution(resolution);
|
||||
this.setCenter(center);
|
||||
this.targetResolution_ = resolution;
|
||||
this.targetCenter_ = center;
|
||||
this.applyTargetState_(false, true);
|
||||
animationCallback(callback, true);
|
||||
}
|
||||
}
|
||||
@@ -1104,30 +1136,74 @@ class View extends BaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the view around a given coordinate.
|
||||
* @param {number} rotation New rotation value for the view.
|
||||
* @param {import("./coordinate.js").Coordinate=} opt_anchor The rotation center.
|
||||
* Adds relative coordinates to the center of the view. Any extent constraint will apply.
|
||||
* @param {import("./coordinate.js").Coordinate} deltaCoordinates Relative value to add.
|
||||
* @api
|
||||
*/
|
||||
rotate(rotation, opt_anchor) {
|
||||
if (opt_anchor !== undefined) {
|
||||
const center = this.calculateCenterRotate(rotation, opt_anchor);
|
||||
this.setCenter(center);
|
||||
}
|
||||
this.setRotation(rotation);
|
||||
adjustCenter(deltaCoordinates) {
|
||||
const center = this.targetCenter_;
|
||||
this.setCenter([center[0] + deltaCoordinates[0], center[1] + deltaCoordinates[1]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @observable
|
||||
* @api
|
||||
*/
|
||||
setCenter(center) {
|
||||
this.set(ViewProperty.CENTER, center);
|
||||
if (this.getAnimating()) {
|
||||
this.cancelAnimations();
|
||||
}
|
||||
this.targetCenter_ = center;
|
||||
this.applyTargetState_();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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.
|
||||
* @observable
|
||||
* @api
|
||||
*/
|
||||
setResolution(resolution) {
|
||||
this.set(ViewProperty.RESOLUTION, resolution);
|
||||
if (this.getAnimating()) {
|
||||
this.cancelAnimations();
|
||||
}
|
||||
this.targetResolution_ = resolution;
|
||||
this.applyTargetState_();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @observable
|
||||
* @api
|
||||
*/
|
||||
setRotation(rotation) {
|
||||
this.set(ViewProperty.ROTATION, rotation);
|
||||
if (this.getAnimating()) {
|
||||
this.cancelAnimations();
|
||||
}
|
||||
this.targetRotation_ = rotation;
|
||||
this.applyTargetState_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom to a specific zoom level.
|
||||
* Zoom to a specific zoom level. Any resolution constrain will apply.
|
||||
* @param {number} zoom Zoom level.
|
||||
* @api
|
||||
*/
|
||||
setZoom(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) {
|
||||
if (options.extent !== undefined) {
|
||||
return createExtent(options.extent);
|
||||
return createExtent(options.extent, options.constrainOnlyCenter,
|
||||
options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true);
|
||||
} else {
|
||||
return centerNone;
|
||||
}
|
||||
@@ -1226,13 +1430,22 @@ export function createResolutionConstraint(options) {
|
||||
const zoomFactor = options.zoomFactor !== undefined ?
|
||||
options.zoomFactor : defaultZoomFactor;
|
||||
|
||||
const smooth =
|
||||
options.smoothResolutionConstraint !== undefined ? options.smoothResolutionConstraint : true;
|
||||
|
||||
if (options.resolutions !== undefined) {
|
||||
const resolutions = options.resolutions;
|
||||
maxResolution = resolutions[minZoom];
|
||||
minResolution = resolutions[maxZoom] !== undefined ?
|
||||
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 {
|
||||
// calculate the default min and max resolution
|
||||
const projection = createProjection(options.projection, 'EPSG:3857');
|
||||
@@ -1276,8 +1489,14 @@ export function createResolutionConstraint(options) {
|
||||
Math.log(maxResolution / minResolution) / Math.log(zoomFactor));
|
||||
minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom);
|
||||
|
||||
if (options.constrainResolution) {
|
||||
resolutionConstraint = createSnapToPower(
|
||||
zoomFactor, maxResolution, maxZoom - minZoom);
|
||||
zoomFactor, maxResolution, minResolution, smooth,
|
||||
!options.constrainOnlyCenter && options.extent);
|
||||
} else {
|
||||
resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth,
|
||||
!options.constrainOnlyCenter && options.extent);
|
||||
}
|
||||
}
|
||||
return {constraint: resolutionConstraint, maxResolution: maxResolution,
|
||||
minResolution: minResolution, minZoom: minZoom, zoomFactor: zoomFactor};
|
||||
|
||||
@@ -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 {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.
|
||||
*/
|
||||
export function createExtent(extent) {
|
||||
export function createExtent(extent, onlyCenter, smooth) {
|
||||
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.
|
||||
*/
|
||||
function(center) {
|
||||
function(center, resolution, size, opt_isMoving) {
|
||||
if (center) {
|
||||
return [
|
||||
clamp(center[0], extent[0], extent[2]),
|
||||
clamp(center[1], extent[1], extent[3])
|
||||
];
|
||||
const viewWidth = onlyCenter ? 0 : size[0] * resolution;
|
||||
const viewHeight = onlyCenter ? 0 : size[1] * resolution;
|
||||
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 {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {replaceNode} from '../dom.js';
|
||||
import {listen} from '../events.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
|
||||
const events = ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'];
|
||||
const events = ['fullscreenchange', 'webkitfullscreenchange', 'MSFullscreenChange'];
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
@@ -209,7 +209,6 @@ function isFullScreenSupported() {
|
||||
const body = document.body;
|
||||
return !!(
|
||||
body.webkitRequestFullscreen ||
|
||||
(body.mozRequestFullScreen && document.mozFullScreenEnabled) ||
|
||||
(body.msRequestFullscreen && document.msFullscreenEnabled) ||
|
||||
(body.requestFullscreen && document.fullscreenEnabled)
|
||||
);
|
||||
@@ -220,8 +219,7 @@ function isFullScreenSupported() {
|
||||
*/
|
||||
function isFullScreen() {
|
||||
return !!(
|
||||
document.webkitIsFullScreen || document.mozFullScreen ||
|
||||
document.msFullscreenElement || document.fullscreenElement
|
||||
document.webkitIsFullScreen || document.msFullscreenElement || document.fullscreenElement
|
||||
);
|
||||
}
|
||||
|
||||
@@ -234,8 +232,6 @@ function requestFullScreen(element) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.msRequestFullscreen) {
|
||||
element.msRequestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen();
|
||||
}
|
||||
@@ -246,9 +242,7 @@ function requestFullScreen(element) {
|
||||
* @param {HTMLElement} element Element to request fullscreen
|
||||
*/
|
||||
function requestFullScreenWithKeys(element) {
|
||||
if (element.mozRequestFullScreenWithKeys) {
|
||||
element.mozRequestFullScreenWithKeys();
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen();
|
||||
} else {
|
||||
requestFullScreen(element);
|
||||
@@ -263,8 +257,6 @@ function exitFullScreen() {
|
||||
document.exitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
|
||||
@@ -169,8 +169,6 @@ export function render(mapEvent) {
|
||||
this.element.classList.remove(CLASS_HIDDEN);
|
||||
}
|
||||
}
|
||||
this.label_.style.msTransform = transform;
|
||||
this.label_.style.webkitTransform = transform;
|
||||
this.label_.style.transform = transform;
|
||||
}
|
||||
this.rotation_ = rotation;
|
||||
|
||||
@@ -201,20 +201,12 @@ class ScaleLine extends Control {
|
||||
ProjUnits.METERS;
|
||||
let pointResolution =
|
||||
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 suffix = '';
|
||||
if (units == Units.DEGREES) {
|
||||
const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES];
|
||||
if (projection.getUnits() == ProjUnits.DEGREES) {
|
||||
nominalCount *= metersPerDegree;
|
||||
} else {
|
||||
pointResolution /= metersPerDegree;
|
||||
}
|
||||
if (nominalCount < metersPerDegree / 60) {
|
||||
suffix = '\u2033'; // seconds
|
||||
pointResolution *= 3600;
|
||||
|
||||
@@ -114,20 +114,20 @@ class Zoom extends Control {
|
||||
// upon it
|
||||
return;
|
||||
}
|
||||
const currentResolution = view.getResolution();
|
||||
if (currentResolution) {
|
||||
const newResolution = view.constrainResolution(currentResolution, delta);
|
||||
const currentZoom = view.getZoom();
|
||||
if (currentZoom !== undefined) {
|
||||
const newZoom = view.getConstrainedZoom(currentZoom + delta);
|
||||
if (this.duration_ > 0) {
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
view.animate({
|
||||
resolution: newResolution,
|
||||
zoom: newZoom,
|
||||
duration: this.duration_,
|
||||
easing: easeOut
|
||||
});
|
||||
} else {
|
||||
view.setResolution(newResolution);
|
||||
view.setZoom(newZoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @module ol/control/ZoomSlider
|
||||
*/
|
||||
import ViewHint from '../ViewHint.js';
|
||||
import Control from './Control.js';
|
||||
import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js';
|
||||
import {easeOut} from '../easing.js';
|
||||
@@ -102,13 +101,13 @@ class ZoomSlider extends Control {
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.previousX_;
|
||||
this.startX_;
|
||||
|
||||
/**
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.previousY_;
|
||||
this.startY_;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
const resolution = this.getResolutionForPosition_(relativePosition);
|
||||
const zoom = view.getConstrainedZoom(view.getZoomForResolution(resolution));
|
||||
|
||||
view.animate({
|
||||
resolution: view.constrainResolution(resolution),
|
||||
zoom: zoom,
|
||||
duration: this.duration_,
|
||||
easing: easeOut
|
||||
});
|
||||
@@ -233,9 +233,10 @@ class ZoomSlider extends Control {
|
||||
*/
|
||||
handleDraggerStart_(event) {
|
||||
if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) {
|
||||
this.getMap().getView().setHint(ViewHint.INTERACTING, 1);
|
||||
this.previousX_ = event.clientX;
|
||||
this.previousY_ = event.clientY;
|
||||
const element = /** @type {HTMLElement} */ (this.element.firstElementChild);
|
||||
this.getMap().getView().beginInteraction();
|
||||
this.startX_ = event.clientX - parseFloat(element.style.left);
|
||||
this.startY_ = event.clientY - parseFloat(element.style.top);
|
||||
this.dragging_ = true;
|
||||
|
||||
if (this.dragListenerKeys_.length === 0) {
|
||||
@@ -259,15 +260,11 @@ class ZoomSlider extends Control {
|
||||
*/
|
||||
handleDraggerDrag_(event) {
|
||||
if (this.dragging_) {
|
||||
const element = /** @type {HTMLElement} */ (this.element.firstElementChild);
|
||||
const deltaX = event.clientX - this.previousX_ + parseFloat(element.style.left);
|
||||
const deltaY = event.clientY - this.previousY_ + parseFloat(element.style.top);
|
||||
const deltaX = event.clientX - this.startX_;
|
||||
const deltaY = event.clientY - this.startY_;
|
||||
const relativePosition = this.getRelativePosition_(deltaX, deltaY);
|
||||
this.currentResolution_ = this.getResolutionForPosition_(relativePosition);
|
||||
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) {
|
||||
if (this.dragging_) {
|
||||
const view = this.getMap().getView();
|
||||
view.setHint(ViewHint.INTERACTING, -1);
|
||||
|
||||
view.animate({
|
||||
resolution: view.constrainResolution(this.currentResolution_),
|
||||
duration: this.duration_,
|
||||
easing: easeOut
|
||||
});
|
||||
view.endInteraction();
|
||||
|
||||
this.dragging_ = false;
|
||||
this.previousX_ = undefined;
|
||||
this.previousY_ = undefined;
|
||||
this.startX_ = undefined;
|
||||
this.startY_ = undefined;
|
||||
this.dragListenerKeys_.forEach(unlistenByKey);
|
||||
this.dragListenerKeys_.length = 0;
|
||||
}
|
||||
@@ -356,7 +347,7 @@ class ZoomSlider extends Control {
|
||||
*/
|
||||
getPositionForResolution_(res) {
|
||||
const fn = this.getMap().getView().getValueForResolutionFunction();
|
||||
return 1 - fn(res);
|
||||
return clamp(1 - fn(res), 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,11 +366,9 @@ export function render(mapEvent) {
|
||||
this.initSlider_();
|
||||
}
|
||||
const res = mapEvent.frameState.viewState.resolution;
|
||||
if (res !== this.currentResolution_) {
|
||||
this.currentResolution_ = res;
|
||||
this.setThumbPosition_(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default ZoomSlider;
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
* @return {CanvasRenderingContext2D} The context.
|
||||
*/
|
||||
export function createCanvasContext2D(opt_width, opt_height) {
|
||||
const canvas = /** @type {HTMLCanvasElement} */ (document.createElement('canvas'));
|
||||
const canvas = document.createElement('canvas');
|
||||
if (opt_width) {
|
||||
canvas.width = opt_width;
|
||||
}
|
||||
if (opt_height) {
|
||||
canvas.height = opt_height;
|
||||
}
|
||||
return /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d'));
|
||||
return canvas.getContext('2d');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -230,6 +230,33 @@ export const mouseOnly = function(mapBrowserEvent) {
|
||||
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
|
||||
|
||||
@@ -83,9 +83,11 @@ export function loadFeaturesXhr(url, format, success, failure) {
|
||||
source = /** @type {ArrayBuffer} */ (xhr.response);
|
||||
}
|
||||
if (source) {
|
||||
success.call(this, format.readFeatures(source,
|
||||
{featureProjection: projection}),
|
||||
format.readProjection(source), format.getLastExtent());
|
||||
success.call(this, format.readFeatures(source, {
|
||||
extent: extent,
|
||||
featureProjection: projection
|
||||
}),
|
||||
format.readProjection(source));
|
||||
} else {
|
||||
failure.call(this);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* @return {EsriJSONPoint} EsriJSON geometry.
|
||||
*/
|
||||
function writePointGeometry(geometry, opt_options) {
|
||||
const coordinates = /** @type {import("../geom/Point.js").default} */ (geometry).getCoordinates();
|
||||
const coordinates = geometry.getCoordinates();
|
||||
/** @type {EsriJSONPoint} */
|
||||
let esriJSON;
|
||||
const layout = /** @type {import("../geom/Point.js").default} */ (geometry).getLayout();
|
||||
const layout = geometry.getLayout();
|
||||
if (layout === GeometryLayout.XYZ) {
|
||||
esriJSON = {
|
||||
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.
|
||||
* @return {EsriJSONPolyline} EsriJSON geometry.
|
||||
*/
|
||||
function writeLineStringGeometry(geometry, opt_options) {
|
||||
const lineString = /** @type {import("../geom/LineString.js").default} */ (geometry);
|
||||
function writeLineStringGeometry(lineString, opt_options) {
|
||||
const hasZM = getHasZM(lineString);
|
||||
return {
|
||||
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.
|
||||
* @return {EsriJSONPolygon} EsriJSON geometry.
|
||||
*/
|
||||
function writePolygonGeometry(geometry, opt_options) {
|
||||
const polygon = /** @type {import("../geom/Polygon.js").default} */ (geometry);
|
||||
function writePolygonGeometry(polygon, opt_options) {
|
||||
// Esri geometries use the left-hand rule
|
||||
const hasZM = getHasZM(polygon);
|
||||
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.
|
||||
* @return {EsriJSONPolyline} EsriJSON geometry.
|
||||
*/
|
||||
function writeMultiLineStringGeometry(geometry, opt_options) {
|
||||
const multiLineString = /** @type {import("../geom/MultiLineString.js").default} */ (geometry);
|
||||
function writeMultiLineStringGeometry(multiLineString, opt_options) {
|
||||
const hasZM = getHasZM(multiLineString);
|
||||
return {
|
||||
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.
|
||||
* @return {EsriJSONMultipoint} EsriJSON geometry.
|
||||
*/
|
||||
function writeMultiPointGeometry(geometry, opt_options) {
|
||||
const multiPoint = /** @type {import("../geom/MultiPoint.js").default} */ (geometry);
|
||||
function writeMultiPointGeometry(multiPoint, opt_options) {
|
||||
const hasZM = getHasZM(multiPoint);
|
||||
return {
|
||||
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.
|
||||
* @return {EsriJSONPolygon} EsriJSON geometry.
|
||||
*/
|
||||
function writeMultiPolygonGeometry(geometry, opt_options) {
|
||||
const hasZM = getHasZM(/** @type {import("../geom/MultiPolygon.js").default} */(geometry));
|
||||
const coordinates = /** @type {import("../geom/MultiPolygon.js").default} */ (geometry).getCoordinates(false);
|
||||
const hasZM = getHasZM(geometry);
|
||||
const coordinates = geometry.getCoordinates(false);
|
||||
const output = [];
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
for (let x = coordinates[i].length - 1; x >= 0; x--) {
|
||||
|
||||
@@ -111,14 +111,6 @@ class FeatureFormat {
|
||||
}, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extent from the source of the last {@link readFeatures} call.
|
||||
* @return {import("../extent.js").Extent} Tile extent.
|
||||
*/
|
||||
getLastExtent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @return {import("./FormatType.js").default} Format.
|
||||
|
||||
@@ -19,6 +19,7 @@ import {linearRingIsClockwise} from '../geom/flat/orient.js';
|
||||
import Projection from '../proj/Projection.js';
|
||||
import Units from '../proj/Units.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;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("../extent.js").Extent}
|
||||
*/
|
||||
this.extent_ = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,10 +154,10 @@ class MVT extends FeatureFormat {
|
||||
* @private
|
||||
* @param {PBF} pbf PBF
|
||||
* @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.
|
||||
*/
|
||||
createFeature_(pbf, rawFeature, opt_options) {
|
||||
createFeature_(pbf, rawFeature, options) {
|
||||
const type = rawFeature.type;
|
||||
if (type === 0) {
|
||||
return null;
|
||||
@@ -181,6 +176,7 @@ class MVT extends FeatureFormat {
|
||||
|
||||
if (this.featureClass_ === RenderFeature) {
|
||||
feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
|
||||
feature.transform(options.dataProjection, options.featureProjection);
|
||||
} else {
|
||||
let geom;
|
||||
if (geometryType == GeometryType.POLYGON) {
|
||||
@@ -213,7 +209,7 @@ class MVT extends FeatureFormat {
|
||||
if (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.setId(id);
|
||||
feature.setProperties(values, true);
|
||||
@@ -222,14 +218,6 @@ class MVT extends FeatureFormat {
|
||||
return feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
getLastExtent() {
|
||||
return this.extent_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
readFeatures(source, opt_options) {
|
||||
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 pbfLayers = pbf.readFields(layersPBFReader, {});
|
||||
/** @type {Array<import("../Feature.js").FeatureLike>} */
|
||||
const features = [];
|
||||
for (const name in pbfLayers) {
|
||||
if (layers && layers.indexOf(name) == -1) {
|
||||
@@ -254,11 +249,13 @@ class MVT extends FeatureFormat {
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
|
||||
@@ -718,8 +718,7 @@ class WKT extends TextFeature {
|
||||
* @inheritDoc
|
||||
*/
|
||||
writeGeometryText(geometry, opt_options) {
|
||||
return encode(/** @type {import("../geom/Geometry.js").default} */ (
|
||||
transformGeometryWithOptions(geometry, true, opt_options)));
|
||||
return encode(transformGeometryWithOptions(geometry, true, opt_options));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {deflateMultiCoordinatesArray} from './flat/deflate.js';
|
||||
import {inflateMultiCoordinatesArray} from './flat/inflate.js';
|
||||
import {getInteriorPointsOfMultiArray} from './flat/interiorpoint.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';
|
||||
|
||||
/**
|
||||
@@ -251,7 +251,7 @@ class MultiPolygon extends SimpleGeometry {
|
||||
getOrientedFlatCoordinates() {
|
||||
if (this.orientedRevision_ != this.getRevision()) {
|
||||
const flatCoordinates = this.flatCoordinates;
|
||||
if (linearRingsAreOriented(
|
||||
if (linearRingssAreOriented(
|
||||
flatCoordinates, 0, this.endss_, this.stride)) {
|
||||
this.orientedFlatCoordinates_ = flatCoordinates;
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,7 @@ import {deflateCoordinatesArray} from './flat/deflate.js';
|
||||
import {inflateCoordinatesArray} from './flat/inflate.js';
|
||||
import {getInteriorPointOfArray} from './flat/interiorpoint.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 {modulo} from '../math.js';
|
||||
|
||||
@@ -266,7 +266,7 @@ class Polygon extends SimpleGeometry {
|
||||
getOrientedFlatCoordinates() {
|
||||
if (this.orientedRevision_ != this.getRevision()) {
|
||||
const flatCoordinates = this.flatCoordinates;
|
||||
if (linearRingIsOriented(
|
||||
if (linearRingsAreOriented(
|
||||
flatCoordinates, 0, this.ends_, this.stride)) {
|
||||
this.orientedFlatCoordinates_ = flatCoordinates;
|
||||
} else {
|
||||
|
||||
@@ -41,7 +41,7 @@ export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
|
||||
* (counter-clockwise exterior ring and clockwise interior rings).
|
||||
* @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;
|
||||
for (let i = 0, ii = ends.length; i < ii; ++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).
|
||||
* @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) {
|
||||
if (!linearRingIsOriented(
|
||||
flatCoordinates, offset, endss[i], stride, opt_right)) {
|
||||
const ends = endss[i];
|
||||
if (!linearRingsAreOriented(
|
||||
flatCoordinates, offset, ends, stride, opt_right)) {
|
||||
return false;
|
||||
}
|
||||
if (ends.length) {
|
||||
offset = ends[ends.length - 1];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,21 +11,19 @@
|
||||
* @param {number} offset Offset.
|
||||
* @param {number} end End.
|
||||
* @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.
|
||||
* @param {S=} opt_this The object to be used as the value of 'this'
|
||||
* within callback.
|
||||
* @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 point2 = [];
|
||||
let ret;
|
||||
for (; (offset + stride) < end; offset += stride) {
|
||||
point2[0] = flatCoordinates[offset + stride];
|
||||
point2[1] = flatCoordinates[offset + stride + 1];
|
||||
ret = callback.call(opt_this, point1, point2);
|
||||
ret = callback(point1, point2);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ export {default as Translate} from './interaction/Translate.js';
|
||||
* 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
|
||||
* 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
|
||||
* 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;
|
||||
if (pinchZoom) {
|
||||
interactions.push(new PinchZoom({
|
||||
constrainResolution: options.constrainResolution,
|
||||
duration: options.zoomDuration
|
||||
}));
|
||||
}
|
||||
@@ -146,7 +143,6 @@ export function defaults(opt_options) {
|
||||
if (mouseWheelZoom) {
|
||||
interactions.push(new MouseWheelZoom({
|
||||
condition: options.onFocusOnly ? focus : undefined,
|
||||
constrainResolution: options.constrainResolution,
|
||||
duration: options.zoomDuration
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/**
|
||||
* @module ol/interaction/DragPan
|
||||
*/
|
||||
import ViewHint from '../ViewHint.js';
|
||||
import {scale as scaleCoordinate, rotate as rotateCoordinate, add as addCoordinate} from '../coordinate.js';
|
||||
import {scale as scaleCoordinate, rotate as rotateCoordinate} from '../coordinate.js';
|
||||
import {easeOut} from '../easing.js';
|
||||
import {noModifierKeys} from '../events/condition.js';
|
||||
import {FALSE} from '../functions.js';
|
||||
@@ -74,10 +73,6 @@ class DragPan extends PointerInteraction {
|
||||
* @inheritDoc
|
||||
*/
|
||||
handleDragEvent(mapBrowserEvent) {
|
||||
if (!this.panning_) {
|
||||
this.panning_ = true;
|
||||
this.getMap().getView().setHint(ViewHint.INTERACTING, 1);
|
||||
}
|
||||
const targetPointers = this.targetPointers;
|
||||
const centroid = centroidFromPointers(targetPointers);
|
||||
if (targetPointers.length == this.lastPointersCount_) {
|
||||
@@ -85,16 +80,15 @@ class DragPan extends PointerInteraction {
|
||||
this.kinetic_.update(centroid[0], centroid[1]);
|
||||
}
|
||||
if (this.lastCentroid) {
|
||||
const deltaX = this.lastCentroid[0] - centroid[0];
|
||||
const deltaY = centroid[1] - this.lastCentroid[1];
|
||||
const delta = [
|
||||
this.lastCentroid[0] - centroid[0],
|
||||
centroid[1] - this.lastCentroid[1]
|
||||
];
|
||||
const map = mapBrowserEvent.map;
|
||||
const view = map.getView();
|
||||
let center = [deltaX, deltaY];
|
||||
scaleCoordinate(center, view.getResolution());
|
||||
rotateCoordinate(center, view.getRotation());
|
||||
addCoordinate(center, view.getCenter());
|
||||
center = view.constrainCenter(center);
|
||||
view.setCenter(center);
|
||||
scaleCoordinate(delta, view.getResolution());
|
||||
rotateCoordinate(delta, view.getRotation());
|
||||
view.adjustCenter(delta);
|
||||
}
|
||||
} else if (this.kinetic_) {
|
||||
// reset so we don't overestimate the kinetic energy after
|
||||
@@ -122,14 +116,14 @@ class DragPan extends PointerInteraction {
|
||||
centerpx[1] - distance * Math.sin(angle)
|
||||
]);
|
||||
view.animate({
|
||||
center: view.constrainCenter(dest),
|
||||
center: view.getConstrainedCenter(dest),
|
||||
duration: 500,
|
||||
easing: easeOut
|
||||
});
|
||||
}
|
||||
if (this.panning_) {
|
||||
this.panning_ = false;
|
||||
view.setHint(ViewHint.INTERACTING, -1);
|
||||
view.endInteraction();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
@@ -153,7 +147,11 @@ class DragPan extends PointerInteraction {
|
||||
this.lastCentroid = null;
|
||||
// stop any current animation
|
||||
if (view.getAnimating()) {
|
||||
view.setCenter(mapBrowserEvent.frameState.viewState.center);
|
||||
view.cancelAnimations();
|
||||
}
|
||||
if (!this.panning_) {
|
||||
this.panning_ = true;
|
||||
this.getMap().getView().beginInteraction();
|
||||
}
|
||||
if (this.kinetic_) {
|
||||
this.kinetic_.begin();
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
* @module ol/interaction/DragRotate
|
||||
*/
|
||||
import {disable} from '../rotationconstraint.js';
|
||||
import ViewHint from '../ViewHint.js';
|
||||
import {altShiftKeysOnly, mouseOnly, mouseActionButton} from '../events/condition.js';
|
||||
import {FALSE} from '../functions.js';
|
||||
import {rotate, rotateWithoutConstraints} from './Interaction.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);
|
||||
if (this.lastAngle_ !== undefined) {
|
||||
const delta = theta - this.lastAngle_;
|
||||
const rotation = view.getRotation();
|
||||
rotateWithoutConstraints(view, rotation - delta);
|
||||
view.adjustRotation(-delta);
|
||||
}
|
||||
this.lastAngle_ = theta;
|
||||
}
|
||||
@@ -97,9 +94,7 @@ class DragRotate extends PointerInteraction {
|
||||
|
||||
const map = mapBrowserEvent.map;
|
||||
const view = map.getView();
|
||||
view.setHint(ViewHint.INTERACTING, -1);
|
||||
const rotation = view.getRotation();
|
||||
rotate(view, rotation, undefined, this.duration_);
|
||||
view.endInteraction(this.duration_);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,7 +109,7 @@ class DragRotate extends PointerInteraction {
|
||||
|
||||
if (mouseActionButton(mapBrowserEvent) && this.condition_(mapBrowserEvent)) {
|
||||
const map = mapBrowserEvent.map;
|
||||
map.getView().setHint(ViewHint.INTERACTING, 1);
|
||||
map.getView().beginInteraction();
|
||||
this.lastAngle_ = undefined;
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/**
|
||||
* @module ol/interaction/DragRotateAndZoom
|
||||
*/
|
||||
import {disable} from '../rotationconstraint.js';
|
||||
import ViewHint from '../ViewHint.js';
|
||||
import {shiftKeyOnly, mouseOnly} from '../events/condition.js';
|
||||
import {rotate, rotateWithoutConstraints, zoom, zoomWithoutConstraints} from './Interaction.js';
|
||||
import PointerInteraction from './Pointer.js';
|
||||
|
||||
|
||||
@@ -88,14 +85,13 @@ class DragRotateAndZoom extends PointerInteraction {
|
||||
const theta = Math.atan2(deltaY, deltaX);
|
||||
const magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
const view = map.getView();
|
||||
if (view.getConstraints().rotation !== disable && this.lastAngle_ !== undefined) {
|
||||
const angleDelta = theta - this.lastAngle_;
|
||||
rotateWithoutConstraints(view, view.getRotation() - angleDelta);
|
||||
if (this.lastAngle_ !== undefined) {
|
||||
const angleDelta = this.lastAngle_ - theta;
|
||||
view.adjustRotation(angleDelta);
|
||||
}
|
||||
this.lastAngle_ = theta;
|
||||
if (this.lastMagnitude_ !== undefined) {
|
||||
const resolution = this.lastMagnitude_ * (view.getResolution() / magnitude);
|
||||
zoomWithoutConstraints(view, resolution);
|
||||
view.adjustResolution(this.lastMagnitude_ / magnitude);
|
||||
}
|
||||
if (this.lastMagnitude_ !== undefined) {
|
||||
this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;
|
||||
@@ -113,10 +109,8 @@ class DragRotateAndZoom extends PointerInteraction {
|
||||
|
||||
const map = mapBrowserEvent.map;
|
||||
const view = map.getView();
|
||||
view.setHint(ViewHint.INTERACTING, -1);
|
||||
const direction = this.lastScaleDelta_ - 1;
|
||||
rotate(view, view.getRotation());
|
||||
zoom(view, view.getResolution(), undefined, this.duration_, direction);
|
||||
const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
|
||||
view.endInteraction(this.duration_, direction);
|
||||
this.lastScaleDelta_ = 0;
|
||||
return false;
|
||||
}
|
||||
@@ -130,7 +124,7 @@ class DragRotateAndZoom extends PointerInteraction {
|
||||
}
|
||||
|
||||
if (this.condition_(mapBrowserEvent)) {
|
||||
mapBrowserEvent.map.getView().setHint(ViewHint.INTERACTING, 1);
|
||||
mapBrowserEvent.map.getView().beginInteraction();
|
||||
this.lastAngle_ = undefined;
|
||||
this.lastMagnitude_ = undefined;
|
||||
return true;
|
||||
|
||||
@@ -80,11 +80,8 @@ function onBoxEnd() {
|
||||
extent = mapExtent;
|
||||
}
|
||||
|
||||
const resolution = view.constrainResolution(
|
||||
view.getResolutionForExtent(extent, size));
|
||||
|
||||
let center = getCenter(extent);
|
||||
center = view.constrainCenter(center);
|
||||
const resolution = view.getConstrainedResolution(view.getResolutionForExtent(extent, size));
|
||||
const center = view.getConstrainedCenter(getCenter(extent), resolution);
|
||||
|
||||
view.animate({
|
||||
resolution: resolution,
|
||||
|
||||
@@ -891,7 +891,7 @@ class Draw extends PointerInteraction {
|
||||
this.sketchFeature_ = null;
|
||||
this.sketchPoint_ = null;
|
||||
this.sketchLine_ = null;
|
||||
/** @type {VectorSource} */ (this.overlay_.getSource()).clear(true);
|
||||
this.overlay_.getSource().clear(true);
|
||||
}
|
||||
return sketchFeature;
|
||||
}
|
||||
@@ -930,7 +930,7 @@ class Draw extends PointerInteraction {
|
||||
if (this.sketchPoint_) {
|
||||
sketchFeatures.push(this.sketchPoint_);
|
||||
}
|
||||
const overlaySource = /** @type {VectorSource} */ (this.overlay_.getSource());
|
||||
const overlaySource = this.overlay_.getSource();
|
||||
overlaySource.clear(true);
|
||||
overlaySource.addFeatures(sketchFeatures);
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ class ExtentInteraction extends PointerInteraction {
|
||||
extentFeature = new Feature(polygonFromExtent(extent));
|
||||
}
|
||||
this.extentFeature_ = extentFeature;
|
||||
/** @type {VectorSource} */ (this.extentOverlay_.getSource()).addFeature(extentFeature);
|
||||
this.extentOverlay_.getSource().addFeature(extentFeature);
|
||||
} else {
|
||||
if (!extent) {
|
||||
extentFeature.setGeometry(undefined);
|
||||
@@ -263,7 +263,7 @@ class ExtentInteraction extends PointerInteraction {
|
||||
if (!vertexFeature) {
|
||||
vertexFeature = new Feature(new Point(vertex));
|
||||
this.vertexFeature_ = vertexFeature;
|
||||
/** @type {VectorSource} */ (this.vertexOverlay_.getSource()).addFeature(vertexFeature);
|
||||
this.vertexOverlay_.getSource().addFeature(vertexFeature);
|
||||
} else {
|
||||
const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
|
||||
geometry.setCoordinates(vertex);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import BaseObject from '../Object.js';
|
||||
import {easeOut, linear} from '../easing.js';
|
||||
import InteractionProperty from './Property.js';
|
||||
import {clamp} from '../math.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -111,76 +110,14 @@ class Interaction extends BaseObject {
|
||||
export function pan(view, delta, opt_duration) {
|
||||
const currentCenter = view.getCenter();
|
||||
if (currentCenter) {
|
||||
const center = view.constrainCenter(
|
||||
[currentCenter[0] + delta[0], currentCenter[1] + delta[1]]);
|
||||
if (opt_duration) {
|
||||
const center = [currentCenter[0] + delta[0], currentCenter[1] + delta[1]];
|
||||
view.animate({
|
||||
duration: opt_duration,
|
||||
duration: opt_duration !== undefined ? opt_duration : 250,
|
||||
easing: linear,
|
||||
center: center
|
||||
center: view.getConstrainedCenter(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.
|
||||
@@ -189,63 +126,24 @@ export function zoom(view, resolution, opt_anchor, opt_duration, opt_direction)
|
||||
* @param {number=} opt_duration Duration.
|
||||
*/
|
||||
export function zoomByDelta(view, delta, opt_anchor, opt_duration) {
|
||||
const currentResolution = view.getResolution();
|
||||
let resolution = view.constrainResolution(currentResolution, delta, 0);
|
||||
const currentZoom = view.getZoom();
|
||||
|
||||
if (resolution !== undefined) {
|
||||
const resolutions = view.getResolutions();
|
||||
resolution = clamp(
|
||||
resolution,
|
||||
view.getMinResolution() || resolutions[resolutions.length - 1],
|
||||
view.getMaxResolution() || resolutions[0]);
|
||||
if (currentZoom === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a constraint on center, we need to change the anchor so that the
|
||||
// new center is within the extent. We first calculate the new center, apply
|
||||
// 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);
|
||||
const newZoom = view.getConstrainedZoom(currentZoom + delta);
|
||||
const newResolution = view.getResolutionForZoom(newZoom);
|
||||
|
||||
opt_anchor = [
|
||||
(resolution * currentCenter[0] - currentResolution * center[0]) /
|
||||
(resolution - currentResolution),
|
||||
(resolution * currentCenter[1] - currentResolution * center[1]) /
|
||||
(resolution - currentResolution)
|
||||
];
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
|
||||
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,
|
||||
resolution: newResolution,
|
||||
anchor: opt_anchor,
|
||||
duration: opt_duration,
|
||||
duration: opt_duration !== undefined ? opt_duration : 250,
|
||||
easing: easeOut
|
||||
});
|
||||
} else {
|
||||
if (opt_anchor) {
|
||||
const center = view.calculateCenterZoom(resolution, opt_anchor);
|
||||
view.setCenter(center);
|
||||
}
|
||||
view.setResolution(resolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Interaction;
|
||||
|
||||
@@ -368,7 +368,7 @@ class Modify extends PointerInteraction {
|
||||
// Remove the vertex feature if the collection of canditate features
|
||||
// is empty.
|
||||
if (this.vertexFeature_ && this.features_.getLength() === 0) {
|
||||
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
|
||||
this.overlay_.getSource().removeFeature(this.vertexFeature_);
|
||||
this.vertexFeature_ = null;
|
||||
}
|
||||
unlisten(feature, EventType.CHANGE,
|
||||
@@ -407,7 +407,7 @@ class Modify extends PointerInteraction {
|
||||
*/
|
||||
setActive(active) {
|
||||
if (this.vertexFeature_ && !active) {
|
||||
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
|
||||
this.overlay_.getSource().removeFeature(this.vertexFeature_);
|
||||
this.vertexFeature_ = null;
|
||||
}
|
||||
super.setActive(active);
|
||||
@@ -658,7 +658,7 @@ class Modify extends PointerInteraction {
|
||||
if (!vertexFeature) {
|
||||
vertexFeature = new Feature(new Point(coordinates));
|
||||
this.vertexFeature_ = vertexFeature;
|
||||
/** @type {VectorSource} */ (this.overlay_.getSource()).addFeature(vertexFeature);
|
||||
this.overlay_.getSource().addFeature(vertexFeature);
|
||||
} else {
|
||||
const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
|
||||
geometry.setCoordinates(coordinates);
|
||||
@@ -944,7 +944,7 @@ class Modify extends PointerInteraction {
|
||||
}
|
||||
}
|
||||
if (this.vertexFeature_) {
|
||||
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
|
||||
this.overlay_.getSource().removeFeature(this.vertexFeature_);
|
||||
this.vertexFeature_ = null;
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1139,7 @@ class Modify extends PointerInteraction {
|
||||
}
|
||||
this.updateSegmentIndices_(geometry, index, segmentData.depth, -1);
|
||||
if (this.vertexFeature_) {
|
||||
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
|
||||
this.overlay_.getSource().removeFeature(this.vertexFeature_);
|
||||
this.vertexFeature_ = null;
|
||||
}
|
||||
dragSegments.length = 0;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* @module ol/interaction/MouseWheelZoom
|
||||
*/
|
||||
import ViewHint from '../ViewHint.js';
|
||||
import {always} from '../events/condition.js';
|
||||
import {easeOut} from '../easing.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import {DEVICE_PIXEL_RATIO, FIREFOX, SAFARI} from '../has.js';
|
||||
import Interaction, {zoomByDelta} from './Interaction.js';
|
||||
@@ -34,9 +32,6 @@ export const Mode = {
|
||||
* {@link module:ol/events/condition~always}.
|
||||
* @property {number} [duration=250] Animation 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
|
||||
* 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.
|
||||
@@ -62,7 +57,13 @@ class MouseWheelZoom extends Interaction {
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.delta_ = 0;
|
||||
this.totalDelta_ = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.lastDelta_ = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -82,12 +83,6 @@ class MouseWheelZoom extends Interaction {
|
||||
*/
|
||||
this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.constrainResolution_ = options.constrainResolution || false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("../events/condition.js").Condition}
|
||||
@@ -137,22 +132,15 @@ class MouseWheelZoom extends Interaction {
|
||||
*/
|
||||
this.trackpadDeltaPerZoom_ = 300;
|
||||
|
||||
/**
|
||||
* The zoom factor by which scroll zooming is allowed to exceed the limits.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.trackpadZoomBuffer_ = 1.5;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
decrementInteractingHint_() {
|
||||
endInteraction_() {
|
||||
this.trackpadTimeoutId_ = undefined;
|
||||
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) {
|
||||
return false;
|
||||
} else {
|
||||
this.lastDelta_ = delta;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
@@ -218,55 +208,15 @@ class MouseWheelZoom extends Interaction {
|
||||
if (this.trackpadTimeoutId_) {
|
||||
clearTimeout(this.trackpadTimeoutId_);
|
||||
} else {
|
||||
view.setHint(ViewHint.INTERACTING, 1);
|
||||
}
|
||||
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
|
||||
});
|
||||
view.beginInteraction();
|
||||
}
|
||||
this.trackpadTimeoutId_ = setTimeout(this.endInteraction_.bind(this), this.trackpadEventGap_);
|
||||
view.adjustZoom(-delta / this.trackpadDeltaPerZoom_, this.lastAnchor_);
|
||||
this.startTime_ = now;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.delta_ += delta;
|
||||
this.totalDelta_ += delta;
|
||||
|
||||
const timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0);
|
||||
|
||||
@@ -286,10 +236,10 @@ class MouseWheelZoom extends Interaction {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
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_);
|
||||
this.mode_ = undefined;
|
||||
this.delta_ = 0;
|
||||
this.totalDelta_ = 0;
|
||||
this.lastAnchor_ = null;
|
||||
this.startTime_ = undefined;
|
||||
this.timeoutId_ = undefined;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/**
|
||||
* @module ol/interaction/PinchRotate
|
||||
*/
|
||||
import ViewHint from '../ViewHint.js';
|
||||
import {FALSE} from '../functions.js';
|
||||
import {rotate, rotateWithoutConstraints} from './Interaction.js';
|
||||
import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js';
|
||||
import {disable} from '../rotationconstraint.js';
|
||||
|
||||
@@ -118,9 +116,8 @@ class PinchRotate extends PointerInteraction {
|
||||
|
||||
// rotate
|
||||
if (this.rotating_) {
|
||||
const rotation = view.getRotation();
|
||||
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) {
|
||||
const map = mapBrowserEvent.map;
|
||||
const view = map.getView();
|
||||
view.setHint(ViewHint.INTERACTING, -1);
|
||||
if (this.rotating_) {
|
||||
const rotation = view.getRotation();
|
||||
rotate(view, rotation, this.anchor_, this.duration_);
|
||||
}
|
||||
view.endInteraction(this.duration_);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@@ -153,7 +146,7 @@ class PinchRotate extends PointerInteraction {
|
||||
this.rotating_ = false;
|
||||
this.rotationDelta_ = 0.0;
|
||||
if (!this.handlingDownUpSequence) {
|
||||
map.getView().setHint(ViewHint.INTERACTING, 1);
|
||||
map.getView().beginInteraction();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
/**
|
||||
* @module ol/interaction/PinchZoom
|
||||
*/
|
||||
import ViewHint from '../ViewHint.js';
|
||||
import {FALSE} from '../functions.js';
|
||||
import {zoom, zoomWithoutConstraints} from './Interaction.js';
|
||||
import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js';
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.constrainResolution_ = options.constrainResolution || false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("../coordinate.js").Coordinate}
|
||||
@@ -91,17 +81,6 @@ class PinchZoom extends PointerInteraction {
|
||||
|
||||
const map = mapBrowserEvent.map;
|
||||
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) {
|
||||
this.lastScaleDelta_ = scaleDelta;
|
||||
@@ -116,7 +95,7 @@ class PinchZoom extends PointerInteraction {
|
||||
|
||||
// scale, bypass the resolution constraint
|
||||
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) {
|
||||
const map = mapBrowserEvent.map;
|
||||
const view = map.getView();
|
||||
view.setHint(ViewHint.INTERACTING, -1);
|
||||
const resolution = view.getResolution();
|
||||
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);
|
||||
}
|
||||
const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
|
||||
view.endInteraction(this.duration_, direction);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@@ -153,7 +123,7 @@ class PinchZoom extends PointerInteraction {
|
||||
this.lastDistance_ = undefined;
|
||||
this.lastScaleDelta_ = 1;
|
||||
if (!this.handlingDownUpSequence) {
|
||||
map.getView().setHint(ViewHint.INTERACTING, 1);
|
||||
map.getView().beginInteraction();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -275,7 +275,7 @@ class Select extends Interaction {
|
||||
* @api
|
||||
*/
|
||||
getFeatures() {
|
||||
return /** @type {VectorSource} */ (this.featureOverlay_.getSource()).getFeaturesCollection();
|
||||
return this.featureOverlay_.getSource().getFeaturesCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,7 @@ import Layer from './Layer.js';
|
||||
* property on the layer object; for example, setting `title: 'My Title'` in the
|
||||
* options means that `title` is observable, and has get/set accessors.
|
||||
*
|
||||
* @extends {Layer<import("../source/Image.js").default>}
|
||||
* @api
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -39,6 +39,7 @@ import {assign} from '../obj.js';
|
||||
* property on the layer object; for example, setting `title: 'My Title'` in the
|
||||
* options means that `title` is observable, and has get/set accessors.
|
||||
*
|
||||
* @extends {Layer<import("../source/Tile.js").default>}
|
||||
* @api
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -61,6 +61,8 @@ const Property = {
|
||||
* property on the layer object; for example, setting `title: 'My Title'` in the
|
||||
* 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
|
||||
*/
|
||||
class BaseVectorLayer extends Layer {
|
||||
@@ -131,13 +133,6 @@ class BaseVectorLayer extends Layer {
|
||||
return this.declutter_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} declutter Declutter.
|
||||
*/
|
||||
setDeclutter(declutter) {
|
||||
this.declutter_ = declutter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
@@ -150,7 +150,11 @@ class Graticule extends VectorLayer {
|
||||
constructor(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.strokeStyle;
|
||||
@@ -422,11 +426,6 @@ class Graticule extends VectorLayer {
|
||||
|
||||
this.setRenderOrder(null);
|
||||
|
||||
this.renderBuffer_ = 0;
|
||||
|
||||
this.updateWhileAnimating_ = true;
|
||||
this.updateWhileInteracting_ = true;
|
||||
|
||||
this.tmpExtent_ = null;
|
||||
}
|
||||
|
||||
@@ -437,7 +436,7 @@ class Graticule extends VectorLayer {
|
||||
* @param {import("../proj/Projection.js").default} projection 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
|
||||
const layerExtent = this.getExtent() || [-Infinity, -Infinity, Infinity, Infinity];
|
||||
|
||||
@@ -62,6 +62,8 @@ import SourceState from '../source/State.js';
|
||||
*
|
||||
* @fires import("../render/Event.js").RenderEvent#prerender
|
||||
* @fires import("../render/Event.js").RenderEvent#postrender
|
||||
*
|
||||
* @template {import("../source/Source.js").default} SourceType
|
||||
*/
|
||||
class Layer extends BaseLayer {
|
||||
/**
|
||||
@@ -106,7 +108,7 @@ class Layer extends BaseLayer {
|
||||
getChangeEventType(LayerProperty.SOURCE),
|
||||
this.handleSourcePropertyChange_, this);
|
||||
|
||||
const source = options.source ? options.source : null;
|
||||
const source = options.source ? /** @type {SourceType} */ (options.source) : null;
|
||||
this.setSource(source);
|
||||
}
|
||||
|
||||
@@ -130,15 +132,12 @@ class Layer extends BaseLayer {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @api
|
||||
*/
|
||||
getSource() {
|
||||
const source = this.get(LayerProperty.SOURCE);
|
||||
return (
|
||||
/** @type {import("../source/Source.js").default} */ (source) || null
|
||||
);
|
||||
return /** @type {SourceType} */ (this.get(LayerProperty.SOURCE)) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,7 +226,7 @@ class Layer extends BaseLayer {
|
||||
|
||||
/**
|
||||
* Set the layer source.
|
||||
* @param {import("../source/Source.js").default} source The layer source.
|
||||
* @param {SourceType} source The layer source.
|
||||
* @observable
|
||||
* @api
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@ import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js';
|
||||
* property on the layer object; for example, setting `title: 'My Title'` in the
|
||||
* options means that `title` is observable, and has get/set accessors.
|
||||
*
|
||||
* @extends {BaseVectorLayer<import("../source/Vector.js").default>}
|
||||
* @api
|
||||
*/
|
||||
class VectorLayer extends BaseVectorLayer {
|
||||
|
||||
@@ -6,7 +6,41 @@ import {assign} from '../obj.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
|
||||
* viewport extent. A larger ratio avoids cut images during panning, but will cause a decrease in performance.
|
||||
*/
|
||||
|
||||
@@ -33,12 +33,13 @@ import {assign} from '../obj.js';
|
||||
* point symbol or line width.
|
||||
* @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
|
||||
* 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
|
||||
* animations. Point symbols and texts are accurately rendered as vectors and can stay upright on
|
||||
* 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("../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
|
||||
@@ -46,8 +47,7 @@ import {assign} from '../obj.js';
|
||||
* 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. When set to `true`, a `renderMode` of `'image'` will be overridden with
|
||||
* `'hybrid'`.
|
||||
* 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
|
||||
@@ -70,6 +70,7 @@ import {assign} from '../obj.js';
|
||||
* options means that `title` is observable, and has get/set accessors.
|
||||
*
|
||||
* @param {Options=} opt_options Options.
|
||||
* @extends {BaseVectorLayer<import("../source/VectorTile.js").default>}
|
||||
* @api
|
||||
*/
|
||||
class VectorTileLayer extends BaseVectorLayer {
|
||||
@@ -85,16 +86,12 @@ class VectorTileLayer extends BaseVectorLayer {
|
||||
|
||||
super(/** @type {import("./Vector.js").Options} */ (baseOptions));
|
||||
|
||||
let renderMode = options.renderMode || VectorTileRenderType.HYBRID;
|
||||
const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
|
||||
assert(renderMode == undefined ||
|
||||
renderMode == VectorTileRenderType.IMAGE ||
|
||||
renderMode == VectorTileRenderType.HYBRID,
|
||||
28); // `renderMode` must be `'image'` or `'hybrid'`
|
||||
|
||||
if (options.declutter && renderMode == VectorTileRenderType.IMAGE) {
|
||||
renderMode = VectorTileRenderType.HYBRID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @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;
|
||||
|
||||
@@ -188,6 +188,12 @@ export function getPointResolution(projection, resolution, point, opt_units) {
|
||||
const getter = projection.getPointResolutionFunc();
|
||||
if (getter) {
|
||||
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 {
|
||||
const units = projection.getUnits();
|
||||
if (units == Units.DEGREES && !opt_units || opt_units == Units.DEGREES) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import {METERS_PER_UNIT} from './Units.js';
|
||||
* @property {function(number, import("../coordinate.js").Coordinate):number} [getPointResolution]
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class RenderBox extends Disposable {
|
||||
* @type {HTMLDivElement}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div'));
|
||||
this.element_ = document.createElement('div');
|
||||
this.element_.style.position = 'absolute';
|
||||
this.element_.className = 'ol-box ' + className;
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ const tmpTransform = createTransform();
|
||||
* 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
|
||||
* through the API is limited to getting the type and extent of the geometry.
|
||||
*
|
||||
*/
|
||||
class RenderFeature {
|
||||
/**
|
||||
* @param {GeometryType} type Geometry type.
|
||||
* @param {Array<number>} flatCoordinates Flat coordinates. These always need
|
||||
* to be right-handed for polygons.
|
||||
@@ -30,7 +32,6 @@ const tmpTransform = createTransform();
|
||||
* @param {Object<string, *>} properties Properties.
|
||||
* @param {number|string|undefined} id Feature id.
|
||||
*/
|
||||
class RenderFeature {
|
||||
constructor(type, flatCoordinates, ends, properties, id) {
|
||||
/**
|
||||
* @private
|
||||
|
||||
@@ -288,22 +288,22 @@ function getMeasureContext() {
|
||||
* @return {import("../size.js").Size} Measurement.
|
||||
*/
|
||||
export const measureTextHeight = (function() {
|
||||
let span;
|
||||
let div;
|
||||
const heights = textHeights;
|
||||
return function(font) {
|
||||
let height = heights[font];
|
||||
if (height == undefined) {
|
||||
if (!span) {
|
||||
span = document.createElement('span');
|
||||
span.textContent = 'M';
|
||||
span.style.margin = span.style.padding = '0 !important';
|
||||
span.style.position = 'absolute !important';
|
||||
span.style.left = '-99999px !important';
|
||||
if (!div) {
|
||||
div = document.createElement('div');
|
||||
div.innerHTML = 'M';
|
||||
div.style.margin = div.style.padding = '0 !important';
|
||||
div.style.position = 'absolute !important';
|
||||
div.style.left = '-99999px !important';
|
||||
}
|
||||
span.style.font = font;
|
||||
document.body.appendChild(span);
|
||||
height = heights[font] = span.offsetHeight;
|
||||
document.body.removeChild(span);
|
||||
div.style.font = font;
|
||||
document.body.appendChild(div);
|
||||
height = heights[font] = div.offsetHeight;
|
||||
document.body.removeChild(div);
|
||||
}
|
||||
return height;
|
||||
};
|
||||
|
||||
@@ -206,7 +206,7 @@ class CanvasBuilder extends VectorContext {
|
||||
* @inheritDoc.
|
||||
*/
|
||||
drawCustom(geometry, feature, renderer) {
|
||||
this.beginGeometry(geometry, feature);
|
||||
this.beginGeometry(feature);
|
||||
const type = geometry.getType();
|
||||
const stride = geometry.getStride();
|
||||
const builderBegin = this.coordinates.length;
|
||||
@@ -248,15 +248,14 @@ class CanvasBuilder extends VectorContext {
|
||||
this.instructions.push([CanvasInstruction.CUSTOM,
|
||||
builderBegin, builderEnd, geometry, renderer]);
|
||||
}
|
||||
this.endGeometry(geometry, feature);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
|
||||
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
||||
*/
|
||||
beginGeometry(geometry, feature) {
|
||||
beginGeometry(feature) {
|
||||
this.beginGeometryInstruction1_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0];
|
||||
this.instructions.push(this.beginGeometryInstruction1_);
|
||||
this.beginGeometryInstruction2_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0];
|
||||
@@ -353,10 +352,9 @@ class CanvasBuilder extends VectorContext {
|
||||
|
||||
/**
|
||||
* @param {import("../canvas.js").FillStrokeState} state State.
|
||||
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
|
||||
* @return {Array<*>} Fill instruction.
|
||||
*/
|
||||
createFill(state, geometry) {
|
||||
createFill(state) {
|
||||
const fillStyle = state.fillStyle;
|
||||
/** @type {Array<*>} */
|
||||
const fillInstruction = [CanvasInstruction.SET_FILL_STYLE, fillStyle];
|
||||
@@ -389,14 +387,13 @@ class CanvasBuilder extends VectorContext {
|
||||
|
||||
/**
|
||||
* @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 {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
|
||||
* @param {function(this:CanvasBuilder, import("../canvas.js").FillStrokeState):Array<*>} createFill Create fill.
|
||||
*/
|
||||
updateFillStyle(state, createFill, geometry) {
|
||||
updateFillStyle(state, createFill) {
|
||||
const fillStyle = state.fillStyle;
|
||||
if (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle) {
|
||||
if (fillStyle !== undefined) {
|
||||
this.instructions.push(createFill.call(this, state, geometry));
|
||||
this.instructions.push(createFill.call(this, state));
|
||||
}
|
||||
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.
|
||||
*/
|
||||
endGeometry(geometry, feature) {
|
||||
endGeometry(feature) {
|
||||
this.beginGeometryInstruction1_[2] = this.instructions.length;
|
||||
this.beginGeometryInstruction1_ = null;
|
||||
this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length;
|
||||
|
||||
@@ -143,6 +143,8 @@ class ExecutorGroup extends Disposable {
|
||||
executors[key].disposeInternal();
|
||||
}
|
||||
}
|
||||
const canvas = this.hitDetectionContext_.canvas;
|
||||
canvas.width = canvas.height = 0;
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
if (!this.image_) {
|
||||
return;
|
||||
}
|
||||
this.beginGeometry(pointGeometry, feature);
|
||||
this.beginGeometry(feature);
|
||||
const flatCoordinates = pointGeometry.getFlatCoordinates();
|
||||
const stride = pointGeometry.getStride();
|
||||
const myBegin = this.coordinates.length;
|
||||
@@ -132,7 +132,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
|
||||
this.scale_, this.width_
|
||||
]);
|
||||
this.endGeometry(pointGeometry, feature);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,7 +142,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
if (!this.image_) {
|
||||
return;
|
||||
}
|
||||
this.beginGeometry(multiPointGeometry, feature);
|
||||
this.beginGeometry(feature);
|
||||
const flatCoordinates = multiPointGeometry.getFlatCoordinates();
|
||||
const stride = multiPointGeometry.getStride();
|
||||
const myBegin = this.coordinates.length;
|
||||
@@ -162,7 +162,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
|
||||
this.scale_, this.width_
|
||||
]);
|
||||
this.endGeometry(multiPointGeometry, feature);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,7 +44,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
|
||||
return;
|
||||
}
|
||||
this.updateStrokeStyle(state, this.applyStroke);
|
||||
this.beginGeometry(lineStringGeometry, feature);
|
||||
this.beginGeometry(feature);
|
||||
this.hitDetectionInstructions.push([
|
||||
CanvasInstruction.SET_STROKE_STYLE,
|
||||
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
|
||||
@@ -54,7 +54,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
|
||||
const stride = lineStringGeometry.getStride();
|
||||
this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
|
||||
this.hitDetectionInstructions.push(strokeInstruction);
|
||||
this.endGeometry(lineStringGeometry, feature);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +68,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
|
||||
return;
|
||||
}
|
||||
this.updateStrokeStyle(state, this.applyStroke);
|
||||
this.beginGeometry(multiLineStringGeometry, feature);
|
||||
this.beginGeometry(feature);
|
||||
this.hitDetectionInstructions.push([
|
||||
CanvasInstruction.SET_STROKE_STYLE,
|
||||
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
|
||||
@@ -82,7 +82,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
|
||||
offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride);
|
||||
}
|
||||
this.hitDetectionInstructions.push(strokeInstruction);
|
||||
this.endGeometry(multiLineStringGeometry, feature);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,8 +72,8 @@ class CanvasPolygonBuilder extends CanvasBuilder {
|
||||
if (fillStyle === undefined && strokeStyle === undefined) {
|
||||
return;
|
||||
}
|
||||
this.setFillStrokeStyles_(circleGeometry);
|
||||
this.beginGeometry(circleGeometry, feature);
|
||||
this.setFillStrokeStyles_();
|
||||
this.beginGeometry(feature);
|
||||
if (state.fillStyle !== undefined) {
|
||||
this.hitDetectionInstructions.push([
|
||||
CanvasInstruction.SET_FILL_STYLE,
|
||||
@@ -103,7 +103,7 @@ class CanvasPolygonBuilder extends CanvasBuilder {
|
||||
this.instructions.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) {
|
||||
return;
|
||||
}
|
||||
this.setFillStrokeStyles_(polygonGeometry);
|
||||
this.beginGeometry(polygonGeometry, feature);
|
||||
this.setFillStrokeStyles_();
|
||||
this.beginGeometry(feature);
|
||||
if (state.fillStyle !== undefined) {
|
||||
this.hitDetectionInstructions.push([
|
||||
CanvasInstruction.SET_FILL_STYLE,
|
||||
@@ -135,7 +135,7 @@ class CanvasPolygonBuilder extends CanvasBuilder {
|
||||
const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
|
||||
const stride = polygonGeometry.getStride();
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
this.setFillStrokeStyles_(multiPolygonGeometry);
|
||||
this.beginGeometry(multiPolygonGeometry, feature);
|
||||
this.setFillStrokeStyles_();
|
||||
this.beginGeometry(feature);
|
||||
if (state.fillStyle !== undefined) {
|
||||
this.hitDetectionInstructions.push([
|
||||
CanvasInstruction.SET_FILL_STYLE,
|
||||
@@ -170,7 +170,7 @@ class CanvasPolygonBuilder extends CanvasBuilder {
|
||||
for (let i = 0, ii = endss.length; i < ii; ++i) {
|
||||
offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride);
|
||||
}
|
||||
this.endGeometry(multiPolygonGeometry, feature);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,13 +195,12 @@ class CanvasPolygonBuilder extends CanvasBuilder {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
|
||||
*/
|
||||
setFillStrokeStyles_(geometry) {
|
||||
setFillStrokeStyles_() {
|
||||
const state = this.state;
|
||||
const fillStyle = state.fillStyle;
|
||||
if (fillStyle !== undefined) {
|
||||
this.updateFillStyle(state, this.createFill, geometry);
|
||||
this.updateFillStyle(state, this.createFill);
|
||||
}
|
||||
if (state.strokeStyle !== undefined) {
|
||||
this.updateStrokeStyle(state, this.applyStroke);
|
||||
|
||||
@@ -184,7 +184,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
ends.push(endss[i][0]);
|
||||
}
|
||||
}
|
||||
this.beginGeometry(geometry, feature);
|
||||
this.beginGeometry(feature);
|
||||
const textAlign = textState.textAlign;
|
||||
let flatOffset = 0;
|
||||
let flatEnd;
|
||||
@@ -204,7 +204,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
this.drawChars_(begin, end, this.declutterGroup_);
|
||||
begin = end;
|
||||
}
|
||||
this.endGeometry(geometry, feature);
|
||||
this.endGeometry(feature);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -258,8 +258,8 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
if (textState.backgroundFill || textState.backgroundStroke) {
|
||||
this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke);
|
||||
if (textState.backgroundFill) {
|
||||
this.updateFillStyle(this.state, this.createFill, geometry);
|
||||
this.hitDetectionInstructions.push(this.createFill(this.state, geometry));
|
||||
this.updateFillStyle(this.state, this.createFill);
|
||||
this.hitDetectionInstructions.push(this.createFill(this.state));
|
||||
}
|
||||
if (textState.backgroundStroke) {
|
||||
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.
|
||||
// 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.endGeometry(geometry, feature);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,10 +221,8 @@ class LayerRenderer extends Observable {
|
||||
* @param {import("../extent.js").Extent} extent Extent.
|
||||
* @param {number} currentZ Current Z.
|
||||
* @param {number} preload Load low resolution tiles up to 'preload' levels.
|
||||
* @param {function(this: T, import("../Tile.js").default)=} opt_tileCallback Tile callback.
|
||||
* @param {T=} opt_this Object to use as `this` in `opt_tileCallback`.
|
||||
* @param {function(import("../Tile.js").default)=} opt_tileCallback Tile callback.
|
||||
* @protected
|
||||
* @template T
|
||||
*/
|
||||
manageTilePyramid(
|
||||
frameState,
|
||||
@@ -235,8 +233,7 @@ class LayerRenderer extends Observable {
|
||||
extent,
|
||||
currentZ,
|
||||
preload,
|
||||
opt_tileCallback,
|
||||
opt_this
|
||||
opt_tileCallback
|
||||
) {
|
||||
const tileSourceKey = getUid(tileSource);
|
||||
if (!(tileSourceKey in frameState.wantedTiles)) {
|
||||
@@ -261,7 +258,7 @@ class LayerRenderer extends Observable {
|
||||
}
|
||||
}
|
||||
if (opt_tileCallback !== undefined) {
|
||||
opt_tileCallback.call(opt_this, tile);
|
||||
opt_tileCallback(tile);
|
||||
}
|
||||
} else {
|
||||
tileSource.useTile(z, x, y, projection);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -43,8 +43,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
const viewState = frameState.viewState;
|
||||
const viewResolution = viewState.resolution;
|
||||
|
||||
const imageLayer = /** @type {import("../../layer/Image.js").default} */ (this.getLayer());
|
||||
const imageSource = /** @type {import("../../source/Image.js").default} */ (imageLayer.getSource());
|
||||
const imageSource = this.getLayer().getSource();
|
||||
|
||||
const hints = frameState.viewHints;
|
||||
|
||||
|
||||
@@ -63,6 +63,14 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
canvas.className = this.getLayer().getClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
disposeInternal() {
|
||||
this.context.canvas.width = this.context.canvas.height = 0;
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
|
||||
@@ -88,7 +88,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
*/
|
||||
getTile(z, x, y, pixelRatio, projection) {
|
||||
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);
|
||||
if (tile.getState() == TileState.ERROR) {
|
||||
if (!tileLayer.getUseInterimTilesOnError()) {
|
||||
@@ -105,6 +105,16 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
return tile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
loadedTileCallback(tiles, zoom, tile) {
|
||||
if (this.isDrawableTile(tile)) {
|
||||
return super.loadedTileCallback(tiles, zoom, tile);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -130,7 +140,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
const pixelRatio = frameState.pixelRatio;
|
||||
|
||||
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 tileGrid = tileSource.getTileGridForProjection(projection);
|
||||
const z = tileGrid.getZForResolution(viewResolution, this.zDirection);
|
||||
@@ -199,7 +209,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
covered = findLoadedTiles(z + 1, childTileRange);
|
||||
}
|
||||
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 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.updateUsedTiles(frameState.usedTiles, tileSource, tile);
|
||||
}
|
||||
@@ -318,7 +328,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
/**
|
||||
* @param {import("../../Tile.js").default} tile Tile.
|
||||
* @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} y Top of the tile.
|
||||
* @param {number} w Width of the tile.
|
||||
@@ -326,7 +335,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
* @param {number} gutter Tile gutter.
|
||||
* @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);
|
||||
if (!image) {
|
||||
return;
|
||||
@@ -334,7 +343,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
const uid = getUid(this);
|
||||
const alpha = transition ? tile.getAlpha(uid, frameState.time) : 1;
|
||||
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)) {
|
||||
this.context.clearRect(x, y, w, h);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ import {getHeight, getWidth, isEmpty, scaleFromCenter} from '../../extent.js';
|
||||
import {assign} from '../../obj.js';
|
||||
import CanvasImageLayerRenderer from './ImageLayer.js';
|
||||
import CanvasVectorLayerRenderer from './VectorLayer.js';
|
||||
import {listen} from '../../events.js';
|
||||
import EventType from '../../events/EventType.js';
|
||||
import ImageState from '../../ImageState.js';
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -82,17 +85,19 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
||||
if (vectorRenderer.prepareFrame(imageFrameState, layerState) &&
|
||||
(vectorRenderer.replayGroupChanged ||
|
||||
!equals(skippedFeatures, newSkippedFeatures))) {
|
||||
context.canvas.width = imageFrameState.size[0] * pixelRatio;
|
||||
context.canvas.height = imageFrameState.size[1] * pixelRatio;
|
||||
vectorRenderer.renderFrame(imageFrameState, layerState);
|
||||
skippedFeatures = newSkippedFeatures;
|
||||
callback();
|
||||
}
|
||||
});
|
||||
if (this.loadImage(image)) {
|
||||
|
||||
listen(image, EventType.CHANGE, function() {
|
||||
if (image.getState() === ImageState.LOADED) {
|
||||
this.image_ = image;
|
||||
this.skippedFeatures_ = skippedFeatures;
|
||||
}
|
||||
}, this);
|
||||
image.load();
|
||||
}
|
||||
|
||||
if (this.image_) {
|
||||
|
||||
@@ -129,7 +129,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
const projection = viewState.projection;
|
||||
const rotation = viewState.rotation;
|
||||
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
|
||||
const clipExtent = layerState.extent;
|
||||
@@ -196,7 +196,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
} else {
|
||||
const resolution = frameState.viewState.resolution;
|
||||
const rotation = frameState.viewState.rotation;
|
||||
const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
|
||||
const layer = this.getLayer();
|
||||
/** @type {!Object<string, boolean>} */
|
||||
const features = {};
|
||||
const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {},
|
||||
@@ -239,7 +239,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
*/
|
||||
prepareFrame(frameState, layerState) {
|
||||
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 interacting = frameState.viewHints[ViewHint.INTERACTING];
|
||||
|
||||
@@ -10,8 +10,6 @@ import EventType from '../../events/EventType.js';
|
||||
import rbush from 'rbush';
|
||||
import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.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 {labelCache} from '../../render/canvas.js';
|
||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||
@@ -152,6 +150,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
*/
|
||||
disposeInternal() {
|
||||
unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this);
|
||||
this.overlayContext_.canvas.width = this.overlayContext_.canvas.height = 0;
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
@@ -205,16 +204,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
* @inheritDoc
|
||||
*/
|
||||
getTileImage(tile) {
|
||||
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
|
||||
return tile.getImage(tileLayer);
|
||||
return tile.getImage(this.getLayer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
prepareFrame(frameState, layerState) {
|
||||
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
|
||||
const layerRevision = layer.getRevision();
|
||||
const layerRevision = this.getLayer().getRevision();
|
||||
if (this.renderedLayerRevision_ != layerRevision) {
|
||||
this.renderedTiles.length = 0;
|
||||
}
|
||||
@@ -229,9 +226,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
* @private
|
||||
*/
|
||||
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 renderOrder = /** @type {import("../../render.js").OrderFunction} */ (layer.getRenderOrder()) || null;
|
||||
const renderOrder = layer.getRenderOrder() || null;
|
||||
|
||||
const builderState = tile.getReplayState(layer);
|
||||
if (!builderState.dirty && builderState.renderedRevision == revision &&
|
||||
@@ -239,7 +236,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource());
|
||||
const source = layer.getSource();
|
||||
const sourceTileGrid = source.getTileGrid();
|
||||
const tileGrid = source.getTileGridForProjection(projection);
|
||||
const zoom = tile.tileCoord[0];
|
||||
@@ -265,12 +262,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
const sharedExtent = getIntersection(tileExtent, sourceTileExtent);
|
||||
const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null :
|
||||
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;
|
||||
const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution,
|
||||
pixelRatio, !!this.declutterTree_);
|
||||
@@ -299,15 +290,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
}
|
||||
for (let i = 0, ii = features.length; i < ii; ++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())) {
|
||||
render.call(this, feature);
|
||||
}
|
||||
@@ -334,22 +316,25 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
const rotation = frameState.viewState.rotation;
|
||||
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
|
||||
const layer = this.getLayer();
|
||||
const source = /** @type {import("../../source/VectorTile").default} */ (layer.getSource());
|
||||
const source = layer.getSource();
|
||||
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
|
||||
/** @type {!Object<string, boolean>} */
|
||||
const features = {};
|
||||
|
||||
const renderedTiles = /** @type {Array<import("../../VectorRenderTile.js").default>} */ (this.renderedTiles);
|
||||
|
||||
let bufferedExtent, found;
|
||||
let found;
|
||||
let i, ii;
|
||||
for (i = 0, ii = renderedTiles.length; i < ii; ++i) {
|
||||
const tile = renderedTiles[i];
|
||||
if (!this.declutterTree_) {
|
||||
// When not decluttering, we only need to consider the tile that contains the given
|
||||
// coordinate, because each feature will be rendered for each tile that contains it.
|
||||
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
|
||||
bufferedExtent = buffer(tileExtent, hitTolerance * resolution, bufferedExtent);
|
||||
if (!containsCoordinate(bufferedExtent, coordinate)) {
|
||||
if (!containsCoordinate(tileExtent, coordinate)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const executorGroups = tile.executorGroups[getUid(layer)];
|
||||
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
|
||||
const executorGroup = executorGroups[t];
|
||||
@@ -359,7 +344,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
* @return {?} Callback result.
|
||||
*/
|
||||
function(feature) {
|
||||
const key = getUid(feature);
|
||||
let key = feature.getId();
|
||||
if (key === undefined) {
|
||||
key = getUid(feature);
|
||||
}
|
||||
if (!(key in features)) {
|
||||
features[key] = true;
|
||||
return callback.call(thisArg, feature, layer);
|
||||
@@ -378,7 +366,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
*/
|
||||
getReplayTransform_(tile, frameState) {
|
||||
const layer = this.getLayer();
|
||||
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource());
|
||||
const source = layer.getSource();
|
||||
const tileGrid = source.getTileGrid();
|
||||
const tileCoord = tile.tileCoord;
|
||||
const tileResolution = tileGrid.getResolution(tileCoord[0]);
|
||||
@@ -435,7 +423,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
|
||||
const context = this.overlayContext_;
|
||||
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 pixelRatio = frameState.pixelRatio;
|
||||
const rotation = frameState.viewState.rotation;
|
||||
@@ -547,6 +535,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
const tile = this.renderTileImageQueue_[uid];
|
||||
frameState.animate = true;
|
||||
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);
|
||||
}
|
||||
clear(this.renderTileImageQueue_);
|
||||
@@ -608,7 +600,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
replayState.renderedTileZ = tile.sourceZ;
|
||||
const tileCoord = tile.wrappedTileCoord;
|
||||
const z = tileCoord[0];
|
||||
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource());
|
||||
const source = layer.getSource();
|
||||
const tileGrid = source.getTileGridForProjection(projection);
|
||||
const resolution = tileGrid.getResolution(z);
|
||||
const context = tile.getContext(layer);
|
||||
|
||||
@@ -264,7 +264,7 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
*/
|
||||
prepareFrame(frameState) {
|
||||
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;
|
||||
|
||||
|
||||
@@ -2,37 +2,87 @@
|
||||
* @module ol/resolutionconstraint
|
||||
*/
|
||||
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 {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 createSnapToResolutions(resolutions) {
|
||||
export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent) {
|
||||
return (
|
||||
/**
|
||||
* @param {number|undefined} resolution Resolution.
|
||||
* @param {number} delta Delta.
|
||||
* @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, delta, direction) {
|
||||
function(resolution, direction, size, opt_isMoving) {
|
||||
if (resolution !== undefined) {
|
||||
let z = linearFindNearest(resolutions, resolution, direction);
|
||||
z = clamp(z + delta, 0, resolutions.length - 1);
|
||||
const index = Math.floor(z);
|
||||
if (z != index && index < resolutions.length - 1) {
|
||||
const power = resolutions[index] / resolutions[index + 1];
|
||||
return resolutions[index] / Math.pow(power, z - index);
|
||||
} else {
|
||||
return resolutions[index];
|
||||
const maxResolution = resolutions[0];
|
||||
const minResolution = resolutions[resolutions.length - 1];
|
||||
const cappedMaxRes = opt_maxExtent ?
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
|
||||
maxResolution;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
const capped = Math.min(cappedMaxRes, resolution);
|
||||
const z = Math.floor(linearFindNearest(resolutions, capped, direction));
|
||||
return resolutions[z];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
@@ -44,29 +94,78 @@ export function createSnapToResolutions(resolutions) {
|
||||
/**
|
||||
* @param {number} power Power.
|
||||
* @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.
|
||||
*/
|
||||
export function createSnapToPower(power, maxResolution, opt_maxLevel) {
|
||||
export function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent) {
|
||||
return (
|
||||
/**
|
||||
* @param {number|undefined} resolution Resolution.
|
||||
* @param {number} delta Delta.
|
||||
* @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, delta, direction) {
|
||||
function(resolution, direction, size, opt_isMoving) {
|
||||
if (resolution !== undefined) {
|
||||
const offset = -direction / 2 + 0.5;
|
||||
const oldLevel = Math.floor(
|
||||
Math.log(maxResolution / resolution) / Math.log(power) + offset);
|
||||
let newLevel = Math.max(oldLevel + delta, 0);
|
||||
if (opt_maxLevel !== undefined) {
|
||||
newLevel = Math.min(newLevel, opt_maxLevel);
|
||||
const cappedMaxRes = opt_maxExtent ?
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
|
||||
maxResolution;
|
||||
const minResolution = opt_minResolution !== undefined ? opt_minResolution : 0;
|
||||
|
||||
// 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 maxResolution / Math.pow(power, newLevel);
|
||||
return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
|
||||
}
|
||||
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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} delta Delta.
|
||||
* @return {number|undefined} Rotation.
|
||||
*/
|
||||
export function disable(rotation, delta) {
|
||||
export function disable(rotation) {
|
||||
if (rotation !== undefined) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -25,12 +24,11 @@ export function disable(rotation, delta) {
|
||||
|
||||
/**
|
||||
* @param {number|undefined} rotation Rotation.
|
||||
* @param {number} delta Delta.
|
||||
* @return {number|undefined} Rotation.
|
||||
*/
|
||||
export function none(rotation, delta) {
|
||||
export function none(rotation) {
|
||||
if (rotation !== undefined) {
|
||||
return rotation + delta;
|
||||
return rotation;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
@@ -46,12 +44,16 @@ export function createSnapToN(n) {
|
||||
return (
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
function(rotation, delta) {
|
||||
function(rotation, opt_isMoving) {
|
||||
if (opt_isMoving) {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
if (rotation !== undefined) {
|
||||
rotation = Math.floor((rotation + delta) / theta + 0.5) * theta;
|
||||
rotation = Math.floor(rotation / theta + 0.5) * theta;
|
||||
return rotation;
|
||||
} else {
|
||||
return undefined;
|
||||
@@ -69,15 +71,19 @@ export function createSnapToZero(opt_tolerance) {
|
||||
return (
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
function(rotation, delta) {
|
||||
function(rotation, opt_isMoving) {
|
||||
if (opt_isMoving) {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
if (rotation !== undefined) {
|
||||
if (Math.abs(rotation + delta) <= tolerance) {
|
||||
if (Math.abs(rotation) <= tolerance) {
|
||||
return 0;
|
||||
} else {
|
||||
return rotation + delta;
|
||||
return rotation;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
refresh() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user