Merge pull request #9308 from ahocevar/vectortile-loader-projection

Simplify vector tile projection handling
This commit is contained in:
Andreas Hocevar
2019-03-11 09:28:48 +01:00
committed by GitHub
10 changed files with 148 additions and 181 deletions

View File

@@ -24,10 +24,10 @@ class VectorTile extends Tile {
this.consumers = 0;
/**
* @private
* Extent of this tile; set by the source.
* @type {import("./extent.js").Extent}
*/
this.extent_ = null;
this.extent = null;
/**
* @private
@@ -48,11 +48,16 @@ class VectorTile extends Tile {
this.loader_;
/**
* Data projection
* @private
* Feature projection of this tile; set by the source.
* @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
@@ -76,15 +81,6 @@ class VectorTile extends Tile {
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.
* @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
* by {@link module:ol/VectorTile~VectorTile#getProjection}.
* Get the features for this tile. Geometries will be in the view projection.
* @return {Array<import("./Feature.js").FeatureLike>} Features.
* @api
*/
@@ -111,16 +106,6 @@ class VectorTile extends Tile {
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
*/
@@ -128,7 +113,7 @@ class VectorTile extends Tile {
if (this.state == TileState.IDLE) {
this.setState(TileState.LOADING);
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.
* @param {Array<import("./Feature.js").default>} features The loaded features.
* @param {import("./proj/Projection.js").default} dataProjection Data projection.
* @param {import("./extent.js").Extent} extent Extent.
*/
onLoad(features, dataProjection, extent) {
this.setProjection(dataProjection);
this.setExtent(extent);
onLoad(features, dataProjection) {
this.setFeatures(features);
}
@@ -151,22 +133,6 @@ class VectorTile extends Tile {
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`.
* Sets the features for the tile.
@@ -178,17 +144,6 @@ class VectorTile extends Tile {
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.
* @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);
}
if (source) {
success.call(this, format.readFeatures(source,
{featureProjection: projection}),
format.readProjection(source), format.getLastExtent());
success.call(this, format.readFeatures(source, {
extent: extent,
featureProjection: projection
}),
format.readProjection(source));
} else {
failure.call(this);
}

View File

@@ -111,14 +111,6 @@ class FeatureFormat {
}, options);
}
/**
* Get the extent from the source of the last {@link readFeatures} call.
* @return {import("../extent.js").Extent} Tile extent.
*/
getLastExtent() {
return null;
}
/**
* @abstract
* @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 Units from '../proj/Units.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;
/**
* @private
* @type {import("../extent.js").Extent}
*/
this.extent_ = null;
}
/**
@@ -159,10 +154,10 @@ class MVT extends FeatureFormat {
* @private
* @param {PBF} pbf PBF
* @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.
*/
createFeature_(pbf, rawFeature, opt_options) {
createFeature_(pbf, rawFeature, options) {
const type = rawFeature.type;
if (type === 0) {
return null;
@@ -181,6 +176,7 @@ class MVT extends FeatureFormat {
if (this.featureClass_ === RenderFeature) {
feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
feature.transform(options.dataProjection, options.featureProjection);
} else {
let geom;
if (geometryType == GeometryType.POLYGON) {
@@ -213,7 +209,7 @@ class MVT extends FeatureFormat {
if (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.setId(id);
feature.setProperties(values, true);
@@ -222,14 +218,6 @@ class MVT extends FeatureFormat {
return feature;
}
/**
* @inheritDoc
* @api
*/
getLastExtent() {
return this.extent_;
}
/**
* @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
*/
readFeatures(source, opt_options) {
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 pbfLayers = pbf.readFields(layersPBFReader, {});
/** @type {Array<import("../Feature.js").FeatureLike>} */
const features = [];
for (const name in pbfLayers) {
if (layers && layers.indexOf(name) == -1) {
@@ -254,11 +249,13 @@ class MVT extends FeatureFormat {
}
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) {
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;

View File

@@ -10,8 +10,6 @@ import EventType from '../../events/EventType.js';
import rbush from 'rbush';
import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.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 {labelCache} from '../../render/canvas.js';
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
@@ -264,12 +262,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const sharedExtent = getIntersection(tileExtent, sourceTileExtent);
const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null :
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;
const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution,
pixelRatio, !!this.declutterTree_);
@@ -298,15 +290,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
for (let i = 0, ii = features.length; i < ii; ++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())) {
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
* boundaries or TopoJSON sources) allows the renderer to optimise fill and
* 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 {typeof import("../VectorTile.js").default} [tileClass] Class used to instantiate image tiles.
* 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 {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid.
* @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
* function(tile, url) {
* tile.setLoader(function() {
* var data = // ... fetch data
* var format = tile.getFormat();
* tile.setProjection(format.readProjection(data));
* tile.setExtent(format.getLastExtent()); // line only required for ol/format/MVT
* tile.setFeatures(format.readFeatures(data, {
* // featureProjection is not required for ol/format/MVT
* featureProjection: map.getView().getProjection()
* }));
* tile.setLoader(function(extent, resolution, projection) {
* fetch(url).then(function(response) {
* response.arrayBuffer().then(function(data) {
* const format = tile.getFormat() // ol/format/MVT configured as source format
* const features = format.readFeatures(data, {
* extent: extent,
* featureProjection: projection
* });
* tile.setFeatures(features);
* });
* });
* }
* });
* ```
@@ -187,9 +189,9 @@ class VectorTile extends UrlTile {
const sourceTileGrid = this.tileGrid;
const sourceExtent = sourceTileGrid.getExtent();
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();
let loadedZ = sourceZ + 1;
@@ -215,6 +217,9 @@ class VectorTile extends UrlTile {
tileUrl == undefined ? TileState.EMPTY : TileState.IDLE,
tileUrl == undefined ? '' : tileUrl,
this.format_, this.tileLoadFunction);
sourceTile.extent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
sourceTile.projection = projection;
sourceTile.resolution = sourceTileGrid.getResolution(sourceTileCoord[0]);
this.sourceTiles_[tileKey] = sourceTile;
empty = empty && sourceTile.getState() === TileState.EMPTY;
listen(sourceTile, EventType.CHANGE, this.handleTileChange, this);