Compare commits

..

1 Commits

Author SHA1 Message Date
ahocevar
f02f6a11ab Version and beta tag for v6.0.0-beta.1 2019-02-04 14:00:42 +01:00
139 changed files with 1776 additions and 2517 deletions

View File

@@ -41,13 +41,14 @@ 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)
## IntelliSense support and type checking for VS Code
## TypeScript and VS Code IntelliSense support
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:
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:
```js
{
"compilerOptions": {
"checkJs": true,
// Enable JavaScript support
"allowJs": true,
// Point to the JSDoc typed sources when using modules from the ol package
"baseUrl": "./",
"paths": {
@@ -56,14 +57,11 @@ The `ol` package contains a `src/` folder with JSDoc annotated sources. TypeScri
}
},
"include": [
"**/*.js",
// Include JavaScript files from the ol package
"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

View File

@@ -4,31 +4,11 @@
#### Backwards incompatible changes
##### 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
##### Removal of the deprecated "inherits" function
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.
@@ -80,10 +60,6 @@ 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.
@@ -117,58 +93,6 @@ 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,
@@ -194,19 +118,8 @@ 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.

View File

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

View File

@@ -21,7 +21,7 @@ Table of contents:
* [Why aren't there any features in my source?](#why-aren-t-there-any-features-in-my-source-)
* [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,30 +371,3 @@ 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()
```

View File

@@ -12,8 +12,9 @@ cloak:
<div id="map" class="map"></div>
<select id="layer-select">
<option value="Aerial">Aerial</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>
<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>
</select>

View File

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

View File

@@ -21,6 +21,8 @@ tags: "center, rotation, openstreetmap"
<div class="padding-bottom"></div>
<div class="center"></div>
</div>
<button id="zoomtoswitzerland">Zoom to Switzerland</button> (best fit).<br/>
<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="zoomtolausanne">Zoom to Lausanne</button> (with min resolution),<br/>
<button id="centerlausanne">Center on Lausanne</button>

View File

