Compare commits

..

1 Commits

Author SHA1 Message Date
ahocevar
5cc1ff0f45 Use beta tag 2019-06-24 09:54:41 +02:00
193 changed files with 1028 additions and 14295 deletions

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;
@@ -166,7 +148,7 @@ 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;
}

View File

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

View File

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

View File

@@ -65,18 +65,18 @@ exportButton.addEventListener('click', function() {
const width = Math.round(dim[0] * resolution / 25.4);
const height = Math.round(dim[1] * resolution / 25.4);
const size = map.getSize();
const 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

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

View File

@@ -103,7 +103,7 @@ function loadData() {
loadData();
const map = new Map({
new Map({
layers: [
new TileLayer({
source: new TileJSON({
@@ -121,18 +121,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

@@ -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

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

View File

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

View File

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

View File

@@ -1,3 +1,3 @@
import apply from 'ol-mapbox-style';
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB');
apply('map', 'https://api.maptiler.com/maps/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

@@ -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

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

View File

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

View File

@@ -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);
});

11295
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.15",
"version": "6.0.0-beta.10",
"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",
"coveralls": "3.0.4",
"eslint": "^6.0.0",
"eslint-config-openlayers": "^12.0.0",
"expect.js": "0.3.1",
"front-matter": "^3.0.2",
"fs-extra": "^8.0.0",
"glob": "^7.1.4",
"globby": "^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.1.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",
"marked": "0.6.2",
"mocha": "6.1.4",
"ol-mapbox-style": "^5.0.0-beta.2",
"pixelmatch": "^5.0.0",
"pngjs": "^3.4.0",
"proj4": "2.5.0",
"puppeteer": "~1.19.0",
"puppeteer": "~1.18.0",
"rollup": "^1.12.0",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^10.0.0",
@@ -97,11 +97,11 @@
"typescript": "^3.4.5",
"url-polyfill": "^1.1.5",
"walk": "^2.3.9",
"webpack": "4.39.2",
"webpack": "4.35.0",
"webpack-cli": "^3.3.2",
"webpack-dev-middleware": "^3.6.2",
"webpack-dev-server": "^3.3.1",
"yargs": "^14.0.0"
"yargs": "^13.2.2"
},
"eslintConfig": {
"extends": "openlayers",

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: 118 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

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

Binary file not shown.

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();

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

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

View File

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

View File

@@ -1224,7 +1224,7 @@ class PluggableMap extends BaseObject {
let frameState = null;
if (size !== undefined && hasArea(size) && view && view.isDef()) {
const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);
viewState = view.getState();
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

@@ -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';
/**
@@ -90,9 +89,8 @@ class FeatureFormat {
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) {
opt_options.dataProjection : this.readProjection(source);
if (opt_options.extent) {
dataProjection = getProjection(dataProjection);
dataProjection.setWorldExtent(opt_options.extent);
}

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

@@ -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

@@ -6,7 +6,6 @@ import BaseObject from '../Object.js';
import LayerProperty from './Property.js';
import {clamp} from '../math.js';
import {assign} from '../obj.js';
import {assert} from '../asserts.js';
/**
@@ -24,10 +23,6 @@ import {assert} from '../asserts.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.
*/
@@ -53,11 +48,8 @@ class BaseLayer extends BaseObject {
* @type {Object<string, *>}
*/
const properties = assign({}, options);
properties[LayerProperty.OPACITY] =
options.opacity !== undefined ? options.opacity : 1;
assert(typeof properties[LayerProperty.OPACITY] === 'number', 64); // Layer opacity must be a number
options.opacity !== undefined ? options.opacity : 1;
properties[LayerProperty.VISIBLE] =
options.visible !== undefined ? options.visible : true;
properties[LayerProperty.Z_INDEX] = options.zIndex;
@@ -65,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}
@@ -115,8 +103,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;
@@ -175,26 +161,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.
@@ -265,30 +231,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.
@@ -296,7 +238,6 @@ class BaseLayer extends BaseObject {
* @api
*/
setOpacity(opacity) {
assert(typeof opacity === 'number', 64); // Layer opacity must be a number
this.set(LayerProperty.OPACITY, opacity);
}

View File

@@ -29,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

@@ -50,8 +50,6 @@ import SourceState from '../source/State.js';
* @property {number} zIndex
* @property {number} maxResolution
* @property {number} minResolution
* @property {number} minZoom
* @property {number} maxZoom
*/
/**
@@ -279,22 +277,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

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

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
@@ -62,6 +62,7 @@ import LabelCache from './canvas/LabelCache.js';
* @property {Array<number>} [padding]
*/
/**
* Container for decluttered replay instructions that need to be rendered or
* omitted together, i.e. when styles render both an image and text, or for the
@@ -75,12 +76,6 @@ import LabelCache from './canvas/LabelCache.js';
*/
/**
* Declutter groups for support of multi geometries.
* @typedef {Array<DeclutterGroup>} DeclutterGroups
*/
/**
* @const
* @type {string}
@@ -97,7 +92,7 @@ export const defaultFillStyle = '#000';
/**
* @const
* @type {CanvasLineCap}
* @type {string}
*/
export const defaultLineCap = 'round';
@@ -118,7 +113,7 @@ export const defaultLineDashOffset = 0;
/**
* @const
* @type {CanvasLineJoin}
* @type {string}
*/
export const defaultLineJoin = 'round';
@@ -185,10 +180,6 @@ export const checkedFonts = {};
*/
let measureContext = null;
/**
* @type {string}
*/
let measureFont;
/**
* @type {!Object<string, number>}
@@ -201,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'];
@@ -209,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;
}
@@ -240,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;
@@ -262,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);
}
@@ -329,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

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

View File

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

View File

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

View File

@@ -692,12 +692,12 @@ class CanvasImmediateRenderer extends VectorContext {
const context = this.context_;
const contextStrokeState = this.contextStrokeState_;
if (!contextStrokeState) {
context.lineCap = 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

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

View File

@@ -2,7 +2,7 @@
* @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';
@@ -95,7 +95,7 @@ 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;
@@ -104,7 +104,7 @@ class CompositeMapRenderer extends MapRenderer {
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;
}
@@ -142,6 +142,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 +150,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

@@ -63,6 +63,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 +151,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;
@@ -377,8 +384,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
return;
}
const uid = getUid(this);
const tileAlpha = transition ? tile.getAlpha(uid, frameState.time) : 1;
const alpha = opacity * tileAlpha;
const alpha = opacity * (transition ? tile.getAlpha(uid, frameState.time) : 1);
const alphaChanged = alpha !== this.context.globalAlpha;
if (alphaChanged) {
this.context.save();
@@ -390,7 +396,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
if (alphaChanged) {
this.context.restore();
}
if (tileAlpha !== 1) {
if (alpha !== 1) {
frameState.animate = true;
} else if (transition) {
tile.endTransition(uid);

View File

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

View File

@@ -24,7 +24,6 @@ import {
makeInverse
} from '../../transform.js';
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import {clear} from '../../obj.js';
/**
@@ -113,6 +112,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
this.tmpTransform_ = createTransform();
// Use nearest lower resolution.
this.zDirection = 1;
}
/**
@@ -342,13 +343,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 +360,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 +378,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc
*/
handleFontsChanged() {
clear(this.renderTileImageQueue_);
const layer = this.getLayer();
if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) {
layer.changed();

View File

@@ -81,7 +81,6 @@ class WebGLLayerRenderer extends LayerRenderer {
getShaderCompileErrors() {
return this.helper.getShaderCompileErrors();
}
}
@@ -232,47 +231,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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

48
src/ol/webgl/Shader.js Normal file
View File

@@ -0,0 +1,48 @@
/**
* @module ol/webgl/Shader
*/
import {abstract} from '../util.js';
/**
* @abstract
*/
class WebGLShader {
/**
* @param {string} source Source.
*/
constructor(source) {
/**
* @private
* @type {string}
*/
this.source_ = source;
}
/**
* @return {boolean} Is animated?
*/
isAnimated() {
return false;
}
/**
* @abstract
* @return {number} Type.
*/
getType() {
return abstract();
}
/**
* @return {string} Source.
*/
getSource() {
return this.source_;
}
}
export default WebGLShader;

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