Simplify vector tile projection handling

This commit is contained in:
ahocevar
2019-03-10 09:34:40 +01:00
parent 7002053678
commit b2722542fe
10 changed files with 148 additions and 181 deletions

View File

@@ -99,6 +99,58 @@ Due to the constraint above (layers can only be added to a single map), the over
Previously, a graticule was not a layer. Now it is. See the graticule example for details on how to add a graticule layer to your map. Previously, a graticule was not a layer. Now it is. See the graticule example for details on how to add a graticule layer to your map.
##### `ol/format/Feature` API change
The `getLastExtent()` method, which was required for custom `tileLoadFunction`s in `ol/source/Vector`, has been removed because it is no longer needed (see below).
##### `ol/VectorTile` API changes
* Removal of the `getProjection()` and `setProjection()` methods. These were used in custom `tileLoadFunction`s on `ol/source/VectorTile`, which work differently now (see below).
* Removal of the `getExtent()` and `setExtent()` methods. These were used in custom `tileLoadFunction`s on `ol/source/VectorTile`, which work differently now (see below).
##### Custom tileLoadFunction on a VectorTile source needs changes
Previously, applications needed to call `setProjection()` and `setExtent()` on the tile in a custom `tileLoadFunction` on `ol/source/VectorTile`. The format's `getLastExtent()` method was used to get the extent. All this is no longer needed. Instead, the `extent` (first argument to the loader function) and `projection` (third argument to the loader function) are simply passed as `extent` and `featureProjection` options to the format's `readFeatures()` method.
Example for an old `tileLoadFunction`:
```js
function(tile, url) {
tile.setLoader(function() {
fetch(url).then(function(response) {
response.arrayBuffer().then(function(data) {
var format = tile.getFormat();
tile.setProjection(format.readProjection(data));
tile.setFeatures(format.readFeatures(data, {
// featureProjection is not required for ol/format/MVT
featureProjection: map.getView().getProjection()
}));
tile.setExtent(format.getLastExtent());
})
})
}
});
```
This function needs to be changed to:
```js
function(tile, url) {
tile.setLoader(function(extent, resolution, projection) {
fetch(url).then(function(response) {
response.arrayBuffer().then(function(data) {
var format = tile.getFormat();
tile.setFeatures(format.readFeatures(data, {
// extent is only required for ol/format/MVT
extent: extent,
featureProjection: projection
}));
})
})
}
});
```
##### Drop of support for the experimental WebGL renderer ##### Drop of support for the experimental WebGL renderer
The WebGL map and layers renderers are gone, replaced by a `WebGLHelper` function that provides a lightweight, The WebGL map and layers renderers are gone, replaced by a `WebGLHelper` function that provides a lightweight,

View File