@@ -47,14 +47,29 @@ const map = new Map({
view: view
});
const zoomtoswitzerland =
document.getElementById('zoomtoswitzerland');
zoomtoswitzerland.addEventListener('click', function() {
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 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];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,11 +4,16 @@ 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
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, 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>

View File

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

View File

@@ -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: 'RoadOnDemand'})
source: new BingMaps({key: key, imagerySet: 'Road'})
});
const imagery = new TileLayer({

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,6 @@ 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;

View File

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

View File

@@ -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 view.
<code>constrainResolution: true</code> when constructing the interaction.
tags: "pinch, zoom, interaction"
---
<div id="map" class="map"></div>

View File

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

View File

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

View File

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

View File

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

View File

@@ -103,9 +103,6 @@ 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 />
@@ -215,9 +212,6 @@ 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 />
@@ -327,9 +321,6 @@ 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 />

View File

@@ -16,7 +16,6 @@ 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'),
@@ -35,7 +34,6 @@ 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'),
@@ -54,7 +52,6 @@ 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'),
@@ -87,7 +84,6 @@ 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;
@@ -102,7 +98,7 @@ const createTextStyle = function(feature, resolution, dom) {
document.getElementsByTagName('head')[0].appendChild(openSans);
openSansAdded = true;
}
const font = weight + ' ' + size + '/' + height + ' ' + dom.font.value;
const font = weight + ' ' + size + ' ' + dom.font.value;
const fillColor = dom.color.value;
const outlineColor = dom.outline.value;
const outlineWidth = parseInt(dom.outlineWidth.value, 10);

View File

@@ -1,6 +1,6 @@
{
"name": "ol",
"version": "6.0.0-beta.3",
"version": "6.0.0-beta.1",
"description": "OpenLayers mapping library",
"keywords": [
"map",
@@ -36,35 +36,35 @@
"url": "https://github.com/openlayers/openlayers/issues"
},
"dependencies": {
"pbf": "3.2.0",
"pbf": "3.1.0",
"pixelworks": "1.1.0",
"rbush": "2.0.2"
},
"devDependencies": {
"@openlayers/eslint-plugin": "^4.0.0-beta.1",
"@types/arcgis-rest-api": "^10.4.4",
"@types/geojson": "^7946.0.6",
"@types/arcgis-rest-api": "^10.4.3",
"@types/geojson": "^7946.0.5",
"@types/pbf": "^3.0.1",
"@types/rbush": "^2.0.2",
"@types/topojson-specification": "^1.0.1",
"@types/topojson-specification": "^1.0.0",
"buble": "^0.19.6",
"buble-loader": "^0.5.1",
"chaikin-smooth": "^1.0.4",
"clean-css-cli": "4.2.1",
"copy-webpack-plugin": "^5.0.0",
"coveralls": "3.0.3",
"eslint": "^5.13.0",
"copy-webpack-plugin": "^4.6.0",
"coveralls": "3.0.1",
"eslint": "^5.8.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": "^9.1.0",
"handlebars": "4.1.0",
"globby": "^8.0.1",
"handlebars": "4.0.11",
"istanbul": "0.4.5",
"jquery": "3.3.1",
"jsdoc": "3.5.5",
"jsdoc-plugin-typescript": "^1.0.7",
"jsdoc-plugin-typescript": "^1.0.6",
"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.1",
"mocha": "6.0.2",
"ol-mapbox-style": "^4.1.0",
"marked": "0.6.0",
"mocha": "5.2.0",
"ol-mapbox-style": "^3.6.3",
"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.2",
"terser-webpack-plugin": "^1.2.1",
"typescript": "^3.2.2",
"url-polyfill": "^1.1.3",
"walk": "^2.3.9",
"webpack": "4.29.6",
"webpack-cli": "^3.2.3",
"webpack-dev-middleware": "^3.5.2",
"webpack": "4.29.0",
"webpack-cli": "^3.2.0",
"webpack-dev-middleware": "^3.5.1",
"webpack-dev-server": "^3.1.14",
"yargs": "^13.2.0"
"yargs": "^12.0.2"
},
"eslintConfig": {
"extends": "openlayers",

View File

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

View File

@@ -4,6 +4,13 @@
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 {
/**
@@ -24,10 +31,10 @@ class VectorTile extends Tile {
this.consumers = 0;
/**
* Extent of this tile; set by the source.
* @private
* @type {import("./extent.js").Extent}
*/
this.extent = null;
this.extent_ = null;
/**
* @private
@@ -48,16 +55,11 @@ class VectorTile extends Tile {
this.loader_;
/**
* Feature projection of this tile; set by the source.
* Data projection
* @private
* @type {import("./proj/Projection.js").default}
*/
this.projection = null;
/**
* Resolution of this tile; set by the source.
* @type {number}
*/
this.resolution;
this.projection_ = null;
/**
* @private
@@ -81,6 +83,15 @@ 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.
@@ -91,7 +102,8 @@ class VectorTile extends Tile {
}
/**
* Get the features for this tile. Geometries will be in the view projection.
* Get the features for this tile. Geometries will be in the projection returned
* by {@link module:ol/VectorTile~VectorTile#getProjection}.
* @return {Array<import("./Feature.js").FeatureLike>} Features.
* @api
*/
@@ -106,6 +118,16 @@ 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
*/
@@ -113,7 +135,7 @@ class VectorTile extends Tile {
if (this.state == TileState.IDLE) {
this.setState(TileState.LOADING);
this.tileLoadFunction_(this, this.url_);
this.loader_(this.extent, this.resolution, this.projection);
this.loader_(null, NaN, null);
}
}
@@ -121,9 +143,12 @@ 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) {
onLoad(features, dataProjection, extent) {
this.setProjection(dataProjection);
this.setFeatures(features);
this.setExtent(extent);
}
/**
@@ -133,6 +158,22 @@ 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.
@@ -144,6 +185,17 @@ 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.

View File

@@ -21,9 +21,6 @@ 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';
/**
@@ -61,8 +58,9 @@ import {createMinMaxResolution} from './resolutionconstraint';
* @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} [nearest=false] If the view `constrainResolution` option is `true`,
* get the nearest extent instead of the closest that actually fits the view.
* @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 {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.
@@ -94,12 +92,7 @@ import {createMinMaxResolution} from './resolutionconstraint';
* used. The `constrainRotation` option has no effect if `enableRotation` is
* `false`.
* @property {import("./extent.js").Extent} [extent] The extent that constrains the
* 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`.
* center, in other words, center cannot be set outside this 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
@@ -120,12 +113,6 @@ import {createMinMaxResolution} from './resolutionconstraint';
* 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
@@ -139,9 +126,10 @@ import {createMinMaxResolution} from './resolutionconstraint';
* @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.
* @property {number} [zoomFactor=2] The zoom factor used to compute the
* corresponding resolution.
* 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.
*/
@@ -155,7 +143,7 @@ import {createMinMaxResolution} from './resolutionconstraint';
* 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 remain fixed
* @property {import("./coordinate.js").Coordinate} [anchor] Optional anchor to remained 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
@@ -196,12 +184,7 @@ const DEFAULT_MIN_ZOOM = 0;
* and `rotation`. Each state has a corresponding getter and setter, e.g.
* `getCenter` and `setCenter` for the `center` state.
*
* 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
* An 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).
@@ -209,19 +192,28 @@ const DEFAULT_MIN_ZOOM = 0;
* ### The constraints
*
* `setCenter`, `setResolution` and `setRotation` can be used to change the
* states of the view, but any constraint defined in the constructor will
* be applied along the way.
* 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.
*
* A View object can have a *resolution constraint*, a *rotation constraint*
* and a *center constraint*.
* But a View object also has a *resolution constraint*, a
* *rotation constraint* and a *center constraint*.
*
* 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.
* 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 *rotation constraint* snaps to specific angles. It is determined
* by the following options: `enableRotation` and `constrainRotation`.
@@ -229,31 +221,9 @@ const DEFAULT_MIN_ZOOM = 0;
* horizontal.
*
* The *center constraint* is determined by the `extent` option. By
* default the view center is not constrained at all.
* default the 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
* @api
*/
class View extends BaseObject {
@@ -292,24 +262,6 @@ 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);
}
@@ -323,6 +275,8 @@ class View extends BaseObject {
* @type {Object<string, *>}
*/
const properties = {};
properties[ViewProperty.CENTER] = options.center !== undefined ?
options.center : null;
const resolutionConstraintInfo = createResolutionConstraint(options);
@@ -370,20 +324,19 @@ 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) {
this.setResolution(options.resolution);
properties[ViewProperty.RESOLUTION] = 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
const resolution = this.getResolutionForZoom(options.zoom);
this.setResolution(clamp(resolution,
this.minResolution_, this.maxResolution_));
} else {
this.setZoom(options.zoom);
properties[ViewProperty.RESOLUTION] = clamp(
Number(this.getResolution() || properties[ViewProperty.RESOLUTION]),
this.minResolution_, this.maxResolution_);
}
}
properties[ViewProperty.ROTATION] = options.rotation !== undefined ? options.rotation : 0;
this.setProperties(properties);
/**
@@ -479,9 +432,9 @@ class View extends BaseObject {
return;
}
let start = Date.now();
let center = this.targetCenter_.slice();
let resolution = this.targetResolution_;
let rotation = this.targetRotation_;
let center = this.getCenter().slice();
let resolution = this.getResolution();
let rotation = this.getRotation();
const series = [];
for (let i = 0; i < animationCount; ++i) {
const options = /** @type {AnimationOptions} */ (arguments[i]);
@@ -497,13 +450,14 @@ class View extends BaseObject {
if (options.center) {
animation.sourceCenter = center;
animation.targetCenter = options.center.slice();
animation.targetCenter = options.center;
center = animation.targetCenter;
}
if (options.zoom !== undefined) {
animation.sourceResolution = resolution;
animation.targetResolution = this.getResolutionForZoom(options.zoom);
animation.targetResolution = this.constrainResolution(
this.maxResolution_, options.zoom - this.minZoom_, 0);
resolution = animation.targetResolution;
} else if (options.resolution) {
animation.sourceResolution = resolution;
@@ -602,31 +556,28 @@ class View extends BaseObject {
const y1 = animation.targetCenter[1];
const x = x0 + progress * (x1 - x0);
const y = y0 + progress * (y1 - y0);
this.targetCenter_ = [x, y];
this.set(ViewProperty.CENTER, [x, y]);
}
if (animation.sourceResolution && animation.targetResolution) {
const resolution = progress === 1 ?
animation.targetResolution :
animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);
if (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.CENTER,
this.calculateCenterZoom(resolution, animation.anchor));
}
this.targetResolution_ = resolution;
this.applyTargetState_(true);
this.set(ViewProperty.RESOLUTION, resolution);
}
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) {
const constrainedRotation = this.constraints_.rotation(rotation, true);
this.targetCenter_ = this.calculateCenterRotate(constrainedRotation, animation.anchor);
this.set(ViewProperty.CENTER,
this.calculateCenterRotate(rotation, animation.anchor));
}
this.targetRotation_ = rotation;
this.set(ViewProperty.ROTATION, rotation);
}
this.applyTargetState_(true);
more = true;
if (!animation.complete) {
break;
@@ -646,10 +597,6 @@ class View extends BaseObject {
if (more && this.updateAnimationKey_ === undefined) {
this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_);
}
if (!this.getAnimating()) {
setTimeout(this.resolveConstraints_.bind(this), 0);
}
}
/**
@@ -687,10 +634,9 @@ 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_(opt_rotation) {
getSizeFromViewport_() {
const size = [100, 100];
const selector = '.ol-viewport[data-view="' + getUid(this) + '"]';
const element = document.querySelector(selector);
@@ -699,15 +645,45 @@ 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.
@@ -817,15 +793,6 @@ 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.
@@ -947,9 +914,9 @@ class View extends BaseObject {
}
/**
* 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.
* 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).
* @return {number|undefined} Zoom.
* @api
*/
@@ -994,16 +961,8 @@ class View extends BaseObject {
* @api
*/
getResolutionForZoom(zoom) {
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_);
}
return /** @type {number} */ (this.constrainResolution(
this.maxResolution_, zoom - this.minZoom_, 0));
}
/**
@@ -1039,12 +998,15 @@ 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.getResolutionForZoom(options.maxZoom);
minResolution = this.constrainResolution(
this.maxResolution_, options.maxZoom - this.minZoom_, 0);
} else {
minResolution = 0;
}
@@ -1074,7 +1036,14 @@ class View extends BaseObject {
[size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);
resolution = isNaN(resolution) ? minResolution :
Math.max(resolution, minResolution);
resolution = this.getConstrainedResolution(resolution, nearest ? 0 : 1);
if (constrainResolution) {
let constrainedResolution = this.constrainResolution(resolution, 0, 0);
if (!nearest && constrainedResolution < resolution) {
constrainedResolution = this.constrainResolution(
constrainedResolution, -1, 0);
}
resolution = constrainedResolution;
}
// calculate center
sinAngle = -sinAngle; // go back to original rotation
@@ -1090,14 +1059,13 @@ class View extends BaseObject {
if (options.duration !== undefined) {
this.animate({
resolution: resolution,
center: this.getConstrainedCenter(center, resolution),
center: center,
duration: options.duration,
easing: options.easing
}, callback);
} else {
this.targetResolution_ = resolution;
this.targetCenter_ = center;
this.applyTargetState_(false, true);
this.setResolution(resolution);
this.setCenter(center);
animationCallback(callback, true);
}
}
@@ -1136,74 +1104,30 @@ class View extends BaseObject {
}
/**
* 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
*/
adjustCenter(deltaCoordinates) {
const center = this.targetCenter_;
this.setCenter([center[0] + deltaCoordinates[0], center[1] + deltaCoordinates[1]]);
}
/**
* 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.
* 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.
* @observable
* @api
*/
adjustRotation(delta, opt_anchor) {
const isMoving = this.getAnimating() || this.getInteracting();
const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving);
rotate(rotation, opt_anchor) {
if (opt_anchor !== undefined) {
this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor);
const center = this.calculateCenterRotate(rotation, opt_anchor);
this.setCenter(center);
}
this.targetRotation_ += delta;
this.applyTargetState_();
this.setRotation(rotation);
}
/**
* Set the center of the current view. Any extent constraint will apply.
* Set the center of the current view.
* @param {import("./coordinate.js").Coordinate|undefined} center The center of the view.
* @observable
* @api
*/
setCenter(center) {
this.targetCenter_ = center;
this.applyTargetState_();
this.set(ViewProperty.CENTER, center);
if (this.getAnimating()) {
this.cancelAnimations();
}
}
/**
@@ -1218,165 +1142,38 @@ class View extends BaseObject {
}
/**
* Set the resolution for this view. Any resolution constraint will apply.
* Set the resolution for this view.
* @param {number|undefined} resolution The resolution of the view.
* @observable
* @api
*/
setResolution(resolution) {
this.targetResolution_ = resolution;
this.applyTargetState_();
}
/**
* 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.targetRotation_ = rotation;
this.applyTargetState_();
}
/**
* 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.set(ViewProperty.RESOLUTION, resolution);
if (this.getAnimating()) {
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
* Set the rotation for this view.
* @param {number} rotation The rotation of the view in radians.
* @observable
* @api
*/
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
});
setRotation(rotation) {
this.set(ViewProperty.ROTATION, rotation);
if (this.getAnimating()) {
this.cancelAnimations();
}
}
/**
* Notify the View that an interaction has started.
* Zoom to a specific zoom level.
* @param {number} zoom Zoom level.
* @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);
setZoom(zoom) {
this.setResolution(this.getResolutionForZoom(zoom));
}
}
@@ -1398,8 +1195,7 @@ function animationCallback(callback, returnValue) {
*/
export function createCenterConstraint(options) {
if (options.extent !== undefined) {
return createExtent(options.extent, options.constrainOnlyCenter,
options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true);
return createExtent(options.extent);
} else {
return centerNone;
}
@@ -1430,22 +1226,13 @@ 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];
if (options.constrainResolution) {
resolutionConstraint = createSnapToResolutions(resolutions, smooth,
!options.constrainOnlyCenter && options.extent);
} else {
resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth,
!options.constrainOnlyCenter && options.extent);
}
resolutionConstraint = createSnapToResolutions(
resolutions);
} else {
// calculate the default min and max resolution
const projection = createProjection(options.projection, 'EPSG:3857');
@@ -1489,14 +1276,8 @@ 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, minResolution, smooth,
!options.constrainOnlyCenter && options.extent);
} else {
resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth,
!options.constrainOnlyCenter && options.extent);
}
resolutionConstraint = createSnapToPower(
zoomFactor, maxResolution, maxZoom - minZoom);
}
return {constraint: resolutionConstraint, maxResolution: maxResolution,
minResolution: minResolution, minZoom: minZoom, zoomFactor: zoomFactor};

