diff --git a/src/d3/pathFeature.js b/src/d3/pathFeature.js index e0bb1e2388..bc93433f42 100644 --- a/src/d3/pathFeature.js +++ b/src/d3/pathFeature.js @@ -3,12 +3,13 @@ var registerFeature = require('../registry').registerFeature; var pathFeature = require('../pathFeature'); /** - * Create a new instance of class pathFeature + * Create a new instance of class geo.d3.pathFeature. * * @class * @alias geo.d3.pathFeature * @extends geo.pathFeature * @extends geo.d3.object + * @param {geo.pathFeature.spec} arg * @returns {geo.d3.pathFeature} */ var d3_pathFeature = function (arg) { @@ -26,11 +27,7 @@ var d3_pathFeature = function (arg) { pathFeature.call(this, arg); object.call(this); - /** - * @private - */ var m_this = this, - s_init = this._init, m_buildTime = timestamp(), s_update = this._update, m_style = {}; @@ -38,21 +35,14 @@ var d3_pathFeature = function (arg) { m_style.style = {}; /** - * Initialize - */ - this._init = function (arg) { - s_init.call(m_this, arg); - return m_this; - }; - - /** - * Build + * Build. * - * @override + * @returns {this} */ this._build = function () { var data = m_this.data() || [], - s_style = m_this.style(), + s_style = m_this.style.get(), + posFunc = m_this.style.get('position'), tmp, diag; s_update.call(m_this); @@ -67,8 +57,8 @@ var d3_pathFeature = function (arg) { data.forEach(function (d, i) { var src, trg; if (i < data.length - 1) { - src = d; - trg = data[i + 1]; + src = posFunc(d, i); + trg = posFunc(data[i + 1], i + 1); tmp.push({ source: m_this.featureGcsToDisplay(src), target: m_this.featureGcsToDisplay(trg) @@ -84,8 +74,8 @@ var d3_pathFeature = function (arg) { m_style.append = 'path'; m_style.classes = ['d3PathFeature']; m_style.style = $.extend({ - 'fill': function () { return false; }, - 'fillColor': function () { return { r: 0, g: 0, b: 0 }; } + fill: function () { return false; }, + fillColor: {r: 0, g: 0, b: 0} }, s_style); m_style.visible = m_this.visible; @@ -97,9 +87,9 @@ var d3_pathFeature = function (arg) { }; /** - * Update + * Update. * - * @override + * @returns {this} */ this._update = function () { s_update.call(m_this); diff --git a/src/pathFeature.js b/src/pathFeature.js index c007cbfc20..f8b9ca293b 100644 --- a/src/pathFeature.js +++ b/src/pathFeature.js @@ -3,11 +3,39 @@ var inherit = require('./inherit'); var feature = require('./feature'); /** - * Create a new instance of class pathFeature + * Specification for pathFeature. + * + * @typedef {geo.feature.spec} geo.pathFeature.spec + * @extends {geo.feature.spec} + * @property {geo.geoPosition|function} [position] Position of the data. + * Default is (data). + */ + +/** + * Style specification for a path feature. + * + * The style for components of the stroke are passed `(dataElement, + * dataIndex)`, where the result applies to the stroke between that data + * element and the following element (at index `dataIndex + `). + * + * @typedef {geo.feature.styleSpec} geo.pathFeature.styleSpec + * @extends geo.feature.styleSpec + * @property {boolean|function} [stroke=true] True to stroke the path. + * @property {geo.geoColor|function} [strokeColor='white'] Color to stroke each + * path. + * @property {number|function} [strokeOpacity=1] Opacity for each path's + * stroke. Opacity is on a [0-1] scale. + * @property {number|function} [strokeWidth=1] The weight of the path's stroke + * in pixels. + */ + +/** + * Create a new instance of class pathFeature. * * @class * @alias geo.pathFeature * @extends geo.feature + * @param {geo.pathFeature.spec} arg * @returns {geo.pathFeature} */ var pathFeature = function (arg) { @@ -18,33 +46,35 @@ var pathFeature = function (arg) { arg = arg || {}; feature.call(this, arg); - /** - * @private - */ var m_this = this, - m_position = arg.position === undefined ? [] : arg.position, s_init = this._init; this.featureType = 'path'; /** - * Get/Set positions + * Get/Set position. * - * @returns {geo.pathFeature} + * @param {function|geo.geoPosition} [val] If not specified, return the + * position accessor. Otherwise, change the position accessor. + * @returns {this|function} */ this.position = function (val) { if (val === undefined) { - return m_position; + return m_this.style('position'); + } + if (val !== m_this.style('position')) { + m_this.style('position', val); + m_this.dataTime().modified(); + m_this.modified(); } - // Copy incoming array of positions - m_position = val; - m_this.dataTime().modified(); - m_this.modified(); return m_this; }; /** - * Initialize + * Initialize. + * + * @param {geo.pathFeature.spec} arg The feature specification. + * @returns {this} */ this._init = function (arg) { s_init.call(m_this, arg); @@ -52,17 +82,22 @@ var pathFeature = function (arg) { var defaultStyle = $.extend( {}, { - 'strokeWidth': function () { return 1; }, - 'strokeColor': function () { return { r: 1.0, g: 1.0, b: 1.0 }; } + strokeWidth: 1, + strokeColor: {r: 1.0, g: 1.0, b: 1.0}, + position: function (d) { return d; } }, arg.style === undefined ? {} : arg.style ); + if (arg.position !== undefined) { + defaultStyle.position = arg.position; + } m_this.style(defaultStyle); - - if (m_position) { - m_this.dataTime().modified(); + if (defaultStyle.position) { + m_this.position(defaultStyle.position); } + m_this.dataTime().modified(); + return m_this; }; this._init(arg); diff --git a/tests/cases/pathFeature.js b/tests/cases/pathFeature.js new file mode 100644 index 0000000000..383db30e6b --- /dev/null +++ b/tests/cases/pathFeature.js @@ -0,0 +1,54 @@ +describe('path feature', function () { + var $ = require('jquery'); + var geo = require('../test-utils').geo; + var createMap = require('../test-utils').createMap; + var destroyMap = require('../test-utils').destroyMap; + var mockAnimationFrame = require('../test-utils').mockAnimationFrame; + var stepAnimationFrame = require('../test-utils').stepAnimationFrame; + var unmockAnimationFrame = require('../test-utils').unmockAnimationFrame; + + var map, layer; + + beforeEach(function () { + mockAnimationFrame(); + map = createMap(); + layer = map.createLayer('feature', {'features': ['path']}); + }); + + afterEach(function () { + destroyMap(); + unmockAnimationFrame(); + }); + + describe('create', function () { + it('direct create', function () { + var path = geo.pathFeature({layer: layer}); + expect(path instanceof geo.pathFeature).toBe(true); + }); + }); + describe('Check public class methods', function () { + it('position', function () { + var feature = layer.createFeature('path'); + expect(feature.position()('a')).toBe('a'); + expect(feature.position(function () { return 'b'; })).toBe(feature); + expect(feature.position()('a')).toBe('b'); + }); + }); + describe('usage', function () { + it('default', function () { + var feature = layer.createFeature('path'); + expect(feature instanceof geo.pathFeature).toBe(true); + feature.data([{x: 4, y: 52}, {x: 5, y: 53}, {x: 4, y: 53.05}]).draw(); + stepAnimationFrame(); + expect($('#map svg path').length).toBe(2); + }); + it('position accessor', function () { + layer.createFeature('path', { + style: {strokeColor: 'black', strokeWidth: 4}, + position: function (d, i) { return {x: d[0], y: d[1]}; } + }).data([[4, 52], [5, 53], [4, 53.05]]).draw(); + stepAnimationFrame(); + expect($('#map svg path').length).toBe(2); + }); + }); +});