@@ -24,10 +24,10 @@ class VectorTile extends Tile {
this.consumers = 0; this.consumers = 0;
/** /**
* @private * Extent of this tile; set by the source.
* @type {import("./extent.js").Extent} * @type {import("./extent.js").Extent}
*/ */
this.extent_ = null; this.extent = null;
/** /**
* @private * @private
@@ -48,11 +48,16 @@ class VectorTile extends Tile {
this.loader_; this.loader_;
/** /**
* Data projection * Feature projection of this tile; set by the source.
* @private
* @type {import("./proj/Projection.js").default} * @type {import("./proj/Projection.js").default}
*/ */
this.projection_ = null; this.projection = null;
/**
* Resolution of this tile; set by the source.
* @type {number}
*/
this.resolution;
/** /**
* @private * @private
@@ -76,15 +81,6 @@ class VectorTile extends Tile {
super.disposeInternal(); super.disposeInternal();
} }
/**
* Gets the extent of the vector tile.
* @return {import("./extent.js").Extent} The extent.
* @api
*/
getExtent() {
return this.extent_;
}
/** /**
* Get the feature format assigned for reading this tile's features. * Get the feature format assigned for reading this tile's features.
* @return {import("./format/Feature.js").default} Feature format. * @return {import("./format/Feature.js").default} Feature format.
@@ -95,8 +91,7 @@ class VectorTile extends Tile {
} }
/** /**
* Get the features for this tile. Geometries will be in the projection returned * Get the features for this tile. Geometries will be in the view projection.
* by {@link module:ol/VectorTile~VectorTile#getProjection}.
* @return {Array<import("./Feature.js").FeatureLike>} Features. * @return {Array<import("./Feature.js").FeatureLike>} Features.
* @api * @api
*/ */
@@ -111,16 +106,6 @@ class VectorTile extends Tile {
return this.url_; return this.url_;
} }
/**
* Get the feature projection of features returned by
* {@link module:ol/VectorTile~VectorTile#getFeatures}.
* @return {import("./proj/Projection.js").default} Feature projection.
* @api
*/
getProjection() {
return this.projection_;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -128,7 +113,7 @@ class VectorTile extends Tile {
if (this.state == TileState.IDLE) { if (this.state == TileState.IDLE) {
this.setState(TileState.LOADING); this.setState(TileState.LOADING);
this.tileLoadFunction_(this, this.url_); this.tileLoadFunction_(this, this.url_);
this.loader_(null, NaN, null); this.loader_(this.extent, this.resolution, this.projection);
} }
} }
@@ -136,11 +121,8 @@ class VectorTile extends Tile {
* Handler for successful tile load. * Handler for successful tile load.
* @param {Array<import("./Feature.js").default>} features The loaded features. * @param {Array<import("./Feature.js").default>} features The loaded features.
* @param {import("./proj/Projection.js").default} dataProjection Data projection. * @param {import("./proj/Projection.js").default} dataProjection Data projection.
* @param {import("./extent.js").Extent} extent Extent.
*/ */
onLoad(features, dataProjection, extent) { onLoad(features, dataProjection) {
this.setProjection(dataProjection);
this.setExtent(extent);
this.setFeatures(features); this.setFeatures(features);
} }
@@ -151,22 +133,6 @@ class VectorTile extends Tile {
this.setState(TileState.ERROR); this.setState(TileState.ERROR);
} }
/**
* Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s
* `tileLoadFunction`. Sets the extent of the vector tile. This is only required
* for tiles in projections with `tile-pixels` as units. The extent should be
* set to `[0, 0, tilePixelSize, tilePixelSize]`, where `tilePixelSize` is
* calculated by multiplying the tile size with the tile pixel ratio. For
* sources using {@link module:ol/format/MVT~MVT} as feature format, the
* {@link module:ol/format/MVT~MVT#getLastExtent} method will return the correct
* extent.
* @param {import("./extent.js").Extent} extent The extent.
* @api
*/
setExtent(extent) {
this.extent_ = extent;
}
/** /**
* Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`.
* Sets the features for the tile. * Sets the features for the tile.
@@ -178,17 +144,6 @@ class VectorTile extends Tile {
this.setState(TileState.LOADED); this.setState(TileState.LOADED);
} }
/**
* Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`.
* Sets the projection of the features that were added with
* {@link module:ol/VectorTile~VectorTile#setFeatures}.
* @param {import("./proj/Projection.js").default} projection Feature projection.
* @api
*/
setProjection(projection) {
this.projection_ = projection;
}
/** /**
* Set the feature loader for reading this tile's features. * Set the feature loader for reading this tile's features.
* @param {import("./featureloader.js").FeatureLoader} loader Feature loader. * @param {import("./featureloader.js").FeatureLoader} loader Feature loader.

View File

@@ -83,9 +83,11 @@ export function loadFeaturesXhr(url, format, success, failure) {
source = /** @type {ArrayBuffer} */ (xhr.response); source = /** @type {ArrayBuffer} */ (xhr.response);
} }
if (source) { if (source) {
success.call(this, format.readFeatures(source, success.call(this, format.readFeatures(source, {
{featureProjection: projection}), extent: extent,
format.readProjection(source), format.getLastExtent()); featureProjection: projection
}),
format.readProjection(source));
} else { } else {
failure.call(this); failure.call(this);
} }

View File

