Moving vector rendering to ol.renderer.canvas.VectorRenderer
This commit is contained in:
@@ -1,27 +1,6 @@
|
||||
goog.provide('ol.renderer.canvas.Renderer');
|
||||
goog.provide('ol.renderer.canvas.SUPPORTED');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.net.ImageLoader');
|
||||
goog.require('goog.vec.Mat4');
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.Pixel');
|
||||
goog.require('ol.canvas');
|
||||
goog.require('ol.geom.Geometry');
|
||||
goog.require('ol.geom.GeometryType');
|
||||
goog.require('ol.geom.LineString');
|
||||
goog.require('ol.geom.MultiLineString');
|
||||
goog.require('ol.geom.MultiPoint');
|
||||
goog.require('ol.geom.MultiPolygon');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.geom.Polygon');
|
||||
goog.require('ol.style.IconLiteral');
|
||||
goog.require('ol.style.LineLiteral');
|
||||
goog.require('ol.style.PointLiteral');
|
||||
goog.require('ol.style.PolygonLiteral');
|
||||
goog.require('ol.style.ShapeLiteral');
|
||||
goog.require('ol.style.ShapeType');
|
||||
goog.require('ol.style.SymbolizerLiteral');
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,420 +8,3 @@ goog.require('ol.style.SymbolizerLiteral');
|
||||
* @type {boolean} Is supported.
|
||||
*/
|
||||
ol.renderer.canvas.SUPPORTED = ol.canvas.SUPPORTED;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {HTMLCanvasElement} canvas Target canvas.
|
||||
* @param {goog.vec.Mat4.Number} transform Transform.
|
||||
* @param {ol.Pixel=} opt_offset Pixel offset for top-left corner. This is
|
||||
* provided as an optional argument as a convenience in cases where the
|
||||
* transform applies to a separate canvas.
|
||||
* @param {function()=} opt_iconLoadedCallback Callback for deferred rendering
|
||||
* when images need to be loaded before rendering.
|
||||
*/
|
||||
ol.renderer.canvas.Renderer =
|
||||
function(canvas, transform, opt_offset, opt_iconLoadedCallback) {
|
||||
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d')),
|
||||
dx = goog.isDef(opt_offset) ? opt_offset.x : 0,
|
||||
dy = goog.isDef(opt_offset) ? opt_offset.y : 0;
|
||||
|
||||
/**
|
||||
* @type {goog.vec.Mat4.Number}
|
||||
* @private
|
||||
*/
|
||||
this.transform_ = transform;
|
||||
context.setTransform(
|
||||
goog.vec.Mat4.getElement(transform, 0, 0),
|
||||
goog.vec.Mat4.getElement(transform, 1, 0),
|
||||
goog.vec.Mat4.getElement(transform, 0, 1),
|
||||
goog.vec.Mat4.getElement(transform, 1, 1),
|
||||
goog.vec.Mat4.getElement(transform, 0, 3) + dx,
|
||||
goog.vec.Mat4.getElement(transform, 1, 3) + dy);
|
||||
|
||||
var vec = [1, 0, 0];
|
||||
goog.vec.Mat4.multVec3NoTranslate(transform, vec, vec);
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.inverseScale_ = 1 / Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
|
||||
|
||||
/**
|
||||
* @type {CanvasRenderingContext2D}
|
||||
* @private
|
||||
*/
|
||||
this.context_ = context;
|
||||
|
||||
/**
|
||||
* @type {function()|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.iconLoadedCallback_ = opt_iconLoadedCallback;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.geom.GeometryType} type Geometry type.
|
||||
* @param {Array.<ol.Feature>} features Array of features.
|
||||
* @param {ol.style.SymbolizerLiteral} symbolizer Symbolizer.
|
||||
* @return {boolean} true if deferred, false if rendered.
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType =
|
||||
function(type, features, symbolizer) {
|
||||
var deferred = false;
|
||||
switch (type) {
|
||||
case ol.geom.GeometryType.POINT:
|
||||
case ol.geom.GeometryType.MULTIPOINT:
|
||||
goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral,
|
||||
'Expected point symbolizer: ' + symbolizer);
|
||||
deferred = this.renderPointFeatures_(
|
||||
features, /** @type {ol.style.PointLiteral} */ (symbolizer));
|
||||
break;
|
||||
case ol.geom.GeometryType.LINESTRING:
|
||||
case ol.geom.GeometryType.MULTILINESTRING:
|
||||
goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral,
|
||||
'Expected line symbolizer: ' + symbolizer);
|
||||
this.renderLineStringFeatures_(
|
||||
features, /** @type {ol.style.LineLiteral} */ (symbolizer));
|
||||
break;
|
||||
case ol.geom.GeometryType.POLYGON:
|
||||
case ol.geom.GeometryType.MULTIPOLYGON:
|
||||
goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral,
|
||||
'Expected polygon symbolizer: ' + symbolizer);
|
||||
this.renderPolygonFeatures_(
|
||||
features, /** @type {ol.style.PolygonLiteral} */ (symbolizer));
|
||||
break;
|
||||
default:
|
||||
throw new Error('Rendering not implemented for geometry type: ' + type);
|
||||
}
|
||||
return deferred;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<ol.Feature>} features Array of line features.
|
||||
* @param {ol.style.LineLiteral} symbolizer Line symbolizer.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ =
|
||||
function(features, symbolizer) {
|
||||
|
||||
var context = this.context_,
|
||||
i, ii, geometry, components, j, jj, line, dim, k, kk, x, y;
|
||||
|
||||
context.globalAlpha = symbolizer.opacity;
|
||||
context.strokeStyle = symbolizer.strokeColor;
|
||||
context.lineWidth = symbolizer.strokeWidth * this.inverseScale_;
|
||||
context.lineCap = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.lineJoin = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.beginPath();
|
||||
for (i = 0, ii = features.length; i < ii; ++i) {
|
||||
geometry = features[i].getGeometry();
|
||||
if (geometry instanceof ol.geom.LineString) {
|
||||
components = [geometry];
|
||||
} else {
|
||||
goog.asserts.assert(geometry instanceof ol.geom.MultiLineString,
|
||||
'Expected MultiLineString');
|
||||
components = geometry.components;
|
||||
}
|
||||
for (j = 0, jj = components.length; j < jj; ++j) {
|
||||
line = components[j];
|
||||
dim = line.dimension;
|
||||
for (k = 0, kk = line.getCount(); k < kk; ++k) {
|
||||
x = line.get(k, 0);
|
||||
y = line.get(k, 1);
|
||||
if (k === 0) {
|
||||
context.moveTo(x, y);
|
||||
} else {
|
||||
context.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.stroke();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<ol.Feature>} features Array of point features.
|
||||
* @param {ol.style.PointLiteral} symbolizer Point symbolizer.
|
||||
* @return {boolean} true if deferred, false if rendered.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ =
|
||||
function(features, symbolizer) {
|
||||
|
||||
var context = this.context_,
|
||||
content, alpha, i, ii, geometry, components, j, jj, point, vec;
|
||||
|
||||
if (symbolizer instanceof ol.style.ShapeLiteral) {
|
||||
content = ol.renderer.canvas.Renderer.renderShape(symbolizer);
|
||||
alpha = 1;
|
||||
} else if (symbolizer instanceof ol.style.IconLiteral) {
|
||||
content = ol.renderer.canvas.Renderer.renderIcon(
|
||||
symbolizer, this.iconLoadedCallback_);
|
||||
alpha = symbolizer.opacity;
|
||||
} else {
|
||||
throw new Error('Unsupported symbolizer: ' + symbolizer);
|
||||
}
|
||||
|
||||
if (goog.isNull(content)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var midWidth = content.width / 2;
|
||||
var midHeight = content.height / 2;
|
||||
context.save();
|
||||
context.setTransform(1, 0, 0, 1, -midWidth, -midHeight);
|
||||
context.globalAlpha = alpha;
|
||||
for (i = 0, ii = features.length; i < ii; ++i) {
|
||||
geometry = features[i].getGeometry();
|
||||
if (geometry instanceof ol.geom.Point) {
|
||||
components = [geometry];
|
||||
} else {
|
||||
goog.asserts.assert(geometry instanceof ol.geom.MultiPoint,
|
||||
'Expected MultiPoint');
|
||||
components = geometry.components;
|
||||
}
|
||||
for (j = 0, jj = components.length; j < jj; ++j) {
|
||||
point = components[j];
|
||||
vec = goog.vec.Mat4.multVec3(
|
||||
this.transform_, [point.get(0), point.get(1), 0], []);
|
||||
context.drawImage(content, vec[0], vec[1], content.width, content.height);
|
||||
}
|
||||
}
|
||||
context.restore();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<ol.Feature>} features Array of polygon features.
|
||||
* @param {ol.style.PolygonLiteral} symbolizer Polygon symbolizer.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ =
|
||||
function(features, symbolizer) {
|
||||
var context = this.context_,
|
||||
strokeColor = symbolizer.strokeColor,
|
||||
fillColor = symbolizer.fillColor,
|
||||
i, ii, geometry, components, j, jj, poly,
|
||||
rings, numRings, ring, dim, k, kk, x, y;
|
||||
|
||||
context.globalAlpha = symbolizer.opacity;
|
||||
if (strokeColor) {
|
||||
context.strokeStyle = symbolizer.strokeColor;
|
||||
context.lineWidth = symbolizer.strokeWidth * this.inverseScale_;
|
||||
context.lineCap = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.lineJoin = 'round'; // TODO: accept this as a symbolizer property
|
||||
}
|
||||
if (fillColor) {
|
||||
context.fillStyle = fillColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Four scenarios covered here:
|
||||
* 1) stroke only, no holes - only need to have a single path
|
||||
* 2) fill only, no holes - only need to have a single path
|
||||
* 3) fill and stroke, no holes
|
||||
* 4) holes - render polygon to sketch canvas first
|
||||
*/
|
||||
context.beginPath();
|
||||
for (i = 0, ii = features.length; i < ii; ++i) {
|
||||
geometry = features[i].getGeometry();
|
||||
if (geometry instanceof ol.geom.Polygon) {
|
||||
components = [geometry];
|
||||
} else {
|
||||
goog.asserts.assert(geometry instanceof ol.geom.MultiPolygon,
|
||||
'Expected MultiPolygon');
|
||||
components = geometry.components;
|
||||
}
|
||||
for (j = 0, jj = components.length; j < jj; ++j) {
|
||||
poly = components[j];
|
||||
dim = poly.dimension;
|
||||
rings = poly.rings;
|
||||
numRings = rings.length;
|
||||
if (numRings > 0) {
|
||||
// TODO: scenario 4
|
||||
ring = rings[0];
|
||||
for (k = 0, kk = ring.getCount(); k < kk; ++k) {
|
||||
x = ring.get(k, 0);
|
||||
y = ring.get(k, 1);
|
||||
if (k === 0) {
|
||||
context.moveTo(x, y);
|
||||
} else {
|
||||
context.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
if (fillColor && strokeColor) {
|
||||
// scenario 3 - fill and stroke each time
|
||||
context.fill();
|
||||
context.stroke();
|
||||
if (i < ii - 1 || j < jj - 1) {
|
||||
context.beginPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(fillColor && strokeColor)) {
|
||||
if (fillColor) {
|
||||
// scenario 2 - fill all at once
|
||||
context.fill();
|
||||
} else {
|
||||
// scenario 1 - stroke all at once
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.style.ShapeLiteral} circle Shape symbolizer.
|
||||
* @return {!HTMLCanvasElement} Canvas element.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.renderCircle_ = function(circle) {
|
||||
var strokeWidth = circle.strokeWidth || 0,
|
||||
size = circle.size + (2 * strokeWidth) + 1,
|
||||
mid = size / 2,
|
||||
canvas = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS)),
|
||||
context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d')),
|
||||
fillColor = circle.fillColor,
|
||||
strokeColor = circle.strokeColor,
|
||||
twoPi = Math.PI * 2;
|
||||
|
||||
canvas.height = size;
|
||||
canvas.width = size;
|
||||
|
||||
context.globalAlpha = circle.opacity;
|
||||
|
||||
if (fillColor) {
|
||||
context.fillStyle = fillColor;
|
||||
}
|
||||
if (strokeColor) {
|
||||
context.lineWidth = strokeWidth;
|
||||
context.strokeStyle = strokeColor;
|
||||
context.lineCap = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.lineJoin = 'round'; // TODO: accept this as a symbolizer property
|
||||
}
|
||||
|
||||
context.beginPath();
|
||||
context.arc(mid, mid, circle.size / 2, 0, twoPi, true);
|
||||
|
||||
if (fillColor) {
|
||||
context.fill();
|
||||
}
|
||||
if (strokeColor) {
|
||||
context.stroke();
|
||||
}
|
||||
return canvas;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.style.ShapeLiteral} shape Shape symbolizer.
|
||||
* @return {!HTMLCanvasElement} Canvas element.
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.renderShape = function(shape) {
|
||||
var canvas;
|
||||
if (shape.type === ol.style.ShapeType.CIRCLE) {
|
||||
canvas = ol.renderer.canvas.Renderer.renderCircle_(shape);
|
||||
} else {
|
||||
throw new Error('Unsupported shape type: ' + shape);
|
||||
}
|
||||
return canvas;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.style.IconLiteral} icon Icon literal.
|
||||
* @param {function()=} opt_callback Callback which will be called when
|
||||
* the icon is loaded and rendering will work without deferring.
|
||||
* @return {HTMLImageElement} image element of null if deferred.
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.renderIcon = function(icon, opt_callback) {
|
||||
var url = icon.url;
|
||||
var image = ol.renderer.canvas.Renderer.icons_[url];
|
||||
var deferred = false;
|
||||
if (!goog.isDef(image)) {
|
||||
deferred = true;
|
||||
image = /** @type {HTMLImageElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.IMG));
|
||||
goog.events.listenOnce(image, goog.events.EventType.ERROR,
|
||||
goog.bind(ol.renderer.canvas.Renderer.handleIconError_, null,
|
||||
opt_callback),
|
||||
false, ol.renderer.canvas.Renderer.renderIcon);
|
||||
goog.events.listenOnce(image, goog.events.EventType.LOAD,
|
||||
goog.bind(ol.renderer.canvas.Renderer.handleIconLoad_, null,
|
||||
opt_callback),
|
||||
false, ol.renderer.canvas.Renderer.renderIcon);
|
||||
image.setAttribute('src', url);
|
||||
ol.renderer.canvas.Renderer.icons_[url] = image;
|
||||
} else if (!goog.isNull(image)) {
|
||||
var width = icon.width,
|
||||
height = icon.height;
|
||||
if (goog.isDef(width) && goog.isDef(height)) {
|
||||
image.width = width;
|
||||
image.height = height;
|
||||
} else if (goog.isDef(width)) {
|
||||
image.height = width / image.width * image.height;
|
||||
image.width = width;
|
||||
} else if (goog.isDef(height)) {
|
||||
image.width = height / image.height * image.width;
|
||||
image.height = height;
|
||||
}
|
||||
}
|
||||
return deferred ? null : image;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @type {Object.<string, HTMLImageElement>}
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.icons_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* @param {function()=} opt_callback Callback.
|
||||
* @param {Event=} opt_event Event.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.handleIconError_ =
|
||||
function(opt_callback, opt_event) {
|
||||
if (goog.isDef(opt_event)) {
|
||||
var url = opt_event.target.getAttribute('src');
|
||||
ol.renderer.canvas.Renderer.icons_[url] = null;
|
||||
ol.renderer.canvas.Renderer.handleIconLoad_(opt_callback, opt_event);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {function()=} opt_callback Callback.
|
||||
* @param {Event=} opt_event Event.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.Renderer.handleIconLoad_ =
|
||||
function(opt_callback, opt_event) {
|
||||
if (goog.isDef(opt_event)) {
|
||||
var url = opt_event.target.getAttribute('src');
|
||||
ol.renderer.canvas.Renderer.icons_[url] =
|
||||
/** @type {HTMLImageElement} */ (opt_event.target);
|
||||
}
|
||||
if (goog.isDef(opt_callback)) {
|
||||
opt_callback();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ goog.require('ol.filter.LogicalOperator');
|
||||
goog.require('ol.geom.GeometryType');
|
||||
goog.require('ol.layer.Vector');
|
||||
goog.require('ol.renderer.canvas.Layer');
|
||||
goog.require('ol.renderer.canvas.Renderer');
|
||||
goog.require('ol.renderer.canvas.VectorRenderer');
|
||||
goog.require('ol.tilegrid.TileGrid');
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
|
||||
sketchCanvas.width = sketchSize.width;
|
||||
sketchCanvas.height = sketchSize.height;
|
||||
|
||||
var sketchCanvasRenderer = new ol.renderer.canvas.Renderer(
|
||||
var sketchCanvasRenderer = new ol.renderer.canvas.VectorRenderer(
|
||||
sketchCanvas, sketchTransform, undefined, this.requestMapRenderFrame_);
|
||||
|
||||
// clear/resize final canvas
|
||||
|
||||
440
src/ol/renderer/canvas/canvasvectorrenderer.js
Normal file
440
src/ol/renderer/canvas/canvasvectorrenderer.js
Normal file
@@ -0,0 +1,440 @@
|
||||
goog.provide('ol.renderer.canvas.VectorRenderer');
|
||||
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.net.ImageLoader');
|
||||
goog.require('goog.vec.Mat4');
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.Pixel');
|
||||
goog.require('ol.canvas');
|
||||
goog.require('ol.geom.Geometry');
|
||||
goog.require('ol.geom.GeometryType');
|
||||
goog.require('ol.geom.LineString');
|
||||
goog.require('ol.geom.MultiLineString');
|
||||
goog.require('ol.geom.MultiPoint');
|
||||
goog.require('ol.geom.MultiPolygon');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.geom.Polygon');
|
||||
goog.require('ol.style.IconLiteral');
|
||||
goog.require('ol.style.LineLiteral');
|
||||
goog.require('ol.style.PointLiteral');
|
||||
goog.require('ol.style.PolygonLiteral');
|
||||
goog.require('ol.style.ShapeLiteral');
|
||||
goog.require('ol.style.ShapeType');
|
||||
goog.require('ol.style.SymbolizerLiteral');
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {HTMLCanvasElement} canvas Target canvas.
|
||||
* @param {goog.vec.Mat4.Number} transform Transform.
|
||||
* @param {ol.Pixel=} opt_offset Pixel offset for top-left corner. This is
|
||||
* provided as an optional argument as a convenience in cases where the
|
||||
* transform applies to a separate canvas.
|
||||
* @param {function()=} opt_iconLoadedCallback Callback for deferred rendering
|
||||
* when images need to be loaded before rendering.
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer =
|
||||
function(canvas, transform, opt_offset, opt_iconLoadedCallback) {
|
||||
|
||||
var context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d')),
|
||||
dx = goog.isDef(opt_offset) ? opt_offset.x : 0,
|
||||
dy = goog.isDef(opt_offset) ? opt_offset.y : 0;
|
||||
|
||||
/**
|
||||
* @type {goog.vec.Mat4.Number}
|
||||
* @private
|
||||
*/
|
||||
this.transform_ = transform;
|
||||
context.setTransform(
|
||||
goog.vec.Mat4.getElement(transform, 0, 0),
|
||||
goog.vec.Mat4.getElement(transform, 1, 0),
|
||||
goog.vec.Mat4.getElement(transform, 0, 1),
|
||||
goog.vec.Mat4.getElement(transform, 1, 1),
|
||||
goog.vec.Mat4.getElement(transform, 0, 3) + dx,
|
||||
goog.vec.Mat4.getElement(transform, 1, 3) + dy);
|
||||
|
||||
var vec = [1, 0, 0];
|
||||
goog.vec.Mat4.multVec3NoTranslate(transform, vec, vec);
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.inverseScale_ = 1 / Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
|
||||
|
||||
/**
|
||||
* @type {CanvasRenderingContext2D}
|
||||
* @private
|
||||
*/
|
||||
this.context_ = context;
|
||||
|
||||
/**
|
||||
* @type {function()|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.iconLoadedCallback_ = opt_iconLoadedCallback;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.geom.GeometryType} type Geometry type.
|
||||
* @param {Array.<ol.Feature>} features Array of features.
|
||||
* @param {ol.style.SymbolizerLiteral} symbolizer Symbolizer.
|
||||
* @return {boolean} true if deferred, false if rendered.
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.prototype.renderFeaturesByGeometryType =
|
||||
function(type, features, symbolizer) {
|
||||
var deferred = false;
|
||||
switch (type) {
|
||||
case ol.geom.GeometryType.POINT:
|
||||
case ol.geom.GeometryType.MULTIPOINT:
|
||||
goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral,
|
||||
'Expected point symbolizer: ' + symbolizer);
|
||||
deferred = this.renderPointFeatures_(
|
||||
features, /** @type {ol.style.PointLiteral} */ (symbolizer));
|
||||
break;
|
||||
case ol.geom.GeometryType.LINESTRING:
|
||||
case ol.geom.GeometryType.MULTILINESTRING:
|
||||
goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral,
|
||||
'Expected line symbolizer: ' + symbolizer);
|
||||
this.renderLineStringFeatures_(
|
||||
features, /** @type {ol.style.LineLiteral} */ (symbolizer));
|
||||
break;
|
||||
case ol.geom.GeometryType.POLYGON:
|
||||
case ol.geom.GeometryType.MULTIPOLYGON:
|
||||
goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral,
|
||||
'Expected polygon symbolizer: ' + symbolizer);
|
||||
this.renderPolygonFeatures_(
|
||||
features, /** @type {ol.style.PolygonLiteral} */ (symbolizer));
|
||||
break;
|
||||
default:
|
||||
throw new Error('Rendering not implemented for geometry type: ' + type);
|
||||
}
|
||||
return deferred;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<ol.Feature>} features Array of line features.
|
||||
* @param {ol.style.LineLiteral} symbolizer Line symbolizer.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.prototype.renderLineStringFeatures_ =
|
||||
function(features, symbolizer) {
|
||||
|
||||
var context = this.context_,
|
||||
i, ii, geometry, components, j, jj, line, dim, k, kk, x, y;
|
||||
|
||||
context.globalAlpha = symbolizer.opacity;
|
||||
context.strokeStyle = symbolizer.strokeColor;
|
||||
context.lineWidth = symbolizer.strokeWidth * this.inverseScale_;
|
||||
context.lineCap = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.lineJoin = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.beginPath();
|
||||
for (i = 0, ii = features.length; i < ii; ++i) {
|
||||
geometry = features[i].getGeometry();
|
||||
if (geometry instanceof ol.geom.LineString) {
|
||||
components = [geometry];
|
||||
} else {
|
||||
goog.asserts.assert(geometry instanceof ol.geom.MultiLineString,
|
||||
'Expected MultiLineString');
|
||||
components = geometry.components;
|
||||
}
|
||||
for (j = 0, jj = components.length; j < jj; ++j) {
|
||||
line = components[j];
|
||||
dim = line.dimension;
|
||||
for (k = 0, kk = line.getCount(); k < kk; ++k) {
|
||||
x = line.get(k, 0);
|
||||
y = line.get(k, 1);
|
||||
if (k === 0) {
|
||||
context.moveTo(x, y);
|
||||
} else {
|
||||
context.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.stroke();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<ol.Feature>} features Array of point features.
|
||||
* @param {ol.style.PointLiteral} symbolizer Point symbolizer.
|
||||
* @return {boolean} true if deferred, false if rendered.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.prototype.renderPointFeatures_ =
|
||||
function(features, symbolizer) {
|
||||
|
||||
var context = this.context_,
|
||||
content, alpha, i, ii, geometry, components, j, jj, point, vec;
|
||||
|
||||
if (symbolizer instanceof ol.style.ShapeLiteral) {
|
||||
content = ol.renderer.canvas.VectorRenderer.renderShape(symbolizer);
|
||||
alpha = 1;
|
||||
} else if (symbolizer instanceof ol.style.IconLiteral) {
|
||||
content = ol.renderer.canvas.VectorRenderer.renderIcon(
|
||||
symbolizer, this.iconLoadedCallback_);
|
||||
alpha = symbolizer.opacity;
|
||||
} else {
|
||||
throw new Error('Unsupported symbolizer: ' + symbolizer);
|
||||
}
|
||||
|
||||
if (goog.isNull(content)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var midWidth = content.width / 2;
|
||||
var midHeight = content.height / 2;
|
||||
context.save();
|
||||
context.setTransform(1, 0, 0, 1, -midWidth, -midHeight);
|
||||
context.globalAlpha = alpha;
|
||||
for (i = 0, ii = features.length; i < ii; ++i) {
|
||||
geometry = features[i].getGeometry();
|
||||
if (geometry instanceof ol.geom.Point) {
|
||||
components = [geometry];
|
||||
} else {
|
||||
goog.asserts.assert(geometry instanceof ol.geom.MultiPoint,
|
||||
'Expected MultiPoint');
|
||||
components = geometry.components;
|
||||
}
|
||||
for (j = 0, jj = components.length; j < jj; ++j) {
|
||||
point = components[j];
|
||||
vec = goog.vec.Mat4.multVec3(
|
||||
this.transform_, [point.get(0), point.get(1), 0], []);
|
||||
context.drawImage(content, vec[0], vec[1], content.width, content.height);
|
||||
}
|
||||
}
|
||||
context.restore();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<ol.Feature>} features Array of polygon features.
|
||||
* @param {ol.style.PolygonLiteral} symbolizer Polygon symbolizer.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.prototype.renderPolygonFeatures_ =
|
||||
function(features, symbolizer) {
|
||||
var context = this.context_,
|
||||
strokeColor = symbolizer.strokeColor,
|
||||
fillColor = symbolizer.fillColor,
|
||||
i, ii, geometry, components, j, jj, poly,
|
||||
rings, numRings, ring, dim, k, kk, x, y;
|
||||
|
||||
context.globalAlpha = symbolizer.opacity;
|
||||
if (strokeColor) {
|
||||
context.strokeStyle = symbolizer.strokeColor;
|
||||
context.lineWidth = symbolizer.strokeWidth * this.inverseScale_;
|
||||
context.lineCap = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.lineJoin = 'round'; // TODO: accept this as a symbolizer property
|
||||
}
|
||||
if (fillColor) {
|
||||
context.fillStyle = fillColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Four scenarios covered here:
|
||||
* 1) stroke only, no holes - only need to have a single path
|
||||
* 2) fill only, no holes - only need to have a single path
|
||||
* 3) fill and stroke, no holes
|
||||
* 4) holes - render polygon to sketch canvas first
|
||||
*/
|
||||
context.beginPath();
|
||||
for (i = 0, ii = features.length; i < ii; ++i) {
|
||||
geometry = features[i].getGeometry();
|
||||
if (geometry instanceof ol.geom.Polygon) {
|
||||
components = [geometry];
|
||||
} else {
|
||||
goog.asserts.assert(geometry instanceof ol.geom.MultiPolygon,
|
||||
'Expected MultiPolygon');
|
||||
components = geometry.components;
|
||||
}
|
||||
for (j = 0, jj = components.length; j < jj; ++j) {
|
||||
poly = components[j];
|
||||
dim = poly.dimension;
|
||||
rings = poly.rings;
|
||||
numRings = rings.length;
|
||||
if (numRings > 0) {
|
||||
// TODO: scenario 4
|
||||
ring = rings[0];
|
||||
for (k = 0, kk = ring.getCount(); k < kk; ++k) {
|
||||
x = ring.get(k, 0);
|
||||
y = ring.get(k, 1);
|
||||
if (k === 0) {
|
||||
context.moveTo(x, y);
|
||||
} else {
|
||||
context.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
if (fillColor && strokeColor) {
|
||||
// scenario 3 - fill and stroke each time
|
||||
context.fill();
|
||||
context.stroke();
|
||||
if (i < ii - 1 || j < jj - 1) {
|
||||
context.beginPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(fillColor && strokeColor)) {
|
||||
if (fillColor) {
|
||||
// scenario 2 - fill all at once
|
||||
context.fill();
|
||||
} else {
|
||||
// scenario 1 - stroke all at once
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.style.ShapeLiteral} circle Shape symbolizer.
|
||||
* @return {!HTMLCanvasElement} Canvas element.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.renderCircle_ = function(circle) {
|
||||
var strokeWidth = circle.strokeWidth || 0,
|
||||
size = circle.size + (2 * strokeWidth) + 1,
|
||||
mid = size / 2,
|
||||
canvas = /** @type {HTMLCanvasElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.CANVAS)),
|
||||
context = /** @type {CanvasRenderingContext2D} */
|
||||
(canvas.getContext('2d')),
|
||||
fillColor = circle.fillColor,
|
||||
strokeColor = circle.strokeColor,
|
||||
twoPi = Math.PI * 2;
|
||||
|
||||
canvas.height = size;
|
||||
canvas.width = size;
|
||||
|
||||
context.globalAlpha = circle.opacity;
|
||||
|
||||
if (fillColor) {
|
||||
context.fillStyle = fillColor;
|
||||
}
|
||||
if (strokeColor) {
|
||||
context.lineWidth = strokeWidth;
|
||||
context.strokeStyle = strokeColor;
|
||||
context.lineCap = 'round'; // TODO: accept this as a symbolizer property
|
||||
context.lineJoin = 'round'; // TODO: accept this as a symbolizer property
|
||||
}
|
||||
|
||||
context.beginPath();
|
||||
context.arc(mid, mid, circle.size / 2, 0, twoPi, true);
|
||||
|
||||
if (fillColor) {
|
||||
context.fill();
|
||||
}
|
||||
if (strokeColor) {
|
||||
context.stroke();
|
||||
}
|
||||
return canvas;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.style.ShapeLiteral} shape Shape symbolizer.
|
||||
* @return {!HTMLCanvasElement} Canvas element.
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.renderShape = function(shape) {
|
||||
var canvas;
|
||||
if (shape.type === ol.style.ShapeType.CIRCLE) {
|
||||
canvas = ol.renderer.canvas.VectorRenderer.renderCircle_(shape);
|
||||
} else {
|
||||
throw new Error('Unsupported shape type: ' + shape);
|
||||
}
|
||||
return canvas;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.style.IconLiteral} icon Icon literal.
|
||||
* @param {function()=} opt_callback Callback which will be called when
|
||||
* the icon is loaded and rendering will work without deferring.
|
||||
* @return {HTMLImageElement} image element of null if deferred.
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.renderIcon = function(icon, opt_callback) {
|
||||
var url = icon.url;
|
||||
var image = ol.renderer.canvas.VectorRenderer.icons_[url];
|
||||
var deferred = false;
|
||||
if (!goog.isDef(image)) {
|
||||
deferred = true;
|
||||
image = /** @type {HTMLImageElement} */
|
||||
(goog.dom.createElement(goog.dom.TagName.IMG));
|
||||
goog.events.listenOnce(image, goog.events.EventType.ERROR,
|
||||
goog.bind(ol.renderer.canvas.VectorRenderer.handleIconError_, null,
|
||||
opt_callback),
|
||||
false, ol.renderer.canvas.VectorRenderer.renderIcon);
|
||||
goog.events.listenOnce(image, goog.events.EventType.LOAD,
|
||||
goog.bind(ol.renderer.canvas.VectorRenderer.handleIconLoad_, null,
|
||||
opt_callback),
|
||||
false, ol.renderer.canvas.VectorRenderer.renderIcon);
|
||||
image.setAttribute('src', url);
|
||||
ol.renderer.canvas.VectorRenderer.icons_[url] = image;
|
||||
} else if (!goog.isNull(image)) {
|
||||
var width = icon.width,
|
||||
height = icon.height;
|
||||
if (goog.isDef(width) && goog.isDef(height)) {
|
||||
image.width = width;
|
||||
image.height = height;
|
||||
} else if (goog.isDef(width)) {
|
||||
image.height = width / image.width * image.height;
|
||||
image.width = width;
|
||||
} else if (goog.isDef(height)) {
|
||||
image.width = height / image.height * image.width;
|
||||
image.height = height;
|
||||
}
|
||||
}
|
||||
return deferred ? null : image;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @type {Object.<string, HTMLImageElement>}
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.icons_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* @param {function()=} opt_callback Callback.
|
||||
* @param {Event=} opt_event Event.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.handleIconError_ =
|
||||
function(opt_callback, opt_event) {
|
||||
if (goog.isDef(opt_event)) {
|
||||
var url = opt_event.target.getAttribute('src');
|
||||
ol.renderer.canvas.VectorRenderer.icons_[url] = null;
|
||||
ol.renderer.canvas.VectorRenderer.handleIconLoad_(opt_callback, opt_event);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {function()=} opt_callback Callback.
|
||||
* @param {Event=} opt_event Event.
|
||||
* @private
|
||||
*/
|
||||
ol.renderer.canvas.VectorRenderer.handleIconLoad_ =
|
||||
function(opt_callback, opt_event) {
|
||||
if (goog.isDef(opt_event)) {
|
||||
var url = opt_event.target.getAttribute('src');
|
||||
ol.renderer.canvas.VectorRenderer.icons_[url] =
|
||||
/** @type {HTMLImageElement} */ (opt_event.target);
|
||||
}
|
||||
if (goog.isDef(opt_callback)) {
|
||||
opt_callback();
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user