Merge pull request #12008 from tschaub/gl
Rendering raster tiles with WebGL
This commit is contained in:
@@ -27,7 +27,8 @@
|
|||||||
<a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a><br>
|
<a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a><br>
|
||||||
<a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a><br>
|
<a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a><br>
|
||||||
<a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a><br>
|
<a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a><br>
|
||||||
<a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a>
|
<a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a><br>
|
||||||
|
<a href="module-ol_layer_WebGLTile-WebGLTileLayer.html">ol/layer/WebGLTile</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
<div class="card h-100 bg-light">
|
<div class="card h-100 bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title">Sources and formats</h4>
|
<h4 class="card-title">Sources and formats</h4>
|
||||||
<a href="module-ol_source_Tile-TileSource.html">Tile sources</a> for <a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a>
|
<a href="module-ol_source_Tile-TileSource.html">Tile sources</a> for <a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a> or <a href="module-ol_layer_WebGLTile-WebGLTileLayer.html">ol/layer/WebGLTile</a>
|
||||||
<br><a href="module-ol_source_Image-ImageSource.html">Image sources</a> for <a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a>
|
<br><a href="module-ol_source_Image-ImageSource.html">Image sources</a> for <a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a>
|
||||||
<br><a href="module-ol_source_Vector-VectorSource.html">Vector sources</a> for <a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a>
|
<br><a href="module-ol_source_Vector-VectorSource.html">Vector sources</a> for <a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a>
|
||||||
<br><a href="module-ol_source_VectorTile-VectorTile.html">Vector tile sources</a> for <a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a>
|
<br><a href="module-ol_source_VectorTile-VectorTile.html">Vector tile sources</a> for <a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a>
|
||||||
@@ -71,7 +72,7 @@
|
|||||||
<div class="card h-100 bg-light">
|
<div class="card h-100 bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title">Projections</h4>
|
<h4 class="card-title">Projections</h4>
|
||||||
<p>All coordinates and extents need to be provided in view projection (default: EPSG:3857). To transform, use <a href="module-ol_proj.html#.transform">ol/proj#transform()</a> and <a href="module-ol_proj.html#.transformExtent">ol/proj#transformExtent()</a>.</p>
|
<p>All coordinates and extents need to be provided in view projection (default: EPSG:3857). To transform coordinates from and to geographic, use <a href="module-ol_proj.html#.fromLonLat">ol/proj#fromLonLat()</a> and <a href="module-ol_proj.html#.toLonLat">ol/proj#toLonLat()</a>. For extents and other projections, use <a href="module-ol_proj.html#.transformExtent">ol/proj#transformExtent()</a> and <a href="module-ol_proj.html#.transform">ol/proj#transform()</a>.<p>
|
||||||
<a href="module-ol_proj.html">ol/proj</a>
|
<a href="module-ol_proj.html">ol/proj</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ export default {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
fallback: {
|
||||||
|
fs: false,
|
||||||
|
http: false,
|
||||||
|
https: false,
|
||||||
|
},
|
||||||
alias: {
|
alias: {
|
||||||
ol: path.resolve('./build/ol'),
|
ol: path.resolve('./build/ol'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: NDVI+NDWI from two 16-bit COGs
|
||||||
|
shortdesc: Calculating NDVI+NDWI as green and blue values.
|
||||||
|
docs: >
|
||||||
|
The GeoTIFF layer in this example calculates the Normalized Difference Vegetation Index (NDVI)
|
||||||
|
and Normalized Difference Water Index (NDWI) from two cloud-optimized Sentinel 2 GeoTIFFs: one
|
||||||
|
with 10 m resolution and red and a near infrared bands, and one with 60 m resolution and a short
|
||||||
|
wave infrared channel. The NDVI is shown as green, the NDWI as blue. The 4th band is the alpha
|
||||||
|
band, which gets added when a source has a `nodata` value configured.
|
||||||
|
tags: "cog, ndvi, ndwi, sentinel, geotiff"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import GeoTIFF from '../src/ol/source/GeoTIFF.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
|
||||||
|
const source = new GeoTIFF({
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R10m.tif',
|
||||||
|
bands: [2, 3],
|
||||||
|
min: 0,
|
||||||
|
nodata: 0,
|
||||||
|
max: 65535,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: 'https://s2downloads.eox.at/demo/Sentinel-2/3857/R60m.tif',
|
||||||
|
bands: [8],
|
||||||
|
min: 0,
|
||||||
|
nodata: 0,
|
||||||
|
max: 65535,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
source.setAttributions(
|
||||||
|
"<a href='https://s2maps.eu'>Sentinel-2 cloudless</a> by <a href='https://eox.at/'>EOX IT Services GmbH</a> (Contains modified Copernicus Sentinel data 2019)"
|
||||||
|
);
|
||||||
|
|
||||||
|
const ndvi = [
|
||||||
|
'/',
|
||||||
|
['-', ['band', 2], ['band', 1]],
|
||||||
|
['+', ['band', 2], ['band', 1]],
|
||||||
|
];
|
||||||
|
|
||||||
|
const ndwi = [
|
||||||
|
'/',
|
||||||
|
['-', ['band', 3], ['band', 1]],
|
||||||
|
['+', ['band', 3], ['band', 1]],
|
||||||
|
];
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
style: {
|
||||||
|
color: [
|
||||||
|
'color',
|
||||||
|
// red: | NDVI - NDWI |
|
||||||
|
['*', 255, ['abs', ['-', ndvi, ndwi]]],
|
||||||
|
// green: NDVI
|
||||||
|
['*', 255, ndvi],
|
||||||
|
// blue: NDWI
|
||||||
|
['*', 255, ndwi],
|
||||||
|
// alpha
|
||||||
|
['band', 4],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: [1900000, 6100000],
|
||||||
|
zoom: 13,
|
||||||
|
minZoom: 10,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: NDVI from a Sentinel 2 COG
|
||||||
|
shortdesc: Calculating NDVI and applying a custom color map.
|
||||||
|
docs: >
|
||||||
|
The GeoTIFF layer in this example draws from two Sentinel 2 sources: a red band and a near infrared band.
|
||||||
|
The layer style includes a `color` expression that calculates the Normalized Difference Vegetation Index (NDVI)
|
||||||
|
from values in the two bands. The `interpolate` expression is used to map NDVI values to colors.
|
||||||
|
tags: "cog, ndvi"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import GeoTIFF from '../src/ol/source/GeoTIFF.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import Projection from '../src/ol/proj/Projection.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import proj4 from 'proj4';
|
||||||
|
import {getCenter} from '../src/ol/extent.js';
|
||||||
|
import {register} from '../src/ol/proj/proj4.js';
|
||||||
|
|
||||||
|
proj4.defs('EPSG:32636', '+proj=utm +zone=36 +datum=WGS84 +units=m +no_defs');
|
||||||
|
register(proj4);
|
||||||
|
|
||||||
|
const projection = new Projection({
|
||||||
|
code: 'EPSG:32636',
|
||||||
|
extent: [166021.44, 0.0, 534994.66, 9329005.18],
|
||||||
|
});
|
||||||
|
|
||||||
|
// metadata from https://s3.us-west-2.amazonaws.com/sentinel-cogs/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/S2A_36QWD_20200701_0_L2A.json
|
||||||
|
const sourceExtent = [499980, 1790220, 609780, 1900020];
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
style: {
|
||||||
|
color: [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
// calculate NDVI, bands come from the sources below
|
||||||
|
[
|
||||||
|
'/',
|
||||||
|
['-', ['band', 2], ['band', 1]],
|
||||||
|
['+', ['band', 2], ['band', 1]],
|
||||||
|
],
|
||||||
|
// color ramp for NDVI values, ranging from -1 to 1
|
||||||
|
-0.2,
|
||||||
|
[191, 191, 191],
|
||||||
|
-0.1,
|
||||||
|
[219, 219, 219],
|
||||||
|
0,
|
||||||
|
[255, 255, 224],
|
||||||
|
0.025,
|
||||||
|
[255, 250, 204],
|
||||||
|
0.05,
|
||||||
|
[237, 232, 181],
|
||||||
|
0.075,
|
||||||
|
[222, 217, 156],
|
||||||
|
0.1,
|
||||||
|
[204, 199, 130],
|
||||||
|
0.125,
|
||||||
|
[189, 184, 107],
|
||||||
|
0.15,
|
||||||
|
[176, 194, 97],
|
||||||
|
0.175,
|
||||||
|
[163, 204, 89],
|
||||||
|
0.2,
|
||||||
|
[145, 191, 82],
|
||||||
|
0.25,
|
||||||
|
[128, 179, 71],
|
||||||
|
0.3,
|
||||||
|
[112, 163, 64],
|
||||||
|
0.35,
|
||||||
|
[97, 150, 54],
|
||||||
|
0.4,
|
||||||
|
[79, 138, 46],
|
||||||
|
0.45,
|
||||||
|
[64, 125, 36],
|
||||||
|
0.5,
|
||||||
|
[48, 110, 28],
|
||||||
|
0.55,
|
||||||
|
[33, 97, 18],
|
||||||
|
0.6,
|
||||||
|
[15, 84, 10],
|
||||||
|
0.65,
|
||||||
|
[0, 69, 0],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: new GeoTIFF({
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
// visible red, band 1 in the style expression above
|
||||||
|
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B04.tif',
|
||||||
|
max: 10000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// near infrared, band 2 in the style expression above
|
||||||
|
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/B08.tif',
|
||||||
|
max: 10000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
extent: sourceExtent,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: getCenter(sourceExtent),
|
||||||
|
extent: sourceExtent,
|
||||||
|
zoom: 9,
|
||||||
|
projection: projection,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: GeoTIFF with Overviews
|
||||||
|
shortdesc: Rendering a GeoTIFF with external overviews as a layer.
|
||||||
|
docs: >
|
||||||
|
In some cases, a GeoTIFF may have external overviews. This example uses the
|
||||||
|
`overviews` property to provide URLs for the external overviews. The example
|
||||||
|
composes a false color composite using shortwave infrared (B6), near infrared (B5),
|
||||||
|
and visible green (B3) bands from a Landsat 8 image.
|
||||||
|
tags: "cog"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import GeoTIFF from '../src/ol/source/GeoTIFF.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import Projection from '../src/ol/proj/Projection.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import proj4 from 'proj4';
|
||||||
|
import {getCenter} from '../src/ol/extent.js';
|
||||||
|
import {register} from '../src/ol/proj/proj4.js';
|
||||||
|
|
||||||
|
proj4.defs('EPSG:32645', '+proj=utm +zone=45 +datum=WGS84 +units=m +no_defs');
|
||||||
|
register(proj4);
|
||||||
|
|
||||||
|
const projection = new Projection({
|
||||||
|
code: 'EPSG:32645',
|
||||||
|
extent: [166021.44, 0.0, 534994.66, 9329005.18],
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourceExtent = [382200, 2279370, 610530, 2512500];
|
||||||
|
|
||||||
|
const base =
|
||||||
|
'https://landsat-pds.s3.amazonaws.com/c1/L8/139/045/LC08_L1TP_139045_20170304_20170316_01_T1/LC08_L1TP_139045_20170304_20170316_01_T1';
|
||||||
|
|
||||||
|
// scale values in this range to 0 - 1
|
||||||
|
const min = 10000;
|
||||||
|
const max = 15000;
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
extent: sourceExtent,
|
||||||
|
style: {
|
||||||
|
saturation: -0.3,
|
||||||
|
},
|
||||||
|
source: new GeoTIFF({
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
url: `${base}_B6.TIF`,
|
||||||
|
overviews: [`${base}_B6.TIF.ovr`],
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
nodata: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: `${base}_B5.TIF`,
|
||||||
|
overviews: [`${base}_B5.TIF.ovr`],
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
nodata: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: `${base}_B3.TIF`,
|
||||||
|
overviews: [`${base}_B3.TIF.ovr`],
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
nodata: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: getCenter(sourceExtent),
|
||||||
|
extent: sourceExtent,
|
||||||
|
zoom: 8,
|
||||||
|
projection: projection,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: GeoTIFF tile pyramid
|
||||||
|
shortdesc: Rendering a COG tile pyramid as layer group.
|
||||||
|
docs: >
|
||||||
|
Data from a Cloud Optimized GeoTIFF (COG) tile pyramid can be rendered as a set of layers. In this
|
||||||
|
example, a pyramid of 3-band GeoTIFFs is used to render RGB data. For each tile of the pyramid, a
|
||||||
|
separate layer is created on demand. The lowest resolution layer serves as preview while higher resolutions are
|
||||||
|
loading.
|
||||||
|
tags: "cog, tilepyramid, stac"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import GeoTIFF from '../src/ol/source/GeoTIFF.js';
|
||||||
|
import LayerGroup from '../src/ol/layer/Group.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import TileGrid from '../src/ol/tilegrid/TileGrid.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import WebGLTileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import {getIntersection} from '../src/ol/extent.js';
|
||||||
|
|
||||||
|
// Metadata from https://s2downloads.eox.at/demo/EOxCloudless/2019/rgb/2019_EOxCloudless_rgb.json
|
||||||
|
|
||||||
|
// Tile grid of the GeoTIFF pyramid layout
|
||||||
|
const tileGrid = new TileGrid({
|
||||||
|
origin: [-180, 90],
|
||||||
|
resolutions: [0.703125, 0.3515625, 0.17578125, 8.7890625e-2, 4.39453125e-2],
|
||||||
|
tileSizes: [
|
||||||
|
[512, 256],
|
||||||
|
[1024, 512],
|
||||||
|
[2048, 1024],
|
||||||
|
[4096, 2048],
|
||||||
|
[4096, 4096],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const pyramid = new LayerGroup();
|
||||||
|
const layerForUrl = {};
|
||||||
|
const zs = tileGrid.getResolutions().length;
|
||||||
|
|
||||||
|
function useLayer(z, x, y) {
|
||||||
|
const url = `https://s2downloads.eox.at/demo/EOxCloudless/2019/rgb/${z}/${y}/${x}.tif`;
|
||||||
|
if (!(url in layerForUrl)) {
|
||||||
|
pyramid.getLayers().push(
|
||||||
|
new WebGLTileLayer({
|
||||||
|
minZoom: z,
|
||||||
|
maxZoom: z === 0 || z === zs - 1 ? undefined : z + 1,
|
||||||
|
extent: tileGrid.getTileCoordExtent([z, x, y]),
|
||||||
|
source: new GeoTIFF({
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
url: url,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
layerForUrl[url] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [pyramid],
|
||||||
|
view: new View({
|
||||||
|
projection: 'EPSG:4326',
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 0,
|
||||||
|
showFullExtent: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add overview layer
|
||||||
|
useLayer(0, 0, 0);
|
||||||
|
|
||||||
|
// Add layer for specific extent on demand
|
||||||
|
map.on('moveend', () => {
|
||||||
|
const view = map.getView();
|
||||||
|
tileGrid.forEachTileCoord(
|
||||||
|
getIntersection([-180, -90, 180, 90], view.calculateExtent()),
|
||||||
|
tileGrid.getZForResolution(view.getResolution()),
|
||||||
|
([z, x, y]) => useLayer(z, x, y)
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Cloud Optimized GeoTIFF (COG)
|
||||||
|
shortdesc: Rendering a COG as a tiled layer.
|
||||||
|
docs: >
|
||||||
|
Tiled data from a Cloud Optimized GeoTIFF (COG) can be rendered as a layer. In this
|
||||||
|
example, a single 3-band GeoTIFF is used to render RGB data.
|
||||||
|
tags: "cog"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import GeoTIFF from '../src/ol/source/GeoTIFF.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import Projection from '../src/ol/proj/Projection.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import proj4 from 'proj4';
|
||||||
|
import {getCenter} from '../src/ol/extent.js';
|
||||||
|
import {register} from '../src/ol/proj/proj4.js';
|
||||||
|
|
||||||
|
proj4.defs('EPSG:32636', '+proj=utm +zone=36 +datum=WGS84 +units=m +no_defs');
|
||||||
|
register(proj4);
|
||||||
|
|
||||||
|
const projection = new Projection({
|
||||||
|
code: 'EPSG:32636',
|
||||||
|
extent: [166021.44, 0.0, 534994.66, 9329005.18],
|
||||||
|
});
|
||||||
|
|
||||||
|
// metadata from https://s3.us-west-2.amazonaws.com/sentinel-cogs/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/S2A_36QWD_20200701_0_L2A.json
|
||||||
|
const sourceExtent = [499980, 1790220, 609780, 1900020];
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new GeoTIFF({
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/2020/S2A_36QWD_20200701_0_L2A/TCI.tif',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
extent: sourceExtent,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: getCenter(sourceExtent),
|
||||||
|
extent: sourceExtent,
|
||||||
|
zoom: 9,
|
||||||
|
projection: projection,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Data Tiles
|
||||||
|
shortdesc: Generating tile data from scratch.
|
||||||
|
docs: >
|
||||||
|
This example generates RGBA tile data from scratch.
|
||||||
|
tags: "data tiles"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import DataTile from '../src/ol/source/DataTile.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
|
||||||
|
const size = 256;
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = size;
|
||||||
|
canvas.height = size;
|
||||||
|
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.strokeStyle = 'white';
|
||||||
|
context.textAlign = 'center';
|
||||||
|
context.font = '24px sans-serif';
|
||||||
|
const lineHeight = 30;
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new DataTile({
|
||||||
|
loader: function (z, x, y) {
|
||||||
|
const half = size / 2;
|
||||||
|
context.clearRect(0, 0, size, size);
|
||||||
|
context.fillStyle = 'rgba(100, 100, 100, 0.5)';
|
||||||
|
context.fillRect(0, 0, size, size);
|
||||||
|
context.fillStyle = 'black';
|
||||||
|
context.fillText(`z: ${z}`, half, half - lineHeight);
|
||||||
|
context.fillText(`x: ${x}`, half, half);
|
||||||
|
context.fillText(`y: ${y}`, half, half + lineHeight);
|
||||||
|
context.strokeRect(0, 0, size, size);
|
||||||
|
const data = context.getImageData(0, 0, size, size).data;
|
||||||
|
return Promise.resolve(data);
|
||||||
|
},
|
||||||
|
// disable opacity transition to avoid overlapping labels during tile loading
|
||||||
|
transition: 0,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#level {
|
||||||
|
display: inline-block;
|
||||||
|
width: 150px;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.location {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#map {
|
||||||
|
background: #8bd4ff;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Sea Level (with WebGL)
|
||||||
|
shortdesc: Render sea level at different elevations
|
||||||
|
docs: >
|
||||||
|
<p>
|
||||||
|
The <code>style</code> property of a WebGL tile layer accepts a <code>color</code> expression that
|
||||||
|
can be used to modify pixel values before rendering. Here, RGB tiles representing elevation
|
||||||
|
data are loaded and rendered so that values at or below sea level are blue, and values
|
||||||
|
above sea level are transparent. The <code>color</code> expression operates on normalized pixel
|
||||||
|
values ranging from 0 to 1. The <code>band</code> operator is used to select normalized values
|
||||||
|
from a single band.
|
||||||
|
</p><p>
|
||||||
|
After converting the normalized RGB values to elevation, the <code>interpolate</code> expression
|
||||||
|
is used to pick colors to apply at a given elevation. Instead of using constant
|
||||||
|
numeric values as the stops in the colors array, the <code>var</code> operator allows you to
|
||||||
|
use a value that can be modified by your application. When the user drags the
|
||||||
|
sea level slider, the <code>layer.updateStyleVariables()</code> function is called to update
|
||||||
|
the <code>level</code> style variable with the value from the slider.
|
||||||
|
</p>
|
||||||
|
tags: "webgl, math, flood"
|
||||||
|
cloak:
|
||||||
|
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||||
|
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
<label>
|
||||||
|
Sea level
|
||||||
|
<input id="level" type="range" min="0" max="100" value="1"/>
|
||||||
|
+<span id="output"></span> m
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
Go to
|
||||||
|
<a class="location" data-center="-122.3267,37.8377" data-zoom="11">San Francisco</a>,
|
||||||
|
<a class="location" data-center="-73.9338,40.6861" data-zoom="11">New York</a>,
|
||||||
|
<a class="location" data-center="72.9481,18.9929" data-zoom="11">Mumbai</a>, or
|
||||||
|
<a class="location" data-center="120.831,31.160" data-zoom="9">Shanghai</a>
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import XYZ from '../src/ol/source/XYZ.js';
|
||||||
|
import {fromLonLat} from '../src/ol/proj.js';
|
||||||
|
|
||||||
|
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||||
|
const attributions =
|
||||||
|
'<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> ' +
|
||||||
|
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>';
|
||||||
|
|
||||||
|
const elevation = new TileLayer({
|
||||||
|
opacity: 0.6,
|
||||||
|
source: new XYZ({
|
||||||
|
url:
|
||||||
|
'https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=' + key,
|
||||||
|
maxZoom: 10,
|
||||||
|
tileSize: 512,
|
||||||
|
crossOrigin: 'anonymous',
|
||||||
|
}),
|
||||||
|
style: {
|
||||||
|
variables: {
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
color: [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
// band math operates on normalized values from 0-1
|
||||||
|
// so we scale by 255 to align with the elevation formula
|
||||||
|
// from https://cloud.maptiler.com/tiles/terrain-rgb/
|
||||||
|
[
|
||||||
|
'+',
|
||||||
|
-10000,
|
||||||
|
[
|
||||||
|
'*',
|
||||||
|
0.1 * 255,
|
||||||
|
[
|
||||||
|
'+',
|
||||||
|
['*', 256 * 256, ['band', 1]],
|
||||||
|
['+', ['*', 256, ['band', 2]], ['band', 3]],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
// use the `level` style variable as a stop in the color ramp
|
||||||
|
['var', 'level'],
|
||||||
|
[139, 212, 255, 1],
|
||||||
|
['+', 0.01, ['var', 'level']],
|
||||||
|
[139, 212, 255, 0],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new XYZ({
|
||||||
|
url: 'https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=' + key,
|
||||||
|
attributions: attributions,
|
||||||
|
crossOrigin: 'anonymous',
|
||||||
|
tileSize: 512,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
elevation,
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: fromLonLat([-122.3267, 37.8377]),
|
||||||
|
zoom: 11,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const control = document.getElementById('level');
|
||||||
|
const output = document.getElementById('output');
|
||||||
|
control.addEventListener('input', function () {
|
||||||
|
output.innerText = control.value;
|
||||||
|
elevation.updateStyleVariables({level: parseFloat(control.value)});
|
||||||
|
});
|
||||||
|
output.innerText = control.value;
|
||||||
|
|
||||||
|
const locations = document.getElementsByClassName('location');
|
||||||
|
for (let i = 0, ii = locations.length; i < ii; ++i) {
|
||||||
|
locations[i].addEventListener('click', relocate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function relocate(event) {
|
||||||
|
const data = event.target.dataset;
|
||||||
|
const view = map.getView();
|
||||||
|
view.setCenter(fromLonLat(data.center.split(',').map(Number)));
|
||||||
|
view.setZoom(Number(data.zoom));
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
table.controls td {
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
table.controls td:nth-child(3) {
|
||||||
|
text-align: right;
|
||||||
|
min-width: 3em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Shaded Relief (with WebGL)
|
||||||
|
shortdesc: Calculate shaded relief from elevation data
|
||||||
|
docs: >
|
||||||
|
<p>
|
||||||
|
For the shaded relief, a single tiled source of elevation data is used as input.
|
||||||
|
The shaded relief is calculated by the layer's <code>style</code> with a <code>color</code>
|
||||||
|
expression. The style variables are updated when the user drags one of the sliders. The
|
||||||
|
<code>band</code> operator is used to sample data from neighboring pixels for calculating slope and
|
||||||
|
aspect, which is done with the <code>['band', bandIndex, xOffset, yOffset]</code> syntax.
|
||||||
|
</p>
|
||||||
|
tags: "webgl, shaded relief"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
<table class="controls">
|
||||||
|
<tr>
|
||||||
|
<td><label for="vert">vertical exaggeration:</label></td>
|
||||||
|
<td><input id="vert" type="range" min="1" max="5" value="1"/></td>
|
||||||
|
<td><span id="vertOut"></span> x</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="sunEl">sun elevation:</label></td>
|
||||||
|
<td><input id="sunEl" type="range" min="0" max="90" value="45"/></td>
|
||||||
|
<td><span id="sunElOut"></span> °</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="sunAz">sun azimuth:</label></td>
|
||||||
|
<td><input id="sunAz" type="range" min="0" max="360" value="45"/></td>
|
||||||
|
<td><span id="sunAzOut"></span> °</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import {OSM, XYZ} from '../src/ol/source.js';
|
||||||
|
import {WebGLTile as TileLayer} from '../src/ol/layer.js';
|
||||||
|
|
||||||
|
const variables = {};
|
||||||
|
|
||||||
|
// The method used to extract elevations from the DEM.
|
||||||
|
// In this case the format used is
|
||||||
|
// red + green * 2 + blue * 3
|
||||||
|
//
|
||||||
|
// Other frequently used methods include the Mapbox format
|
||||||
|
// (red * 256 * 256 + green * 256 + blue) * 0.1 - 10000
|
||||||
|
// and the Terrarium format
|
||||||
|
// (red * 256 + green + blue / 256) - 32768
|
||||||
|
function elevation(xOffset, yOffset) {
|
||||||
|
return [
|
||||||
|
'+',
|
||||||
|
['*', 256, ['band', 1, xOffset, yOffset]],
|
||||||
|
[
|
||||||
|
'+',
|
||||||
|
['*', 2 * 256, ['band', 2, xOffset, yOffset]],
|
||||||
|
['*', 3 * 256, ['band', 3, xOffset, yOffset]],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a shaded relief image given elevation data. Uses a 3x3
|
||||||
|
// neighborhood for determining slope and aspect.
|
||||||
|
const dp = ['*', 2, ['resolution']];
|
||||||
|
const z0x = ['*', ['var', 'vert'], elevation(-1, 0)];
|
||||||
|
const z1x = ['*', ['var', 'vert'], elevation(1, 0)];
|
||||||
|
const dzdx = ['/', ['-', z1x, z0x], dp];
|
||||||
|
const z0y = ['*', ['var', 'vert'], elevation(0, -1)];
|
||||||
|
const z1y = ['*', ['var', 'vert'], elevation(0, 1)];
|
||||||
|
const dzdy = ['/', ['-', z1y, z0y], dp];
|
||||||
|
const slope = ['atan', ['^', ['+', ['^', dzdx, 2], ['^', dzdy, 2]], 0.5]];
|
||||||
|
const aspect = ['clamp', ['atan', ['-', 0, dzdx], dzdy], -Math.PI, Math.PI];
|
||||||
|
const sunEl = ['*', Math.PI / 180, ['var', 'sunEl']];
|
||||||
|
const sunAz = ['*', Math.PI / 180, ['var', 'sunAz']];
|
||||||
|
|
||||||
|
const cosIncidence = [
|
||||||
|
'+',
|
||||||
|
['*', ['sin', sunEl], ['cos', slope]],
|
||||||
|
['*', ['*', ['cos', sunEl], ['sin', slope]], ['cos', ['-', sunAz, aspect]]],
|
||||||
|
];
|
||||||
|
const scaled = ['*', 255, cosIncidence];
|
||||||
|
|
||||||
|
const shadedRelief = new TileLayer({
|
||||||
|
opacity: 0.3,
|
||||||
|
source: new XYZ({
|
||||||
|
url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png',
|
||||||
|
crossOrigin: 'anonymous',
|
||||||
|
}),
|
||||||
|
style: {
|
||||||
|
variables: variables,
|
||||||
|
color: ['color', scaled, scaled, scaled],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const controlIds = ['vert', 'sunEl', 'sunAz'];
|
||||||
|
controlIds.forEach(function (id) {
|
||||||
|
const control = document.getElementById(id);
|
||||||
|
const output = document.getElementById(id + 'Out');
|
||||||
|
function updateValues() {
|
||||||
|
output.innerText = control.value;
|
||||||
|
variables[id] = Number(control.value);
|
||||||
|
}
|
||||||
|
updateValues();
|
||||||
|
control.addEventListener('input', () => {
|
||||||
|
updateValues();
|
||||||
|
shadedRelief.updateStyleVariables(variables);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new OSM(),
|
||||||
|
}),
|
||||||
|
shadedRelief,
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
extent: [-13675026, 4439648, -13580856, 4580292],
|
||||||
|
center: [-13615645, 4497969],
|
||||||
|
minZoom: 10,
|
||||||
|
maxZoom: 16,
|
||||||
|
zoom: 13,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: WebGL Tile Layer Styles
|
||||||
|
shortdesc: Styling raster tiles with WebGL.
|
||||||
|
docs: >
|
||||||
|
The `style` property of a WebGL tile layer can be used to adjust properties like
|
||||||
|
`exposure`, `contrast`, and `saturation`. Typically those values would be set to
|
||||||
|
numeric constants to apply a filter to imagery. In this example, the style properties
|
||||||
|
are set to variables that can be updated based on application state. Adjusting the
|
||||||
|
sliders results in a call to `layer.updateStyleVariables()` to use new values for the
|
||||||
|
associated style properties.
|
||||||
|
tags: "webgl, style"
|
||||||
|
cloak:
|
||||||
|
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||||
|
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
<div id="controls">
|
||||||
|
<label>
|
||||||
|
<input id="exposure" type="range" min="-0.5" max="0.5" step="0.01">
|
||||||
|
<br>exposure <span id="exposure-value"></span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input id="contrast" type="range" min="-0.5" max="0.5" step="0.01">
|
||||||
|
<br>contrast <span id="contrast-value"></span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input id="saturation" type="range" min="-0.5" max="0.5" step="0.01">
|
||||||
|
<br>saturation <span id="saturation-value"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import XYZ from '../src/ol/source/XYZ.js';
|
||||||
|
|
||||||
|
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||||
|
const attributions =
|
||||||
|
'<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> ' +
|
||||||
|
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>';
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
exposure: 0,
|
||||||
|
contrast: 0,
|
||||||
|
saturation: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const layer = new TileLayer({
|
||||||
|
style: {
|
||||||
|
exposure: ['var', 'exposure'],
|
||||||
|
contrast: ['var', 'contrast'],
|
||||||
|
saturation: ['var', 'saturation'],
|
||||||
|
variables: variables,
|
||||||
|
},
|
||||||
|
source: new XYZ({
|
||||||
|
crossOrigin: 'anonymous', // TODO: determine if we can avoid this
|
||||||
|
attributions: attributions,
|
||||||
|
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
|
||||||
|
maxZoom: 20,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [layer],
|
||||||
|
view: new View({
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const name in variables) {
|
||||||
|
const element = document.getElementById(name);
|
||||||
|
const value = variables[name];
|
||||||
|
element.value = value.toString();
|
||||||
|
document.getElementById(`${name}-value`).innerText = `(${value})`;
|
||||||
|
|
||||||
|
element.addEventListener('input', function (event) {
|
||||||
|
const value = parseFloat(event.target.value);
|
||||||
|
document.getElementById(`${name}-value`).innerText = `(${value})`;
|
||||||
|
|
||||||
|
const updates = {};
|
||||||
|
updates[name] = value;
|
||||||
|
layer.updateStyleVariables(updates);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: WebGL Tiles
|
||||||
|
shortdesc: Rendering raster data with WebGL.
|
||||||
|
docs: >
|
||||||
|
This example uses WebGL to raster tiles on a map.
|
||||||
|
tags: "webgl, osm"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import OSM from '../src/ol/source/OSM.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new OSM(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -101,6 +101,8 @@ export default {
|
|||||||
resolve: {
|
resolve: {
|
||||||
fallback: {
|
fallback: {
|
||||||
fs: false,
|
fs: false,
|
||||||
|
http: false,
|
||||||
|
https: false,
|
||||||
},
|
},
|
||||||
alias: {
|
alias: {
|
||||||
// allow imports from 'ol/module' instead of specifiying the source path
|
// allow imports from 'ol/module' instead of specifiying the source path
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ module.exports = function loader() {
|
|||||||
build(this.resource, {minify})
|
build(this.resource, {minify})
|
||||||
.then((chunk) => {
|
.then((chunk) => {
|
||||||
for (const filePath in chunk.modules) {
|
for (const filePath in chunk.modules) {
|
||||||
this.addDependency(filePath);
|
try {
|
||||||
|
const dependency = require.resolve(filePath);
|
||||||
|
this.addDependency(dependency);
|
||||||
|
} catch (e) {
|
||||||
|
// empty catch block
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callback(null, chunk.code);
|
callback(null, chunk.code);
|
||||||
})
|
})
|
||||||
|
|||||||
Generated
+232
-40
@@ -9,6 +9,7 @@
|
|||||||
"version": "6.6.2-dev",
|
"version": "6.6.2-dev",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"geotiff": "^1.0.4",
|
||||||
"ol-mapbox-style": "^6.4.1",
|
"ol-mapbox-style": "^6.4.1",
|
||||||
"pbf": "3.2.1",
|
"pbf": "3.2.1",
|
||||||
"rbush": "^3.0.1"
|
"rbush": "^3.0.1"
|
||||||
@@ -62,6 +63,7 @@
|
|||||||
"pngjs": "^6.0.0",
|
"pngjs": "^6.0.0",
|
||||||
"proj4": "^2.7.5",
|
"proj4": "^2.7.5",
|
||||||
"puppeteer": "10.2.0",
|
"puppeteer": "10.2.0",
|
||||||
|
"regenerator-runtime": "^0.13.9",
|
||||||
"rollup": "^2.42.3",
|
"rollup": "^2.42.3",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"serve-static": "^1.14.0",
|
"serve-static": "^1.14.0",
|
||||||
@@ -1837,6 +1839,15 @@
|
|||||||
"eslint": ">=5.1.0"
|
"eslint": ">=5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@petamoriken/float16": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-0r8nE5Q60tj3FbWWYLjAdGnWZgP7CMWXNaI5UsNzypRyxLDb/uvOl5SDw8GcPNu6pSTOt+KSI+0oL6fhSpNOFQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": ">=4.17.5 <5.0.0",
|
||||||
|
"lodash-es": ">=4.17.5 <5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
||||||
@@ -2968,7 +2979,6 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
@@ -3336,6 +3346,11 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/content-type-parser": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ=="
|
||||||
|
},
|
||||||
"node_modules/convert-source-map": {
|
"node_modules/convert-source-map": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
|
||||||
@@ -3576,7 +3591,6 @@
|
|||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
},
|
},
|
||||||
@@ -4659,6 +4673,15 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/esm": {
|
||||||
|
"version": "3.2.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
|
||||||
|
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/espree": {
|
"node_modules/espree": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
|
||||||
@@ -5330,6 +5353,24 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/geotiff": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/geotiff/-/geotiff-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-JmtpvVHlxvyrWgT6Uf0sy7flmXhjWtG0cqVv+G9fMcupV4DAPdTv7tkhsoMnn9RpIIwolveB/VnyII8cRMOD7A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@petamoriken/float16": "^1.0.7",
|
||||||
|
"content-type-parser": "^1.0.2",
|
||||||
|
"lru-cache": "^6.0.0",
|
||||||
|
"pako": "^1.0.11",
|
||||||
|
"parse-headers": "^2.0.2",
|
||||||
|
"threads": "^1.3.1",
|
||||||
|
"txml": "^3.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"browsers": "defaults",
|
||||||
|
"node": ">=10.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-caller-file": {
|
"node_modules/get-caller-file": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
@@ -5924,8 +5965,7 @@
|
|||||||
"node_modules/inherits": {
|
"node_modules/inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/internal-ip": {
|
"node_modules/internal-ip": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
@@ -6190,6 +6230,17 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-observable": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-path-cwd": {
|
"node_modules/is-path-cwd": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
|
||||||
@@ -7123,8 +7174,12 @@
|
|||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
"dev": true
|
},
|
||||||
|
"node_modules/lodash-es": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.clonedeep": {
|
"node_modules/lodash.clonedeep": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
@@ -7292,7 +7347,6 @@
|
|||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
},
|
},
|
||||||
@@ -7699,8 +7753,7 @@
|
|||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/multicast-dns": {
|
"node_modules/multicast-dns": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.3",
|
||||||
@@ -7947,6 +8000,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/observable-fns": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg=="
|
||||||
|
},
|
||||||
"node_modules/obuf": {
|
"node_modules/obuf": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||||
@@ -8172,6 +8230,11 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@@ -8184,6 +8247,11 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/parse-headers": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA=="
|
||||||
|
},
|
||||||
"node_modules/parse-json": {
|
"node_modules/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
@@ -8926,7 +8994,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"string_decoder": "^1.1.1",
|
"string_decoder": "^1.1.1",
|
||||||
@@ -8979,9 +9046,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.13.7",
|
"version": "0.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
|
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/regenerator-transform": {
|
"node_modules/regenerator-transform": {
|
||||||
@@ -9965,7 +10032,6 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.2.0"
|
"safe-buffer": "~5.2.0"
|
||||||
}
|
}
|
||||||
@@ -9974,7 +10040,6 @@
|
|||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -10314,18 +10379,53 @@
|
|||||||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/threads": {
|
||||||
|
"version": "1.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/threads/-/threads-1.6.5.tgz",
|
||||||
|
"integrity": "sha512-yL1NN4qZ25crW8wDoGn7TqbENJ69w3zCEjIGXpbqmQ4I+QHrG8+DLaZVKoX74OQUXWCI2lbbrUxDxAbr1xjDGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"callsites": "^3.1.0",
|
||||||
|
"debug": "^4.2.0",
|
||||||
|
"is-observable": "^2.1.0",
|
||||||
|
"observable-fns": "^0.6.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/andywer/threads.js?sponsor=1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"tiny-worker": ">= 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/through": {
|
"node_modules/through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/through2": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"readable-stream": "2 || 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/thunky": {
|
"node_modules/thunky": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-worker": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"esm": "^3.2.25"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
|
||||||
@@ -10423,6 +10523,14 @@
|
|||||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/txml": {
|
||||||
|
"version": "3.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/txml/-/txml-3.2.5.tgz",
|
||||||
|
"integrity": "sha512-AtN8AgJLiDanttIXJaQlxH8/R0NOCNwto8kcO7BaxdLgsN9b7itM9lnTD7c2O3TadP+hHB9j7ra5XGFRPNnk/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"through2": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
@@ -10630,8 +10738,7 @@
|
|||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/utils-merge": {
|
"node_modules/utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -11374,8 +11481,7 @@
|
|||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/yargs": {
|
"node_modules/yargs": {
|
||||||
"version": "17.1.1",
|
"version": "17.1.1",
|
||||||
@@ -12680,6 +12786,15 @@
|
|||||||
"minimatch": "^3.0.4"
|
"minimatch": "^3.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@petamoriken/float16": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-0r8nE5Q60tj3FbWWYLjAdGnWZgP7CMWXNaI5UsNzypRyxLDb/uvOl5SDw8GcPNu6pSTOt+KSI+0oL6fhSpNOFQ==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": ">=4.17.5 <5.0.0",
|
||||||
|
"lodash-es": ">=4.17.5 <5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rollup/plugin-babel": {
|
"@rollup/plugin-babel": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
||||||
@@ -13604,8 +13719,7 @@
|
|||||||
"callsites": {
|
"callsites": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"camelcase": {
|
"camelcase": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
@@ -13901,6 +14015,11 @@
|
|||||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"content-type-parser": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ=="
|
||||||
|
},
|
||||||
"convert-source-map": {
|
"convert-source-map": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
|
||||||
@@ -14082,7 +14201,6 @@
|
|||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
}
|
}
|
||||||
@@ -14922,6 +15040,12 @@
|
|||||||
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"esm": {
|
||||||
|
"version": "3.2.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
|
||||||
|
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"espree": {
|
"espree": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
|
||||||
@@ -15448,6 +15572,20 @@
|
|||||||
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"geotiff": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/geotiff/-/geotiff-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-JmtpvVHlxvyrWgT6Uf0sy7flmXhjWtG0cqVv+G9fMcupV4DAPdTv7tkhsoMnn9RpIIwolveB/VnyII8cRMOD7A==",
|
||||||
|
"requires": {
|
||||||
|
"@petamoriken/float16": "^1.0.7",
|
||||||
|
"content-type-parser": "^1.0.2",
|
||||||
|
"lru-cache": "^6.0.0",
|
||||||
|
"pako": "^1.0.11",
|
||||||
|
"parse-headers": "^2.0.2",
|
||||||
|
"threads": "^1.3.1",
|
||||||
|
"txml": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"get-caller-file": {
|
"get-caller-file": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
@@ -15895,8 +16033,7 @@
|
|||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"internal-ip": {
|
"internal-ip": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
@@ -16071,6 +16208,11 @@
|
|||||||
"integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
|
"integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-observable": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw=="
|
||||||
|
},
|
||||||
"is-path-cwd": {
|
"is-path-cwd": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
|
||||||
@@ -16802,8 +16944,12 @@
|
|||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
"dev": true
|
},
|
||||||
|
"lodash-es": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
},
|
},
|
||||||
"lodash.clonedeep": {
|
"lodash.clonedeep": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
@@ -16939,7 +17085,6 @@
|
|||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
}
|
}
|
||||||
@@ -17249,8 +17394,7 @@
|
|||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"multicast-dns": {
|
"multicast-dns": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.3",
|
||||||
@@ -17441,6 +17585,11 @@
|
|||||||
"es-abstract": "^1.18.2"
|
"es-abstract": "^1.18.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"observable-fns": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg=="
|
||||||
|
},
|
||||||
"obuf": {
|
"obuf": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||||
@@ -17599,6 +17748,11 @@
|
|||||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
|
},
|
||||||
"parent-module": {
|
"parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@@ -17608,6 +17762,11 @@
|
|||||||
"callsites": "^3.0.0"
|
"callsites": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"parse-headers": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA=="
|
||||||
|
},
|
||||||
"parse-json": {
|
"parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
@@ -18178,7 +18337,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"string_decoder": "^1.1.1",
|
"string_decoder": "^1.1.1",
|
||||||
@@ -18219,9 +18377,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.7",
|
"version": "0.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
|
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"regenerator-transform": {
|
"regenerator-transform": {
|
||||||
@@ -19015,7 +19173,6 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "~5.2.0"
|
"safe-buffer": "~5.2.0"
|
||||||
},
|
},
|
||||||
@@ -19023,8 +19180,7 @@
|
|||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -19274,18 +19430,48 @@
|
|||||||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"threads": {
|
||||||
|
"version": "1.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/threads/-/threads-1.6.5.tgz",
|
||||||
|
"integrity": "sha512-yL1NN4qZ25crW8wDoGn7TqbENJ69w3zCEjIGXpbqmQ4I+QHrG8+DLaZVKoX74OQUXWCI2lbbrUxDxAbr1xjDGQ==",
|
||||||
|
"requires": {
|
||||||
|
"callsites": "^3.1.0",
|
||||||
|
"debug": "^4.2.0",
|
||||||
|
"is-observable": "^2.1.0",
|
||||||
|
"observable-fns": "^0.6.1",
|
||||||
|
"tiny-worker": ">= 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"through": {
|
"through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"through2": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"readable-stream": "2 || 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"thunky": {
|
"thunky": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"tiny-worker": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"esm": "^3.2.25"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
|
||||||
@@ -19364,6 +19550,14 @@
|
|||||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"txml": {
|
||||||
|
"version": "3.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/txml/-/txml-3.2.5.tgz",
|
||||||
|
"integrity": "sha512-AtN8AgJLiDanttIXJaQlxH8/R0NOCNwto8kcO7BaxdLgsN9b7itM9lnTD7c2O3TadP+hHB9j7ra5XGFRPNnk/g==",
|
||||||
|
"requires": {
|
||||||
|
"through2": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"type-check": {
|
"type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
@@ -19518,8 +19712,7 @@
|
|||||||
"util-deprecate": {
|
"util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"utils-merge": {
|
"utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -20044,8 +20237,7 @@
|
|||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "17.1.1",
|
"version": "17.1.1",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
"url": "https://opencollective.com/openlayers"
|
"url": "https://opencollective.com/openlayers"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"geotiff": "^1.0.4",
|
||||||
"ol-mapbox-style": "^6.4.1",
|
"ol-mapbox-style": "^6.4.1",
|
||||||
"pbf": "3.2.1",
|
"pbf": "3.2.1",
|
||||||
"rbush": "^3.0.1"
|
"rbush": "^3.0.1"
|
||||||
@@ -98,6 +99,7 @@
|
|||||||
"pngjs": "^6.0.0",
|
"pngjs": "^6.0.0",
|
||||||
"proj4": "^2.7.5",
|
"proj4": "^2.7.5",
|
||||||
"puppeteer": "10.2.0",
|
"puppeteer": "10.2.0",
|
||||||
|
"regenerator-runtime": "^0.13.9",
|
||||||
"rollup": "^2.42.3",
|
"rollup": "^2.42.3",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"serve-static": "^1.14.0",
|
"serve-static": "^1.14.0",
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* @module ol/DataTile
|
||||||
|
*/
|
||||||
|
import Tile from './Tile.js';
|
||||||
|
import TileState from './TileState.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data that can be used with a DataTile.
|
||||||
|
* @typedef {Uint8Array|Uint8ClampedArray|DataView} Data
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Options
|
||||||
|
* @property {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
|
||||||
|
* @property {function() : Promise<Data>} loader Data loader.
|
||||||
|
* @property {number} [transition=250] A duration for tile opacity
|
||||||
|
* transitions in milliseconds. A duration of 0 disables the opacity transition.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DataTile extends Tile {
|
||||||
|
/**
|
||||||
|
* @param {Options} options Tile options.
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
const state = TileState.IDLE;
|
||||||
|
|
||||||
|
super(options.tileCoord, state, {transition: options.transition});
|
||||||
|
|
||||||
|
this.loader_ = options.loader;
|
||||||
|
this.data_ = null;
|
||||||
|
this.error_ = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data for the tile.
|
||||||
|
* @return {Data} Tile data.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
getData() {
|
||||||
|
return this.data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get any loading error.
|
||||||
|
* @return {Error} Loading error.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
getError() {
|
||||||
|
return this.error_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load not yet loaded URI.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
this.state = TileState.LOADING;
|
||||||
|
this.changed();
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
this.loader_()
|
||||||
|
.then(function (data) {
|
||||||
|
self.data_ = data;
|
||||||
|
self.state = TileState.LOADED;
|
||||||
|
self.changed();
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
self.error_ = error;
|
||||||
|
self.state = TileState.ERROR;
|
||||||
|
self.changed();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataTile;
|
||||||
@@ -13,3 +13,4 @@ export {default as Vector} from './layer/Vector.js';
|
|||||||
export {default as VectorImage} from './layer/VectorImage.js';
|
export {default as VectorImage} from './layer/VectorImage.js';
|
||||||
export {default as VectorTile} from './layer/VectorTile.js';
|
export {default as VectorTile} from './layer/VectorTile.js';
|
||||||
export {default as WebGLPoints} from './layer/WebGLPoints.js';
|
export {default as WebGLPoints} from './layer/WebGLPoints.js';
|
||||||
|
export {default as WebGLTile} from './layer/WebGLTile.js';
|
||||||
|
|||||||
@@ -0,0 +1,309 @@
|
|||||||
|
/**
|
||||||
|
* @module ol/layer/WebGLTile
|
||||||
|
*/
|
||||||
|
import BaseTileLayer from './BaseTile.js';
|
||||||
|
import WebGLTileLayerRenderer, {
|
||||||
|
Attributes,
|
||||||
|
Uniforms,
|
||||||
|
} from '../renderer/webgl/TileLayer.js';
|
||||||
|
import {
|
||||||
|
ValueTypes,
|
||||||
|
expressionToGlsl,
|
||||||
|
getStringNumberEquivalent,
|
||||||
|
uniformNameForVariable,
|
||||||
|
} from '../style/expressions.js';
|
||||||
|
import {assign} from '../obj.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Style
|
||||||
|
* Translates tile data to rendered pixels.
|
||||||
|
*
|
||||||
|
* @property {Object<string, number>} [variables] Style variables. Each variable must hold a number. These
|
||||||
|
* variables can be used in the `color`, `brightness`, `contrast`, `exposure`, `saturation` and `gamma`
|
||||||
|
* {@link import("../style/expressions.js").ExpressionValue expressions}, using the `['var', 'varName']` operator.
|
||||||
|
* To update style variables, use the {@link import("./WebGLTile.js").default#updateStyleVariables} method.
|
||||||
|
* @property {import("../style/expressions.js").ExpressionValue} [color] An expression applied to color values.
|
||||||
|
* @property {import("../style/expressions.js").ExpressionValue} [brightness=0] Value used to decrease or increase
|
||||||
|
* the layer brightness. Values range from -1 to 1.
|
||||||
|
* @property {import("../style/expressions.js").ExpressionValue} [contrast=0] Value used to decrease or increase
|
||||||
|
* the layer contrast. Values range from -1 to 1.
|
||||||
|
* @property {import("../style/expressions.js").ExpressionValue} [exposure=0] Value used to decrease or increase
|
||||||
|
* the layer exposure. Values range from -1 to 1.
|
||||||
|
* @property {import("../style/expressions.js").ExpressionValue} [saturation=0] Value used to decrease or increase
|
||||||
|
* the layer saturation. Values range from -1 to 1.
|
||||||
|
* @property {import("../style/expressions.js").ExpressionValue} [gamma=1] Apply a gamma correction to the layer.
|
||||||
|
* Values range from 0 to infinity.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Options
|
||||||
|
* @property {Style} [style] Style to apply to the layer.
|
||||||
|
* @property {string} [className='ol-layer'] A CSS class name to set to the layer element.
|
||||||
|
* @property {number} [opacity=1] Opacity (0, 1).
|
||||||
|
* @property {boolean} [visible=true] Visibility.
|
||||||
|
* @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be
|
||||||
|
* rendered outside of this extent.
|
||||||
|
* @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers
|
||||||
|
* will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed
|
||||||
|
* for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`
|
||||||
|
* method was used.
|
||||||
|
* @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
|
* be visible.
|
||||||
|
* @property {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 {number} [preload=0] Preload. Load low-resolution tiles up to `preload` levels. `0`
|
||||||
|
* means no preloading.
|
||||||
|
* @property {import("../source/Tile.js").default} [source] Source for this layer.
|
||||||
|
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
|
||||||
|
* this layer in its layers collection, and the layer will be rendered on top. This is useful for
|
||||||
|
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
|
||||||
|
* use {@link module:ol/Map#addLayer}.
|
||||||
|
* @property {boolean} [useInterimTilesOnError=true] Use interim tiles on error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ParsedStyle
|
||||||
|
* @property {string} vertexShader The vertex shader.
|
||||||
|
* @property {string} fragmentShader The fragment shader.
|
||||||
|
* @property {Object<string,import("../webgl/Helper.js").UniformValue>} uniforms Uniform definitions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Style} style The layer style.
|
||||||
|
* @param {number} [bandCount] The number of bands.
|
||||||
|
* @return {ParsedStyle} Shaders and uniforms generated from the style.
|
||||||
|
*/
|
||||||
|
function parseStyle(style, bandCount) {
|
||||||
|
const vertexShader = `
|
||||||
|
attribute vec2 ${Attributes.TEXTURE_COORD};
|
||||||
|
uniform mat4 ${Uniforms.TILE_TRANSFORM};
|
||||||
|
uniform float ${Uniforms.DEPTH};
|
||||||
|
|
||||||
|
varying vec2 v_textureCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_textureCoord = ${Attributes.TEXTURE_COORD};
|
||||||
|
gl_Position = ${Uniforms.TILE_TRANSFORM} * vec4(${Attributes.TEXTURE_COORD}, ${Uniforms.DEPTH}, 1.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../style/expressions.js").ParsingContext}
|
||||||
|
*/
|
||||||
|
const context = {
|
||||||
|
inFragmentShader: true,
|
||||||
|
variables: [],
|
||||||
|
attributes: [],
|
||||||
|
stringLiteralsMap: {},
|
||||||
|
bandCount: bandCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
const pipeline = [];
|
||||||
|
|
||||||
|
if (style.color !== undefined) {
|
||||||
|
const color = expressionToGlsl(context, style.color, ValueTypes.COLOR);
|
||||||
|
pipeline.push(`color = ${color};`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.contrast !== undefined) {
|
||||||
|
const contrast = expressionToGlsl(
|
||||||
|
context,
|
||||||
|
style.contrast,
|
||||||
|
ValueTypes.NUMBER
|
||||||
|
);
|
||||||
|
pipeline.push(
|
||||||
|
`color.rgb = clamp((${contrast} + 1.0) * color.rgb - (${contrast} / 2.0), vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0));`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.exposure !== undefined) {
|
||||||
|
const exposure = expressionToGlsl(
|
||||||
|
context,
|
||||||
|
style.exposure,
|
||||||
|
ValueTypes.NUMBER
|
||||||
|
);
|
||||||
|
pipeline.push(
|
||||||
|
`color.rgb = clamp((${exposure} + 1.0) * color.rgb, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0));`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.saturation !== undefined) {
|
||||||
|
const saturation = expressionToGlsl(
|
||||||
|
context,
|
||||||
|
style.saturation,
|
||||||
|
ValueTypes.NUMBER
|
||||||
|
);
|
||||||
|
pipeline.push(`
|
||||||
|
float saturation = ${saturation} + 1.0;
|
||||||
|
float sr = (1.0 - saturation) * 0.2126;
|
||||||
|
float sg = (1.0 - saturation) * 0.7152;
|
||||||
|
float sb = (1.0 - saturation) * 0.0722;
|
||||||
|
mat3 saturationMatrix = mat3(
|
||||||
|
sr + saturation, sr, sr,
|
||||||
|
sg, sg + saturation, sg,
|
||||||
|
sb, sb, sb + saturation
|
||||||
|
);
|
||||||
|
color.rgb = clamp(saturationMatrix * color.rgb, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0));
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.gamma !== undefined) {
|
||||||
|
const gamma = expressionToGlsl(context, style.gamma, ValueTypes.NUMBER);
|
||||||
|
pipeline.push(`color.rgb = pow(color.rgb, vec3(1.0 / ${gamma}));`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.brightness !== undefined) {
|
||||||
|
const brightness = expressionToGlsl(
|
||||||
|
context,
|
||||||
|
style.brightness,
|
||||||
|
ValueTypes.NUMBER
|
||||||
|
);
|
||||||
|
pipeline.push(
|
||||||
|
`color.rgb = clamp(color.rgb + ${brightness}, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0));`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Object<string,import("../webgl/Helper").UniformValue>} */
|
||||||
|
const uniforms = {};
|
||||||
|
|
||||||
|
const numVariables = context.variables.length;
|
||||||
|
if (numVariables > 1 && !style.variables) {
|
||||||
|
throw new Error(
|
||||||
|
`Missing variables in style (expected ${context.variables})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < numVariables; ++i) {
|
||||||
|
const variableName = context.variables[i];
|
||||||
|
if (!(variableName in style.variables)) {
|
||||||
|
throw new Error(`Missing '${variableName}' in style variables`);
|
||||||
|
}
|
||||||
|
const uniformName = uniformNameForVariable(variableName);
|
||||||
|
uniforms[uniformName] = function () {
|
||||||
|
let value = style.variables[variableName];
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
value = getStringNumberEquivalent(context, value);
|
||||||
|
}
|
||||||
|
return value !== undefined ? value : -9999999; // to avoid matching with the first string literal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniformDeclarations = Object.keys(uniforms).map(function (name) {
|
||||||
|
return `uniform float ${name};`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const textureCount = Math.ceil(bandCount / 4);
|
||||||
|
const colorAssignments = new Array(textureCount);
|
||||||
|
for (let textureIndex = 0; textureIndex < textureCount; ++textureIndex) {
|
||||||
|
const uniformName = Uniforms.TILE_TEXTURE_PREFIX + textureIndex;
|
||||||
|
uniformDeclarations.push(`uniform sampler2D ${uniformName};`);
|
||||||
|
colorAssignments[
|
||||||
|
textureIndex
|
||||||
|
] = `vec4 color${textureIndex} = texture2D(${uniformName}, v_textureCoord);`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fragmentShader = `
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp float;
|
||||||
|
#else
|
||||||
|
precision mediump float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
varying vec2 v_textureCoord;
|
||||||
|
uniform float ${Uniforms.TRANSITION_ALPHA};
|
||||||
|
uniform float ${Uniforms.TEXTURE_PIXEL_WIDTH};
|
||||||
|
uniform float ${Uniforms.TEXTURE_PIXEL_HEIGHT};
|
||||||
|
uniform float ${Uniforms.RESOLUTION};
|
||||||
|
uniform float ${Uniforms.ZOOM};
|
||||||
|
|
||||||
|
${uniformDeclarations.join('\n')}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
${colorAssignments.join('\n')}
|
||||||
|
|
||||||
|
vec4 color = color0;
|
||||||
|
|
||||||
|
${pipeline.join('\n')}
|
||||||
|
|
||||||
|
if (color.a == 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = color;
|
||||||
|
gl_FragColor.rgb *= gl_FragColor.a;
|
||||||
|
gl_FragColor *= ${Uniforms.TRANSITION_ALPHA};
|
||||||
|
}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
vertexShader: vertexShader,
|
||||||
|
fragmentShader: fragmentShader,
|
||||||
|
uniforms: uniforms,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* For layer sources that provide pre-rendered, tiled images in grids that are
|
||||||
|
* organized by zoom levels for specific resolutions.
|
||||||
|
* Note that any property set in the options is set as a {@link module:ol/Object~BaseObject}
|
||||||
|
* property on the layer object; for example, setting `title: 'My Title'` in the
|
||||||
|
* options means that `title` is observable, and has get/set accessors.
|
||||||
|
*
|
||||||
|
* @extends BaseTileLayer<import("../source/DataTile.js").default|import("../source/TileImage.js").default>
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
class WebGLTileLayer extends BaseTileLayer {
|
||||||
|
/**
|
||||||
|
* @param {Options} opt_options Tile layer options.
|
||||||
|
*/
|
||||||
|
constructor(opt_options) {
|
||||||
|
const options = opt_options ? assign({}, opt_options) : {};
|
||||||
|
|
||||||
|
const style = options.style || {};
|
||||||
|
delete options.style;
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Style}
|
||||||
|
*/
|
||||||
|
this.style_ = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a renderer for this layer.
|
||||||
|
* @return {import("../renderer/Layer.js").default} A layer renderer.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
createRenderer() {
|
||||||
|
const source = this.getSource();
|
||||||
|
const parsedStyle = parseStyle(
|
||||||
|
this.style_,
|
||||||
|
'bandCount' in source ? source.bandCount : 4
|
||||||
|
);
|
||||||
|
|
||||||
|
this.styleVariables_ = this.style_.variables || {};
|
||||||
|
|
||||||
|
return new WebGLTileLayerRenderer(this, {
|
||||||
|
vertexShader: parsedStyle.vertexShader,
|
||||||
|
fragmentShader: parsedStyle.fragmentShader,
|
||||||
|
uniforms: parsedStyle.uniforms,
|
||||||
|
className: this.getClassName(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update any variables used by the layer style and trigger a re-render.
|
||||||
|
* @param {Object<string, number>} variables Variables to update.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
updateStyleVariables(variables) {
|
||||||
|
assign(this.styleVariables_, variables);
|
||||||
|
this.changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WebGLTileLayer;
|
||||||
@@ -0,0 +1,543 @@
|
|||||||
|
/**
|
||||||
|
* @module ol/renderer/webgl/TileLayer
|
||||||
|
*/
|
||||||
|
import LRUCache from '../../structs/LRUCache.js';
|
||||||
|
import State from '../../source/State.js';
|
||||||
|
import TileRange from '../../TileRange.js';
|
||||||
|
import TileState from '../../TileState.js';
|
||||||
|
import TileTexture from '../../webgl/TileTexture.js';
|
||||||
|
import WebGLArrayBuffer from '../../webgl/Buffer.js';
|
||||||
|
import WebGLLayerRenderer from './Layer.js';
|
||||||
|
import {AttributeType} from '../../webgl/Helper.js';
|
||||||
|
import {ELEMENT_ARRAY_BUFFER, STATIC_DRAW} from '../../webgl.js';
|
||||||
|
import {
|
||||||
|
compose as composeTransform,
|
||||||
|
create as createTransform,
|
||||||
|
} from '../../transform.js';
|
||||||
|
import {
|
||||||
|
create as createMat4,
|
||||||
|
fromTransform as mat4FromTransform,
|
||||||
|
} from '../../vec/mat4.js';
|
||||||
|
import {
|
||||||
|
createOrUpdate as createTileCoord,
|
||||||
|
getKeyZXY,
|
||||||
|
getKey as getTileCoordKey,
|
||||||
|
} from '../../tilecoord.js';
|
||||||
|
import {fromUserExtent} from '../../proj.js';
|
||||||
|
import {getIntersection} from '../../extent.js';
|
||||||
|
import {getUid} from '../../util.js';
|
||||||
|
import {isEmpty} from '../../extent.js';
|
||||||
|
import {numberSafeCompareFunction} from '../../array.js';
|
||||||
|
import {toSize} from '../../size.js';
|
||||||
|
|
||||||
|
export const Uniforms = {
|
||||||
|
TILE_TEXTURE_PREFIX: 'u_tileTexture',
|
||||||
|
TILE_TRANSFORM: 'u_tileTransform',
|
||||||
|
TRANSITION_ALPHA: 'u_transitionAlpha',
|
||||||
|
DEPTH: 'u_depth',
|
||||||
|
TEXTURE_PIXEL_WIDTH: 'u_texturePixelWidth',
|
||||||
|
TEXTURE_PIXEL_HEIGHT: 'u_texturePixelHeight',
|
||||||
|
RESOLUTION: 'u_resolution',
|
||||||
|
ZOOM: 'u_zoom',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Attributes = {
|
||||||
|
TEXTURE_COORD: 'a_textureCoord',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<import('../../webgl/Helper.js').AttributeDescription>}
|
||||||
|
*/
|
||||||
|
const attributeDescriptions = [
|
||||||
|
{
|
||||||
|
name: Attributes.TEXTURE_COORD,
|
||||||
|
size: 2,
|
||||||
|
type: AttributeType.FLOAT,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a zoom level into a depth value ranging from -1 to 1.
|
||||||
|
* @param {number} z A zoom level.
|
||||||
|
* @return {number} A depth value.
|
||||||
|
*/
|
||||||
|
function depthForZ(z) {
|
||||||
|
return 2 * (1 - 1 / (z + 1)) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a tile texture to the lookup.
|
||||||
|
* @param {Object<string, Array<import("../../webgl/TileTexture.js").default>>} tileTexturesByZ Lookup of
|
||||||
|
* tile textures by zoom level.
|
||||||
|
* @param {import("../../webgl/TileTexture.js").default} tileTexture A tile texture.
|
||||||
|
* @param {number} z The zoom level.
|
||||||
|
*/
|
||||||
|
function addTileTextureToLookup(tileTexturesByZ, tileTexture, z) {
|
||||||
|
if (!(z in tileTexturesByZ)) {
|
||||||
|
tileTexturesByZ[z] = [];
|
||||||
|
}
|
||||||
|
tileTexturesByZ[z].push(tileTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
* @return {import("../../extent.js").Extent} Extent.
|
||||||
|
*/
|
||||||
|
function getRenderExtent(frameState) {
|
||||||
|
const layerState = frameState.layerStatesArray[frameState.layerIndex];
|
||||||
|
let extent = frameState.extent;
|
||||||
|
if (layerState.extent) {
|
||||||
|
extent = getIntersection(
|
||||||
|
extent,
|
||||||
|
fromUserExtent(layerState.extent, frameState.viewState.projection)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Options
|
||||||
|
* @property {string} vertexShader Vertex shader source.
|
||||||
|
* @property {string} fragmentShader Fragment shader source.
|
||||||
|
* @property {Object<string, import("../../webgl/Helper").UniformValue>} [uniforms] Additional uniforms
|
||||||
|
* made available to shaders.
|
||||||
|
* @property {string} [className='ol-layer'] A CSS class name to set to the canvas element.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* WebGL renderer for tile layers.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||||
|
/**
|
||||||
|
* @param {import("../../layer/WebGLTile.js").default} tileLayer Tile layer.
|
||||||
|
* @param {Options} options Options.
|
||||||
|
*/
|
||||||
|
constructor(tileLayer, options) {
|
||||||
|
super(tileLayer, {
|
||||||
|
uniforms: options.uniforms,
|
||||||
|
className: options.className,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This transform converts tile i, j coordinates to screen coordinates.
|
||||||
|
* @type {import("../../transform.js").Transform}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.tileTransform_ = createTransform();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<number>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.tempMat4_ = createMat4();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../../TileRange.js").default}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.tempTileRange_ = new TileRange(0, 0, 0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../../tilecoord.js").TileCoord}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.tempTileCoord_ = createTileCoord(0, 0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../../size.js").Size}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.tempSize_ = [0, 0];
|
||||||
|
|
||||||
|
this.program_ = this.helper.getProgram(
|
||||||
|
options.fragmentShader,
|
||||||
|
options.vertexShader
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiles are rendered as a quad with the following structure:
|
||||||
|
*
|
||||||
|
* [P3]---------[P2]
|
||||||
|
* |` |
|
||||||
|
* | ` B |
|
||||||
|
* | ` |
|
||||||
|
* | ` |
|
||||||
|
* | A ` |
|
||||||
|
* | ` |
|
||||||
|
* [P0]---------[P1]
|
||||||
|
*
|
||||||
|
* Triangle A: P0, P1, P3
|
||||||
|
* Triangle B: P1, P2, P3
|
||||||
|
*/
|
||||||
|
const indices = new WebGLArrayBuffer(ELEMENT_ARRAY_BUFFER, STATIC_DRAW);
|
||||||
|
indices.fromArray([0, 1, 3, 1, 2, 3]);
|
||||||
|
this.helper.flushBufferData(indices);
|
||||||
|
this.indices_ = indices;
|
||||||
|
|
||||||
|
this.tileTextureCache_ = new LRUCache(512);
|
||||||
|
|
||||||
|
this.renderedOpacity_ = NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* @param {import("../../Tile.js").default} tile Tile.
|
||||||
|
* @return {boolean} Tile is drawable.
|
||||||
|
*/
|
||||||
|
isDrawableTile(tile) {
|
||||||
|
const tileLayer = this.getLayer();
|
||||||
|
const tileState = tile.getState();
|
||||||
|
const useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
||||||
|
return (
|
||||||
|
tileState == TileState.LOADED ||
|
||||||
|
tileState == TileState.EMPTY ||
|
||||||
|
(tileState == TileState.ERROR && !useInterimTilesOnError)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether render should be called.
|
||||||
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
* @return {boolean} Layer is ready to be rendered.
|
||||||
|
*/
|
||||||
|
prepareFrame(frameState) {
|
||||||
|
if (isEmpty(getRenderExtent(frameState))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const source = this.getLayer().getSource();
|
||||||
|
if (!source) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return source.getState() === State.READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the layer.
|
||||||
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
* @return {HTMLElement} The rendered element.
|
||||||
|
*/
|
||||||
|
renderFrame(frameState) {
|
||||||
|
this.preRender(frameState);
|
||||||
|
|
||||||
|
const viewState = frameState.viewState;
|
||||||
|
const layerState = frameState.layerStatesArray[frameState.layerIndex];
|
||||||
|
const extent = getRenderExtent(frameState);
|
||||||
|
const tileLayer = this.getLayer();
|
||||||
|
const tileSource = tileLayer.getSource();
|
||||||
|
const tileGrid = tileSource.getTileGridForProjection(viewState.projection);
|
||||||
|
const z = tileGrid.getZForResolution(
|
||||||
|
viewState.resolution,
|
||||||
|
tileSource.zDirection
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object<string, Array<import("../../webgl/TileTexture.js").default>>}
|
||||||
|
*/
|
||||||
|
const tileTexturesByZ = {};
|
||||||
|
|
||||||
|
const tileTextureCache = this.tileTextureCache_;
|
||||||
|
const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
|
||||||
|
|
||||||
|
const tileSourceKey = getUid(tileSource);
|
||||||
|
if (!(tileSourceKey in frameState.wantedTiles)) {
|
||||||
|
frameState.wantedTiles[tileSourceKey] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const wantedTiles = frameState.wantedTiles[tileSourceKey];
|
||||||
|
|
||||||
|
const tileResolution = tileGrid.getResolution(z);
|
||||||
|
|
||||||
|
for (let x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||||
|
for (let y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||||
|
const tileCoord = createTileCoord(z, x, y, this.tempTileCoord_);
|
||||||
|
const tileCoordKey = getTileCoordKey(tileCoord);
|
||||||
|
|
||||||
|
let tileTexture, tile;
|
||||||
|
if (tileTextureCache.containsKey(tileCoordKey)) {
|
||||||
|
tileTexture = tileTextureCache.get(tileCoordKey);
|
||||||
|
tile = tileTexture.tile;
|
||||||
|
}
|
||||||
|
if (!tileTexture || tileTexture.tile.key !== tileSource.getKey()) {
|
||||||
|
tile = tileSource.getTile(
|
||||||
|
z,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
frameState.pixelRatio,
|
||||||
|
viewState.projection
|
||||||
|
);
|
||||||
|
if (!tileTexture) {
|
||||||
|
tileTexture = new TileTexture(tile, tileGrid, this.helper);
|
||||||
|
tileTextureCache.set(tileCoordKey, tileTexture);
|
||||||
|
} else {
|
||||||
|
tileTexture.setTile(
|
||||||
|
this.isDrawableTile(tile) ? tile : tile.getInterimTile()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTileTextureToLookup(tileTexturesByZ, tileTexture, z);
|
||||||
|
|
||||||
|
const tileQueueKey = tile.getKey();
|
||||||
|
wantedTiles[tileQueueKey] = true;
|
||||||
|
|
||||||
|
if (tile.getState() === TileState.IDLE) {
|
||||||
|
if (!frameState.tileQueue.isKeyQueued(tileQueueKey)) {
|
||||||
|
frameState.tileQueue.enqueue([
|
||||||
|
tile,
|
||||||
|
tileSourceKey,
|
||||||
|
tileGrid.getTileCoordCenter(tileCoord),
|
||||||
|
tileResolution,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lookup of alpha values for tiles at the target rendering resolution
|
||||||
|
* for tiles that are in transition. If a tile coord key is absent from
|
||||||
|
* this lookup, the tile should be rendered at alpha 1.
|
||||||
|
* @type {Object<string, number>}
|
||||||
|
*/
|
||||||
|
const alphaLookup = {};
|
||||||
|
|
||||||
|
const uid = getUid(this);
|
||||||
|
const time = frameState.time;
|
||||||
|
let blend = false;
|
||||||
|
|
||||||
|
// look for cached tiles to use if a target tile is not ready
|
||||||
|
const tileTextures = tileTexturesByZ[z];
|
||||||
|
for (let i = 0, ii = tileTextures.length; i < ii; ++i) {
|
||||||
|
const tileTexture = tileTextures[i];
|
||||||
|
const tile = tileTexture.tile;
|
||||||
|
const tileCoord = tile.tileCoord;
|
||||||
|
|
||||||
|
if (tileTexture.loaded) {
|
||||||
|
const alpha = tile.getAlpha(uid, time);
|
||||||
|
if (alpha === 1) {
|
||||||
|
// no need to look for alt tiles
|
||||||
|
tile.endTransition(uid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
blend = true;
|
||||||
|
const tileCoordKey = getTileCoordKey(tileCoord);
|
||||||
|
alphaLookup[tileCoordKey] = alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first look for child tiles (at z + 1)
|
||||||
|
const coveredByChildren = this.findAltTiles_(
|
||||||
|
tileGrid,
|
||||||
|
tileCoord,
|
||||||
|
z + 1,
|
||||||
|
tileTexturesByZ
|
||||||
|
);
|
||||||
|
|
||||||
|
if (coveredByChildren) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// next look for parent tiles
|
||||||
|
for (let parentZ = z - 1; parentZ >= tileGrid.minZoom; --parentZ) {
|
||||||
|
const coveredByParent = this.findAltTiles_(
|
||||||
|
tileGrid,
|
||||||
|
tileCoord,
|
||||||
|
parentZ,
|
||||||
|
tileTexturesByZ
|
||||||
|
);
|
||||||
|
|
||||||
|
if (coveredByParent) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.helper.useProgram(this.program_);
|
||||||
|
this.helper.prepareDraw(frameState, !blend);
|
||||||
|
|
||||||
|
const zs = Object.keys(tileTexturesByZ)
|
||||||
|
.map(Number)
|
||||||
|
.sort(numberSafeCompareFunction);
|
||||||
|
|
||||||
|
const gl = this.helper.getGL();
|
||||||
|
|
||||||
|
const centerX = viewState.center[0];
|
||||||
|
const centerY = viewState.center[1];
|
||||||
|
|
||||||
|
for (let j = 0, jj = zs.length; j < jj; ++j) {
|
||||||
|
const tileZ = zs[j];
|
||||||
|
const tileResolution = tileGrid.getResolution(tileZ);
|
||||||
|
const tileSize = toSize(tileGrid.getTileSize(tileZ), this.tempSize_);
|
||||||
|
const tileOrigin = tileGrid.getOrigin(tileZ);
|
||||||
|
|
||||||
|
const centerI =
|
||||||
|
(centerX - tileOrigin[0]) / (tileSize[0] * tileResolution);
|
||||||
|
const centerJ =
|
||||||
|
(tileOrigin[1] - centerY) / (tileSize[1] * tileResolution);
|
||||||
|
|
||||||
|
const tileScale = viewState.resolution / tileResolution;
|
||||||
|
|
||||||
|
const depth = depthForZ(tileZ);
|
||||||
|
const tileTextures = tileTexturesByZ[tileZ];
|
||||||
|
for (let i = 0, ii = tileTextures.length; i < ii; ++i) {
|
||||||
|
const tileTexture = tileTextures[i];
|
||||||
|
if (!tileTexture.loaded) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const tile = tileTexture.tile;
|
||||||
|
const tileCoord = tile.tileCoord;
|
||||||
|
const tileCoordKey = getTileCoordKey(tileCoord);
|
||||||
|
|
||||||
|
const tileCenterI = tileCoord[1];
|
||||||
|
const tileCenterJ = tileCoord[2];
|
||||||
|
|
||||||
|
composeTransform(
|
||||||
|
this.tileTransform_,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2 / ((frameState.size[0] * tileScale) / tileSize[0]),
|
||||||
|
-2 / ((frameState.size[1] * tileScale) / tileSize[1]),
|
||||||
|
viewState.rotation,
|
||||||
|
-(centerI - tileCenterI),
|
||||||
|
-(centerJ - tileCenterJ)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.helper.setUniformMatrixValue(
|
||||||
|
Uniforms.TILE_TRANSFORM,
|
||||||
|
mat4FromTransform(this.tempMat4_, this.tileTransform_)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.helper.bindBuffer(tileTexture.coords);
|
||||||
|
this.helper.bindBuffer(this.indices_);
|
||||||
|
this.helper.enableAttributes(attributeDescriptions);
|
||||||
|
|
||||||
|
for (
|
||||||
|
let textureIndex = 0;
|
||||||
|
textureIndex < tileTexture.textures.length;
|
||||||
|
++textureIndex
|
||||||
|
) {
|
||||||
|
const textureProperty = 'TEXTURE' + textureIndex;
|
||||||
|
const uniformName = Uniforms.TILE_TEXTURE_PREFIX + textureIndex;
|
||||||
|
gl.activeTexture(gl[textureProperty]);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, tileTexture.textures[textureIndex]);
|
||||||
|
gl.uniform1i(this.helper.getUniformLocation(uniformName), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const alpha =
|
||||||
|
tileCoordKey in alphaLookup ? alphaLookup[tileCoordKey] : 1;
|
||||||
|
|
||||||
|
if (alpha < 1) {
|
||||||
|
frameState.animate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.helper.setUniformFloatValue(Uniforms.TRANSITION_ALPHA, alpha);
|
||||||
|
this.helper.setUniformFloatValue(Uniforms.DEPTH, depth);
|
||||||
|
this.helper.setUniformFloatValue(
|
||||||
|
Uniforms.TEXTURE_PIXEL_WIDTH,
|
||||||
|
tileSize[0]
|
||||||
|
);
|
||||||
|
this.helper.setUniformFloatValue(
|
||||||
|
Uniforms.TEXTURE_PIXEL_HEIGHT,
|
||||||
|
tileSize[1]
|
||||||
|
);
|
||||||
|
this.helper.setUniformFloatValue(
|
||||||
|
Uniforms.RESOLUTION,
|
||||||
|
viewState.resolution
|
||||||
|
);
|
||||||
|
this.helper.setUniformFloatValue(Uniforms.ZOOM, viewState.zoom);
|
||||||
|
|
||||||
|
this.helper.drawElements(0, this.indices_.getSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.helper.finalizeDraw(frameState);
|
||||||
|
|
||||||
|
const canvas = this.helper.getCanvas();
|
||||||
|
|
||||||
|
const opacity = layerState.opacity;
|
||||||
|
if (this.renderedOpacity_ !== opacity) {
|
||||||
|
canvas.style.opacity = String(opacity);
|
||||||
|
this.renderedOpacity_ = opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (tileTextureCache.canExpireCache()) {
|
||||||
|
const tileTexture = tileTextureCache.pop();
|
||||||
|
tileTexture.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: let the renderers manage their own cache instead of managing the source cache
|
||||||
|
if (tileSource.canExpireCache()) {
|
||||||
|
/**
|
||||||
|
* @param {import("../../PluggableMap.js").default} map Map.
|
||||||
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
*/
|
||||||
|
const postRenderFunction = function (map, frameState) {
|
||||||
|
const tileSourceKey = getUid(tileSource);
|
||||||
|
if (tileSourceKey in frameState.usedTiles) {
|
||||||
|
tileSource.expireCache(
|
||||||
|
frameState.viewState.projection,
|
||||||
|
frameState.usedTiles[tileSourceKey]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
frameState.postRenderFunctions.push(postRenderFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.postRender(frameState);
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look for tiles covering the provided tile coordinate at an alternate
|
||||||
|
* zoom level. Loaded tiles will be added to the provided tile texture lookup.
|
||||||
|
* @param {import("../../tilegrid/TileGrid.js").default} tileGrid The tile grid.
|
||||||
|
* @param {import("../../tilecoord.js").TileCoord} tileCoord The target tile coordinate.
|
||||||
|
* @param {number} altZ The alternate zoom level.
|
||||||
|
* @param {Object<string, Array<import("../../webgl/TileTexture.js").default>>} tileTexturesByZ Lookup of
|
||||||
|
* tile textures by zoom level.
|
||||||
|
* @return {boolean} The tile coordinate is covered by loaded tiles at the alternate zoom level.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
findAltTiles_(tileGrid, tileCoord, altZ, tileTexturesByZ) {
|
||||||
|
const tileRange = tileGrid.getTileRangeForTileCoordAndZ(
|
||||||
|
tileCoord,
|
||||||
|
altZ,
|
||||||
|
this.tempTileRange_
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tileRange) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let covered = true;
|
||||||
|
const tileTextureCache = this.tileTextureCache_;
|
||||||
|
for (let x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||||
|
for (let y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||||
|
const cacheKey = getKeyZXY(altZ, x, y);
|
||||||
|
let loaded = false;
|
||||||
|
if (tileTextureCache.containsKey(cacheKey)) {
|
||||||
|
const tileTexture = tileTextureCache.get(cacheKey);
|
||||||
|
if (tileTexture.loaded) {
|
||||||
|
addTileTextureToLookup(tileTexturesByZ, tileTexture, altZ);
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!loaded) {
|
||||||
|
covered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return covered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* @return {import("../../layer/WebGLTile.js").default}
|
||||||
|
*/
|
||||||
|
WebGLTileLayerRenderer.prototype.getLayer;
|
||||||
|
|
||||||
|
export default WebGLTileLayerRenderer;
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
export {default as BingMaps} from './source/BingMaps.js';
|
export {default as BingMaps} from './source/BingMaps.js';
|
||||||
export {default as CartoDB} from './source/CartoDB.js';
|
export {default as CartoDB} from './source/CartoDB.js';
|
||||||
export {default as Cluster} from './source/Cluster.js';
|
export {default as Cluster} from './source/Cluster.js';
|
||||||
|
export {default as DataTile} from './source/DataTile.js';
|
||||||
|
export {default as GeoTIFF} from './source/GeoTIFF.js';
|
||||||
export {default as IIIF} from './source/IIIF.js';
|
export {default as IIIF} from './source/IIIF.js';
|
||||||
export {default as Image} from './source/Image.js';
|
export {default as Image} from './source/Image.js';
|
||||||
export {default as ImageArcGISRest} from './source/ImageArcGISRest.js';
|
export {default as ImageArcGISRest} from './source/ImageArcGISRest.js';
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* @module ol/source/DataTile
|
||||||
|
*/
|
||||||
|
import DataTile from '../DataTile.js';
|
||||||
|
import EventType from '../events/EventType.js';
|
||||||
|
import TileEventType from './TileEventType.js';
|
||||||
|
import TileSource, {TileSourceEvent} from './Tile.js';
|
||||||
|
import TileState from '../TileState.js';
|
||||||
|
import {assign} from '../obj.js';
|
||||||
|
import {createXYZ, extentFromProjection} from '../tilegrid.js';
|
||||||
|
import {getKeyZXY} from '../tilecoord.js';
|
||||||
|
import {getUid} from '../util.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Options
|
||||||
|
* @property {function(number, number, number) : Promise<import("../DataTile.js").Data>} [loader] Data loader. Called with z, x, and y tile coordinates.
|
||||||
|
* Returns a promise that resolves to a {@link import("../DataTile.js").Data}.
|
||||||
|
* @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided.
|
||||||
|
* @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided.
|
||||||
|
* @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the tiles.
|
||||||
|
* @property {number} [maxResolution] Optional tile grid resolution at level zero. Not used if `tileGrid` is provided.
|
||||||
|
* @property {import("../proj.js").ProjectionLike} [projection='EPSG:3857'] Tile projection.
|
||||||
|
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid.
|
||||||
|
* @property {boolean} [opaque=false] Whether the layer is opaque.
|
||||||
|
* @property {import("./State.js").default} [state] The source state.
|
||||||
|
* @property {number} [cacheSize] Number of tiles to retain in the cache.
|
||||||
|
* @property {number} [tilePixelRatio] Tile pixel ratio.
|
||||||
|
* @property {boolean} [wrapX=true] Render tiles beyond the antimeridian.
|
||||||
|
* @property {number} [transition] Transition time when fading in new tiles (in miliseconds).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* Base class for sources providing tiles divided into a tile grid.
|
||||||
|
*
|
||||||
|
* @fires import("./Tile.js").TileSourceEvent
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
class DataTileSource extends TileSource {
|
||||||
|
/**
|
||||||
|
* @param {Options} options Image tile options.
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
const projection =
|
||||||
|
options.projection === undefined ? 'EPSG:3857' : options.projection;
|
||||||
|
|
||||||
|
let tileGrid = options.tileGrid;
|
||||||
|
if (tileGrid === undefined && projection) {
|
||||||
|
tileGrid = createXYZ({
|
||||||
|
extent: extentFromProjection(projection),
|
||||||
|
maxResolution: options.maxResolution,
|
||||||
|
maxZoom: options.maxZoom,
|
||||||
|
minZoom: options.minZoom,
|
||||||
|
tileSize: options.tileSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
super({
|
||||||
|
cacheSize: options.cacheSize,
|
||||||
|
projection: projection,
|
||||||
|
tileGrid: tileGrid,
|
||||||
|
opaque: options.opaque,
|
||||||
|
state: options.state,
|
||||||
|
tilePixelRatio: options.tilePixelRatio,
|
||||||
|
wrapX: options.wrapX,
|
||||||
|
transition: options.transition,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {!Object<string, boolean>}
|
||||||
|
*/
|
||||||
|
this.tileLoadingKeys_ = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.loader_ = options.loader;
|
||||||
|
|
||||||
|
this.handleTileChange_ = this.handleTileChange_.bind(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.bandCount = 4; // assume RGBA
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {function(number, number, number) : Promise<import("../DataTile.js").Data>} loader The data loader.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
setLoader(loader) {
|
||||||
|
this.loader_ = loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract
|
||||||
|
* @param {number} z Tile coordinate z.
|
||||||
|
* @param {number} x Tile coordinate x.
|
||||||
|
* @param {number} y Tile coordinate y.
|
||||||
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {import("../proj/Projection.js").default} projection Projection.
|
||||||
|
* @return {!DataTile} Tile.
|
||||||
|
*/
|
||||||
|
getTile(z, x, y, pixelRatio, projection) {
|
||||||
|
const tileCoordKey = getKeyZXY(z, x, y);
|
||||||
|
if (this.tileCache.containsKey(tileCoordKey)) {
|
||||||
|
return this.tileCache.get(tileCoordKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceLoader = this.loader_;
|
||||||
|
function loader() {
|
||||||
|
return sourceLoader(z, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tile = new DataTile(
|
||||||
|
assign({tileCoord: [z, x, y], loader: loader}, this.tileOptions)
|
||||||
|
);
|
||||||
|
tile.key = this.getKey();
|
||||||
|
tile.addEventListener(EventType.CHANGE, this.handleTileChange_);
|
||||||
|
|
||||||
|
this.tileCache.set(tileCoordKey, tile);
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle tile change events.
|
||||||
|
* @param {import("../events/Event.js").default} event Event.
|
||||||
|
*/
|
||||||
|
handleTileChange_(event) {
|
||||||
|
const tile = /** @type {import("../Tile.js").default} */ (event.target);
|
||||||
|
const uid = getUid(tile);
|
||||||
|
const tileState = tile.getState();
|
||||||
|
let type;
|
||||||
|
if (tileState == TileState.LOADING) {
|
||||||
|
this.tileLoadingKeys_[uid] = true;
|
||||||
|
type = TileEventType.TILELOADSTART;
|
||||||
|
} else if (uid in this.tileLoadingKeys_) {
|
||||||
|
delete this.tileLoadingKeys_[uid];
|
||||||
|
type =
|
||||||
|
tileState == TileState.ERROR
|
||||||
|
? TileEventType.TILELOADERROR
|
||||||
|
: tileState == TileState.LOADED
|
||||||
|
? TileEventType.TILELOADEND
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
if (type) {
|
||||||
|
this.dispatchEvent(new TileSourceEvent(type, tile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataTileSource;
|
||||||
@@ -0,0 +1,517 @@
|
|||||||
|
/**
|
||||||
|
* @module ol/source/GeoTIFF
|
||||||
|
*/
|
||||||
|
import DataTile from './DataTile.js';
|
||||||
|
import State from './State.js';
|
||||||
|
import TileGrid from '../tilegrid/TileGrid.js';
|
||||||
|
import {Pool, fromUrl as tiffFromUrl, fromUrls as tiffFromUrls} from 'geotiff';
|
||||||
|
import {create as createDecoderWorker} from '../worker/geotiff-decoder.js';
|
||||||
|
import {getIntersection} from '../extent.js';
|
||||||
|
import {get as getProjection} from '../proj.js';
|
||||||
|
import {toSize} from '../size.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SourceInfo
|
||||||
|
* @property {string} url URL for the source GeoTIFF.
|
||||||
|
* @property {Array<string>} [overviews] List of any overview URLs.
|
||||||
|
* @property {number} [min=0] The minimum source data value. Rendered values are scaled from 0 to 1 based on
|
||||||
|
* the configured min and max.
|
||||||
|
* @property {number} [max] The maximum source data value. Rendered values are scaled from 0 to 1 based on
|
||||||
|
* the configured min and max.
|
||||||
|
* @property {number} [nodata] Values to discard. When provided, an additional band (alpha) will be added
|
||||||
|
* to the data.
|
||||||
|
* @property {Array<number>} [bands] Indices of the bands to be read from. If not provided, all bands will
|
||||||
|
* be read. If, for example, a GeoTIFF has red, green, blue and near-infrared bands and you only need the
|
||||||
|
* infrared band, configure `bands: [3]`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let workerPool;
|
||||||
|
function getWorkerPool() {
|
||||||
|
if (!workerPool) {
|
||||||
|
workerPool = new Pool(undefined, createDecoderWorker());
|
||||||
|
}
|
||||||
|
return workerPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("geotiff/src/geotiff.js").GeoTIFF|import("geotiff/src/geotiff.js").MultiGeoTIFF} tiff A GeoTIFF.
|
||||||
|
* @return {Promise<Array<import("geotiff/src/geotiffimage.js").GeoTIFFImage>>} Resolves to a list of images.
|
||||||
|
*/
|
||||||
|
function getImagesForTIFF(tiff) {
|
||||||
|
return tiff.getImageCount().then(function (count) {
|
||||||
|
const requests = new Array(count);
|
||||||
|
for (let i = 0; i < count; ++i) {
|
||||||
|
requests[i] = tiff.getImage(i);
|
||||||
|
}
|
||||||
|
return Promise.all(requests);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SourceInfo} source The GeoTIFF source.
|
||||||
|
* @return {Promise<Array<import("geotiff/src/geotiffimage.js").GeoTIFFImage>>} Resolves to a list of images.
|
||||||
|
*/
|
||||||
|
function getImagesForSource(source) {
|
||||||
|
let request;
|
||||||
|
if (source.overviews) {
|
||||||
|
request = tiffFromUrls(source.url, source.overviews);
|
||||||
|
} else {
|
||||||
|
request = tiffFromUrl(source.url);
|
||||||
|
}
|
||||||
|
return request.then(getImagesForTIFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number|Array<number>|Array<Array<number>>} expected Expected value.
|
||||||
|
* @param {number|Array<number>|Array<Array<number>>} got Actual value.
|
||||||
|
* @param {number} tolerance Accepted tolerance in fraction of expected between expected and got.
|
||||||
|
* @param {string} message The error message.
|
||||||
|
*/
|
||||||
|
function assertEqual(expected, got, tolerance, message) {
|
||||||
|
if (Array.isArray(expected)) {
|
||||||
|
const length = expected.length;
|
||||||
|
if (!Array.isArray(got) || length != got.length) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < length; ++i) {
|
||||||
|
assertEqual(expected[i], got[i], tolerance, message);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
got = /** @type {number} */ (got);
|
||||||
|
if (Math.abs(expected - got) > tolerance * expected) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} array The data array.
|
||||||
|
* @return {number} The minimum value.
|
||||||
|
*/
|
||||||
|
function getMinForDataType(array) {
|
||||||
|
if (array instanceof Int8Array) {
|
||||||
|
return -128;
|
||||||
|
}
|
||||||
|
if (array instanceof Int16Array) {
|
||||||
|
return -32768;
|
||||||
|
}
|
||||||
|
if (array instanceof Int32Array) {
|
||||||
|
return -2147483648;
|
||||||
|
}
|
||||||
|
if (array instanceof Float32Array) {
|
||||||
|
return 1.2e-38;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} array The data array.
|
||||||
|
* @return {number} The maximum value.
|
||||||
|
*/
|
||||||
|
function getMaxForDataType(array) {
|
||||||
|
if (array instanceof Int8Array) {
|
||||||
|
return 127;
|
||||||
|
}
|
||||||
|
if (array instanceof Uint8Array) {
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
if (array instanceof Uint8ClampedArray) {
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
if (array instanceof Int16Array) {
|
||||||
|
return 32767;
|
||||||
|
}
|
||||||
|
if (array instanceof Uint16Array) {
|
||||||
|
return 65535;
|
||||||
|
}
|
||||||
|
if (array instanceof Int32Array) {
|
||||||
|
return 2147483647;
|
||||||
|
}
|
||||||
|
if (array instanceof Uint32Array) {
|
||||||
|
return 4294967295;
|
||||||
|
}
|
||||||
|
if (array instanceof Float32Array) {
|
||||||
|
return 3.4e38;
|
||||||
|
}
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Options
|
||||||
|
* @property {Array<SourceInfo>} sources List of information about GeoTIFF sources.
|
||||||
|
* Multiple sources can be combined when their resolution sets are equal after applying a scale.
|
||||||
|
* The list of sources defines a mapping between input bands as they are read from each GeoTIFF, and
|
||||||
|
* the output bands that are provided by data tiles. To control which bands to read from each GeoTIFF,
|
||||||
|
* use the {@link import("./GeoTIFF.js").SourceInfo bands} property. If, for example, you spedify two
|
||||||
|
* sources, one with 3 bands and {@link import("./GeoTIFF.js").SourceInfo nodata} configured, and
|
||||||
|
* another with 1 band, the resulting data tiles will have 5 bands: 3 from the first source, 1 alpha
|
||||||
|
* band from the first source, and 1 band from the second source.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* A source for working with GeoTIFF data.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
class GeoTIFFSource extends DataTile {
|
||||||
|
/**
|
||||||
|
* @param {Options} options Data tile options.
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
super({
|
||||||
|
state: State.LOADING,
|
||||||
|
tileGrid: null,
|
||||||
|
projection: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<SourceInfo>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.sourceInfo_ = options.sources;
|
||||||
|
|
||||||
|
const numSources = this.sourceInfo_.length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<Array<import("geotiff/src/geotiffimage.js").GeoTIFFImage>>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.sourceImagery_ = new Array(numSources);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<number>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.resolutionFactors_ = new Array(numSources);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<number>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.samplesPerPixel_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<Array<number>>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.nodataValues_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.addAlpha_ = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Error}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.error_ = null;
|
||||||
|
|
||||||
|
this.setKey(this.sourceInfo_.map((source) => source.url).join(','));
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
const requests = new Array(numSources);
|
||||||
|
for (let i = 0; i < numSources; ++i) {
|
||||||
|
requests[i] = getImagesForSource(this.sourceInfo_[i]);
|
||||||
|
}
|
||||||
|
Promise.all(requests)
|
||||||
|
.then(function (sources) {
|
||||||
|
self.configure_(sources);
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
self.error_ = error;
|
||||||
|
self.setState(State.ERROR);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Error} A source loading error. When the source state is `error`, use this function
|
||||||
|
* to get more information about the error. To debug a faulty configuration, you may want to use
|
||||||
|
* a listener like
|
||||||
|
* ```js
|
||||||
|
* geotiffSource.on('change', () => {
|
||||||
|
* if (geotiffSource.getState() === 'error') {
|
||||||
|
* console.error(geotiffSource.getError());
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
getError() {
|
||||||
|
return this.error_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the tile grid based on images within the source GeoTIFFs. Each GeoTIFF
|
||||||
|
* must have the same internal tiled structure.
|
||||||
|
* @param {Array<Array<import("geotiff/src/geotiffimage.js").GeoTIFFImage>>} sources Each source is a list of images
|
||||||
|
* from a single GeoTIFF.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
configure_(sources) {
|
||||||
|
let extent;
|
||||||
|
let origin;
|
||||||
|
let tileSizes;
|
||||||
|
let resolutions;
|
||||||
|
const samplesPerPixel = new Array(sources.length);
|
||||||
|
const nodataValues = new Array(sources.length);
|
||||||
|
let minZoom = 0;
|
||||||
|
|
||||||
|
const sourceCount = sources.length;
|
||||||
|
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
|
||||||
|
const images = sources[sourceIndex];
|
||||||
|
const imageCount = images.length;
|
||||||
|
|
||||||
|
let sourceExtent;
|
||||||
|
let sourceOrigin;
|
||||||
|
const sourceTileSizes = new Array(imageCount);
|
||||||
|
const sourceResolutions = new Array(imageCount);
|
||||||
|
|
||||||
|
nodataValues[sourceIndex] = new Array(imageCount);
|
||||||
|
|
||||||
|
for (let imageIndex = 0; imageIndex < imageCount; ++imageIndex) {
|
||||||
|
const image = images[imageIndex];
|
||||||
|
const nodataValue = image.getGDALNoData();
|
||||||
|
nodataValues[sourceIndex][imageIndex] =
|
||||||
|
nodataValue === null ? NaN : nodataValue;
|
||||||
|
|
||||||
|
const wantedSamples = this.sourceInfo_[sourceIndex].bands;
|
||||||
|
samplesPerPixel[sourceIndex] = wantedSamples
|
||||||
|
? wantedSamples.length
|
||||||
|
: image.getSamplesPerPixel();
|
||||||
|
const level = imageCount - (imageIndex + 1);
|
||||||
|
|
||||||
|
if (!sourceExtent) {
|
||||||
|
sourceExtent = image.getBoundingBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sourceOrigin) {
|
||||||
|
sourceOrigin = image.getOrigin().slice(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceResolutions[level] = image.getResolution(images[0])[0];
|
||||||
|
sourceTileSizes[level] = [image.getTileWidth(), image.getTileHeight()];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extent) {
|
||||||
|
extent = sourceExtent;
|
||||||
|
} else {
|
||||||
|
getIntersection(extent, sourceExtent, extent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!origin) {
|
||||||
|
origin = sourceOrigin;
|
||||||
|
} else {
|
||||||
|
const message = `Origin mismatch for source ${sourceIndex}, got [${sourceOrigin}] but expected [${origin}]`;
|
||||||
|
assertEqual(origin, sourceOrigin, 0, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolutions) {
|
||||||
|
resolutions = sourceResolutions;
|
||||||
|
this.resolutionFactors_[sourceIndex] = 1;
|
||||||
|
} else {
|
||||||
|
if (resolutions.length - minZoom > sourceResolutions.length) {
|
||||||
|
minZoom = resolutions.length - sourceResolutions.length;
|
||||||
|
}
|
||||||
|
const resolutionFactor =
|
||||||
|
resolutions[resolutions.length - 1] /
|
||||||
|
sourceResolutions[sourceResolutions.length - 1];
|
||||||
|
this.resolutionFactors_[sourceIndex] = resolutionFactor;
|
||||||
|
const scaledSourceResolutions = sourceResolutions.map(
|
||||||
|
(resolution) => (resolution *= resolutionFactor)
|
||||||
|
);
|
||||||
|
const message = `Resolution mismatch for source ${sourceIndex}, got [${scaledSourceResolutions}] but expected [${resolutions}]`;
|
||||||
|
assertEqual(
|
||||||
|
resolutions.slice(minZoom, resolutions.length),
|
||||||
|
scaledSourceResolutions,
|
||||||
|
0.005,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tileSizes) {
|
||||||
|
tileSizes = sourceTileSizes;
|
||||||
|
} else {
|
||||||
|
assertEqual(
|
||||||
|
tileSizes.slice(minZoom, tileSizes.length),
|
||||||
|
sourceTileSizes,
|
||||||
|
0,
|
||||||
|
`Tile size mismatch for source ${sourceIndex}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sourceImagery_[sourceIndex] = images.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0, ii = this.sourceImagery_.length; i < ii; ++i) {
|
||||||
|
const sourceImagery = this.sourceImagery_[i];
|
||||||
|
while (sourceImagery.length < resolutions.length) {
|
||||||
|
sourceImagery.unshift(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.getProjection()) {
|
||||||
|
const firstImage = sources[0][0];
|
||||||
|
if (firstImage.geoKeys) {
|
||||||
|
const code =
|
||||||
|
firstImage.geoKeys.ProjectedCSTypeGeoKey ||
|
||||||
|
firstImage.geoKeys.GeographicTypeGeoKey;
|
||||||
|
if (code) {
|
||||||
|
this.projection = getProjection(`EPSG:${code}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.samplesPerPixel_ = samplesPerPixel;
|
||||||
|
this.nodataValues_ = nodataValues;
|
||||||
|
|
||||||
|
// decide if we need to add an alpha band to handle nodata
|
||||||
|
outer: for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
|
||||||
|
// option 1: source is configured with a nodata value
|
||||||
|
if (this.sourceInfo_[sourceIndex].nodata !== undefined) {
|
||||||
|
this.addAlpha_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = nodataValues[sourceIndex];
|
||||||
|
|
||||||
|
// option 2: check image metadata for limited bands
|
||||||
|
const bands = this.sourceInfo_[sourceIndex].bands;
|
||||||
|
if (bands) {
|
||||||
|
for (let i = 0; i < bands.length; ++i) {
|
||||||
|
if (!isNaN(values[bands[i]])) {
|
||||||
|
this.addAlpha_ = true;
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// option 3: check image metadata for all bands
|
||||||
|
for (let imageIndex = 0; imageIndex < values.length; ++imageIndex) {
|
||||||
|
if (!isNaN(values[imageIndex])) {
|
||||||
|
this.addAlpha_ = true;
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const additionalBands = this.addAlpha_ ? 1 : 0;
|
||||||
|
this.bandCount =
|
||||||
|
samplesPerPixel.reduce((accumulator, value) => {
|
||||||
|
accumulator += value;
|
||||||
|
return accumulator;
|
||||||
|
}, 0) + additionalBands;
|
||||||
|
|
||||||
|
const tileGrid = new TileGrid({
|
||||||
|
extent: extent,
|
||||||
|
minZoom: minZoom,
|
||||||
|
origin: origin,
|
||||||
|
resolutions: resolutions,
|
||||||
|
tileSizes: tileSizes,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tileGrid = tileGrid;
|
||||||
|
|
||||||
|
this.setLoader(this.loadTile_.bind(this));
|
||||||
|
this.setState(State.READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTile_(z, x, y) {
|
||||||
|
const size = toSize(this.tileGrid.getTileSize(z));
|
||||||
|
|
||||||
|
const sourceCount = this.sourceImagery_.length;
|
||||||
|
const requests = new Array(sourceCount);
|
||||||
|
const addAlpha = this.addAlpha_;
|
||||||
|
const bandCount = this.bandCount;
|
||||||
|
const samplesPerPixel = this.samplesPerPixel_;
|
||||||
|
const sourceInfo = this.sourceInfo_;
|
||||||
|
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
|
||||||
|
const source = sourceInfo[sourceIndex];
|
||||||
|
const resolutionFactor = this.resolutionFactors_[sourceIndex];
|
||||||
|
const pixelBounds = [
|
||||||
|
Math.round(x * (size[0] * resolutionFactor)),
|
||||||
|
Math.round(y * (size[1] * resolutionFactor)),
|
||||||
|
Math.round((x + 1) * (size[0] * resolutionFactor)),
|
||||||
|
Math.round((y + 1) * (size[1] * resolutionFactor)),
|
||||||
|
];
|
||||||
|
const image = this.sourceImagery_[sourceIndex][z];
|
||||||
|
requests[sourceIndex] = image.readRasters({
|
||||||
|
window: pixelBounds,
|
||||||
|
width: size[0],
|
||||||
|
height: size[1],
|
||||||
|
samples: source.bands,
|
||||||
|
fillValue: source.nodata,
|
||||||
|
pool: getWorkerPool(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const pixelCount = size[0] * size[1];
|
||||||
|
const dataLength = pixelCount * bandCount;
|
||||||
|
const nodataValues = this.nodataValues_;
|
||||||
|
|
||||||
|
return Promise.all(requests).then(function (sourceSamples) {
|
||||||
|
const data = new Uint8ClampedArray(dataLength);
|
||||||
|
let dataIndex = 0;
|
||||||
|
for (let pixelIndex = 0; pixelIndex < pixelCount; ++pixelIndex) {
|
||||||
|
let transparent = addAlpha;
|
||||||
|
for (let sourceIndex = 0; sourceIndex < sourceCount; ++sourceIndex) {
|
||||||
|
const source = sourceInfo[sourceIndex];
|
||||||
|
let min = source.min;
|
||||||
|
if (min === undefined) {
|
||||||
|
min = getMinForDataType(sourceSamples[sourceIndex][0]);
|
||||||
|
}
|
||||||
|
let max = source.max;
|
||||||
|
if (max === undefined) {
|
||||||
|
max = getMaxForDataType(sourceSamples[sourceIndex][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gain = 255 / (max - min);
|
||||||
|
const bias = -min * gain;
|
||||||
|
|
||||||
|
for (
|
||||||
|
let sampleIndex = 0;
|
||||||
|
sampleIndex < samplesPerPixel[sourceIndex];
|
||||||
|
++sampleIndex
|
||||||
|
) {
|
||||||
|
const sourceValue =
|
||||||
|
sourceSamples[sourceIndex][sampleIndex][pixelIndex];
|
||||||
|
|
||||||
|
const value = gain * sourceValue + bias;
|
||||||
|
if (!addAlpha) {
|
||||||
|
data[dataIndex] = value;
|
||||||
|
} else {
|
||||||
|
let nodata = source.nodata;
|
||||||
|
if (nodata === undefined) {
|
||||||
|
let bandIndex;
|
||||||
|
if (source.bands) {
|
||||||
|
bandIndex = source.bands[sampleIndex];
|
||||||
|
} else {
|
||||||
|
bandIndex = sampleIndex;
|
||||||
|
}
|
||||||
|
nodata = nodataValues[sourceIndex][bandIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceValue !== nodata) {
|
||||||
|
transparent = false;
|
||||||
|
data[dataIndex] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addAlpha) {
|
||||||
|
if (!transparent) {
|
||||||
|
data[dataIndex] = 255;
|
||||||
|
}
|
||||||
|
dataIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GeoTIFFSource;
|
||||||
+111
-11
@@ -3,21 +3,29 @@
|
|||||||
* @module ol/style/expressions
|
* @module ol/style/expressions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {Uniforms} from '../renderer/webgl/TileLayer.js';
|
||||||
import {asArray, isStringColor} from '../color.js';
|
import {asArray, isStringColor} from '../color.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base type used for literal style parameters; can be a number literal or the output of an operator,
|
* Base type used for literal style parameters; can be a number literal or the output of an operator,
|
||||||
* which in turns takes {@link ExpressionValue} arguments.
|
* which in turns takes {@link import("./expressions.js").ExpressionValue} arguments.
|
||||||
*
|
*
|
||||||
* The following operators can be used:
|
* The following operators can be used:
|
||||||
*
|
*
|
||||||
* * Reading operators:
|
* * Reading operators:
|
||||||
|
* * `['band', bandIndex, xOffset, yOffset]` For tile layers only. Fetches pixel values from band
|
||||||
|
* `bandIndex` of the source's data. The first `bandIndex` of the source data is `1`. Fetched values
|
||||||
|
* are in the 0..1 range. {@link import("../source/TileImage.js").default} sources have 4 bands: red,
|
||||||
|
* green, blue and alpha. {@link import("../source/DataTile.js").default} sources can have any number
|
||||||
|
* of bands, depending on the underlying data source and
|
||||||
|
* {@link import("../source/GeoTIFF.js").Options configuration}. `xOffset` and `yOffset` are optional
|
||||||
|
* and allow specifying pixel offsets for x and y. This is used for sampling data from neighboring pixels.
|
||||||
* * `['get', 'attributeName']` fetches a feature attribute (it will be prefixed by `a_` in the shader)
|
* * `['get', 'attributeName']` fetches a feature attribute (it will be prefixed by `a_` in the shader)
|
||||||
* Note: those will be taken from the attributes provided to the renderer
|
* Note: those will be taken from the attributes provided to the renderer
|
||||||
* * `['var', 'varName']` fetches a value from the style variables, or 0 if undefined
|
|
||||||
* * `['time']` returns the time in seconds since the creation of the layer
|
|
||||||
* * `['zoom']` returns the current zoom level
|
|
||||||
* * `['resolution']` returns the current resolution
|
* * `['resolution']` returns the current resolution
|
||||||
|
* * `['time']` returns the time in seconds since the creation of the layer
|
||||||
|
* * `['var', 'varName']` fetches a value from the style variables, or 0 if undefined
|
||||||
|
* * `['zoom']` returns the current zoom level
|
||||||
*
|
*
|
||||||
* * Math operators:
|
* * Math operators:
|
||||||
* * `['*', value1, value2]` multiplies `value1` by `value2`
|
* * `['*', value1, value2]` multiplies `value1` by `value2`
|
||||||
@@ -27,6 +35,10 @@ import {asArray, isStringColor} from '../color.js';
|
|||||||
* * `['clamp', value, low, high]` clamps `value` between `low` and `high`
|
* * `['clamp', value, low, high]` clamps `value` between `low` and `high`
|
||||||
* * `['%', value1, value2]` returns the result of `value1 % value2` (modulo)
|
* * `['%', value1, value2]` returns the result of `value1 % value2` (modulo)
|
||||||
* * `['^', value1, value2]` returns the value of `value1` raised to the `value2` power
|
* * `['^', value1, value2]` returns the value of `value1` raised to the `value2` power
|
||||||
|
* * `['abs', value1]` returns the absolute value of `value1`
|
||||||
|
* * `['sin', value1]` returns the sine of `value1`
|
||||||
|
* * `['cos', value1]` returns the cosine of `value1`
|
||||||
|
* * `['atan', value1, value2]` returns `atan2(value1, value2)`. If `value2` is not provided, returns `atan(value1)`
|
||||||
*
|
*
|
||||||
* * Transform operators:
|
* * Transform operators:
|
||||||
* * `['case', condition1, output1, ...conditionN, outputN, fallback]` selects the first output whose corresponding
|
* * `['case', condition1, output1, ...conditionN, outputN, fallback]` selects the first output whose corresponding
|
||||||
@@ -73,6 +85,7 @@ import {asArray, isStringColor} from '../color.js';
|
|||||||
* * {@link module:ol/color~Color}
|
* * {@link module:ol/color~Color}
|
||||||
*
|
*
|
||||||
* @typedef {Array<*>|import("../color.js").Color|string|number|boolean} ExpressionValue
|
* @typedef {Array<*>|import("../color.js").Color|string|number|boolean} ExpressionValue
|
||||||
|
* @api
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,6 +183,7 @@ export function isTypeUnique(valueType) {
|
|||||||
* @property {Array<string>} variables List of variables used in the expression; contains **unprefixed names**
|
* @property {Array<string>} variables List of variables used in the expression; contains **unprefixed names**
|
||||||
* @property {Array<string>} attributes List of attributes used in the expression; contains **unprefixed names**
|
* @property {Array<string>} attributes List of attributes used in the expression; contains **unprefixed names**
|
||||||
* @property {Object<string, number>} stringLiteralsMap This object maps all encountered string values to a number
|
* @property {Object<string, number>} stringLiteralsMap This object maps all encountered string values to a number
|
||||||
|
* @property {number} [bandCount] Number of bands per pixel.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -402,6 +416,43 @@ Operators['var'] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Operators['band'] = {
|
||||||
|
getReturnType: function (args) {
|
||||||
|
return ValueTypes.NUMBER;
|
||||||
|
},
|
||||||
|
toGlsl: function (context, args) {
|
||||||
|
assertArgsMinCount(args, 1);
|
||||||
|
assertArgsMaxCount(args, 3);
|
||||||
|
const band = args[0];
|
||||||
|
if (typeof band !== 'number') {
|
||||||
|
throw new Error('Band index must be a number');
|
||||||
|
}
|
||||||
|
const zeroBasedBand = band - 1;
|
||||||
|
const colorIndex = Math.floor(zeroBasedBand / 4);
|
||||||
|
let bandIndex = zeroBasedBand % 4;
|
||||||
|
if (band === context.bandCount && bandIndex === 1) {
|
||||||
|
// LUMINANCE_ALPHA - band 1 assigned to rgb and band 2 assigned to alpha
|
||||||
|
bandIndex = 3;
|
||||||
|
}
|
||||||
|
if (args.length === 1) {
|
||||||
|
return `color${colorIndex}[${bandIndex}]`;
|
||||||
|
} else {
|
||||||
|
const xOffset = args[1];
|
||||||
|
const yOffset = args[2] || 0;
|
||||||
|
assertNumber(xOffset);
|
||||||
|
assertNumber(yOffset);
|
||||||
|
const uniformName = Uniforms.TILE_TEXTURE_PREFIX + colorIndex;
|
||||||
|
return `texture2D(${uniformName}, v_textureCoord + vec2(${expressionToGlsl(
|
||||||
|
context,
|
||||||
|
xOffset
|
||||||
|
)} / ${Uniforms.TEXTURE_PIXEL_WIDTH}, ${expressionToGlsl(
|
||||||
|
context,
|
||||||
|
yOffset
|
||||||
|
)} / ${Uniforms.TEXTURE_PIXEL_HEIGHT}))[${bandIndex}]`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Operators['time'] = {
|
Operators['time'] = {
|
||||||
getReturnType: function (args) {
|
getReturnType: function (args) {
|
||||||
return ValueTypes.NUMBER;
|
return ValueTypes.NUMBER;
|
||||||
@@ -529,6 +580,56 @@ Operators['^'] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Operators['abs'] = {
|
||||||
|
getReturnType: function (args) {
|
||||||
|
return ValueTypes.NUMBER;
|
||||||
|
},
|
||||||
|
toGlsl: function (context, args) {
|
||||||
|
assertArgsCount(args, 1);
|
||||||
|
assertNumbers(args);
|
||||||
|
return `abs(${expressionToGlsl(context, args[0])})`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Operators['sin'] = {
|
||||||
|
getReturnType: function (args) {
|
||||||
|
return ValueTypes.NUMBER;
|
||||||
|
},
|
||||||
|
toGlsl: function (context, args) {
|
||||||
|
assertArgsCount(args, 1);
|
||||||
|
assertNumbers(args);
|
||||||
|
return `sin(${expressionToGlsl(context, args[0])})`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Operators['cos'] = {
|
||||||
|
getReturnType: function (args) {
|
||||||
|
return ValueTypes.NUMBER;
|
||||||
|
},
|
||||||
|
toGlsl: function (context, args) {
|
||||||
|
assertArgsCount(args, 1);
|
||||||
|
assertNumbers(args);
|
||||||
|
return `cos(${expressionToGlsl(context, args[0])})`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Operators['atan'] = {
|
||||||
|
getReturnType: function (args) {
|
||||||
|
return ValueTypes.NUMBER;
|
||||||
|
},
|
||||||
|
toGlsl: function (context, args) {
|
||||||
|
assertArgsMinCount(args, 1);
|
||||||
|
assertArgsMaxCount(args, 2);
|
||||||
|
assertNumbers(args);
|
||||||
|
return args.length === 2
|
||||||
|
? `atan(${expressionToGlsl(context, args[0])}, ${expressionToGlsl(
|
||||||
|
context,
|
||||||
|
args[1]
|
||||||
|
)})`
|
||||||
|
: `atan(${expressionToGlsl(context, args[0])})`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Operators['>'] = {
|
Operators['>'] = {
|
||||||
getReturnType: function (args) {
|
getReturnType: function (args) {
|
||||||
return ValueTypes.BOOLEAN;
|
return ValueTypes.BOOLEAN;
|
||||||
@@ -748,17 +849,16 @@ Operators['interpolate'] = {
|
|||||||
assertUniqueInferredType(args, outputType);
|
assertUniqueInferredType(args, outputType);
|
||||||
|
|
||||||
const input = expressionToGlsl(context, args[1]);
|
const input = expressionToGlsl(context, args[1]);
|
||||||
let result = null;
|
const exponent = numberToGlsl(interpolation);
|
||||||
|
|
||||||
|
let result = '';
|
||||||
for (let i = 2; i < args.length - 2; i += 2) {
|
for (let i = 2; i < args.length - 2; i += 2) {
|
||||||
const stop1 = expressionToGlsl(context, args[i]);
|
const stop1 = expressionToGlsl(context, args[i]);
|
||||||
const output1 = expressionToGlsl(context, args[i + 1], outputType);
|
const output1 =
|
||||||
|
result || expressionToGlsl(context, args[i + 1], outputType);
|
||||||
const stop2 = expressionToGlsl(context, args[i + 2]);
|
const stop2 = expressionToGlsl(context, args[i + 2]);
|
||||||
const output2 = expressionToGlsl(context, args[i + 3], outputType);
|
const output2 = expressionToGlsl(context, args[i + 3], outputType);
|
||||||
result = `mix(${
|
result = `mix(${output1}, ${output2}, pow(clamp((${input} - ${stop1}) / (${stop2} - ${stop1}), 0.0, 1.0), ${exponent}))`;
|
||||||
result || output1
|
|
||||||
}, ${output2}, pow(clamp((${input} - ${stop1}) / (${stop2} - ${stop1}), 0.0, 1.0), ${numberToGlsl(
|
|
||||||
interpolation
|
|
||||||
)}))`;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -434,9 +434,10 @@ class WebGLHelper extends Disposable {
|
|||||||
* Post process passes will be initialized here, the first one being bound as a render target for
|
* Post process passes will be initialized here, the first one being bound as a render target for
|
||||||
* subsequent draw calls.
|
* subsequent draw calls.
|
||||||
* @param {import("../PluggableMap.js").FrameState} frameState current frame state
|
* @param {import("../PluggableMap.js").FrameState} frameState current frame state
|
||||||
|
* @param {boolean} [opt_disableAlphaBlend] If true, no alpha blending will happen.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
prepareDraw(frameState) {
|
prepareDraw(frameState, opt_disableAlphaBlend) {
|
||||||
const gl = this.getGL();
|
const gl = this.getGL();
|
||||||
const canvas = this.getCanvas();
|
const canvas = this.getCanvas();
|
||||||
const size = frameState.size;
|
const size = frameState.size;
|
||||||
@@ -459,7 +460,10 @@ class WebGLHelper extends Disposable {
|
|||||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
gl.enable(gl.BLEND);
|
gl.enable(gl.BLEND);
|
||||||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
gl.blendFunc(
|
||||||
|
gl.ONE,
|
||||||
|
opt_disableAlphaBlend ? gl.ZERO : gl.ONE_MINUS_SRC_ALPHA
|
||||||
|
);
|
||||||
|
|
||||||
gl.useProgram(this.currentProgram_);
|
gl.useProgram(this.currentProgram_);
|
||||||
this.applyFrameState(frameState);
|
this.applyFrameState(frameState);
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
/**
|
||||||
|
* @module ol/webgl/TileTexture
|
||||||
|
*/
|
||||||
|
|
||||||
|
import EventTarget from '../events/Target.js';
|
||||||
|
import EventType from '../events/EventType.js';
|
||||||
|
import ImageTile from '../ImageTile.js';
|
||||||
|
import TileState from '../TileState.js';
|
||||||
|
import WebGLArrayBuffer from './Buffer.js';
|
||||||
|
import {ARRAY_BUFFER, STATIC_DRAW} from '../webgl.js';
|
||||||
|
import {toSize} from '../size.js';
|
||||||
|
|
||||||
|
function bindAndConfigure(gl, texture) {
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
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);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WebGLRenderingContext} gl The WebGL context.
|
||||||
|
* @param {WebGLTexture} texture The texture.
|
||||||
|
* @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image The image.
|
||||||
|
*/
|
||||||
|
function uploadImageTexture(gl, texture, image) {
|
||||||
|
bindAndConfigure(gl, texture);
|
||||||
|
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WebGLRenderingContext} gl The WebGL context.
|
||||||
|
* @param {WebGLTexture} texture The texture.
|
||||||
|
* @param {import("../DataTile.js").Data} data The pixel data.
|
||||||
|
* @param {import("../size.js").Size} size The pixel size.
|
||||||
|
* @param {number} bandCount The band count.
|
||||||
|
*/
|
||||||
|
function uploadDataTexture(gl, texture, data, size, bandCount) {
|
||||||
|
bindAndConfigure(gl, texture);
|
||||||
|
|
||||||
|
let format;
|
||||||
|
switch (bandCount) {
|
||||||
|
case 1: {
|
||||||
|
format = gl.LUMINANCE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
format = gl.LUMINANCE_ALPHA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
format = gl.RGB;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
format = gl.RGBA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error(`Unsupported number of bands: ${bandCount}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.texImage2D(
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
format,
|
||||||
|
size[0],
|
||||||
|
size[1],
|
||||||
|
0,
|
||||||
|
format,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TileTexture extends EventTarget {
|
||||||
|
/**
|
||||||
|
* @param {import("../DataTile.js").default|import("../ImageTile.js").default} tile The tile.
|
||||||
|
* @param {import("../tilegrid/TileGrid.js").default} grid Tile grid.
|
||||||
|
* @param {import("../webgl/Helper.js").default} helper WebGL helper.
|
||||||
|
*/
|
||||||
|
constructor(tile, grid, helper) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../DataTile.js").default|import("../ImageTile.js").default}
|
||||||
|
*/
|
||||||
|
this.tile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array<WebGLTexture>}
|
||||||
|
*/
|
||||||
|
this.textures = [];
|
||||||
|
this.handleTileChange_ = this.handleTileChange_.bind(this);
|
||||||
|
|
||||||
|
this.size = toSize(grid.getTileSize(tile.tileCoord[0]));
|
||||||
|
|
||||||
|
this.bandCount = NaN;
|
||||||
|
|
||||||
|
this.helper_ = helper;
|
||||||
|
|
||||||
|
const coords = new WebGLArrayBuffer(ARRAY_BUFFER, STATIC_DRAW);
|
||||||
|
coords.fromArray([
|
||||||
|
0, // P0
|
||||||
|
1,
|
||||||
|
1, // P1
|
||||||
|
1,
|
||||||
|
1, // P2
|
||||||
|
0,
|
||||||
|
0, // P3
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
helper.flushBufferData(coords);
|
||||||
|
|
||||||
|
this.coords = coords;
|
||||||
|
this.setTile(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("../DataTile.js").default|import("../ImageTile.js").default} tile Tile.
|
||||||
|
*/
|
||||||
|
setTile(tile) {
|
||||||
|
if (tile !== this.tile) {
|
||||||
|
if (this.tile) {
|
||||||
|
this.tile.removeEventListener(EventType.CHANGE, this.handleTileChange_);
|
||||||
|
}
|
||||||
|
this.tile = tile;
|
||||||
|
this.textures.length = 0;
|
||||||
|
this.loaded = tile.getState() === TileState.LOADED;
|
||||||
|
if (this.loaded) {
|
||||||
|
this.uploadTile_();
|
||||||
|
} else {
|
||||||
|
tile.addEventListener(EventType.CHANGE, this.handleTileChange_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadTile_() {
|
||||||
|
const gl = this.helper_.getGL();
|
||||||
|
const tile = this.tile;
|
||||||
|
|
||||||
|
if (tile instanceof ImageTile) {
|
||||||
|
const texture = gl.createTexture();
|
||||||
|
this.textures.push(texture);
|
||||||
|
this.bandCount = 4;
|
||||||
|
uploadImageTexture(gl, texture, tile.getImage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = tile.getData();
|
||||||
|
const pixelCount = this.size[0] * this.size[1];
|
||||||
|
this.bandCount = data.byteLength / pixelCount;
|
||||||
|
const textureCount = Math.ceil(this.bandCount / 4);
|
||||||
|
|
||||||
|
if (textureCount === 1) {
|
||||||
|
const texture = gl.createTexture();
|
||||||
|
this.textures.push(texture);
|
||||||
|
uploadDataTexture(gl, texture, data, this.size, this.bandCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const textureDataArrays = new Array(textureCount);
|
||||||
|
for (let textureIndex = 0; textureIndex < textureCount; ++textureIndex) {
|
||||||
|
const texture = gl.createTexture();
|
||||||
|
this.textures.push(texture);
|
||||||
|
|
||||||
|
const bandCount =
|
||||||
|
textureIndex < textureCount - 1 ? 4 : this.bandCount % 4;
|
||||||
|
textureDataArrays[textureIndex] = new Uint8Array(pixelCount * bandCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueCount = pixelCount * this.bandCount;
|
||||||
|
for (let dataIndex = 0; dataIndex < valueCount; ++dataIndex) {
|
||||||
|
const bandIndex = dataIndex % this.bandCount;
|
||||||
|
const textureBandIndex = bandIndex % 4;
|
||||||
|
const textureIndex = Math.floor(bandIndex / 4);
|
||||||
|
const bandCount =
|
||||||
|
textureIndex < textureCount - 1 ? 4 : this.bandCount % 4;
|
||||||
|
const pixelIndex = Math.floor(dataIndex / this.bandCount);
|
||||||
|
textureDataArrays[textureIndex][
|
||||||
|
pixelIndex * bandCount + textureBandIndex
|
||||||
|
] = data[dataIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let textureIndex = 0; textureIndex < textureCount; ++textureIndex) {
|
||||||
|
const bandCount =
|
||||||
|
textureIndex < textureCount - 1 ? 4 : this.bandCount % 4;
|
||||||
|
const texture = this.textures[textureIndex];
|
||||||
|
const data = textureDataArrays[textureIndex];
|
||||||
|
uploadDataTexture(gl, texture, data, this.size, bandCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTileChange_() {
|
||||||
|
if (this.tile.getState() === TileState.LOADED) {
|
||||||
|
this.loaded = true;
|
||||||
|
this.uploadTile_();
|
||||||
|
this.dispatchEvent(EventType.CHANGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disposeInternal() {
|
||||||
|
const gl = this.helper_.getGL();
|
||||||
|
this.helper_.deleteBuffer(this.coords);
|
||||||
|
for (let i = 0; i < this.textures.length; ++i) {
|
||||||
|
gl.deleteTexture(this.textures[i]);
|
||||||
|
}
|
||||||
|
this.tile.removeEventListener(EventType.CHANGE, this.handleTileChange_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TileTexture;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/* eslint-disable sort-imports-es6-autofix/sort-imports-es6 */
|
||||||
|
import 'regenerator-runtime/runtime.js';
|
||||||
|
import 'geotiff/src/decoder.worker.js';
|
||||||
|
|
||||||
|
export let create;
|
||||||
@@ -71,6 +71,13 @@ module.exports = function (karma) {
|
|||||||
webpack: {
|
webpack: {
|
||||||
devtool: 'inline-source-map',
|
devtool: 'inline-source-map',
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
|
resolve: {
|
||||||
|
fallback: {
|
||||||
|
fs: false,
|
||||||
|
http: false,
|
||||||
|
https: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import DataTile from '../../../../src/ol/DataTile.js';
|
||||||
|
import TileState from '../../../../src/ol/TileState.js';
|
||||||
|
|
||||||
|
describe('ol.DataTile', function () {
|
||||||
|
/** @type {Promise<import('../../../../src/ol/DataTile.js').Data} */
|
||||||
|
let loader;
|
||||||
|
beforeEach(function () {
|
||||||
|
loader = function () {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 256;
|
||||||
|
canvas.height = 256;
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.fillStyle = 'red';
|
||||||
|
context.fillRect(0, 0, 256, 256);
|
||||||
|
resolve(context.getImageData(0, 0, 256, 256).data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', function () {
|
||||||
|
it('sets options', function () {
|
||||||
|
const tileCoord = [0, 0, 0];
|
||||||
|
const tile = new DataTile({
|
||||||
|
tileCoord: tileCoord,
|
||||||
|
loader: loader,
|
||||||
|
transition: 200,
|
||||||
|
});
|
||||||
|
expect(tile.tileCoord).to.equal(tileCoord);
|
||||||
|
expect(tile.transition_).to.be(200);
|
||||||
|
expect(tile.loader_).to.equal(loader);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#load()', function () {
|
||||||
|
it('handles loading states correctly', function (done) {
|
||||||
|
const tileCoord = [0, 0, 0];
|
||||||
|
const tile = new DataTile({
|
||||||
|
tileCoord: tileCoord,
|
||||||
|
loader: loader,
|
||||||
|
});
|
||||||
|
expect(tile.getState()).to.be(TileState.IDLE);
|
||||||
|
tile.load();
|
||||||
|
expect(tile.getState()).to.be(TileState.LOADING);
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(tile.getState()).to.be(TileState.LOADED);
|
||||||
|
done();
|
||||||
|
}, 16);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import DataTileSource from '../../../../../src/ol/source/DataTile.js';
|
||||||
|
import Map from '../../../../../src/ol/Map.js';
|
||||||
|
import View from '../../../../../src/ol/View.js';
|
||||||
|
import WebGLHelper from '../../../../../src/ol/webgl/Helper.js';
|
||||||
|
import WebGLTileLayer from '../../../../../src/ol/layer/WebGLTile.js';
|
||||||
|
import {createCanvasContext2D} from '../../../../../src/ol/dom.js';
|
||||||
|
|
||||||
|
describe('ol.layer.Tile', function () {
|
||||||
|
/** @type {WebGLTileLayer} */
|
||||||
|
let layer;
|
||||||
|
/** @type {Map} */
|
||||||
|
let map, target;
|
||||||
|
|
||||||
|
beforeEach(function (done) {
|
||||||
|
layer = new WebGLTileLayer({
|
||||||
|
className: 'testlayer',
|
||||||
|
source: new DataTileSource({
|
||||||
|
loader(z, x, y) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolve(new ImageData(256, 256));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
style: {
|
||||||
|
variables: {
|
||||||
|
r: 0,
|
||||||
|
g: 255,
|
||||||
|
b: 0,
|
||||||
|
},
|
||||||
|
color: ['color', ['var', 'r'], ['var', 'g'], ['var', 'b']],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
target = document.createElement('div');
|
||||||
|
target.style.width = '100px';
|
||||||
|
target.style.height = '100px';
|
||||||
|
document.body.appendChild(target);
|
||||||
|
map = new Map({
|
||||||
|
target: target,
|
||||||
|
layers: [layer],
|
||||||
|
view: new View({
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
map.once('rendercomplete', () => done());
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
map.setTarget(null);
|
||||||
|
document.body.removeChild(target);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates fragment and vertex shaders', function () {
|
||||||
|
const compileShaderSpy = sinon.spy(WebGLHelper.prototype, 'compileShader');
|
||||||
|
layer.createRenderer();
|
||||||
|
compileShaderSpy.restore();
|
||||||
|
expect(compileShaderSpy.callCount).to.be(2);
|
||||||
|
expect(compileShaderSpy.getCall(0).args[0].replace(/[ \n]+/g, ' ')).to.be(
|
||||||
|
`
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp float;
|
||||||
|
#else
|
||||||
|
precision mediump float;
|
||||||
|
#endif
|
||||||
|
varying vec2 v_textureCoord;
|
||||||
|
uniform float u_transitionAlpha;
|
||||||
|
uniform float u_texturePixelWidth;
|
||||||
|
uniform float u_texturePixelHeight;
|
||||||
|
uniform float u_resolution;
|
||||||
|
uniform float u_zoom;
|
||||||
|
uniform float u_var_r;
|
||||||
|
uniform float u_var_g;
|
||||||
|
uniform float u_var_b;
|
||||||
|
uniform sampler2D u_tileTexture0;
|
||||||
|
void main() {
|
||||||
|
vec4 color0 = texture2D(u_tileTexture0, v_textureCoord);
|
||||||
|
vec4 color = color0;
|
||||||
|
color = vec4(u_var_r / 255.0, u_var_g / 255.0, u_var_b / 255.0, 1.0);
|
||||||
|
if (color.a == 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
gl_FragColor = color;
|
||||||
|
gl_FragColor.rgb *= gl_FragColor.a;
|
||||||
|
gl_FragColor *= u_transitionAlpha;
|
||||||
|
}`.replace(/[ \n]+/g, ' ')
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(compileShaderSpy.getCall(1).args[0].replace(/[ \n]+/g, ' ')).to.be(
|
||||||
|
`
|
||||||
|
attribute vec2 a_textureCoord;
|
||||||
|
uniform mat4 u_tileTransform;
|
||||||
|
uniform float u_depth;
|
||||||
|
|
||||||
|
varying vec2 v_textureCoord;
|
||||||
|
void main() {
|
||||||
|
v_textureCoord = a_textureCoord;
|
||||||
|
gl_Position = u_tileTransform * vec4(a_textureCoord, u_depth, 1.0);
|
||||||
|
}
|
||||||
|
`.replace(/[ \n]+/g, ' ')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates style variables', function (done) {
|
||||||
|
layer.updateStyleVariables({
|
||||||
|
r: 255,
|
||||||
|
g: 0,
|
||||||
|
b: 255,
|
||||||
|
});
|
||||||
|
expect(layer.styleVariables_['r']).to.be(255);
|
||||||
|
const targetContext = createCanvasContext2D(100, 100);
|
||||||
|
layer.on('postrender', () => {
|
||||||
|
targetContext.clearRect(0, 0, 100, 100);
|
||||||
|
targetContext.drawImage(target.querySelector('.testlayer'), 0, 0);
|
||||||
|
});
|
||||||
|
map.once('rendercomplete', () => {
|
||||||
|
expect(Array.from(targetContext.getImageData(0, 0, 1, 1).data)).to.eql([
|
||||||
|
255, 0, 255, 255,
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws on incorrect style configs', function () {
|
||||||
|
function incorrectStyle() {
|
||||||
|
layer.style_ = {
|
||||||
|
variables: {
|
||||||
|
'red': 25,
|
||||||
|
'green': 200,
|
||||||
|
},
|
||||||
|
exposure: 0,
|
||||||
|
contrast: 0,
|
||||||
|
saturation: 0,
|
||||||
|
color: ['color', ['var', 'red'], ['var', 'green'], ['var', 'blue']],
|
||||||
|
};
|
||||||
|
layer.createRenderer();
|
||||||
|
}
|
||||||
|
expect(incorrectStyle).to.throwException(); // missing 'blue' in styleVariables
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import TileQueue from '../../../../../../src/ol/TileQueue.js';
|
||||||
|
import TileState from '../../../../../../src/ol/TileState.js';
|
||||||
|
import WebGLTileLayer from '../../../../../../src/ol/layer/WebGLTile.js';
|
||||||
|
import {DataTile} from '../../../../../../src/ol/source.js';
|
||||||
|
import {VOID} from '../../../../../../src/ol/functions.js';
|
||||||
|
import {create} from '../../../../../../src/ol/transform.js';
|
||||||
|
import {createCanvasContext2D} from '../../../../../../src/ol/dom.js';
|
||||||
|
import {get} from '../../../../../../src/ol/proj.js';
|
||||||
|
|
||||||
|
describe('ol.renderer.webgl.TileLayer', function () {
|
||||||
|
/** @type {import("../../../../../../src/ol/renderer/webgl/TileLayer.js").default} */
|
||||||
|
let renderer;
|
||||||
|
/** @type {WebGLTileLayer} */
|
||||||
|
let tileLayer;
|
||||||
|
/** @type {import('../../../../../../src/ol/PluggableMap.js').FrameState} */
|
||||||
|
let frameState;
|
||||||
|
beforeEach(function () {
|
||||||
|
const size = 256;
|
||||||
|
const context = createCanvasContext2D(size, size);
|
||||||
|
|
||||||
|
tileLayer = new WebGLTileLayer({
|
||||||
|
source: new DataTile({
|
||||||
|
loader: function (z, x, y) {
|
||||||
|
context.clearRect(0, 0, size, size);
|
||||||
|
context.fillStyle = 'rgba(100, 100, 100, 0.5)';
|
||||||
|
context.fillRect(0, 0, size, size);
|
||||||
|
const data = context.getImageData(0, 0, size, size).data;
|
||||||
|
return Promise.resolve(data);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
renderer = tileLayer.createRenderer();
|
||||||
|
|
||||||
|
const proj = get('EPSG:3857');
|
||||||
|
frameState = {
|
||||||
|
layerStatesArray: [tileLayer.getLayerState()],
|
||||||
|
layerIndex: 0,
|
||||||
|
extent: proj.getExtent(),
|
||||||
|
pixelRatio: 1,
|
||||||
|
pixelToCoordinateTransform: create(),
|
||||||
|
postRenderFunctions: [],
|
||||||
|
time: Date.now(),
|
||||||
|
viewHints: [],
|
||||||
|
viewState: {
|
||||||
|
center: [0, 0],
|
||||||
|
resolution: 156543.03392804097,
|
||||||
|
projection: proj,
|
||||||
|
},
|
||||||
|
size: [256, 256],
|
||||||
|
usedTiles: {},
|
||||||
|
wantedTiles: {},
|
||||||
|
tileQueue: new TileQueue(VOID, VOID),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#prepareFrame()', function () {
|
||||||
|
const source = tileLayer.getSource();
|
||||||
|
tileLayer.setSource(null);
|
||||||
|
expect(renderer.prepareFrame(frameState)).to.be(false);
|
||||||
|
tileLayer.setSource(source);
|
||||||
|
expect(renderer.prepareFrame(frameState)).to.be(true);
|
||||||
|
const tileGrid = source.getTileGrid();
|
||||||
|
tileLayer.setExtent(tileGrid.getTileCoordExtent([2, 0, 0]));
|
||||||
|
frameState.resolution = tileGrid.getResolution(2);
|
||||||
|
frameState.extent = tileGrid.getTileCoordExtent([2, 2, 2]);
|
||||||
|
frameState.layerStatesArray = [tileLayer.getLayerState()];
|
||||||
|
expect(renderer.prepareFrame(frameState)).to.be(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#renderFrame()', function () {
|
||||||
|
const rendered = renderer.renderFrame(frameState);
|
||||||
|
expect(rendered).to.be.a(HTMLCanvasElement);
|
||||||
|
expect(frameState.tileQueue.getCount()).to.be(1);
|
||||||
|
expect(Object.keys(frameState.wantedTiles).length).to.be(1);
|
||||||
|
expect(frameState.postRenderFunctions.length).to.be(0); // no tile expired
|
||||||
|
expect(renderer.tileTextureCache_.count_).to.be(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#isDrawableTile()', function (done) {
|
||||||
|
const tile = tileLayer.getSource().getTile(0, 0, 0);
|
||||||
|
expect(renderer.isDrawableTile(tile)).to.be(false);
|
||||||
|
tileLayer.getSource().on('tileloadend', () => {
|
||||||
|
expect(renderer.isDrawableTile(tile)).to.be(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
const errorTile = tileLayer.getSource().getTile(1, 0, 1);
|
||||||
|
errorTile.setState(TileState.ERROR);
|
||||||
|
tileLayer.setUseInterimTilesOnError(false);
|
||||||
|
expect(renderer.isDrawableTile(errorTile)).to.be(true);
|
||||||
|
tileLayer.setUseInterimTilesOnError(true);
|
||||||
|
expect(renderer.isDrawableTile(errorTile)).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import DataTile from '../../../../../src/ol/DataTile.js';
|
||||||
|
import DataTileSource from '../../../../../src/ol/source/DataTile.js';
|
||||||
|
import TileState from '../../../../../src/ol/TileState.js';
|
||||||
|
|
||||||
|
describe('ol.source.DataTile', function () {
|
||||||
|
/** @type {DataTileSource} */
|
||||||
|
let source;
|
||||||
|
beforeEach(function () {
|
||||||
|
const loader = function (z, x, y) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 256;
|
||||||
|
canvas.height = 256;
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
// encode tile coordinate in rgb
|
||||||
|
context.fillStyle = `rgb(${z}, ${x % 255}, ${y % 255})`;
|
||||||
|
context.fillRect(0, 0, 256, 256);
|
||||||
|
resolve(context.getImageData(0, 0, 256, 256).data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
source = new DataTileSource({
|
||||||
|
loader: loader,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getTile()', function () {
|
||||||
|
it('gets tiles and fires a tileloadend event', function (done) {
|
||||||
|
const tile = source.getTile(3, 2, 1);
|
||||||
|
expect(tile).to.be.a(DataTile);
|
||||||
|
expect(tile.state).to.be(TileState.IDLE);
|
||||||
|
|
||||||
|
source.on('tileloadend', () => {
|
||||||
|
expect(tile.state).to.be(TileState.LOADED);
|
||||||
|
// decode tile coordinate from rgb
|
||||||
|
expect(Array.from(tile.getData().slice(0, 3))).to.eql([3, 2, 1]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
tile.load();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import GeoTIFFSource from '../../../../../src/ol/source/GeoTIFF.js';
|
||||||
|
import State from '../../../../../src/ol/source/State.js';
|
||||||
|
import TileState from '../../../../../src/ol/TileState.js';
|
||||||
|
|
||||||
|
describe('ol.source.GeoTIFF', function () {
|
||||||
|
/** @type {GeoTIFFSource} */
|
||||||
|
let source;
|
||||||
|
beforeEach(function () {
|
||||||
|
source = new GeoTIFFSource({
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
url: 'spec/ol/source/images/0-0-0.tif',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('manages load states', function (done) {
|
||||||
|
expect(source.getState()).to.be(State.LOADING);
|
||||||
|
source.on('change', () => {
|
||||||
|
expect(source.getState()).to.be(State.READY);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('configures itself from source metadata', function (done) {
|
||||||
|
source.on('change', () => {
|
||||||
|
expect(source.addAlpha_).to.be(true);
|
||||||
|
expect(source.bandCount).to.be(4);
|
||||||
|
expect(source.nodataValues_).to.eql([[0]]);
|
||||||
|
expect(source.getTileGrid().getResolutions().length).to.be(1);
|
||||||
|
expect(source.projection.getCode()).to.be('EPSG:4326');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads tiles', function (done) {
|
||||||
|
source.on('change', () => {
|
||||||
|
const tile = source.getTile(0, 0, 0);
|
||||||
|
source.on('tileloadend', () => {
|
||||||
|
expect(tile.getState()).to.be(TileState.LOADED);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Binary file not shown.
@@ -235,6 +235,18 @@ describe('ol.style.expressions', function () {
|
|||||||
expect(expressionToGlsl(context, ['^', ['%', ['time'], 10], 2])).to.eql(
|
expect(expressionToGlsl(context, ['^', ['%', ['time'], 10], 2])).to.eql(
|
||||||
'pow(mod(u_time, 10.0), 2.0)'
|
'pow(mod(u_time, 10.0), 2.0)'
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
expressionToGlsl(context, [
|
||||||
|
'abs',
|
||||||
|
['-', ['get', 'attr3'], ['get', 'attr2']],
|
||||||
|
])
|
||||||
|
).to.eql('abs((a_attr3 - a_attr2))');
|
||||||
|
expect(expressionToGlsl(context, ['sin', 1])).to.eql('sin(1.0)');
|
||||||
|
expect(expressionToGlsl(context, ['cos', 1])).to.eql('cos(1.0)');
|
||||||
|
expect(expressionToGlsl(context, ['atan', 1])).to.eql('atan(1.0)');
|
||||||
|
expect(expressionToGlsl(context, ['atan', 1, 0.5])).to.eql(
|
||||||
|
'atan(1.0, 0.5)'
|
||||||
|
);
|
||||||
expect(expressionToGlsl(context, ['>', 10, ['get', 'attr4']])).to.eql(
|
expect(expressionToGlsl(context, ['>', 10, ['get', 'attr4']])).to.eql(
|
||||||
'(10.0 > a_attr4)'
|
'(10.0 > a_attr4)'
|
||||||
);
|
);
|
||||||
@@ -277,6 +289,10 @@ describe('ol.style.expressions', function () {
|
|||||||
expect(
|
expect(
|
||||||
expressionToGlsl(context, ['color', ['get', 'attr4'], 1, 2, 0.5])
|
expressionToGlsl(context, ['color', ['get', 'attr4'], 1, 2, 0.5])
|
||||||
).to.eql('vec4(a_attr4 / 255.0, 1.0 / 255.0, 2.0 / 255.0, 0.5)');
|
).to.eql('vec4(a_attr4 / 255.0, 1.0 / 255.0, 2.0 / 255.0, 0.5)');
|
||||||
|
expect(expressionToGlsl(context, ['band', 1])).to.eql('color0[0]');
|
||||||
|
expect(expressionToGlsl(context, ['band', 1, -1, 2])).to.eql(
|
||||||
|
'texture2D(u_tileTexture0, v_textureCoord + vec2(-1.0 / u_texturePixelWidth, 2.0 / u_texturePixelHeight))[0]'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if the value does not match the type', function () {
|
it('throws if the value does not match the type', function () {
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import DataTile from '../../../../../src/ol/DataTile.js';
|
||||||
|
import DataTileSource from '../../../../../src/ol/source/DataTile.js';
|
||||||
|
import ImageTile from '../../../../../src/ol/ImageTile.js';
|
||||||
|
import TileState from '../../../../../src/ol/TileState.js';
|
||||||
|
import TileTexture from '../../../../../src/ol/webgl/TileTexture.js';
|
||||||
|
import WebGLArrayBuffer from '../../../../../src/ol/webgl/Buffer.js';
|
||||||
|
import WebGLTileLayer from '../../../../../src/ol/layer/WebGLTile.js';
|
||||||
|
import {createCanvasContext2D} from '../../../../../src/ol/dom.js';
|
||||||
|
|
||||||
|
describe('ol.webgl.TileTexture', function () {
|
||||||
|
/** @type {TileTexture} */
|
||||||
|
let tileTexture;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
const layer = new WebGLTileLayer({
|
||||||
|
source: new DataTileSource({
|
||||||
|
loader(z, x, y) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const context = createCanvasContext2D(256, 256);
|
||||||
|
context.fillStyle = `rgb(${z}, ${x % 255}, ${y % 255})`;
|
||||||
|
context.fillRect(0, 0, 256, 256);
|
||||||
|
resolve(context.getImageData(0, 0, 256, 256).data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const renderer =
|
||||||
|
/** @type {import("../../../../../src/ol/renderer/webgl/TileLayer.js").default} */ (
|
||||||
|
layer.createRenderer()
|
||||||
|
);
|
||||||
|
tileTexture = new TileTexture(
|
||||||
|
layer.getSource().getTile(3, 2, 1),
|
||||||
|
layer.getSource().getTileGrid(),
|
||||||
|
renderer.helper
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('constructor', function () {
|
||||||
|
expect(tileTexture.tile.tileCoord).to.eql([3, 2, 1]);
|
||||||
|
expect(tileTexture.coords).to.be.a(WebGLArrayBuffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles data tiles', function (done) {
|
||||||
|
const dataTile = tileTexture.tile;
|
||||||
|
expect(tileTexture.loaded).to.be(false);
|
||||||
|
expect(dataTile.getState()).to.be(TileState.IDLE);
|
||||||
|
tileTexture.addEventListener('change', () => {
|
||||||
|
if (dataTile.getState() === TileState.LOADED) {
|
||||||
|
expect(tileTexture.loaded).to.be(true);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dataTile.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles image tiles', function () {
|
||||||
|
const imageTile = new ImageTile([0, 0, 0], TileState.LOADED);
|
||||||
|
tileTexture.setTile(imageTile);
|
||||||
|
expect(tileTexture.loaded).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registers and unregisters change listener', function () {
|
||||||
|
const tile = tileTexture.tile;
|
||||||
|
expect(tile.getListeners('change').length).to.be(2);
|
||||||
|
tileTexture.dispose();
|
||||||
|
expect(tile.getListeners('change').length).to.be(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates metadata and unregisters change listener when setting a different tile', function (done) {
|
||||||
|
const tile = tileTexture.tile;
|
||||||
|
expect(tile.getListeners('change').length).to.be(2);
|
||||||
|
const differentTile = new DataTile({
|
||||||
|
tileCoord: [1, 0, 1],
|
||||||
|
loader(z, x, y) {
|
||||||
|
return Promise.resolve(new Uint8Array(256 * 256 * 3));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
tileTexture.setTile(differentTile);
|
||||||
|
expect(tile.getListeners('change').length).to.be(1);
|
||||||
|
tileTexture.addEventListener('change', () => {
|
||||||
|
expect(tileTexture.bandCount).to.be(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
differentTile.load();
|
||||||
|
});
|
||||||
|
});
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
@@ -0,0 +1,26 @@
|
|||||||
|
import Map from '../../../../src/ol/Map.js';
|
||||||
|
import TileLayer from '../../../../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../../../../src/ol/View.js';
|
||||||
|
import XYZ from '../../../../src/ol/source/XYZ.js';
|
||||||
|
import {fromLonLat} from '../../../../src/ol/proj.js';
|
||||||
|
|
||||||
|
const center = fromLonLat([8.6, 50.1]);
|
||||||
|
|
||||||
|
new Map({
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new XYZ({
|
||||||
|
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg',
|
||||||
|
transition: 0,
|
||||||
|
crossOrigin: 'anonymous',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
center: center,
|
||||||
|
zoom: 3,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
render();
|
||||||
Reference in New Issue
Block a user