Compare commits

..

1 Commits

Author SHA1 Message Date
ahocevar
fbe815853d Publish with beta tag 2019-05-30 15:58:19 +02:00
172 changed files with 1640 additions and 15038 deletions

View File

@@ -4,19 +4,9 @@
#### Backwards incompatible changes
#### Removal of `TOUCH` constant from `ol/has`
If you were previously using this constant, you can check if `'ontouchstart'` is defined in `window` instead.
```js
if ('ontouchstart' in window) {
// ...
}
```
#### Removal of `GEOLOCATION` constant from `ol/has`
If you were previously using this constant, you can check if `'geolocation'` is defined in `navigator` instead.
If you were previously using this constant, you can check if `'geolocation'` is define in `navigator` instead.
```js
if ('geolocation' in navigator) {

View File

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

View File

@@ -214,39 +214,55 @@ function buildNav(members) {
}
return 0;
});
function createEntry(type, v) {
return {
type: type,
longname: v.longname,
name: v.name,
classes: find({
kind: 'class',
memberof: v.longname
}).map(createEntry.bind(this, 'class')),
members: find({
kind: 'member',
memberof: v.longname
}),
methods: find({
kind: 'function',
memberof: v.longname
}),
typedefs: find({
kind: 'typedef',
memberof: v.longname
}),
events: find({
kind: 'event',
memberof: v.longname
})
};
}
_.each(merged, function(v) {
// exclude interfaces from sidebar
if (v.interface !== true) {
if (v.kind == 'module') {
nav.push(createEntry('module', v));
nav.push({
type: 'module',
longname: v.longname,
name: v.name,
members: find({
kind: 'member',
memberof: v.longname
}),
methods: find({
kind: 'function',
memberof: v.longname
}),
typedefs: find({
kind: 'typedef',
memberof: v.longname
}),
events: find({
kind: 'event',
memberof: v.longname
})
});
}
if (v.kind == 'class') {
nav.push({
type: 'class',
longname: v.longname,
name: v.name,
members: find({
kind: 'member',
memberof: v.longname
}),
methods: find({
kind: 'function',
memberof: v.longname
}),
typedefs: find({
kind: 'typedef',
memberof: v.longname
}),
fires: v.fires,
events: find({
kind: 'event',
memberof: v.longname
})
});
}
}
});

View File

@@ -12,18 +12,13 @@ $(function () {
var $item = $(v);
if ($item.data('name') && regexp.test($item.data('name'))) {
const container = $item.parent().parent().parent();
container.show();
container.closest('.itemMembers').show();
container.closest('.item').show();
$item.show();
$item.closest('.itemMembers').show();
$item.closest('.item').show();
}
});
} else {
$el.find('.item, .itemMembers').hide();
$('.navigation>ul>li').show();
$el.find('.item, .itemMembers').show();
}
$el.find('.list').scrollTop(0);

View File

@@ -18,7 +18,7 @@
<sup class="variation"><?js= doc.variation ?></sup>
<?js } ?></h2>
<br>
<?js if (doc.stability || doc.kind == 'namespace' || doc.kind == 'module') {
<?js if (doc.stability || doc.kind == 'namespace') {
var ancestors = doc.ancestors.map(a => a.replace(/>\./g, '>').replace(/\.</g, '<')).join('/');
var parts = [];
if (ancestors) {
@@ -26,20 +26,7 @@
}
var importPath = parts.join('/');
?>
<?js
var nameParts = doc.name.split('/');
var moduleName = nameParts[nameParts.length - 1];
if(moduleName) {
var firstChar = moduleName.charAt(0);
moduleName = firstChar.toUpperCase() + moduleName.slice(1);
var isClassModule = firstChar.toUpperCase() === firstChar;
}
?>
<?js if (doc.kind == 'module' && !isClassModule && nameParts.length < 3) {?>
<pre class="prettyprint source"><code>import * as ol<?js= moduleName ?> from '<?js= doc.name ?>';</code></pre>
<?js } else if(doc.kind !== 'module') { ?>
<pre class="prettyprint source"><code>import <?js= doc.name ?> from '<?js= importPath ?>';</code></pre>
<?js } ?>
<pre class="prettyprint source"><code>import <?js= doc.name ?> from '<?js= importPath ?>';</code></pre>
<?js } ?>
<?js if (doc.classdesc) { ?>
<div class="class-description"><?js= doc.classdesc ?></div>
@@ -156,7 +143,6 @@
<h3 class="subsection-title">Methods</h3>
<dl><?js methods.forEach(function(m) { ?>
<?js m.parent = doc ?>
<?js= self.partial('method.tmpl', m) ?>
<?js }); ?></dl>
<?js } ?>

View File

@@ -27,10 +27,6 @@ var self = this;
</dt>
<dd class="<?js= (data.stability && data.stability !== 'stable') ? 'unstable' : '' ?>">
<?js if (data.parent && data.parent.kind == 'module' && data.parent.name.split('ol/').length < 3) { ?>
<pre class="prettyprint source"><code>import {<?js= data.name ?>} from '<?js= data.parent.name ?>';</code></pre>
<?js } ?>
<?js if (data.description) { ?>
<div class="description">
<?js= data.description ?>

View File

@@ -10,12 +10,11 @@ function toShortName(name) {
</div>
<ul class="list">
<?js
let navbuilder;
this.nav.forEach(navbuilder = function (item) {
this.nav.forEach(function (item) {
?>
<li class="item" data-name="<?js= item.longname ?>">
<span class="title">
<?js= self.linkto(item.longname, item.type === 'module' ? item.longname.replace('module:', '') : item.name) ?>
<?js= self.linkto(item.longname, item.longname.replace('module:', '')) ?>
<?js if (item.type === 'namespace' &&
(item.members.length + item.typedefs.length + item.methods.length +
item.events.length > 0)) { ?>
@@ -23,18 +22,6 @@ function toShortName(name) {
</span>
<ul class="members itemMembers">
<?js
if (item.classes.length) {
?>
<span class="subtitle">Classes</span>
<?js
item.classes.forEach(function (v) {
navbuilder(v);
});
}
?>
</ul>
<ul class="members itemMembers">
<?js
if (item.members.length) {
?>
<span class="subtitle">Members</span>

View File

@@ -228,11 +228,3 @@ Missing or invalid `size`.
### 61
Cannot determine IIIF Image API version from provided image information JSON.
### 62
A `WebGLArrayBuffer` must either be of type `ELEMENT_ARRAY_BUFFER` or `ARRAY_BUFFER`.
### 63
Support for the `OES_element_index_uint` WebGL extension is mandatory for WebGL layers.

View File

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

View File

@@ -129,7 +129,7 @@ const map = new Map({
layers: [
new TileLayer({
source: new TileJSON({
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json'
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure'
})
}),
new VectorLayer({

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ import VectorTileSource from '../src/ol/source/VectorTile.js';
import {Tile as TileLayer, VectorTile as VectorTileLayer} from '../src/ol/layer.js';
import Projection from '../src/ol/proj/Projection.js';
// Converts geojson-vt data to GeoJSON
const replacer = function(key, value) {
if (value.geometry) {
let type;
@@ -46,6 +46,11 @@ const replacer = function(key, value) {
}
};
const tilePixels = new Projection({
code: 'TILE_PIXELS',
units: 'tile-pixels'
});
const map = new Map({
layers: [
new TileLayer({
@@ -68,22 +73,23 @@ fetch(url).then(function(response) {
debug: 1
});
const vectorSource = new VectorTileSource({
format: new GeoJSON({
// Data returned from geojson-vt is in tile pixel units
dataProjection: new Projection({
code: 'TILE_PIXELS',
units: 'tile-pixels',
extent: [0, 0, 4096, 4096]
})
}),
tileUrlFunction: function(tileCoord) {
format: new GeoJSON(),
tileLoadFunction: function(tile) {
const format = tile.getFormat();
const tileCoord = tile.getTileCoord();
const data = tileIndex.getTile(tileCoord[0], tileCoord[1], tileCoord[2]);
const geojson = JSON.stringify({
type: 'FeatureCollection',
features: data ? data.features : []
}, replacer);
return 'data:application/json;charset=UTF-8,' + geojson;
}
const features = format.readFeatures(
JSON.stringify({
type: 'FeatureCollection',
features: data ? data.features : []
}, replacer));
tile.setLoader(function() {
tile.setFeatures(features);
tile.setProjection(tilePixels);
});
},
url: 'data:' // arbitrary url, we don't use it in the tileLoadFunction
});
const vectorLayer = new VectorTileLayer({
source: vectorSource

View File

@@ -56,7 +56,7 @@ const vectorLayer = new VectorLayer({
const rasterLayer = new TileLayer({
source: new TileJSON({
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json',
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
crossOrigin: ''
})
});

View File

@@ -13,9 +13,5 @@ docs: >
The dataset contains around 80k points and can be found here: https://www.kaggle.com/NUFORC/ufo-sightings
tags: "webgl, icon, sprite, point, ufo"
cloak:
- key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg
value: Your Mapbox access token from https://mapbox.com/ here
---
<div id="map" class="map"></div>
<div>Current sighting: <span id="info"></span></div>

View File

@@ -10,8 +10,6 @@ import {fromLonLat} from '../src/ol/proj.js';
import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer.js';
import {lerp} from '../src/ol/math.js';
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
const vectorSource = new Vector({
features: [],
attributions: 'National UFO Reporting Center'
@@ -103,11 +101,11 @@ function loadData() {
loadData();
const map = new Map({
new Map({
layers: [
new TileLayer({
source: new TileJSON({
url: 'https://api.tiles.mapbox.com/v4/mapbox.world-dark.json?access_token=' + key,
url: 'https://api.tiles.mapbox.com/v3/mapbox.world-dark.json?secure',
crossOrigin: 'anonymous'
})
}),
@@ -121,18 +119,3 @@ const map = new Map({
zoom: 2
})
});
const info = document.getElementById('info');
map.on('pointermove', function(evt) {
if (map.getView().getInteracting()) {
return;
}
const pixel = evt.pixel;
info.innerText = '';
map.forEachFeatureAtPixel(pixel, function(feature) {
const datetime = feature.get('datetime');
const duration = feature.get('duration');
const shape = feature.get('shape');
info.innerText = 'On ' + datetime + ', lasted ' + duration + ' seconds and had a "' + shape + '" shape.';
});
});

View File

@@ -37,7 +37,7 @@ const vectorLayer = new VectorLayer({
const rasterLayer = new TileLayer({
source: new TileJSON({
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json',
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
crossOrigin: ''
})
});

View File

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

View File

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

View File

@@ -3,13 +3,13 @@ layout: example.html
title: Mapbox-gl Layer
shortdesc: Example of a Mapbox-gl-js layer integration.
docs: >
Show how to add a mapbox-gl-js layer in an openlayers map. **Note**: Make sure to get your own API key at https://www.maptiler.com/cloud/ when using this example. No map will be visible when the API key has expired.
tags: "simple, mapbox, vector, tiles, maptiler"
Show how to add a mapbox-gl-js layer in an openlayers map. **Note**: Make sure to get your own Mapbox API key when using this example. No map will be visible when the API key has expired.
tags: "simple, mapbox, vector, tiles"
resources:
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.js
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.css
cloak:
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Get your own API key at https://www.maptiler.com/cloud/
- key: ER67WIiPdCQvhgsUjoWK
value: Your Mapbox access token from https://mapbox.com/ here
---
<div id="map" class="map"></div>

View File

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

View File

@@ -2,10 +2,10 @@
layout: example-verbatim.html
title: Vector tiles created from a Mapbox Style object
shortdesc: Example of using ol-mapbox-style with tiles from tilehosting.com.
tags: "vector tiles, mapbox style, ol-mapbox-style, maptiler"
tags: "vector tiles, mapbox style, ol-mapbox-style"
cloak:
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Get your own API key at https://www.maptiler.com/cloud/
- key: lirfd6Fegsjkvs0lshxe
value: Your API key from http://tilehosting.com/ here
---
<!doctype html>
<html lang="en">

View File

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

View File

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

View File

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

View File

@@ -7,9 +7,6 @@ docs: >
Click on the map to get a popup. The popup is composed of a few basic elements: a container, a close button, and a place for the content. To anchor the popup to the map, an <code>ol/Overlay</code> is created with the popup container. A listener is registered for the map's <code>click</code> event to display the popup, and another listener is set as the <code>click</code> handler for the close button to hide the popup.
</p>
tags: "overlay, popup"
cloak:
- key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg
value: Your Mapbox access token from https://mapbox.com/ here
---
<div id="map" class="map"></div>
<div id="popup" class="ol-popup">

View File

@@ -6,7 +6,6 @@ import TileLayer from '../src/ol/layer/Tile.js';
import {toLonLat} from '../src/ol/proj.js';
import TileJSON from '../src/ol/source/TileJSON.js';
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
/**
* Elements that make up the popup.
@@ -46,7 +45,7 @@ const map = new Map({
layers: [
new TileLayer({
source: new TileJSON({
url: 'https://api.tiles.mapbox.com/v4/mapbox.natural-earth-hypso-bathy.json?access_token=' + key,
url: 'https://api.tiles.mapbox.com/v3/mapbox.natural-earth-hypso-bathy.json?secure',
crossOrigin: 'anonymous'
})
})

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ const map = new Map({
layers: [
new TileLayer({
source: new TileJSON({
url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json',
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
crossOrigin: 'anonymous'
})
})

View File

@@ -4,7 +4,7 @@ title: UTFGrid
shortdesc: This example shows how to read data from a UTFGrid source.
docs: >
<p>Point to a country to see its name and flag.</p>
Tiles made with <a href="http://tilemill.com">TileMill</a>. Hosting on <a href="mapbox.com">mapbox.com</a> or with open-source <a href="https://github.com/klokantech/tileserver-php/">TileServer</a>.
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

View File

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

View File

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

11228
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

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

View File

@@ -22,16 +22,5 @@ module.exports = {
context: __dirname,
target: 'web',
entry: entry,
devtool: 'source-map',
module: {
rules: [{
test: /\.js$/,
use: {
loader: path.join(__dirname, '../examples/webpack/worker-loader.js')
},
include: [
path.join(__dirname, '../src/ol/worker')
]
}]
}
devtool: 'source-map'
};

View File

@@ -6,7 +6,6 @@ import ImageState from './ImageState.js';
import {listenOnce, unlistenByKey} from './events.js';
import EventType from './events/EventType.js';
import {getHeight} from './extent.js';
import {IMAGE_DECODE} from './has.js';
/**
@@ -158,8 +157,7 @@ class ImageWrapper extends ImageBase {
*/
export function listenImage(image, loadHandler, errorHandler) {
const img = /** @type {HTMLImageElement} */ (image);
if (img.src && IMAGE_DECODE) {
if (img.decode) {
const promise = img.decode();
let listening = true;
const unlisten = function() {
@@ -171,13 +169,7 @@ export function listenImage(image, loadHandler, errorHandler) {
}
}).catch(function(error) {
if (listening) {
// FIXME: Unconditionally call errorHandler() when this bug is fixed upstream:
// https://bugs.webkit.org/show_bug.cgi?id=198527
if (error.name === 'EncodingError' && error.message === 'Invalid image type.') {
loadHandler();
} else {
errorHandler();
}
errorHandler();
}
});
return unlisten;

View File

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

View File

@@ -260,6 +260,12 @@ class MapBrowserEventHandler extends EventTarget {
this.dragging_);
this.dispatchEvent(newEvent);
}
// Some native android browser triggers mousemove events during small period
// of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
// https://code.google.com/p/android/issues/detail?id=19827
// ex: Galaxy Tab P3110 + Android 4.1.1
pointerEvent.preventDefault();
}
/**

View File

@@ -22,7 +22,7 @@ import {listen, unlistenByKey, unlisten} from './events.js';
import EventType from './events/EventType.js';
import {createEmpty, clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js';
import {TRUE} from './functions.js';
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE} from './has.js';
import {DEVICE_PIXEL_RATIO, TOUCH} from './has.js';
import LayerGroup from './layer/Group.js';
import {hasArea} from './size.js';
import {DROP} from './structs/PriorityQueue.js';
@@ -230,7 +230,7 @@ class PluggableMap extends BaseObject {
* @type {!HTMLElement}
*/
this.viewport_ = document.createElement('div');
this.viewport_.className = 'ol-viewport' + ('ontouchstart' in window ? ' ol-touch' : '');
this.viewport_.className = 'ol-viewport' + (TOUCH ? ' ol-touch' : '');
this.viewport_.style.position = 'relative';
this.viewport_.style.overflow = 'hidden';
this.viewport_.style.width = '100%';
@@ -301,11 +301,6 @@ class PluggableMap extends BaseObject {
*/
this.interactions = optionsInternal.interactions || new Collection();
/**
* @type {import("./events/Target.js").default}
*/
this.labelCache_ = null;
/**
* @type {import("./events.js").EventsKey}
*/
@@ -328,7 +323,7 @@ class PluggableMap extends BaseObject {
* @type {import("./renderer/Map.js").default}
* @private
*/
this.renderer_ = null;
this.renderer_ = this.createRenderer();
/**
* @type {function(Event): void|undefined}
@@ -516,6 +511,24 @@ class PluggableMap extends BaseObject {
overlay.setMap(this);
}
/**
* Attach a label cache for listening to font changes.
* @param {import("./events/Target.js").default} labelCache Label cache.
*/
attachLabelCache(labelCache) {
this.detachLabelCache();
this.labelCacheListenerKey_ = listen(labelCache, EventType.CLEAR, this.redrawText.bind(this));
}
/**
* Detach the label cache, i.e. no longer listen to font changes.
*/
detachLabelCache() {
if (this.labelCacheListenerKey_) {
unlistenByKey(this.labelCacheListenerKey_);
}
}
/**
*
* @inheritDoc
@@ -529,6 +542,11 @@ class PluggableMap extends BaseObject {
removeEventListener(EventType.RESIZE, this.handleResize_, false);
this.handleResize_ = undefined;
}
if (this.animationDelayKey_) {
cancelAnimationFrame(this.animationDelayKey_);
this.animationDelayKey_ = undefined;
}
this.detachLabelCache();
this.setTarget(null);
super.disposeInternal();
}
@@ -967,7 +985,7 @@ class PluggableMap extends BaseObject {
if (frameState) {
const hints = frameState.viewHints;
if (hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]) {
const lowOnFrameBudget = !IMAGE_DECODE && Date.now() - frameState.time > 8;
const lowOnFrameBudget = Date.now() - frameState.time > 8;
maxTotalLoading = lowOnFrameBudget ? 0 : 8;
maxNewLoads = lowOnFrameBudget ? 0 : 2;
}
@@ -1023,14 +1041,6 @@ class PluggableMap extends BaseObject {
}
if (!targetElement) {
if (this.renderer_) {
this.renderer_.dispose();
this.renderer_ = null;
}
if (this.animationDelayKey_) {
cancelAnimationFrame(this.animationDelayKey_);
this.animationDelayKey_ = undefined;
}
removeNode(this.viewport_);
if (this.handleResize_ !== undefined) {
removeEventListener(EventType.RESIZE, this.handleResize_, false);
@@ -1038,9 +1048,6 @@ class PluggableMap extends BaseObject {
}
} else {
targetElement.appendChild(this.viewport_);
if (!this.renderer_) {
this.renderer_ = this.createRenderer();
}
const keyboardEventTarget = !this.keyboardEventTarget_ ?
targetElement : this.keyboardEventTarget_;
@@ -1159,7 +1166,7 @@ class PluggableMap extends BaseObject {
* @api
*/
render() {
if (this.renderer_ && this.animationDelayKey_ === undefined) {
if (this.animationDelayKey_ === undefined) {
this.animationDelayKey_ = requestAnimationFrame(this.animationDelay_);
}
}
@@ -1224,7 +1231,7 @@ class PluggableMap extends BaseObject {
let frameState = null;
if (size !== undefined && hasArea(size) && view && view.isDef()) {
const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);
viewState = view.getState();
viewState = view.getState(this.pixelRatio_);
frameState = {
animate: false,
coordinateToPixelTransform: this.coordinateToPixelTransform_,

View File

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

View File

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

View File

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

View File

@@ -51,8 +51,8 @@ class ControlledMap extends PluggableMap {
* @property {boolean} [collapsible=true] Whether the control can be collapsed or not.
* @property {string|HTMLElement} [label='»'] Text label to use for the collapsed
* overviewmap button. Instead of text, also an element (e.g. a `span` element) can be used.
* @property {Array<import("../layer/Layer.js").default>|import("../Collection.js").default<import("../layer/Layer.js").default>} [layers]
* Layers for the overview map.
* @property {Array<import("../layer/Layer.js").default>|import("../Collection.js").default<import("../layer/Layer.js").default>} layers
* Layers for the overview map (mandatory).
* @property {function(import("../MapEvent.js").default)} [render] Function called when the control
* should be re-rendered. This is called in a `requestAnimationFrame` callback.
* @property {HTMLElement|string} [target] Specify a target if you want the control
@@ -159,9 +159,13 @@ class OverviewMap extends Control {
const ovmap = this.ovmap_;
if (options.layers) {
options.layers.forEach(function(layer) {
ovmap.addLayer(layer);
});
/** @type {Array<import("../layer/Layer.js").default>} */ (options.layers).forEach(
/**
* @param {import("../layer/Layer.js").default} layer Layer.
*/
(function(layer) {
ovmap.addLayer(layer);
}).bind(this));
}
const box = document.createElement('div');

View File

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

View File

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

View File

@@ -4,7 +4,6 @@
import {assign} from '../obj.js';
import {abstract} from '../util.js';
import {get as getProjection, equivalent as equivalentProjection, transformExtent} from '../proj.js';
import Units from '../proj/Units.js';
/**
@@ -14,10 +13,8 @@ import Units from '../proj/Units.js';
* the `dataProjection` of the format is assigned (where set). If the projection
* can not be derived from the data and if no `dataProjection` is set for a format,
* the features will not be reprojected.
* @property {import("../extent.js").Extent} [extent] Tile extent in map units of the tile being read.
* This is only required when reading data with tile pixels as geometry units. When configured,
* a `dataProjection` with `TILE_PIXELS` as `units` and the tile's pixel extent as `extent` needs to be
* provided.
* @property {import("../extent.js").Extent} [extent] Tile extent of the tile being read. This is only used and
* required for {@link module:ol/format/MVT}.
* @property {import("../proj.js").ProjectionLike} [featureProjection] Projection of the feature geometries
* created by the format reader. If not provided, features will be returned in the
* `dataProjection`.
@@ -89,15 +86,9 @@ class FeatureFormat {
getReadOptions(source, opt_options) {
let options;
if (opt_options) {
let dataProjection = opt_options.dataProjection ?
getProjection(opt_options.dataProjection) : this.readProjection(source);
if (opt_options.extent &&
dataProjection && dataProjection.getUnits() === Units.TILE_PIXELS) {
dataProjection = getProjection(dataProjection);
dataProjection.setWorldExtent(opt_options.extent);
}
options = {
dataProjection: dataProjection,
dataProjection: opt_options.dataProjection ?
opt_options.dataProjection : this.readProjection(source),
featureProjection: opt_options.featureProjection
};
}

View File

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

View File

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

View File

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

View File

@@ -39,8 +39,30 @@ export const MAC = ua.indexOf('macintosh') !== -1;
*/
export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
/**
* Image.prototype.decode() is supported.
* True if browser supports touch events.
* @const
* @type {boolean}
* @api
*/
export const TOUCH = 'ontouchstart' in window;
/**
* True if browser supports pointer events.
* @const
* @type {boolean}
*/
export const IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode;
export const POINTER = 'PointerEvent' in window;
/**
* True if browser supports ms pointer events (IE 10).
* @const
* @type {boolean}
*/
export const MSPOINTER = !!(navigator.msPointerEnabled);
export {HAS as WEBGL} from './webgl.js';

View File

@@ -167,7 +167,7 @@ class DragPan extends PointerInteraction {
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Browser event.
* @param {ol.MapBrowserEvent} mapBrowserEvent Browser event.
* @return {boolean} Combined condition result.
*/
function defaultCondition(mapBrowserEvent) {

View File

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

View File

@@ -35,13 +35,6 @@ const TranslateEventType = {
TRANSLATEEND: 'translateend'
};
/**
* A function that takes an {@link module:ol/Feature} or
* {@link module:ol/render/Feature} and an
* {@link module:ol/layer/Layer} and returns `true` if the feature may be
* translated or `false` otherwise.
* @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default):boolean} FilterFunction
*/
/**
* @typedef {Object} Options
@@ -52,10 +45,6 @@ const TranslateEventType = {
* function will be called for each layer in the map and should return
* `true` for layers that you want to be translatable. If the option is
* absent, all visible layers will be considered translatable.
* @property {FilterFunction} [filter] A function
* that takes an {@link module:ol/Feature} and an
* {@link module:ol/layer/Layer} and returns `true` if the feature may be
* translated or `false` otherwise.
* @property {number} [hitTolerance=0] Hit-detection tolerance. Pixels inside the radius around the given position
* will be checked for features.
*/
@@ -147,12 +136,6 @@ class Translate extends PointerInteraction {
*/
this.layerFilter_ = layerFilter;
/**
* @private
* @type {FilterFunction}
*/
this.filter_ = options.filter ? options.filter : TRUE;
/**
* @private
* @type {number}
@@ -262,11 +245,9 @@ class Translate extends PointerInteraction {
*/
featuresAtPixel_(pixel, map) {
return map.forEachFeatureAtPixel(pixel,
function(feature, layer) {
if (this.filter_(feature, layer)) {
if (!this.features_ || includes(this.features_.getArray(), feature)) {
return feature;
}
function(feature) {
if (!this.features_ || includes(this.features_.getArray(), feature)) {
return feature;
}
}.bind(this), {
layerFilter: this.layerFilter_,

View File

@@ -23,10 +23,6 @@ import {assign} from '../obj.js';
* visible.
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible.
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
* visible.
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
* be visible.
*/
@@ -61,10 +57,6 @@ class BaseLayer extends BaseObject {
options.maxResolution !== undefined ? options.maxResolution : Infinity;
properties[LayerProperty.MIN_RESOLUTION] =
options.minResolution !== undefined ? options.minResolution : 0;
properties[LayerProperty.MIN_ZOOM] =
options.minZoom !== undefined ? options.minZoom : -Infinity;
properties[LayerProperty.MAX_ZOOM] =
options.maxZoom !== undefined ? options.maxZoom : Infinity;
/**
* @type {string}
@@ -101,8 +93,7 @@ class BaseLayer extends BaseObject {
/** @type {import("./Layer.js").State} */
const state = this.state_ || /** @type {?} */ ({
layer: this,
managed: opt_managed === undefined ? true : opt_managed,
hasOverlay: false
managed: opt_managed === undefined ? true : opt_managed
});
state.opacity = clamp(Math.round(this.getOpacity() * 100) / 100, 0, 1);
state.sourceState = this.getSourceState();
@@ -111,8 +102,6 @@ class BaseLayer extends BaseObject {
state.zIndex = this.getZIndex() || (state.managed === false ? Infinity : 0);
state.maxResolution = this.getMaxResolution();
state.minResolution = Math.max(this.getMinResolution(), 0);
state.minZoom = this.getMinZoom();
state.maxZoom = this.getMaxZoom();
this.state_ = state;
return state;
@@ -171,26 +160,6 @@ class BaseLayer extends BaseObject {
return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION));
}
/**
* Return the minimum zoom level of the layer.
* @return {number} The minimum zoom level of the layer.
* @observable
* @api
*/
getMinZoom() {
return /** @type {number} */ (this.get(LayerProperty.MIN_ZOOM));
}
/**
* Return the maximum zoom level of the layer.
* @return {number} The maximum zoom level of the layer.
* @observable
* @api
*/
getMaxZoom() {
return /** @type {number} */ (this.get(LayerProperty.MAX_ZOOM));
}
/**
* Return the opacity of the layer (between 0 and 1).
* @return {number} The opacity of the layer.
@@ -261,30 +230,6 @@ class BaseLayer extends BaseObject {
this.set(LayerProperty.MIN_RESOLUTION, minResolution);
}
/**
* Set the maximum zoom (exclusive) at which the layer is visible.
* Note that the zoom levels for layer visibility are based on the
* view zoom level, which may be different from a tile source zoom level.
* @param {number} maxZoom The maximum zoom of the layer.
* @observable
* @api
*/
setMaxZoom(maxZoom) {
this.set(LayerProperty.MAX_ZOOM, maxZoom);
}
/**
* Set the minimum zoom (inclusive) at which the layer is visible.
* Note that the zoom levels for layer visibility are based on the
* view zoom level, which may be different from a tile source zoom level.
* @param {number} minZoom The minimum zoom of the layer.
* @observable
* @api
*/
setMinZoom(minZoom) {
this.set(LayerProperty.MIN_ZOOM, minZoom);
}
/**
* Set the opacity of the layer, allowed values range from 0 to 1.
* @param {number} opacity The opacity of the layer.

View File

@@ -72,7 +72,8 @@ class BaseVectorLayer extends Layer {
* @param {Options=} opt_options Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ?
opt_options : /** @type {Options} */ ({});
const baseOptions = assign({}, options);

View File

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

View File

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

View File

@@ -45,13 +45,10 @@ import SourceState from '../source/State.js';
* @property {SourceState} sourceState
* @property {boolean} visible
* @property {boolean} managed
* @property {boolean} hasOverlay Set by the renderer when an overlay for points and text is used.
* @property {import("../extent.js").Extent} [extent]
* @property {number} zIndex
* @property {number} maxResolution
* @property {number} minResolution
* @property {number} minZoom
* @property {number} maxZoom
*/
/**
@@ -279,22 +276,17 @@ class Layer extends BaseLayer {
/**
* Return `true` if the layer is visible and if the provided view state
* has resolution and zoom levels that are in range of the layer's min/max.
* Return `true` if the layer is visible, and if the passed resolution is
* between the layer's minResolution and maxResolution. The comparison is
* inclusive for `minResolution` and exclusive for `maxResolution`.
* @param {State} layerState Layer state.
* @param {import("../View.js").State} viewState View state.
* @return {boolean} The layer is visible at the given view state.
* @param {number} resolution Resolution.
* @return {boolean} The layer is visible at the given resolution.
*/
export function inView(layerState, viewState) {
if (!layerState.visible) {
return false;
}
const resolution = viewState.resolution;
if (resolution < layerState.minResolution || resolution >= layerState.maxResolution) {
return false;
}
const zoom = viewState.zoom;
return zoom > layerState.minZoom && zoom <= layerState.maxZoom;
export function visibleAtResolution(layerState, resolution) {
return layerState.visible && resolution >= layerState.minResolution &&
resolution < layerState.maxResolution;
}
export default Layer;

View File

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

View File

@@ -5,6 +5,11 @@ import BaseTileLayer from './BaseTile.js';
import CanvasTileLayerRenderer from '../renderer/canvas/TileLayer.js';
/**
* @typedef {import("./BaseTile.js").Options} Options
*/
/**
* @classdesc
* For layer sources that provide pre-rendered, tiled images in grids that are
@@ -18,7 +23,7 @@ import CanvasTileLayerRenderer from '../renderer/canvas/TileLayer.js';
class TileLayer extends BaseTileLayer {
/**
* @param {import("./BaseTile.js").Options=} opt_options Tile layer options.
* @param {Options=} opt_options Tile layer options.
*/
constructor(opt_options) {
super(opt_options);

View File

@@ -5,6 +5,11 @@ import BaseVectorLayer from './BaseVector.js';
import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js';
/**
* @typedef {import("./BaseVector.js").Options} Options
*/
/**
* @classdesc
* Vector data that is rendered client-side.
@@ -17,7 +22,7 @@ import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js';
*/
class VectorLayer extends BaseVectorLayer {
/**
* @param {import("./BaseVector.js").Options=} opt_options Options.
* @param {Options=} opt_options Options.
*/
constructor(opt_options) {
super(opt_options);

View File

@@ -60,7 +60,7 @@ class VectorImageLayer extends BaseVectorLayer {
* @param {Options=} opt_options Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : /** @type {Options} */ ({});
const baseOptions = assign({}, options);
delete baseOptions.imageRatio;

View File

@@ -86,12 +86,12 @@ class VectorTileLayer extends BaseVectorLayer {
delete baseOptions.preload;
delete baseOptions.useInterimTilesOnError;
super(/** @type {import("./BaseVector.js").Options} */ (baseOptions));
super(/** @type {import("./Vector.js").Options} */ (baseOptions));
const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
assert(renderMode == undefined ||
renderMode == VectorTileRenderType.IMAGE ||
renderMode == VectorTileRenderType.HYBRID,
renderMode == VectorTileRenderType.IMAGE ||
renderMode == VectorTileRenderType.HYBRID,
28); // `renderMode` must be `'image'` or `'hybrid'`
/**

View File

@@ -63,6 +63,10 @@
border: 1px solid black;
}
.ol-overlay-container {
will-change: left,right,top,bottom;
}
.ol-unsupported {
display: none;
}

View File

@@ -34,6 +34,7 @@
import {listen, unlisten} from '../events.js';
import EventTarget from '../events/Target.js';
import {POINTER, MSPOINTER, TOUCH} from '../has.js';
import PointerEventType from './EventType.js';
import MouseSource, {prepareEvent as prepareMouseEvent} from './MouseSource.js';
import MsSource from './MsSource.js';
@@ -123,15 +124,15 @@ class PointerEventHandler extends EventTarget {
* that generate pointer events.
*/
registerSources() {
if ('PointerEvent' in window) {
if (POINTER) {
this.registerSource('native', new NativeSource(this));
} else if (window.navigator.msPointerEnabled) {
} else if (MSPOINTER) {
this.registerSource('ms', new MsSource(this));
} else {
const mouseSource = new MouseSource(this);
this.registerSource('mouse', mouseSource);
if ('ontouchstart' in window) {
if (TOUCH) {
this.registerSource('touch', new TouchSource(this, mouseSource));
}
}

View File

@@ -60,8 +60,8 @@ import {toEPSG4326, fromEPSG4326, PROJECTIONS as EPSG3857_PROJECTIONS} from './p
import {PROJECTIONS as EPSG4326_PROJECTIONS} from './proj/epsg4326.js';
import Projection from './proj/Projection.js';
import Units, {METERS_PER_UNIT} from './proj/Units.js';
import * as projections from './proj/projections.js';
import {add as addTransformFunc, clear as clearTransformFuncs, get as getTransformFunc} from './proj/transforms.js';
import {add as addProj, clear as clearProj, get as getProj} from './proj/projections.js';
/**
@@ -133,7 +133,7 @@ export function identityTransform(input, opt_output, opt_dimension) {
* @api
*/
export function addProjection(projection) {
addProj(projection.getCode(), projection);
projections.add(projection.getCode(), projection);
addTransformFunc(projection, projection, cloneTransform);
}
@@ -157,7 +157,7 @@ export function addProjections(projections) {
*/
export function get(projectionLike) {
return typeof projectionLike === 'string' ?
getProj(/** @type {string} */ (projectionLike)) :
projections.get(/** @type {string} */ (projectionLike)) :
(/** @type {Projection} */ (projectionLike) || null);
}
@@ -271,7 +271,7 @@ export function addEquivalentTransforms(projections1, projections2, forwardTrans
* Clear all cached projections and transforms.
*/
export function clearAllProjections() {
clearProj();
projections.clear();
clearTransformFuncs();
}

View File

@@ -12,8 +12,9 @@ class RenderEvent extends Event {
* CSS pixels to rendered pixels.
* @param {import("../PluggableMap.js").FrameState=} opt_frameState Frame state.
* @param {?CanvasRenderingContext2D=} opt_context Context.
* @param {?import("../webgl/Helper.js").default=} opt_glContext WebGL Context.
*/
constructor(type, opt_inversePixelTransform, opt_frameState, opt_context) {
constructor(type, opt_inversePixelTransform, opt_frameState, opt_context, opt_glContext) {
super(type);
@@ -40,6 +41,14 @@ class RenderEvent extends Event {
*/
this.context = opt_context;
/**
* WebGL context. Only available when a WebGL renderer is used, null
* otherwise.
* @type {import("../webgl/Helper.js").default|null|undefined}
* @api
*/
this.glContext = opt_glContext;
}
}

View File

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

View File

@@ -207,8 +207,8 @@ class Executor extends Disposable {
if (strokeKey) {
context.strokeStyle = strokeState.strokeStyle;
context.lineWidth = strokeWidth;
context.lineCap = strokeState.lineCap;
context.lineJoin = strokeState.lineJoin;
context.lineCap = /** @type {CanvasLineCap} */ (strokeState.lineCap);
context.lineJoin = /** @type {CanvasLineJoin} */ (strokeState.lineJoin);
context.miterLimit = strokeState.miterLimit;
if (context.setLineDash && strokeState.lineDash.length) {
context.setLineDash(strokeState.lineDash);

View File

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

View File

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

View File

@@ -2,15 +2,13 @@
* @module ol/renderer/Composite
*/
import {CLASS_UNSELECTABLE} from '../css.js';
import {inView} from '../layer/Layer.js';
import {visibleAtResolution} from '../layer/Layer.js';
import RenderEvent from '../render/Event.js';
import RenderEventType from '../render/EventType.js';
import MapRenderer from './Map.js';
import SourceState from '../source/State.js';
import {replaceChildren} from '../dom.js';
import {labelCache} from '../render/canvas.js';
import EventType from '../events/EventType.js';
import {listen, unlistenByKey} from '../events.js';
/**
@@ -25,11 +23,7 @@ class CompositeMapRenderer extends MapRenderer {
*/
constructor(map) {
super(map);
/**
* @type {import("../events.js").EventsKey}
*/
this.labelCacheKey_ = listen(labelCache, EventType.CLEAR, map.redrawText.bind(map));
map.attachLabelCache(labelCache);
/**
* @private
@@ -72,11 +66,6 @@ class CompositeMapRenderer extends MapRenderer {
}
}
disposeInternal() {
unlistenByKey(this.labelCacheKey_);
super.disposeInternal();
}
/**
* @inheritDoc
*/
@@ -95,31 +84,22 @@ class CompositeMapRenderer extends MapRenderer {
const layerStatesArray = frameState.layerStatesArray.sort(function(a, b) {
return a.zIndex - b.zIndex;
});
const viewState = frameState.viewState;
const viewResolution = frameState.viewState.resolution;
this.children_.length = 0;
let hasOverlay = false;
let previousElement = null;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
hasOverlay = hasOverlay || layerState.hasOverlay;
frameState.layerIndex = i;
if (!inView(layerState, viewState) ||
if (!visibleAtResolution(layerState, viewResolution) ||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
continue;
}
const layer = layerState.layer;
const element = layer.render(frameState, previousElement);
if (!element) {
continue;
}
if ((element !== previousElement || i == ii - 1) && element.childElementCount === 2 && !hasOverlay) {
element.removeChild(element.lastElementChild);
}
if (element !== previousElement) {
this.children_.push(element);
hasOverlay = false;
previousElement = element;
}
}
@@ -142,6 +122,7 @@ class CompositeMapRenderer extends MapRenderer {
*/
forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, layerFilter) {
const viewState = frameState.viewState;
const viewResolution = viewState.resolution;
const layerStates = frameState.layerStatesArray;
const numLayers = layerStates.length;
@@ -149,7 +130,7 @@ class CompositeMapRenderer extends MapRenderer {
for (let i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i];
const layer = layerState.layer;
if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter(layer)) {
if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter(layer)) {
const layerRenderer = layer.getRenderer();
const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance);
if (data) {

View File

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

View File

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

View File

@@ -95,15 +95,15 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
}
// set forward and inverse pixel transforms
composeTransform(this.pixelTransform,
composeTransform(this.pixelTransform_,
frameState.size[0] / 2, frameState.size[1] / 2,
1 / pixelRatio, 1 / pixelRatio,
rotation,
-width / 2, -height / 2
);
makeInverse(this.inversePixelTransform, this.pixelTransform);
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
this.useContainer(target, this.pixelTransform, layerState.opacity);
this.useContainer(target, this.pixelTransform_, layerState.opacity);
const context = this.context;
const canvas = context.canvas;
@@ -160,7 +160,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
context.restore();
}
const canvasTransform = transformToString(this.pixelTransform);
const canvasTransform = transformToString(this.pixelTransform_);
if (canvasTransform !== canvas.style.transform) {
canvas.style.transform = canvasTransform;
}

View File

@@ -44,18 +44,18 @@ class CanvasLayerRenderer extends LayerRenderer {
/**
* The transform for rendered pixels to viewport CSS pixels. This transform must
* be set when rendering a frame and may be used by other functions after rendering.
* @protected
* @private
* @type {import("../../transform.js").Transform}
*/
this.pixelTransform = createTransform();
this.pixelTransform_ = createTransform();
/**
* The transform for viewport CSS pixels to rendered pixels. This transform must
* be set when rendering a frame and may be used by other functions after rendering.
* @protected
* @private
* @type {import("../../transform.js").Transform}
*/
this.inversePixelTransform = createTransform();
this.inversePixelTransform_ = createTransform();
/**
* @protected
@@ -163,7 +163,7 @@ class CanvasLayerRenderer extends LayerRenderer {
applyTransform(frameState.coordinateToPixelTransform, bottomRight);
applyTransform(frameState.coordinateToPixelTransform, bottomLeft);
const inverted = this.inversePixelTransform;
const inverted = this.inversePixelTransform_;
applyTransform(inverted, topLeft);
applyTransform(inverted, topRight);
applyTransform(inverted, bottomRight);
@@ -187,7 +187,7 @@ class CanvasLayerRenderer extends LayerRenderer {
dispatchRenderEvent_(type, context, frameState) {
const layer = this.getLayer();
if (layer.hasListener(type)) {
const event = new RenderEvent(type, this.inversePixelTransform, frameState, context);
const event = new RenderEvent(type, this.inversePixelTransform_, frameState, context, null);
layer.dispatchEvent(event);
}
}
@@ -240,7 +240,7 @@ class CanvasLayerRenderer extends LayerRenderer {
* returned, and empty array will be returned.
*/
getDataAtPixel(pixel, frameState, hitTolerance) {
const renderPixel = applyTransform(this.inversePixelTransform, pixel.slice());
const renderPixel = applyTransform(this.inversePixelTransform_, pixel.slice());
const context = this.context;
let data;

View File

@@ -7,7 +7,6 @@ import TileState from '../../TileState.js';
import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js';
import CanvasLayerRenderer from './Layer.js';
import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
import {numberSafeCompareFunction} from '../../array.js';
/**
* @classdesc
@@ -63,6 +62,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
* @type {import("../../TileRange.js").default}
*/
this.tmpTileRange_ = new TileRange(0, 0, 0, 0);
/**
* @protected
* @type {number}
*/
this.zDirection = 0;
}
/**
@@ -145,7 +150,8 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const tileSource = tileLayer.getSource();
const sourceRevision = tileSource.getRevision();
const tileGrid = tileSource.getTileGridForProjection(projection);
const z = tileGrid.getZForResolution(viewResolution, tileSource.zDirection);
const zDirection = tileSource.zDirection === undefined ? this.zDirection : tileSource.zDirection;
const z = tileGrid.getZForResolution(viewResolution, zDirection);
const tileResolution = tileGrid.getResolution(z);
let extent = frameState.extent;
@@ -221,18 +227,18 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const canvasScale = tileResolution / viewResolution;
// set forward and inverse pixel transforms
composeTransform(this.pixelTransform,
composeTransform(this.pixelTransform_,
frameState.size[0] / 2, frameState.size[1] / 2,
1 / tilePixelRatio, 1 / tilePixelRatio,
rotation,
-width / 2, -height / 2
);
this.useContainer(target, this.pixelTransform, layerState.opacity);
this.useContainer(target, this.pixelTransform_, layerState.opacity);
const context = this.context;
const canvas = context.canvas;
makeInverse(this.inversePixelTransform, this.pixelTransform);
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
// set scale transform for calculating tile positions on the canvas
composeTransform(this.tempTransform_,
@@ -258,7 +264,15 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
this.renderedTiles.length = 0;
/** @type {Array<number>} */
let zs = Object.keys(tilesToDrawByZ).map(Number);
zs.sort(numberSafeCompareFunction);
zs.sort(function(a, b) {
if (a === z) {
return 1;
} else if (b === z) {
return -1;
} else {
return a > b ? 1 : a < b ? -1 : 0;
}
});
let clips, clipZs, currentClip;
if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) {
@@ -297,37 +311,32 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const h = nextY - y;
const transition = z === currentZ;
const inTransition = transition && tile.getAlpha(getUid(this), frameState.time) !== 1;
if (!inTransition) {
if (clips) {
// Clip mask for regions in this tile that already filled by a higher z tile
context.save();
currentClip = [x, y, x + w, y, x + w, y + h, x, y + h];
for (let i = 0, ii = clips.length; i < ii; ++i) {
if (z !== currentZ && currentZ < clipZs[i]) {
const clip = clips[i];
context.beginPath();
// counter-clockwise (outer ring) for current tile
context.moveTo(currentClip[0], currentClip[1]);
context.lineTo(currentClip[2], currentClip[3]);
context.lineTo(currentClip[4], currentClip[5]);
context.lineTo(currentClip[6], currentClip[7]);
// clockwise (inner ring) for higher z tile
context.moveTo(clip[6], clip[7]);
context.lineTo(clip[4], clip[5]);
context.lineTo(clip[2], clip[3]);
context.lineTo(clip[0], clip[1]);
context.clip();
}
if (clips && (!transition || tile.getAlpha(getUid(this), frameState.time) === 1)) {
// Clip mask for regions in this tile that already filled by a higher z tile
context.save();
currentClip = [x, y, x + w, y, x + w, y + h, x, y + h];
for (let i = 0, ii = clips.length; i < ii; ++i) {
if (z !== currentZ && currentZ < clipZs[i]) {
const clip = clips[i];
context.beginPath();
// counter-clockwise (outer ring) for current tile
context.moveTo(currentClip[0], currentClip[1]);
context.lineTo(currentClip[2], currentClip[3]);
context.lineTo(currentClip[4], currentClip[5]);
context.lineTo(currentClip[6], currentClip[7]);
// clockwise (inner ring) for higher z tile
context.moveTo(clip[6], clip[7]);
context.lineTo(clip[4], clip[5]);
context.lineTo(clip[2], clip[3]);
context.lineTo(clip[0], clip[1]);
context.clip();
}
clips.push(currentClip);
clipZs.push(currentZ);
} else {
context.clearRect(x, y, w, h);
}
clips.push(currentClip);
clipZs.push(currentZ);
}
this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity);
if (clips && !inTransition) {
if (clips) {
context.restore();
}
this.renderedTiles.push(tile);
@@ -352,7 +361,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
context.restore();
}
const canvasTransform = transformToString(this.pixelTransform);
const canvasTransform = transformToString(this.pixelTransform_);
if (canvasTransform !== canvas.style.transform) {
canvas.style.transform = canvasTransform;
}

View File

@@ -86,10 +86,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
const layerState = frameState.layerStatesArray[frameState.layerIndex];
// set forward and inverse pixel transforms
makeScale(this.pixelTransform, 1 / pixelRatio, 1 / pixelRatio);
makeInverse(this.inversePixelTransform, this.pixelTransform);
makeScale(this.pixelTransform_, 1 / pixelRatio, 1 / pixelRatio);
makeInverse(this.inversePixelTransform_, this.pixelTransform_);
this.useContainer(target, this.pixelTransform, layerState.opacity);
this.useContainer(target, this.pixelTransform_, layerState.opacity);
const context = this.context;
const canvas = context.canvas;
@@ -107,7 +107,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
if (canvas.width != width || canvas.height != height) {
canvas.width = width;
canvas.height = height;
const canvasTransform = transformToString(this.pixelTransform);
const canvasTransform = transformToString(this.pixelTransform_);
if (canvas.style.transform !== canvasTransform) {
canvas.style.transform = canvasTransform;
}

View File

@@ -24,7 +24,6 @@ import {
makeInverse
} from '../../transform.js';
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import {clear} from '../../obj.js';
/**
@@ -65,11 +64,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
this.overlayContext_ = null;
/**
* @type {string}
*/
this.overlayContextUid_;
/**
* The transform for rendered pixels to viewport CSS pixels for the overlay canvas.
* @private
@@ -113,6 +107,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
this.tmpTransform_ = createTransform();
// Use nearest lower resolution.
this.zDirection = 1;
}
/**
@@ -128,9 +124,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
const containerReused = this.containerReused;
super.useContainer(target, transform, opacity);
if (containerReused) {
this.overlayContext_ = overlayContext || null;
this.overlayContextUid_ = overlayContext ? getUid(overlayContext) : undefined;
if (containerReused && !this.containerReused && !overlayContext) {
this.overlayContext_ = null;
}
if (this.containerReused && overlayContext) {
this.overlayContext_ = overlayContext;
}
if (!this.overlayContext_) {
const overlayContext = createCanvasContext2D();
@@ -138,7 +136,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
style.position = 'absolute';
style.transformOrigin = 'top left';
this.overlayContext_ = overlayContext;
this.overlayContextUid_ = getUid(overlayContext);
}
if (this.container.childElementCount === 1) {
this.container.appendChild(this.overlayContext_.canvas);
@@ -222,8 +219,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc
*/
prepareFrame(frameState) {
const layerState = frameState.layerStatesArray[frameState.layerIndex];
layerState.hasOverlay = true;
const layerRevision = this.getLayer().getRevision();
if (this.renderedLayerRevision_ != layerRevision) {
this.renderedTiles.length = 0;
@@ -342,13 +337,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
let i, ii;
for (i = 0, ii = renderedTiles.length; i < ii; ++i) {
const tile = renderedTiles[i];
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
const tileContainsCoordinate = containsCoordinate(tileExtent, coordinate);
if (!declutter) {
// When not decluttering, we only need to consider the tile that contains the given
// coordinate, because each feature will be rendered for each tile that contains it.
if (!tileContainsCoordinate) {
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
if (!containsCoordinate(tileExtent, coordinate)) {
continue;
}
}
@@ -361,15 +354,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @return {?} Callback result.
*/
function(feature) {
if (tileContainsCoordinate || (declutteredFeatures && declutteredFeatures.indexOf(feature) !== -1)) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
}, layer.getDeclutter() ? declutteredFeatures : null);
}
@@ -381,7 +372,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc
*/
handleFontsChanged() {
clear(this.renderTileImageQueue_);
const layer = this.getLayer();
if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) {
layer.changed();
@@ -417,7 +407,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
// Unqueue tiles from the image queue when we don't need any more
const usedTiles = frameState.usedTiles[getUid(source)];
for (const tileUid in this.renderTileImageQueue_) {
if (!usedTiles || !(tileUid in usedTiles)) {
if (!(tileUid in usedTiles)) {
delete this.renderTileImageQueue_[tileUid];
}
}
@@ -444,7 +434,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (canvas.style.transform !== canvasTransform) {
canvas.style.transform = canvasTransform;
}
} else if (getUid(context) === this.overlayContextUid_) {
} else if (!this.containerReused) {
context.clearRect(0, 0, width, height);
}

View File

@@ -5,26 +5,6 @@ import LayerRenderer from '../Layer.js';
import WebGLHelper from '../../webgl/Helper.js';
/**
* @enum {string}
*/
export const WebGLWorkerMessageType = {
GENERATE_BUFFERS: 'GENERATE_BUFFERS'
};
/**
* @typedef {Object} WebGLWorkerGenerateBuffersMessage
* This message will trigger the generation of a vertex and an index buffer based on the given render instructions.
* When the buffers are generated, the worked will send a message of the same type to the main thread, with
* the generated buffers in it.
* Note that any addition properties present in the message *will* be sent back to the main thread.
* @property {WebGLWorkerMessageType} type Message type
* @property {ArrayBuffer} renderInstructions Render instructions raw binary buffer.
* @property {ArrayBuffer} [vertexBuffer] Vertices array raw binary buffer (sent by the worker).
* @property {ArrayBuffer} [indexBuffer] Indices array raw binary buffer (sent by the worker).
* @property {number} [customAttributesCount] Amount of custom attributes count in the render instructions.
*/
/**
* @typedef {Object} PostProcessesOptions
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
@@ -56,11 +36,7 @@ class WebGLLayerRenderer extends LayerRenderer {
const options = opt_options || {};
/**
* @type {WebGLHelper}
* @protected
*/
this.helper = new WebGLHelper({
this.helper_ = new WebGLHelper({
postProcesses: options.postProcesses,
uniforms: options.uniforms
});
@@ -79,152 +55,96 @@ class WebGLLayerRenderer extends LayerRenderer {
* @api
*/
getShaderCompileErrors() {
return this.helper.getShaderCompileErrors();
return this.helper_.getShaderCompileErrors();
}
}
/**
* @param {Float32Array} instructions Instructons array in which to write.
* @param {number} elementIndex Index from which render instructions will be written.
* @param {number} x Point center X coordinate
* @param {number} y Point center Y coordinate
* @param {number} u0 Left texture coordinate
* @param {number} v0 Bottom texture coordinate
* @param {number} u1 Right texture coordinate
* @param {number} v1 Top texture coordinate
* @param {number} size Radius of the point
* @param {number} opacity Opacity
* @param {boolean} rotateWithView If true, the point will stay aligned with the view
* @param {Array<number>} color Array holding red, green, blue, alpha values
* @return {number} Index from which the next element should be written
* @private
* Pushes vertices and indices to the given buffers using the geometry coordinates and the following properties
* from the feature:
* - `color`
* - `opacity`
* - `size` (for points)
* - `u0`, `v0`, `u1`, `v1` (for points)
* - `rotateWithView` (for points)
* - `width` (for lines)
* Custom attributes can be designated using the `opt_attributes` argument, otherwise other properties on the
* feature will be ignored.
* @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer in which new vertices will be pushed.
* @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer in which new indices will be pushed.
* @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature in geojson format, coordinates
* expressed in EPSG:4326.
* @param {Array<string>} [opt_attributes] Custom attributes. An array of properties which will be read from the
* feature and pushed in the buffer in the given order. Note: attributes can only be numerical! Any other type or
* NaN will result in `0` being pushed in the buffer.
*/
export function writePointFeatureInstructions(instructions, elementIndex, x, y, u0, v0, u1, v1, size, opacity, rotateWithView, color) {
let i = elementIndex;
instructions[i++] = x;
instructions[i++] = y;
instructions[i++] = u0;
instructions[i++] = v0;
instructions[i++] = u1;
instructions[i++] = v1;
instructions[i++] = size;
instructions[i++] = opacity;
instructions[i++] = rotateWithView ? 1 : 0;
instructions[i++] = color[0];
instructions[i++] = color[1];
instructions[i++] = color[2];
instructions[i++] = color[3];
return i;
export function pushFeatureToBuffer(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) {
if (!geojsonFeature.geometry) {
return;
}
switch (geojsonFeature.geometry.type) {
case 'Point':
pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes);
return;
default:
return;
}
}
const tmpArray_ = [];
const bufferPositions_ = {vertexPosition: 0, indexPosition: 0};
export const POINT_INSTRUCTIONS_COUNT = 13;
export const POINT_VERTEX_STRIDE = 12;
function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha) {
buffer[pos + 0] = x;
buffer[pos + 1] = y;
buffer[pos + 2] = offsetX;
buffer[pos + 3] = offsetY;
buffer[pos + 4] = u;
buffer[pos + 5] = v;
buffer[pos + 6] = opacity;
buffer[pos + 7] = rotateWithView;
buffer[pos + 8] = red;
buffer[pos + 9] = green;
buffer[pos + 10] = blue;
buffer[pos + 11] = alpha;
}
function writeCustomAttrs(buffer, pos, customAttrs) {
if (customAttrs.length) {
buffer.set(customAttrs, pos);
}
}
/**
* An object holding positions both in an index and a vertex buffer.
* @typedef {Object} BufferPositions
* @property {number} vertexPosition Position in the vertex buffer
* @property {number} indexPosition Position in the index buffer
*/
/**
* Pushes a quad (two triangles) based on a point geometry
* @param {Float32Array} instructions Array of render instructions for points.
* @param {number} elementIndex Index from which render instructions will be read.
* @param {Float32Array} vertexBuffer Buffer in the form of a typed array.
* @param {Uint32Array} indexBuffer Buffer in the form of a typed array.
* @param {BufferPositions} [bufferPositions] Buffer write positions; if not specified, positions will be set at 0.
* @param {number} [count] Amount of render instructions that will be read. Default value is POINT_INSTRUCTIONS_COUNT
* but a higher value can be provided; all values beyond the default count will be put in the vertices buffer as
* is, thus allowing specifying custom attributes. Please note: this value should not vary inside the same buffer or
* rendering will break.
* @return {BufferPositions} New buffer positions where to write next
* @property {number} vertexPosition New position in the vertex buffer where future writes should start.
* @property {number} indexPosition New position in the index buffer where future writes should start.
* @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer
* @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer
* @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature
* @param {Array<string>} [opt_attributes] Custom attributes
* @private
*/
export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, bufferPositions, count) {
const count_ = count > POINT_INSTRUCTIONS_COUNT ? count : POINT_INSTRUCTIONS_COUNT;
function pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) {
const stride = 12 + (opt_attributes !== undefined ? opt_attributes.length : 0);
const x = instructions[elementIndex + 0];
const y = instructions[elementIndex + 1];
const u0 = instructions[elementIndex + 2];
const v0 = instructions[elementIndex + 3];
const u1 = instructions[elementIndex + 4];
const v1 = instructions[elementIndex + 5];
const size = instructions[elementIndex + 6];
const opacity = instructions[elementIndex + 7];
const rotateWithView = instructions[elementIndex + 8];
const red = instructions[elementIndex + 9];
const green = instructions[elementIndex + 10];
const blue = instructions[elementIndex + 11];
const alpha = instructions[elementIndex + 12];
// the default vertex buffer stride is 12, plus additional custom values if any
const baseStride = POINT_VERTEX_STRIDE;
const stride = baseStride + count_ - POINT_INSTRUCTIONS_COUNT;
const x = geojsonFeature.geometry.coordinates[0];
const y = geojsonFeature.geometry.coordinates[1];
const u0 = geojsonFeature.properties.u0;
const v0 = geojsonFeature.properties.v0;
const u1 = geojsonFeature.properties.u1;
const v1 = geojsonFeature.properties.v1;
const size = geojsonFeature.properties.size;
const opacity = geojsonFeature.properties.opacity;
const rotateWithView = geojsonFeature.properties.rotateWithView;
const color = geojsonFeature.properties.color;
const red = color[0];
const green = color[1];
const blue = color[2];
const alpha = color[3];
const baseIndex = vertexBuffer.getArray().length / stride;
// read custom numerical attributes on the feature
const customAttrs = tmpArray_;
customAttrs.length = count_ - POINT_INSTRUCTIONS_COUNT;
for (let i = 0; i < customAttrs.length; i++) {
customAttrs[i] = instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + i];
const customAttributeValues = tmpArray_;
customAttributeValues.length = opt_attributes ? opt_attributes.length : 0;
for (let i = 0; i < customAttributeValues.length; i++) {
customAttributeValues[i] = parseFloat(geojsonFeature.properties[opt_attributes[i]]) || 0;
}
let vPos = bufferPositions ? bufferPositions.vertexPosition : 0;
let iPos = bufferPositions ? bufferPositions.indexPosition : 0;
const baseIndex = vPos / stride;
// push vertices for each of the four quad corners (first standard then custom attributes)
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;
vertexBuffer.getArray().push(x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha);
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;
vertexBuffer.getArray().push(x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha);
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;
vertexBuffer.getArray().push(x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha);
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;
vertexBuffer.getArray().push(x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha);
Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues);
indexBuffer[iPos++] = baseIndex; indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 3;
indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 2; indexBuffer[iPos++] = baseIndex + 3;
bufferPositions_.vertexPosition = vPos;
bufferPositions_.indexPosition = iPos;
return bufferPositions_;
indexBuffer.getArray().push(
baseIndex, baseIndex + 1, baseIndex + 3,
baseIndex + 1, baseIndex + 2, baseIndex + 3
);
}
/**
@@ -232,47 +152,11 @@ export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuf
* @private
* @return {ImageData} Image data.
*/
export function getBlankImageData() {
export function getBlankTexture() {
const canvas = document.createElement('canvas');
const image = canvas.getContext('2d').createImageData(1, 1);
image.data[0] = image.data[1] = image.data[2] = image.data[3] = 255;
return image;
}
/**
* Generates a color array based on a numerical id
* Note: the range for each component is 0 to 1 with 256 steps
* @param {number} id Id
* @param {Array<number>} [opt_array] Reusable array
* @return {Array<number>} Color array containing the encoded id
*/
export function colorEncodeId(id, opt_array) {
const array = opt_array || [];
const radix = 256;
const divide = radix - 1;
array[0] = Math.floor(id / radix / radix / radix) / divide;
array[1] = (Math.floor(id / radix / radix) % radix) / divide;
array[2] = (Math.floor(id / radix) % radix) / divide;
array[3] = (id % radix) / divide;
return array;
}
/**
* Reads an id from a color-encoded array
* Note: the expected range for each component is 0 to 1 with 256 steps.
* @param {Array<number>} color Color array containing the encoded id
* @return {number} Decoded id
*/
export function colorDecodeId(color) {
let id = 0;
const radix = 256;
const mult = radix - 1;
id += Math.round(color[0] * radix * radix * radix * mult);
id += Math.round(color[1] * radix * radix * mult);
id += Math.round(color[2] * radix * mult);
id += Math.round(color[3] * mult);
return id;
}
export default WebGLLayerRenderer;

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