Simplify vector tile projection handling
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user