Allow maps to be configured with a promise for view props

This commit is contained in:
Tim Schaub
2021-09-21 00:06:26 +00:00
parent 79a54e33bb
commit d5813deb08
9 changed files with 230 additions and 116 deletions

View File

@@ -137,7 +137,7 @@ import {removeNode} from './dom.js';
* element itself or the `id` of the element. If not specified at construction
* time, {@link module:ol/Map~Map#setTarget} must be called for the map to be
* rendered. If passed by element, the container can be in a secondary document.
* @property {View} [view] The map's view. No layer sources will be
* @property {View|Promise<import("./View.js").ViewOptions>} [view] The map's view. No layer sources will be
* fetched unless this is specified at construction time or through
* {@link module:ol/Map~Map#setView}.
*/
@@ -388,6 +388,13 @@ class PluggableMap extends BaseObject {
// is "defined" already.
this.setProperties(optionsInternal.values);
const map = this;
if (options.view && !(options.view instanceof View)) {
options.view.then(function (viewOptions) {
map.setView(new View(viewOptions));
});
}
this.controls.addEventListener(
CollectionEventType.ADD,
/**
@@ -1496,12 +1503,24 @@ class PluggableMap extends BaseObject {
/**
* Set the view for this map.
* @param {View} view The view that controls this map.
* @param {View|Promise<import("./View.js").ViewOptions>} view The view that controls this map.
* It is also possible to pass a promise that resolves to options for constructing a view. This
* alternative allows view properties to be resolved by sources or other components that load
* view-related metadata.
* @observable
* @api
*/
setView(view) {
this.set(MapProperty.VIEW, view);
if (!view || view instanceof View) {
this.set(MapProperty.VIEW, view);
return;
}
this.set(MapProperty.VIEW, new View());
const map = this;
view.then(function (viewOptions) {
map.setView(new View(viewOptions));
});
}
/**
@@ -1600,7 +1619,7 @@ function createOptionsInternal(options) {
values[MapProperty.TARGET] = options.target;
values[MapProperty.VIEW] =
options.view !== undefined ? options.view : new View();
options.view instanceof View ? options.view : new View();
let controls;
if (options.controls !== undefined) {

View File

@@ -5,10 +5,15 @@ 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 {Projection, get as getCachedProjection} from '../proj.js';
import {
Projection,
get as getCachedProjection,
toUserCoordinate,
toUserExtent,
} from '../proj.js';
import {clamp} from '../math.js';
import {create as createDecoderWorker} from '../worker/geotiff-decoder.js';
import {getIntersection} from '../extent.js';
import {getCenter, getIntersection} from '../extent.js';
import {toSize} from '../size.js';
import {fromCode as unitsFromCode} from '../proj/Units.js';
@@ -181,15 +186,18 @@ function getImagesForSource(source) {
* @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.
* @param {function(Error):void} rejector A function to be called with any error.
*/
function assertEqual(expected, got, tolerance, message) {
function assertEqual(expected, got, tolerance, message, rejector) {
if (Array.isArray(expected)) {
const length = expected.length;
if (!Array.isArray(got) || length != got.length) {
throw new Error(message);
const error = new Error(message);
rejector(error);
throw error;
}
for (let i = 0; i < length; ++i) {
assertEqual(expected[i], got[i], tolerance, message);
assertEqual(expected[i], got[i], tolerance, message, rejector);
}
return;
}
@@ -433,7 +441,7 @@ class GeoTIFFSource extends DataTile {
origin = sourceOrigin;
} else {
const message = `Origin mismatch for source ${sourceIndex}, got [${sourceOrigin}] but expected [${origin}]`;
assertEqual(origin, sourceOrigin, 0, message);
assertEqual(origin, sourceOrigin, 0, message, this.viewRejector);
}
if (!resolutions) {
@@ -455,7 +463,8 @@ class GeoTIFFSource extends DataTile {
resolutions.slice(minZoom, resolutions.length),
scaledSourceResolutions,
0.005,
message
message,
this.viewRejector
);
}
@@ -466,7 +475,8 @@ class GeoTIFFSource extends DataTile {
tileSizes.slice(minZoom, tileSizes.length),
sourceTileSizes,
0,
`Tile size mismatch for source ${sourceIndex}`
`Tile size mismatch for source ${sourceIndex}`,
this.viewRejector
);
}
@@ -545,6 +555,13 @@ class GeoTIFFSource extends DataTile {
this.setLoader(this.loadTile_.bind(this));
this.setState(State.READY);
this.viewResolver({
projection: this.projection,
resolutions: resolutions,
center: toUserCoordinate(getCenter(extent), this.projection),
extent: toUserExtent(extent, this.projection),
zoom: 0,
});
}
loadTile_(z, x, y) {
@@ -650,4 +667,27 @@ class GeoTIFFSource extends DataTile {
}
}
/**
* Get a promise for view properties based on the source. Use the result of this function
* as the `view` option in a map constructor.
*
* const source = new GeoTIFF(options);
*
* const map = new Map({
* target: 'map',
* layers: [
* new TileLayer({
* source: source,
* }),
* ],
* view: source.getView(),
* });
*
* @function
* @return {Promise<import("../View.js").ViewOptions>} A promise for view-related properties.
* @api
*
*/
GeoTIFFSource.prototype.getView;
export default GeoTIFFSource;

View File

@@ -90,6 +90,28 @@ class Source extends BaseObject {
* @type {boolean}
*/
this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false;
/**
* @protected
* @type {function(import("../View.js").ViewOptions):void}
*/
this.viewResolver = null;
/**
* @protected
* @type {function(Error):void}
*/
this.viewRejector = null;
const self = this;
/**
* @private
* @type {Promise<import("../View.js").ViewOptions>}
*/
this.viewPromise_ = new Promise(function (resolve, reject) {
self.viewResolver = resolve;
self.viewRejector = reject;
});
}
/**
@@ -126,6 +148,13 @@ class Source extends BaseObject {
return abstract();
}
/**
* @return {Promise<import("../View.js").ViewOptions>} A promise for view-related properties.
*/
getView() {
return this.viewPromise_;
}
/**
* Get the state of the source, see {@link module:ol/source/State~State} for possible states.
* @return {import("./State.js").default} State.