Tiled vector rendering
This needs more work still - see inline TODOs. It also has a major bug - rendered vector features do not scale and do not change their offset during panning. So only the initial view is correct.
This commit is contained in:
@@ -4,6 +4,7 @@ goog.require('goog.vec.Mat4');
|
|||||||
goog.require('ol.filter.Geometry');
|
goog.require('ol.filter.Geometry');
|
||||||
goog.require('ol.geom.GeometryType');
|
goog.require('ol.geom.GeometryType');
|
||||||
goog.require('ol.layer.Vector');
|
goog.require('ol.layer.Vector');
|
||||||
|
goog.require('ol.tilegrid.TileGrid');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -35,6 +36,18 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) {
|
|||||||
*/
|
*/
|
||||||
this.context_ = null;
|
this.context_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLCanvasElement}
|
||||||
|
*/
|
||||||
|
this.sketchCanvas_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Object.<string, HTMLCanvasElement>}
|
||||||
|
*/
|
||||||
|
this.tileCache_ = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {!goog.vec.Mat4.Number}
|
* @type {!goog.vec.Mat4.Number}
|
||||||
@@ -91,8 +104,25 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
|
|||||||
var layer = this.getVectorLayer();
|
var layer = this.getVectorLayer();
|
||||||
var source = layer.getVectorSource();
|
var source = layer.getVectorSource();
|
||||||
var extent = frameState.extent;
|
var extent = frameState.extent;
|
||||||
|
var view2DState = frameState.view2DState;
|
||||||
|
var tileGrid = source.getTileGrid();
|
||||||
|
|
||||||
var canvasSize = frameState.size;
|
if (goog.isNull(tileGrid)) {
|
||||||
|
// lazy tile source creation to match the view projection
|
||||||
|
tileGrid = ol.tilegrid.createForProjection(
|
||||||
|
view2DState.projection, 22);
|
||||||
|
source.setTileGrid(tileGrid);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tileSize = tileGrid.getTileSize();
|
||||||
|
var z = tileGrid.getZForResolution(view2DState.resolution);
|
||||||
|
var tileResolution = tileGrid.getResolution(z);
|
||||||
|
var tileRange = tileGrid.getTileRangeForExtentAndResolution(
|
||||||
|
frameState.extent, tileResolution);
|
||||||
|
|
||||||
|
var canvasSize = new ol.Size(
|
||||||
|
tileSize.width * tileRange.getWidth(),
|
||||||
|
tileSize.height * tileRange.getHeight());
|
||||||
|
|
||||||
var canvas, context;
|
var canvas, context;
|
||||||
if (goog.isNull(this.canvas_)) {
|
if (goog.isNull(this.canvas_)) {
|
||||||
@@ -104,6 +134,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
|
|||||||
this.canvas_ = canvas;
|
this.canvas_ = canvas;
|
||||||
this.canvasSize_ = canvasSize;
|
this.canvasSize_ = canvasSize;
|
||||||
this.context_ = context;
|
this.context_ = context;
|
||||||
|
this.sketchCanvas_ = /** @type {HTMLCanvasElement} */
|
||||||
|
canvas.cloneNode(false);
|
||||||
} else {
|
} else {
|
||||||
canvas = this.canvas_;
|
canvas = this.canvas_;
|
||||||
context = this.context_;
|
context = this.context_;
|
||||||
@@ -113,14 +145,6 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
|
|||||||
this.canvasSize_ = canvasSize;
|
this.canvasSize_ = canvasSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For now, we create a canvas renderer and have it render all features with
|
|
||||||
* every call to renderFrame.
|
|
||||||
* TODO: only render newly visible/dirty areas
|
|
||||||
*/
|
|
||||||
var canvasRenderer = new ol.renderer.canvas.Renderer(
|
|
||||||
canvas, frameState.coordinateToPixelMatrix);
|
|
||||||
|
|
||||||
// TODO: implement layer.setStyle(style) where style is a set of rules
|
// TODO: implement layer.setStyle(style) where style is a set of rules
|
||||||
// and a rule has a filter and array of symbolizers
|
// and a rule has a filter and array of symbolizers
|
||||||
var symbolizers = {};
|
var symbolizers = {};
|
||||||
@@ -144,19 +168,68 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
|
|||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var sketchCanvas = this.sketchCanvas_;
|
||||||
|
// clear the sketch canvas
|
||||||
|
sketchCanvas.width = canvasSize.width;
|
||||||
|
sketchCanvas.height = canvasSize.height;
|
||||||
|
// TODO: Use transform for tile resolution, not frameState resolution
|
||||||
|
var sketchCanvasRenderer = new ol.renderer.canvas.Renderer(
|
||||||
|
sketchCanvas, frameState.coordinateToPixelMatrix);
|
||||||
|
var renderedFeatures = {};
|
||||||
|
var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y;
|
||||||
// render features by geometry type
|
// render features by geometry type
|
||||||
var filters = this.geometryFilters_,
|
var filters = this.geometryFilters_,
|
||||||
numFilters = filters.length,
|
numFilters = filters.length,
|
||||||
i, filter, type, features, symbolizer;
|
i, filter, type, features, symbolizer;
|
||||||
|
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||||
for (i = 0; i < numFilters; ++i) {
|
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||||
filter = filters[i];
|
tileCoord = new ol.TileCoord(z, x, y);
|
||||||
type = filter.getType();
|
key = tileCoord.toString();
|
||||||
features = source.getFeatures(filter);
|
if (key in this.tileCache_) {
|
||||||
if (features.length) {
|
tile = this.tileCache_[key];
|
||||||
// TODO: layer.getSymbolizerLiterals(features) or similar
|
} else {
|
||||||
symbolizer = symbolizers[type];
|
tileExtent = tileGrid.getTileCoordExtent(tileCoord);
|
||||||
canvasRenderer.renderFeaturesByGeometryType(type, features, symbolizer);
|
// TODO: instead of filtering here, do this on the source and maintain
|
||||||
|
// a spatial index
|
||||||
|
function filterFn(feature) {
|
||||||
|
var id = goog.getUid(feature);
|
||||||
|
var include = !(id in renderedFeatures) &&
|
||||||
|
feature.getGeometry().getBounds().intersects(tileExtent);
|
||||||
|
if (include === true) {
|
||||||
|
renderedFeatures[id] = true;
|
||||||
|
}
|
||||||
|
return include;
|
||||||
|
}
|
||||||
|
for (i = 0; i < numFilters; ++i) {
|
||||||
|
filter = filters[i];
|
||||||
|
type = filter.getType();
|
||||||
|
features = source.getFeatures(filter);
|
||||||
|
// TODO: spatial indes of tiles - see filterFn above
|
||||||
|
features = goog.array.filter(features, filterFn);
|
||||||
|
if (features.length) {
|
||||||
|
// TODO: layer.getSymbolizerLiterals(features) or similar
|
||||||
|
symbolizer = symbolizers[type];
|
||||||
|
sketchCanvasRenderer.renderFeaturesByGeometryType(
|
||||||
|
type, features, symbolizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tile = /** @type {HTMLCanvasElement} */
|
||||||
|
goog.dom.createElement(goog.dom.TagName.CANVAS);
|
||||||
|
tile.width = tileSize.width;
|
||||||
|
tile.height = tileSize.height;
|
||||||
|
tileContext = tile.getContext('2d');
|
||||||
|
tileContext.drawImage(sketchCanvas,
|
||||||
|
x * tileSize.width, (tileRange.maxY - y) * tileSize.height,
|
||||||
|
tileSize.width, tileSize.height,
|
||||||
|
0, 0, tileSize.width, tileSize.height);
|
||||||
|
this.tileCache_[key] = tile;
|
||||||
|
}
|
||||||
|
// TODO: transform (scale, offset)
|
||||||
|
context.drawImage(tile,
|
||||||
|
0, 0,
|
||||||
|
tileSize.width, tileSize.height,
|
||||||
|
x * tileSize.width, (tileRange.maxY - y) * tileSize.height,
|
||||||
|
tileSize.width, tileSize.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ ol.source.FeatureCache.prototype.getFeaturesByIds_ = function(ids) {
|
|||||||
/**
|
/**
|
||||||
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
||||||
* extent: (ol.Extent|undefined),
|
* extent: (ol.Extent|undefined),
|
||||||
* projection: (ol.Projection|undefined)}}
|
* projection: (ol.Projection|undefined),
|
||||||
|
* tileGrid: (ol.tilegrid.TileGrid|undefined)}}
|
||||||
*/
|
*/
|
||||||
ol.source.VectorOptions;
|
ol.source.VectorOptions;
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ ol.source.VectorOptions;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @extends {ol.source.Source}
|
* @extends {ol.source.TileSource}
|
||||||
* @param {ol.source.VectorOptions} options Source options.
|
* @param {ol.source.VectorOptions} options Source options.
|
||||||
*/
|
*/
|
||||||
ol.source.Vector = function(options) {
|
ol.source.Vector = function(options) {
|
||||||
@@ -142,7 +143,8 @@ ol.source.Vector = function(options) {
|
|||||||
goog.base(this, {
|
goog.base(this, {
|
||||||
attributions: options.attributions,
|
attributions: options.attributions,
|
||||||
extent: options.extent,
|
extent: options.extent,
|
||||||
projection: options.projection
|
projection: options.projection,
|
||||||
|
tileGrid: options.tileGrid
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,7 +154,15 @@ ol.source.Vector = function(options) {
|
|||||||
this.featureCache_ = new ol.source.FeatureCache();
|
this.featureCache_ = new ol.source.FeatureCache();
|
||||||
|
|
||||||
};
|
};
|
||||||
goog.inherits(ol.source.Vector, ol.source.Source);
|
goog.inherits(ol.source.Vector, ol.source.TileSource);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.tilegrid.TileGrid} tileGrid tile grid.
|
||||||
|
*/
|
||||||
|
ol.source.Vector.prototype.setTileGrid = function(tileGrid) {
|
||||||
|
this.tileGrid = tileGrid;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user