Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Properly rendering features to canvas tiles

It looks like this approach will work well for panning (as anticipated).  For animated zooming, it is not going to work as is.  It looks like the canvas tile generation is too much for this type of animation loop.  Though there are clearly still areas for optimization:

 * Don't create new tiles while animating between zoom levels.  Using existing tiles only while animating should bring a significant performance gain.
 * Simple spatial index for tiles - each tile coord in the matrix could have a feature lookup object (keyed by id).  This needs to account for rendered dimension (as witnessed by the point being cut by a tile).  Given that the current example uses only three features, adding the spatial index should only be a minor improvement.
 * Reuse a fixed set of canvas tiles that are generated at construction (and increased/decreased with view size changes).
 * If a fixed set of tiles is not used, at least new ones could be cloned from existing ones (minor).
 * Do some profiling to look for more ideas.

In addition, world-wrapping needs addressed.  I don't think this renderer is the right (or at least the only) place to address this.  And the cache of tiles needs to be managed for real.  But hey, at least we've got a working tiled vector renderer now.
  • Loading branch information...
commit 4e3f984796799a8ba70f31522dbae3ae05e22a9b 1 parent f2b325b
Tim Schaub tschaub authored
41 examples/vector-layer.html
View
@@ -2,23 +2,46 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<link rel="stylesheet" href="style.css" type="text/css">
<style type="text/css">
- #map {
- width: 512px;
- height: 256px;
+ html, body, #map {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
}
- #canvas {
- padding-top: 2em;
+ #text {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ z-index: 20000;
+ background-color: white;
+ padding: 0 0.5em 0.5em 0.5em;
+ border-radius: 4px;
+ }
+ @media only screen and (max-width: 600px) {
+ #text {
+ display: none;
+ }
}
</style>
- <link rel="stylesheet" href="../css/ol.css" type="text/css">
- <title>ol3 vector demo</title>
+ <title>Full-screen example</title>
</head>
<body>
- <div id="map"></div>
- <div id="canvas"></div>
+ <div id="map">
+ <div id="text">
+ <h1 id="title">Vector rendering example</h1>
+ <div id="shortdesc">Uses a canvas renderer for drawing vector features.</div>
+ <div id="docs">
+ <p>See the
+ <a href="vector-layer.js" target="_blank">vector-layer.js source</a>
+ to see how this is done.</p>
+ </div>
+ </div>
+ </div>
+ <div id="tags">vector, feature, canvas</div>
<script src="loader.js?id=vector-layer" type="text/javascript"></script>
</body>
</html>
4 examples/vector-layer.js
View
@@ -37,7 +37,3 @@ var map = new ol.Map({
zoom: 0
})
});
-
-// TODO: remove me
-document.getElementById('canvas').appendChild(
- map.renderer_.getLayerRenderer(vector).canvas_);
138 src/ol/renderer/canvas/canvasvectorlayerrenderer.js
View
@@ -79,9 +79,19 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) {
/**
* @private
+ * @type {ol.Extent}
+ */
+ this.renderedExtent_ = null;
+
+ /**
+ * Flag to be set internally when we know something has changed that suggests
+ * we need to re-render.
+ * TODO: discuss setting this for all layers when something changes before
+ * calling map.render().
+ * @private
* @type {boolean}
*/
- this.layerChanged_ = false;
+ this.dirty_ = false;
// TODO: implement layer.setStyle(style) where style is a set of rules
@@ -143,13 +153,14 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() {
ol.renderer.canvas.VectorLayer.prototype.renderFrame =
function(frameState, layerState) {
+ // TODO: consider bailing out here if rendered center and resolution
+ // have not changed. Requires that other change listeners set a dirty flag.
+
var view2DState = frameState.view2DState,
resolution = view2DState.resolution,
- extent = frameState.extent;
-
- var layer = this.getVectorLayer();
- var source = layer.getVectorSource();
- var tileGrid = source.getTileGrid();
+ extent = frameState.extent,
+ source = this.getVectorLayer().getVectorSource(),
+ tileGrid = source.getTileGrid();
if (goog.isNull(tileGrid)) {
// lazy tile source creation to match the view projection
@@ -158,12 +169,40 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
source.setTileGrid(tileGrid);
}
- var tileSize = tileGrid.getTileSize();
- var z = tileGrid.getZForResolution(resolution);
- var tileResolution = tileGrid.getResolution(z);
- var tileRange = tileGrid.getTileRangeForExtentAndResolution(
- extent, tileResolution);
- var tileRangeExtent = tileGrid.getTileRangeExtent(z, tileRange);
+ // set up transform for the layer canvas to be drawn to the map canvas
+ var tileSize = tileGrid.getTileSize(),
+ z = tileGrid.getZForResolution(resolution),
+ tileResolution = tileGrid.getResolution(z),
+ tileRange = tileGrid.getTileRangeForExtentAndResolution(
+ extent, tileResolution),
+ tileRangeExtent = tileGrid.getTileRangeExtent(z, tileRange),
+ sketchOrigin = tileRangeExtent.getTopLeft(),
+ transform = this.transform_;
+
+ goog.vec.Mat4.makeIdentity(transform);
+ goog.vec.Mat4.translate(transform,
+ frameState.size.width / 2,
+ frameState.size.height / 2,
+ 0);
+ goog.vec.Mat4.scale(transform,
+ tileResolution / resolution, tileResolution / resolution, 1);
+ goog.vec.Mat4.rotateZ(transform, view2DState.rotation);
+ goog.vec.Mat4.translate(transform,
+ (sketchOrigin.x - view2DState.center.x) / tileResolution,
+ (view2DState.center.y - sketchOrigin.y) / tileResolution,
+ 0);
+
+ /**
+ * Fastest path out of here. This method is called many many times while
+ * there is nothing to do (e.g. while waiting for tiles from every other
+ * layer to load.) Do not put anything above here that is more expensive than
+ * necessary. And look for ways to get here faster.
+ */
+ if (!this.dirty_ && this.renderedResolution_ === tileResolution &&
+ // TODO: extent.equals()
+ this.renderedResolution_.toString() === tileRangeExtent.toString()) {
+ return;
+ }
// clear tiles at alt-z
if (this.renderedResolution_ != tileResolution) {
@@ -179,48 +218,37 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
tileSize.width * tileRange.getWidth(),
tileSize.height * tileRange.getHeight());
- sketchCanvas.width = sketchSize.width;
- sketchCanvas.height = sketchSize.height;
-
- // clear/resize final canvas
- var finalCanvas = this.canvas_;
- finalCanvas.width = sketchSize.width;
- finalCanvas.height = sketchSize.height;
- var finalContext = this.context_;
-
- var sketchOrigin = tileRangeExtent.getTopLeft();
- var frameOrigin = extent.getTopLeft();
- var transform = this.transform_;
- goog.vec.Mat4.makeIdentity(transform);
- goog.vec.Mat4.translate(transform,
- frameState.size.width / 2, frameState.size.height / 2, 0);
- goog.vec.Mat4.rotateZ(transform, view2DState.rotation);
- goog.vec.Mat4.scale(
- transform,
- tileResolution / view2DState.resolution,
- tileResolution / view2DState.resolution,
- 1);
- goog.vec.Mat4.translate(
- transform,
- (frameOrigin.x - view2DState.center.x) / tileResolution,
- (view2DState.center.y - frameOrigin.y) / tileResolution,
- 0);
-
+ // transform for map coords to sketch canvas pixel coords
var sketchTransform = this.sketchTransform_;
+ var halfWidth = sketchSize.width / 2;
+ var halfHeight = sketchSize.height / 2;
goog.vec.Mat4.makeIdentity(sketchTransform);
- goog.vec.Mat4.scale(
- sketchTransform,
+ goog.vec.Mat4.translate(sketchTransform,
+ halfWidth,
+ halfHeight,
+ 0);
+ goog.vec.Mat4.scale(sketchTransform,
1 / tileResolution,
-1 / tileResolution,
1);
- goog.vec.Mat4.translate(
- sketchTransform,
- -sketchOrigin.x,
- -sketchOrigin.y,
+ goog.vec.Mat4.translate(sketchTransform,
+ -(sketchOrigin.x + halfWidth * tileResolution),
+ -(sketchOrigin.y - halfHeight * tileResolution),
0);
+ // clear/resize sketch canvas
+ sketchCanvas.width = sketchSize.width;
+ sketchCanvas.height = sketchSize.height;
+
var sketchCanvasRenderer = new ol.renderer.canvas.Renderer(
sketchCanvas, sketchTransform);
+
+ // clear/resize final canvas
+ var finalCanvas = this.canvas_;
+ finalCanvas.width = sketchSize.width;
+ finalCanvas.height = sketchSize.height;
+ var finalContext = this.context_;
+
var renderedFeatures = {};
var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y;
// render features by geometry type
@@ -264,26 +292,14 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
tile.height = tileSize.height;
tileContext = tile.getContext('2d');
- // TODO: remove me
- if (goog.DEBUG) {
- tileContext.strokeStyle = '#999999';
- tileContext.fillStyle = '#999999';
- tileContext.textAlign = 'center';
- tileContext.textBaseline = 'middle';
- tileContext.font = '24px sans-serif';
- tileContext.strokeRect(0.5, 0.5, tileSize.width - 1,
- tileSize.height - 1);
- tileContext.fillText(tileCoord.toString(), tileSize.width / 2,
- tileSize.height / 2);
- }
-
tileContext.drawImage(sketchCanvas,
- -x * tileSize.width, -(tileRange.maxY - y) * tileSize.height);
+ (tileRange.minX - x) * tileSize.width,
+ (y - tileRange.maxY) * tileSize.height);
this.tileCache_[key] = tile;
}
finalContext.drawImage(tile,
- (tileExtent.minX - frameOrigin.x) / tileResolution,
- (frameOrigin.y - tileExtent.maxY) / tileResolution);
+ tileSize.width * (x - tileRange.minX),
+ tileSize.height * (tileRange.maxY - y));
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.