Allow maps to be configured with a promise for view props
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
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: [
|
||||
@@ -57,9 +56,5 @@ const map = new Map({
|
||||
source,
|
||||
}),
|
||||
],
|
||||
view: new View({
|
||||
center: [1900000, 6100000],
|
||||
zoom: 13,
|
||||
minZoom: 10,
|
||||
}),
|
||||
view: source.getView(),
|
||||
});
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
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';
|
||||
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);
|
||||
|
||||
// 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 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,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const map = new Map({
|
||||
target: 'map',
|
||||
@@ -69,27 +74,8 @@ const map = new Map({
|
||||
[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,
|
||||
source: source,
|
||||
}),
|
||||
],
|
||||
view: new View({
|
||||
projection: 'EPSG:32636',
|
||||
center: getCenter(sourceExtent),
|
||||
extent: sourceExtent,
|
||||
zoom: 9,
|
||||
}),
|
||||
view: source.getView(),
|
||||
});
|
||||
|
||||
@@ -1,62 +1,49 @@
|
||||
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';
|
||||
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 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 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';
|
||||
|
||||
const 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,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
}),
|
||||
source: source,
|
||||
}),
|
||||
],
|
||||
view: new View({
|
||||
projection: 'EPSG:32645',
|
||||
center: getCenter(sourceExtent),
|
||||
extent: sourceExtent,
|
||||
zoom: 8,
|
||||
}),
|
||||
view: source.getView(),
|
||||
});
|
||||
|
||||
@@ -1,35 +1,21 @@
|
||||
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';
|
||||
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);
|
||||
|
||||
// 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 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',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
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,
|
||||
source: source,
|
||||
}),
|
||||
],
|
||||
view: new View({
|
||||
projection: 'EPSG:32636',
|
||||
center: getCenter(sourceExtent),
|
||||
extent: sourceExtent,
|
||||
zoom: 9,
|
||||
}),
|
||||
view: source.getView(),
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -33,13 +33,73 @@ import {createXYZ} from '../../../../src/ol/tilegrid.js';
|
||||
import {defaults as defaultInteractions} from '../../../../src/ol/interaction.js';
|
||||
import {tile as tileStrategy} from '../../../../src/ol/loadingstrategy.js';
|
||||
|
||||
describe('ol.Map', function () {
|
||||
describe('ol/Map', function () {
|
||||
describe('constructor', function () {
|
||||
it('creates a new map', function () {
|
||||
const map = new Map({});
|
||||
expect(map).to.be.a(Map);
|
||||
});
|
||||
|
||||
it('accepts a promise for view options', (done) => {
|
||||
let resolve;
|
||||
|
||||
const map = new Map({
|
||||
view: new Promise((r) => {
|
||||
resolve = r;
|
||||
}),
|
||||
});
|
||||
|
||||
expect(map.getView()).to.be.a(View);
|
||||
expect(map.getView().isDef()).to.be(false);
|
||||
|
||||
map.once('change:view', () => {
|
||||
const view = map.getView();
|
||||
expect(view).to.be.a(View);
|
||||
expect(view.isDef()).to.be(true);
|
||||
expect(view.getCenter()).to.eql([1, 2]);
|
||||
expect(view.getZoom()).to.be(3);
|
||||
done();
|
||||
});
|
||||
|
||||
resolve({
|
||||
center: [1, 2],
|
||||
zoom: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it('allows the view to be set with a promise later after construction', (done) => {
|
||||
const map = new Map({
|
||||
view: new View({zoom: 1, center: [0, 0]}),
|
||||
});
|
||||
|
||||
expect(map.getView()).to.be.a(View);
|
||||
expect(map.getView().isDef()).to.be(true);
|
||||
|
||||
let resolve;
|
||||
map.setView(
|
||||
new Promise((r) => {
|
||||
resolve = r;
|
||||
})
|
||||
);
|
||||
|
||||
expect(map.getView()).to.be.a(View);
|
||||
expect(map.getView().isDef()).to.be(false);
|
||||
|
||||
map.once('change:view', () => {
|
||||
const view = map.getView();
|
||||
expect(view).to.be.a(View);
|
||||
expect(view.isDef()).to.be(true);
|
||||
expect(view.getCenter()).to.eql([1, 2]);
|
||||
expect(view.getZoom()).to.be(3);
|
||||
done();
|
||||
});
|
||||
|
||||
resolve({
|
||||
center: [1, 2],
|
||||
zoom: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a set of default interactions', function () {
|
||||
const map = new Map({});
|
||||
const interactions = map.getInteractions();
|
||||
@@ -60,6 +60,18 @@ describe('ol/source/GeoTIFF', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves view properties', function (done) {
|
||||
source.getView().then((viewOptions) => {
|
||||
const projection = viewOptions.projection;
|
||||
expect(projection.getCode()).to.be('EPSG:4326');
|
||||
expect(projection.getUnits()).to.be('degrees');
|
||||
expect(viewOptions.extent).to.eql([-180, -90, 180, 90]);
|
||||
expect(viewOptions.center).to.eql([0, 0]);
|
||||
expect(viewOptions.resolutions).to.eql([0.703125]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('loads tiles', function (done) {
|
||||
source.on('change', () => {
|
||||
const tile = source.getTile(0, 0, 0);
|
||||
|
||||
Reference in New Issue
Block a user