ol-stac 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/events/ErrorEvent.d.ts +17 -0
- package/events/ErrorEvent.d.ts.map +1 -0
- package/{src/ol/events → events}/ErrorEvent.js +9 -11
- package/events/ErrorEvent.js.map +1 -0
- package/layer/STAC.d.ts +422 -0
- package/layer/STAC.d.ts.map +1 -0
- package/layer/STAC.js +698 -0
- package/layer/STAC.js.map +1 -0
- package/layer/stacUtil.d.ts +46 -0
- package/layer/stacUtil.d.ts.map +1 -0
- package/layer/stacUtil.js +154 -0
- package/layer/stacUtil.js.map +1 -0
- package/package.json +3 -85
- package/source/GeoTIFF2.d.ts +35 -0
- package/source/GeoTIFF2.d.ts.map +1 -0
- package/{src/ol/source → source}/GeoTIFF2.js +9 -16
- package/source/GeoTIFF2.js.map +1 -0
- package/.eslintignore +0 -2
- package/.github/workflows/deploy.yml +0 -67
- package/.github/workflows/publish.yml +0 -32
- package/.github/workflows/release.yml +0 -21
- package/.github/workflows/test.yml +0 -85
- package/CHANGELOG.md +0 -14
- package/DEVELOPING.md +0 -104
- package/config/jsdoc/api/conf.json +0 -38
- package/config/jsdoc/api/index.md +0 -1
- package/config/jsdoc/api/readme.md +0 -65
- package/config/jsdoc/api/template/README.md +0 -3
- package/config/jsdoc/api/template/publish.js +0 -743
- package/config/jsdoc/api/template/static/scripts/linenumber.js +0 -11
- package/config/jsdoc/api/template/static/scripts/main.js +0 -305
- package/config/jsdoc/api/template/static/scripts/prettify/Apache-License-2.0.txt +0 -202
- package/config/jsdoc/api/template/static/scripts/prettify/lang-css.js +0 -2
- package/config/jsdoc/api/template/static/scripts/prettify/prettify.js +0 -28
- package/config/jsdoc/api/template/static/styles/carbon.css +0 -65
- package/config/jsdoc/api/template/static/styles/jaguar.css +0 -512
- package/config/jsdoc/api/template/static/styles/prettify-jsdoc.css +0 -111
- package/config/jsdoc/api/template/static/styles/prettify-tomorrow.css +0 -132
- package/config/jsdoc/api/template/static/theme +0 -1
- package/config/jsdoc/api/template/tmpl/container.tmpl +0 -205
- package/config/jsdoc/api/template/tmpl/details.tmpl +0 -84
- package/config/jsdoc/api/template/tmpl/example.tmpl +0 -2
- package/config/jsdoc/api/template/tmpl/examples.tmpl +0 -16
- package/config/jsdoc/api/template/tmpl/exceptions.tmpl +0 -30
- package/config/jsdoc/api/template/tmpl/layout.tmpl +0 -92
- package/config/jsdoc/api/template/tmpl/mainpage.tmpl +0 -14
- package/config/jsdoc/api/template/tmpl/members.tmpl +0 -38
- package/config/jsdoc/api/template/tmpl/method.tmpl +0 -132
- package/config/jsdoc/api/template/tmpl/navigation.tmpl +0 -59
- package/config/jsdoc/api/template/tmpl/observables.tmpl +0 -39
- package/config/jsdoc/api/template/tmpl/params.tmpl +0 -109
- package/config/jsdoc/api/template/tmpl/properties.tmpl +0 -85
- package/config/jsdoc/api/template/tmpl/returns.tmpl +0 -72
- package/config/jsdoc/api/template/tmpl/source.tmpl +0 -8
- package/config/jsdoc/api/template/tmpl/stability.tmpl +0 -7
- package/config/jsdoc/api/template/tmpl/tutorial.tmpl +0 -19
- package/config/jsdoc/api/template/tmpl/type.tmpl +0 -7
- package/config/jsdoc/info/conf.json +0 -22
- package/config/jsdoc/info/publish.js +0 -175
- package/config/jsdoc/package.json +0 -4
- package/config/jsdoc/plugins/api.cjs +0 -181
- package/config/jsdoc/plugins/default-export.cjs +0 -35
- package/config/jsdoc/plugins/define-plugin.cjs +0 -33
- package/config/jsdoc/plugins/events.cjs +0 -39
- package/config/jsdoc/plugins/inline-options.cjs +0 -102
- package/config/jsdoc/plugins/markdown.cjs +0 -119
- package/config/jsdoc/plugins/observable.cjs +0 -69
- package/config/jsdoc/plugins/virtual-plugin.cjs +0 -16
- package/config/rollup-full-build.js +0 -24
- package/config/tsconfig-build.json +0 -25
- package/examples/.eslintrc +0 -29
- package/examples/index.html +0 -100
- package/examples/index.js +0 -109
- package/examples/planetary-computer.html +0 -14
- package/examples/planetary-computer.js +0 -53
- package/examples/resources/Jugl.js +0 -15
- package/examples/resources/common.js +0 -111
- package/examples/stac-collection-webmaplinks.html +0 -9
- package/examples/stac-collection-webmaplinks.js +0 -55
- package/examples/stac-item-from-object.html +0 -9
- package/examples/stac-item-from-object.js +0 -102
- package/examples/stac-item-tileserver.html +0 -9
- package/examples/stac-item-tileserver.js +0 -34
- package/examples/stac-item.html +0 -9
- package/examples/stac-item.js +0 -45
- package/examples/stac-itemcollection.html +0 -11
- package/examples/stac-itemcollection.js +0 -39
- package/examples/templates/example.html +0 -206
- package/examples/templates/readme.md +0 -3
- package/examples/webpack/config.mjs +0 -69
- package/examples/webpack/example-builder.js +0 -410
- package/site/build.js +0 -26
- package/site/layouts/default.hbs +0 -78
- package/site/src/doc/faq.md +0 -8
- package/site/src/doc/index.md +0 -22
- package/site/src/doc/quickstart.md +0 -121
- package/site/src/doc/tutorials/index.md +0 -8
- package/site/src/download/index.hbs +0 -62
- package/site/src/favicon.ico +0 -0
- package/site/src/index.hbs +0 -71
- package/site/src/theme/index.css +0 -26
- package/site/src/theme/site.css +0 -531
- package/src/ol/layer/STAC.js +0 -780
- package/src/ol/layer/stacUtil.js +0 -169
- package/tasks/.eslintrc +0 -5
- package/tasks/build-website.sh +0 -90
- package/tasks/create-release.js +0 -107
- package/tasks/generate-index.js +0 -125
- package/tasks/generate-info.js +0 -189
- package/tasks/get-latest-release.js +0 -36
- package/tasks/newest-tag.js +0 -52
- package/tasks/prepare-package.js +0 -46
- package/tasks/publish.sh +0 -90
- package/test/README.md +0 -56
- package/test/browser/.eslintrc +0 -14
- package/test/browser/karma.config.cjs +0 -94
- package/test/browser/spec/ol/layer/STAC.test.js +0 -18
- package/test/browser/test-extensions.js +0 -419
- package/tsconfig.json +0 -62
package/layer/STAC.js
ADDED
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ol/layer/STAC
|
|
3
|
+
*/
|
|
4
|
+
import ErrorEvent from '../events/ErrorEvent.js';
|
|
5
|
+
import GeoJSON from 'ol/format/GeoJSON.js';
|
|
6
|
+
import GeoTIFF from '../source/GeoTIFF2.js';
|
|
7
|
+
import ImageLayer from 'ol/layer/Image.js';
|
|
8
|
+
import Layer from 'ol/layer/Layer.js';
|
|
9
|
+
import LayerGroup from 'ol/layer/Group.js';
|
|
10
|
+
import StaticImage from 'ol/source/ImageStatic.js';
|
|
11
|
+
import TileJSON from 'ol/source/TileJSON.js';
|
|
12
|
+
import TileLayer from 'ol/layer/Tile.js';
|
|
13
|
+
import VectorLayer from 'ol/layer/Vector.js';
|
|
14
|
+
import VectorSource from 'ol/source/Vector.js';
|
|
15
|
+
import WMS from 'ol/source/TileWMS.js';
|
|
16
|
+
import WMTS, { optionsFromCapabilities } from 'ol/source/WMTS.js';
|
|
17
|
+
import WebGLTileLayer from 'ol/layer/WebGLTile.js';
|
|
18
|
+
import XYZ from 'ol/source/XYZ.js';
|
|
19
|
+
import create, { Asset, STAC } from 'stac-js';
|
|
20
|
+
import { defaultBoundsStyle, defaultCollectionStyle, getBoundsStyle, getGeoTiffSourceInfoFromAsset, getProjection, getSpecificWebMapUrl, getWmtsCapabilities, } from './stacUtil.js';
|
|
21
|
+
import { toGeoJSON } from 'stac-js/src/geo.js';
|
|
22
|
+
import { transformExtent } from 'ol/proj.js';
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {import("ol/extent.js").Extent} Extent
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {import("../source/GeoTIFF2.js").Options2} GeoTIFFSourceOptions
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {import("ol/source/ImageStatic.js").Options} ImageStaticSourceOptions
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {import("stac-js").Link} Link
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {import("stac-js").STACObject} STACObject
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {import('ol/style.js').Style} Style
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* @typedef {import("ol/source/XYZ.js").Options} XYZSourceOptions
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {Object} Options
|
|
46
|
+
* @property {string} [url] The STAC URL. Any of `url` and `data` must be provided.
|
|
47
|
+
* Can also be used as url for data, if it is absolute and doesn't contain a self link.
|
|
48
|
+
* @property {STAC|Asset|Object} [data] The STAC metadata. Any of `url` and `data` must be provided.
|
|
49
|
+
* `data` take precedence over `url`.
|
|
50
|
+
* @property {Array<string|Asset>|null} [assets=null] The selector for the assets to be rendered,
|
|
51
|
+
* only for STAC Items and Collections.
|
|
52
|
+
* This can be an array of strings corresponding to asset keys or Asset objects.
|
|
53
|
+
* null shows the default asset, an empty array shows no asset.
|
|
54
|
+
* @property {Array<number>} [bands] The (one-based) bands to show.
|
|
55
|
+
* @property {function(GeoTIFFSourceOptions, Asset):(GeoTIFFSourceOptions|Promise<GeoTIFFSourceOptions>)} [getGeoTIFFSourceOptions]
|
|
56
|
+
* Optional function that can be used to configure the underlying GeoTIFF sources. The function can do any additional work
|
|
57
|
+
* and return the completed options or a promise for the same. The function will be called with the current source options
|
|
58
|
+
* and the STAC Asset.
|
|
59
|
+
* @property {function(ImageStaticSourceOptions, (Asset|Link)):(ImageStaticSourceOptions|Promise<ImageStaticSourceOptions>)} [getImageStaticSourceOptions]
|
|
60
|
+
* Optional function that can be used to configure the underlying ImageStatic sources. The function can do any additional work
|
|
61
|
+
* and return the completed options or a promise for the same. The function will be called with the current source options
|
|
62
|
+
* and the STAC Asset or Link.
|
|
63
|
+
* @property {function(XYZSourceOptions, (Asset|Link)):(XYZSourceOptions|Promise<XYZSourceOptions>)} [getXYZSourceOptions]
|
|
64
|
+
* Optional function that can be used to configure the underlying XYZ sources that displays imagery. The function can do any
|
|
65
|
+
* additional work and return the completed options or a promise for the same. The function will be called with the current
|
|
66
|
+
* source options and the STAC Asset or Link.
|
|
67
|
+
* @property {boolean} [displayGeoTiffByDefault=false] Allow to choose non-cloud-optimized GeoTiffs as default image to show,
|
|
68
|
+
* which might not work well for larger files or larger amounts of files.
|
|
69
|
+
* @property {boolean} [displayPreview=false] Allow to display images that a browser can display (e.g. PNG, JPEG),
|
|
70
|
+
* usually assets with role `thumbnail` or the link with relation type `preview`.
|
|
71
|
+
* The previews are usually not covering the full extents and as such may be placed incorrectly on the map.
|
|
72
|
+
* For performance reasons, it is recommended to enable this option if you pass in STAC API Items.
|
|
73
|
+
* @property {boolean} [displayOverview=true] Allow to display COGs and, if `displayGeoTiffByDefault` is enabled, GeoTiffs,
|
|
74
|
+
* usually the assets with role `overview` or `visual`.
|
|
75
|
+
* @property {string|boolean} [displayWebMapLink=false] Allow to display a layer based on the information provided through the
|
|
76
|
+
* web map links extension. It is only used if no other data is shown. You can set a specific type of
|
|
77
|
+
* web map link (`tilejson`, `wms`, `wmts`, `xyz`), let OpenLayers choose (`true`) or disable the functionality (`false`).
|
|
78
|
+
* @property {Style} [boundsStyle] The style for the overall bounds / footprint.
|
|
79
|
+
* @property {Style} [collectionStyle] The style for individual items in a list of STAC Items or Collections.
|
|
80
|
+
* @property {null|string} [crossOrigin] For thumbnails: The `crossOrigin` attribute for loaded images / tiles.
|
|
81
|
+
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
|
82
|
+
* @property {function(Asset):string|null} [buildTileUrlTemplate=null] A function that generates a URL template for a tile server (XYZ),
|
|
83
|
+
* which will be used instead of the client-side GeoTIFF rendering (except if `useTileLayerAsFallback` is `true`).
|
|
84
|
+
* @property {boolean} [useTileLayerAsFallback=false] Uses the given URL template only when the client-side GeoTIFF rendering fails.
|
|
85
|
+
* @property {number} [opacity=1] Opacity (0, 1).
|
|
86
|
+
* @property {boolean} [visible=true] Visibility.
|
|
87
|
+
* @property {Extent} [extent] The bounding extent for layer rendering. The layer will not be
|
|
88
|
+
* rendered outside of this extent.
|
|
89
|
+
* @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers
|
|
90
|
+
* will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed
|
|
91
|
+
* for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`
|
|
92
|
+
* method was used.
|
|
93
|
+
* @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be
|
|
94
|
+
* visible.
|
|
95
|
+
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
|
96
|
+
* be visible.
|
|
97
|
+
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
|
98
|
+
* visible.
|
|
99
|
+
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
|
100
|
+
* be visible.
|
|
101
|
+
*/
|
|
102
|
+
/**
|
|
103
|
+
* @classdesc
|
|
104
|
+
* Renders STAC entities such as STAC Items, Collectons or lists of them as returned by APIs.
|
|
105
|
+
* The layers created by this LayerGroup all have a 'stac' value that can be retrieved using `layer.get('stac')`.
|
|
106
|
+
*
|
|
107
|
+
* @extends LayerGroup
|
|
108
|
+
* @fires sourceready
|
|
109
|
+
* @fires assetsready
|
|
110
|
+
* @fires ErorEvent#event:error
|
|
111
|
+
* @api
|
|
112
|
+
*/
|
|
113
|
+
class STACLayer extends LayerGroup {
|
|
114
|
+
/**
|
|
115
|
+
* @param {Options} options Layer options.
|
|
116
|
+
*/
|
|
117
|
+
constructor(options) {
|
|
118
|
+
const superOptions = {};
|
|
119
|
+
[
|
|
120
|
+
'opacity',
|
|
121
|
+
'visible',
|
|
122
|
+
'zIndex',
|
|
123
|
+
'minResolution',
|
|
124
|
+
'maxResolution',
|
|
125
|
+
'minZoom',
|
|
126
|
+
'maxZoom',
|
|
127
|
+
].forEach((key) => (superOptions[key] = options[key]));
|
|
128
|
+
super(superOptions);
|
|
129
|
+
/**
|
|
130
|
+
* @type {function(GeoTIFFSourceOptions, Asset):(GeoTIFFSourceOptions|Promise<GeoTIFFSourceOptions>)}
|
|
131
|
+
* @private
|
|
132
|
+
*/
|
|
133
|
+
this.getGeoTIFFSourceOptions_ = options.getGeoTIFFSourceOptions;
|
|
134
|
+
/**
|
|
135
|
+
* @type {function(ImageStaticSourceOptions, (Asset|Link)):(ImageStaticSourceOptions|Promise<ImageStaticSourceOptions>)}
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
this.getImageStaticSourceOptions_ = options.getImageStaticSourceOptions;
|
|
139
|
+
/**
|
|
140
|
+
* @type {function(XYZSourceOptions, (Asset|Link)):(XYZSourceOptions|Promise<XYZSourceOptions>)}
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
this.getXYZSourceOptions_ = options.getXYZSourceOptions;
|
|
144
|
+
/**
|
|
145
|
+
* @type {STAC|Asset}
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
this.data_;
|
|
149
|
+
/**
|
|
150
|
+
* @type {Array<Asset> | null}
|
|
151
|
+
* @private
|
|
152
|
+
*/
|
|
153
|
+
this.assets_ = null;
|
|
154
|
+
/**
|
|
155
|
+
* @type {Array<number>}
|
|
156
|
+
* @private
|
|
157
|
+
*/
|
|
158
|
+
this.bands_ = [];
|
|
159
|
+
/**
|
|
160
|
+
* @type {string | null}
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
this.crossOrigin_ = options.crossOrigin || null;
|
|
164
|
+
/**
|
|
165
|
+
* @type {boolean}
|
|
166
|
+
* @private
|
|
167
|
+
*/
|
|
168
|
+
this.displayGeoTiffByDefault_ = Boolean(options.displayGeoTiffByDefault);
|
|
169
|
+
/**
|
|
170
|
+
* @type {boolean}
|
|
171
|
+
* @private
|
|
172
|
+
*/
|
|
173
|
+
this.displayPreview_ = Boolean(options.displayPreview);
|
|
174
|
+
/**
|
|
175
|
+
* @type {boolean}
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
this.displayOverview_ = options.displayOverview === false ? false : true;
|
|
179
|
+
/**
|
|
180
|
+
* @type {string|boolean}
|
|
181
|
+
*/
|
|
182
|
+
this.displayWebMapLink_ = options.displayWebMapLink || false;
|
|
183
|
+
/**
|
|
184
|
+
* @type {function(Asset):string|null}
|
|
185
|
+
* @private
|
|
186
|
+
*/
|
|
187
|
+
this.buildTileUrlTemplate_ = options.buildTileUrlTemplate || null;
|
|
188
|
+
/**
|
|
189
|
+
* @type {boolean}
|
|
190
|
+
* @private
|
|
191
|
+
*/
|
|
192
|
+
this.useTileLayerAsFallback_ = options.useTileLayerAsFallback || false;
|
|
193
|
+
/**
|
|
194
|
+
* @type {Style}
|
|
195
|
+
* @private
|
|
196
|
+
*/
|
|
197
|
+
this.boundsStyle_ = options.boundsStyle || defaultBoundsStyle;
|
|
198
|
+
/**
|
|
199
|
+
* @type {Style}
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
this.collectionStyle_ = options.collectionStyle || defaultCollectionStyle;
|
|
203
|
+
/**
|
|
204
|
+
* @type {VectorLayer|null}
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
this.boundsLayer_ = null;
|
|
208
|
+
if (options.data) {
|
|
209
|
+
try {
|
|
210
|
+
this.configure_(options.data, options.url, options.assets, options.bands);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.handleError_(error);
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (!options.url) {
|
|
218
|
+
throw new Error('Either url or data must be provided');
|
|
219
|
+
}
|
|
220
|
+
fetch(options.url)
|
|
221
|
+
.then((response) => response.json())
|
|
222
|
+
.then((data) => this.configure_(data, options.url, options.assets, options.bands))
|
|
223
|
+
.catch((error) => this.handleError_(error));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Returns the vector layer that visualizes the bounds / footprint.
|
|
227
|
+
* @return {VectorLayer|null} The vector layer for the bounds
|
|
228
|
+
*/
|
|
229
|
+
getBoundsLayer() {
|
|
230
|
+
return this.boundsLayer_;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* @private
|
|
234
|
+
* @param {Error} error The error.
|
|
235
|
+
*/
|
|
236
|
+
handleError_(error) {
|
|
237
|
+
/**
|
|
238
|
+
* Error event.
|
|
239
|
+
*
|
|
240
|
+
* @event ErorEvent#event:error
|
|
241
|
+
* @type {Object}
|
|
242
|
+
* @property {Error} error - Provides the original error.
|
|
243
|
+
*/
|
|
244
|
+
this.dispatchEvent(new ErrorEvent(error));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* @private
|
|
248
|
+
* @param {STAC|Asset|Object} data The STAC data.
|
|
249
|
+
* @param {string} url The url to the data.
|
|
250
|
+
* @param {Array<Asset|string> | null} assets The assets to show.
|
|
251
|
+
* @param {Array<number>} bands The (one-based) bands to show.
|
|
252
|
+
*/
|
|
253
|
+
configure_(data, url = null, assets = null, bands = []) {
|
|
254
|
+
if (data instanceof Asset || data instanceof STAC) {
|
|
255
|
+
this.data_ = data;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
this.data_ = create(data);
|
|
259
|
+
}
|
|
260
|
+
if (url && url.includes('://')) {
|
|
261
|
+
this.data_.setAbsoluteUrl(url);
|
|
262
|
+
}
|
|
263
|
+
this.bands_ = bands;
|
|
264
|
+
this.boundsLayer_ = this.addFootprint_();
|
|
265
|
+
const updateBoundsStyle = () => {
|
|
266
|
+
if (this.boundsLayer_) {
|
|
267
|
+
this.boundsLayer_.setStyle(getBoundsStyle(this.boundsStyle_, this));
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
this.getLayers().on('add', updateBoundsStyle);
|
|
271
|
+
this.getLayers().on('remove', updateBoundsStyle);
|
|
272
|
+
this.setAssets(assets)
|
|
273
|
+
.then(() => {
|
|
274
|
+
/**
|
|
275
|
+
* Invoked once all assets are loaded and shown on the map.
|
|
276
|
+
*
|
|
277
|
+
* @event assetsready
|
|
278
|
+
*/
|
|
279
|
+
return this.dispatchEvent('assetsready');
|
|
280
|
+
})
|
|
281
|
+
.catch((error) => this.handleError_(error));
|
|
282
|
+
/**
|
|
283
|
+
* Invoked once the source is ready.
|
|
284
|
+
* If you provide the data inline, the event is likely fired before you can
|
|
285
|
+
* attach a listener to it. So this only really helps if a url is provided.
|
|
286
|
+
*
|
|
287
|
+
* @event sourceready
|
|
288
|
+
*/
|
|
289
|
+
this.dispatchEvent('sourceready');
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* @private
|
|
293
|
+
* @return {Promise} Resolves when complete.
|
|
294
|
+
*/
|
|
295
|
+
async addApiCollection_() {
|
|
296
|
+
const promises = this.getData()
|
|
297
|
+
.getAll()
|
|
298
|
+
.map((obj) => {
|
|
299
|
+
const subgroup = new STACLayer({
|
|
300
|
+
data: obj,
|
|
301
|
+
crossOrigin: this.crossOrigin_,
|
|
302
|
+
boundsStyle: this.collectionStyle_,
|
|
303
|
+
displayGeoTiffByDefault: this.displayGeoTiffByDefault_,
|
|
304
|
+
displayOverview: this.displayOverview_,
|
|
305
|
+
displayPreview: this.displayPreview_,
|
|
306
|
+
});
|
|
307
|
+
this.addLayer_(subgroup);
|
|
308
|
+
return subgroup;
|
|
309
|
+
});
|
|
310
|
+
return await Promise.all(promises);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* @private
|
|
314
|
+
* @return {Promise} Resolves when complete.
|
|
315
|
+
*/
|
|
316
|
+
async addStacAssets_() {
|
|
317
|
+
let assets = this.getAssets();
|
|
318
|
+
if (assets === null) {
|
|
319
|
+
assets = [];
|
|
320
|
+
// No specific asset given by the user, visualize the default geotiff
|
|
321
|
+
const geotiff = this.getData().getDefaultGeoTIFF(true, !this.displayGeoTiffByDefault_);
|
|
322
|
+
if (geotiff) {
|
|
323
|
+
assets.push(geotiff);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
// This may return Links or Assets
|
|
327
|
+
const thumbnails = this.getData().getThumbnails();
|
|
328
|
+
if (thumbnails.length > 0) {
|
|
329
|
+
assets.push(thumbnails[0]);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const promises = assets.map((asset) => this.addImagery_(asset));
|
|
334
|
+
return await Promise.all(promises);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* @private
|
|
338
|
+
* @param {Asset|Link} [ref] A STAC Link or Asset
|
|
339
|
+
* @return {Promise<Layer|undefined>} Resolves with a Layer or undefined when complete.
|
|
340
|
+
*/
|
|
341
|
+
async addImagery_(ref) {
|
|
342
|
+
if (!ref) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (ref.isGeoTIFF()) {
|
|
346
|
+
return await this.addGeoTiff_(ref);
|
|
347
|
+
}
|
|
348
|
+
if (ref.canBrowserDisplayImage()) {
|
|
349
|
+
return await this.addThumbnail_(ref);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* @private
|
|
354
|
+
* @param {Asset|Link} [thumbnail] A STAC Link or Asset
|
|
355
|
+
* @return {Promise<ImageLayer|undefined>} Resolves with am ImageLayer or udnefined when complete.
|
|
356
|
+
*/
|
|
357
|
+
async addThumbnail_(thumbnail) {
|
|
358
|
+
if (!this.displayPreview_) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* @type {ImageStaticSourceOptions}
|
|
363
|
+
*/
|
|
364
|
+
let options = {
|
|
365
|
+
url: thumbnail.getAbsoluteUrl(),
|
|
366
|
+
projection: await getProjection(thumbnail, 'EPSG:4326'),
|
|
367
|
+
imageExtent: thumbnail.getContext().getBoundingBox(),
|
|
368
|
+
crossOrigin: this.crossOrigin_,
|
|
369
|
+
};
|
|
370
|
+
if (this.getImageStaticSourceOptions_) {
|
|
371
|
+
options = await this.getImageStaticSourceOptions_(options, thumbnail);
|
|
372
|
+
}
|
|
373
|
+
const layer = new ImageLayer({
|
|
374
|
+
source: new StaticImage(options),
|
|
375
|
+
});
|
|
376
|
+
this.addLayer_(layer, thumbnail);
|
|
377
|
+
return layer;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Adds a layer for the web map links available in the STAC links.
|
|
381
|
+
* @return {Promise<Array<TileLayer>|undefined>} Resolves with a Layer or undefined when complete.
|
|
382
|
+
*/
|
|
383
|
+
async addWebMapLinks_() {
|
|
384
|
+
const links = this.getWebMapLinks();
|
|
385
|
+
if (links.length > 0) {
|
|
386
|
+
return await this.addLayerForLink(links[0]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Adds a layer for a link that implements the web-map-links extension.
|
|
391
|
+
* Supports: TileJSON, WMS, WMTS, XYZ
|
|
392
|
+
* @see https://github.com/stac-extensions/web-map-links
|
|
393
|
+
* @param {Link} link A web map link
|
|
394
|
+
* @return {Promise<Array<TileLayer>|undefined>} Resolves with a list of layers or undefined when complete.
|
|
395
|
+
*/
|
|
396
|
+
async addLayerForLink(link) {
|
|
397
|
+
// Replace any occurances of {s} if possible, otherwise return
|
|
398
|
+
const url = getSpecificWebMapUrl(link);
|
|
399
|
+
if (!url) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const options = {
|
|
403
|
+
attributions: link.getMetadata('attribution') ||
|
|
404
|
+
this.data_.getMetadata('attribution'),
|
|
405
|
+
crossOrigin: this.crossOrigin_,
|
|
406
|
+
url,
|
|
407
|
+
};
|
|
408
|
+
const sources = [];
|
|
409
|
+
switch (link.rel) {
|
|
410
|
+
case 'tilejson':
|
|
411
|
+
sources.push(new TileJSON(options));
|
|
412
|
+
break;
|
|
413
|
+
case 'wms':
|
|
414
|
+
if (!Array.isArray(link['wms:layers'])) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const styles = link['wms:styles'] || '';
|
|
418
|
+
for (const layer of link['wms:layers']) {
|
|
419
|
+
const params = Object.assign({
|
|
420
|
+
LAYERS: layer,
|
|
421
|
+
STYLES: styles,
|
|
422
|
+
}, link['wms:dimensions']);
|
|
423
|
+
const wmsOptions = Object.assign({}, options, { params });
|
|
424
|
+
sources.push(new WMS(wmsOptions));
|
|
425
|
+
}
|
|
426
|
+
break;
|
|
427
|
+
case 'wmts':
|
|
428
|
+
const wmtsCapabilities = await getWmtsCapabilities(url);
|
|
429
|
+
if (!wmtsCapabilities) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const layers = Array.isArray(link['wmts:layer'])
|
|
433
|
+
? link['wmts:layer']
|
|
434
|
+
: [link['wmts:layer']];
|
|
435
|
+
for (const layer of layers) {
|
|
436
|
+
const wmtsOptions = Object.assign({}, options, { layer });
|
|
437
|
+
sources.push(new WMTS(optionsFromCapabilities(wmtsCapabilities, wmtsOptions)));
|
|
438
|
+
}
|
|
439
|
+
break;
|
|
440
|
+
case 'xyz':
|
|
441
|
+
sources.push(new XYZ(options));
|
|
442
|
+
break;
|
|
443
|
+
default:
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
return sources.map((source) => {
|
|
447
|
+
const layer = new TileLayer({
|
|
448
|
+
source,
|
|
449
|
+
});
|
|
450
|
+
this.addLayer_(layer, link);
|
|
451
|
+
return layer;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* @private
|
|
456
|
+
* @param {Asset} [asset] A STAC Asset
|
|
457
|
+
* @return {Promise<Layer|undefined>} Resolves with a Layer or undefined when complete.
|
|
458
|
+
*/
|
|
459
|
+
async addGeoTiff_(asset) {
|
|
460
|
+
if (!this.displayOverview_) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (this.buildTileUrlTemplate_ && !this.useTileLayerAsFallback_) {
|
|
464
|
+
return await this.addTileLayerForImagery_(asset);
|
|
465
|
+
}
|
|
466
|
+
const sourceInfo = getGeoTiffSourceInfoFromAsset(asset, this.bands_);
|
|
467
|
+
/**
|
|
468
|
+
* @type {GeoTIFFSourceOptions}
|
|
469
|
+
*/
|
|
470
|
+
let options = {
|
|
471
|
+
sources: [sourceInfo],
|
|
472
|
+
};
|
|
473
|
+
const projection = await getProjection(asset);
|
|
474
|
+
if (projection) {
|
|
475
|
+
options.projection = projection;
|
|
476
|
+
}
|
|
477
|
+
if (this.getGeoTIFFSourceOptions_) {
|
|
478
|
+
options = await this.getGeoTIFFSourceOptions_(options, asset);
|
|
479
|
+
}
|
|
480
|
+
const tileserverFallback = async (asset, layer) => {
|
|
481
|
+
if (layer) {
|
|
482
|
+
this.getLayers().remove(layer);
|
|
483
|
+
}
|
|
484
|
+
return await this.addTileLayerForImagery_(asset);
|
|
485
|
+
};
|
|
486
|
+
try {
|
|
487
|
+
const source = new GeoTIFF(options);
|
|
488
|
+
const layer = new WebGLTileLayer({ source });
|
|
489
|
+
if (this.useTileLayerAsFallback_) {
|
|
490
|
+
const errorFn = () => tileserverFallback(asset, layer);
|
|
491
|
+
source.on('error', errorFn);
|
|
492
|
+
source.on('tileloaderror', errorFn);
|
|
493
|
+
// see https://github.com/openlayers/openlayers/issues/14926
|
|
494
|
+
source.on('change', () => {
|
|
495
|
+
if (source.getState() === 'error') {
|
|
496
|
+
tileserverFallback(asset, layer);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
layer.on('error', errorFn);
|
|
500
|
+
// Call this to ensure we can load the GeoTIFF, otherwise try fallback
|
|
501
|
+
await source.getView();
|
|
502
|
+
}
|
|
503
|
+
this.addLayer_(layer, asset);
|
|
504
|
+
return layer;
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
if (this.useTileLayerAsFallback_) {
|
|
508
|
+
return await tileserverFallback(asset, null);
|
|
509
|
+
}
|
|
510
|
+
this.handleError_(error);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* @private
|
|
515
|
+
* @param {Asset|Link} [data] A STAC Asset or Link
|
|
516
|
+
* @return {Promise<TileLayer>} Resolves with a TileLayer when complete.
|
|
517
|
+
*/
|
|
518
|
+
async addTileLayerForImagery_(data) {
|
|
519
|
+
/**
|
|
520
|
+
* @type {XYZSourceOptions}
|
|
521
|
+
*/
|
|
522
|
+
let options = {
|
|
523
|
+
crossOrigin: this.crossOrigin_,
|
|
524
|
+
url: this.buildTileUrlTemplate_(data),
|
|
525
|
+
};
|
|
526
|
+
if (this.getXYZSourceOptions_) {
|
|
527
|
+
options = await this.getXYZSourceOptions_(options, data);
|
|
528
|
+
}
|
|
529
|
+
const layer = new TileLayer({
|
|
530
|
+
source: new XYZ(options),
|
|
531
|
+
});
|
|
532
|
+
this.addLayer_(layer, data);
|
|
533
|
+
return layer;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* @param {Layer|LayerGroup} [layer] A Layer to add to the LayerGroup
|
|
537
|
+
* @param {STACObject} [data] The STAC object, can be any class exposed by stac-js
|
|
538
|
+
* @param {number} [zIndex=0] The z-index for the layer
|
|
539
|
+
* @private
|
|
540
|
+
*/
|
|
541
|
+
addLayer_(layer, data, zIndex = 0) {
|
|
542
|
+
layer.set('stac', data);
|
|
543
|
+
layer.setZIndex(zIndex);
|
|
544
|
+
this.getLayers().push(layer);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* @private
|
|
548
|
+
* @return {VectorLayer|null} The vector layer showing the geometry/bbox.
|
|
549
|
+
*/
|
|
550
|
+
addFootprint_() {
|
|
551
|
+
let geojson = null;
|
|
552
|
+
const data = this.getData();
|
|
553
|
+
if (data.isItemCollection() || data.isCollectionCollection()) {
|
|
554
|
+
geojson = toGeoJSON(data.getBoundingBox());
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
geojson = data.toGeoJSON();
|
|
558
|
+
}
|
|
559
|
+
if (geojson) {
|
|
560
|
+
const format = new GeoJSON();
|
|
561
|
+
const source = new VectorSource({
|
|
562
|
+
format,
|
|
563
|
+
loader: (extent, resolution, projection) => {
|
|
564
|
+
const features = format.readFeatures(geojson, {
|
|
565
|
+
featureProjection: projection,
|
|
566
|
+
});
|
|
567
|
+
source.addFeatures(features);
|
|
568
|
+
},
|
|
569
|
+
});
|
|
570
|
+
const vectorLayer = new VectorLayer({
|
|
571
|
+
source,
|
|
572
|
+
style: getBoundsStyle(this.boundsStyle_, this),
|
|
573
|
+
});
|
|
574
|
+
vectorLayer.set('bounds', true);
|
|
575
|
+
this.addLayer_(vectorLayer, data, 1);
|
|
576
|
+
return vectorLayer;
|
|
577
|
+
}
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* @private
|
|
582
|
+
*/
|
|
583
|
+
async updateLayers_() {
|
|
584
|
+
// Remove old layers
|
|
585
|
+
const oldLayers = this.getLayers();
|
|
586
|
+
for (let i = oldLayers.getLength() - 1; i >= 0; i--) {
|
|
587
|
+
const layer = oldLayers.item(i);
|
|
588
|
+
const stac = layer.get('stac');
|
|
589
|
+
if (stac && (stac.isLink() || stac.isAsset())) {
|
|
590
|
+
oldLayers.removeAt(i);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// Add new layers
|
|
594
|
+
const data = this.getData();
|
|
595
|
+
if (data.isItemCollection() || data.isCollectionCollection()) {
|
|
596
|
+
await this.addApiCollection_();
|
|
597
|
+
}
|
|
598
|
+
else if (data.isItem() || data.isCollection()) {
|
|
599
|
+
await this.addStacAssets_();
|
|
600
|
+
}
|
|
601
|
+
if (this.displayWebMapLink_ && this.hasOnlyBounds()) {
|
|
602
|
+
await this.addWebMapLinks_();
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Indicates whether the LayerGroup shows only the bounds layer (i.e. no imagery/tile layers).
|
|
607
|
+
* @return {boolean} `true` if only the bounds layer is shown, `false` otherwise.
|
|
608
|
+
*/
|
|
609
|
+
hasOnlyBounds() {
|
|
610
|
+
const boundsLayer = this.getBoundsLayer();
|
|
611
|
+
const imgLayer = this.getLayersArray().find((layer) => layer !== boundsLayer);
|
|
612
|
+
return typeof imgLayer === 'undefined';
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Returns all potential web map links based on the given value for `displayWebMapLink`.
|
|
616
|
+
* @return {Array<Link>} An array of links.
|
|
617
|
+
*/
|
|
618
|
+
getWebMapLinks() {
|
|
619
|
+
let types = ['xyz', 'tilejson', 'wmts', 'wms']; // This also defines the priority
|
|
620
|
+
if (typeof this.displayWebMapLink_ === 'string') {
|
|
621
|
+
types = [this.displayWebMapLink_];
|
|
622
|
+
}
|
|
623
|
+
const links = this.data_.getLinksWithRels(types);
|
|
624
|
+
links.sort((a, b) => {
|
|
625
|
+
const prioA = types.indexOf(a.rel);
|
|
626
|
+
const prioB = types.indexOf(b.rel);
|
|
627
|
+
return prioA - prioB;
|
|
628
|
+
});
|
|
629
|
+
return links;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Update the assets to be rendered.
|
|
633
|
+
* @param {Array<string|Asset>|null} assets The assets to show.
|
|
634
|
+
* @return {Promise} Resolves when all assets are rendered.
|
|
635
|
+
*/
|
|
636
|
+
async setAssets(assets) {
|
|
637
|
+
if (Array.isArray(assets)) {
|
|
638
|
+
this.assets_ = assets.map((asset) => {
|
|
639
|
+
if (typeof asset === 'string') {
|
|
640
|
+
return this.getData().getAsset(asset);
|
|
641
|
+
}
|
|
642
|
+
if (!(asset instanceof Asset)) {
|
|
643
|
+
return new Asset(asset);
|
|
644
|
+
}
|
|
645
|
+
return asset;
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
this.assets_ = null;
|
|
650
|
+
}
|
|
651
|
+
await this.updateLayers_();
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Get the STAC object.
|
|
655
|
+
*
|
|
656
|
+
* @return {STAC|Asset} The STAC object.
|
|
657
|
+
*/
|
|
658
|
+
getData() {
|
|
659
|
+
return this.data_;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Get the STAC assets shown.
|
|
663
|
+
*
|
|
664
|
+
* @return {Array<Asset>} The STAC assets.
|
|
665
|
+
*/
|
|
666
|
+
getAssets() {
|
|
667
|
+
return this.assets_;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Get the extent of the layer.
|
|
671
|
+
*
|
|
672
|
+
* @return {Extent|undefined} The layer extent.
|
|
673
|
+
*/
|
|
674
|
+
getExtent() {
|
|
675
|
+
const data = this.getData();
|
|
676
|
+
if (!data) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const layer = this.getLayers().item(0);
|
|
680
|
+
if (!layer || !(layer instanceof Layer)) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const map = layer.getMapInternal();
|
|
684
|
+
if (!map) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
const view = map.getView();
|
|
688
|
+
if (!view) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
const bbox = data.getBoundingBox();
|
|
692
|
+
if (bbox) {
|
|
693
|
+
return transformExtent(bbox, 'EPSG:4326', view.getProjection());
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
export default STACLayer;
|
|
698
|
+
//# sourceMappingURL=STAC.js.map
|