View File

@@ -5,57 +5,26 @@ import {clamp} from './math.js';
/**
* @typedef {function((import("./coordinate.js").Coordinate|undefined), number, import("./size.js").Size, boolean=): (import("./coordinate.js").Coordinate|undefined)} Type
* @typedef {function((import("./coordinate.js").Coordinate|undefined)): (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, onlyCenter, smooth) {
export function createExtent(extent) {
return (
/**
* @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.
* @param {import("./coordinate.js").Coordinate=} center Center.
* @return {import("./coordinate.js").Coordinate|undefined} Center.
*/
function(center, resolution, size, opt_isMoving) {
function(center) {
if (center) {
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];
return [
clamp(center[0], extent[0], extent[2]),
clamp(center[1], extent[1], extent[3])
];
} else {
return undefined;
}

View File

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

View File

@@ -169,6 +169,8 @@ 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;

View File

@@ -201,12 +201,20 @@ 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];
nominalCount *= metersPerDegree;
if (projection.getUnits() == ProjUnits.DEGREES) {
nominalCount *= metersPerDegree;
} else {
pointResolution /= metersPerDegree;
}
if (nominalCount < metersPerDegree / 60) {
suffix = '\u2033'; // seconds
pointResolution *= 3600;

View File

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

View File

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

View File

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

View File

@@ -230,33 +230,6 @@ 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

View File

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

View File

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

View File

@@ -111,6 +111,14 @@ 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.

View File

@@ -19,7 +19,6 @@ 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';
/**
@@ -84,6 +83,12 @@ class MVT extends FeatureFormat {
*/
this.layers_ = options.layers ? options.layers : null;
/**
* @private
* @type {import("../extent.js").Extent}
*/
this.extent_ = null;
}
/**
@@ -154,10 +159,10 @@ class MVT extends FeatureFormat {
* @private
* @param {PBF} pbf PBF
* @param {Object} rawFeature Raw Mapbox feature.
* @param {import("./Feature.js").ReadOptions} options Read options.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options.
* @return {import("../Feature.js").FeatureLike} Feature.
*/
createFeature_(pbf, rawFeature, options) {
createFeature_(pbf, rawFeature, opt_options) {
const type = rawFeature.type;
if (type === 0) {
return null;
@@ -176,7 +181,6 @@ 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) {
@@ -209,7 +213,7 @@ class MVT extends FeatureFormat {
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
}
const geometry = transformGeometryWithOptions(geom, false, options);
const geometry = transformGeometryWithOptions(geom, false, this.adaptOptions(opt_options));
feature.setGeometry(geometry);
feature.setId(id);
feature.setProperties(values, true);
@@ -218,6 +222,14 @@ class MVT extends FeatureFormat {
return feature;
}
/**
* @inheritDoc
* @api
*/
getLastExtent() {
return this.extent_;
}
/**
* @inheritDoc
*/
@@ -226,22 +238,15 @@ class MVT extends FeatureFormat {
}
/**
* Read all features.
*
* @param {ArrayBuffer} source Source.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options.
* @return {Array<import("../Feature.js").FeatureLike>} Features.
* @inheritDoc
* @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) {
@@ -249,13 +254,11 @@ 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, options));
features.push(this.createFeature_(pbf, rawFeature));
}
this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
}
return features;

View File

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

View File

@@ -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 {linearRingssAreOriented, orientLinearRingsArray} from './flat/orient.js';
import {linearRingsAreOriented, 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 (linearRingssAreOriented(
if (linearRingsAreOriented(
flatCoordinates, 0, this.endss_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates;
} else {

View File

@@ -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 {linearRingsAreOriented, orientLinearRings} from './flat/orient.js';
import {linearRingIsOriented, 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 (linearRingsAreOriented(
if (linearRingIsOriented(
flatCoordinates, 0, this.ends_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates;
} else {

View File

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

View File

@@ -11,19 +11,21 @@
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @param {function(import("../../coordinate.js").Coordinate, import("../../coordinate.js").Coordinate): T} callback Function
* @param {function(this: S, 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
* @template T,S
*/
export function forEach(flatCoordinates, offset, end, stride, callback) {
export function forEach(flatCoordinates, offset, end, stride, callback, opt_this) {
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(point1, point2);
ret = callback.call(opt_this, point1, point2);
if (ret) {
return ret;
}

View File

@@ -44,6 +44,8 @@ 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.
@@ -125,6 +127,7 @@ 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
}));
}
@@ -143,6 +146,7 @@ export function defaults(opt_options) {
if (mouseWheelZoom) {
interactions.push(new MouseWheelZoom({
condition: options.onFocusOnly ? focus : undefined,
constrainResolution: options.constrainResolution,
duration: options.zoomDuration
}));
}

View File

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

View File

@@ -2,8 +2,10 @@
* @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';
@@ -78,7 +80,8 @@ 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_;
view.adjustRotation(-delta);
const rotation = view.getRotation();
rotateWithoutConstraints(view, rotation - delta);
}
this.lastAngle_ = theta;
}
@@ -94,7 +97,9 @@ class DragRotate extends PointerInteraction {
const map = mapBrowserEvent.map;
const view = map.getView();
view.endInteraction(this.duration_);
view.setHint(ViewHint.INTERACTING, -1);
const rotation = view.getRotation();
rotate(view, rotation, undefined, this.duration_);
return false;
}
@@ -109,7 +114,7 @@ class DragRotate extends PointerInteraction {
if (mouseActionButton(mapBrowserEvent) && this.condition_(mapBrowserEvent)) {
const map = mapBrowserEvent.map;
map.getView().beginInteraction();
map.getView().setHint(ViewHint.INTERACTING, 1);
this.lastAngle_ = undefined;
return true;
} else {

View File

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

View File

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

View File

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

View File

@@ -242,7 +242,7 @@ class ExtentInteraction extends PointerInteraction {
extentFeature = new Feature(polygonFromExtent(extent));
}
this.extentFeature_ = extentFeature;
this.extentOverlay_.getSource().addFeature(extentFeature);
/** @type {VectorSource} */ (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;
this.vertexOverlay_.getSource().addFeature(vertexFeature);
/** @type {VectorSource} */ (this.vertexOverlay_.getSource()).addFeature(vertexFeature);
} else {
const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
geometry.setCoordinates(vertex);

View File

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

View File

@@ -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) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (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) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (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;
this.overlay_.getSource().addFeature(vertexFeature);
/** @type {VectorSource} */ (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_) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (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_) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
dragSegments.length = 0;

View File

@@ -1,7 +1,9 @@
/**
* @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';
@@ -32,6 +34,9 @@ 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.
@@ -57,13 +62,7 @@ class MouseWheelZoom extends Interaction {
* @private
* @type {number}
*/
this.totalDelta_ = 0;
/**
* @private
* @type {number}
*/
this.lastDelta_ = 0;
this.delta_ = 0;
/**
* @private
@@ -83,6 +82,12 @@ 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}
@@ -132,15 +137,22 @@ 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
*/
endInteraction_() {
decrementInteractingHint_() {
this.trackpadTimeoutId_ = undefined;
const view = this.getMap().getView();
view.endInteraction(undefined, Math.sign(this.lastDelta_), this.lastAnchor_);
view.setHint(ViewHint.INTERACTING, -1);
}
/**
@@ -187,8 +199,6 @@ class MouseWheelZoom extends Interaction {
if (delta === 0) {
return false;
} else {
this.lastDelta_ = delta;
}
const now = Date.now();
@@ -208,15 +218,55 @@ class MouseWheelZoom extends Interaction {
if (this.trackpadTimeoutId_) {
clearTimeout(this.trackpadTimeoutId_);
} else {
view.beginInteraction();
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
});
}
this.trackpadTimeoutId_ = setTimeout(this.endInteraction_.bind(this), this.trackpadEventGap_);
view.adjustZoom(-delta / this.trackpadDeltaPerZoom_, this.lastAnchor_);
this.startTime_ = now;
return false;
}
this.totalDelta_ += delta;
this.delta_ += delta;
const timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0);
@@ -236,10 +286,10 @@ class MouseWheelZoom extends Interaction {
view.cancelAnimations();
}
const maxDelta = MAX_DELTA;
const delta = clamp(this.totalDelta_, -maxDelta, maxDelta);
const delta = clamp(this.delta_, -maxDelta, maxDelta);
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
this.mode_ = undefined;
this.totalDelta_ = 0;
this.delta_ = 0;
this.lastAnchor_ = null;
this.startTime_ = undefined;
this.timeoutId_ = undefined;

View File

@@ -1,7 +1,9 @@
/**
* @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';
@@ -116,8 +118,9 @@ class PinchRotate extends PointerInteraction {
// rotate
if (this.rotating_) {
const rotation = view.getRotation();
map.render();
view.adjustRotation(rotationDelta, this.anchor_);
rotateWithoutConstraints(view, rotation + rotationDelta, this.anchor_);
}
}
@@ -128,7 +131,11 @@ class PinchRotate extends PointerInteraction {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
view.endInteraction(this.duration_);
view.setHint(ViewHint.INTERACTING, -1);
if (this.rotating_) {
const rotation = view.getRotation();
rotate(view, rotation, this.anchor_, this.duration_);
}
return false;
} else {
return true;
@@ -146,7 +153,7 @@ class PinchRotate extends PointerInteraction {
this.rotating_ = false;
this.rotationDelta_ = 0.0;
if (!this.handlingDownUpSequence) {
map.getView().beginInteraction();
map.getView().setHint(ViewHint.INTERACTING, 1);
}
return true;
} else {

View File

@@ -1,13 +1,17 @@
/**
* @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.
*/
@@ -33,6 +37,12 @@ class PinchZoom extends PointerInteraction {
super(pointerOptions);
/**
* @private
* @type {boolean}
*/
this.constrainResolution_ = options.constrainResolution || false;
/**
* @private
* @type {import("../coordinate.js").Coordinate}
@@ -81,6 +91,17 @@ 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;
@@ -95,7 +116,7 @@ class PinchZoom extends PointerInteraction {
// scale, bypass the resolution constraint
map.render();
view.adjustResolution(scaleDelta, this.anchor_);
zoomWithoutConstraints(view, newResolution, this.anchor_);
}
/**
@@ -105,8 +126,17 @@ class PinchZoom extends PointerInteraction {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
view.endInteraction(this.duration_, direction);
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);
}
return false;
} else {
return true;
@@ -123,7 +153,7 @@ class PinchZoom extends PointerInteraction {
this.lastDistance_ = undefined;
this.lastScaleDelta_ = 1;
if (!this.handlingDownUpSequence) {
map.getView().beginInteraction();
map.getView().setHint(ViewHint.INTERACTING, 1);
}
return true;
} else {

View File

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

View File

@@ -34,7 +34,6 @@ 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 {
@@ -49,4 +48,12 @@ 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;

View File

@@ -39,7 +39,6 @@ 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 {
@@ -103,4 +102,13 @@ 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;

View File

@@ -61,8 +61,6 @@ 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 {
@@ -133,6 +131,13 @@ class BaseVectorLayer extends Layer {
return this.declutter_;
}
/**
* @param {boolean} declutter Declutter.
*/
setDeclutter(declutter) {
this.declutter_ = declutter;
}
/**
* @return {number|undefined} Render buffer.
*/
@@ -214,4 +219,13 @@ 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;

View File

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

View File

@@ -62,8 +62,6 @@ 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 {
/**
@@ -108,7 +106,7 @@ class Layer extends BaseLayer {
getChangeEventType(LayerProperty.SOURCE),
this.handleSourcePropertyChange_, this);
const source = options.source ? /** @type {SourceType} */ (options.source) : null;
const source = options.source ? options.source : null;
this.setSource(source);
}
@@ -132,12 +130,15 @@ class Layer extends BaseLayer {
/**
* Get the layer source.
* @return {SourceType} The layer source (or `null` if not yet set).
* @return {import("../source/Source.js").default} The layer source (or `null` if not yet set).
* @observable
* @api
*/
getSource() {
return /** @type {SourceType} */ (this.get(LayerProperty.SOURCE)) || null;
const source = this.get(LayerProperty.SOURCE);
return (
/** @type {import("../source/Source.js").default} */ (source) || null
);
}
/**
@@ -226,7 +227,7 @@ class Layer extends BaseLayer {
/**
* Set the layer source.
* @param {SourceType} source The layer source.
* @param {import("../source/Source.js").default} source The layer source.
* @observable
* @api
*/

View File

@@ -17,7 +17,6 @@ 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 {

View File

@@ -6,41 +6,7 @@ import {assign} from '../obj.js';
import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.js';
/**
* @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`.
* @typedef {import("./BaseVector.js").Options} Options
* @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.
*/

View File

@@ -33,13 +33,12 @@ 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. When `declutter`
* is set to `true`, the decluttering is done per tile resulting in labels and point symbols getting
* cut off at tile boundaries.
* are always rotated with the view and pixels are scaled during zoom animations.
* * `'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
@@ -47,7 +46,8 @@ 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.
* means higher priority. When set to `true`, a `renderMode` of `'image'` will be overridden with
* `'hybrid'`.
* @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,7 +70,6 @@ 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 {
@@ -86,12 +85,16 @@ class VectorTileLayer extends BaseVectorLayer {
super(/** @type {import("./Vector.js").Options} */ (baseOptions));
const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
let 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}
@@ -162,4 +165,11 @@ 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;

View File

@@ -188,12 +188,6 @@ 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) {

View File

@@ -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 in projection units at the passed coordinate. If this is `undefined`,
* the `{number}` resolution at the passed coordinate. If this is `undefined`,
* the default {@link module:ol/proj#getPointResolution} function will be used.
*/

View File

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

View File

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

View File

@@ -288,22 +288,22 @@ function getMeasureContext() {
* @return {import("../size.js").Size} Measurement.
*/
export const measureTextHeight = (function() {
let div;
let span;
const heights = textHeights;
return function(font) {
let height = heights[font];
if (height == undefined) {
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';
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';
}
div.style.font = font;
document.body.appendChild(div);
height = heights[font] = div.offsetHeight;
document.body.removeChild(div);
span.style.font = font;
document.body.appendChild(span);
height = heights[font] = span.offsetHeight;
document.body.removeChild(span);
}
return height;
};

View File

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

View File

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

View File

@@ -113,7 +113,7 @@ class CanvasImageBuilder extends CanvasBuilder {
if (!this.image_) {
return;
}
this.beginGeometry(feature);
this.beginGeometry(pointGeometry, 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(feature);
this.endGeometry(pointGeometry, feature);
}
/**
@@ -142,7 +142,7 @@ class CanvasImageBuilder extends CanvasBuilder {
if (!this.image_) {
return;
}
this.beginGeometry(feature);
this.beginGeometry(multiPointGeometry, 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(feature);
this.endGeometry(multiPointGeometry, feature);
}
/**

View File

@@ -44,7 +44,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(feature);
this.beginGeometry(lineStringGeometry, 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(feature);
this.endGeometry(lineStringGeometry, feature);
}
/**
@@ -68,7 +68,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(feature);
this.beginGeometry(multiLineStringGeometry, 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(feature);
this.endGeometry(multiLineStringGeometry, feature);
}
/**

View File

@@ -72,8 +72,8 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(feature);
this.setFillStrokeStyles_(circleGeometry);
this.beginGeometry(circleGeometry, 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(feature);
this.endGeometry(circleGeometry, feature);
}
/**
@@ -116,8 +116,8 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(feature);
this.setFillStrokeStyles_(polygonGeometry);
this.beginGeometry(polygonGeometry, 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(feature);
this.endGeometry(polygonGeometry, feature);
}
/**
@@ -148,8 +148,8 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(feature);
this.setFillStrokeStyles_(multiPolygonGeometry);
this.beginGeometry(multiPolygonGeometry, 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(feature);
this.endGeometry(multiPolygonGeometry, feature);
}
/**
@@ -195,12 +195,13 @@ class CanvasPolygonBuilder extends CanvasBuilder {
/**
* @private
* @param {import("../../geom/Geometry.js").default|import("../Feature.js").default} geometry Geometry.
*/
setFillStrokeStyles_() {
setFillStrokeStyles_(geometry) {
const state = this.state;
const fillStyle = state.fillStyle;
if (fillStyle !== undefined) {
this.updateFillStyle(state, this.createFill);
this.updateFillStyle(state, this.createFill, geometry);
}
if (state.strokeStyle !== undefined) {
this.updateStrokeStyle(state, this.applyStroke);

View File

@@ -184,7 +184,7 @@ class CanvasTextBuilder extends CanvasBuilder {
ends.push(endss[i][0]);
}
}
this.beginGeometry(feature);
this.beginGeometry(geometry, 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(feature);
this.endGeometry(geometry, 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);
this.hitDetectionInstructions.push(this.createFill(this.state));
this.updateFillStyle(this.state, this.createFill, geometry);
this.hitDetectionInstructions.push(this.createFill(this.state, geometry));
}
if (textState.backgroundStroke) {
this.updateStrokeStyle(this.state, this.applyStroke);
@@ -267,7 +267,7 @@ class CanvasTextBuilder extends CanvasBuilder {
}
}
this.beginGeometry(feature);
this.beginGeometry(geometry, 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(feature);
this.endGeometry(geometry, feature);
}
}

View File

@@ -221,8 +221,10 @@ 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(import("../Tile.js").default)=} opt_tileCallback Tile callback.
* @param {function(this: T, import("../Tile.js").default)=} opt_tileCallback Tile callback.
* @param {T=} opt_this Object to use as `this` in `opt_tileCallback`.
* @protected
* @template T
*/
manageTilePyramid(
frameState,
@@ -233,7 +235,8 @@ class LayerRenderer extends Observable {
extent,
currentZ,
preload,
opt_tileCallback
opt_tileCallback,
opt_this
) {
const tileSourceKey = getUid(tileSource);
if (!(tileSourceKey in frameState.wantedTiles)) {
@@ -258,7 +261,7 @@ class LayerRenderer extends Observable {
}
}
if (opt_tileCallback !== undefined) {
opt_tileCallback(tile);
opt_tileCallback.call(opt_this, tile);
}
} else {
tileSource.useTile(z, x, y, projection);

View File

@@ -305,4 +305,12 @@ 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;

View File

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

View File

@@ -63,14 +63,6 @@ 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.

View File

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

View File

@@ -8,9 +8,6 @@ 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
@@ -85,19 +82,17 @@ 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();
}
});
listen(image, EventType.CHANGE, function() {
if (image.getState() === ImageState.LOADED) {
this.image_ = image;
this.skippedFeatures_ = skippedFeatures;
}
}, this);
image.load();
if (this.loadImage(image)) {
this.image_ = image;
this.skippedFeatures_ = skippedFeatures;
}
}
if (this.image_) {

View File

@@ -129,7 +129,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
const projection = viewState.projection;
const rotation = viewState.rotation;
const projectionExtent = projection.getExtent();
const vectorSource = this.getLayer().getSource();
const vectorSource = /** @type {import("../../source/Vector.js").default} */ (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 = this.getLayer();
const layer = /** @type {import("../../layer/Vector.js").default} */ (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 = vectorLayer.getSource();
const vectorSource = /** @type {import("../../source/Vector.js").default} */ (vectorLayer.getSource());
const animating = frameState.viewHints[ViewHint.ANIMATING];
const interacting = frameState.viewHints[ViewHint.INTERACTING];

View File

@@ -10,6 +10,8 @@ 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';
@@ -150,7 +152,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
disposeInternal() {
unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this);
this.overlayContext_.canvas.width = this.overlayContext_.canvas.height = 0;
super.disposeInternal();
}
@@ -204,14 +205,16 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc
*/
getTileImage(tile) {
return tile.getImage(this.getLayer());
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
return tile.getImage(tileLayer);
}
/**
* @inheritDoc
*/
prepareFrame(frameState, layerState) {
const layerRevision = this.getLayer().getRevision();
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const layerRevision = layer.getRevision();
if (this.renderedLayerRevision_ != layerRevision) {
this.renderedTiles.length = 0;
}
@@ -226,9 +229,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @private
*/
updateExecutorGroup_(tile, pixelRatio, projection) {
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
const revision = layer.getRevision();
const renderOrder = layer.getRenderOrder() || null;
const renderOrder = /** @type {import("../../render.js").OrderFunction} */ (layer.getRenderOrder()) || null;
const builderState = tile.getReplayState(layer);
if (!builderState.dirty && builderState.renderedRevision == revision &&
@@ -236,7 +239,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
return;
}
const source = layer.getSource();
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource());
const sourceTileGrid = source.getTileGrid();
const tileGrid = source.getTileGridForProjection(projection);
const zoom = tile.tileCoord[0];
@@ -262,6 +265,12 @@ 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_);
@@ -290,6 +299,15 @@ 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);
}
@@ -316,24 +334,21 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const rotation = frameState.viewState.rotation;
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
const layer = this.getLayer();
const source = layer.getSource();
const source = /** @type {import("../../source/VectorTile").default} */ (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 found;
let bufferedExtent, 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);
if (!containsCoordinate(tileExtent, coordinate)) {
continue;
}
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
bufferedExtent = buffer(tileExtent, hitTolerance * resolution, bufferedExtent);
if (!containsCoordinate(bufferedExtent, coordinate)) {
continue;
}
const executorGroups = tile.executorGroups[getUid(layer)];
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
@@ -344,10 +359,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @return {?} Callback result.
*/
function(feature) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
const key = getUid(feature);
if (!(key in features)) {
features[key] = true;
return callback.call(thisArg, feature, layer);
@@ -366,7 +378,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
getReplayTransform_(tile, frameState) {
const layer = this.getLayer();
const source = layer.getSource();
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource());
const tileGrid = source.getTileGrid();
const tileCoord = tile.tileCoord;
const tileResolution = tileGrid.getResolution(tileCoord[0]);
@@ -423,7 +435,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const context = this.overlayContext_;
const declutterReplays = layer.getDeclutter() ? {} : null;
const source = layer.getSource();
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource());
const replayTypes = VECTOR_REPLAYS[renderMode];
const pixelRatio = frameState.pixelRatio;
const rotation = frameState.viewState.rotation;
@@ -535,10 +547,6 @@ 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_);
@@ -600,7 +608,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
replayState.renderedTileZ = tile.sourceZ;
const tileCoord = tile.wrappedTileCoord;
const z = tileCoord[0];
const source = layer.getSource();
const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource());
const tileGrid = source.getTileGridForProjection(projection);
const resolution = tileGrid.getResolution(z);
const context = tile.getContext(layer);

View File

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

View File

@@ -2,87 +2,37 @@
* @module ol/resolutionconstraint
*/
import {linearFindNearest} from './array.js';
import {getHeight, getWidth} from './extent';
import {clamp} from './math';
import {clamp} from './math.js';
/**
* @typedef {function((number|undefined), number, import("./size.js").Size, boolean=): (number|undefined)} Type
* @typedef {function((number|undefined), number, number): (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, opt_smooth, opt_maxExtent) {
export function createSnapToResolutions(resolutions) {
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, direction, size, opt_isMoving) {
function(resolution, delta, direction) {
if (resolution !== undefined) {
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);
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 capped = Math.min(cappedMaxRes, resolution);
const z = Math.floor(linearFindNearest(resolutions, capped, direction));
return resolutions[z];
} else {
return undefined;
}
@@ -94,78 +44,29 @@ export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent)
/**
* @param {number} power Power.
* @param {number} maxResolution Maximum resolution.
* @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.
* @param {number=} opt_maxLevel Maximum level.
* @return {Type} Zoom function.
*/
export function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent) {
export function createSnapToPower(power, maxResolution, opt_maxLevel) {
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, direction, size, opt_isMoving) {
function(resolution, delta, direction) {
if (resolution !== undefined) {
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 getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
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 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);
return maxResolution / Math.pow(power, newLevel);
} 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;
}
}
);
}

View File

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

View File

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

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