@@ -111,14 +111,6 @@ class FeatureFormat {
}, options); }, options);
} }
/**
* Get the extent from the source of the last {@link readFeatures} call.
* @return {import("../extent.js").Extent} Tile extent.
*/
getLastExtent() {
return null;
}
/** /**
* @abstract * @abstract
* @return {import("./FormatType.js").default} Format. * @return {import("./FormatType.js").default} Format.

View File

@@ -19,6 +19,7 @@ import {linearRingIsClockwise} from '../geom/flat/orient.js';
import Projection from '../proj/Projection.js'; import Projection from '../proj/Projection.js';
import Units from '../proj/Units.js'; import Units from '../proj/Units.js';
import RenderFeature from '../render/Feature.js'; import RenderFeature from '../render/Feature.js';
import {get} from '../proj.js';
/** /**
@@ -83,12 +84,6 @@ class MVT extends FeatureFormat {
*/ */
this.layers_ = options.layers ? options.layers : null; this.layers_ = options.layers ? options.layers : null;
/**
* @private
* @type {import("../extent.js").Extent}
*/
this.extent_ = null;
} }
/** /**
@@ -159,10 +154,10 @@ class MVT extends FeatureFormat {
* @private * @private
* @param {PBF} pbf PBF * @param {PBF} pbf PBF
* @param {Object} rawFeature Raw Mapbox feature. * @param {Object} rawFeature Raw Mapbox feature.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options. * @param {import("./Feature.js").ReadOptions} options Read options.
* @return {import("../Feature.js").FeatureLike} Feature. * @return {import("../Feature.js").FeatureLike} Feature.
*/ */
createFeature_(pbf, rawFeature, opt_options) { createFeature_(pbf, rawFeature, options) {
const type = rawFeature.type; const type = rawFeature.type;
if (type === 0) { if (type === 0) {
return null; return null;
@@ -181,6 +176,7 @@ class MVT extends FeatureFormat {
if (this.featureClass_ === RenderFeature) { if (this.featureClass_ === RenderFeature) {
feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id); feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
feature.transform(options.dataProjection, options.featureProjection);
} else { } else {
let geom; let geom;
if (geometryType == GeometryType.POLYGON) { if (geometryType == GeometryType.POLYGON) {
@@ -213,7 +209,7 @@ class MVT extends FeatureFormat {
if (this.geometryName_) { if (this.geometryName_) {
feature.setGeometryName(this.geometryName_); feature.setGeometryName(this.geometryName_);
} }
const geometry = transformGeometryWithOptions(geom, false, this.adaptOptions(opt_options)); const geometry = transformGeometryWithOptions(geom, false, options);
feature.setGeometry(geometry); feature.setGeometry(geometry);
feature.setId(id); feature.setId(id);
feature.setProperties(values, true); feature.setProperties(values, true);
@@ -222,14 +218,6 @@ class MVT extends FeatureFormat {
return feature; return feature;
} }
/**
* @inheritDoc
* @api
*/
getLastExtent() {
return this.extent_;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -238,15 +226,22 @@ class MVT extends FeatureFormat {
} }
/** /**
* @inheritDoc * Read all features.
*
* @param {ArrayBuffer} source Source.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options.
* @return {Array<import("../Feature.js").FeatureLike>} Features.
* @api * @api
*/ */
readFeatures(source, opt_options) { readFeatures(source, opt_options) {
const layers = this.layers_; const layers = this.layers_;
const options = /** @type {import("./Feature.js").ReadOptions} */ (this.adaptOptions(opt_options));
const dataProjection = get(options.dataProjection);
dataProjection.setWorldExtent(options.extent);
options.dataProjection = dataProjection;
const pbf = new PBF(/** @type {ArrayBuffer} */ (source)); const pbf = new PBF(/** @type {ArrayBuffer} */ (source));
const pbfLayers = pbf.readFields(layersPBFReader, {}); const pbfLayers = pbf.readFields(layersPBFReader, {});
/** @type {Array<import("../Feature.js").FeatureLike>} */
const features = []; const features = [];
for (const name in pbfLayers) { for (const name in pbfLayers) {
if (layers && layers.indexOf(name) == -1) { if (layers && layers.indexOf(name) == -1) {
@@ -254,11 +249,13 @@ class MVT extends FeatureFormat {
} }
const pbfLayer = pbfLayers[name]; const pbfLayer = pbfLayers[name];
const extent = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
dataProjection.setExtent(extent);
for (let i = 0, ii = pbfLayer.length; i < ii; ++i) { for (let i = 0, ii = pbfLayer.length; i < ii; ++i) {
const rawFeature = readRawFeature(pbf, pbfLayer, i); const rawFeature = readRawFeature(pbf, pbfLayer, i);
features.push(this.createFeature_(pbf, rawFeature)); features.push(this.createFeature_(pbf, rawFeature, options));
} }
this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
} }
return features; return features;

View File

@@ -10,8 +10,6 @@ import EventType from '../../events/EventType.js';
import rbush from 'rbush'; import rbush from 'rbush';
import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js'; import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js';
import VectorTileRenderType from '../../layer/VectorTileRenderType.js'; import VectorTileRenderType from '../../layer/VectorTileRenderType.js';
import {equivalent as equivalentProjection} from '../../proj.js';
import Units from '../../proj/Units.js';
import ReplayType from '../../render/canvas/BuilderType.js'; import ReplayType from '../../render/canvas/BuilderType.js';
import {labelCache} from '../../render/canvas.js'; import {labelCache} from '../../render/canvas.js';
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js'; import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
@@ -264,12 +262,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const sharedExtent = getIntersection(tileExtent, sourceTileExtent); const sharedExtent = getIntersection(tileExtent, sourceTileExtent);
const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null : const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null :
buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent); buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent);
const tileProjection = sourceTile.getProjection();
let reproject = false;
if (!equivalentProjection(projection, tileProjection)) {
reproject = true;
sourceTile.setProjection(projection);
}
builderState.dirty = false; builderState.dirty = false;
const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution, const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution,
pixelRatio, !!this.declutterTree_); pixelRatio, !!this.declutterTree_);
@@ -298,15 +290,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
} }
for (let i = 0, ii = features.length; i < ii; ++i) { for (let i = 0, ii = features.length; i < ii; ++i) {
const feature = features[i]; const feature = features[i];
if (reproject) {
if (tileProjection.getUnits() == Units.TILE_PIXELS) {
// projected tile extent
tileProjection.setWorldExtent(sourceTileExtent);
// tile extent in tile pixel space
tileProjection.setExtent(sourceTile.getExtent());
}
feature.getGeometry().transform(tileProjection, projection);
}
if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) { if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) {
render.call(this, feature); render.call(this, feature);
} }

View File

@@ -26,7 +26,7 @@ import {equals} from '../array.js';
* to `false` (e.g. for sources with polygons that represent administrative * to `false` (e.g. for sources with polygons that represent administrative
* boundaries or TopoJSON sources) allows the renderer to optimise fill and * boundaries or TopoJSON sources) allows the renderer to optimise fill and
* stroke operations. * stroke operations.
* @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection. * @property {import("../proj.js").ProjectionLike} [projection='EPSG:3857'] Projection of the tile grid.
* @property {import("./State.js").default} [state] Source state. * @property {import("./State.js").default} [state] Source state.
* @property {typeof import("../VectorTile.js").default} [tileClass] Class used to instantiate image tiles. * @property {typeof import("../VectorTile.js").default} [tileClass] Class used to instantiate image tiles.
* Default is {@link module:ol/VectorTile}. * Default is {@link module:ol/VectorTile}.
@@ -35,18 +35,20 @@ import {equals} from '../array.js';
* @property {number|import("../size.js").Size} [tileSize=512] Optional tile size. * @property {number|import("../size.js").Size} [tileSize=512] Optional tile size.
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid. * @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid.
* @property {import("../Tile.js").LoadFunction} [tileLoadFunction] * @property {import("../Tile.js").LoadFunction} [tileLoadFunction]
* Optional function to load a tile given a URL. Could look like this: * Optional function to load a tile given a URL. Could look like this for pbf tiles:
* ```js * ```js
* function(tile, url) { * function(tile, url) {
* tile.setLoader(function() { * tile.setLoader(function(extent, resolution, projection) {
* var data = // ... fetch data * fetch(url).then(function(response) {
* var format = tile.getFormat(); * response.arrayBuffer().then(function(data) {
* tile.setProjection(format.readProjection(data)); * const format = tile.getFormat() // ol/format/MVT configured as source format
* tile.setExtent(format.getLastExtent()); // line only required for ol/format/MVT * const features = format.readFeatures(data, {
* tile.setFeatures(format.readFeatures(data, { * extent: extent,
* // featureProjection is not required for ol/format/MVT * featureProjection: projection
* featureProjection: map.getView().getProjection() * });
* })); * tile.setFeatures(features);
* });
* });
* } * }
* }); * });
* ``` * ```
@@ -187,9 +189,9 @@ class VectorTile extends UrlTile {
const sourceTileGrid = this.tileGrid; const sourceTileGrid = this.tileGrid;
const sourceExtent = sourceTileGrid.getExtent(); const sourceExtent = sourceTileGrid.getExtent();
if (sourceExtent) { if (sourceExtent) {
getIntersection(extent, sourceTileGrid.getExtent(), extent); getIntersection(extent, sourceExtent, extent);
} }
const sourceZ = sourceTileGrid.getZForResolution(resolution); const sourceZ = sourceTileGrid.getZForResolution(resolution/*, 1*/);
const minZoom = sourceTileGrid.getMinZoom(); const minZoom = sourceTileGrid.getMinZoom();
let loadedZ = sourceZ + 1; let loadedZ = sourceZ + 1;
@@ -215,6 +217,9 @@ class VectorTile extends UrlTile {
tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, tileUrl == undefined ? TileState.EMPTY : TileState.IDLE,
tileUrl == undefined ? '' : tileUrl, tileUrl == undefined ? '' : tileUrl,
this.format_, this.tileLoadFunction); this.format_, this.tileLoadFunction);
sourceTile.extent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
sourceTile.projection = projection;
sourceTile.resolution = sourceTileGrid.getResolution(sourceTileCoord[0]);
this.sourceTiles_[tileKey] = sourceTile; this.sourceTiles_[tileKey] = sourceTile;
empty = empty && sourceTile.getState() === TileState.EMPTY; empty = empty && sourceTile.getState() === TileState.EMPTY;
listen(sourceTile, EventType.CHANGE, this.handleTileChange, this); listen(sourceTile, EventType.CHANGE, this.handleTileChange, this);

View File

@@ -1,5 +1,4 @@
import Feature from '../../../../src/ol/Feature.js'; import Feature from '../../../../src/ol/Feature.js';
import {getWidth} from '../../../../src/ol/extent.js';
import MVT from '../../../../src/ol/format/MVT.js'; import MVT from '../../../../src/ol/format/MVT.js';
import Point from '../../../../src/ol/geom/Point.js'; import Point from '../../../../src/ol/geom/Point.js';
import Polygon from '../../../../src/ol/geom/Polygon.js'; import Polygon from '../../../../src/ol/geom/Polygon.js';
@@ -22,15 +21,20 @@ where('ArrayBuffer.isView').describe('ol.format.MVT', function() {
describe('#readFeatures', function() { describe('#readFeatures', function() {
const options = {
featureProjection: 'EPSG:3857',
extent: [1824704.739223726, 6141868.096770482, 1827150.7241288517, 6144314.081675608]
};
it('uses ol.render.Feature as feature class by default', function() { it('uses ol.render.Feature as feature class by default', function() {
const format = new MVT({layers: ['water']}); const format = new MVT({layers: ['water']});
const features = format.readFeatures(data); const features = format.readFeatures(data, options);
expect(features[0]).to.be.a(RenderFeature); expect(features[0]).to.be.a(RenderFeature);
}); });
it('parses only specified layers', function() { it('parses only specified layers', function() {
const format = new MVT({layers: ['water']}); const format = new MVT({layers: ['water']});
const features = format.readFeatures(data); const features = format.readFeatures(data, options);
expect(features.length).to.be(10); expect(features.length).to.be(10);
}); });
@@ -64,29 +68,27 @@ where('ArrayBuffer.isView').describe('ol.format.MVT', function() {
featureClass: Feature, featureClass: Feature,
layers: ['building'] layers: ['building']
}); });
let features = format.readFeatures(data); let features = format.readFeatures(data, options);
expect(features[0].getId()).to.be(2); expect(features[0].getId()).to.be(2);
// ol.render.Feature // ol.render.Feature
format = new MVT({ format = new MVT({
layers: ['building'] layers: ['building']
}); });
features = format.readFeatures(data); features = format.readFeatures(data, options);
expect(features[0].getId()).to.be(2); expect(features[0].getId()).to.be(2);
}); });
it('sets the extent of the last readFeatures call', function() {
const format = new MVT();
format.readFeatures(data);
const extent = format.getLastExtent();
expect(getWidth(extent)).to.be(4096);
});
}); });
}); });
describe('ol.format.MVT', function() { describe('ol.format.MVT', function() {
const options = {
featureProjection: 'EPSG:3857',
extent: [1824704.739223726, 6141868.096770482, 1827150.7241288517, 6144314.081675608]
};
describe('#createFeature_', function() { describe('#createFeature_', function() {
it('accepts a geometryName', function() { it('accepts a geometryName', function() {
const format = new MVT({ const format = new MVT({
@@ -176,7 +178,9 @@ describe('ol.format.MVT', function() {
ends.push(10, 20); ends.push(10, 20);
createdEnds = ends; createdEnds = ends;
}; };
const feature = format.createFeature_({}, rawFeature); format.dataProjection.setExtent([0, 0, 4096, 4096]);
format.dataProjection.setWorldExtent(options.extent);
const feature = format.createFeature_({}, rawFeature, format.adaptOptions(options));
expect(feature).to.be.a(RenderFeature); expect(feature).to.be.a(RenderFeature);
expect(feature.getType()).to.be('Polygon'); expect(feature.getType()).to.be('Polygon');
expect(feature.getFlatCoordinates()).to.equal(createdFlatCoordinates); expect(feature.getFlatCoordinates()).to.equal(createdFlatCoordinates);

View File

@@ -9,8 +9,7 @@ import {getCenter} from '../../../../../src/ol/extent.js';
import MVT from '../../../../../src/ol/format/MVT.js'; import MVT from '../../../../../src/ol/format/MVT.js';
import Point from '../../../../../src/ol/geom/Point.js'; import Point from '../../../../../src/ol/geom/Point.js';
import VectorTileLayer from '../../../../../src/ol/layer/VectorTile.js'; import VectorTileLayer from '../../../../../src/ol/layer/VectorTile.js';
import {get as getProjection, fromLonLat} from '../../../../../src/ol/proj.js'; import {get as getProjection} from '../../../../../src/ol/proj.js';
import Projection from '../../../../../src/ol/proj/Projection.js';
import {checkedFonts} from '../../../../../src/ol/render/canvas.js'; import {checkedFonts} from '../../../../../src/ol/render/canvas.js';
import RenderFeature from '../../../../../src/ol/render/Feature.js'; import RenderFeature from '../../../../../src/ol/render/Feature.js';
import CanvasVectorTileLayerRenderer from '../../../../../src/ol/renderer/canvas/VectorTileLayer.js'; import CanvasVectorTileLayerRenderer from '../../../../../src/ol/renderer/canvas/VectorTileLayer.js';
@@ -64,7 +63,6 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.setFeatures([feature1, feature2, feature3]); this.setFeatures([feature1, feature2, feature3]);
this.setProjection(getProjection('EPSG:4326'));
this.setState(TileState.LOADED); this.setState(TileState.LOADED);
tileCallback(this); tileCallback(this);
} }
@@ -188,30 +186,6 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
}, 1600); }, 1600);
}); });
it('transforms geometries when tile and view projection are different', function() {
let tile;
tileCallback = function(t) {
tile = t;
};
map.renderSync();
expect(tile.getProjection()).to.equal(getProjection('EPSG:3857'));
expect(feature1.getGeometry().getCoordinates()).to.eql(fromLonLat([1, -1]));
});
it('Geometries are transformed from tile-pixels', function() {
const proj = new Projection({code: 'EPSG:3857', units: 'tile-pixels'});
let tile;
tileCallback = function(t) {
t.setProjection(proj);
t.setExtent([0, 0, 4096, 4096]);
tile = t;
};
map.renderSync();
expect(tile.getProjection()).to.equal(getProjection('EPSG:3857'));
expect(feature1.getGeometry().getCoordinates()).to.eql([-20027724.40316874, 20047292.282409746]);
expect(feature3.flatCoordinates_).to.eql([-20027724.40316874, 20047292.282409746]);
});
it('works for multiple layers that use the same source', function() { it('works for multiple layers that use the same source', function() {
const layer2 = new VectorTileLayer({ const layer2 = new VectorTileLayer({
source: source, source: source,
@@ -240,7 +214,6 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
}) })
}); });
const sourceTile = new VectorTile([0, 0, 0], 2); const sourceTile = new VectorTile([0, 0, 0], 2);
sourceTile.setProjection(getProjection('EPSG:3857'));
sourceTile.features_ = []; sourceTile.features_ = [];
sourceTile.getImage = function() { sourceTile.getImage = function() {
return document.createElement('canvas'); return document.createElement('canvas');
@@ -299,7 +272,6 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
beforeEach(function() { beforeEach(function() {
const sourceTile = new VectorTile([0, 0, 0]); const sourceTile = new VectorTile([0, 0, 0]);
sourceTile.setState(TileState.LOADED); sourceTile.setState(TileState.LOADED);
sourceTile.setProjection(getProjection('EPSG:3857'));
source = new VectorTileSource({ source = new VectorTileSource({
tileClass: TileClass, tileClass: TileClass,
tileGrid: createXYZ() tileGrid: createXYZ()

View File

@@ -1,38 +1,43 @@
import Feature from '../../../src/ol/Feature.js';
import {defaultLoadFunction} from '../../../src/ol/source/VectorTile.js'; import {defaultLoadFunction} from '../../../src/ol/source/VectorTile.js';
import VectorTile from '../../../src/ol/VectorTile.js'; import VectorTile from '../../../src/ol/VectorTile.js';
import {listen} from '../../../src/ol/events.js'; import {listen} from '../../../src/ol/events.js';
import TextFeature from '../../../src/ol/format/TextFeature.js'; import GeoJSON from '../../../src/ol/format/GeoJSON.js';
import MVT from '../../../src/ol/format/MVT.js';
import {get as getProjection} from '../../../src/ol/proj.js'; import {get as getProjection} from '../../../src/ol/proj.js';
import Projection from '../../../src/ol/proj/Projection.js'; import {createXYZ} from '../../../src/ol/tilegrid.js';
describe('ol.VectorTile', function() { describe('ol.VectorTile', function() {
it('loader sets features on the tile and updates proj units', function(done) { it('loader reprojects GeoJSON features', function(done) {
// mock format that return a tile-pixels feature const format = new GeoJSON();
const format = new TextFeature();
format.readProjection = function(source) {
return new Projection({
code: '',
units: 'tile-pixels'
});
};
format.readFeatures = function(source, options) {
return [new Feature()];
};
const tile = new VectorTile([0, 0, 0], null, null, format); const tile = new VectorTile([0, 0, 0], null, null, format);
const url = 'spec/ol/data/point.json'; const url = 'spec/ol/data/point.json';
defaultLoadFunction(tile, url); defaultLoadFunction(tile, url);
const loader = tile.loader_; const loader = tile.loader_;
listen(tile, 'change', function(e) { listen(tile, 'change', function(e) {
expect(tile.getFeatures().length).to.be.greaterThan(0); expect(tile.getFeatures()[0].getGeometry().getFlatCoordinates()).to.eql([-9724792.346778862, 4164041.638405114]);
expect(tile.getProjection().getUnits()).to.be('tile-pixels');
done(); done();
}); });
loader.call(tile, [], 1, getProjection('EPSG:3857')); loader.call(tile, [], 1, getProjection('EPSG:3857'));
}); });
it('loader reprojects MVT features', function(done) {
const format = new MVT();
const tileGrid = createXYZ({
tileSize: 512
});
const tile = new VectorTile([14, 8938, 5680], null, null, format);
const url = 'spec/ol/data/14-8938-5680.vector.pbf';
defaultLoadFunction(tile, url);
const loader = tile.loader_;
listen(tile, 'change', function(e) {
expect(tile.getFeatures()[1246].getGeometry().getFlatCoordinates()).to.eql([1827804.0218549764, 6144812.116688028]);
done();
});
const extent = tileGrid.getTileCoordExtent(tile.tileCoord);
loader.call(tile, extent, 1, getProjection('EPSG:3857'));
});
}); });